From a0042ef21947267c96f69b4dd9849d41900d0b0a Mon Sep 17 00:00:00 2001 From: Anton Arbring Date: Wed, 13 May 2015 16:15:50 -0400 Subject: [PATCH 001/329] More last minute fixes Fixed plane bug (again) changed rex animation (again) More key binds etc --- include/openspace/rendering/model/renderablemodel.h | 1 + .../openspace/rendering/renderableplaneprojection.h | 1 + scripts/bind_keys.lua | 13 ++++++++++++- scripts/default_settings.lua | 5 +++-- shaders/modules/crawlingline/crawlingline_fs.glsl | 4 ++-- shaders/modules/crawlingline/crawlingline_vs.glsl | 12 ++++++++++++ src/rendering/model/renderablemodel.cpp | 4 +++- src/rendering/renderablecrawlingline.cpp | 6 +++--- src/rendering/renderableplaneprojection.cpp | 11 +++++++++-- src/rendering/renderabletrail.cpp | 8 ++++---- src/rendering/renderengine.cpp | 2 +- 11 files changed, 51 insertions(+), 16 deletions(-) diff --git a/include/openspace/rendering/model/renderablemodel.h b/include/openspace/rendering/model/renderablemodel.h index 2522d136df..4ec28fd212 100644 --- a/include/openspace/rendering/model/renderablemodel.h +++ b/include/openspace/rendering/model/renderablemodel.h @@ -72,6 +72,7 @@ private: std::string _target; bool _isGhost; + int _frameCount; psc _sunPosition; diff --git a/include/openspace/rendering/renderableplaneprojection.h b/include/openspace/rendering/renderableplaneprojection.h index d65d123d4c..8a7e8e4d75 100644 --- a/include/openspace/rendering/renderableplaneprojection.h +++ b/include/openspace/rendering/renderableplaneprojection.h @@ -89,6 +89,7 @@ namespace openspace { std::string _spacecraft; std::string _instrument; + double _previousTime; target _target; std::string _name; bool _moving; diff --git a/scripts/bind_keys.lua b/scripts/bind_keys.lua index 6340812048..9ae91e5c89 100644 --- a/scripts/bind_keys.lua +++ b/scripts/bind_keys.lua @@ -25,6 +25,14 @@ openspace.bindKey("7", "openspace.time.setDeltaTime(360)") openspace.bindKey("8", "openspace.time.setDeltaTime(540)") openspace.bindKey("9", "openspace.time.setDeltaTime(720)") +openspace.bindKey("=", "openspace.time.setTime('2015-07-14T09:00:00.00'); openspace.setPropertyValue('PlutoProjection.renderable.clearAllProjections', true); openspace.setPropertyValue('Charon.renderable.clearAllProjections', true);") + +-- Quickfix backjumps in pluto sequence +openspace.bindKey("F9", "openspace.time.setTime('2015-07-14T09:00:00.00'); openspace.setPropertyValue('PlutoProjection.renderable.clearAllProjections', true); openspace.setPropertyValue('Charon.renderable.clearAllProjections', true);") +openspace.bindKey("F10", "openspace.time.setTime('2015-07-14T10:00:00.00'); openspace.setPropertyValue('PlutoProjection.renderable.clearAllProjections', true); openspace.setPropertyValue('Charon.renderable.clearAllProjections', true);") +openspace.bindKey("F11", "openspace.time.setTime('2015-07-14T11:17:00.00'); openspace.setPropertyValue('PlutoProjection.renderable.clearAllProjections', true); openspace.setPropertyValue('Charon.renderable.clearAllProjections', true);") +openspace.bindKey("F12", "openspace.time.setTime('2015-07-14T12:45:00.00'); openspace.setPropertyValue('PlutoProjection.renderable.clearAllProjections', true); openspace.setPropertyValue('Charon.renderable.clearAllProjections', true);") + openspace.bindKey("a", "openspace.setOrigin('NewHorizons')") openspace.bindKey("s", "openspace.setOrigin('PlutoProjection')") openspace.bindKey("d", "openspace.setOrigin('Charon')") @@ -34,7 +42,7 @@ openspace.bindKey("x", "openspace.setOrigin('Europa')") openspace.bindKey("g", "openspace.time.setTime('2007-02-28T11:40:00.00'); openspace.time.setDeltaTime(1);") -openspace.bindKey("h", "openspace.time.setTime('2015-07-14T09:00:00.00'); openspace.time.setDeltaTime(1); openspace.changeCoordinateSystem('Pluto'); openspace.printInfo('Changing Viewpoint to Pluto-in-center');") +openspace.bindKey("h", "openspace.time.setTime('2015-07-14T09:00:00.00'); openspace.time.setDeltaTime(1); openspace.changeCoordinateSystem('Pluto'); openspace.setOrigin('PlutoProjection'); openspace.printInfo('Changing Viewpoint to Pluto-in-center');") --[[ openspace.bindKey("1", "openspace.time.setTime('2007-02-27T16:40:00.00'); openspace.time.setDeltaTime(10)") @@ -49,6 +57,8 @@ openspace.bindKey("7", "openspace.time.setTime('2015-07-14T15:02:46.00'); opensp openspace.bindKey("q", "local b = openspace.getPropertyValue('SunMarker.renderable.enabled'); openspace.setPropertyValue('SunMarker.renderable.enabled', not b)") openspace.bindKey("e", "local b = openspace.getPropertyValue('EarthMarker.renderable.enabled'); openspace.setPropertyValue('EarthMarker.renderable.enabled', not b)") +openspace.bindKey("o", "local b = openspace.getPropertyValue('PlutoTrail.renderable.enabled'); openspace.setPropertyValue('PlutoTrail.renderable.enabled', not b)") + openspace.bindKey("k", "local b = openspace.getPropertyValue('HydraText.renderable.enabled'); openspace.setPropertyValue('HydraText.renderable.enabled', not b)") openspace.bindKey("k", "local b = openspace.getPropertyValue('CharonText.renderable.enabled'); openspace.setPropertyValue('CharonText.renderable.enabled', not b)") openspace.bindKey("k", "local b = openspace.getPropertyValue('NixText.renderable.enabled'); openspace.setPropertyValue('NixText.renderable.enabled', not b)") @@ -69,6 +79,7 @@ openspace.bindKey("m", "local b = openspace.getPropertyValue('NH_RALPH_MVIC_FT.r openspace.bindKey("m", "local b = openspace.getPropertyValue('NH_RALPH_MVIC_METHANE.renderable.solidDraw'); openspace.setPropertyValue('NH_RALPH_MVIC_METHANE.renderable.solidDraw', not b)") openspace.bindKey("m", "local b = openspace.getPropertyValue('NH_RALPH_MVIC_NIR.renderable.solidDraw'); openspace.setPropertyValue('NH_RALPH_MVIC_NIR.renderable.solidDraw', not b)") openspace.bindKey("m", "local b = openspace.getPropertyValue('NH_ALICE_AIRGLOW.renderable.solidDraw'); openspace.setPropertyValue('NH_ALICE_AIRGLOW.renderable.solidDraw', not b)") +openspace.bindKey("m", "local b = openspace.getPropertyValue('NH_ALICE_SOC.renderable.solidDraw'); openspace.setPropertyValue('NH_ALICE_AIRGLOW.renderable.solidDraw', not b)") openspace.bindKey("p", "local b = openspace.getPropertyValue('JupiterProjection.renderable.performProjection'); openspace.setPropertyValue('JupiterProjection.renderable.performProjection', not b)") openspace.bindKey("p", "local b = openspace.getPropertyValue('Io.renderable.performProjection'); openspace.setPropertyValue('Io.renderable.performProjection', not b)") diff --git a/scripts/default_settings.lua b/scripts/default_settings.lua index 534a6507bb..1b696627cb 100644 --- a/scripts/default_settings.lua +++ b/scripts/default_settings.lua @@ -4,9 +4,10 @@ openspace.printInfo("Setting default values") openspace.setPropertyValue("Sun.renderable.enabled", false) -openspace.setPropertyValue("SunMarker.renderable.enabled", false) -openspace.setPropertyValue("EarthMarker.renderable.enabled", false) +openspace.setPropertyValue("SunMarker.renderable.enabled", true) +openspace.setPropertyValue("EarthMarker.renderable.enabled", true) openspace.setPropertyValue("Constellation Bounds.renderable.enabled", false) +openspace.setPropertyValue("PlutoTrail.renderable.enabled", false) openspace.setPropertyValue("MilkyWay.renderable.transparency", 0.75) openspace.setPropertyValue("MilkyWay.renderable.segments", 50) diff --git a/shaders/modules/crawlingline/crawlingline_fs.glsl b/shaders/modules/crawlingline/crawlingline_fs.glsl index f6c31a0d96..71cb6332d1 100644 --- a/shaders/modules/crawlingline/crawlingline_fs.glsl +++ b/shaders/modules/crawlingline/crawlingline_fs.glsl @@ -30,6 +30,7 @@ uniform vec3 color; uniform float _alpha; in vec4 vs_position; +in vec4 vs_color; #include "ABuffer/abufferStruct.hglsl" #include "ABuffer/abufferAddToBuffer.hglsl" @@ -37,9 +38,8 @@ in vec4 vs_position; void main() { vec4 position = vs_position; + vec4 diffuse = vs_color; float depth = pscDepth(position); - // vec4 diffuse = texture(texture1, vs_st); - vec4 diffuse = vec4(color, 1.0); diffuse.a = _alpha; ABufferStruct_t frag = createGeometryFragment(diffuse, position, depth); addToBuffer(frag); diff --git a/shaders/modules/crawlingline/crawlingline_vs.glsl b/shaders/modules/crawlingline/crawlingline_vs.glsl index 114802885c..3614f54cf5 100644 --- a/shaders/modules/crawlingline/crawlingline_vs.glsl +++ b/shaders/modules/crawlingline/crawlingline_vs.glsl @@ -27,16 +27,28 @@ uniform mat4 ViewProjection; uniform mat4 ModelTransform; +uniform vec3 color; + layout(location = 0) in vec4 in_position; +out vec4 vs_color; out vec4 vs_position; +const int targetId = 1; #include "PowerScaling/powerScaling_vs.hglsl" void main() { vs_position = in_position; vec4 tmp = in_position; + int id = gl_VertexID; + + vec3 black = { 0.f, 0.f, 0.f }; + if(id == targetId) + vs_color.xyz = black; + else + vs_color.xyz = color; + vec4 position = pscTransform(tmp, ModelTransform); vs_position = tmp; position = ViewProjection * position; diff --git a/src/rendering/model/renderablemodel.cpp b/src/rendering/model/renderablemodel.cpp index edea55ff3c..f3480cb0fa 100644 --- a/src/rendering/model/renderablemodel.cpp +++ b/src/rendering/model/renderablemodel.cpp @@ -71,6 +71,7 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) , _alpha(1.f) , _fading("fading", "Fade", 0) , _performFade("performFading", "Perform Fading", false) + , _frameCount(0) { std::string name; bool success = dictionary.getValue(constants::scenegraphnode::keyName, name); @@ -159,6 +160,7 @@ bool RenderableModel::deinitialize() { void RenderableModel::render(const RenderData& data) { _programObject->activate(); + _frameCount++; double lt; glm::mat4 transform = glm::mat4(1); @@ -174,7 +176,7 @@ void RenderableModel::render(const RenderData& data) { double time = openspace::Time::ref().currentTime(); bool targetPositionCoverage = openspace::SpiceManager::ref().hasSpkCoverage(_target, time); if (!targetPositionCoverage){ - int frame = ImGui::GetFrameCount() % 180; + int frame = _frameCount % 180; float fadingFactor = static_cast(sin((frame * M_PI) / 180)); _alpha = 0.5f + fadingFactor * 0.5f; diff --git a/src/rendering/renderablecrawlingline.cpp b/src/rendering/renderablecrawlingline.cpp index 125e9c4b5a..a4f8d1a2c9 100644 --- a/src/rendering/renderablecrawlingline.cpp +++ b/src/rendering/renderablecrawlingline.cpp @@ -123,8 +123,8 @@ void RenderableCrawlingLine::render(const RenderData& data) { _program->setUniform("ViewProjection", data.camera.viewProjectionMatrix()); _program->setUniform("ModelTransform", transform); - int frame = _frameCounter % 20; - float fadingFactor = static_cast(sin((frame * pi_c()) / 20)); + int frame = _frameCounter % 60; + float fadingFactor = static_cast(sin((frame * pi_c()) / 60)); float alpha = 0.6f + fadingFactor*0.4f; glLineWidth(2.f); @@ -165,7 +165,7 @@ void RenderableCrawlingLine::update(const UpdateData& data) { glm::dvec3 boresight; bool found = openspace::SpiceManager::ref().getFieldOfView(_source, shape, instrument, boresight, bounds); - glm::vec4 target(boresight[0], boresight[1], boresight[2], 14); + glm::vec4 target(boresight[0], boresight[1], boresight[2], 12); target = tmp * target; _positions[TargetPosition] = target; diff --git a/src/rendering/renderableplaneprojection.cpp b/src/rendering/renderableplaneprojection.cpp index 57c85fb131..c5a129b6f2 100644 --- a/src/rendering/renderableplaneprojection.cpp +++ b/src/rendering/renderableplaneprojection.cpp @@ -58,6 +58,7 @@ RenderablePlaneProjection::RenderablePlaneProjection(const ghoul::Dictionary& di , _name("ImagePlane") , _texturePath("") , _planeIsDirty(false) + , _previousTime(0) { dictionary.getValue(keySpacecraft, _spacecraft); @@ -148,12 +149,18 @@ void RenderablePlaneProjection::update(const UpdateData& data) { openspace::SpiceManager::ref().getPositionTransformMatrix(_target.frame, galacticFrame, time, _stateMatrix); + double timePast = 0.0; + if (img.path != "") + { + timePast = abs(img.startTime - _previousTime); + } + std::string tex = _texturePath; if (img.path != "" && (_moving || _planeIsDirty)) updatePlane(img, time); - else if (img.path != "" && img.path != tex) { - time = img.startTime; + else if (img.path != "" && timePast > DBL_EPSILON) { + _previousTime = time = img.startTime; updatePlane(img, time); } diff --git a/src/rendering/renderabletrail.cpp b/src/rendering/renderabletrail.cpp index 9e6233131c..4898c00a91 100644 --- a/src/rendering/renderabletrail.cpp +++ b/src/rendering/renderabletrail.cpp @@ -286,10 +286,10 @@ void RenderableTrail::fullYearSweep(double time) { bool validPosition = true; _vertexArray.resize(segments+2); for (int i = 0; i < segments+2; i++) { - if (start > time) - time = start; - else if (end < time) - time = end; + //if (start > time) + // time = start; + //else if (end < time) + // time = end; SpiceManager::ref().getTargetPosition(_target, _observer, _frame, "NONE", time, pscPos, lightTime); pscPos[3] += 3; diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 4f446ea7e6..206f48d848 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -560,7 +560,7 @@ namespace openspace { double t = 1.0 - remaining / openspace::ImageSequencer2::ref().getIntervalLength(); std::string progress = "|"; int g = static_cast((t* 24) + 1); - + g = std::max(g, 0); for (int i = 0; i < g; i++) progress.append("-"); progress.append(">"); From 7d513a5a1f65b3e388898ae51006a3b43a3b904a Mon Sep 17 00:00:00 2001 From: Anton Arbring Date: Wed, 13 May 2015 16:45:50 -0400 Subject: [PATCH 002/329] Remove/change keybinds --- scripts/bind_keys.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/bind_keys.lua b/scripts/bind_keys.lua index 9ae91e5c89..9f12c28a74 100644 --- a/scripts/bind_keys.lua +++ b/scripts/bind_keys.lua @@ -8,8 +8,7 @@ openspace.bindKey("F3", "openspace.setPerformanceMeasurement(false)") openspace.bindKey("F5", "openspace.changeCoordinateSystem('Sun'); openspace.printInfo('Changing Viewpoint to Sun-in-center');"); openspace.bindKey("F6", "openspace.changeCoordinateSystem('Jupiter'); openspace.printInfo('Changing Viewpoint to Jupiter-in-center');"); openspace.bindKey("F7", "openspace.changeCoordinateSystem('Pluto'); openspace.printInfo('Changing Viewpoint to Pluto-in-center');"); -openspace.bindKey("F11", "openspace.fadeOut(2)") -openspace.bindKey("F12", "openspace.fadeIn(2)") + openspace.bindKey("PRINT_SCREEN", "openspace.takeScreenshot()") openspace.bindKey("SPACE", "openspace.time.togglePause()") @@ -25,7 +24,7 @@ openspace.bindKey("7", "openspace.time.setDeltaTime(360)") openspace.bindKey("8", "openspace.time.setDeltaTime(540)") openspace.bindKey("9", "openspace.time.setDeltaTime(720)") -openspace.bindKey("=", "openspace.time.setTime('2015-07-14T09:00:00.00'); openspace.setPropertyValue('PlutoProjection.renderable.clearAllProjections', true); openspace.setPropertyValue('Charon.renderable.clearAllProjections', true);") +openspace.bindKey("F8", "openspace.time.setTime('2015-07-14T09:00:00.00'); openspace.setPropertyValue('PlutoProjection.renderable.clearAllProjections', true); openspace.setPropertyValue('Charon.renderable.clearAllProjections', true);") -- Quickfix backjumps in pluto sequence openspace.bindKey("F9", "openspace.time.setTime('2015-07-14T09:00:00.00'); openspace.setPropertyValue('PlutoProjection.renderable.clearAllProjections', true); openspace.setPropertyValue('Charon.renderable.clearAllProjections', true);") From 8f5ba101e4b8927169414abcdacbf4bc8183e930 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 14 May 2015 12:15:51 +0200 Subject: [PATCH 003/329] Remove stereo setting from single.xml Create TimelineView in a bundle on Mac --- config/sgct/single.xml | 1 - gui/timelineview/CMakeLists.txt | 13 ++++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/config/sgct/single.xml b/config/sgct/single.xml index 8086791404..fabd2d3177 100644 --- a/config/sgct/single.xml +++ b/config/sgct/single.xml @@ -5,7 +5,6 @@ - diff --git a/gui/timelineview/CMakeLists.txt b/gui/timelineview/CMakeLists.txt index f9687382ab..241d78d868 100644 --- a/gui/timelineview/CMakeLists.txt +++ b/gui/timelineview/CMakeLists.txt @@ -6,6 +6,17 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) find_package(Qt5Widgets) find_package(Qt5Network) +if (APPLE) +add_executable(TimelineView MACOSX_BUNDLE main.cpp mainwindow.cpp configurationwidget.cpp informationwidget.cpp controlwidget.cpp timelinewidget.cpp) +else (APPLE) add_executable(TimelineView main.cpp mainwindow.cpp configurationwidget.cpp informationwidget.cpp controlwidget.cpp timelinewidget.cpp) -#add_executable(TimelineView WIN32 main.cpp mainwindow.cpp configurationwidget.cpp informationwidget.cpp controlwidget.cpp timelinewidget.cpp) +endif () + target_link_libraries(TimelineView Qt5::Widgets Qt5::Network) + +if (APPLE) +INSTALL(CODE " + include(BundleUtilities) + fixup_bundle(\"/Users/alex/Development/OpenSpace/bin/openspace/Debug/TimelineView.app/Contents/MacOS/TimelineView\" \"/Users/alex/Development/OpenSpace/bin/openspace/Debug/TimelineView.app/Contents/plugins/platforms/libqcocoa.dylib\" \"\") + " COMPONENT Runtime) +endif () From 6a9fdef2e09bb9a2e05596d1c79e6b8dab761b3b Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 14 May 2015 12:50:17 +0200 Subject: [PATCH 004/329] Fixes for Mac rendering of stars and milkyway --- shaders/modules/sphere/sphere_fs.glsl | 6 ++--- shaders/modules/stars/star_fs.glsl | 3 +++ src/rendering/renderablesphere.cpp | 2 -- src/rendering/stars/renderablestars.cpp | 8 ++++--- src/scene/scenegraph.cpp | 30 ++++++++++++++++++++++--- 5 files changed, 38 insertions(+), 11 deletions(-) diff --git a/shaders/modules/sphere/sphere_fs.glsl b/shaders/modules/sphere/sphere_fs.glsl index 77f25101d3..eb3ba55162 100644 --- a/shaders/modules/sphere/sphere_fs.glsl +++ b/shaders/modules/sphere/sphere_fs.glsl @@ -39,8 +39,8 @@ void main() { vec4 position = vs_position; // This has to be fixed with the ScaleGraph in place (precision deficiency in depth buffer) ---abock - // float depth = pscDepth(position); - float depth = 1000.0; + //float depth = pscDepth(position); + float depth = 0.1; vec4 diffuse; vec2 texCoord = vs_st; @@ -71,7 +71,7 @@ void main() // diffuse = vec4(0.0); // #endif - // diffuse = vec4(1.0, 0.0, 0.0, 1.0); + //diffuse = vec4(1.0, 0.0, 0.0, 1.0); ABufferStruct_t frag = createGeometryFragment(diffuse, position, depth); addToBuffer(frag); diff --git a/shaders/modules/stars/star_fs.glsl b/shaders/modules/stars/star_fs.glsl index 2e522c28aa..162f70b0e2 100644 --- a/shaders/modules/stars/star_fs.glsl +++ b/shaders/modules/stars/star_fs.glsl @@ -91,4 +91,7 @@ void main() { ABufferStruct_t frag = createGeometryFragment(fullColor, position, depth); addToBuffer(frag); + + if (fullColor.a == 0) + discard; } diff --git a/src/rendering/renderablesphere.cpp b/src/rendering/renderablesphere.cpp index 93f65d0066..9cc8a97b1e 100644 --- a/src/rendering/renderablesphere.cpp +++ b/src/rendering/renderablesphere.cpp @@ -186,9 +186,7 @@ void RenderableSphere::update(const UpdateData& data) { } void RenderableSphere::loadTexture() { - LDEBUG("loadTexture"); if (_texturePath.value() != "") { - LDEBUG("loadTexture2"); ghoul::opengl::Texture* texture = ghoul::io::TextureReader::ref().loadTexture(absPath(_texturePath)); if (texture) { LDEBUG("Loaded texture from '" << absPath(_texturePath) << "'"); diff --git a/src/rendering/stars/renderablestars.cpp b/src/rendering/stars/renderablestars.cpp index bf8aa1886f..fc69275e71 100644 --- a/src/rendering/stars/renderablestars.cpp +++ b/src/rendering/stars/renderablestars.cpp @@ -174,8 +174,9 @@ bool RenderableStars::deinitialize() { } void RenderableStars::render(const RenderData& data) { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA); + //glEnable(GL_BLEND); + //glBlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA); + glDisable(GL_DEPTH_TEST); _program->activate(); @@ -221,7 +222,8 @@ void RenderableStars::render(const RenderData& data) { _program->setIgnoreUniformLocationError(false); _program->deactivate(); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_DEPTH_TEST); } void RenderableStars::update(const UpdateData& data) { diff --git a/src/scene/scenegraph.cpp b/src/scene/scenegraph.cpp index bc4a0bf59b..f4591efd64 100644 --- a/src/scene/scenegraph.cpp +++ b/src/scene/scenegraph.cpp @@ -44,8 +44,6 @@ namespace { namespace openspace { SceneGraph::SceneGraph() { - - } void SceneGraph::clear() { @@ -266,7 +264,7 @@ bool SceneGraph::loadFromFile(const std::string& sceneDescription) { LERROR("Topological sort failed"); return false; } - + return true; } @@ -316,6 +314,32 @@ bool SceneGraph::sortTopologially() { } +#ifdef __APPLE__ + auto it = std::find_if( + _topologicalSortedNodes.begin(), + _topologicalSortedNodes.end(), + [](SceneGraphNode* node) { + return node->name() == "Stars"; + } + ); + SceneGraphNode* n = *it; + _topologicalSortedNodes.erase(it); + _topologicalSortedNodes.insert(_topologicalSortedNodes.begin() + 3, n); + + it = std::find_if( + _topologicalSortedNodes.begin(), + _topologicalSortedNodes.end(), + [](SceneGraphNode* node) { + return node->name() == "MilkyWay"; + } + ); + n = *it; + _topologicalSortedNodes.erase(it); + _topologicalSortedNodes.insert(_topologicalSortedNodes.begin() + 2, n); +#endif + + + return true; } From fe9b3172c65ffcebf0118c771dfa3ef7be0f2ce0 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 17 May 2015 12:07:08 +0200 Subject: [PATCH 005/329] Committing the depth hack for renderablemodel Adding GL_STENCIL_BIT for Mac as it seems to help with an NSOpenGL error --- shaders/model_fs.glsl | 3 ++- src/main.cpp | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/shaders/model_fs.glsl b/shaders/model_fs.glsl index f9e799c2ec..2739e2dd33 100644 --- a/shaders/model_fs.glsl +++ b/shaders/model_fs.glsl @@ -52,6 +52,7 @@ void main() { vec4 position = vs_position; float depth = pscDepth(position); + depth = length(campos - position); vec4 diffuse = texture(texture1, vs_st); diffuse[3] = fading; @@ -89,5 +90,5 @@ void main() ABufferStruct_t frag = createGeometryFragment(diffuse, position, depth); addToBuffer(frag); } - + } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 0b7e94a4d9..9005b246f8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -79,6 +79,10 @@ int main(int argc, char** argv) { sgct::MessageHandler::instance()->setLogToCallback(true); sgct::MessageHandler::instance()->setLogCallback(mainLogCallback); +#ifdef __APPLE__ + glfwWindowHint(GLFW_STENCIL_BITS, 8); +#endif + LDEBUG("Creating SGCT Engine"); _sgctEngine = new sgct::Engine(newArgc, newArgv); From 217753bf86652ce4d6855307b17e40ce21b9026c Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 17 May 2015 13:59:30 +0200 Subject: [PATCH 006/329] Reverting last changes as they do not work on Windows --- shaders/model_fs.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shaders/model_fs.glsl b/shaders/model_fs.glsl index 2739e2dd33..bf3b904c97 100644 --- a/shaders/model_fs.glsl +++ b/shaders/model_fs.glsl @@ -52,7 +52,7 @@ void main() { vec4 position = vs_position; float depth = pscDepth(position); - depth = length(campos - position); + //depth = length(campos - position); vec4 diffuse = texture(texture1, vs_st); diffuse[3] = fading; From 25343d78c50cac136eb996db91dbfd24afd330d7 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 18 May 2015 16:51:42 +0200 Subject: [PATCH 007/329] Updated Ghoul version --- ext/ghoul | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/ghoul b/ext/ghoul index bf2ab81efb..0bf383e31f 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit bf2ab81efbb379a7a4fd92c6d87dd10ca530c575 +Subproject commit 0bf383e31fd5fd942be138958246041f83da4f25 From 66c06a411f1c27bbc1a3c2986afbefb22367b7c6 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 18 May 2015 17:49:44 +0200 Subject: [PATCH 008/329] Updated Lua version --- CMakeLists.txt | 2 +- ext/ghoul | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a02ab368c..be0d4644c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,7 +109,7 @@ set(DEPENDENT_LIBS ${DEPENDENT_LIBS} ${GLEW_LIBRARIES}) # Lua set(LUA_ROOT_DIR "${GHOUL_ROOT_DIR}/ext/lua") -include_directories("${LUA_ROOT_DIR}/include") +include_directories("${LUA_ROOT_DIR}/src") # Spice set(SPICE_ROOT_DIR "${OPENSPACE_EXT_DIR}/spice") diff --git a/ext/ghoul b/ext/ghoul index 0bf383e31f..c0dc8b8380 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 0bf383e31fd5fd942be138958246041f83da4f25 +Subproject commit c0dc8b83805c34f435770159772c23de583bb529 From 1ce07f205e6ea9bb3fe8576cf50f93b6041d2355 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 18 May 2015 17:57:53 +0200 Subject: [PATCH 009/329] Cleaning up RenderEngine --- src/rendering/renderengine.cpp | 1476 ++++++++++++++++---------------- 1 file changed, 731 insertions(+), 745 deletions(-) diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index c86e637293..b17eb71438 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -82,255 +82,248 @@ namespace { namespace openspace { - const std::string RenderEngine::PerformanceMeasurementSharedData = - "OpenSpacePerformanceMeasurementSharedData"; +const std::string RenderEngine::PerformanceMeasurementSharedData = + "OpenSpacePerformanceMeasurementSharedData"; - namespace luascriptfunctions { +namespace luascriptfunctions { - int changeCoordinateSystem(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); +int changeCoordinateSystem(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - std::string newCenter = std::string(lua_tostring(L, -1)); - OsEng.renderEngine()->changeViewPoint(newCenter); - return 1; - } + std::string newCenter = std::string(lua_tostring(L, -1)); + OsEng.renderEngine()->changeViewPoint(newCenter); + return 1; +} - /** - * \ingroup LuaScripts - * takeScreenshot(): - * Save the rendering to an image file - */ - int takeScreenshot(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 0) - return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); - OsEng.renderEngine()->takeScreenshot(); - return 0; - } +/** + * \ingroup LuaScripts + * takeScreenshot(): + * Save the rendering to an image file + */ +int takeScreenshot(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 0) + return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); + OsEng.renderEngine()->takeScreenshot(); + return 0; +} - /** - * \ingroup LuaScripts - * visualizeABuffer(bool): - * Toggle the visualization of the ABuffer - */ - int visualizeABuffer(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); +/** +* \ingroup LuaScripts +* visualizeABuffer(bool): +* Toggle the visualization of the ABuffer +*/ +int visualizeABuffer(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - const int type = lua_type(L, -1); - if (type != LUA_TBOOLEAN) - return luaL_error(L, "Expected argument of type 'bool'"); - bool b = lua_toboolean(L, -1) != 0; - OsEng.renderEngine()->toggleVisualizeABuffer(b); - return 0; - } + const int type = lua_type(L, -1); + if (type != LUA_TBOOLEAN) + return luaL_error(L, "Expected argument of type 'bool'"); + bool b = lua_toboolean(L, -1) != 0; + OsEng.renderEngine()->toggleVisualizeABuffer(b); + return 0; +} - /** - * \ingroup LuaScripts - * visualizeABuffer(bool): - * Toggle the visualization of the ABuffer - */ - int showRenderInformation(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); +/** +* \ingroup LuaScripts +* visualizeABuffer(bool): +* Toggle the visualization of the ABuffer +*/ +int showRenderInformation(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - const int type = lua_type(L, -1); - if (type != LUA_TBOOLEAN) - return luaL_error(L, "Expected argument of type 'bool'"); - bool b = lua_toboolean(L, -1) != 0; - OsEng.renderEngine()->toggleInfoText(b); - return 0; - } + const int type = lua_type(L, -1); + if (type != LUA_TBOOLEAN) + return luaL_error(L, "Expected argument of type 'bool'"); + bool b = lua_toboolean(L, -1) != 0; + OsEng.renderEngine()->toggleInfoText(b); + return 0; +} - /** - * \ingroup LuaScripts - * showSGCTRenderStatistics(bool): - * Set the rendering of the SGCTRenderStatistics - */ - int showSGCTRenderStatistics(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); +/** + * \ingroup LuaScripts + * showSGCTRenderStatistics(bool): + * Set the rendering of the SGCTRenderStatistics + */ +int showSGCTRenderStatistics(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - const int type = lua_type(L, -1); - if (type != LUA_TBOOLEAN) - return luaL_error(L, "Expected argument of type 'bool'"); - bool b = lua_toboolean(L, -1) != 0; - OsEng.renderEngine()->setSGCTRenderStatistics(b); - return 0; - } + const int type = lua_type(L, -1); + if (type != LUA_TBOOLEAN) + return luaL_error(L, "Expected argument of type 'bool'"); + bool b = lua_toboolean(L, -1) != 0; + OsEng.renderEngine()->setSGCTRenderStatistics(b); + return 0; +} - /** - * \ingroup LuaScripts - * visualizeABuffer(bool): - * Toggle the visualization of the ABuffer - */ - int setPerformanceMeasurement(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); +/** +* \ingroup LuaScripts +* visualizeABuffer(bool): +* Toggle the visualization of the ABuffer +*/ +int setPerformanceMeasurement(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - bool b = lua_toboolean(L, -1) != 0; - OsEng.renderEngine()->setPerformanceMeasurements(b); - return 0; - } + bool b = lua_toboolean(L, -1) != 0; + OsEng.renderEngine()->setPerformanceMeasurements(b); + return 0; +} - /** - * \ingroup LuaScripts - * fadeIn(float): - * start a global fadein over (float) seconds - */ - int fadeIn(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); +/** +* \ingroup LuaScripts +* fadeIn(float): +* start a global fadein over (float) seconds +*/ +int fadeIn(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - double t = luaL_checknumber(L, -1); + double t = luaL_checknumber(L, -1); - OsEng.renderEngine()->startFading(1, static_cast(t)); - return 0; - } - /** - * \ingroup LuaScripts - * fadeIn(float): - * start a global fadeout over (float) seconds - */ - int fadeOut(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + OsEng.renderEngine()->startFading(1, static_cast(t)); + return 0; +} +/** +* \ingroup LuaScripts +* fadeIn(float): +* start a global fadeout over (float) seconds +*/ +int fadeOut(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - double t = luaL_checknumber(L, -1); + double t = luaL_checknumber(L, -1); - OsEng.renderEngine()->startFading(-1, static_cast(t)); - return 0; - } + OsEng.renderEngine()->startFading(-1, static_cast(t)); + return 0; +} - } // namespace luascriptfunctions +} // namespace luascriptfunctions +RenderEngine::RenderEngine() + : _mainCamera(nullptr) + , _sceneGraph(nullptr) + , _abuffer(nullptr) + , _log(nullptr) + , _showInfo(true) + , _showScreenLog(true) + , _takeScreenshot(false) + , _doPerformanceMeasurements(false) + , _performanceMemory(nullptr) + , _globalBlackOutFactor(0.f) + , _fadeDuration(2.f) + , _currentFadeTime(0.f) + , _fadeDirection(0) + , _sgctRenderStatisticsVisible(false) + , _visualizeABuffer(false) + , _visualizer(nullptr) +{ + _onScreenInformation = { + glm::vec2(0.f), + 12, + -1 + }; +} - RenderEngine::RenderEngine() - : _mainCamera(nullptr) - , _sceneGraph(nullptr) - , _abuffer(nullptr) - , _log(nullptr) - , _showInfo(true) - , _showScreenLog(true) - , _takeScreenshot(false) - , _doPerformanceMeasurements(false) - , _performanceMemory(nullptr) - , _globalBlackOutFactor(0.f) - , _fadeDuration(2.f) - , _currentFadeTime(0.f) - , _fadeDirection(0) - , _sgctRenderStatisticsVisible(false) - , _visualizeABuffer(false) - , _visualizer(nullptr) - { - _onScreenInformation = { - glm::vec2(0.f), - 12, - -1 - }; - } +RenderEngine::~RenderEngine() { + delete _abuffer; + _abuffer = nullptr; - RenderEngine::~RenderEngine() - { - if (_abuffer) - delete _abuffer; - _abuffer = nullptr; + delete _sceneGraph; + _sceneGraph = nullptr; - if (_sceneGraph) - delete _sceneGraph; - _sceneGraph = nullptr; + delete _mainCamera; + delete _visualizer; - delete _mainCamera; - if (_visualizer) - delete _visualizer; + delete _performanceMemory; + if (ghoul::SharedMemory::exists(PerformanceMeasurementSharedData)) + ghoul::SharedMemory::remove(PerformanceMeasurementSharedData); +} - delete _performanceMemory; - if (ghoul::SharedMemory::exists(PerformanceMeasurementSharedData)) - ghoul::SharedMemory::remove(PerformanceMeasurementSharedData); - } +bool RenderEngine::initialize() { + generateGlslConfig(); - bool RenderEngine::initialize() - { - generateGlslConfig(); - - // init camera and set temporary position and scaling - _mainCamera = new Camera(); - _mainCamera->setScaling(glm::vec2(1.0, -8.0)); - _mainCamera->setPosition(psc(0.f, 0.f, 1.499823f, 11.f)); - OsEng.interactionHandler()->setCamera(_mainCamera); + // init camera and set temporary position and scaling + _mainCamera = new Camera(); + _mainCamera->setScaling(glm::vec2(1.0, -8.0)); + _mainCamera->setPosition(psc(0.f, 0.f, 1.499823f, 11.f)); + OsEng.interactionHandler()->setCamera(_mainCamera); #ifdef GHOUL_USE_DEVIL - ghoul::io::TextureReader::ref().addReader(new ghoul::io::impl::TextureReaderDevIL); + ghoul::io::TextureReader::ref().addReader(new ghoul::io::impl::TextureReaderDevIL); #endif // GHOUL_USE_DEVIL #ifdef GHOUL_USE_FREEIMAGE - ghoul::io::TextureReader::ref().addReader(new ghoul::io::impl::TextureReaderFreeImage); + ghoul::io::TextureReader::ref().addReader(new ghoul::io::impl::TextureReaderFreeImage); #endif // GHOUL_USE_FREEIMAGE - ghoul::io::TextureReader::ref().addReader(new ghoul::io::impl::TextureReaderCMAP); + ghoul::io::TextureReader::ref().addReader(new ghoul::io::impl::TextureReaderCMAP); #if ABUFFER_IMPLEMENTATION == ABUFFER_FRAMEBUFFER - _abuffer = new ABufferFramebuffer(); + _abuffer = new ABufferFramebuffer(); #elif ABUFFER_IMPLEMENTATION == ABUFFER_SINGLE_LINKED - _abuffer = new ABufferSingleLinked(); + _abuffer = new ABufferSingleLinked(); #elif ABUFFER_IMPLEMENTATION == ABUFFER_FIXED - _abuffer = new ABufferFixed(); + _abuffer = new ABufferFixed(); #elif ABUFFER_IMPLEMENTATION == ABUFFER_DYNAMIC - _abuffer = new ABufferDynamic(); + _abuffer = new ABufferDynamic(); #endif - return true; + return true; +} + +bool RenderEngine::initializeGL() { + // LDEBUG("RenderEngine::initializeGL()"); + sgct::SGCTWindow* wPtr = sgct::Engine::instance()->getActiveWindowPtr(); + + // TODO: Fix the power scaled coordinates in such a way that these + // values can be set to more realistic values + + // set the close clip plane and the far clip plane to extreme values while in + // development + sgct::Engine::instance()->setNearAndFarClippingPlanes(0.001f, 1000.0f); + // sgct::Engine::instance()->setNearAndFarClippingPlanes(0.1f, 30.0f); + + // calculating the maximum field of view for the camera, used to + // determine visibility of objects in the scene graph + if (wPtr->isUsingFisheyeRendering()) { + // fisheye mode, looking upwards to the "dome" + glm::vec4 upDirection(0, 1, 0, 0); + + // get the tilt and rotate the view + const float tilt = wPtr->getFisheyeTilt(); + glm::mat4 tiltMatrix + = glm::rotate(glm::mat4(1.0f), tilt, glm::vec3(1.0f, 0.0f, 0.0f)); + const glm::vec4 viewdir = tiltMatrix * upDirection; + + // set the tilted view and the FOV + _mainCamera->setCameraDirection(glm::vec3(viewdir[0], viewdir[1], viewdir[2])); + _mainCamera->setMaxFov(wPtr->getFisheyeFOV()); + _mainCamera->setLookUpVector(glm::vec3(0.0, 1.0, 0.0)); } - - bool RenderEngine::initializeGL() - { - // LDEBUG("RenderEngine::initializeGL()"); - sgct::SGCTWindow* wPtr = sgct::Engine::instance()->getActiveWindowPtr(); - - // TODO: Fix the power scaled coordinates in such a way that these - // values can be set to more realistic values - - // set the close clip plane and the far clip plane to extreme values while in - // development - sgct::Engine::instance()->setNearAndFarClippingPlanes(0.001f, 1000.0f); - // sgct::Engine::instance()->setNearAndFarClippingPlanes(0.1f, 30.0f); - - // calculating the maximum field of view for the camera, used to - // determine visibility of objects in the scene graph - if (wPtr->isUsingFisheyeRendering()) { - // fisheye mode, looking upwards to the "dome" - glm::vec4 upDirection(0, 1, 0, 0); - - // get the tilt and rotate the view - const float tilt = wPtr->getFisheyeTilt(); - glm::mat4 tiltMatrix - = glm::rotate(glm::mat4(1.0f), tilt, glm::vec3(1.0f, 0.0f, 0.0f)); - const glm::vec4 viewdir = tiltMatrix * upDirection; - - // set the tilted view and the FOV - _mainCamera->setCameraDirection(glm::vec3(viewdir[0], viewdir[1], viewdir[2])); - _mainCamera->setMaxFov(wPtr->getFisheyeFOV()); - _mainCamera->setLookUpVector(glm::vec3(0.0, 1.0, 0.0)); - } - else { - // get corner positions, calculating the forth to easily calculate center - glm::vec3 corners[4]; - corners[0] = wPtr->getCurrentViewport()->getViewPlaneCoords( - sgct_core::Viewport::LowerLeft); - corners[1] = wPtr->getCurrentViewport()->getViewPlaneCoords( - sgct_core::Viewport::UpperLeft); - corners[2] = wPtr->getCurrentViewport()->getViewPlaneCoords( - sgct_core::Viewport::UpperRight); - corners[3] = glm::vec3(corners[2][0], corners[0][1], corners[2][2]); - const glm::vec3 center = (corners[0] + corners[1] + corners[2] + corners[3]) - / 4.0f; + else { + // get corner positions, calculating the forth to easily calculate center + glm::vec3 corners[4]; + corners[0] = wPtr->getCurrentViewport()->getViewPlaneCoords( + sgct_core::Viewport::LowerLeft); + corners[1] = wPtr->getCurrentViewport()->getViewPlaneCoords( + sgct_core::Viewport::UpperLeft); + corners[2] = wPtr->getCurrentViewport()->getViewPlaneCoords( + sgct_core::Viewport::UpperRight); + corners[3] = glm::vec3(corners[2][0], corners[0][1], corners[2][2]); + const glm::vec3 center = (corners[0] + corners[1] + corners[2] + corners[3]) + / 4.0f; //#if 0 @@ -344,641 +337,634 @@ namespace openspace { // const glm::vec3 eyePosition // = sgct_core::ClusterManager::instance()->getUserPtr()->getPos(); //#endif - const glm::vec3 eyePosition = sgct_core::ClusterManager::instance()->getDefaultUserPtr()->getPos(); - // get viewdirection, stores the direction in the camera, used for culling - const glm::vec3 viewdir = glm::normalize(eyePosition - center); - _mainCamera->setCameraDirection(-viewdir); - _mainCamera->setLookUpVector(glm::vec3(0.0, 1.0, 0.0)); + const glm::vec3 eyePosition = sgct_core::ClusterManager::instance()->getDefaultUserPtr()->getPos(); + // get viewdirection, stores the direction in the camera, used for culling + const glm::vec3 viewdir = glm::normalize(eyePosition - center); + _mainCamera->setCameraDirection(-viewdir); + _mainCamera->setLookUpVector(glm::vec3(0.0, 1.0, 0.0)); - // set the initial fov to be 0.0 which means everything will be culled - float maxFov = 0.0f; + // set the initial fov to be 0.0 which means everything will be culled + float maxFov = 0.0f; - // for each corner - for (int i = 0; i < 4; ++i) { - // calculate radians to corner - glm::vec3 dir = glm::normalize(eyePosition - corners[i]); - float radsbetween = acos(glm::dot(viewdir, dir)) - / (glm::length(viewdir) * glm::length(dir)); + // for each corner + for (int i = 0; i < 4; ++i) { + // calculate radians to corner + glm::vec3 dir = glm::normalize(eyePosition - corners[i]); + float radsbetween = acos(glm::dot(viewdir, dir)) + / (glm::length(viewdir) * glm::length(dir)); - // the angle to a corner is larger than the current maxima - if (radsbetween > maxFov) { - maxFov = radsbetween; - } + // the angle to a corner is larger than the current maxima + if (radsbetween > maxFov) { + maxFov = radsbetween; } - _mainCamera->setMaxFov(maxFov); } - - _abuffer->initialize(); - - _log = new ScreenLog(); - ghoul::logging::LogManager::ref().addLog(_log); - - _visualizer = new ABufferVisualizer(); - - // successful init - return true; + _mainCamera->setMaxFov(maxFov); } - void RenderEngine::preSynchronization(){ - if (_mainCamera){ - _mainCamera->preSynchronization(); + _abuffer->initialize(); + + _log = new ScreenLog(); + ghoul::logging::LogManager::ref().addLog(_log); + + _visualizer = new ABufferVisualizer(); + + // successful init + return true; +} + +void RenderEngine::preSynchronization() { + if (_mainCamera) + _mainCamera->preSynchronization(); +} + +void RenderEngine::postSynchronizationPreDraw() { + sgct::Engine::instance()->setStatsGraphVisibility(_sgctRenderStatisticsVisible); + //temporary fade funtionality + if (_fadeDirection != 0) { + if (_currentFadeTime > _fadeDuration){ + _fadeDirection = 0; + _globalBlackOutFactor = fminf(1.f, fmaxf(0.f, _globalBlackOutFactor)); + } + else { + if (_fadeDirection < 0) + _globalBlackOutFactor = glm::smoothstep(1.f, 0.f, _currentFadeTime / _fadeDuration); + else + _globalBlackOutFactor = glm::smoothstep(0.f, 1.f, _currentFadeTime / _fadeDuration); + _currentFadeTime += static_cast(sgct::Engine::instance()->getAvgDt()); } } - void RenderEngine::postSynchronizationPreDraw() - { - sgct::Engine::instance()->setStatsGraphVisibility(_sgctRenderStatisticsVisible); - //temporary fade funtionality - if (_fadeDirection != 0){ - if (_currentFadeTime > _fadeDuration){ - _fadeDirection = 0; - _globalBlackOutFactor = fminf(1.f, fmaxf(0.f, _globalBlackOutFactor)); - } - else{ + if (_mainCamera) + _mainCamera->postSynchronizationPreDraw(); - if (_fadeDirection < 0){ - _globalBlackOutFactor = glm::smoothstep(1.f, 0.f, _currentFadeTime / _fadeDuration); - } - else{ - _globalBlackOutFactor = glm::smoothstep(0.f, 1.f, _currentFadeTime / _fadeDuration); - } - _currentFadeTime += static_cast(sgct::Engine::instance()->getAvgDt()); - } + sgct_core::SGCTNode* thisNode = sgct_core::ClusterManager::instance()->getThisNodePtr(); + bool updateAbuffer = false; + for (unsigned int i = 0; i < thisNode->getNumberOfWindows(); i++) { + if (sgct::Engine::instance()->getWindowPtr(i)->isWindowResized()) { + updateAbuffer = true; + break; } + } + if (updateAbuffer) { + generateGlslConfig(); + _abuffer->reinitialize(); + } - if (_mainCamera){ - _mainCamera->postSynchronizationPreDraw(); - } + // converts the quaternion used to rotation matrices + _mainCamera->compileViewRotationMatrix(); - sgct_core::SGCTNode * thisNode = sgct_core::ClusterManager::instance()->getThisNodePtr(); - bool updateAbuffer = false; - for (unsigned int i = 0; i < thisNode->getNumberOfWindows(); i++) { - if (sgct::Engine::instance()->getWindowPtr(i)->isWindowResized()) { - updateAbuffer = true; - break; - } - } - if (updateAbuffer) { - generateGlslConfig(); - _abuffer->reinitialize(); - } + // update and evaluate the scene starting from the root node + _sceneGraph->update({ + Time::ref().currentTime(), + Time::ref().timeJumped(), + Time::ref().deltaTime(), + _doPerformanceMeasurements + }); + _sceneGraph->evaluate(_mainCamera); - // converts the quaternion used to rotation matrices - _mainCamera->compileViewRotationMatrix(); + // clear the abuffer before rendering the scene + _abuffer->clear(); - // update and evaluate the scene starting from the root node - _sceneGraph->update({ - Time::ref().currentTime(), - Time::ref().timeJumped(), - Time::ref().deltaTime(), - _doPerformanceMeasurements - }); - _sceneGraph->evaluate(_mainCamera); + //Allow focus node to update camera (enables camera-following) + //FIX LATER: THIS CAUSES MASTER NODE TO BE ONE FRAME AHEAD OF SLAVES + //if (const SceneGraphNode* node = OsEng.ref().interactionHandler().focusNode()){ + //node->updateCamera(_mainCamera); + //} - // clear the abuffer before rendering the scene +} + +void RenderEngine::render(const glm::mat4 &projectionMatrix, const glm::mat4 &viewMatrix) { + // We need the window pointer + sgct::SGCTWindow* w = sgct::Engine::instance()->getActiveWindowPtr(); + if (w->isUsingFisheyeRendering()) _abuffer->clear(); - //Allow focus node to update camera (enables camera-following) - //FIX LATER: THIS CAUSES MASTER NODE TO BE ONE FRAME AHEAD OF SLAVES - //if (const SceneGraphNode* node = OsEng.ref().interactionHandler().focusNode()){ - //node->updateCamera(_mainCamera); - //} - - } - - void RenderEngine::render(const glm::mat4 &projectionMatrix, const glm::mat4 &viewMatrix) - { - // We need the window pointer - sgct::SGCTWindow* w = sgct::Engine::instance()->getActiveWindowPtr(); - if (w->isUsingFisheyeRendering()) - _abuffer->clear(); - - // SGCT resets certain settings + // SGCT resets certain settings #ifndef __APPLE__ - glDisable(GL_DEPTH_TEST); - glDisable(GL_CULL_FACE); - glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_BLEND); #else - glEnable(GL_DEPTH_TEST); + glEnable(GL_DEPTH_TEST); // glDisable(GL_CULL_FACE); - glEnable(GL_CULL_FACE); + glEnable(GL_CULL_FACE); // glDisable(GL_BLEND); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +#endif + // setup the camera for the current frame + + _mainCamera->setViewMatrix( + viewMatrix ); + _mainCamera->setProjectionMatrix( + projectionMatrix); + //Is this really necessary to store? @JK + _mainCamera->setViewProjectionMatrix(projectionMatrix * viewMatrix); + + // We only want to skip the rendering if we are the master and we want to + // disable the rendering for the master + if (!(OsEng.isMaster() && _disableMasterRendering)) { + if (!_visualizeABuffer) { + _abuffer->preRender(); + _sceneGraph->render({ + *_mainCamera, + psc(), + _doPerformanceMeasurements + }); + _abuffer->postRender(); + glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -#endif - // setup the camera for the current frame - - _mainCamera->setViewMatrix( - viewMatrix ); - _mainCamera->setProjectionMatrix( - projectionMatrix); - //Is this really necessary to store? @JK - _mainCamera->setViewProjectionMatrix(projectionMatrix * viewMatrix); - - // We only want to skip the rendering if we are the master and we want to - // disable the rendering for the master - if (!(OsEng.isMaster() && _disableMasterRendering)) { - if (!_visualizeABuffer) { - _abuffer->preRender(); - _sceneGraph->render({ - *_mainCamera, - psc(), - _doPerformanceMeasurements - }); - _abuffer->postRender(); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - _abuffer->resolve(); - glDisable(GL_BLEND); - } - else { - _visualizer->render(); - } - } + _abuffer->resolve(); + glDisable(GL_BLEND); + } + else { + _visualizer->render(); + } + } #if 1 #define PrintText(__i__, __format__, ...) Freetype::print(font, 10.f, static_cast(startY - font_size_mono * __i__ * 2), __format__, __VA_ARGS__); #define PrintColorTextArg(__i__, __format__, __size__, __color__, ...) Freetype::print(font, __size__, static_cast(startY - font_size_mono * __i__ * 2), __color__, __format__, __VA_ARGS__); #define PrintColorText(__i__, __format__, __size__, __color__) Freetype::print(font, __size__, static_cast(startY - font_size_mono * __i__ * 2), __color__, __format__); - if (_onScreenInformation._node != -1) { - int thisId = sgct_core::ClusterManager::instance()->getThisNodeId(); + if (_onScreenInformation._node != -1) { + int thisId = sgct_core::ClusterManager::instance()->getThisNodeId(); - if (thisId == _onScreenInformation._node) { - const unsigned int font_size_mono = _onScreenInformation._size; - int x1, xSize, y1, ySize; - const sgct_text::Font* font = sgct_text::FontManager::instance()->getFont(constants::fonts::keyMono, font_size_mono); - sgct::Engine::instance()->getActiveWindowPtr()->getCurrentViewportPixelCoords(x1, y1, xSize, ySize); - int startY = ySize - 2 * font_size_mono; + if (thisId == _onScreenInformation._node) { + const unsigned int font_size_mono = _onScreenInformation._size; + int x1, xSize, y1, ySize; + const sgct_text::Font* font = sgct_text::FontManager::instance()->getFont(constants::fonts::keyMono, font_size_mono); + sgct::Engine::instance()->getActiveWindowPtr()->getCurrentViewportPixelCoords(x1, y1, xSize, ySize); + int startY = ySize - 2 * font_size_mono; - } - } + } + } - // Print some useful information on the master viewport - if (OsEng.ref().isMaster() && !w->isUsingFisheyeRendering()) { + // Print some useful information on the master viewport + if (OsEng.ref().isMaster() && !w->isUsingFisheyeRendering()) { - // TODO: Adjust font_size properly when using retina screen - const int font_size_mono = 10; - const int font_size_light = 8; - const int font_with_light = static_cast(font_size_light*0.7); - const sgct_text::Font* fontLight = sgct_text::FontManager::instance()->getFont(constants::fonts::keyLight, font_size_light); - const sgct_text::Font* fontMono = sgct_text::FontManager::instance()->getFont(constants::fonts::keyMono, font_size_mono); + // TODO: Adjust font_size properly when using retina screen + const int font_size_mono = 10; + const int font_size_light = 8; + const int font_with_light = static_cast(font_size_light*0.7); + const sgct_text::Font* fontLight = sgct_text::FontManager::instance()->getFont(constants::fonts::keyLight, font_size_light); + const sgct_text::Font* fontMono = sgct_text::FontManager::instance()->getFont(constants::fonts::keyMono, font_size_mono); - if (_showInfo) { - const sgct_text::Font* font = fontMono; - int x1, xSize, y1, ySize; - sgct::Engine::instance()->getActiveWindowPtr()->getCurrentViewportPixelCoords(x1, y1, xSize, ySize); - int startY = ySize - 2 * font_size_mono; - const glm::vec2& scaling = _mainCamera->scaling(); - const glm::vec3& viewdirection = _mainCamera->viewDirection(); - const psc& position = _mainCamera->position(); - const psc& origin = OsEng.interactionHandler()->focusNode()->worldPosition(); - const PowerScaledScalar& pssl = (position - origin).length(); + if (_showInfo) { + const sgct_text::Font* font = fontMono; + int x1, xSize, y1, ySize; + sgct::Engine::instance()->getActiveWindowPtr()->getCurrentViewportPixelCoords(x1, y1, xSize, ySize); + int startY = ySize - 2 * font_size_mono; + const glm::vec2& scaling = _mainCamera->scaling(); + const glm::vec3& viewdirection = _mainCamera->viewDirection(); + const psc& position = _mainCamera->position(); + const psc& origin = OsEng.interactionHandler()->focusNode()->worldPosition(); + const PowerScaledScalar& pssl = (position - origin).length(); - // Next 2 lines neccesary for instrument switching to work. - double currentTime = Time::ref().currentTime(); - // GUI PRINT - // Using a macro to shorten line length and increase readability + // Next 2 lines neccesary for instrument switching to work. + double currentTime = Time::ref().currentTime(); + // GUI PRINT + // Using a macro to shorten line length and increase readability - int line = 0; + int line = 0; - //PrintText(line++, "Date: %s", Time::ref().currentTimeUTC().c_str()); - PrintColorTextArg(line++, "Date: %s", 10, glm::vec4(1), Time::ref().currentTimeUTC().c_str()); - glm::vec4 targetColor(0.00, 0.75, 1.00, 1); - double dt = Time::ref().deltaTime(); - PrintColorTextArg(line++, "Simulation increment (s): %.0f", 10, glm::vec4(1), dt); - PrintText(line++, "Avg. Frametime: %.5f", sgct::Engine::instance()->getAvgDt()); + //PrintText(line++, "Date: %s", Time::ref().currentTimeUTC().c_str()); + PrintColorTextArg(line++, "Date: %s", 10, glm::vec4(1), Time::ref().currentTimeUTC().c_str()); + glm::vec4 targetColor(0.00, 0.75, 1.00, 1); + double dt = Time::ref().deltaTime(); + PrintColorTextArg(line++, "Simulation increment (s): %.0f", 10, glm::vec4(1), dt); + PrintText(line++, "Avg. Frametime: %.5f", sgct::Engine::instance()->getAvgDt()); - //PrintText(line++, "Drawtime: %.5f", sgct::Engine::instance()->getDrawTime()); - //PrintText(line++, "Frametime: %.5f", sgct::Engine::instance()->getDt()); - //PrintText(i++, "Origin: (% .5f, % .5f, % .5f, % .5f)", origin[0], origin[1], origin[2], origin[3]); - //PrintText(i++, "Cam pos: (% .5f, % .5f, % .5f, % .5f)", position[0], position[1], position[2], position[3]); - //PrintText(i++, "View dir: (% .5f, % .5f, % .5f)", viewdirection[0], viewdirection[1], viewdirection[2]); - //PrintText(i++, "Cam->origin: (% .15f, % .4f)", pssl[0], pssl[1]); - //PrintText(i++, "Scaling: (% .5f, % .5f)", scaling[0], scaling[1]); + //PrintText(line++, "Drawtime: %.5f", sgct::Engine::instance()->getDrawTime()); + //PrintText(line++, "Frametime: %.5f", sgct::Engine::instance()->getDt()); + //PrintText(i++, "Origin: (% .5f, % .5f, % .5f, % .5f)", origin[0], origin[1], origin[2], origin[3]); + //PrintText(i++, "Cam pos: (% .5f, % .5f, % .5f, % .5f)", position[0], position[1], position[2], position[3]); + //PrintText(i++, "View dir: (% .5f, % .5f, % .5f)", viewdirection[0], viewdirection[1], viewdirection[2]); + //PrintText(i++, "Cam->origin: (% .15f, % .4f)", pssl[0], pssl[1]); + //PrintText(i++, "Scaling: (% .5f, % .5f)", scaling[0], scaling[1]); - if (openspace::ImageSequencer2::ref().isReady()) { - double remaining = openspace::ImageSequencer2::ref().getNextCaptureTime() - currentTime; - double t = 1.0 - remaining / openspace::ImageSequencer2::ref().getIntervalLength(); - std::string progress = "|"; - int g = static_cast((t* 24) + 1); + if (openspace::ImageSequencer2::ref().isReady()) { + double remaining = openspace::ImageSequencer2::ref().getNextCaptureTime() - currentTime; + double t = 1.0 - remaining / openspace::ImageSequencer2::ref().getIntervalLength(); + std::string progress = "|"; + int g = static_cast((t* 24) + 1); - for (int i = 0; i < g; i++) - progress.append("-"); - progress.append(">"); - for (int i = 0; i < 25 - g; i++) - progress.append(" "); + for (int i = 0; i < g; i++) + progress.append("-"); + progress.append(">"); + for (int i = 0; i < 25 - g; i++) + progress.append(" "); - std::string str = ""; - openspace::SpiceManager::ref().getDateFromET(openspace::ImageSequencer2::ref().getNextCaptureTime(), str, "YYYY MON DD HR:MN:SC"); + std::string str = ""; + openspace::SpiceManager::ref().getDateFromET(openspace::ImageSequencer2::ref().getNextCaptureTime(), str, "YYYY MON DD HR:MN:SC"); - glm::vec4 active(0.6, 1, 0.00, 1); - glm::vec4 brigther_active(0.9, 1, 0.75, 1); + glm::vec4 active(0.6, 1, 0.00, 1); + glm::vec4 brigther_active(0.9, 1, 0.75, 1); - progress.append("|"); - if (remaining > 0){ - brigther_active *= (1 - t); - PrintColorText(line++, "Next instrument activity:", 10, active*t + brigther_active); - PrintColorTextArg(line++, "%.0f s %s %.1f %%", 10, active*t + brigther_active, remaining, progress.c_str(), t * 100); - PrintColorTextArg(line++, "Data acquisition time: %s", 10, active, str.c_str()); + progress.append("|"); + if (remaining > 0){ + brigther_active *= (1 - t); + PrintColorText(line++, "Next instrument activity:", 10, active*t + brigther_active); + PrintColorTextArg(line++, "%.0f s %s %.1f %%", 10, active*t + brigther_active, remaining, progress.c_str(), t * 100); + PrintColorTextArg(line++, "Data acquisition time: %s", 10, active, str.c_str()); - } - std::pair nextTarget = ImageSequencer2::ref().getNextTarget(); - std::pair currentTarget = ImageSequencer2::ref().getCurrentTarget(); + } + std::pair nextTarget = ImageSequencer2::ref().getNextTarget(); + std::pair currentTarget = ImageSequencer2::ref().getCurrentTarget(); - if (currentTarget.first > 0.0) { - int timeleft = static_cast(nextTarget.first - currentTime); + if (currentTarget.first > 0.0) { + int timeleft = static_cast(nextTarget.first - currentTime); - int hour = timeleft / 3600; - int second = timeleft % 3600; - int minute = second / 60; - second = second % 60; + int hour = timeleft / 3600; + int second = timeleft % 3600; + int minute = second / 60; + second = second % 60; - std::string hh, mm, ss, coundtown; + std::string hh, mm, ss, coundtown; - if (hour < 10) hh.append("0"); - if (minute < 10) mm.append("0"); - if (second < 10) ss.append("0"); + if (hour < 10) hh.append("0"); + if (minute < 10) mm.append("0"); + if (second < 10) ss.append("0"); - hh.append(std::to_string(hour)); - mm.append(std::to_string(minute)); - ss.append(std::to_string(second)); + hh.append(std::to_string(hour)); + mm.append(std::to_string(minute)); + ss.append(std::to_string(second)); - PrintColorTextArg(line++, "Data acquisition adjacency: [%s:%s:%s]", 10, targetColor, hh.c_str(), mm.c_str(), ss.c_str()); + PrintColorTextArg(line++, "Data acquisition adjacency: [%s:%s:%s]", 10, targetColor, hh.c_str(), mm.c_str(), ss.c_str()); - std::pair> incidentTargets = ImageSequencer2::ref().getIncidentTargetList(2); - std::string space; - glm::vec4 color; - size_t isize = incidentTargets.second.size(); - for (size_t p = 0; p < isize; p++){ - double t = static_cast(p + 1) / static_cast(isize + 1); - t = (p > isize / 2) ? 1 - t : t; - t += 0.3; - color = (p == isize / 2) ? targetColor : glm::vec4(t, t, t, 1); - PrintColorTextArg(line, "%s%s", 10, color, space.c_str(), incidentTargets.second[p].c_str()); - for (int k = 0; k < incidentTargets.second[p].size() + 2; k++) - space += " "; - } - line++; - - std::map activeMap = ImageSequencer2::ref().getActiveInstruments(); - glm::vec4 firing(0.58-t, 1-t, 1-t, 1); - glm::vec4 notFiring(0.5, 0.5, 0.5, 1); - PrintColorText(line++, "Active Instruments: ", 10, active); - for (auto t : activeMap){ - if (t.second == false){ - PrintColorText(line, "| |", 10, glm::vec4(0.3, 0.3, 0.3, 1)); - PrintColorTextArg(line++, " %5s", 10, glm::vec4(0.3, 0.3, 0.3, 1), t.first.c_str()); - } - else{ - PrintColorText(line, "|", 10, glm::vec4(0.3, 0.3, 0.3, 1)); - if (t.first == "NH_LORRI"){ - PrintColorText(line, " + ", 10, firing); - } - PrintColorText(line, " |", 10, glm::vec4(0.3, 0.3, 0.3, 1)); - PrintColorTextArg(line++, " %5s", 10, active, t.first.c_str()); - } - } - } + std::pair> incidentTargets = ImageSequencer2::ref().getIncidentTargetList(2); + std::string space; + glm::vec4 color; + size_t isize = incidentTargets.second.size(); + for (size_t p = 0; p < isize; p++){ + double t = static_cast(p + 1) / static_cast(isize + 1); + t = (p > isize / 2) ? 1 - t : t; + t += 0.3; + color = (p == isize / 2) ? targetColor : glm::vec4(t, t, t, 1); + PrintColorTextArg(line, "%s%s", 10, color, space.c_str(), incidentTargets.second[p].c_str()); + for (int k = 0; k < incidentTargets.second[p].size() + 2; k++) + space += " "; } + line++; + + std::map activeMap = ImageSequencer2::ref().getActiveInstruments(); + glm::vec4 firing(0.58-t, 1-t, 1-t, 1); + glm::vec4 notFiring(0.5, 0.5, 0.5, 1); + PrintColorText(line++, "Active Instruments: ", 10, active); + for (auto t : activeMap){ + if (t.second == false){ + PrintColorText(line, "| |", 10, glm::vec4(0.3, 0.3, 0.3, 1)); + PrintColorTextArg(line++, " %5s", 10, glm::vec4(0.3, 0.3, 0.3, 1), t.first.c_str()); + } + else{ + PrintColorText(line, "|", 10, glm::vec4(0.3, 0.3, 0.3, 1)); + if (t.first == "NH_LORRI"){ + PrintColorText(line, " + ", 10, firing); + } + PrintColorText(line, " |", 10, glm::vec4(0.3, 0.3, 0.3, 1)); + PrintColorTextArg(line++, " %5s", 10, active, t.first.c_str()); + } + } + } + } #undef PrintText + } + + if (_showScreenLog) + { + const sgct_text::Font* font = fontLight; + const int max = 10; + const int category_length = 20; + const int msg_length = 140; + const float ttl = 15.f; + const float fade = 5.f; + auto entries = _log->last(max); + + const glm::vec4 white(0.9, 0.9, 0.9, 1); + const glm::vec4 red(1, 0, 0, 1); + const glm::vec4 yellow(1, 1, 0, 1); + const glm::vec4 green(0, 1, 0, 1); + const glm::vec4 blue(0, 0, 1, 1); + + size_t nr = 1; + for (auto& it = entries.first; it != entries.second; ++it) { + const ScreenLog::LogEntry* e = &(*it); + + const double t = sgct::Engine::instance()->getTime(); + float diff = static_cast(t - e->timeStamp); + + // Since all log entries are ordered, once one is exceeding TTL, all have + if (diff > ttl) + break; + + float alpha = 1; + float ttf = ttl - fade; + if (diff > ttf) { + diff = diff - ttf; + float p = 0.8f - diff / fade; + alpha = (p <= 0.f) ? 0.f : pow(p, 0.3f); } - if (_showScreenLog) - { - const sgct_text::Font* font = fontLight; - const int max = 10; - const int category_length = 20; - const int msg_length = 140; - const float ttl = 15.f; - const float fade = 5.f; - auto entries = _log->last(max); + // Since all log entries are ordered, once one exceeds alpha, all have + if (alpha <= 0.0) + break; - const glm::vec4 white(0.9, 0.9, 0.9, 1); - const glm::vec4 red(1, 0, 0, 1); - const glm::vec4 yellow(1, 1, 0, 1); - const glm::vec4 green(0, 1, 0, 1); - const glm::vec4 blue(0, 0, 1, 1); + const std::string lvl = "(" + ghoul::logging::LogManager::stringFromLevel(e->level) + ")"; + const std::string& message = e->message.substr(0, msg_length); + nr += std::count(message.begin(), message.end(), '\n'); - size_t nr = 1; - for (auto& it = entries.first; it != entries.second; ++it) { - const ScreenLog::LogEntry* e = &(*it); + Freetype::print(font, 10.f, static_cast(font_size_light * nr * 2), white*alpha, + "%-14s %s%s", // Format + e->timeString.c_str(), // Time string + e->category.substr(0, category_length).c_str(), // Category string (up to category_length) + e->category.length() > 20 ? "..." : ""); // Pad category with "..." if exceeds category_length - const double t = sgct::Engine::instance()->getTime(); - float diff = static_cast(t - e->timeStamp); + glm::vec4 color = white; + if (e->level == ghoul::logging::LogManager::LogLevel::Debug) + color = green; + if (e->level == ghoul::logging::LogManager::LogLevel::Warning) + color = yellow; + if (e->level == ghoul::logging::LogManager::LogLevel::Error) + color = red; + if (e->level == ghoul::logging::LogManager::LogLevel::Fatal) + color = blue; - // Since all log entries are ordered, once one is exceeding TTL, all have - if (diff > ttl) - break; - - float alpha = 1; - float ttf = ttl - fade; - if (diff > ttf) { - diff = diff - ttf; - float p = 0.8f - diff / fade; - alpha = (p <= 0.f) ? 0.f : pow(p, 0.3f); - } - - // Since all log entries are ordered, once one exceeds alpha, all have - if (alpha <= 0.0) - break; - - const std::string lvl = "(" + ghoul::logging::LogManager::stringFromLevel(e->level) + ")"; - const std::string& message = e->message.substr(0, msg_length); - nr += std::count(message.begin(), message.end(), '\n'); - - Freetype::print(font, 10.f, static_cast(font_size_light * nr * 2), white*alpha, - "%-14s %s%s", // Format - e->timeString.c_str(), // Time string - e->category.substr(0, category_length).c_str(), // Category string (up to category_length) - e->category.length() > 20 ? "..." : ""); // Pad category with "..." if exceeds category_length - - glm::vec4 color = white; - if (e->level == ghoul::logging::LogManager::LogLevel::Debug) - color = green; - if (e->level == ghoul::logging::LogManager::LogLevel::Warning) - color = yellow; - if (e->level == ghoul::logging::LogManager::LogLevel::Error) - color = red; - if (e->level == ghoul::logging::LogManager::LogLevel::Fatal) - color = blue; - - Freetype::print(font, static_cast(10 + 39 * font_with_light), static_cast(font_size_light * nr * 2), color*alpha, "%s", lvl.c_str()); + Freetype::print(font, static_cast(10 + 39 * font_with_light), static_cast(font_size_light * nr * 2), color*alpha, "%s", lvl.c_str()); - Freetype::print(font, static_cast(10 + 53 * font_with_light), static_cast(font_size_light * nr * 2), white*alpha, "%s", message.c_str()); - ++nr; - } - } + Freetype::print(font, static_cast(10 + 53 * font_with_light), static_cast(font_size_light * nr * 2), white*alpha, "%s", message.c_str()); + ++nr; } + } + } #endif - } +} - void RenderEngine::postDraw() { - if (Time::ref().timeJumped()) - Time::ref().setTimeJumped(false); - if (_takeScreenshot) { - sgct::Engine::instance()->takeScreenshot(); - _takeScreenshot = false; - } +void RenderEngine::postDraw() { + if (Time::ref().timeJumped()) + Time::ref().setTimeJumped(false); + if (_takeScreenshot) { + sgct::Engine::instance()->takeScreenshot(); + _takeScreenshot = false; + } - if (_doPerformanceMeasurements) - storePerformanceMeasurements(); - } + if (_doPerformanceMeasurements) + storePerformanceMeasurements(); +} - void RenderEngine::takeScreenshot() { - _takeScreenshot = true; - } +void RenderEngine::takeScreenshot() { + _takeScreenshot = true; +} - void RenderEngine::toggleVisualizeABuffer(bool b) { - _visualizeABuffer = b; - if (!_visualizeABuffer) - return; +void RenderEngine::toggleVisualizeABuffer(bool b) { + _visualizeABuffer = b; + if (!_visualizeABuffer) + return; - std::vector _d = _abuffer->pixelData(); - _visualizer->updateData(_d); - } + std::vector _d = _abuffer->pixelData(); + _visualizer->updateData(_d); +} - void RenderEngine::toggleInfoText(bool b) { - _showInfo = b; - } +void RenderEngine::toggleInfoText(bool b) { + _showInfo = b; +} - Scene* RenderEngine::scene() - { - // TODO custom assert (ticket #5) - assert(_sceneGraph); - return _sceneGraph; - } +Scene* RenderEngine::scene() +{ + // TODO custom assert (ticket #5) + assert(_sceneGraph); + return _sceneGraph; +} - void RenderEngine::setSceneGraph(Scene* sceneGraph) - { - _sceneGraph = sceneGraph; - } +void RenderEngine::setSceneGraph(Scene* sceneGraph) +{ + _sceneGraph = sceneGraph; +} - void RenderEngine::serialize(SyncBuffer* syncBuffer) { - if (_mainCamera){ - _mainCamera->serialize(syncBuffer); - } +void RenderEngine::serialize(SyncBuffer* syncBuffer) { + if (_mainCamera){ + _mainCamera->serialize(syncBuffer); + } - syncBuffer->encode(_onScreenInformation._node); - syncBuffer->encode(_onScreenInformation._position.x); - syncBuffer->encode(_onScreenInformation._position.y); - syncBuffer->encode(_onScreenInformation._size); - } + syncBuffer->encode(_onScreenInformation._node); + syncBuffer->encode(_onScreenInformation._position.x); + syncBuffer->encode(_onScreenInformation._position.y); + syncBuffer->encode(_onScreenInformation._size); +} - void RenderEngine::deserialize(SyncBuffer* syncBuffer) { - if (_mainCamera){ - _mainCamera->deserialize(syncBuffer); - } - syncBuffer->decode(_onScreenInformation._node); - syncBuffer->decode(_onScreenInformation._position.x); - syncBuffer->decode(_onScreenInformation._position.y); - syncBuffer->decode(_onScreenInformation._size); +void RenderEngine::deserialize(SyncBuffer* syncBuffer) { + if (_mainCamera){ + _mainCamera->deserialize(syncBuffer); + } + syncBuffer->decode(_onScreenInformation._node); + syncBuffer->decode(_onScreenInformation._position.x); + syncBuffer->decode(_onScreenInformation._position.y); + syncBuffer->decode(_onScreenInformation._size); - } +} - Camera* RenderEngine::camera() const { - return _mainCamera; - } +Camera* RenderEngine::camera() const { + return _mainCamera; +} - ABuffer* RenderEngine::abuffer() const { - return _abuffer; - } +ABuffer* RenderEngine::abuffer() const { + return _abuffer; +} - float RenderEngine::globalBlackOutFactor(){ - return _globalBlackOutFactor; - } +float RenderEngine::globalBlackOutFactor(){ + return _globalBlackOutFactor; +} - void RenderEngine::setGlobalBlackOutFactor(float opacity){ - _globalBlackOutFactor = opacity; - } +void RenderEngine::setGlobalBlackOutFactor(float opacity){ + _globalBlackOutFactor = opacity; +} - void RenderEngine::startFading(int direction, float fadeDuration){ - _fadeDirection = direction; - _fadeDuration = fadeDuration; - _currentFadeTime = 0.f; - } +void RenderEngine::startFading(int direction, float fadeDuration){ + _fadeDirection = direction; + _fadeDuration = fadeDuration; + _currentFadeTime = 0.f; +} - void RenderEngine::generateGlslConfig() { - LDEBUG("Generating GLSLS config, expect shader recompilation"); - int xSize = sgct::Engine::instance()->getActiveWindowPtr()->getXFramebufferResolution();; - int ySize = sgct::Engine::instance()->getActiveWindowPtr()->getYFramebufferResolution();; +void RenderEngine::generateGlslConfig() { + LDEBUG("Generating GLSLS config, expect shader recompilation"); + int xSize = sgct::Engine::instance()->getActiveWindowPtr()->getXFramebufferResolution();; + int ySize = sgct::Engine::instance()->getActiveWindowPtr()->getYFramebufferResolution();; - // TODO: Make this file creation dynamic and better in every way - // TODO: If the screen size changes it is enough if this file is regenerated to - // recompile all necessary files - std::ofstream os(absPath("${SHADERS_GENERATED}/constants.hglsl")); - os << "#ifndef CONSTANTS_HGLSL\n" - << "#define CONSTANTS_HGLSL\n" - << "#define SCREEN_WIDTH " << xSize << "\n" - << "#define SCREEN_HEIGHT " << ySize << "\n" - << "#define MAX_LAYERS " << ABuffer::MAX_LAYERS << "\n" - << "#define ABUFFER_FRAMEBUFFER " << ABUFFER_FRAMEBUFFER << "\n" - << "#define ABUFFER_SINGLE_LINKED " << ABUFFER_SINGLE_LINKED << "\n" - << "#define ABUFFER_FIXED " << ABUFFER_FIXED << "\n" - << "#define ABUFFER_DYNAMIC " << ABUFFER_DYNAMIC << "\n" - << "#define ABUFFER_IMPLEMENTATION " << ABUFFER_IMPLEMENTATION << "\n"; - // System specific + // TODO: Make this file creation dynamic and better in every way + // TODO: If the screen size changes it is enough if this file is regenerated to + // recompile all necessary files + std::ofstream os(absPath("${SHADERS_GENERATED}/constants.hglsl")); + os << "#ifndef CONSTANTS_HGLSL\n" + << "#define CONSTANTS_HGLSL\n" + << "#define SCREEN_WIDTH " << xSize << "\n" + << "#define SCREEN_HEIGHT " << ySize << "\n" + << "#define MAX_LAYERS " << ABuffer::MAX_LAYERS << "\n" + << "#define ABUFFER_FRAMEBUFFER " << ABUFFER_FRAMEBUFFER << "\n" + << "#define ABUFFER_SINGLE_LINKED " << ABUFFER_SINGLE_LINKED << "\n" + << "#define ABUFFER_FIXED " << ABUFFER_FIXED << "\n" + << "#define ABUFFER_DYNAMIC " << ABUFFER_DYNAMIC << "\n" + << "#define ABUFFER_IMPLEMENTATION " << ABUFFER_IMPLEMENTATION << "\n"; + // System specific #ifdef WIN32 - os << "#define WIN32\n"; + os << "#define WIN32\n"; #endif #ifdef __APPLE__ - os << "#define APPLE\n"; + os << "#define APPLE\n"; #endif #ifdef __linux__ - os << "#define linux\n"; + os << "#define linux\n"; #endif - os << "#endif\n"; + os << "#endif\n"; - os.close(); - } + os.close(); +} - scripting::ScriptEngine::LuaLibrary RenderEngine::luaLibrary() { - return{ +scripting::ScriptEngine::LuaLibrary RenderEngine::luaLibrary() { + return { + "", + { + { + "takeScreenshot", + &luascriptfunctions::takeScreenshot, "", - { - { - "takeScreenshot", - &luascriptfunctions::takeScreenshot, - "", - "Renders the current image to a file on disk" - }, - { - "visualizeABuffer", - &luascriptfunctions::visualizeABuffer, - "bool", - "Toggles the visualization of the ABuffer" - }, - { - "showRenderInformation", - &luascriptfunctions::showRenderInformation, - "bool", - "Toggles the showing of render information on-screen text" - }, - { - "showSGCTRenderStatistics", - &luascriptfunctions::showSGCTRenderStatistics, - "bool", - "Toggles the visibility of the SGCT rendering information" - }, - { - "setPerformanceMeasurement", - &luascriptfunctions::setPerformanceMeasurement, - "bool", - "Sets the performance measurements" - }, - // These are temporary ---abock - { - "changeCoordinateSystem", - &luascriptfunctions::changeCoordinateSystem, - "string", - "Changes the origin of the coordinate system to the passed node" - }, - { - "fadeIn", - &luascriptfunctions::fadeIn, - "number", - "" - }, - //also temporary @JK - { - "fadeOut", - &luascriptfunctions::fadeOut, - "number", - "" - }, - }, - }; + "Renders the current image to a file on disk" + }, + { + "visualizeABuffer", + &luascriptfunctions::visualizeABuffer, + "bool", + "Toggles the visualization of the ABuffer" + }, + { + "showRenderInformation", + &luascriptfunctions::showRenderInformation, + "bool", + "Toggles the showing of render information on-screen text" + }, + { + "showSGCTRenderStatistics", + &luascriptfunctions::showSGCTRenderStatistics, + "bool", + "Toggles the visibility of the SGCT rendering information" + }, + { + "setPerformanceMeasurement", + &luascriptfunctions::setPerformanceMeasurement, + "bool", + "Sets the performance measurements" + }, + // These are temporary ---abock + { + "changeCoordinateSystem", + &luascriptfunctions::changeCoordinateSystem, + "string", + "Changes the origin of the coordinate system to the passed node" + }, + { + "fadeIn", + &luascriptfunctions::fadeIn, + "number", + "" + }, + //also temporary @JK + { + "fadeOut", + &luascriptfunctions::fadeOut, + "number", + "" + }, + }, + }; +} + +void RenderEngine::setPerformanceMeasurements(bool performanceMeasurements) { + _doPerformanceMeasurements = performanceMeasurements; +} + +bool RenderEngine::doesPerformanceMeasurements() const { + return _doPerformanceMeasurements; +} + +void RenderEngine::storePerformanceMeasurements() { + const int8_t Version = 0; + const int nValues = 250; + const int lengthName = 256; + const int maxValues = 256; + + struct PerformanceLayout { + int8_t version; + int32_t nValuesPerEntry; + int32_t nEntries; + int32_t maxNameLength; + int32_t maxEntries; + + struct PerformanceLayoutEntry { + char name[lengthName]; + float renderTime[nValues]; + float updateRenderable[nValues]; + float updateEphemeris[nValues]; + + int32_t currentRenderTime; + int32_t currentUpdateRenderable; + int32_t currentUpdateEphemeris; + }; + + PerformanceLayoutEntry entries[maxValues]; + }; + + const int nNodes = static_cast(scene()->allSceneGraphNodes().size()); + if (!_performanceMemory) { + + // Compute the total size + const int totalSize = sizeof(int8_t) + 4 * sizeof(int32_t) + + maxValues * sizeof(PerformanceLayout::PerformanceLayoutEntry); + LINFO("Create shared memory of " << totalSize << " bytes"); + + ghoul::SharedMemory::create(PerformanceMeasurementSharedData, totalSize); + _performanceMemory = new ghoul::SharedMemory(PerformanceMeasurementSharedData); + + PerformanceLayout* layout = reinterpret_cast(_performanceMemory->pointer()); + layout->version = Version; + layout->nValuesPerEntry = nValues; + layout->nEntries = nNodes; + layout->maxNameLength = lengthName; + layout->maxEntries = maxValues; + + memset(layout->entries, 0, maxValues * sizeof(PerformanceLayout::PerformanceLayoutEntry)); + + for (int i = 0; i < nNodes; ++i) { + SceneGraphNode* node = scene()->allSceneGraphNodes()[i]; + + memset(layout->entries[i].name, 0, lengthName); + strcpy(layout->entries[i].name, node->name().c_str()); + + layout->entries[i].currentRenderTime = 0; + layout->entries[i].currentUpdateRenderable = 0; + layout->entries[i].currentUpdateEphemeris = 0; } + } - void RenderEngine::setPerformanceMeasurements(bool performanceMeasurements) { - _doPerformanceMeasurements = performanceMeasurements; - } + PerformanceLayout* layout = reinterpret_cast(_performanceMemory->pointer()); + _performanceMemory->acquireLock(); + for (int i = 0; i < nNodes; ++i) { + SceneGraphNode* node = scene()->allSceneGraphNodes()[i]; + SceneGraphNode::PerformanceRecord r = node->performanceRecord(); + PerformanceLayout::PerformanceLayoutEntry& entry = layout->entries[i]; - bool RenderEngine::doesPerformanceMeasurements() const { - return _doPerformanceMeasurements; - } + entry.renderTime[entry.currentRenderTime] = r.renderTime / 1000.f; + entry.updateEphemeris[entry.currentUpdateEphemeris] = r.updateTimeEphemeris / 1000.f; + entry.updateRenderable[entry.currentUpdateRenderable] = r.updateTimeRenderable / 1000.f; - void RenderEngine::storePerformanceMeasurements() { - const int8_t Version = 0; - const int nValues = 250; - const int lengthName = 256; - const int maxValues = 256; - - struct PerformanceLayout { - int8_t version; - int32_t nValuesPerEntry; - int32_t nEntries; - int32_t maxNameLength; - int32_t maxEntries; - - struct PerformanceLayoutEntry { - char name[lengthName]; - float renderTime[nValues]; - float updateRenderable[nValues]; - float updateEphemeris[nValues]; - - int32_t currentRenderTime; - int32_t currentUpdateRenderable; - int32_t currentUpdateEphemeris; - }; - - PerformanceLayoutEntry entries[maxValues]; - }; - - const int nNodes = static_cast(scene()->allSceneGraphNodes().size()); - if (!_performanceMemory) { - - // Compute the total size - const int totalSize = sizeof(int8_t) + 4 * sizeof(int32_t) + - maxValues * sizeof(PerformanceLayout::PerformanceLayoutEntry); - LINFO("Create shared memory of " << totalSize << " bytes"); - - ghoul::SharedMemory::create(PerformanceMeasurementSharedData, totalSize); - _performanceMemory = new ghoul::SharedMemory(PerformanceMeasurementSharedData); - - PerformanceLayout* layout = reinterpret_cast(_performanceMemory->pointer()); - layout->version = Version; - layout->nValuesPerEntry = nValues; - layout->nEntries = nNodes; - layout->maxNameLength = lengthName; - layout->maxEntries = maxValues; - - memset(layout->entries, 0, maxValues * sizeof(PerformanceLayout::PerformanceLayoutEntry)); - - for (int i = 0; i < nNodes; ++i) { - SceneGraphNode* node = scene()->allSceneGraphNodes()[i]; - - memset(layout->entries[i].name, 0, lengthName); - strcpy(layout->entries[i].name, node->name().c_str()); - - layout->entries[i].currentRenderTime = 0; - layout->entries[i].currentUpdateRenderable = 0; - layout->entries[i].currentUpdateEphemeris = 0; - } - } - - PerformanceLayout* layout = reinterpret_cast(_performanceMemory->pointer()); - _performanceMemory->acquireLock(); - for (int i = 0; i < nNodes; ++i) { - SceneGraphNode* node = scene()->allSceneGraphNodes()[i]; - SceneGraphNode::PerformanceRecord r = node->performanceRecord(); - PerformanceLayout::PerformanceLayoutEntry& entry = layout->entries[i]; - - entry.renderTime[entry.currentRenderTime] = r.renderTime / 1000.f; - entry.updateEphemeris[entry.currentUpdateEphemeris] = r.updateTimeEphemeris / 1000.f; - entry.updateRenderable[entry.currentUpdateRenderable] = r.updateTimeRenderable / 1000.f; - - entry.currentRenderTime = (entry.currentRenderTime + 1) % nValues; - entry.currentUpdateEphemeris = (entry.currentUpdateEphemeris + 1) % nValues; - entry.currentUpdateRenderable = (entry.currentUpdateRenderable + 1) % nValues; - } - _performanceMemory->releaseLock(); - } + entry.currentRenderTime = (entry.currentRenderTime + 1) % nValues; + entry.currentUpdateEphemeris = (entry.currentUpdateEphemeris + 1) % nValues; + entry.currentUpdateRenderable = (entry.currentUpdateRenderable + 1) % nValues; + } + _performanceMemory->releaseLock(); +} // This method is temporary and will be removed once the scalegraph is in effect ---abock void RenderEngine::changeViewPoint(std::string origin) { From 36b921a0173300cd392f7459b6c9a5771d4223d2 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 18 May 2015 18:34:48 +0200 Subject: [PATCH 010/329] More cleanup of RenderEngine --- src/rendering/renderengine.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index b17eb71438..a92227826a 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -732,15 +732,13 @@ void RenderEngine::toggleInfoText(bool b) { _showInfo = b; } -Scene* RenderEngine::scene() -{ +Scene* RenderEngine::scene() { // TODO custom assert (ticket #5) assert(_sceneGraph); return _sceneGraph; } -void RenderEngine::setSceneGraph(Scene* sceneGraph) -{ +void RenderEngine::setSceneGraph(Scene* sceneGraph) { _sceneGraph = sceneGraph; } @@ -775,15 +773,15 @@ ABuffer* RenderEngine::abuffer() const { return _abuffer; } -float RenderEngine::globalBlackOutFactor(){ +float RenderEngine::globalBlackOutFactor() { return _globalBlackOutFactor; } -void RenderEngine::setGlobalBlackOutFactor(float opacity){ +void RenderEngine::setGlobalBlackOutFactor(float opacity) { _globalBlackOutFactor = opacity; } -void RenderEngine::startFading(int direction, float fadeDuration){ +void RenderEngine::startFading(int direction, float fadeDuration) { _fadeDirection = direction; _fadeDuration = fadeDuration; _currentFadeTime = 0.f; From 15908e75a322840dba9877f97052f6f6a0a3178f Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 18 May 2015 18:52:59 +0200 Subject: [PATCH 011/329] Undo sphere changes to make stars look proper again --- shaders/modules/sphere/sphere_fs.glsl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/shaders/modules/sphere/sphere_fs.glsl b/shaders/modules/sphere/sphere_fs.glsl index eb3ba55162..3008e06ae5 100644 --- a/shaders/modules/sphere/sphere_fs.glsl +++ b/shaders/modules/sphere/sphere_fs.glsl @@ -39,8 +39,8 @@ void main() { vec4 position = vs_position; // This has to be fixed with the ScaleGraph in place (precision deficiency in depth buffer) ---abock - //float depth = pscDepth(position); - float depth = 0.1; + // float depth = pscDepth(position); + float depth = 200; vec4 diffuse; vec2 texCoord = vs_st; @@ -54,6 +54,8 @@ void main() diffuse.a *= alpha; + // diffuse.a = 0.0; + //vec4 diffuse = vec4(1,vs_st,1); //vec4 diffuse = vec4(1,0,0,1); // if(position.w > 9.0) { From 6fdd17242878dda36ad5abc91a345a0ba0d38bb0 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 18 May 2015 19:18:35 +0200 Subject: [PATCH 012/329] Fix initialization order of image sequencer to remove an error of unknown token --- src/engine/openspaceengine.cpp | 3 ++- src/util/imagesequencer2.cpp | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index bd846fcaec..4efa950e62 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -103,7 +103,6 @@ OpenSpaceEngine::OpenSpaceEngine(std::string programName) { SpiceManager::initialize(); Time::initialize(); - ImageSequencer2::initialize(); FactoryManager::initialize(); ghoul::systemcapabilities::SystemCapabilities::initialize(); } @@ -222,6 +221,8 @@ bool OpenSpaceEngine::create( FileSys.createCacheManager(absPath("${" + constants::configurationmanager::keyCache + "}")); _engine->_console->initialize(); + ImageSequencer2::initialize(); + // Register the provided shader directories ghoul::opengl::ShaderObject::addIncludePath("${SHADERS}"); diff --git a/src/util/imagesequencer2.cpp b/src/util/imagesequencer2.cpp index 41ffa1d74a..f17e28bc16 100644 --- a/src/util/imagesequencer2.cpp +++ b/src/util/imagesequencer2.cpp @@ -46,8 +46,7 @@ namespace openspace { ImageSequencer2* ImageSequencer2::_instance = nullptr; ImageSequencer2::ImageSequencer2() - : _defaultCaptureImage(absPath("${OPENSPACE_DATA}/scene/common/textures/placeholder_blank.png")) - , _latestImage() + : _latestImage() , _hasData(false) {} @@ -58,6 +57,7 @@ ImageSequencer2& ImageSequencer2::ref() { void ImageSequencer2::initialize() { assert(_instance == nullptr); _instance = new ImageSequencer2; + _instance->_defaultCaptureImage = absPath("${OPENSPACE_DATA}/scene/common/textures/placeholder_blank.png"); } void ImageSequencer2::deinitialize() { From 79673d5f530edb35994b3753416009a6b5ab01c3 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 18 May 2015 19:26:09 +0200 Subject: [PATCH 013/329] Do not log OpenGL version if the default version is used --- src/engine/openspaceengine.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 4efa950e62..258fb0771c 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -241,7 +241,8 @@ bool OpenSpaceEngine::create( } openGlVersion = commandlineArgumentPlaceholders.openGlVersion; - LINFO("Using OpenGL version " << openGlVersion); + if (openGlVersion != DefaultOpenGlVersion) + LINFO("Using OpenGL version " << openGlVersion); // Prepend the outgoing sgctArguments with the program name // as well as the configuration file that sgct is supposed to use From abdd1af2a9c4304adbe6214702fc2c0543244e66 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 19 May 2015 00:47:47 +0200 Subject: [PATCH 014/329] Cleaning up abuffer classes Make ABuffer type selectable in openspace.cfg --- include/openspace/abuffer/abufferfixed.h | 21 +++---- include/openspace/rendering/renderengine.h | 3 +- openspace.cfg | 64 ++++++++++++---------- src/abuffer/abufferSingleLinked.cpp | 5 ++ src/abuffer/abufferfixed.cpp | 3 +- src/engine/openspaceengine.cpp | 9 ++- src/rendering/renderengine.cpp | 60 ++++++++++++++------ 7 files changed, 105 insertions(+), 60 deletions(-) diff --git a/include/openspace/abuffer/abufferfixed.h b/include/openspace/abuffer/abufferfixed.h index 75c5117b57..cefdf10806 100644 --- a/include/openspace/abuffer/abufferfixed.h +++ b/include/openspace/abuffer/abufferfixed.h @@ -33,19 +33,19 @@ class ABufferFixed: public ABuffer { public: ABufferFixed(); - virtual ~ABufferFixed(); - virtual bool initialize(); + ~ABufferFixed(); + bool initialize() override; - virtual void clear(); - virtual void preRender(); - virtual void postRender(); + void clear() override; + void preRender() override; + void postRender() override; + + std::vector pixelData() override; - std::vector pixelData(); protected: - virtual bool reinitializeInternal(); + virtual bool reinitializeInternal() override; private: - GLuint *_data; GLuint _anchorPointerTexture; GLuint _anchorPointerTextureInitializer; @@ -53,11 +53,8 @@ private: GLuint _atomicCounterTexture; GLuint _fragmentBuffer; GLuint _fragmentTexture; - - - - }; // ABufferFixed + } // openspace #endif // __ABUFFERFIXED_H__ \ No newline at end of file diff --git a/include/openspace/rendering/renderengine.h b/include/openspace/rendering/renderengine.h index e9b4402fd5..8466028514 100644 --- a/include/openspace/rendering/renderengine.h +++ b/include/openspace/rendering/renderengine.h @@ -51,7 +51,7 @@ public: RenderEngine(); ~RenderEngine(); - bool initialize(); + bool initialize(const std::string& renderingMethod); void setSceneGraph(Scene* sceneGraph); Scene* scene(); @@ -113,6 +113,7 @@ private: Camera* _mainCamera; Scene* _sceneGraph; ABuffer* _abuffer; + int _abufferImplementation; ScreenLog* _log; bool _showInfo; diff --git a/openspace.cfg b/openspace.cfg index 1b3f990e52..505bf7812c 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -1,26 +1,36 @@ return { - Paths = { - SGCT = "${BASE_PATH}/config/sgct", - SCRIPTS = "${BASE_PATH}/scripts", - SHADERS = "${BASE_PATH}/shaders", - SHADERS_GENERATED = "${SHADERS}/generated", - OPENSPACE_DATA = "${BASE_PATH}/openspace-data", - TESTDIR = "${BASE_PATH}/tests", - CONFIG = "${BASE_PATH}/config", - CACHE = "${BASE_PATH}/cache", - FONTS = "${OPENSPACE_DATA}/fonts", - PLUTO_KERNELS = "${OPENSPACE_DATA}/spice/Pluto", - JP_KERNELS = "${OPENSPACE_DATA}/spice/JP_KERNELS" - }, - SpiceKernel = { - Time = "${OPENSPACE_DATA}/spice/naif0010.tls", - LeapSecond = "${OPENSPACE_DATA}/spice/pck00010.tpc", - NewHorizons = "${OPENSPACE_DATA}/spice/nhmeta.tm" - }, - Fonts = { - Mono = "${FONTS}/Droid_Sans_Mono/DroidSansMono.ttf", - Light = "${FONTS}/Roboto/Roboto-Regular.ttf" - }, + -- Determines which SGCT configuration file is loaded, that is, if there rendering + -- occurs in a single window, a fisheye projection, or a dome cluster system + SGCTConfig = "${SGCT}/single.xml", + --SGCTConfig = "${SGCT}/single_fisheye.xml", + --SGCTConfig = "${SGCT}/two_nodes.xml", + + -- Sets the scene that is to be loaded by OpenSpace. A scene file is a description + -- of all entities that will be visible during an instance of OpenSpace + Scene = "${OPENSPACE_DATA}/scene/default_nh.scene", + + Paths = { + SGCT = "${BASE_PATH}/config/sgct", + SCRIPTS = "${BASE_PATH}/scripts", + SHADERS = "${BASE_PATH}/shaders", + SHADERS_GENERATED = "${SHADERS}/generated", + OPENSPACE_DATA = "${BASE_PATH}/openspace-data", + TESTDIR = "${BASE_PATH}/tests", + CONFIG = "${BASE_PATH}/config", + CACHE = "${BASE_PATH}/cache", + FONTS = "${OPENSPACE_DATA}/fonts", + PLUTO_KERNELS = "${OPENSPACE_DATA}/spice/Pluto", + JP_KERNELS = "${OPENSPACE_DATA}/spice/JP_KERNELS" + }, + SpiceKernel = { + Time = "${OPENSPACE_DATA}/spice/naif0010.tls", + LeapSecond = "${OPENSPACE_DATA}/spice/pck00010.tpc", + NewHorizons = "${OPENSPACE_DATA}/spice/nhmeta.tm" + }, + Fonts = { + Mono = "${FONTS}/Droid_Sans_Mono/DroidSansMono.ttf", + Light = "${FONTS}/Roboto/Roboto-Regular.ttf" + }, StartupScripts = { "${SCRIPTS}/default_startup.lua" }, @@ -39,11 +49,9 @@ return { File = "${BASE_PATH}/LuaScripting.txt" }, PropertyDocumentationFile = { - Type = "text", - File = "${BASE_PATH}/Properties.txt" + Type = "text", + File = "${BASE_PATH}/Properties.txt" }, - SGCTConfig = "${SGCT}/single.xml", - --SGCTConfig = "${SGCT}/single_fisheye.xml", - --SGCTConfig = "${SGCT}/two_nodes.xml", - Scene = "${OPENSPACE_DATA}/scene/default_nh.scene", + RenderingMethod = "ABufferSingleLinked" -- On Windows and Unix + -- RenderingMethod = "ABufferFrameBuffer" -- On Mac due to OpenGL 4.1 restrictions } \ No newline at end of file diff --git a/src/abuffer/abufferSingleLinked.cpp b/src/abuffer/abufferSingleLinked.cpp index 7c5e85058c..118de840c6 100644 --- a/src/abuffer/abufferSingleLinked.cpp +++ b/src/abuffer/abufferSingleLinked.cpp @@ -68,12 +68,17 @@ bool ABufferSingleLinked::initialize() { // BUFFERS // ============================ glGenTextures(1, &_anchorPointerTexture); + LDEBUG("AnchorPointerTexture ID: " << _anchorPointerTexture); glGenBuffers(1, &_anchorPointerTextureInitializer); + LDEBUG("AnchorPointerTextureInitializer ID: " << _anchorPointerTextureInitializer); glGenBuffers(1, &_atomicCounterBuffer); + LDEBUG("AtomicCounterBuffer ID: " << _atomicCounterBuffer); glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, _atomicCounterBuffer); glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(GLuint), NULL, GL_DYNAMIC_COPY); glGenBuffers(1, &_fragmentBuffer); + LDEBUG("FragmentBuffer ID: " << _fragmentBuffer); glGenTextures(1, &_fragmentTexture); + LDEBUG("FragmentTexture ID: " << _fragmentTexture); reinitialize(); diff --git a/src/abuffer/abufferfixed.cpp b/src/abuffer/abufferfixed.cpp index 75e2b8d2a8..9277f70e3f 100644 --- a/src/abuffer/abufferfixed.cpp +++ b/src/abuffer/abufferfixed.cpp @@ -188,5 +188,4 @@ std::vector ABufferFixed::pixelData() { return d; } - -} // openspace \ No newline at end of file +} // openspace diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 258fb0771c..55f026401b 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -75,6 +75,9 @@ namespace { const std::string _sgctConfigArgumentCommand = "-config"; + const std::string KeyRenderingMethod = "RenderingMethod"; + const std::string DefaultRenderingMethod = "ABufferSingleLinked"; + const std::string DefaultOpenGlVersion = "4.3"; struct { @@ -319,7 +322,11 @@ bool OpenSpaceEngine::initialize() { _renderEngine->setSceneGraph(sceneGraph); // initialize the RenderEngine - _renderEngine->initialize(); + if (_configurationManager->hasKeyAndValue(KeyRenderingMethod)) + _renderEngine->initialize(_configurationManager->value(KeyRenderingMethod)); + else + _renderEngine->initialize(DefaultRenderingMethod); + sceneGraph->initialize(); std::string sceneDescriptionPath; diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index a92227826a..640a90502d 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -70,14 +70,21 @@ #define ABUFFER_FIXED 2 #define ABUFFER_DYNAMIC 3 -#ifdef __APPLE__ -#define ABUFFER_IMPLEMENTATION ABUFFER_FRAMEBUFFER -#else -#define ABUFFER_IMPLEMENTATION ABUFFER_SINGLE_LINKED -#endif +//#ifdef __APPLE__ +//#define ABUFFER_IMPLEMENTATION ABUFFER_FRAMEBUFFER +//#else +//#define ABUFFER_IMPLEMENTATION ABUFFER_SINGLE_LINKED +//#endif namespace { const std::string _loggerCat = "RenderEngine"; + + const std::map RenderingMethods = { + { "ABufferFrameBuffer", ABUFFER_FRAMEBUFFER}, + { "ABufferSingleLinked", ABUFFER_SINGLE_LINKED }, + { "ABufferFixed", ABUFFER_FIXED }, + { "ABufferDynamic", ABUFFER_DYNAMIC } + }; } namespace openspace { @@ -216,6 +223,7 @@ RenderEngine::RenderEngine() : _mainCamera(nullptr) , _sceneGraph(nullptr) , _abuffer(nullptr) + , _abufferImplementation(-1) , _log(nullptr) , _showInfo(true) , _showScreenLog(true) @@ -252,7 +260,35 @@ RenderEngine::~RenderEngine() { ghoul::SharedMemory::remove(PerformanceMeasurementSharedData); } -bool RenderEngine::initialize() { +bool RenderEngine::initialize(const std::string& renderingMethod) { + auto it = RenderingMethods.find(renderingMethod); + if (it == RenderingMethods.end()) { + LFATAL("Rendering method '" << renderingMethod << "' not among the available " + << "rendering methods"); + return false; + } + else { + _abufferImplementation = it->second; + switch (_abufferImplementation) { + case ABUFFER_FRAMEBUFFER: + LINFO("Creating ABufferFramebuffer implementation"); + _abuffer = new ABufferFramebuffer; + break; + case ABUFFER_SINGLE_LINKED: + LINFO("Creating ABufferSingleLinked implementation"); + _abuffer = new ABufferSingleLinked(); + break; + case ABUFFER_FIXED: + LINFO("Creating ABufferFixed implementation"); + _abuffer = new ABufferFixed(); + break; + case ABUFFER_DYNAMIC: + LINFO("Creating ABufferDynamic implementation"); + _abuffer = new ABufferDynamic(); + break; + } + } + generateGlslConfig(); // init camera and set temporary position and scaling @@ -270,15 +306,6 @@ bool RenderEngine::initialize() { ghoul::io::TextureReader::ref().addReader(new ghoul::io::impl::TextureReaderCMAP); -#if ABUFFER_IMPLEMENTATION == ABUFFER_FRAMEBUFFER - _abuffer = new ABufferFramebuffer(); -#elif ABUFFER_IMPLEMENTATION == ABUFFER_SINGLE_LINKED - _abuffer = new ABufferSingleLinked(); -#elif ABUFFER_IMPLEMENTATION == ABUFFER_FIXED - _abuffer = new ABufferFixed(); -#elif ABUFFER_IMPLEMENTATION == ABUFFER_DYNAMIC - _abuffer = new ABufferDynamic(); -#endif return true; } @@ -788,6 +815,7 @@ void RenderEngine::startFading(int direction, float fadeDuration) { } void RenderEngine::generateGlslConfig() { + ghoul_assert(_abuffer != nullptr, "ABuffer not initialized"); LDEBUG("Generating GLSLS config, expect shader recompilation"); int xSize = sgct::Engine::instance()->getActiveWindowPtr()->getXFramebufferResolution();; int ySize = sgct::Engine::instance()->getActiveWindowPtr()->getYFramebufferResolution();; @@ -805,7 +833,7 @@ void RenderEngine::generateGlslConfig() { << "#define ABUFFER_SINGLE_LINKED " << ABUFFER_SINGLE_LINKED << "\n" << "#define ABUFFER_FIXED " << ABUFFER_FIXED << "\n" << "#define ABUFFER_DYNAMIC " << ABUFFER_DYNAMIC << "\n" - << "#define ABUFFER_IMPLEMENTATION " << ABUFFER_IMPLEMENTATION << "\n"; + << "#define ABUFFER_IMPLEMENTATION " << _abufferImplementation << "\n"; // System specific #ifdef WIN32 os << "#define WIN32\n"; From 02dbe8c2b3366c90ace2ec54173b50ce54873131 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 19 May 2015 00:53:11 +0200 Subject: [PATCH 015/329] Renamed abufferSingleLinked files to abuffersinglelinked for capitalization conformance --- .../abuffer/{abufferSingleLinked.h => abuffersinglelinked.h} | 0 .../{abufferSingleLinked.cpp => abuffersinglelinked.cpp} | 2 +- src/rendering/renderengine.cpp | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename include/openspace/abuffer/{abufferSingleLinked.h => abuffersinglelinked.h} (100%) rename src/abuffer/{abufferSingleLinked.cpp => abuffersinglelinked.cpp} (99%) diff --git a/include/openspace/abuffer/abufferSingleLinked.h b/include/openspace/abuffer/abuffersinglelinked.h similarity index 100% rename from include/openspace/abuffer/abufferSingleLinked.h rename to include/openspace/abuffer/abuffersinglelinked.h diff --git a/src/abuffer/abufferSingleLinked.cpp b/src/abuffer/abuffersinglelinked.cpp similarity index 99% rename from src/abuffer/abufferSingleLinked.cpp rename to src/abuffer/abuffersinglelinked.cpp index 118de840c6..89750f6ced 100644 --- a/src/abuffer/abufferSingleLinked.cpp +++ b/src/abuffer/abuffersinglelinked.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 640a90502d..ce675200b7 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include #include From dc665639d5d68b31b81552eb31c9fb01b593427f Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 19 May 2015 01:05:19 +0200 Subject: [PATCH 016/329] Fixed crash with ABufferFixed implementation --- .../openspace/rendering/planets/renderableplanetprojection.h | 1 + shaders/ABuffer/abufferSort.hglsl | 4 ++-- src/abuffer/abufferfixed.cpp | 3 --- src/rendering/renderableplane.cpp | 2 -- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/include/openspace/rendering/planets/renderableplanetprojection.h b/include/openspace/rendering/planets/renderableplanetprojection.h index a585aa5b0d..174fb75c16 100644 --- a/include/openspace/rendering/planets/renderableplanetprojection.h +++ b/include/openspace/rendering/planets/renderableplanetprojection.h @@ -24,6 +24,7 @@ #ifndef __RENDERABLEPLANETPROJECTION_H__ #define __RENDERABLEPLANETPROJECTION_H__ + #include // open space includes diff --git a/shaders/ABuffer/abufferSort.hglsl b/shaders/ABuffer/abufferSort.hglsl index 478813a86a..8be2180e0a 100644 --- a/shaders/ABuffer/abufferSort.hglsl +++ b/shaders/ABuffer/abufferSort.hglsl @@ -57,9 +57,9 @@ int build_local_fragments_list() { return int(frag_count); #endif -#if ABUFFER_IMPLEMENTATION == ABUFFER_FRAMEBUFFER +// #if ABUFFER_IMPLEMENTATION == ABUFFER_FRAMEBUFFER return 0; -#endif +// #endif } float pscLength(vec4 v1, vec4 v2) { diff --git a/src/abuffer/abufferfixed.cpp b/src/abuffer/abufferfixed.cpp index 9277f70e3f..37455ac16a 100644 --- a/src/abuffer/abufferfixed.cpp +++ b/src/abuffer/abufferfixed.cpp @@ -45,9 +45,6 @@ ABufferFixed::ABufferFixed(): _data(0), _anchorPointerTexture(0), {} ABufferFixed::~ABufferFixed() { - if(_data != 0) - delete _data; - glDeleteTextures(1,&_anchorPointerTexture); glDeleteTextures(1,&_fragmentTexture); // glDeleteTextures(1,&_atomicCounterTexture); diff --git a/src/rendering/renderableplane.cpp b/src/rendering/renderableplane.cpp index a986c10985..ab144a22ee 100644 --- a/src/rendering/renderableplane.cpp +++ b/src/rendering/renderableplane.cpp @@ -183,9 +183,7 @@ void RenderablePlane::update(const UpdateData& data) { } void RenderablePlane::loadTexture() { - LDEBUG("loadTexture"); if (_texturePath.value() != "") { - LDEBUG("loadTexture2"); ghoul::opengl::Texture* texture = ghoul::io::TextureReader::ref().loadTexture(absPath(_texturePath)); if (texture) { LDEBUG("Loaded texture from '" << absPath(_texturePath) << "'"); From 8860a1cd1ffc144ac5d36471fab8769f6f681d9c Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 19 May 2015 01:09:57 +0200 Subject: [PATCH 017/329] Remove vao, vbo and ibo property declarations from simplespheregeometryprojection class --- .../rendering/planets/simplespheregeometryprojection.h | 4 ---- src/rendering/planets/simplespheregeometryprojection.cpp | 9 --------- 2 files changed, 13 deletions(-) diff --git a/include/openspace/rendering/planets/simplespheregeometryprojection.h b/include/openspace/rendering/planets/simplespheregeometryprojection.h index 4d3cda59f5..b64634b9ce 100644 --- a/include/openspace/rendering/planets/simplespheregeometryprojection.h +++ b/include/openspace/rendering/planets/simplespheregeometryprojection.h @@ -54,10 +54,6 @@ private: properties::IntProperty _segments; std::string _name; - properties::IntProperty _vaoID; - properties::IntProperty _vBufferID; - properties::IntProperty _iBufferID; - PowerScaledSphere* _planet; }; diff --git a/src/rendering/planets/simplespheregeometryprojection.cpp b/src/rendering/planets/simplespheregeometryprojection.cpp index d2706bc8de..a6ef51a9d8 100644 --- a/src/rendering/planets/simplespheregeometryprojection.cpp +++ b/src/rendering/planets/simplespheregeometryprojection.cpp @@ -45,9 +45,6 @@ SimpleSphereGeometryProjection::SimpleSphereGeometryProjection(const ghoul::Dict , _realRadius("radius", "Radius", glm::vec4(1.f, 1.f, 1.f, 0.f), glm::vec4(-10.f, -10.f, -10.f, -20.f), glm::vec4(10.f, 10.f, 10.f, 20.f)) , _segments("segments", "Segments", 20, 1, 1000) - , _vaoID("vaoID", "Vao", 1, 1, 1) - , _vBufferID("vboID", "Vbo", 1, 1, 1) - , _iBufferID("iboID", "Ibo", 1, 1, 1) , _planet(nullptr) { using constants::scenegraphnode::keyName; @@ -100,12 +97,6 @@ bool SimpleSphereGeometryProjection::initialize(RenderablePlanetProjection* pare createSphere(); //need to have this accessible in planetgeometryprojection for now -- Michal - _vaoID = static_cast(_planet->_vaoID); - _vBufferID = static_cast(_planet->_vBufferID); - _iBufferID = static_cast(_planet->_iBufferID); - addProperty(_vaoID); - addProperty(_vBufferID); - addProperty(_iBufferID); return success; } From 9e02d35d8a245049b6aaeba62655bcd24854d6aa Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 19 May 2015 01:13:53 +0200 Subject: [PATCH 018/329] Fix spelling errors in method names --- include/openspace/scene/scenegraph.h | 2 +- include/openspace/util/imagesequencer2.h | 2 +- src/rendering/renderablefov.cpp | 2 +- src/scene/scenegraph.cpp | 4 ++-- src/util/imagesequencer2.cpp | 6 +++--- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/openspace/scene/scenegraph.h b/include/openspace/scene/scenegraph.h index d8a49cf15d..87c1f4da8a 100644 --- a/include/openspace/scene/scenegraph.h +++ b/include/openspace/scene/scenegraph.h @@ -60,7 +60,7 @@ private: }; bool nodeIsDependentOnRoot(SceneGraphNodeInternal* node); - bool sortTopologially(); + bool sortTopologically(); SceneGraphNodeInternal* nodeByName(const std::string& name); diff --git a/include/openspace/util/imagesequencer2.h b/include/openspace/util/imagesequencer2.h index 37b1f857d2..326fc00c7a 100644 --- a/include/openspace/util/imagesequencer2.h +++ b/include/openspace/util/imagesequencer2.h @@ -132,7 +132,7 @@ public: /* * returns true if instrumentID is within a capture range. */ - bool instumentActive(std::string instrumentID); + bool instrumentActive(std::string instrumentID); float instrumentActiveTime(const std::string& instrumentID) const; diff --git a/src/rendering/renderablefov.cpp b/src/rendering/renderablefov.cpp index 7f4f3fd54b..808b52987c 100644 --- a/src/rendering/renderablefov.cpp +++ b/src/rendering/renderablefov.cpp @@ -426,7 +426,7 @@ void RenderableFov::render(const RenderData& data) { setPscUniforms(_programObject, &data.camera, data.position); if (openspace::ImageSequencer2::ref().isReady()){ - drawFOV = ImageSequencer2::ref().instumentActive(_instrumentID); + drawFOV = ImageSequencer2::ref().instrumentActive(_instrumentID); } diff --git a/src/scene/scenegraph.cpp b/src/scene/scenegraph.cpp index f4591efd64..bcb359bfec 100644 --- a/src/scene/scenegraph.cpp +++ b/src/scene/scenegraph.cpp @@ -259,7 +259,7 @@ bool SceneGraph::loadFromFile(const std::string& sceneDescription) { } } - bool s = sortTopologially(); + bool s = sortTopologically(); if (!s) { LERROR("Topological sort failed"); return false; @@ -281,7 +281,7 @@ bool SceneGraph::nodeIsDependentOnRoot(SceneGraphNodeInternal* node) { } } -bool SceneGraph::sortTopologially() { +bool SceneGraph::sortTopologically() { if (_nodes.empty()) return true; diff --git a/src/util/imagesequencer2.cpp b/src/util/imagesequencer2.cpp index f17e28bc16..46c7434031 100644 --- a/src/util/imagesequencer2.cpp +++ b/src/util/imagesequencer2.cpp @@ -174,7 +174,7 @@ std::map ImageSequencer2::getActiveInstruments(){ // for each spice-instrument for (auto instrumentID : key.second->getTranslation()){ // check if the spice-instrument is active - if (instumentActive(instrumentID)){ + if (instrumentActive(instrumentID)){ // go over switching map for (auto instrument : _switchingMap){ // if instrument is present in switching map @@ -189,7 +189,7 @@ std::map ImageSequencer2::getActiveInstruments(){ // return entire map, seen in GUI. return _switchingMap; } -bool ImageSequencer2::instumentActive(std::string instrumentID){ +bool ImageSequencer2::instrumentActive(std::string instrumentID){ for (auto i : _instrumentTimes){ //check if this instrument is in range if (i.second.inRange(_currentTime)){ @@ -227,7 +227,7 @@ bool ImageSequencer2::getImagePaths(std::vector& captures, std::string projectee, std::string instrumentID){ - if (!instumentActive(instrumentID) && !Time::ref().timeJumped()) return false; + if (!instrumentActive(instrumentID) && !Time::ref().timeJumped()) return false; // dev. note: this is only due to LORRI being the only instrument implemented so far. return getImagePaths(captures, projectee); } From 683ce14449048144c3201368176c7137d00ebe98 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 19 May 2015 13:58:00 +0200 Subject: [PATCH 019/329] Various cleanups in main.cpp Adapted to new Ghoul version --- ext/ghoul | 2 +- include/openspace/engine/openspaceengine.h | 4 +- src/engine/openspaceengine.cpp | 4 +- src/main.cpp | 72 +++++++++------------- 4 files changed, 34 insertions(+), 48 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index c0dc8b8380..3b859c6c54 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit c0dc8b83805c34f435770159772c23de583bb529 +Subproject commit 3b859c6c54658d16b3be26446b65bdfcc6d0d057 diff --git a/include/openspace/engine/openspaceengine.h b/include/openspace/engine/openspaceengine.h index 4e85dee7c3..e4b593d97c 100644 --- a/include/openspace/engine/openspaceengine.h +++ b/include/openspace/engine/openspaceengine.h @@ -83,8 +83,8 @@ public: void keyboardCallback(int key, int action); void charCallback(unsigned int codepoint); void mouseButtonCallback(int key, int action); - void mousePositionCallback(int x, int y); - void mouseScrollWheelCallback(int pos); + void mousePositionCallback(double x, double y); + void mouseScrollWheelCallback(double pos); void externalControlCallback(const char* receivedChars, int size, int clientId); void encode(); void decode(); diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 55f026401b..018462997c 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -690,13 +690,13 @@ void OpenSpaceEngine::mouseButtonCallback(int key, int action) { } } -void OpenSpaceEngine::mousePositionCallback(int x, int y) { +void OpenSpaceEngine::mousePositionCallback(double x, double y) { if (_isMaster) { _interactionHandler->mousePositionCallback(x, y); } } -void OpenSpaceEngine::mouseScrollWheelCallback(int pos) { +void OpenSpaceEngine::mouseScrollWheelCallback(double pos) { if (_isMaster) { if (_gui->isEnabled()) { bool isConsumed = _gui->mouseWheelCallback(pos); diff --git a/src/main.cpp b/src/main.cpp index 9005b246f8..3396b8ef25 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -134,7 +134,7 @@ int main(int argc, char** argv) { } //is this node the master? (must be set after call to _sgctEngine->init()) - OsEng.ref().setMaster(_sgctEngine->isMaster()); + OsEng.setMaster(_sgctEngine->isMaster()); // Main loop LDEBUG("Starting rendering loop"); @@ -151,8 +151,7 @@ int main(int argc, char** argv) { exit(EXIT_SUCCESS); } -void mainInitFunc() -{ +void mainInitFunc() { bool success = OsEng.initialize(); if (success) success = OsEng.initializeGL(); @@ -168,83 +167,70 @@ void mainInitFunc() setupPostFX(); } -void mainPreSyncFunc() -{ +void mainPreSyncFunc() { OsEng.preSynchronization(); } -void mainPostSyncPreDrawFunc() -{ +void mainPostSyncPreDrawFunc() { OsEng.postSynchronizationPreDraw(); } -void mainRenderFunc() -{ +void mainRenderFunc() { + using glm::mat4; + using glm::translate; //not the most efficient, but for clarity @JK - glm::mat4 userMatrix = glm::translate(glm::mat4(1.f), _sgctEngine->getDefaultUserPtr()->getPos()); - glm::mat4 sceneMatrix = _sgctEngine->getModelMatrix(); - glm::mat4 viewMatrix = _sgctEngine->getActiveViewMatrix() * userMatrix; + mat4 userMatrix = translate(mat4(1.f), _sgctEngine->getDefaultUserPtr()->getPos()); + mat4 sceneMatrix = _sgctEngine->getModelMatrix(); + mat4 viewMatrix = _sgctEngine->getActiveViewMatrix() * userMatrix; //dont shift nav-direction on master, makes it very tricky to navigate @JK - if (!OsEng.ref().isMaster()){ + if (!OsEng.ref().isMaster()) viewMatrix = viewMatrix * sceneMatrix; - } - glm::mat4 projectionMatrix = _sgctEngine->getActiveProjectionMatrix(); + mat4 projectionMatrix = _sgctEngine->getActiveProjectionMatrix(); OsEng.render(projectionMatrix, viewMatrix); } -void mainPostDrawFunc() -{ +void mainPostDrawFunc() { OsEng.postDraw(); } -void mainExternalControlCallback(const char* receivedChars, int size) -{ - if (OsEng.ref().isMaster()) +void mainExternalControlCallback(const char* receivedChars, int size) { + if (OsEng.isMaster()) OsEng.externalControlCallback(receivedChars, size, 0); } -void mainKeyboardCallback(int key, int action) -{ - if (OsEng.ref().isMaster()) +void mainKeyboardCallback(int key, int action) { + if (OsEng.isMaster()) OsEng.keyboardCallback(key, action); } -void mainMouseButtonCallback(int key, int action) -{ - if (OsEng.ref().isMaster()) +void mainMouseButtonCallback(int key, int action) { + if (OsEng.isMaster()) OsEng.mouseButtonCallback(key, action); } -void mainMousePosCallback(double x, double y) -{ - // TODO use float instead - if (OsEng.ref().isMaster()) - OsEng.mousePositionCallback(static_cast(x), static_cast(y)); +void mainMousePosCallback(double x, double y) { + if (OsEng.isMaster()) + OsEng.mousePositionCallback(x, y); } -void mainMouseScrollCallback(double posX, double posY) -{ - // TODO use float instead - if (OsEng.ref().isMaster()) - OsEng.mouseScrollWheelCallback(static_cast(posY)); +void mainMouseScrollCallback(double posX, double posY) { + if (OsEng.isMaster()) + OsEng.mouseScrollWheelCallback(posY); } void mainCharCallback(unsigned int codepoint) { - - if (OsEng.ref().isMaster()) + if (OsEng.isMaster()) OsEng.charCallback(codepoint); } -void mainEncodeFun() -{ +void mainEncodeFun() { OsEng.encode(); } -void mainDecodeFun() -{ +void mainDecodeFun() { OsEng.decode(); } @@ -259,7 +245,7 @@ void postFXPass(){ if (OsEng.isMaster()) glUniform1f(_postFXOpacityLoc, 1.f); else - glUniform1f(_postFXOpacityLoc, OsEng.ref().renderEngine()->globalBlackOutFactor()); + glUniform1f(_postFXOpacityLoc, OsEng.renderEngine()->globalBlackOutFactor()); } void setupPostFX(){ From db3a578e2e2889082ac01ee8f2e729f92b856b95 Mon Sep 17 00:00:00 2001 From: Anton Arbring Date: Tue, 19 May 2015 12:22:04 -0400 Subject: [PATCH 020/329] Updated submodule commit references --- ext/ghoul | 2 +- openspace-data | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index bf2ab81efb..3b859c6c54 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit bf2ab81efbb379a7a4fd92c6d87dd10ca530c575 +Subproject commit 3b859c6c54658d16b3be26446b65bdfcc6d0d057 diff --git a/openspace-data b/openspace-data index 3fe0b83814..54af421ff5 160000 --- a/openspace-data +++ b/openspace-data @@ -1 +1 @@ -Subproject commit 3fe0b83814a3494007244e1607299273bee519ad +Subproject commit 54af421ff583392c559748983251da6521c73c8a From abc8f7a5d8bd05e1c91d9f3bf9c2917b9ee778b6 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 20 May 2015 03:07:09 +0200 Subject: [PATCH 021/329] Make OpenSpace use new Cache version --- ext/ghoul | 2 +- src/engine/openspaceengine.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index 3b859c6c54..460ad44f0c 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 3b859c6c54658d16b3be26446b65bdfcc6d0d057 +Subproject commit 460ad44f0c7208ef7a5a9d199792f1f4954c2310 diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 018462997c..db01bd7374 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -79,6 +79,8 @@ namespace { const std::string DefaultRenderingMethod = "ABufferSingleLinked"; const std::string DefaultOpenGlVersion = "4.3"; + + const int CacheVersion = 1; struct { std::string configurationName; @@ -221,7 +223,7 @@ bool OpenSpaceEngine::create( } // Create the cachemanager - FileSys.createCacheManager(absPath("${" + constants::configurationmanager::keyCache + "}")); + FileSys.createCacheManager(absPath("${" + constants::configurationmanager::keyCache + "}"), CacheVersion); _engine->_console->initialize(); ImageSequencer2::initialize(); From 18752b9d8be93ebe6758c6da64aef0a37f3c1677 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 20 May 2015 10:33:36 +0200 Subject: [PATCH 022/329] Remove individual dirty flags for ProgramObjects and replace with new Ghoul version Moved projectiveTexture shader into modules directory --- ext/ghoul | 2 +- .../rendering/renderablecrawlingline.h | 1 - .../rendering/renderablefieldlines.h | 1 - include/openspace/rendering/renderablepath.h | 87 +++++++++---------- include/openspace/rendering/renderableplane.h | 1 - .../rendering/renderableplaneprojection.h | 75 ++++++++-------- .../openspace/rendering/renderablesphere.h | 1 - include/openspace/rendering/renderabletrail.h | 1 - .../stars/renderableconstellationbounds.h | 1 - .../rendering/stars/renderablestars.h | 1 - .../projection}/projectiveTexture_fs.glsl | 4 +- .../projection}/projectiveTexture_vs.glsl | 0 shaders/modules/stars/star_fs.glsl | 2 +- src/main.cpp | 1 + .../planets/renderableplanetprojection.cpp | 21 +++-- src/rendering/renderablecrawlingline.cpp | 4 +- src/rendering/renderablefieldlines.cpp | 11 +-- src/rendering/renderablepath.cpp | 6 +- src/rendering/renderableplane.cpp | 6 +- src/rendering/renderableplaneprojection.cpp | 5 +- src/rendering/renderablesphere.cpp | 6 +- src/rendering/renderabletrail.cpp | 6 +- .../stars/renderableconstellationbounds.cpp | 6 +- src/rendering/stars/renderablestars.cpp | 5 +- src/scene/scene.cpp | 9 -- 25 files changed, 109 insertions(+), 154 deletions(-) rename shaders/{ => modules/projection}/projectiveTexture_fs.glsl (99%) rename shaders/{ => modules/projection}/projectiveTexture_vs.glsl (100%) diff --git a/ext/ghoul b/ext/ghoul index 460ad44f0c..711fd33d85 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 460ad44f0c7208ef7a5a9d199792f1f4954c2310 +Subproject commit 711fd33d8536abc52242ae266566245bdc199a97 diff --git a/include/openspace/rendering/renderablecrawlingline.h b/include/openspace/rendering/renderablecrawlingline.h index a7979b2e0a..57356d9792 100644 --- a/include/openspace/rendering/renderablecrawlingline.h +++ b/include/openspace/rendering/renderablecrawlingline.h @@ -43,7 +43,6 @@ public: private: ghoul::opengl::ProgramObject* _program; - bool _programIsDirty; std::string _instrumentName; std::string _source; diff --git a/include/openspace/rendering/renderablefieldlines.h b/include/openspace/rendering/renderablefieldlines.h index 3f6ca14ffe..bd37f69b30 100644 --- a/include/openspace/rendering/renderablefieldlines.h +++ b/include/openspace/rendering/renderablefieldlines.h @@ -76,7 +76,6 @@ private: properties::StringProperty _seedPointSourceFile; ghoul::opengl::ProgramObject* _program; - bool _programIsDirty; ghoul::Dictionary _vectorFieldInfo; ghoul::Dictionary _fieldlineInfo; diff --git a/include/openspace/rendering/renderablepath.h b/include/openspace/rendering/renderablepath.h index 32b449df69..03637503a5 100644 --- a/include/openspace/rendering/renderablepath.h +++ b/include/openspace/rendering/renderablepath.h @@ -40,55 +40,54 @@ namespace ghoul { namespace openspace { - class RenderablePath : public Renderable { - public: - RenderablePath(const ghoul::Dictionary& dictionary); +class RenderablePath : public Renderable { +public: + RenderablePath(const ghoul::Dictionary& dictionary); - bool initialize() override; - bool deinitialize() override; + bool initialize() override; + bool deinitialize() override; - bool isReady() const override; + bool isReady() const override; - void render(const RenderData& data) override; - void update(const UpdateData& data) override; + void render(const RenderData& data) override; + void update(const UpdateData& data) override; - void calculatePath(std::string observer); - private: - struct VertexInfo { - float x, y, z, e; - //float r, g, b, a; - }; - void sendToGPU(); - void addPosition(psc pos); - void addColor(glm::vec4 col); - - glm::vec3 _lineColor; - glm::vec4 _lastPosition; - properties::FloatProperty _lineFade; - properties::FloatProperty _lineWidth; - properties::BoolProperty _drawLine; - - ghoul::opengl::ProgramObject* _programObject; - bool _programIsDirty; - - bool _successfullDictionaryFetch; - - std::string _target; - std::string _observer; - std::string _frame; - - GLuint _vaoID; - GLuint _vBufferID; - - bool _needsSweep; - - std::vector _vertexArray; - - float _increment; - double _start; - double _stop; - float _distanceFade; + void calculatePath(std::string observer); +private: + struct VertexInfo { + float x, y, z, e; + //float r, g, b, a; }; + void sendToGPU(); + void addPosition(psc pos); + void addColor(glm::vec4 col); + + glm::vec3 _lineColor; + glm::vec4 _lastPosition; + properties::FloatProperty _lineFade; + properties::FloatProperty _lineWidth; + properties::BoolProperty _drawLine; + + ghoul::opengl::ProgramObject* _programObject; + + bool _successfullDictionaryFetch; + + std::string _target; + std::string _observer; + std::string _frame; + + GLuint _vaoID; + GLuint _vBufferID; + + bool _needsSweep; + + std::vector _vertexArray; + + float _increment; + double _start; + double _stop; + float _distanceFade; +}; } // namespace openspace diff --git a/include/openspace/rendering/renderableplane.h b/include/openspace/rendering/renderableplane.h index 2c296da5f7..105ca7c7f9 100644 --- a/include/openspace/rendering/renderableplane.h +++ b/include/openspace/rendering/renderableplane.h @@ -75,7 +75,6 @@ private: bool _planeIsDirty; ghoul::opengl::ProgramObject* _shader; - bool _programIsDirty; bool _textureIsDirty; ghoul::opengl::Texture* _texture; ghoul::filesystem::File* _textureFile; diff --git a/include/openspace/rendering/renderableplaneprojection.h b/include/openspace/rendering/renderableplaneprojection.h index 8a7e8e4d75..92c5e479f6 100644 --- a/include/openspace/rendering/renderableplaneprojection.h +++ b/include/openspace/rendering/renderableplaneprojection.h @@ -43,57 +43,56 @@ namespace ghoul { } namespace openspace { - struct LinePoint; +struct LinePoint; - struct target { - std::string body; - std::string frame; - std::string node; - }; +struct target { + std::string body; + std::string frame; + std::string node; +}; - class RenderablePlaneProjection : public Renderable { +class RenderablePlaneProjection : public Renderable { - public: - RenderablePlaneProjection(const ghoul::Dictionary& dictionary); - ~RenderablePlaneProjection(); +public: + RenderablePlaneProjection(const ghoul::Dictionary& dictionary); + ~RenderablePlaneProjection(); - bool initialize() override; - bool deinitialize() override; + bool initialize() override; + bool deinitialize() override; - bool isReady() const override; + bool isReady() const override; - void render(const RenderData& data) override; - void update(const UpdateData& data) override; + void render(const RenderData& data) override; + void update(const UpdateData& data) override; - private: - void loadTexture(); - void updatePlane(const Image img, double currentTime); - std::string findClosestTarget(double currentTime); - void setTarget(std::string body); +private: + void loadTexture(); + void updatePlane(const Image img, double currentTime); + std::string findClosestTarget(double currentTime); + void setTarget(std::string body); - std::string _texturePath; + std::string _texturePath; - bool _planeIsDirty; + bool _planeIsDirty; - glm::dmat3 _stateMatrix; - std::string _frame; + glm::dmat3 _stateMatrix; + std::string _frame; - ghoul::opengl::ProgramObject* _shader; - bool _programIsDirty; - bool _textureIsDirty; - ghoul::opengl::Texture* _texture; - ghoul::filesystem::File* _textureFile; - GLuint _quad; - GLuint _vertexPositionBuffer; - std::string _spacecraft; - std::string _instrument; + ghoul::opengl::ProgramObject* _shader; + bool _textureIsDirty; + ghoul::opengl::Texture* _texture; + ghoul::filesystem::File* _textureFile; + GLuint _quad; + GLuint _vertexPositionBuffer; + std::string _spacecraft; + std::string _instrument; - double _previousTime; - target _target; - std::string _name; - bool _moving; - }; + double _previousTime; + target _target; + std::string _name; + bool _moving; +}; } // namespace openspace #endif diff --git a/include/openspace/rendering/renderablesphere.h b/include/openspace/rendering/renderablesphere.h index 2389998ce4..e8b7defddc 100644 --- a/include/openspace/rendering/renderablesphere.h +++ b/include/openspace/rendering/renderablesphere.h @@ -67,7 +67,6 @@ private: PowerScaledSphere* _sphere; - bool _programIsDirty; bool _sphereIsDirty; }; diff --git a/include/openspace/rendering/renderabletrail.h b/include/openspace/rendering/renderabletrail.h index 38a66a5070..cd04a00c22 100644 --- a/include/openspace/rendering/renderabletrail.h +++ b/include/openspace/rendering/renderabletrail.h @@ -66,7 +66,6 @@ private: properties::BoolProperty _showTimestamps; ghoul::opengl::ProgramObject* _programObject; - bool _programIsDirty; bool _successfullDictionaryFetch; diff --git a/include/openspace/rendering/stars/renderableconstellationbounds.h b/include/openspace/rendering/stars/renderableconstellationbounds.h index b127682f25..08299054f8 100644 --- a/include/openspace/rendering/stars/renderableconstellationbounds.h +++ b/include/openspace/rendering/stars/renderableconstellationbounds.h @@ -102,7 +102,6 @@ private: std::string _constellationFilename; ///< The file containing constellation names ghoul::opengl::ProgramObject* _program; - bool _programIsDirty; /// The list of all loaded constellation bounds std::vector _constellationBounds; diff --git a/include/openspace/rendering/stars/renderablestars.h b/include/openspace/rendering/stars/renderablestars.h index 4dac59231f..542a604850 100644 --- a/include/openspace/rendering/stars/renderablestars.h +++ b/include/openspace/rendering/stars/renderablestars.h @@ -77,7 +77,6 @@ private: properties::FloatProperty _minBillboardSize; ghoul::opengl::ProgramObject* _program; - bool _programIsDirty; std::string _speckFile; diff --git a/shaders/projectiveTexture_fs.glsl b/shaders/modules/projection/projectiveTexture_fs.glsl similarity index 99% rename from shaders/projectiveTexture_fs.glsl rename to shaders/modules/projection/projectiveTexture_fs.glsl index e6ba17dce7..6f34a71974 100644 --- a/shaders/projectiveTexture_fs.glsl +++ b/shaders/modules/projection/projectiveTexture_fs.glsl @@ -27,9 +27,9 @@ uniform vec4 campos; uniform vec4 objpos; //uniform vec3 camdir; // add this for specular + - -uniform float time; +uniform float time; uniform sampler2D texture1; uniform sampler2D texture2; diff --git a/shaders/projectiveTexture_vs.glsl b/shaders/modules/projection/projectiveTexture_vs.glsl similarity index 100% rename from shaders/projectiveTexture_vs.glsl rename to shaders/modules/projection/projectiveTexture_vs.glsl diff --git a/shaders/modules/stars/star_fs.glsl b/shaders/modules/stars/star_fs.glsl index 162f70b0e2..efde60046d 100644 --- a/shaders/modules/stars/star_fs.glsl +++ b/shaders/modules/stars/star_fs.glsl @@ -28,7 +28,7 @@ const int COLOROPTION_COLOR = 0; const int COLOROPTION_VELOCITY = 1; const int COLOROPTION_SPEED = 2; - + uniform sampler2D psfTexture; uniform sampler1D colorTexture; uniform float minBillboardSize; diff --git a/src/main.cpp b/src/main.cpp index 3396b8ef25..af5dc9325b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -74,6 +74,7 @@ int main(int argc, char** argv) { for (int i = 0; i < newArgc; ++i) newArgv[i] = const_cast(sgctArguments.at(i).c_str()); + // Need to set this before the creation of the sgct::Engine sgct::MessageHandler::instance()->setLogToConsole(false); sgct::MessageHandler::instance()->setShowTime(false); sgct::MessageHandler::instance()->setLogToCallback(true); diff --git a/src/rendering/planets/renderableplanetprojection.cpp b/src/rendering/planets/renderableplanetprojection.cpp index a0cb398db5..7e81e679a3 100644 --- a/src/rendering/planets/renderableplanetprojection.cpp +++ b/src/rendering/planets/renderableplanetprojection.cpp @@ -208,15 +208,20 @@ RenderablePlanetProjection::RenderablePlanetProjection(const ghoul::Dictionary& } } -RenderablePlanetProjection::~RenderablePlanetProjection(){ +RenderablePlanetProjection::~RenderablePlanetProjection() { deinitialize(); } -bool RenderablePlanetProjection::initialize(){ +bool RenderablePlanetProjection::initialize() { bool completeSuccess = true; - if (_programObject == nullptr) - completeSuccess - &= OsEng.ref().configurationManager()->getValue("projectiveProgram", _programObject); + if (_programObject == nullptr) { + // projection program + _programObject = ghoul::opengl::ProgramObject::Build("projectiveProgram", + "${SHADERS}/modules/projection/projectiveTexture_vs.glsl", + "${SHADERS}/modules/projection/projectiveTexture_fs.glsl"); + if (!_programObject) + return false; + } if (_fboProgramObject == nullptr) completeSuccess @@ -492,8 +497,10 @@ void RenderablePlanetProjection::update(const UpdateData& data){ if (openspace::ImageSequencer2::ref().isReady() && _performProjection){ openspace::ImageSequencer2::ref().updateSequencer(_time); _capture = openspace::ImageSequencer2::ref().getImagePaths(_imageTimes, _projecteeID, _instrumentID); - } - //floor fading to decimal + } + + if (_programObject->isDirty()) + _programObject->rebuildFromFile(); } void RenderablePlanetProjection::loadProjectionTexture(){ diff --git a/src/rendering/renderablecrawlingline.cpp b/src/rendering/renderablecrawlingline.cpp index 3e1166a890..b9510bc6cd 100644 --- a/src/rendering/renderablecrawlingline.cpp +++ b/src/rendering/renderablecrawlingline.cpp @@ -48,7 +48,6 @@ namespace openspace { RenderableCrawlingLine::RenderableCrawlingLine(const ghoul::Dictionary& dictionary) : Renderable(dictionary) , _program(nullptr) - , _programIsDirty(false) , _imageSequenceTime(-1.f) , _vao(0) , _vbo(0) @@ -83,7 +82,6 @@ bool RenderableCrawlingLine::initialize() { ); if (!_program) return false; - _program->setProgramObjectCallback([&](ghoul::opengl::ProgramObject*){ _programIsDirty = true; }); glGenVertexArrays(1, &_vao); glGenBuffers(1, &_vbo); @@ -148,6 +146,8 @@ void RenderableCrawlingLine::render(const RenderData& data) { } void RenderableCrawlingLine::update(const UpdateData& data) { + if (_program->isDirty()) + _program->rebuildFromFile(); glm::dmat3 transformMatrix = glm::dmat3(1); openspace::SpiceManager::ref().getPositionTransformMatrix(_source, _referenceFrame, data.time, transformMatrix); diff --git a/src/rendering/renderablefieldlines.cpp b/src/rendering/renderablefieldlines.cpp index 06babef237..0df2d05e98 100644 --- a/src/rendering/renderablefieldlines.cpp +++ b/src/rendering/renderablefieldlines.cpp @@ -85,7 +85,6 @@ RenderableFieldlines::RenderableFieldlines(const ghoul::Dictionary& dictionary) , _seedPointSource("source", "SeedPoint Source") , _seedPointSourceFile("sourceFile", "SeedPoint File") , _program(nullptr) - , _programIsDirty(false) , _seedPointsAreDirty(true) , _fieldLinesAreDirty(true) , _fieldlineVAO(0) @@ -210,12 +209,6 @@ bool RenderableFieldlines::initialize() { if (!_program) return false; - _program->setProgramObjectCallback( - [&](ghoul::opengl::ProgramObject*) { - this->_programIsDirty = true; - } - ); - return true; } @@ -251,10 +244,8 @@ void RenderableFieldlines::render(const RenderData& data) { } void RenderableFieldlines::update(const UpdateData&) { - if (_programIsDirty) { + if (_program->isDirty()) _program->rebuildFromFile(); - _programIsDirty = false; - } if (_seedPointsAreDirty) { loadSeedPoints(); diff --git a/src/rendering/renderablepath.cpp b/src/rendering/renderablepath.cpp index a5185ece21..9a3a2eff43 100644 --- a/src/rendering/renderablepath.cpp +++ b/src/rendering/renderablepath.cpp @@ -62,7 +62,6 @@ RenderablePath::RenderablePath(const ghoul::Dictionary& dictionary) , _lineWidth("lineWidth", "Line Width", 2.f, 1.f, 20.f) , _drawLine("drawline", "Draw Line", false) , _programObject(nullptr) - , _programIsDirty(true) , _successfullDictionaryFetch(true) , _vaoID(0) , _vBufferID(0) @@ -107,7 +106,6 @@ bool RenderablePath::initialize() { ); if (!_programObject) return false; - _programObject->setProgramObjectCallback([&](ghoul::opengl::ProgramObject*){ _programIsDirty = true; }); bool intervalSet = hasTimeInterval(); if (intervalSet) { @@ -197,10 +195,8 @@ void RenderablePath::update(const UpdateData& data) { return; } - if (_programIsDirty) { + if (_programObject->isDirty()) _programObject->rebuildFromFile(); - _programIsDirty = false; - } } void RenderablePath::calculatePath(std::string observer) { diff --git a/src/rendering/renderableplane.cpp b/src/rendering/renderableplane.cpp index ab144a22ee..64badebdde 100644 --- a/src/rendering/renderableplane.cpp +++ b/src/rendering/renderableplane.cpp @@ -54,7 +54,6 @@ RenderablePlane::RenderablePlane(const ghoul::Dictionary& dictionary) , _size("size", "Size", glm::vec2(1,1), glm::vec2(0.f), glm::vec2(1.f, 25.f)) , _origin(Origin::Center) , _shader(nullptr) - , _programIsDirty(false) , _textureIsDirty(false) , _texture(nullptr) , _quad(0) @@ -129,7 +128,6 @@ bool RenderablePlane::initialize() { if (_shader == nullptr) OsEng.ref().configurationManager()->getValue("planeProgram", _shader); - _shader->setProgramObjectCallback([&](ghoul::opengl::ProgramObject*){ _programIsDirty = true; }); loadTexture(); return isReady(); @@ -168,10 +166,8 @@ void RenderablePlane::render(const RenderData& data) { } void RenderablePlane::update(const UpdateData& data) { - if (_programIsDirty) { + if (_shader->isDirty()) _shader->rebuildFromFile(); - _programIsDirty = false; - } if (_planeIsDirty) createPlane(); diff --git a/src/rendering/renderableplaneprojection.cpp b/src/rendering/renderableplaneprojection.cpp index fed5ad02f6..ff9cb2fd47 100644 --- a/src/rendering/renderableplaneprojection.cpp +++ b/src/rendering/renderableplaneprojection.cpp @@ -52,7 +52,6 @@ RenderablePlaneProjection::RenderablePlaneProjection(const ghoul::Dictionary& di , _texturePath("") , _planeIsDirty(false) , _shader(nullptr) - , _programIsDirty(false) , _textureIsDirty(false) , _texture(nullptr) , _quad(0) @@ -162,10 +161,8 @@ void RenderablePlaneProjection::update(const UpdateData& data) { updatePlane(img, time); } - if (_programIsDirty) { + if (_shader->isDirty()) _shader->rebuildFromFile(); - _programIsDirty = false; - } if (_textureIsDirty) { loadTexture(); diff --git a/src/rendering/renderablesphere.cpp b/src/rendering/renderablesphere.cpp index 9cc8a97b1e..e8bfd3ce96 100644 --- a/src/rendering/renderablesphere.cpp +++ b/src/rendering/renderablesphere.cpp @@ -59,7 +59,6 @@ RenderableSphere::RenderableSphere(const ghoul::Dictionary& dictionary) , _shader(nullptr) , _texture(nullptr) , _sphere(nullptr) - , _programIsDirty(false) , _sphereIsDirty(false) { std::string path; @@ -130,7 +129,6 @@ bool RenderableSphere::initialize() { "${SHADERS}/modules/sphere/sphere_fs.glsl"); if (!_shader) return false; - _shader->setProgramObjectCallback([&](ghoul::opengl::ProgramObject*){ _programIsDirty = true; }); loadTexture(); @@ -172,10 +170,8 @@ void RenderableSphere::render(const RenderData& data) { } void RenderableSphere::update(const UpdateData& data) { - if (_programIsDirty) { + if (_shader->isDirty()) _shader->rebuildFromFile(); - _programIsDirty = false; - } if (_sphereIsDirty) { delete _sphere; diff --git a/src/rendering/renderabletrail.cpp b/src/rendering/renderabletrail.cpp index 04172a4b05..b02ec2d3f2 100644 --- a/src/rendering/renderabletrail.cpp +++ b/src/rendering/renderabletrail.cpp @@ -64,7 +64,6 @@ RenderableTrail::RenderableTrail(const ghoul::Dictionary& dictionary) , _lineWidth("lineWidth", "Line Width", 2.f, 1.f, 20.f) , _showTimestamps("timestamps", "Show Timestamps", false) , _programObject(nullptr) - , _programIsDirty(true) , _successfullDictionaryFetch(true) , _vaoID(0) , _vBufferID(0) @@ -119,7 +118,6 @@ bool RenderableTrail::initialize() { "${SHADERS}/modules/trails/ephemeris_fs.glsl"); if (!_programObject) return false; - _programObject->setProgramObjectCallback([&](ghoul::opengl::ProgramObject*){ _programIsDirty = true; }); return completeSuccess; } @@ -195,10 +193,8 @@ void RenderableTrail::update(const UpdateData& data) { return; } - if (_programIsDirty) { + if (_programObject->isDirty()) _programObject->rebuildFromFile(); - _programIsDirty = false; - } double lightTime = 0.0; psc pscPos; diff --git a/src/rendering/stars/renderableconstellationbounds.cpp b/src/rendering/stars/renderableconstellationbounds.cpp index 43a60c05a6..25f87139b4 100644 --- a/src/rendering/stars/renderableconstellationbounds.cpp +++ b/src/rendering/stars/renderableconstellationbounds.cpp @@ -60,7 +60,6 @@ RenderableConstellationBounds::RenderableConstellationBounds( : Renderable(dictionary) , _vertexFilename("") , _constellationFilename("") - , _programIsDirty(false) , _distance("distance", "Distance to the celestial Sphere", 15.f, 0.f, 30.f) , _constellationSelection("constellationSelection", "Constellation Selection") , _originReferenceFrame("") @@ -96,7 +95,6 @@ bool RenderableConstellationBounds::initialize() { "${SHADERS}/modules/constellationbounds/constellationbounds_fs.glsl"); if (!_program) return false; - _program->setProgramObjectCallback([&](ghoul::opengl::ProgramObject*){ this->_programIsDirty = true; }); bool loadSuccess = loadVertexFile(); if (!loadSuccess) @@ -178,10 +176,8 @@ void RenderableConstellationBounds::render(const RenderData& data) { } void RenderableConstellationBounds::update(const UpdateData& data) { - if (_programIsDirty) { + if (_program->isDirty()) _program->rebuildFromFile(); - _programIsDirty = false; - } SpiceManager::ref().getPositionTransformMatrix( _originReferenceFrame, diff --git a/src/rendering/stars/renderablestars.cpp b/src/rendering/stars/renderablestars.cpp index fc69275e71..4b0532fc90 100644 --- a/src/rendering/stars/renderablestars.cpp +++ b/src/rendering/stars/renderablestars.cpp @@ -90,7 +90,6 @@ RenderableStars::RenderableStars(const ghoul::Dictionary& dictionary) , _scaleFactor("scaleFactor", "Scale Factor", 5.f, 0.f, 10.f) , _minBillboardSize("minBillboardSize", "Min Billboard Size", 15.f, 1.f, 100.f) , _program(nullptr) - , _programIsDirty(false) , _speckFile("") , _nValuesPerStar(0) , _vao(0) @@ -151,7 +150,6 @@ bool RenderableStars::initialize() { "${SHADERS}/modules/stars/star_ge.glsl"); if (!_program) return false; - _program->setProgramObjectCallback([&](ghoul::opengl::ProgramObject*){ _programIsDirty = true; }); completeSuccess &= loadData(); completeSuccess &= (_pointSpreadFunctionTexture != nullptr); @@ -227,10 +225,9 @@ void RenderableStars::render(const RenderData& data) { } void RenderableStars::update(const UpdateData& data) { - if (_programIsDirty) { + if (_program->isDirty()) { _program->rebuildFromFile(); _dataIsDirty = true; - _programIsDirty = false; } if (_dataIsDirty) { diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 3ce1d2f7ab..7d26ec72f9 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -184,15 +184,6 @@ bool Scene::initialize() { _programs.push_back(tmpProgram); OsEng.ref().configurationManager()->setValue("fboPassProgram", tmpProgram); - // projection program - tmpProgram = ProgramObject::Build("projectiveProgram", - "${SHADERS}/projectiveTexture_vs.glsl", - "${SHADERS}/projectiveTexture_fs.glsl"); - if (!tmpProgram) return false; - tmpProgram->setProgramObjectCallback(cb); - _programs.push_back(tmpProgram); - OsEng.ref().configurationManager()->setValue("projectiveProgram", tmpProgram); - // pscstandard tmpProgram = ProgramObject::Build("pscstandard", "${SHADERS}/pscstandard_vs.glsl", From b8cd01ea4ee95cec55bf13f0d9b5f87f5a00e453 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 20 May 2015 11:18:14 +0200 Subject: [PATCH 023/329] Removing ghosting from renderablemodel Organizing shaders --- .../rendering/model/renderablemodel.h | 2 +- shaders/{ => modules/model}/model_fs.glsl | 0 shaders/{ => modules/model}/model_vs.glsl | 0 shaders/{ => modules/projection}/fov_fs.glsl | 0 shaders/{ => modules/projection}/fov_vs.glsl | 0 src/rendering/model/renderablemodel.cpp | 57 +++++++++++-------- src/scene/scene.cpp | 20 ------- 7 files changed, 33 insertions(+), 46 deletions(-) rename shaders/{ => modules/model}/model_fs.glsl (100%) rename shaders/{ => modules/model}/model_vs.glsl (100%) rename shaders/{ => modules/projection}/fov_fs.glsl (100%) rename shaders/{ => modules/projection}/fov_vs.glsl (100%) diff --git a/include/openspace/rendering/model/renderablemodel.h b/include/openspace/rendering/model/renderablemodel.h index 4ec28fd212..968f27a149 100644 --- a/include/openspace/rendering/model/renderablemodel.h +++ b/include/openspace/rendering/model/renderablemodel.h @@ -71,7 +71,7 @@ private: std::string _destination; std::string _target; - bool _isGhost; + //bool _isGhost; int _frameCount; psc _sunPosition; diff --git a/shaders/model_fs.glsl b/shaders/modules/model/model_fs.glsl similarity index 100% rename from shaders/model_fs.glsl rename to shaders/modules/model/model_fs.glsl diff --git a/shaders/model_vs.glsl b/shaders/modules/model/model_vs.glsl similarity index 100% rename from shaders/model_vs.glsl rename to shaders/modules/model/model_vs.glsl diff --git a/shaders/fov_fs.glsl b/shaders/modules/projection/fov_fs.glsl similarity index 100% rename from shaders/fov_fs.glsl rename to shaders/modules/projection/fov_fs.glsl diff --git a/shaders/fov_vs.glsl b/shaders/modules/projection/fov_vs.glsl similarity index 100% rename from shaders/fov_vs.glsl rename to shaders/modules/projection/fov_vs.glsl diff --git a/src/rendering/model/renderablemodel.cpp b/src/rendering/model/renderablemodel.cpp index a68e2d5727..6ef0fdb0b1 100644 --- a/src/rendering/model/renderablemodel.cpp +++ b/src/rendering/model/renderablemodel.cpp @@ -54,7 +54,7 @@ namespace { const std::string keyStart = "StartTime"; const std::string keyEnd = "EndTime"; const std::string keyFading = "Shading.Fadeable"; - const std::string keyGhosting = "Shading.Ghosting"; + //const std::string keyGhosting = "Shading.Ghosting"; } @@ -69,7 +69,7 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) , _texture(nullptr) , _geometry(nullptr) , _alpha(1.f) - , _isGhost(false) + //, _isGhost(false) , _performShading("performShading", "Perform Shading", true) , _frameCount(0) { @@ -115,11 +115,11 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) } addProperty(_performFade); - if (dictionary.hasKeyAndValue(keyGhosting)) { - bool ghosting; - dictionary.getValue(keyGhosting, ghosting); - _isGhost = ghosting; - } + //if (dictionary.hasKeyAndValue(keyGhosting)) { + // bool ghosting; + // dictionary.getValue(keyGhosting, ghosting); + // _isGhost = ghosting; + //} } bool RenderableModel::isReady() const { @@ -131,9 +131,14 @@ bool RenderableModel::isReady() const { bool RenderableModel::initialize() { bool completeSuccess = true; - if (_programObject == nullptr) - completeSuccess - &= OsEng.ref().configurationManager()->getValue("GenericModelShader", _programObject); + if (_programObject == nullptr) { + // NH shader + _programObject = ghoul::opengl::ProgramObject::Build("ModelProgram", + "${SHADERS}/modules/model/model_vs.glsl", + "${SHADERS}/modules/model/model_fs.glsl"); + if (!_programObject) + return false; + } loadTexture(); @@ -219,24 +224,26 @@ void RenderableModel::render(const RenderData& data) { } void RenderableModel::update(const UpdateData& data) { + if (_programObject->isDirty()) + _programObject->rebuildFromFile(); double _time = data.time; double futureTime; - if (_isGhost){ - futureTime = openspace::ImageSequencer2::ref().getNextCaptureTime(); - double remaining = openspace::ImageSequencer2::ref().getNextCaptureTime() - data.time; - double interval = openspace::ImageSequencer2::ref().getIntervalLength(); - double t = 1.f - remaining / openspace::ImageSequencer2::ref().getIntervalLength(); - if (interval > 60) { - if (t < 0.8) - _fading = static_cast(t); - else if (t >= 0.95) - _fading = _fading - 0.5f; - } - else - _fading = 0.f; - _time = futureTime; - } + //if (_isGhost){ + // futureTime = openspace::ImageSequencer2::ref().getNextCaptureTime(); + // double remaining = openspace::ImageSequencer2::ref().getNextCaptureTime() - data.time; + // double interval = openspace::ImageSequencer2::ref().getIntervalLength(); + // double t = 1.f - remaining / openspace::ImageSequencer2::ref().getIntervalLength(); + // if (interval > 60) { + // if (t < 0.8) + // _fading = static_cast(t); + // else if (t >= 0.95) + // _fading = _fading - 0.5f; + // } + // else + // _fading = 0.f; + // _time = futureTime; + //} // set spice-orientation in accordance to timestamp if (!_source.empty()) diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 7d26ec72f9..b71a699eae 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -170,11 +170,6 @@ bool Scene::initialize() { _programUpdateLock.unlock(); }; - // Start Timing for building SceneGraph shaders - typedef std::chrono::high_resolution_clock clock_; - typedef std::chrono::duration > second_; - std::chrono::time_point beginning(clock_::now()); - // fboPassthrough program tmpProgram = ProgramObject::Build("fboPassProgram", "${SHADERS}/fboPass_vs.glsl", @@ -193,16 +188,6 @@ bool Scene::initialize() { _programs.push_back(tmpProgram); OsEng.ref().configurationManager()->setValue("pscShader", tmpProgram); - - // NH shader - tmpProgram = ProgramObject::Build("ModelProgram", - "${SHADERS}/model_vs.glsl", - "${SHADERS}/model_fs.glsl"); - if (!tmpProgram) return false; - tmpProgram->setProgramObjectCallback(cb); - _programs.push_back(tmpProgram); - OsEng.ref().configurationManager()->setValue("GenericModelShader", tmpProgram); - // Night texture program tmpProgram = ProgramObject::Build("nightTextureProgram", "${SHADERS}/nighttexture_vs.glsl", @@ -257,11 +242,6 @@ bool Scene::initialize() { _programs.push_back(tmpProgram); OsEng.ref().configurationManager()->setValue("GridProgram", tmpProgram); - // Done building shaders - double elapsed = std::chrono::duration_cast(clock_::now()-beginning).count(); - LINFO("Time to load scene graph shaders: " << elapsed << " seconds"); - - return true; } From 154a949e8b351bc0c7948b8f0f815c1d5d244c1d Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 20 May 2015 11:28:51 +0200 Subject: [PATCH 024/329] Cleaning up more shaders --- shaders/grid_fs.glsl | 75 --------------------- shaders/grid_vs.glsl | 60 ----------------- src/rendering/renderablefov.cpp | 10 ++- src/rendering/renderableplane.cpp | 14 ++-- src/rendering/renderableplaneprojection.cpp | 9 ++- src/scene/scene.cpp | 36 ---------- 6 files changed, 24 insertions(+), 180 deletions(-) delete mode 100644 shaders/grid_fs.glsl delete mode 100644 shaders/grid_vs.glsl diff --git a/shaders/grid_fs.glsl b/shaders/grid_fs.glsl deleted file mode 100644 index b0f572056d..0000000000 --- a/shaders/grid_fs.glsl +++ /dev/null @@ -1,75 +0,0 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014 * - * * - * 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. * - ****************************************************************************************/ - -#version __CONTEXT__ - -uniform mat4 ViewProjection; -uniform mat4 ModelTransform; -uniform vec4 gridColor; - -in vec2 vs_st; -//in vec3 vs_stp; -in vec4 vs_normal; -in vec4 vs_position; - - -#include "ABuffer/abufferStruct.hglsl" -#include "ABuffer/abufferAddToBuffer.hglsl" -#include "PowerScaling/powerScaling_fs.hglsl" - -// out vec4 diffuse; -void main() -{ - - // set the depth - //gl_FragDepth = depth; - //gl_FragDepth = 0.5; - - // color - // diffuse = texture(texture1, vs_st); - //diffuse = vec4(vs_position.z,0.0, 0.0, 1.0); - // diffuse = vec4(vs_position.xyz * pow(10, vs_position.w), 1.0); - //diffuse = vec4(vs_st, 0.0, 1.0); - //diffuse = vec4(1.0,1.0, 0.0, 1.0); - //diffuse = vec4(depth*5,0.0, 0.0, 1.0); - //diffuse = vec4(vs_position.w,0.0, 0.0, 1.0); - - vec4 diffuse = vec4(0.4,0.4,0.4,1); - /*if( floor(vs_st[0]) == -2){ - diffuse = gridColor*2.f; - }else{ - diffuse = gridColor; - }*/ - diffuse = gridColor; - - - vec4 position = vs_position; - float depth = pscDepth(position); - // gl_FragDepth = depth; - - //ABufferStruct_t frag = createGeometryFragment(vec4(1,0,0,1), position, depth); - ABufferStruct_t frag = createGeometryFragment(diffuse, position, depth); - addToBuffer(frag); - -} \ No newline at end of file diff --git a/shaders/grid_vs.glsl b/shaders/grid_vs.glsl deleted file mode 100644 index 521d167fc7..0000000000 --- a/shaders/grid_vs.glsl +++ /dev/null @@ -1,60 +0,0 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014 * - * * - * 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. * - ****************************************************************************************/ - -#version __CONTEXT__ - -uniform mat4 ViewProjection; -uniform mat4 ModelTransform; -uniform vec4 gridColor; - -layout(location = 0) in vec4 in_position; -//in vec3 in_position; -layout(location = 1) in vec2 in_st; -layout(location = 2) in vec3 in_normal; - -out vec2 vs_st; -out vec3 vs_stp; -out vec4 vs_normal; -out vec4 vs_position; - -#include "PowerScaling/powerScaling_vs.hglsl" - -void main() -{ - // set variables - vs_st = in_st; - //vs_stp = in_position.xyz; - vs_normal = normalize(ModelTransform * vec4(in_normal,0)); - - - vec4 tmp = in_position; - - // this is wrong for the normal. The normal transform is the transposed inverse of the model transform - vs_normal = normalize(ModelTransform * vec4(in_normal,0)); - - vec4 position = pscTransform(tmp, ModelTransform); - vs_position = tmp; - position = ViewProjection * position; - gl_Position = z_normalization(position); -} \ No newline at end of file diff --git a/src/rendering/renderablefov.cpp b/src/rendering/renderablefov.cpp index 808b52987c..fa2d13d451 100644 --- a/src/rendering/renderablefov.cpp +++ b/src/rendering/renderablefov.cpp @@ -143,9 +143,13 @@ RenderableFov::~RenderableFov() { bool RenderableFov::initialize() { bool completeSuccess = true; - if (_programObject == nullptr) - completeSuccess &= OsEng.ref().configurationManager()->getValue("FovProgram", _programObject); - completeSuccess &= OsEng.ref().configurationManager()->getValue("EphemerisProgram", _programObject); + if (_programObject == nullptr) { + _programObject = ghoul::opengl::ProgramObject::Build("FovProgram", + "${SHADERS}/modules/projection/fov_vs.glsl", + "${SHADERS}/modules/projection/fov_fs.glsl"); + if (!_programObject) + return false; + } allocateData(); sendToGPU(); diff --git a/src/rendering/renderableplane.cpp b/src/rendering/renderableplane.cpp index 64badebdde..816f394e26 100644 --- a/src/rendering/renderableplane.cpp +++ b/src/rendering/renderableplane.cpp @@ -125,10 +125,16 @@ bool RenderablePlane::initialize() { glGenBuffers(1, &_vertexPositionBuffer); // generate buffer createPlane(); - if (_shader == nullptr) - OsEng.ref().configurationManager()->getValue("planeProgram", _shader); - - loadTexture(); + if (_shader == nullptr) { + // Plane Program + _shader = ghoul::opengl::ProgramObject::Build("PlaneProgram", + "${SHADERS}/modules/plane/plane_vs.glsl", + "${SHADERS}/modules/plane/plane_fs.glsl"); + if (!_shader) + return false; + } + + loadTexture(); return isReady(); } diff --git a/src/rendering/renderableplaneprojection.cpp b/src/rendering/renderableplaneprojection.cpp index ff9cb2fd47..0351e61d44 100644 --- a/src/rendering/renderableplaneprojection.cpp +++ b/src/rendering/renderableplaneprojection.cpp @@ -92,8 +92,13 @@ bool RenderablePlaneProjection::initialize() { glGenBuffers(1, &_vertexPositionBuffer); // generate buffer // Plane program - if (_shader == nullptr) - OsEng.ref().configurationManager()->getValue("imagePlaneProgram", _shader); + if (_shader == nullptr) { + // Image Plane Program + _shader = ghoul::opengl::ProgramObject::Build("ImagePlaneProgram", + "${SHADERS}/modules/imageplane/imageplane_vs.glsl", + "${SHADERS}/modules/imageplane/imageplane_fs.glsl"); + if (!_shader) return false; + } setTarget("JUPITER"); loadTexture(); diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index b71a699eae..000a70b2dd 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -197,33 +197,6 @@ bool Scene::initialize() { _programs.push_back(tmpProgram); OsEng.ref().configurationManager()->setValue("nightTextureProgram", tmpProgram); - // Fov Program - tmpProgram = ProgramObject::Build("FovProgram", - "${SHADERS}/fov_vs.glsl", - "${SHADERS}/fov_fs.glsl"); - if (!tmpProgram) return false; - tmpProgram->setProgramObjectCallback(cb); - _programs.push_back(tmpProgram); - OsEng.ref().configurationManager()->setValue("FovProgram", tmpProgram); - - // Plane Program - tmpProgram = ProgramObject::Build("planeProgram", - "${SHADERS}/modules/plane/plane_vs.glsl", - "${SHADERS}/modules/plane/plane_fs.glsl"); - if (!tmpProgram) return false; - tmpProgram->setProgramObjectCallback(cb); - _programs.push_back(tmpProgram); - OsEng.ref().configurationManager()->setValue("planeProgram", tmpProgram); - - // Image Plane Program - tmpProgram = ProgramObject::Build("imagePlaneProgram", - "${SHADERS}/modules/imageplane/imageplane_vs.glsl", - "${SHADERS}/modules/imageplane/imageplane_fs.glsl"); - if (!tmpProgram) return false; - tmpProgram->setProgramObjectCallback(cb); - _programs.push_back(tmpProgram); - OsEng.ref().configurationManager()->setValue("imagePlaneProgram", tmpProgram); - // RaycastProgram tmpProgram = ProgramObject::Build("RaycastProgram", "${SHADERS}/exitpoints.vert", @@ -233,15 +206,6 @@ bool Scene::initialize() { _programs.push_back(tmpProgram); OsEng.ref().configurationManager()->setValue("RaycastProgram", tmpProgram); - // Grid program - tmpProgram = ProgramObject::Build("Grid", - "${SHADERS}/grid_vs.glsl", - "${SHADERS}/grid_fs.glsl"); - if (!tmpProgram) return false; - tmpProgram->setProgramObjectCallback(cb); - _programs.push_back(tmpProgram); - OsEng.ref().configurationManager()->setValue("GridProgram", tmpProgram); - return true; } From 2bb2b906ff9ffbe52f404d3f0150fec014b52317 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 20 May 2015 12:46:29 +0200 Subject: [PATCH 025/329] Outsource Lua functions into their own inline files --- src/interaction/interactionhandler.cpp | 196 +----------------- src/interaction/interactionhandler_lua.inl | 221 +++++++++++++++++++++ src/interaction/luaconsole.cpp | 48 +---- src/interaction/luaconsole_lua.inl | 73 +++++++ src/rendering/renderengine.cpp | 134 +------------ src/rendering/renderengine_lua.inl | 154 ++++++++++++++ src/scene/scene.cpp | 92 +-------- src/scene/scene_lua.inl | 112 +++++++++++ src/scripting/scriptengine.cpp | 128 +----------- src/scripting/scriptengine_lua.inl | 153 ++++++++++++++ src/util/time.cpp | 130 +----------- src/util/time_lua.inl | 154 ++++++++++++++ 12 files changed, 879 insertions(+), 716 deletions(-) create mode 100644 src/interaction/interactionhandler_lua.inl create mode 100644 src/interaction/luaconsole_lua.inl create mode 100644 src/rendering/renderengine_lua.inl create mode 100644 src/scene/scene_lua.inl create mode 100644 src/scripting/scriptengine_lua.inl create mode 100644 src/util/time_lua.inl diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index f51c9f8b7e..f3d702cb4c 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -182,202 +182,10 @@ namespace { } } +#include "interactionhandler_lua.inl" + namespace openspace { -namespace luascriptfunctions { - -/** - * \ingroup LuaScripts - * setOrigin(): - * Set the origin of the camera - */ -int setOrigin(lua_State* L) { - using ghoul::lua::luaTypeToString; - const std::string _loggerCat = "lua.setOrigin"; - - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - const int type = lua_type(L, -1); - if (type != LUA_TSTRING) - return luaL_error(L, "Expected string, got %i", type); - - std::string s = luaL_checkstring(L, -1); - - SceneGraphNode* node = sceneGraphNode(s); - if (!node) { - LWARNING("Could not find a node in scenegraph called '" << s <<"'"); - return 0; - } - - OsEng.interactionHandler()->setFocusNode(node); - - return 0; -} - -/** -* \ingroup LuaScripts -* bindKey(): -* Binds a key to Lua command -*/ -int bindKey(lua_State* L) { - using ghoul::lua::luaTypeToString; - const std::string _loggerCat = "lua.bindKey"; - - int nArguments = lua_gettop(L); - if (nArguments != 2) - return luaL_error(L, "Expected %i arguments, got %i", 2, nArguments); - - - std::string key = luaL_checkstring(L, -2); - std::string command = luaL_checkstring(L, -1); - - if (command.empty()) - return luaL_error(L, "Command string is empty"); - - int iKey = stringToKey(key); - - if (iKey == SGCT_KEY_UNKNOWN) { - LERROR("Could not find key '"<< key <<"'"); - return 0; - } - - - OsEng.interactionHandler()->bindKey(iKey, command); - - return 0; -} - -/** -* \ingroup LuaScripts -* clearKeys(): -* Clears all key bindings -*/ -int clearKeys(lua_State* L) { - using ghoul::lua::luaTypeToString; - const std::string _loggerCat = "lua.clearKeys"; - - int nArguments = lua_gettop(L); - if (nArguments != 0) - return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); - - OsEng.interactionHandler()->resetKeyBindings(); - - return 0; -} - -/** -* \ingroup LuaScripts -* dt(bool): -* Get current frame time -*/ -int dt(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 0) - return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); - - lua_pushnumber(L,OsEng.interactionHandler()->deltaTime()); - return 1; -} - -/** -* \ingroup LuaScripts -* distance(double, double): -* Change distance to origin -*/ -int distance(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 2) - return luaL_error(L, "Expected %i arguments, got %i", 2, nArguments); - - double d1 = luaL_checknumber(L, -2); - double d2 = luaL_checknumber(L, -1); - PowerScaledScalar dist(static_cast(d1), static_cast(d2)); - OsEng.interactionHandler()->distanceDelta(dist); - return 0; -} - -/** - * \ingroup LuaScripts - * setInteractionSensitivity(double): - * Changes the global interaction sensitivity to the passed value - */ -int setInteractionSensitivity(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - float sensitivity = static_cast(luaL_checknumber(L, -1)); - OsEng.interactionHandler()->setInteractionSensitivity(sensitivity); - return 0; -} - -/** - * \ingroup LuaScripts - * interactionSensitivity(): - * Returns the current, global interaction sensitivity - */ -int interactionSensitivity(lua_State* L) { - float sensitivity = OsEng.interactionHandler()->interactionSensitivity(); - lua_pushnumber(L, sensitivity); - return 1; -} - -/** - * \ingroup LuaScripts - * setInvertRoll(bool): - * Determines if the roll movement is inverted - */ -int setInvertRoll(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - bool invert = lua_toboolean(L, -1) == 1; - OsEng.interactionHandler()->setInvertRoll(invert); - return 0; -} - -/** - * \ingroup LuaScripts - * invertRoll(): - * Returns the current setting for inversion of roll movement - */ -int invertRoll(lua_State* L) { - bool invert = OsEng.interactionHandler()->invertRoll(); - lua_pushboolean(L, invert); - return 1; -} - -/** - * \ingroup LuaScripts - * setInvertRotation(bool): - * Determines if the rotation movement is inverted - */ -int setInvertRotation(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - bool invert = lua_toboolean(L, -1) == 1; - OsEng.interactionHandler()->setInvertRotation(invert); - return 0; -} - -/** - * \ingroup LuaScripts - * invertRotation(): - * Returns the current setting for inversion of rotation movement - */ -int invertRotation(lua_State* L) { - bool invert = OsEng.interactionHandler()->invertRotation(); - lua_pushboolean(L, invert); - return 1; -} - -} // namespace luascriptfunctions - //InteractionHandler::InteractionHandler() { // // initiate pointers // _camera = nullptr; diff --git a/src/interaction/interactionhandler_lua.inl b/src/interaction/interactionhandler_lua.inl new file mode 100644 index 0000000000..ee87c72341 --- /dev/null +++ b/src/interaction/interactionhandler_lua.inl @@ -0,0 +1,221 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * 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. * + ****************************************************************************************/ + +namespace openspace { + +namespace luascriptfunctions { + +/** + * \ingroup LuaScripts + * setOrigin(): + * Set the origin of the camera + */ +int setOrigin(lua_State* L) { + using ghoul::lua::luaTypeToString; + const std::string _loggerCat = "lua.setOrigin"; + + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + + const int type = lua_type(L, -1); + if (type != LUA_TSTRING) + return luaL_error(L, "Expected string, got %i", type); + + std::string s = luaL_checkstring(L, -1); + + SceneGraphNode* node = sceneGraphNode(s); + if (!node) { + LWARNING("Could not find a node in scenegraph called '" << s <<"'"); + return 0; + } + + OsEng.interactionHandler()->setFocusNode(node); + + return 0; +} + +/** +* \ingroup LuaScripts +* bindKey(): +* Binds a key to Lua command +*/ +int bindKey(lua_State* L) { + using ghoul::lua::luaTypeToString; + const std::string _loggerCat = "lua.bindKey"; + + int nArguments = lua_gettop(L); + if (nArguments != 2) + return luaL_error(L, "Expected %i arguments, got %i", 2, nArguments); + + + std::string key = luaL_checkstring(L, -2); + std::string command = luaL_checkstring(L, -1); + + if (command.empty()) + return luaL_error(L, "Command string is empty"); + + int iKey = stringToKey(key); + + if (iKey == SGCT_KEY_UNKNOWN) { + LERROR("Could not find key '"<< key <<"'"); + return 0; + } + + + OsEng.interactionHandler()->bindKey(iKey, command); + + return 0; +} + +/** +* \ingroup LuaScripts +* clearKeys(): +* Clears all key bindings +*/ +int clearKeys(lua_State* L) { + using ghoul::lua::luaTypeToString; + const std::string _loggerCat = "lua.clearKeys"; + + int nArguments = lua_gettop(L); + if (nArguments != 0) + return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); + + OsEng.interactionHandler()->resetKeyBindings(); + + return 0; +} + +/** +* \ingroup LuaScripts +* dt(bool): +* Get current frame time +*/ +int dt(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 0) + return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); + + lua_pushnumber(L,OsEng.interactionHandler()->deltaTime()); + return 1; +} + +/** +* \ingroup LuaScripts +* distance(double, double): +* Change distance to origin +*/ +int distance(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 2) + return luaL_error(L, "Expected %i arguments, got %i", 2, nArguments); + + double d1 = luaL_checknumber(L, -2); + double d2 = luaL_checknumber(L, -1); + PowerScaledScalar dist(static_cast(d1), static_cast(d2)); + OsEng.interactionHandler()->distanceDelta(dist); + return 0; +} + +/** + * \ingroup LuaScripts + * setInteractionSensitivity(double): + * Changes the global interaction sensitivity to the passed value + */ +int setInteractionSensitivity(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + + float sensitivity = static_cast(luaL_checknumber(L, -1)); + OsEng.interactionHandler()->setInteractionSensitivity(sensitivity); + return 0; +} + +/** + * \ingroup LuaScripts + * interactionSensitivity(): + * Returns the current, global interaction sensitivity + */ +int interactionSensitivity(lua_State* L) { + float sensitivity = OsEng.interactionHandler()->interactionSensitivity(); + lua_pushnumber(L, sensitivity); + return 1; +} + +/** + * \ingroup LuaScripts + * setInvertRoll(bool): + * Determines if the roll movement is inverted + */ +int setInvertRoll(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + + bool invert = lua_toboolean(L, -1) == 1; + OsEng.interactionHandler()->setInvertRoll(invert); + return 0; +} + +/** + * \ingroup LuaScripts + * invertRoll(): + * Returns the current setting for inversion of roll movement + */ +int invertRoll(lua_State* L) { + bool invert = OsEng.interactionHandler()->invertRoll(); + lua_pushboolean(L, invert); + return 1; +} + +/** + * \ingroup LuaScripts + * setInvertRotation(bool): + * Determines if the rotation movement is inverted + */ +int setInvertRotation(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + + bool invert = lua_toboolean(L, -1) == 1; + OsEng.interactionHandler()->setInvertRotation(invert); + return 0; +} + +/** + * \ingroup LuaScripts + * invertRotation(): + * Returns the current setting for inversion of rotation movement + */ +int invertRotation(lua_State* L) { + bool invert = OsEng.interactionHandler()->invertRotation(); + lua_pushboolean(L, invert); + return 1; +} + +} // namespace luascriptfunctions + +} // namespace openspace diff --git a/src/interaction/luaconsole.cpp b/src/interaction/luaconsole.cpp index 59387bf8c3..920d7a7f24 100644 --- a/src/interaction/luaconsole.cpp +++ b/src/interaction/luaconsole.cpp @@ -44,54 +44,10 @@ namespace { const int NoAutoComplete = -1; } +#include "luaconsole_lua.inl" + namespace openspace { -namespace luascriptfunctions { - -/** - * \ingroup LuaScripts - * show(): - * Shows the console - */ -int show(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 0) - return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); - - OsEng.console()->setVisible(true); - return 0; -} - -/** - * \ingroup LuaScripts - * hide(): - * Hides the console - */ -int hide(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 0) - return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); - - OsEng.console()->setVisible(false); - return 0; -} - -/** - * \ingroup LuaScripts - * toggle(): - * Toggles the console - */ -int toggle(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 0) - return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); - - OsEng.console()->toggleVisibility(); - return 0; -} - -} - LuaConsole::LuaConsole() : _inputPosition(0) , _activeCommand(0) diff --git a/src/interaction/luaconsole_lua.inl b/src/interaction/luaconsole_lua.inl new file mode 100644 index 0000000000..368855d411 --- /dev/null +++ b/src/interaction/luaconsole_lua.inl @@ -0,0 +1,73 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * 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. * + ****************************************************************************************/ + +namespace openspace { + +namespace luascriptfunctions { + +/** + * \ingroup LuaScripts + * show(): + * Shows the console + */ +int show(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 0) + return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); + + OsEng.console()->setVisible(true); + return 0; +} + +/** + * \ingroup LuaScripts + * hide(): + * Hides the console + */ +int hide(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 0) + return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); + + OsEng.console()->setVisible(false); + return 0; +} + +/** + * \ingroup LuaScripts + * toggle(): + * Toggles the console + */ +int toggle(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 0) + return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); + + OsEng.console()->toggleVisibility(); + return 0; +} + +} // namespace luascriptfunctions + +} // namespace openspace diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index d0e2392256..354c40088b 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -44,7 +44,6 @@ #include #include #include -#include #include #include @@ -70,11 +69,7 @@ #define ABUFFER_FIXED 2 #define ABUFFER_DYNAMIC 3 -//#ifdef __APPLE__ -//#define ABUFFER_IMPLEMENTATION ABUFFER_FRAMEBUFFER -//#else -//#define ABUFFER_IMPLEMENTATION ABUFFER_SINGLE_LINKED -//#endif +#include "renderengine_lua.inl" namespace { const std::string _loggerCat = "RenderEngine"; @@ -92,133 +87,6 @@ namespace openspace { const std::string RenderEngine::PerformanceMeasurementSharedData = "OpenSpacePerformanceMeasurementSharedData"; -namespace luascriptfunctions { - -int changeCoordinateSystem(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - std::string newCenter = std::string(lua_tostring(L, -1)); - OsEng.renderEngine()->changeViewPoint(newCenter); - return 1; -} - -/** - * \ingroup LuaScripts - * takeScreenshot(): - * Save the rendering to an image file - */ -int takeScreenshot(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 0) - return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); - OsEng.renderEngine()->takeScreenshot(); - return 0; -} - -/** -* \ingroup LuaScripts -* visualizeABuffer(bool): -* Toggle the visualization of the ABuffer -*/ -int visualizeABuffer(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - const int type = lua_type(L, -1); - if (type != LUA_TBOOLEAN) - return luaL_error(L, "Expected argument of type 'bool'"); - bool b = lua_toboolean(L, -1) != 0; - OsEng.renderEngine()->toggleVisualizeABuffer(b); - return 0; -} - -/** -* \ingroup LuaScripts -* visualizeABuffer(bool): -* Toggle the visualization of the ABuffer -*/ -int showRenderInformation(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - const int type = lua_type(L, -1); - if (type != LUA_TBOOLEAN) - return luaL_error(L, "Expected argument of type 'bool'"); - bool b = lua_toboolean(L, -1) != 0; - OsEng.renderEngine()->toggleInfoText(b); - return 0; -} - -/** - * \ingroup LuaScripts - * showSGCTRenderStatistics(bool): - * Set the rendering of the SGCTRenderStatistics - */ -int showSGCTRenderStatistics(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - const int type = lua_type(L, -1); - if (type != LUA_TBOOLEAN) - return luaL_error(L, "Expected argument of type 'bool'"); - bool b = lua_toboolean(L, -1) != 0; - OsEng.renderEngine()->setSGCTRenderStatistics(b); - return 0; -} - -/** -* \ingroup LuaScripts -* visualizeABuffer(bool): -* Toggle the visualization of the ABuffer -*/ -int setPerformanceMeasurement(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - bool b = lua_toboolean(L, -1) != 0; - OsEng.renderEngine()->setPerformanceMeasurements(b); - return 0; -} - -/** -* \ingroup LuaScripts -* fadeIn(float): -* start a global fadein over (float) seconds -*/ -int fadeIn(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - double t = luaL_checknumber(L, -1); - - OsEng.renderEngine()->startFading(1, static_cast(t)); - return 0; -} -/** -* \ingroup LuaScripts -* fadeIn(float): -* start a global fadeout over (float) seconds -*/ -int fadeOut(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - double t = luaL_checknumber(L, -1); - - OsEng.renderEngine()->startFading(-1, static_cast(t)); - return 0; -} - -} // namespace luascriptfunctions - RenderEngine::RenderEngine() : _mainCamera(nullptr) , _sceneGraph(nullptr) diff --git a/src/rendering/renderengine_lua.inl b/src/rendering/renderengine_lua.inl new file mode 100644 index 0000000000..eb664afee7 --- /dev/null +++ b/src/rendering/renderengine_lua.inl @@ -0,0 +1,154 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * 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. * + ****************************************************************************************/ + +namespace openspace { + +namespace luascriptfunctions { + +int changeCoordinateSystem(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + + std::string newCenter = std::string(lua_tostring(L, -1)); + OsEng.renderEngine()->changeViewPoint(newCenter); + return 1; +} + +/** + * \ingroup LuaScripts + * takeScreenshot(): + * Save the rendering to an image file + */ +int takeScreenshot(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 0) + return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); + OsEng.renderEngine()->takeScreenshot(); + return 0; +} + +/** +* \ingroup LuaScripts +* visualizeABuffer(bool): +* Toggle the visualization of the ABuffer +*/ +int visualizeABuffer(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + + const int type = lua_type(L, -1); + if (type != LUA_TBOOLEAN) + return luaL_error(L, "Expected argument of type 'bool'"); + bool b = lua_toboolean(L, -1) != 0; + OsEng.renderEngine()->toggleVisualizeABuffer(b); + return 0; +} + +/** +* \ingroup LuaScripts +* visualizeABuffer(bool): +* Toggle the visualization of the ABuffer +*/ +int showRenderInformation(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + + const int type = lua_type(L, -1); + if (type != LUA_TBOOLEAN) + return luaL_error(L, "Expected argument of type 'bool'"); + bool b = lua_toboolean(L, -1) != 0; + OsEng.renderEngine()->toggleInfoText(b); + return 0; +} + +/** + * \ingroup LuaScripts + * showSGCTRenderStatistics(bool): + * Set the rendering of the SGCTRenderStatistics + */ +int showSGCTRenderStatistics(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + + const int type = lua_type(L, -1); + if (type != LUA_TBOOLEAN) + return luaL_error(L, "Expected argument of type 'bool'"); + bool b = lua_toboolean(L, -1) != 0; + OsEng.renderEngine()->setSGCTRenderStatistics(b); + return 0; +} + +/** +* \ingroup LuaScripts +* visualizeABuffer(bool): +* Toggle the visualization of the ABuffer +*/ +int setPerformanceMeasurement(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + + bool b = lua_toboolean(L, -1) != 0; + OsEng.renderEngine()->setPerformanceMeasurements(b); + return 0; +} + +/** +* \ingroup LuaScripts +* fadeIn(float): +* start a global fadein over (float) seconds +*/ +int fadeIn(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + + double t = luaL_checknumber(L, -1); + + OsEng.renderEngine()->startFading(1, static_cast(t)); + return 0; +} +/** +* \ingroup LuaScripts +* fadeIn(float): +* start a global fadeout over (float) seconds +*/ +int fadeOut(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + + double t = luaL_checknumber(L, -1); + + OsEng.renderEngine()->startFading(-1, static_cast(t)); + return 0; +} + +} // namespace luascriptfunctions + +}// namespace openspace diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 000a70b2dd..4486a34668 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -53,6 +53,8 @@ #include #include +#include "scene_lua.inl" + namespace { const std::string _loggerCat = "SceneGraph"; const std::string _moduleExtension = ".mod"; @@ -62,95 +64,7 @@ namespace { namespace openspace { -namespace luascriptfunctions { - -/** - * \ingroup LuaScripts - * setPropertyValue(string, *): - * Sets the property identified by the URI in the first argument to the value passed to - * the second argument. The type of the second argument is arbitrary, but it must agree - * with the type the denoted Property expects - */ -int property_setValue(lua_State* L) { - static const std::string _loggerCat = "property_setValue"; - using ghoul::lua::errorLocation; - using ghoul::lua::luaTypeToString; - - int nArguments = lua_gettop(L); - SCRIPT_CHECK_ARGUMENTS(L, 2, nArguments); - - std::string uri = luaL_checkstring(L, -2); - const int type = lua_type(L, -1); - - openspace::properties::Property* prop = property(uri); - if (!prop) { - LERROR(errorLocation(L) << "Property with URI '" << uri << "' was not found"); - return 0; - } - - - if (type != prop->typeLua()) { - LERROR(errorLocation(L) << "Property '" << uri << - "' does not accept input of type '" << luaTypeToString(type) << - "'. Requested type: '" << luaTypeToString(prop->typeLua()) << "'"); - return 0; - } - else - prop->setLua(L); - - return 0; -} - -/** - * \ingroup LuaScripts - * getPropertyValue(string): - * Returns the value of the property identified by the passed URI as a Lua object that can - * be passed to the setPropertyValue method. - */ -int property_getValue(lua_State* L) { - static const std::string _loggerCat = "property_getValue"; - using ghoul::lua::errorLocation; - - int nArguments = lua_gettop(L); - SCRIPT_CHECK_ARGUMENTS(L, 1, nArguments); - - std::string uri = luaL_checkstring(L, -1); - - openspace::properties::Property* prop = property(uri); - if (!prop) { - LERROR(errorLocation(L) << "Property with URL '" << uri << "' was not found"); - return 0; - } - else - prop->getLua(L); - return 1; -} - -/** - * \ingroup LuaScripts - * getPropertyValue(string): - * Returns the value of the property identified by the passed URI as a Lua object that can - * be passed to the setPropertyValue method. - */ -int loadScene(lua_State* L) { - static const std::string _loggerCat = "loadScene"; - - int nArguments = lua_gettop(L); - SCRIPT_CHECK_ARGUMENTS(L, 1, nArguments); - - std::string sceneFile = luaL_checkstring(L, -1); - - OsEng.renderEngine()->scene()->scheduleLoadSceneFile(sceneFile); - - return 0; -} - -} // namespace luascriptfunctions - -Scene::Scene() - : _focus(SceneGraphNode::RootNodeName) -{ -} +Scene::Scene() : _focus(SceneGraphNode::RootNodeName) {} Scene::~Scene() { deinitialize(); diff --git a/src/scene/scene_lua.inl b/src/scene/scene_lua.inl new file mode 100644 index 0000000000..c08347e1d5 --- /dev/null +++ b/src/scene/scene_lua.inl @@ -0,0 +1,112 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * 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. * + ****************************************************************************************/ + +namespace openspace { + +namespace luascriptfunctions { + +/** + * \ingroup LuaScripts + * setPropertyValue(string, *): + * Sets the property identified by the URI in the first argument to the value passed to + * the second argument. The type of the second argument is arbitrary, but it must agree + * with the type the denoted Property expects + */ +int property_setValue(lua_State* L) { + static const std::string _loggerCat = "property_setValue"; + using ghoul::lua::errorLocation; + using ghoul::lua::luaTypeToString; + + int nArguments = lua_gettop(L); + SCRIPT_CHECK_ARGUMENTS(L, 2, nArguments); + + std::string uri = luaL_checkstring(L, -2); + const int type = lua_type(L, -1); + + openspace::properties::Property* prop = property(uri); + if (!prop) { + LERROR(errorLocation(L) << "Property with URI '" << uri << "' was not found"); + return 0; + } + + + if (type != prop->typeLua()) { + LERROR(errorLocation(L) << "Property '" << uri << + "' does not accept input of type '" << luaTypeToString(type) << + "'. Requested type: '" << luaTypeToString(prop->typeLua()) << "'"); + return 0; + } + else + prop->setLua(L); + + return 0; +} + +/** + * \ingroup LuaScripts + * getPropertyValue(string): + * Returns the value of the property identified by the passed URI as a Lua object that can + * be passed to the setPropertyValue method. + */ +int property_getValue(lua_State* L) { + static const std::string _loggerCat = "property_getValue"; + using ghoul::lua::errorLocation; + + int nArguments = lua_gettop(L); + SCRIPT_CHECK_ARGUMENTS(L, 1, nArguments); + + std::string uri = luaL_checkstring(L, -1); + + openspace::properties::Property* prop = property(uri); + if (!prop) { + LERROR(errorLocation(L) << "Property with URL '" << uri << "' was not found"); + return 0; + } + else + prop->getLua(L); + return 1; +} + +/** + * \ingroup LuaScripts + * getPropertyValue(string): + * Returns the value of the property identified by the passed URI as a Lua object that can + * be passed to the setPropertyValue method. + */ +int loadScene(lua_State* L) { + static const std::string _loggerCat = "loadScene"; + + int nArguments = lua_gettop(L); + SCRIPT_CHECK_ARGUMENTS(L, 1, nArguments); + + std::string sceneFile = luaL_checkstring(L, -1); + + OsEng.renderEngine()->scene()->scheduleLoadSceneFile(sceneFile); + + return 0; +} + +} // namespace luascriptfunctions + +} // namespace openspace diff --git a/src/scripting/scriptengine.cpp b/src/scripting/scriptengine.cpp index 771b00670a..83cc1bc49e 100644 --- a/src/scripting/scriptengine.cpp +++ b/src/scripting/scriptengine.cpp @@ -32,134 +32,10 @@ #include #include +#include "scriptengine_lua.inl" + namespace openspace { -namespace luascriptfunctions { - - int printInternal(ghoul::logging::LogManager::LogLevel level, lua_State* L) { - using ghoul::lua::luaTypeToString; - const std::string _loggerCat = "print"; - - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - const int type = lua_type(L, -1); - switch (type) { - case LUA_TNONE: - case LUA_TLIGHTUSERDATA: - case LUA_TTABLE: - case LUA_TFUNCTION: - case LUA_TUSERDATA: - case LUA_TTHREAD: - LOG(level, "Function parameter was of type '" << - luaTypeToString(type) << "'"); - case LUA_TNIL: - break; - case LUA_TBOOLEAN: - LOG(level, lua_toboolean(L, -1)); - break; - case LUA_TNUMBER: - LOG(level, lua_tonumber(L, -1)); - break; - case LUA_TSTRING: - LOG(level, lua_tostring(L, -1)); - break; - } - return 0; - } - - /** - * \ingroup LuaScripts - * printDebug(*): - * Logs the passed value to the installed LogManager with a LogLevel of 'Debug'. - * For Boolean, numbers, and strings, the internal values are printed, for all other - * types, the type is printed instead - */ - int printDebug(lua_State* L) { - return printInternal(ghoul::logging::LogManager::LogLevel::Debug, L); - } - - /** - * \ingroup LuaScripts - * printInfo(*): - * Logs the passed value to the installed LogManager with a LogLevel of 'Info'. - * For Boolean, numbers, and strings, the internal values are printed, for all other - * types, the type is printed instead - */ - int printInfo(lua_State* L) { - return printInternal(ghoul::logging::LogManager::LogLevel::Info, L); - } - - /** - * \ingroup LuaScripts - * printWarning(*): - * Logs the passed value to the installed LogManager with a LogLevel of 'Warning'. - * For Boolean, numbers, and strings, the internal values are printed, for all other - * types, the type is printed instead - */ - int printWarning(lua_State* L) { - return printInternal(ghoul::logging::LogManager::LogLevel::Warning, L); - } - - /** - * \ingroup LuaScripts - * printError(*): - * Logs the passed value to the installed LogManager with a LogLevel of 'Error'. - * For Boolean, numbers, and strings, the internal values are printed, for all other - * types, the type is printed instead - */ - int printError(lua_State* L) { - return printInternal(ghoul::logging::LogManager::LogLevel::Error, L); - } - - /** - * \ingroup LuaScripts - * printFatal(*): - * Logs the passed value to the installed LogManager with a LogLevel of 'Fatal'. - * For Boolean, numbers, and strings, the internal values are printed, for all other - * types, the type is printed instead - */ - int printFatal(lua_State* L) { - return printInternal(ghoul::logging::LogManager::LogLevel::Fatal, L); - } - - /** - * \ingroup LuaScripts - * absPath(string): - * Passes the argument to FileSystem::absolutePath, which resolves occuring path - * tokens and returns the absolute path. - */ - int absolutePath(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %d arguments, got %d", 1, nArguments); - - std::string path = luaL_checkstring(L, -1); - path = absPath(path); - lua_pushstring(L, path.c_str()); - return 1; - } - - /** - * \ingroup LuaScripts - * setPathToken(string, string): - * Registers the path token provided by the first argument to the path in the second - * argument. If the path token already exists, it will be silently overridden. - */ - int setPathToken(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 2) - return luaL_error(L, "Expected %i arguments, got %i", 2, nArguments); - - std::string pathToken = luaL_checkstring(L, -1); - std::string path = luaL_checkstring(L, -2); - FileSys.registerPathToken(pathToken, path, true); - return 0; - } - -} // namespace luascriptfunctions - namespace scripting { namespace { diff --git a/src/scripting/scriptengine_lua.inl b/src/scripting/scriptengine_lua.inl new file mode 100644 index 0000000000..869b5efb37 --- /dev/null +++ b/src/scripting/scriptengine_lua.inl @@ -0,0 +1,153 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * 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. * + ****************************************************************************************/ + +namespace openspace { + +namespace luascriptfunctions { + + int printInternal(ghoul::logging::LogManager::LogLevel level, lua_State* L) { + using ghoul::lua::luaTypeToString; + const std::string _loggerCat = "print"; + + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + + const int type = lua_type(L, -1); + switch (type) { + case LUA_TNONE: + case LUA_TLIGHTUSERDATA: + case LUA_TTABLE: + case LUA_TFUNCTION: + case LUA_TUSERDATA: + case LUA_TTHREAD: + LOG(level, "Function parameter was of type '" << + luaTypeToString(type) << "'"); + case LUA_TNIL: + break; + case LUA_TBOOLEAN: + LOG(level, lua_toboolean(L, -1)); + break; + case LUA_TNUMBER: + LOG(level, lua_tonumber(L, -1)); + break; + case LUA_TSTRING: + LOG(level, lua_tostring(L, -1)); + break; + } + return 0; + } + + /** + * \ingroup LuaScripts + * printDebug(*): + * Logs the passed value to the installed LogManager with a LogLevel of 'Debug'. + * For Boolean, numbers, and strings, the internal values are printed, for all other + * types, the type is printed instead + */ + int printDebug(lua_State* L) { + return printInternal(ghoul::logging::LogManager::LogLevel::Debug, L); + } + + /** + * \ingroup LuaScripts + * printInfo(*): + * Logs the passed value to the installed LogManager with a LogLevel of 'Info'. + * For Boolean, numbers, and strings, the internal values are printed, for all other + * types, the type is printed instead + */ + int printInfo(lua_State* L) { + return printInternal(ghoul::logging::LogManager::LogLevel::Info, L); + } + + /** + * \ingroup LuaScripts + * printWarning(*): + * Logs the passed value to the installed LogManager with a LogLevel of 'Warning'. + * For Boolean, numbers, and strings, the internal values are printed, for all other + * types, the type is printed instead + */ + int printWarning(lua_State* L) { + return printInternal(ghoul::logging::LogManager::LogLevel::Warning, L); + } + + /** + * \ingroup LuaScripts + * printError(*): + * Logs the passed value to the installed LogManager with a LogLevel of 'Error'. + * For Boolean, numbers, and strings, the internal values are printed, for all other + * types, the type is printed instead + */ + int printError(lua_State* L) { + return printInternal(ghoul::logging::LogManager::LogLevel::Error, L); + } + + /** + * \ingroup LuaScripts + * printFatal(*): + * Logs the passed value to the installed LogManager with a LogLevel of 'Fatal'. + * For Boolean, numbers, and strings, the internal values are printed, for all other + * types, the type is printed instead + */ + int printFatal(lua_State* L) { + return printInternal(ghoul::logging::LogManager::LogLevel::Fatal, L); + } + + /** + * \ingroup LuaScripts + * absPath(string): + * Passes the argument to FileSystem::absolutePath, which resolves occuring path + * tokens and returns the absolute path. + */ + int absolutePath(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %d arguments, got %d", 1, nArguments); + + std::string path = luaL_checkstring(L, -1); + path = absPath(path); + lua_pushstring(L, path.c_str()); + return 1; + } + + /** + * \ingroup LuaScripts + * setPathToken(string, string): + * Registers the path token provided by the first argument to the path in the second + * argument. If the path token already exists, it will be silently overridden. + */ + int setPathToken(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 2) + return luaL_error(L, "Expected %i arguments, got %i", 2, nArguments); + + std::string pathToken = luaL_checkstring(L, -1); + std::string path = luaL_checkstring(L, -2); + FileSys.registerPathToken(pathToken, path, true); + return 0; + } + +} // namespace luascriptfunctions + +} // namespace openspace diff --git a/src/util/time.cpp b/src/util/time.cpp index 4590c08064..8bbf95b81c 100644 --- a/src/util/time.cpp +++ b/src/util/time.cpp @@ -35,140 +35,14 @@ #include #include +#include "time_lua.inl" + namespace { const std::string _loggerCat = "Time"; } namespace openspace { -namespace luascriptfunctions { - -/** - * \ingroup LuaScripts - * setDeltaTime(number): - * Sets the delta time by calling the Time::setDeltaTime method - */ -int time_setDeltaTime(lua_State* L) { - const bool isFunction = (lua_isfunction(L, -1) != 0); - if (isFunction) { - // If the top of the stack is a function, it is ourself - const char* msg = lua_pushfstring(L, "method called without argument"); - return luaL_error(L, "bad argument (%s)", msg); - } - - const bool isNumber = (lua_isnumber(L, -1) != 0); - if (isNumber) { - double value = lua_tonumber(L, -1); - openspace::Time::ref().setDeltaTime(value); - return 0; - } - else { - const char* msg = lua_pushfstring(L, "%s expected, got %s", - lua_typename(L, LUA_TNUMBER), luaL_typename(L, -1)); - return luaL_error(L, "bad argument #%d (%s)", 1, msg); - } - -} - -/** - * \ingroup LuaScripts - * deltaTime(): - * Returns the delta time by calling the Time::deltaTime method - */ -int time_deltaTime(lua_State* L) { - lua_pushnumber(L, openspace::Time::ref().deltaTime()); - return 1; -} - -/** - * \ingroup LuaScripts - * togglePause(): - * Toggles a pause functionm i.e. setting the delta time to 0 and restoring it afterwards - */ -int time_togglePause(lua_State* L) { - openspace::Time::ref().togglePause(); - return 0; -} - -/** - * \ingroup LuaScripts - * togglePause(): - * Toggles a pause functionm i.e. setting the delta time to 0 and restoring it afterwards - */ -int time_setPause(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - bool pause = lua_toboolean(L, -1) == 1; - openspace::Time::ref().setPause(pause); - return 0; -} - - -/** - * \ingroup LuaScripts - * setTime({number, string}): - * Sets the simulation time to the passed value. If the parameter is a number, it is - * interpreted as the number of seconds past the J2000 epoch and the - * Time::setTime(double) method is called. If the parameter is a string, it is - * interpreted as a structured date string and the Time::setTime(std::string) method - * is called - */ -int time_setTime(lua_State* L) { - const bool isFunction = (lua_isfunction(L, -1) != 0); - if (isFunction) { - // If the top of the stack is a function, it is ourself - const char* msg = lua_pushfstring(L, "method called without argument"); - return luaL_error(L, "bad argument (%s)", 1, msg); - } - - const bool isNumber = (lua_isnumber(L, -1) != 0); - const bool isString = (lua_isstring(L, -1) != 0); - if (!isNumber && !isString) { - const char* msg = lua_pushfstring(L, "%s or %s expected, got %s", - lua_typename(L, LUA_TNUMBER), - lua_typename(L, LUA_TSTRING), luaL_typename(L, -1)); - return luaL_error(L, "bad argument #%d (%s)", 1, msg); - } - if (isNumber) { - double value = lua_tonumber(L, -1); - openspace::Time::ref().setTime(value); - return 0; - } - if (isString) { - const char* time = lua_tostring(L, -1); - openspace::Time::ref().setTime(time); - return 0; - } - return 0; -} - -/** - * \ingroup LuaScripts - * currentTime(): - * Returns the current simulation time as the number of seconds past the J2000 epoch. - * It is returned by calling the Time::currentTime method. - */ -int time_currentTime(lua_State* L) { - lua_pushnumber(L, openspace::Time::ref().currentTime()); - return 1; -} - -/** - * \ingroup LuaScripts - * currentTimeUTC(): - * Returns the current simulation time as a structured ISO 8601 string using the UTC - * timezone by calling the Time::currentTimeUTC method - */ -int time_currentTimeUTC(lua_State* L) { - lua_pushstring(L, openspace::Time::ref().currentTimeUTC().c_str()); - return 1; -} - -} // namespace luascriptfunctions - - Time* Time::_instance = nullptr; Time::Time() diff --git a/src/util/time_lua.inl b/src/util/time_lua.inl new file mode 100644 index 0000000000..384da5bec9 --- /dev/null +++ b/src/util/time_lua.inl @@ -0,0 +1,154 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * 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. * + ****************************************************************************************/ + +namespace openspace { + +namespace luascriptfunctions { + +/** + * \ingroup LuaScripts + * setDeltaTime(number): + * Sets the delta time by calling the Time::setDeltaTime method + */ +int time_setDeltaTime(lua_State* L) { + const bool isFunction = (lua_isfunction(L, -1) != 0); + if (isFunction) { + // If the top of the stack is a function, it is ourself + const char* msg = lua_pushfstring(L, "method called without argument"); + return luaL_error(L, "bad argument (%s)", msg); + } + + const bool isNumber = (lua_isnumber(L, -1) != 0); + if (isNumber) { + double value = lua_tonumber(L, -1); + openspace::Time::ref().setDeltaTime(value); + return 0; + } + else { + const char* msg = lua_pushfstring(L, "%s expected, got %s", + lua_typename(L, LUA_TNUMBER), luaL_typename(L, -1)); + return luaL_error(L, "bad argument #%d (%s)", 1, msg); + } + +} + +/** + * \ingroup LuaScripts + * deltaTime(): + * Returns the delta time by calling the Time::deltaTime method + */ +int time_deltaTime(lua_State* L) { + lua_pushnumber(L, openspace::Time::ref().deltaTime()); + return 1; +} + +/** + * \ingroup LuaScripts + * togglePause(): + * Toggles a pause functionm i.e. setting the delta time to 0 and restoring it afterwards + */ +int time_togglePause(lua_State* L) { + openspace::Time::ref().togglePause(); + return 0; +} + +/** + * \ingroup LuaScripts + * togglePause(): + * Toggles a pause functionm i.e. setting the delta time to 0 and restoring it afterwards + */ +int time_setPause(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + + bool pause = lua_toboolean(L, -1) == 1; + openspace::Time::ref().setPause(pause); + return 0; +} + + +/** + * \ingroup LuaScripts + * setTime({number, string}): + * Sets the simulation time to the passed value. If the parameter is a number, it is + * interpreted as the number of seconds past the J2000 epoch and the + * Time::setTime(double) method is called. If the parameter is a string, it is + * interpreted as a structured date string and the Time::setTime(std::string) method + * is called + */ +int time_setTime(lua_State* L) { + const bool isFunction = (lua_isfunction(L, -1) != 0); + if (isFunction) { + // If the top of the stack is a function, it is ourself + const char* msg = lua_pushfstring(L, "method called without argument"); + return luaL_error(L, "bad argument (%s)", 1, msg); + } + + const bool isNumber = (lua_isnumber(L, -1) != 0); + const bool isString = (lua_isstring(L, -1) != 0); + if (!isNumber && !isString) { + const char* msg = lua_pushfstring(L, "%s or %s expected, got %s", + lua_typename(L, LUA_TNUMBER), + lua_typename(L, LUA_TSTRING), luaL_typename(L, -1)); + return luaL_error(L, "bad argument #%d (%s)", 1, msg); + } + if (isNumber) { + double value = lua_tonumber(L, -1); + openspace::Time::ref().setTime(value); + return 0; + } + if (isString) { + const char* time = lua_tostring(L, -1); + openspace::Time::ref().setTime(time); + return 0; + } + return 0; +} + +/** + * \ingroup LuaScripts + * currentTime(): + * Returns the current simulation time as the number of seconds past the J2000 epoch. + * It is returned by calling the Time::currentTime method. + */ +int time_currentTime(lua_State* L) { + lua_pushnumber(L, openspace::Time::ref().currentTime()); + return 1; +} + +/** + * \ingroup LuaScripts + * currentTimeUTC(): + * Returns the current simulation time as a structured ISO 8601 string using the UTC + * timezone by calling the Time::currentTimeUTC method + */ +int time_currentTimeUTC(lua_State* L) { + lua_pushstring(L, openspace::Time::ref().currentTimeUTC().c_str()); + return 1; +} + +} // namespace luascriptfunctions + +} // namespace openspace From 4de4c015d7ecafe424724298e9f0cbf7946593e6 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 20 May 2015 16:32:28 +0200 Subject: [PATCH 026/329] Starting to clean out constants.h file by moving constants into the relevant classes --- .../openspace/engine/configurationmanager.h | 20 +++++++ include/openspace/rendering/renderable.h | 2 - include/openspace/scene/scenegraphnode.h | 4 ++ include/openspace/util/constants.h | 38 +++----------- src/engine/configurationmanager.cpp | 35 ++++++++++--- src/engine/openspaceengine.cpp | 52 ++++++++----------- src/rendering/model/renderablemodel.cpp | 11 ++-- src/rendering/planets/renderableplanet.cpp | 8 ++- .../planets/renderableplanetprojection.cpp | 17 +++--- src/rendering/renderable.cpp | 23 -------- src/rendering/renderableplane.cpp | 2 +- src/rendering/renderableplaneprojection.cpp | 2 +- src/rendering/renderablesphere.cpp | 9 +--- src/rendering/renderablevolumegl.cpp | 6 +-- src/scene/scene.cpp | 29 ++++++----- src/scene/scenegraph.cpp | 33 ++++++------ src/scene/scenegraphnode.cpp | 11 ++-- 17 files changed, 140 insertions(+), 162 deletions(-) diff --git a/include/openspace/engine/configurationmanager.h b/include/openspace/engine/configurationmanager.h index 617c359a64..2013bb3aa6 100644 --- a/include/openspace/engine/configurationmanager.h +++ b/include/openspace/engine/configurationmanager.h @@ -31,6 +31,26 @@ namespace openspace { class ConfigurationManager : public ghoul::Dictionary { public: + static const std::string KeyPaths; + static const std::string KeyCache; + static const std::string KeyCachePath; + static const std::string KeyFonts; + static const std::string KeyConfigSgct; + static const std::string KeyLuaDocumentationType; + static const std::string KeyLuaDocumentationFile; + static const std::string KeyPropertyDocumentationType; + static const std::string KeyPropertyDocumentationFile; + static const std::string KeyConfigScene; + static const std::string KeyEnableGui; + static const std::string KeyStartupScript; + static const std::string KeySettingsScript; + static const std::string KeySpiceTimeKernel; + static const std::string KeySpiceLeapsecondKernel; + static const std::string KeyLogLevel; + static const std::string KeyLogImmediateFlush; + static const std::string KeyLogs; + static const std::string KeyDisableMasterRendering; + bool loadFromFile(const std::string& filename); private: diff --git a/include/openspace/rendering/renderable.h b/include/openspace/rendering/renderable.h index 1149c247a8..6bd501cd26 100644 --- a/include/openspace/rendering/renderable.h +++ b/include/openspace/rendering/renderable.h @@ -78,14 +78,12 @@ public: void setBody(std::string& body); protected: - std::string findPath(const std::string& path); void setPscUniforms(ghoul::opengl::ProgramObject* program, const Camera* camera, const PowerScaledCoordinate& position); private: properties::BoolProperty _enabled; PowerScaledScalar boundingSphere_; - std::string _relativePath; std::string _startTime; std::string _endTime; std::string _targetBody; diff --git a/include/openspace/scene/scenegraphnode.h b/include/openspace/scene/scenegraphnode.h index a27228a3e2..306d8db371 100644 --- a/include/openspace/scene/scenegraphnode.h +++ b/include/openspace/scene/scenegraphnode.h @@ -51,6 +51,10 @@ public: }; static std::string RootNodeName; + + static const std::string KeyName; + static const std::string KeyParentName; + static const std::string KeyDependencies; SceneGraphNode(); ~SceneGraphNode(); diff --git a/include/openspace/util/constants.h b/include/openspace/util/constants.h index e29df4932d..3f2970bb63 100644 --- a/include/openspace/util/constants.h +++ b/include/openspace/util/constants.h @@ -36,37 +36,15 @@ namespace fonts { const std::string keyLight = "Light"; } // namespace fonts -namespace configurationmanager { - const std::string keyPaths = "Paths"; - const std::string keyCache = "CACHE"; - const std::string keyCachePath = keyPaths + "." + keyCache; - const std::string keyFonts = "Fonts"; - const std::string keyConfigSgct = "SGCTConfig"; - const std::string keyLuaDocumentationType = "LuaDocumentationFile.Type"; - const std::string keyLuaDocumentationFile = "LuaDocumentationFile.File"; - const std::string keyPropertyDocumentationType = "PropertyDocumentationFile.Type"; - const std::string keyPropertyDocumentationFile = "PropertyDocumentationFile.File"; - const std::string keyConfigScene = "Scene"; - const std::string keyEnableGui = "EnableGUI"; - const std::string keyStartupScript = "StartupScripts"; - const std::string keySettingsScript = "SettingsScripts"; - const std::string keySpiceTimeKernel = "SpiceKernel.Time"; - const std::string keySpiceLeapsecondKernel = "SpiceKernel.LeapSecond"; - const std::string keyLogLevel = "Logging.LogLevel"; - const std::string keyLogImmediateFlush = "Logging.ImmediateFlush"; - const std::string keyLogs = "Logging.Logs"; - const std::string keyDisableMasterRendering = "DisableRenderingOnMaster"; -} // namespace configurationmanager - namespace scenegraph { - const std::string keyPathScene = "ScenePath"; - const std::string keyCommonFolder = "CommonFolder"; - const std::string keyModules = "Modules"; - const std::string keyCamera = "Camera"; - const std::string keyFocusObject = "Focus"; - const std::string keyPositionObject = "Position"; - const std::string keyViewOffset = "Offset"; - const std::string keyPathModule = "ModulePath"; + // const std::string keyPathScene = "ScenePath"; + //const std::string keyCommonFolder = "CommonFolder"; + // const std::string keyModules = "Modules"; + // const std::string keyCamera = "Camera"; + // const std::string keyFocusObject = "Focus"; + // const std::string keyPositionObject = "Position"; + // const std::string keyViewOffset = "Offset"; + // const std::string keyPathModule = "ModulePath"; } // namespace scenegraph namespace scenegraphnode { diff --git a/src/engine/configurationmanager.cpp b/src/engine/configurationmanager.cpp index a668841252..65f9f26f81 100644 --- a/src/engine/configurationmanager.cpp +++ b/src/engine/configurationmanager.cpp @@ -39,9 +39,28 @@ namespace { namespace openspace { +const std::string ConfigurationManager::KeyPaths = "Paths"; +const std::string ConfigurationManager::KeyCache = "CACHE"; +const std::string ConfigurationManager::KeyCachePath = KeyPaths + "." + KeyCache; +const std::string ConfigurationManager::KeyFonts = "Fonts"; +const std::string ConfigurationManager::KeyConfigSgct = "SGCTConfig"; +const std::string ConfigurationManager::KeyLuaDocumentationType = "LuaDocumentationFile.Type"; +const std::string ConfigurationManager::KeyLuaDocumentationFile = "LuaDocumentationFile.File"; +const std::string ConfigurationManager::KeyPropertyDocumentationType = "PropertyDocumentationFile.Type"; +const std::string ConfigurationManager::KeyPropertyDocumentationFile = "PropertyDocumentationFile.File"; +const std::string ConfigurationManager::KeyConfigScene = "Scene"; +const std::string ConfigurationManager::KeyEnableGui = "EnableGUI"; +const std::string ConfigurationManager::KeyStartupScript = "StartupScripts"; +const std::string ConfigurationManager::KeySettingsScript = "SettingsScripts"; +const std::string ConfigurationManager::KeySpiceTimeKernel = "SpiceKernel.Time"; +const std::string ConfigurationManager::KeySpiceLeapsecondKernel = "SpiceKernel.LeapSecond"; +const std::string ConfigurationManager::KeyLogLevel = "Logging.LogLevel"; +const std::string ConfigurationManager::KeyLogImmediateFlush = "Logging.ImmediateFlush"; +const std::string ConfigurationManager::KeyLogs = "Logging.Logs"; +const std::string ConfigurationManager::KeyDisableMasterRendering = "DisableRenderingOnMaster"; + bool ConfigurationManager::loadFromFile(const std::string& filename) { using ghoul::filesystem::FileSystem; - using constants::configurationmanager::keyPaths; if (!FileSys.fileExists(filename)) { LERROR("Could not find file '" << filename << "'"); return false; @@ -65,9 +84,9 @@ bool ConfigurationManager::loadFromFile(const std::string& filename) { // Register all the paths ghoul::Dictionary dictionary; - const bool success = getValue(keyPaths, dictionary); + const bool success = getValue(KeyPaths, dictionary); if (!success) { - LERROR("Configuration does not contain the key '" << keyPaths << "'"); + LERROR("Configuration does not contain the key '" << KeyPaths << "'"); return false; } @@ -94,17 +113,17 @@ bool ConfigurationManager::loadFromFile(const std::string& filename) { // Remove the Paths dictionary from the configuration manager as those paths might // change later and we don't want to be forced to keep our local copy up to date - removeKey(keyPaths); + removeKey(KeyPaths); return true; } bool ConfigurationManager::checkCompleteness() const { std::vector requiredTokens = { - constants::configurationmanager::keyPaths, - constants::configurationmanager::keyCachePath, - constants::configurationmanager::keyFonts, - constants::configurationmanager::keyConfigSgct + KeyPaths, + KeyCachePath, + KeyFonts, + KeyConfigSgct }; bool totalSuccess = true; diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index db01bd7374..f1277c5d58 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -223,7 +223,7 @@ bool OpenSpaceEngine::create( } // Create the cachemanager - FileSys.createCacheManager(absPath("${" + constants::configurationmanager::keyCache + "}"), CacheVersion); + FileSys.createCacheManager(absPath("${" + ConfigurationManager::KeyCache + "}"), CacheVersion); _engine->_console->initialize(); ImageSequencer2::initialize(); @@ -237,7 +237,7 @@ bool OpenSpaceEngine::create( LDEBUG("Determining SGCT configuration file"); std::string sgctConfigurationPath = _sgctDefaultConfigFile; _engine->configurationManager()->getValue( - constants::configurationmanager::keyConfigSgct, sgctConfigurationPath); + ConfigurationManager::KeyConfigSgct, sgctConfigurationPath); if (!commandlineArgumentPlaceholders.sgctConfigurationName.empty()) { LDEBUG("Overwriting SGCT configuration file with commandline argument: " << @@ -299,15 +299,13 @@ bool OpenSpaceEngine::initialize() { scriptEngine()->initialize(); // If a LuaDocumentationFile was specified, generate it now - using constants::configurationmanager::keyLuaDocumentationType; - using constants::configurationmanager::keyLuaDocumentationFile; - const bool hasType = configurationManager()->hasKey(keyLuaDocumentationType); - const bool hasFile = configurationManager()->hasKey(keyLuaDocumentationFile); + const bool hasType = configurationManager()->hasKey(ConfigurationManager::KeyLuaDocumentationType); + const bool hasFile = configurationManager()->hasKey(ConfigurationManager::KeyLuaDocumentationFile); if (hasType && hasFile) { std::string luaDocumentationType; - configurationManager()->getValue(keyLuaDocumentationType, luaDocumentationType); + configurationManager()->getValue(ConfigurationManager::KeyLuaDocumentationType, luaDocumentationType); std::string luaDocumentationFile; - configurationManager()->getValue(keyLuaDocumentationFile, luaDocumentationFile); + configurationManager()->getValue(ConfigurationManager::KeyLuaDocumentationFile, luaDocumentationFile); luaDocumentationFile = absPath(luaDocumentationFile); _scriptEngine->writeDocumentation(luaDocumentationFile, luaDocumentationType); @@ -315,7 +313,7 @@ bool OpenSpaceEngine::initialize() { bool disableMasterRendering = false; configurationManager()->getValue( - constants::configurationmanager::keyDisableMasterRendering, disableMasterRendering); + ConfigurationManager::KeyDisableMasterRendering, disableMasterRendering); _renderEngine->setDisableRenderingOnMaster(disableMasterRendering); @@ -333,7 +331,7 @@ bool OpenSpaceEngine::initialize() { std::string sceneDescriptionPath; success = configurationManager()->getValue( - constants::configurationmanager::keyConfigScene, sceneDescriptionPath); + ConfigurationManager::KeyConfigScene, sceneDescriptionPath); if (success) sceneGraph->scheduleLoadSceneFile(sceneDescriptionPath); @@ -419,11 +417,11 @@ bool OpenSpaceEngine::findConfiguration(std::string& filename) { bool OpenSpaceEngine::loadSpiceKernels() { // Load time kernel - using constants::configurationmanager::keySpiceTimeKernel; std::string timeKernel; - bool success = configurationManager()->getValue(keySpiceTimeKernel, timeKernel); + bool success = configurationManager()->getValue(ConfigurationManager::KeySpiceTimeKernel, timeKernel); + // Move this to configurationmanager::completenesscheck ---abock if (!success) { - LERROR("Configuration file does not contain a '" << keySpiceTimeKernel << "'"); + LERROR("Configuration file does not contain a '" << ConfigurationManager::KeySpiceTimeKernel << "'"); return false; } SpiceManager::KernelIdentifier id = @@ -434,11 +432,11 @@ bool OpenSpaceEngine::loadSpiceKernels() { } // Load SPICE leap second kernel - using constants::configurationmanager::keySpiceLeapsecondKernel; std::string leapSecondKernel; - success = configurationManager()->getValue(keySpiceLeapsecondKernel, leapSecondKernel); + success = configurationManager()->getValue(ConfigurationManager::KeySpiceLeapsecondKernel, leapSecondKernel); if (!success) { - LERROR("Configuration file does not have a '" << keySpiceLeapsecondKernel << "'"); + // Move this to configurationmanager::completenesscheck ---abock + LERROR("Configuration file does not have a '" << ConfigurationManager::KeySpiceLeapsecondKernel << "'"); return false; } id = SpiceManager::ref().loadKernel(std::move(leapSecondKernel)); @@ -472,14 +470,14 @@ void OpenSpaceEngine::runScripts(const ghoul::Dictionary& scripts) { void OpenSpaceEngine::runStartupScripts() { ghoul::Dictionary scripts; configurationManager()->getValue( - constants::configurationmanager::keyStartupScript, scripts); + ConfigurationManager::KeyStartupScript, scripts); runScripts(scripts); } void OpenSpaceEngine::runSettingsScripts() { ghoul::Dictionary scripts; configurationManager()->getValue( - constants::configurationmanager::keySettingsScript, scripts); + ConfigurationManager::KeySettingsScript, scripts); runScripts(scripts); } @@ -488,7 +486,7 @@ void OpenSpaceEngine::loadFonts() { sgct_text::FontManager::FontPath local = sgct_text::FontManager::FontPath::FontPath_Local; ghoul::Dictionary fonts; - configurationManager()->getValue(constants::configurationmanager::keyFonts, fonts); + configurationManager()->getValue(ConfigurationManager::KeyFonts, fonts); for (const std::string& key : fonts.keys()) { std::string font; @@ -505,18 +503,12 @@ void OpenSpaceEngine::loadFonts() { } void OpenSpaceEngine::configureLogging() { - using constants::configurationmanager::keyLogLevel; - using constants::configurationmanager::keyLogs; - - if (configurationManager()->hasKeyAndValue(keyLogLevel)) { - using constants::configurationmanager::keyLogLevel; - using constants::configurationmanager::keyLogImmediateFlush; - + if (configurationManager()->hasKeyAndValue(ConfigurationManager::KeyLogLevel)) { std::string logLevel; - configurationManager()->getValue(keyLogLevel, logLevel); + configurationManager()->getValue(ConfigurationManager::KeyLogLevel, logLevel); bool immediateFlush = false; - configurationManager()->getValue(keyLogImmediateFlush, immediateFlush); + configurationManager()->getValue(ConfigurationManager::KeyLogImmediateFlush, immediateFlush); LogManager::LogLevel level = LogManager::levelFromString(logLevel); LogManager::deinitialize(); @@ -524,9 +516,9 @@ void OpenSpaceEngine::configureLogging() { LogMgr.addLog(new ConsoleLog); } - if (configurationManager()->hasKeyAndValue(keyLogs)) { + if (configurationManager()->hasKeyAndValue(ConfigurationManager::KeyLogs)) { ghoul::Dictionary logs; - configurationManager()->getValue(keyLogs, logs); + configurationManager()->getValue(ConfigurationManager::KeyLogs, logs); for (size_t i = 1; i <= logs.size(); ++i) { ghoul::Dictionary logInfo; diff --git a/src/rendering/model/renderablemodel.cpp b/src/rendering/model/renderablemodel.cpp index 6ef0fdb0b1..67dc2e9306 100644 --- a/src/rendering/model/renderablemodel.cpp +++ b/src/rendering/model/renderablemodel.cpp @@ -76,23 +76,24 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) std::string name; bool success = dictionary.getValue(constants::scenegraphnode::keyName, name); ghoul_assert(success, "Name was not passed to RenderableModel"); - std::string path; - success = dictionary.getValue(constants::scenegraph::keyPathModule, path); - ghoul_assert(success, "Module path was not passed to RenderableModel"); + //std::string path; + //success = dictionary.getValue(constants::scenegraph::keyPathModule, path); + // ghoul_assert(success, "Module path was not passed to RenderableModel"); ghoul::Dictionary geometryDictionary; success = dictionary.getValue( constants::renderablemodel::keyGeometry, geometryDictionary); if (success) { geometryDictionary.setValue(constants::scenegraphnode::keyName, name); - geometryDictionary.setValue(constants::scenegraph::keyPathModule, path); + //geometryDictionary.setValue(constants::scenegraph::keyPathModule, path); _geometry = modelgeometry::ModelGeometry::createFromDictionary(geometryDictionary); } std::string texturePath = ""; success = dictionary.getValue("Textures.Color", texturePath); if (success) - _colorTexturePath = path + "/" + texturePath; + //_colorTexturePath = /*path + "/"*/ + texturePath; + _colorTexturePath = absPath(texturePath); addPropertySubOwner(_geometry); diff --git a/src/rendering/planets/renderableplanet.cpp b/src/rendering/planets/renderableplanet.cpp index 3eddfe5e7b..803ed7c182 100644 --- a/src/rendering/planets/renderableplanet.cpp +++ b/src/rendering/planets/renderableplanet.cpp @@ -71,8 +71,8 @@ RenderablePlanet::RenderablePlanet(const ghoul::Dictionary& dictionary) ghoul_assert(success, "RenderablePlanet need the '" <uploadTexture(); _texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); @@ -529,7 +524,7 @@ void RenderablePlanetProjection::loadTexture(){ delete _textureOriginal; _textureOriginal = nullptr; if (_colorTexturePath.value() != "") { - _textureOriginal = ghoul::io::TextureReader::ref().loadTexture(absPath(_colorTexturePath)); + _textureOriginal = ghoul::io::TextureReader::ref().loadTexture(_colorTexturePath); if (_textureOriginal) { _textureOriginal->uploadTexture(); _textureOriginal->setFilter(ghoul::opengl::Texture::FilterMode::Linear); @@ -538,7 +533,7 @@ void RenderablePlanetProjection::loadTexture(){ delete _textureWhiteSquare; _textureWhiteSquare = nullptr; if (_colorTexturePath.value() != "") { - _textureWhiteSquare = ghoul::io::TextureReader::ref().loadTexture(absPath(_defaultProjImage)); + _textureWhiteSquare = ghoul::io::TextureReader::ref().loadTexture(_defaultProjImage); if (_textureWhiteSquare) { _textureWhiteSquare->uploadTexture(); _textureWhiteSquare->setFilter(ghoul::opengl::Texture::FilterMode::Linear); diff --git a/src/rendering/renderable.cpp b/src/rendering/renderable.cpp index 1f9e0780fb..a8b4798a6c 100644 --- a/src/rendering/renderable.cpp +++ b/src/rendering/renderable.cpp @@ -85,15 +85,6 @@ Renderable::Renderable(const ghoul::Dictionary& dictionary) "Scenegraphnode need to specify '" << constants::scenegraphnode::keyName << "' because renderables is going to use this for debugging!"); #endif - // get path if available - bool success = dictionary.getValue(constants::scenegraph::keyPathModule, _relativePath); -#ifndef NDEBUG - ghoul_assert(success, - "Scenegraphnode need to specify '" << constants::scenegraph::keyPathModule - << "' because renderables is going to use this for debugging!"); -#endif - if (success) - _relativePath += ghoul::filesystem::FileSystem::PathSeparator; dictionary.getValue(keyStart, _startTime); dictionary.getValue(keyEnd, _endTime); @@ -121,20 +112,6 @@ void Renderable::update(const UpdateData&) { } -std::string Renderable::findPath(const std::string& path) { - std::string tmp = absPath(path); - if(FileSys.fileExists(tmp)) - return tmp; - - tmp = absPath(_relativePath + path); - if(FileSys.fileExists(tmp)) - return tmp; - - LERROR("Could not find file '" << path << "'"); - - return ""; -} - void Renderable::setPscUniforms( ghoul::opengl::ProgramObject* program, const Camera* camera, diff --git a/src/rendering/renderableplane.cpp b/src/rendering/renderableplane.cpp index 816f394e26..c001effc75 100644 --- a/src/rendering/renderableplane.cpp +++ b/src/rendering/renderableplane.cpp @@ -91,7 +91,7 @@ RenderablePlane::RenderablePlane(const ghoul::Dictionary& dictionary) std::string texturePath = ""; bool success = dictionary.getValue("Texture", texturePath); if (success) { - _texturePath = findPath(texturePath); + _texturePath = absPath(texturePath); _textureFile = new ghoul::filesystem::File(_texturePath); } diff --git a/src/rendering/renderableplaneprojection.cpp b/src/rendering/renderableplaneprojection.cpp index 0351e61d44..10e41c8dc3 100644 --- a/src/rendering/renderableplaneprojection.cpp +++ b/src/rendering/renderableplaneprojection.cpp @@ -67,7 +67,7 @@ RenderablePlaneProjection::RenderablePlaneProjection(const ghoul::Dictionary& di std::string texturePath = ""; bool success = dictionary.getValue(keyTexture, _texturePath); if (success) { - _texturePath = findPath(_texturePath); + _texturePath = absPath(_texturePath); _textureFile = new ghoul::filesystem::File(_texturePath); } diff --git a/src/rendering/renderablesphere.cpp b/src/rendering/renderablesphere.cpp index e8bfd3ce96..7606d7180e 100644 --- a/src/rendering/renderablesphere.cpp +++ b/src/rendering/renderablesphere.cpp @@ -61,11 +61,6 @@ RenderableSphere::RenderableSphere(const ghoul::Dictionary& dictionary) , _sphere(nullptr) , _sphereIsDirty(false) { - std::string path; - bool success = dictionary.getValue(constants::scenegraph::keyPathModule, path); - ghoul_assert(success, - "RenderablePlanet need the '" << constants::scenegraph::keyPathModule << "' be specified"); - if (dictionary.hasKeyAndValue(keySize)) { glm::vec2 size; dictionary.getValue(keySize, size); @@ -81,7 +76,7 @@ RenderableSphere::RenderableSphere(const ghoul::Dictionary& dictionary) if (dictionary.hasKeyAndValue(keyTexture)) { std::string texture; dictionary.getValue(keyTexture, texture); - _texturePath = path + '/' + texture; + _texturePath = absPath(texture); } _orientation.addOption(Outside, "Outside"); @@ -183,7 +178,7 @@ void RenderableSphere::update(const UpdateData& data) { void RenderableSphere::loadTexture() { if (_texturePath.value() != "") { - ghoul::opengl::Texture* texture = ghoul::io::TextureReader::ref().loadTexture(absPath(_texturePath)); + ghoul::opengl::Texture* texture = ghoul::io::TextureReader::ref().loadTexture(_texturePath); if (texture) { LDEBUG("Loaded texture from '" << absPath(_texturePath) << "'"); texture->uploadTexture(); diff --git a/src/rendering/renderablevolumegl.cpp b/src/rendering/renderablevolumegl.cpp index 80a2a583a8..9d58a0efc9 100644 --- a/src/rendering/renderablevolumegl.cpp +++ b/src/rendering/renderablevolumegl.cpp @@ -74,7 +74,7 @@ RenderableVolumeGL::RenderableVolumeGL(const ghoul::Dictionary& dictionary) constants::renderablevolumegl::keyVolume << "'"); return; } - _filename = findPath(_filename); + _filename = absPath(_filename); if (_filename == "") { return; } @@ -93,7 +93,7 @@ RenderableVolumeGL::RenderableVolumeGL(const ghoul::Dictionary& dictionary) constants::renderablevolumegl::keyTransferFunction << "'"); return; } - _transferFunctionPath = findPath(_transferFunctionPath); + _transferFunctionPath = absPath(_transferFunctionPath); _transferFunctionFile = new ghoul::filesystem::File(_transferFunctionPath, true); _samplerFilename = ""; @@ -104,7 +104,7 @@ RenderableVolumeGL::RenderableVolumeGL(const ghoul::Dictionary& dictionary) constants::renderablevolumegl::keySampler << "'"); return; } - _samplerFilename = findPath(_samplerFilename); + _samplerFilename = absPath(_samplerFilename); KameleonWrapper kw(_filename); auto t = kw.getGridUnits(); diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 4486a34668..4e6546b773 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -60,6 +60,11 @@ namespace { const std::string _moduleExtension = ".mod"; const std::string _defaultCommonDirectory = "common"; const std::string _commonModuleToken = "${COMMON_MODULE}"; + + const std::string KeyCamera = "Camera"; + const std::string KeyFocusObject = "Focus"; + const std::string KeyPositionObject = "Position"; + const std::string KeyViewOffset = "Offset"; } namespace openspace { @@ -257,12 +262,12 @@ bool Scene::loadSceneInternal(const std::string& sceneDescriptionFilePath) { // TODO: Make it less hard-coded and more flexible when nodes are not found ghoul::Dictionary cameraDictionary; - if (dictionary.getValue(constants::scenegraph::keyCamera, cameraDictionary)) { + if (dictionary.getValue(KeyCamera, cameraDictionary)) { LDEBUG("Camera dictionary found"); std::string focus; - if (cameraDictionary.hasKey(constants::scenegraph::keyFocusObject) - && cameraDictionary.getValue(constants::scenegraph::keyFocusObject, focus)) + if (cameraDictionary.hasKey(KeyFocusObject) + && cameraDictionary.getValue(KeyFocusObject, focus)) { auto focusIterator = std::find_if( _graph.nodes().begin(), @@ -357,8 +362,8 @@ bool Scene::loadSceneInternal(const std::string& sceneDescriptionFilePath) { OsEng.interactionHandler()->setFocusNode(_graph.rootNode()); glm::vec4 position; - if (cameraDictionary.hasKey(constants::scenegraph::keyPositionObject) - && cameraDictionary.getValue(constants::scenegraph::keyPositionObject, position)) { + if (cameraDictionary.hasKey(KeyPositionObject) + && cameraDictionary.getValue(KeyPositionObject, position)) { LDEBUG("Camera position is (" << position[0] << ", " @@ -381,8 +386,8 @@ bool Scene::loadSceneInternal(const std::string& sceneDescriptionFilePath) { c->setScaling(cameraScaling); glm::vec3 viewOffset; - if (cameraDictionary.hasKey(constants::scenegraph::keyViewOffset) - && cameraDictionary.getValue(constants::scenegraph::keyViewOffset, viewOffset)) { + if (cameraDictionary.hasKey(KeyViewOffset) + && cameraDictionary.getValue(KeyViewOffset, viewOffset)) { glm::quat rot = glm::quat(viewOffset); c->rotate(rot); } @@ -396,15 +401,13 @@ bool Scene::loadSceneInternal(const std::string& sceneDescriptionFilePath) { } // If a LuaDocumentationFile was specified, generate it now - using constants::configurationmanager::keyPropertyDocumentationType; - using constants::configurationmanager::keyPropertyDocumentationFile; - const bool hasType = OsEng.configurationManager()->hasKey(keyPropertyDocumentationType); - const bool hasFile = OsEng.configurationManager()->hasKey(keyPropertyDocumentationFile); + const bool hasType = OsEng.configurationManager()->hasKey(ConfigurationManager::KeyPropertyDocumentationType); + const bool hasFile = OsEng.configurationManager()->hasKey(ConfigurationManager::KeyPropertyDocumentationFile); if (hasType && hasFile) { std::string propertyDocumentationType; - OsEng.configurationManager()->getValue(keyPropertyDocumentationType, propertyDocumentationType); + OsEng.configurationManager()->getValue(ConfigurationManager::KeyPropertyDocumentationType, propertyDocumentationType); std::string propertyDocumentationFile; - OsEng.configurationManager()->getValue(keyPropertyDocumentationFile, propertyDocumentationFile); + OsEng.configurationManager()->getValue(ConfigurationManager::KeyPropertyDocumentationFile, propertyDocumentationFile); propertyDocumentationFile = absPath(propertyDocumentationFile); writePropertyDocumentation(propertyDocumentationFile, propertyDocumentationType); diff --git a/src/scene/scenegraph.cpp b/src/scene/scenegraph.cpp index bcb359bfec..8dab57e29a 100644 --- a/src/scene/scenegraph.cpp +++ b/src/scene/scenegraph.cpp @@ -26,7 +26,6 @@ #include #include -#include #include #include @@ -39,6 +38,11 @@ namespace { const std::string _moduleExtension = ".mod"; const std::string _defaultCommonDirectory = "common"; const std::string _commonModuleToken = "${COMMON_MODULE}"; + + const std::string KeyPathScene = "ScenePath"; + const std::string KeyModules = "Modules"; + const std::string KeyCommonFolder = "CommonFolder"; + const std::string KeyPathModule = "ModulePath"; } namespace openspace { @@ -78,7 +82,7 @@ bool SceneGraph::loadFromFile(const std::string& sceneDescription) { std::string sceneDescriptionDirectory = ghoul::filesystem::File(absSceneFile, true).directoryName(); std::string sceneDirectory("."); - sceneDictionary.getValue(constants::scenegraph::keyPathScene, sceneDirectory); + sceneDictionary.getValue(KeyPathScene, sceneDirectory); // The scene path could either be an absolute or relative path to the description // paths directory @@ -91,14 +95,13 @@ bool SceneGraph::loadFromFile(const std::string& sceneDescription) { else if (FileSys.directoryExists(absoluteCandidate)) sceneDirectory = absoluteCandidate; else { - LERROR("The '" << constants::scenegraph::keyPathScene << "' pointed to a " + LERROR("The '" << KeyPathScene << "' pointed to a " "path '" << sceneDirectory << "' that did not exist"); return false; } - using constants::scenegraph::keyModules; ghoul::Dictionary moduleDictionary; - success = sceneDictionary.getValue(keyModules, moduleDictionary); + success = sceneDictionary.getValue(KeyModules, moduleDictionary); if (!success) // There are no modules that are loaded return true; @@ -107,13 +110,12 @@ bool SceneGraph::loadFromFile(const std::string& sceneDescription) { OsEng.scriptEngine()->initializeLuaState(state); // Get the common directory - using constants::scenegraph::keyCommonFolder; - bool commonFolderSpecified = sceneDictionary.hasKey(keyCommonFolder); - bool commonFolderCorrectType = sceneDictionary.hasKeyAndValue(keyCommonFolder); + bool commonFolderSpecified = sceneDictionary.hasKey(KeyCommonFolder); + bool commonFolderCorrectType = sceneDictionary.hasKeyAndValue(KeyCommonFolder); if (commonFolderSpecified) { if (commonFolderCorrectType) { - std::string commonFolder = sceneDictionary.value(keyCommonFolder); + std::string commonFolder = sceneDictionary.value(KeyCommonFolder); std::string fullCommonFolder = FileSys.pathByAppendingComponent( sceneDirectory, commonFolder @@ -182,10 +184,10 @@ bool SceneGraph::loadFromFile(const std::string& sceneDescription) { std::string parentName; moduleDictionary.getValue(key, element); - element.setValue(constants::scenegraph::keyPathModule, modulePath); + element.setValue(KeyPathModule, modulePath); - element.getValue(constants::scenegraphnode::keyName, nodeName); - element.getValue(constants::scenegraphnode::keyParentName, parentName); + element.getValue(SceneGraphNode::KeyName, nodeName); + element.getValue(SceneGraphNode::KeyParentName, parentName); FileSys.setCurrentDirectory(modulePath); SceneGraphNode* node = SceneGraphNode::createFromDictionary(element); @@ -199,11 +201,10 @@ bool SceneGraph::loadFromFile(const std::string& sceneDescription) { parents[nodeName] = parentName; // Also include loaded dependencies - using constants::scenegraphnode::keyDependencies; - if (element.hasKey(keyDependencies)) { - if (element.hasValue(keyDependencies)) { + if (element.hasKey(SceneGraphNode::KeyDependencies)) { + if (element.hasValue(SceneGraphNode::KeyDependencies)) { ghoul::Dictionary nodeDependencies; - element.getValue(constants::scenegraphnode::keyDependencies, nodeDependencies); + element.getValue(SceneGraphNode::KeyDependencies, nodeDependencies); std::vector keys = nodeDependencies.keys(); for (const std::string& key : keys) { diff --git a/src/scene/scenegraphnode.cpp b/src/scene/scenegraphnode.cpp index f0e13470fa..d37a4821b1 100644 --- a/src/scene/scenegraphnode.cpp +++ b/src/scene/scenegraphnode.cpp @@ -51,6 +51,9 @@ const std::string _loggerCat = "SceneGraphNode"; namespace openspace { std::string SceneGraphNode::RootNodeName = "Root"; +const std::string SceneGraphNode::KeyName = "Name"; +const std::string SceneGraphNode::KeyParentName = "Parent"; +const std::string SceneGraphNode::KeyDependencies = "Dependencies"; SceneGraphNode* SceneGraphNode::createFromDictionary(const ghoul::Dictionary& dictionary) { @@ -59,12 +62,8 @@ SceneGraphNode* SceneGraphNode::createFromDictionary(const ghoul::Dictionary& di SceneGraphNode* result = new SceneGraphNode; - std::string path; - dictionary.getValue(keyPathModule, path); - if (!dictionary.hasValue(keyName)) { - LERROR("SceneGraphNode in '" << path << "' did not contain a '" - << keyName << "' key"); + LERROR("SceneGraphNode did not contain a '" << keyName << "' key"); return nullptr; } std::string name; @@ -76,7 +75,6 @@ SceneGraphNode* SceneGraphNode::createFromDictionary(const ghoul::Dictionary& di dictionary.getValue(keyRenderable, renderableDictionary); renderableDictionary.setValue(keyName, name); - renderableDictionary.setValue(keyPathModule, path); result->_renderable = Renderable::createFromDictionary(renderableDictionary); if (result->_renderable == nullptr) { @@ -91,7 +89,6 @@ SceneGraphNode* SceneGraphNode::createFromDictionary(const ghoul::Dictionary& di if (dictionary.hasKey(keyEphemeris)) { ghoul::Dictionary ephemerisDictionary; dictionary.getValue(keyEphemeris, ephemerisDictionary); - ephemerisDictionary.setValue(keyPathModule, path); result->_ephemeris = Ephemeris::createFromDictionary(ephemerisDictionary); if (result->_ephemeris == nullptr) { LERROR("Failed to create ephemeris for SceneGraphNode '" From fdead75cfad12b797988c47758bb2e3a6708b982 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 20 May 2015 17:42:47 +0200 Subject: [PATCH 027/329] Remove move values from constants.h Disable ghosting in ephemeris to remove dependency on imagesequencer --- include/openspace/scene/spiceephemeris.h | 2 +- include/openspace/util/constants.h | 80 ------------------- src/rendering/model/renderablemodel.cpp | 9 +-- src/rendering/planets/planetgeometry.cpp | 11 ++- .../planets/planetgeometryprojection.cpp | 9 +-- src/rendering/renderable.cpp | 17 ++-- src/rendering/renderableplaneprojection.cpp | 29 ++++--- src/rendering/renderablesphericalgrid.cpp | 16 ++-- src/rendering/renderablevolumegl.cpp | 43 +++++----- src/rendering/stars/renderablestars.cpp | 14 ++-- src/scene/dynamicephemeris.cpp | 14 ++-- src/scene/ephemeris.cpp | 24 +++--- src/scene/scenegraphnode.cpp | 37 ++++----- src/scene/spiceephemeris.cpp | 37 ++++----- src/scene/staticephemeris.cpp | 14 ++-- 15 files changed, 132 insertions(+), 224 deletions(-) diff --git a/include/openspace/scene/spiceephemeris.h b/include/openspace/scene/spiceephemeris.h index a5381223cf..afdd5964c0 100644 --- a/include/openspace/scene/spiceephemeris.h +++ b/include/openspace/scene/spiceephemeris.h @@ -42,7 +42,7 @@ private: std::string _originName; psc _position; bool _kernelsLoadedSuccessfully; - std::string _ghosting; + //std::string _ghosting; std::string _name; }; diff --git a/include/openspace/util/constants.h b/include/openspace/util/constants.h index 3f2970bb63..2c05890afb 100644 --- a/include/openspace/util/constants.h +++ b/include/openspace/util/constants.h @@ -36,90 +36,10 @@ namespace fonts { const std::string keyLight = "Light"; } // namespace fonts -namespace scenegraph { - // const std::string keyPathScene = "ScenePath"; - //const std::string keyCommonFolder = "CommonFolder"; - // const std::string keyModules = "Modules"; - // const std::string keyCamera = "Camera"; - // const std::string keyFocusObject = "Focus"; - // const std::string keyPositionObject = "Position"; - // const std::string keyViewOffset = "Offset"; - // const std::string keyPathModule = "ModulePath"; -} // namespace scenegraph - namespace scenegraphnode { const std::string keyName = "Name"; - const std::string keyParentName = "Parent"; - const std::string keyDependencies = "Dependencies"; - const std::string keyRenderable = "Renderable"; - const std::string keyEphemeris = "Ephemeris"; } // namespace scenegraphnode -namespace renderable { - const std::string keyType = "Type"; -} // namespace renderable - -namespace planetgeometry { - const std::string keyType = "Type"; -} // namespace planetgeometry - -namespace renderablemodel { - const std::string keyGeometry = "Geometry"; -} // namespace renderablemodel - -namespace renderableplaneprojection { - const std::string keySpacecraft = "Spacecraft"; - const std::string keyInstrument = "Instrument"; - const std::string keyMoving = "Moving"; - const std::string keyTexture = "Texture"; - const std::string keyName = "Name"; - const std::string galacticFrame = "GALACTIC"; - const double REALLY_FAR = 99999999999; -} // namespace renderableplaneprojection - -namespace renderablestars { - const std::string keyFile = "File"; - const std::string keyTexture = "Texture"; - const std::string keyColorMap = "ColorMap"; -} // namespace renderablestars - -namespace renderablevolumegl { - const std::string keyVolume = "Volume"; - const std::string keyHints = "Hints"; - const std::string keyTransferFunction = "TransferFunction"; - const std::string keySampler = "Sampler"; - const std::string keyBoxScaling = "BoxScaling"; - const std::string keyVolumeName = "VolumeName"; - const std::string keyTransferFunctionName = "TransferFunctionName"; -} // namespace renderablevolumegl - -namespace renderablesphericalgrid { - const std::string gridType = "GridType"; - const std::string gridColor = "GridColor"; - const std::string gridMatrix = "GridMatrix"; - const std::string gridSegments = "GridSegments"; - const std::string gridRadius = "GridRadius"; - const std::string gridPatentsRotiation = "ParentsRotation"; -} // namespace renderablesphericalgrid - -namespace ephemeris { - const std::string keyType = "Type"; -} // namespace ephemeris - -namespace staticephemeris { - const std::string keyPosition = "Position"; -} // namespace staticephemeris - -namespace dynamicephemeris { - const std::string keyPosition = "Position"; -} // namespace dynamicephemeris - -namespace spiceephemeris { - const std::string keyBody = "Body"; - const std::string keyOrigin = "Observer"; - const std::string keyKernels = "Kernels"; -} // namespace spiceephemeris - } // namespace constants } // namespace openspace diff --git a/src/rendering/model/renderablemodel.cpp b/src/rendering/model/renderablemodel.cpp index 67dc2e9306..d53f24d42e 100644 --- a/src/rendering/model/renderablemodel.cpp +++ b/src/rendering/model/renderablemodel.cpp @@ -50,6 +50,7 @@ namespace { const std::string _loggerCat = "RenderableModel"; const std::string keySource = "Rotation.Source"; const std::string keyDestination = "Rotation.Destination"; + const std::string keyGeometry = "Geometry"; const std::string keyBody = "Body"; const std::string keyStart = "StartTime"; const std::string keyEnd = "EndTime"; @@ -76,23 +77,17 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) std::string name; bool success = dictionary.getValue(constants::scenegraphnode::keyName, name); ghoul_assert(success, "Name was not passed to RenderableModel"); - //std::string path; - //success = dictionary.getValue(constants::scenegraph::keyPathModule, path); - // ghoul_assert(success, "Module path was not passed to RenderableModel"); ghoul::Dictionary geometryDictionary; - success = dictionary.getValue( - constants::renderablemodel::keyGeometry, geometryDictionary); + success = dictionary.getValue(keyGeometry, geometryDictionary); if (success) { geometryDictionary.setValue(constants::scenegraphnode::keyName, name); - //geometryDictionary.setValue(constants::scenegraph::keyPathModule, path); _geometry = modelgeometry::ModelGeometry::createFromDictionary(geometryDictionary); } std::string texturePath = ""; success = dictionary.getValue("Textures.Color", texturePath); if (success) - //_colorTexturePath = /*path + "/"*/ + texturePath; _colorTexturePath = absPath(texturePath); addPropertySubOwner(_geometry); diff --git a/src/rendering/planets/planetgeometry.cpp b/src/rendering/planets/planetgeometry.cpp index da1b9c6348..0d9a744820 100644 --- a/src/rendering/planets/planetgeometry.cpp +++ b/src/rendering/planets/planetgeometry.cpp @@ -28,20 +28,19 @@ #include namespace { -const std::string _loggerCat = "PlanetGeometry"; + const std::string _loggerCat = "PlanetGeometry"; + const std::string KeyType = "Type"; } namespace openspace { namespace planetgeometry { -PlanetGeometry* PlanetGeometry::createFromDictionary(const ghoul::Dictionary& dictionary) -{ +PlanetGeometry* PlanetGeometry::createFromDictionary(const ghoul::Dictionary& dictionary) { std::string geometryType; - const bool success = dictionary.getValue( - constants::planetgeometry::keyType, geometryType); + const bool success = dictionary.getValue(KeyType, geometryType); if (!success) { LERROR("PlanetGeometry did not contain a correct value of the key '" - << constants::planetgeometry::keyType << "'"); + << KeyType << "'"); return nullptr; } ghoul::TemplateFactory* factory diff --git a/src/rendering/planets/planetgeometryprojection.cpp b/src/rendering/planets/planetgeometryprojection.cpp index 936fd95567..a1507a831c 100644 --- a/src/rendering/planets/planetgeometryprojection.cpp +++ b/src/rendering/planets/planetgeometryprojection.cpp @@ -24,11 +24,11 @@ #include #include -#include #include namespace { -const std::string _loggerCat = "PlanetGeometryProjection"; + const std::string _loggerCat = "PlanetGeometryProjection"; + const std::string KeyType = "Type"; } namespace openspace { @@ -37,11 +37,10 @@ namespace planetgeometryprojection { PlanetGeometryProjection* PlanetGeometryProjection::createFromDictionary(const ghoul::Dictionary& dictionary) { std::string geometryType; - const bool success = dictionary.getValue( - constants::planetgeometry::keyType, geometryType); + const bool success = dictionary.getValue(KeyType, geometryType); if (!success) { LERROR("PlanetGeometry did not contain a correct value of the key '" - << constants::planetgeometry::keyType << "'"); + << KeyType << "'"); return nullptr; } ghoul::TemplateFactory* factory diff --git a/src/rendering/renderable.cpp b/src/rendering/renderable.cpp index a8b4798a6c..7de7c556e4 100644 --- a/src/rendering/renderable.cpp +++ b/src/rendering/renderable.cpp @@ -36,26 +36,25 @@ #include namespace { -const std::string _loggerCat = "Renderable"; -const std::string keyBody = "Body"; -const std::string keyStart = "StartTime"; -const std::string keyEnd = "EndTime"; + const std::string _loggerCat = "Renderable"; + const std::string keyBody = "Body"; + const std::string keyStart = "StartTime"; + const std::string keyEnd = "EndTime"; + const std::string KeyType = "Type"; } namespace openspace { -Renderable* Renderable::createFromDictionary(const ghoul::Dictionary& dictionary) -{ +Renderable* Renderable::createFromDictionary(const ghoul::Dictionary& dictionary) { // The name is passed down from the SceneGraphNode std::string name; bool success = dictionary.getValue(constants::scenegraphnode::keyName, name); assert(success); std::string renderableType; - success = dictionary.getValue(constants::renderable::keyType, renderableType); + success = dictionary.getValue(KeyType, renderableType); if (!success) { - LERROR("Renderable '" << name << "' did not have key '" - << constants::renderable::keyType << "'"); + LERROR("Renderable '" << name << "' did not have key '" << KeyType << "'"); return nullptr; } diff --git a/src/rendering/renderableplaneprojection.cpp b/src/rendering/renderableplaneprojection.cpp index 10e41c8dc3..b8d290f156 100644 --- a/src/rendering/renderableplaneprojection.cpp +++ b/src/rendering/renderableplaneprojection.cpp @@ -41,12 +41,17 @@ namespace { const std::string _loggerCat = "RenderablePlaneProjection"; + const std::string KeySpacecraft = "Spacecraft"; + const std::string KeyInstrument = "Instrument"; + const std::string KeyMoving = "Moving"; + const std::string KeyTexture = "Texture"; + const std::string KeyName = "Name"; + const std::string GalacticFrame = "GALACTIC"; + const double REALLY_FAR = 99999999999; } namespace openspace { -using namespace constants::renderableplaneprojection; - RenderablePlaneProjection::RenderablePlaneProjection(const ghoul::Dictionary& dictionary) : Renderable(dictionary) , _texturePath("") @@ -59,13 +64,13 @@ RenderablePlaneProjection::RenderablePlaneProjection(const ghoul::Dictionary& di , _name("ImagePlane") , _previousTime(0) { - dictionary.getValue(keySpacecraft, _spacecraft); - dictionary.getValue(keyInstrument, _instrument); - dictionary.getValue(keyMoving, _moving); - dictionary.getValue(keyName, _name); + dictionary.getValue(KeySpacecraft, _spacecraft); + dictionary.getValue(KeyInstrument, _instrument); + dictionary.getValue(KeyMoving, _moving); + dictionary.getValue(KeyName, _name); std::string texturePath = ""; - bool success = dictionary.getValue(keyTexture, _texturePath); + bool success = dictionary.getValue(KeyTexture, _texturePath); if (success) { _texturePath = absPath(_texturePath); _textureFile = new ghoul::filesystem::File(_texturePath); @@ -149,7 +154,7 @@ void RenderablePlaneProjection::update(const UpdateData& data) { double time = data.time; const Image img = openspace::ImageSequencer2::ref().getLatestImageForInstrument(_instrument); - openspace::SpiceManager::ref().getPositionTransformMatrix(_target.frame, galacticFrame, time, _stateMatrix); + openspace::SpiceManager::ref().getPositionTransformMatrix(_target.frame, GalacticFrame, time, _stateMatrix); double timePast = 0.0; if (img.path != "") @@ -217,17 +222,17 @@ void RenderablePlaneProjection::updatePlane(const Image img, double currentTime) double lt; psc projection[4]; - SpiceManager::ref().getTargetPosition(_target.body, _spacecraft, galacticFrame, "CN+S", currentTime, vecToTarget, lt); + SpiceManager::ref().getTargetPosition(_target.body, _spacecraft, GalacticFrame, "CN+S", currentTime, vecToTarget, lt); // The apparent position, CN+S, makes image align best with target for (int j = 0; j < bounds.size(); ++j) { - openspace::SpiceManager::ref().frameConversion(bounds[j], frame, galacticFrame, currentTime); + openspace::SpiceManager::ref().frameConversion(bounds[j], frame, GalacticFrame, currentTime); glm::dvec3 cornerPosition = openspace::SpiceManager::ref().orthogonalProjection(vecToTarget, bounds[j]); if (!_moving) { cornerPosition -= vecToTarget; } - openspace::SpiceManager::ref().frameConversion(cornerPosition, galacticFrame, _target.frame, currentTime); + openspace::SpiceManager::ref().frameConversion(cornerPosition, GalacticFrame, _target.frame, currentTime); projection[j] = PowerScaledCoordinate::CreatePowerScaledCoordinate(cornerPosition[0], cornerPosition[1], cornerPosition[2]); projection[j][3] += 3; @@ -308,7 +313,7 @@ std::string RenderablePlaneProjection::findClosestTarget(double currentTime) { psc spacecraftPos; double lt; - SpiceManager::ref().getTargetPosition(_spacecraft, "SSB", galacticFrame, "NONE", currentTime, spacecraftPos, lt); + SpiceManager::ref().getTargetPosition(_spacecraft, "SSB", GalacticFrame, "NONE", currentTime, spacecraftPos, lt); for (auto node : nodes) diff --git a/src/rendering/renderablesphericalgrid.cpp b/src/rendering/renderablesphericalgrid.cpp index 05e009bd3a..5c6a1e8389 100644 --- a/src/rendering/renderablesphericalgrid.cpp +++ b/src/rendering/renderablesphericalgrid.cpp @@ -34,6 +34,12 @@ namespace { const std::string _loggerCat = "RenderableSphericalGrid"; + const std::string KeyGridType = "GridType"; + const std::string KeyGridColor = "GridColor"; + const std::string KeyGridMatrix = "GridMatrix"; + const std::string KeyGridSegments = "GridSegments"; + const std::string KeyGridRadius = "GridRadius"; + const std::string KeyGridParentsRotation = "ParentsRotation"; } namespace openspace { @@ -49,15 +55,15 @@ RenderableSphericalGrid::RenderableSphericalGrid(const ghoul::Dictionary& dictio , _mode(GL_LINES) { _gridMatrix = glm::mat4(1); - dictionary.getValue(constants::renderablesphericalgrid::gridType , _gridType); - dictionary.getValue(constants::renderablesphericalgrid::gridColor , _gridColor); + dictionary.getValue(KeyGridType, _gridType); + dictionary.getValue(KeyGridColor, _gridColor); - staticGrid = dictionary.getValue(constants::renderablesphericalgrid::gridMatrix, _gridMatrix); + staticGrid = dictionary.getValue(KeyGridMatrix, _gridMatrix); if (!staticGrid){ - staticGrid = dictionary.getValue(constants::renderablesphericalgrid::gridPatentsRotiation, _parentsRotation); + staticGrid = dictionary.getValue(KeyGridParentsRotation, _parentsRotation); } - dictionary.getValue(constants::renderablesphericalgrid::gridSegments, _segments); + dictionary.getValue(KeyGridSegments, _segments); /*glm::vec2 radius; diff --git a/src/rendering/renderablevolumegl.cpp b/src/rendering/renderablevolumegl.cpp index 9d58a0efc9..26452d692a 100644 --- a/src/rendering/renderablevolumegl.cpp +++ b/src/rendering/renderablevolumegl.cpp @@ -31,7 +31,6 @@ #include #include -// ghoul includes #include #include #include @@ -39,11 +38,17 @@ #include #include -// std #include namespace { const std::string _loggerCat = "RenderableVolumeGL"; + const std::string KeyVolume = "Volume"; + const std::string KeyHints = "Hints"; + const std::string KeyTransferFunction = "TransferFunction"; + const std::string KeySampler = "Sampler"; + const std::string KeyBoxScaling = "BoxScaling"; + const std::string KeyVolumeName = "VolumeName"; + const std::string KeyTransferFunctionName = "TransferFunctionName"; } namespace openspace { @@ -67,11 +72,9 @@ RenderableVolumeGL::RenderableVolumeGL(const ghoul::Dictionary& dictionary) assert(success); _filename = ""; - success = dictionary.getValue(constants::renderablevolumegl::keyVolume, - _filename); + success = dictionary.getValue(KeyVolume, _filename); if (!success) { - LERROR("Node '" << name << "' did not contain a valid '" << - constants::renderablevolumegl::keyVolume << "'"); + LERROR("Node '" << name << "' did not contain a valid '" << KeyVolume << "'"); return; } _filename = absPath(_filename); @@ -81,47 +84,42 @@ RenderableVolumeGL::RenderableVolumeGL(const ghoul::Dictionary& dictionary) LDEBUG("Volume Filename: " << _filename); - dictionary.getValue(constants::renderablevolumegl::keyHints, _hintsDictionary); + dictionary.getValue(KeyHints, _hintsDictionary); _transferFunction = nullptr; _transferFunctionFile = nullptr; _transferFunctionPath = ""; - success = dictionary.getValue( - constants::renderablevolumegl::keyTransferFunction, _transferFunctionPath); + success = dictionary.getValue(KeyTransferFunction, _transferFunctionPath); if (!success) { LERROR("Node '" << name << "' did not contain a valid '" << - constants::renderablevolumegl::keyTransferFunction << "'"); + KeyTransferFunction << "'"); return; } _transferFunctionPath = absPath(_transferFunctionPath); _transferFunctionFile = new ghoul::filesystem::File(_transferFunctionPath, true); _samplerFilename = ""; - success = dictionary.getValue(constants::renderablevolumegl::keySampler, - _samplerFilename); + success = dictionary.getValue(KeySampler, _samplerFilename); if (!success) { - LERROR("Node '" << name << "' did not contain a valid '" << - constants::renderablevolumegl::keySampler << "'"); + LERROR("Node '" << name << "' did not contain a valid '" << KeySampler << "'"); return; } _samplerFilename = absPath(_samplerFilename); KameleonWrapper kw(_filename); auto t = kw.getGridUnits(); - if (dictionary.hasKey(constants::renderablevolumegl::keyBoxScaling)) { + if (dictionary.hasKey(KeyBoxScaling)) { glm::vec4 scalingVec4(_boxScaling, _w); - success = dictionary.getValue(constants::renderablevolumegl::keyBoxScaling, - scalingVec4); + success = dictionary.getValue(KeyBoxScaling, scalingVec4); if (success) { _boxScaling = scalingVec4.xyz; _w = scalingVec4.w; } else { - success = dictionary.getValue(constants::renderablevolumegl::keyBoxScaling, - _boxScaling); + success = dictionary.getValue(KeyBoxScaling, _boxScaling); if (!success) { LERROR("Node '" << name << "' did not contain a valid '" << - constants::renderablevolumegl::keyBoxScaling << "'"); + KeyBoxScaling << "'"); return; } } @@ -157,9 +155,8 @@ RenderableVolumeGL::RenderableVolumeGL(const ghoul::Dictionary& dictionary) // Current spherical models no need for offset } - dictionary.getValue(constants::renderablevolumegl::keyVolumeName, _volumeName); - dictionary.getValue(constants::renderablevolumegl::keyTransferFunctionName, - _transferFunctionName); + dictionary.getValue(KeyVolumeName, _volumeName); + dictionary.getValue(KeyTransferFunctionName, _transferFunctionName); setBoundingSphere(PowerScaledScalar::CreatePSS(glm::length(_boxScaling)*pow(10,_w))); } diff --git a/src/rendering/stars/renderablestars.cpp b/src/rendering/stars/renderablestars.cpp index 4b0532fc90..2b3fc5d29c 100644 --- a/src/rendering/stars/renderablestars.cpp +++ b/src/rendering/stars/renderablestars.cpp @@ -24,7 +24,6 @@ #include -#include #include #include @@ -39,6 +38,10 @@ namespace { const std::string _loggerCat = "RenderableStars"; + const std::string KeyFile = "File"; + const std::string KeyTexture = "Texture"; + const std::string KeyColorMap = "ColorMap"; + ghoul::filesystem::File* _psfTextureFile; ghoul::filesystem::File* _colorTextureFile; @@ -98,18 +101,17 @@ RenderableStars::RenderableStars(const ghoul::Dictionary& dictionary) using ghoul::filesystem::File; std::string texturePath = ""; - dictionary.getValue(constants::renderablestars::keyTexture, texturePath); + dictionary.getValue(KeyTexture, texturePath); _pointSpreadFunctionTexturePath = absPath(texturePath); _psfTextureFile = new File(_pointSpreadFunctionTexturePath); - dictionary.getValue(constants::renderablestars::keyColorMap, texturePath); + dictionary.getValue(KeyColorMap, texturePath); _colorTexturePath = absPath(texturePath); _colorTextureFile = new File(_colorTexturePath); - bool success = dictionary.getValue(constants::renderablestars::keyFile, _speckFile); + bool success = dictionary.getValue(KeyFile, _speckFile); if (!success) { - LERROR("SpeckDataSource did not contain key '" << - constants::renderablestars::keyFile << "'"); + LERROR("SpeckDataSource did not contain key '" << KeyFile << "'"); return; } _speckFile = absPath(_speckFile); diff --git a/src/scene/dynamicephemeris.cpp b/src/scene/dynamicephemeris.cpp index 605be3aaef..46fe087d55 100644 --- a/src/scene/dynamicephemeris.cpp +++ b/src/scene/dynamicephemeris.cpp @@ -24,19 +24,19 @@ #include -#include +namespace { + const std::string KeyPosition = "Position"; +} namespace openspace { -using namespace constants::dynamicephemeris; - DynamicEphemeris::DynamicEphemeris(const ghoul::Dictionary& dictionary) : _position(0.f, 0.f, 0.f, 0.f) { - const bool hasPosition = dictionary.hasKeyAndValue(keyPosition); + const bool hasPosition = dictionary.hasKeyAndValue(KeyPosition); if (hasPosition) { glm::vec4 tmp; - dictionary.getValue(keyPosition, tmp); + dictionary.getValue(KeyPosition, tmp); _position = tmp; } } @@ -51,8 +51,6 @@ void DynamicEphemeris::setPosition(psc pos) { _position = pos; } -void DynamicEphemeris::update(const UpdateData&) { - -} +void DynamicEphemeris::update(const UpdateData&) {} } // namespace openspace \ No newline at end of file diff --git a/src/scene/ephemeris.cpp b/src/scene/ephemeris.cpp index ccb2acea24..f96f3aa299 100644 --- a/src/scene/ephemeris.cpp +++ b/src/scene/ephemeris.cpp @@ -27,20 +27,20 @@ #include namespace { -const std::string _loggerCat = "Ephemeris"; + const std::string _loggerCat = "Ephemeris"; + const std::string KeyType = "Type"; } namespace openspace { -Ephemeris* Ephemeris::createFromDictionary(const ghoul::Dictionary& dictionary) -{ - if (!dictionary.hasValue(constants::ephemeris::keyType)) { - LERROR("Ephemeris did not have key '" << constants::ephemeris::keyType << "'"); +Ephemeris* Ephemeris::createFromDictionary(const ghoul::Dictionary& dictionary) { + if (!dictionary.hasValue(KeyType)) { + LERROR("Ephemeris did not have key '" << KeyType << "'"); return nullptr; } std::string ephemerisType; - dictionary.getValue(constants::ephemeris::keyType, ephemerisType); + dictionary.getValue(KeyType, ephemerisType); ghoul::TemplateFactory* factory = FactoryManager::ref().factory(); Ephemeris* result = factory->create(ephemerisType, dictionary); @@ -52,17 +52,11 @@ Ephemeris* Ephemeris::createFromDictionary(const ghoul::Dictionary& dictionary) return result; } -Ephemeris::Ephemeris() -{ -} +Ephemeris::Ephemeris() {} -Ephemeris::Ephemeris(const ghoul::Dictionary& dictionary) -{ -} +Ephemeris::Ephemeris(const ghoul::Dictionary& dictionary) {} -Ephemeris::~Ephemeris() -{ -} +Ephemeris::~Ephemeris() {} bool Ephemeris::initialize() { return true; diff --git a/src/scene/scenegraphnode.cpp b/src/scene/scenegraphnode.cpp index d37a4821b1..4b6f28ea98 100644 --- a/src/scene/scenegraphnode.cpp +++ b/src/scene/scenegraphnode.cpp @@ -45,7 +45,9 @@ #include namespace { -const std::string _loggerCat = "SceneGraphNode"; + const std::string _loggerCat = "SceneGraphNode"; + const std::string KeyRenderable = "Renderable"; + const std::string KeyEphemeris = "Ephemeris"; } namespace openspace { @@ -57,24 +59,21 @@ const std::string SceneGraphNode::KeyDependencies = "Dependencies"; SceneGraphNode* SceneGraphNode::createFromDictionary(const ghoul::Dictionary& dictionary) { - using namespace constants::scenegraph; - using namespace constants::scenegraphnode; - SceneGraphNode* result = new SceneGraphNode; - if (!dictionary.hasValue(keyName)) { - LERROR("SceneGraphNode did not contain a '" << keyName << "' key"); + if (!dictionary.hasValue(KeyName)) { + LERROR("SceneGraphNode did not contain a '" << KeyName << "' key"); return nullptr; } std::string name; - dictionary.getValue(keyName, name); + dictionary.getValue(KeyName, name); result->setName(name); - if (dictionary.hasValue(keyRenderable)) { + if (dictionary.hasValue(KeyRenderable)) { ghoul::Dictionary renderableDictionary; - dictionary.getValue(keyRenderable, renderableDictionary); + dictionary.getValue(KeyRenderable, renderableDictionary); - renderableDictionary.setValue(keyName, name); + renderableDictionary.setValue(KeyName, name); result->_renderable = Renderable::createFromDictionary(renderableDictionary); if (result->_renderable == nullptr) { @@ -86,9 +85,9 @@ SceneGraphNode* SceneGraphNode::createFromDictionary(const ghoul::Dictionary& di LDEBUG("Successfully create renderable for '" << result->name() << "'"); } - if (dictionary.hasKey(keyEphemeris)) { + if (dictionary.hasKey(KeyEphemeris)) { ghoul::Dictionary ephemerisDictionary; - dictionary.getValue(keyEphemeris, ephemerisDictionary); + dictionary.getValue(KeyEphemeris, ephemerisDictionary); result->_ephemeris = Ephemeris::createFromDictionary(ephemerisDictionary); if (result->_ephemeris == nullptr) { LERROR("Failed to create ephemeris for SceneGraphNode '" @@ -100,9 +99,8 @@ SceneGraphNode* SceneGraphNode::createFromDictionary(const ghoul::Dictionary& di } std::string parentName; - if (!dictionary.getValue(constants::scenegraphnode::keyParentName, parentName)) { - LWARNING("Could not find '" << constants::scenegraphnode::keyParentName << - "' key, using 'Root'."); + if (!dictionary.getValue(KeyParentName, parentName)) { + LWARNING("Could not find '" << KeyParentName << "' key, using 'Root'."); parentName = "Root"; } @@ -130,13 +128,11 @@ SceneGraphNode::SceneGraphNode() { } -SceneGraphNode::~SceneGraphNode() -{ +SceneGraphNode::~SceneGraphNode() { deinitialize(); } -bool SceneGraphNode::initialize() -{ +bool SceneGraphNode::initialize() { if (_renderable) _renderable->initialize(); @@ -146,8 +142,7 @@ bool SceneGraphNode::initialize() return true; } -bool SceneGraphNode::deinitialize() -{ +bool SceneGraphNode::deinitialize() { LDEBUG("Deinitialize: " << name()); if (_renderable) { diff --git a/src/scene/spiceephemeris.cpp b/src/scene/spiceephemeris.cpp index f31c4489e3..1459d490a4 100644 --- a/src/scene/spiceephemeris.cpp +++ b/src/scene/spiceephemeris.cpp @@ -24,43 +24,44 @@ #include -#include #include #include #include namespace { const std::string _loggerCat = "SpiceEphemeris"; - const std::string keyGhosting = "EphmerisGhosting"; + //const std::string keyGhosting = "EphmerisGhosting"; + + const std::string KeyBody = "Body"; + const std::string KeyOrigin = "Observer"; + const std::string KeyKernels = "Kernels"; } namespace openspace { -using namespace constants::spiceephemeris; - SpiceEphemeris::SpiceEphemeris(const ghoul::Dictionary& dictionary) : _targetName("") , _originName("") , _position() , _kernelsLoadedSuccessfully(true) { - const bool hasBody = dictionary.getValue(keyBody, _targetName); + const bool hasBody = dictionary.getValue(KeyBody, _targetName); if (!hasBody) - LERROR("SpiceEphemeris does not contain the key '" << keyBody << "'"); + LERROR("SpiceEphemeris does not contain the key '" << KeyBody << "'"); - const bool hasObserver = dictionary.getValue(keyOrigin, _originName); + const bool hasObserver = dictionary.getValue(KeyOrigin, _originName); if (!hasObserver) - LERROR("SpiceEphemeris does not contain the key '" << keyOrigin << "'"); + LERROR("SpiceEphemeris does not contain the key '" << KeyOrigin << "'"); - dictionary.getValue(keyGhosting, _ghosting); + //dictionary.getValue(keyGhosting, _ghosting); ghoul::Dictionary kernels; - dictionary.getValue(keyKernels, kernels); + dictionary.getValue(KeyKernels, kernels); for (size_t i = 1; i <= kernels.size(); ++i) { std::string kernel; bool success = kernels.getValue(std::to_string(i), kernel); if (!success) - LERROR("'" << keyKernels << "' has to be an array-style table"); + LERROR("'" << KeyKernels << "' has to be an array-style table"); SpiceManager::KernelIdentifier id = SpiceManager::ref().loadKernel(kernel); _kernelsLoadedSuccessfully &= (id != SpiceManager::KernelFailed); @@ -80,13 +81,13 @@ void SpiceEphemeris::update(const UpdateData& data) { SpiceManager::ref().getTargetPosition(_targetName, _originName, "GALACTIC", "NONE", data.time, position, lightTime); - double interval = openspace::ImageSequencer2::ref().getIntervalLength(); - if (_ghosting == "TRUE" && interval > 60){ - double _time = openspace::ImageSequencer2::ref().getNextCaptureTime(); - SpiceManager::ref().getTargetPosition(_targetName, _originName, - "GALACTIC", "NONE", _time, position, lightTime); - } - + //double interval = openspace::ImageSequencer2::ref().getIntervalLength(); + //if (_ghosting == "TRUE" && interval > 60){ + // double _time = openspace::ImageSequencer2::ref().getNextCaptureTime(); + // SpiceManager::ref().getTargetPosition(_targetName, _originName, + // "GALACTIC", "NONE", _time, position, lightTime); + //} + // _position = psc::CreatePowerScaledCoordinate(position.x, position.y, position.z); _position[3] += 3; } diff --git a/src/scene/staticephemeris.cpp b/src/scene/staticephemeris.cpp index a87f75b897..e7bb684c51 100644 --- a/src/scene/staticephemeris.cpp +++ b/src/scene/staticephemeris.cpp @@ -24,19 +24,19 @@ #include -#include +namespace { + const std::string KeyPosition = "Position"; +} namespace openspace { -using namespace constants::staticephemeris; - StaticEphemeris::StaticEphemeris(const ghoul::Dictionary& dictionary) : _position(0.f, 0.f, 0.f, 0.f) { - const bool hasPosition = dictionary.hasKeyAndValue(keyPosition); + const bool hasPosition = dictionary.hasKeyAndValue(KeyPosition); if (hasPosition) { glm::vec4 tmp; - dictionary.getValue(keyPosition, tmp); + dictionary.getValue(KeyPosition, tmp); _position = tmp; } } @@ -47,8 +47,6 @@ const psc& StaticEphemeris::position() const { return _position; } -void StaticEphemeris::update(const UpdateData&) { - -} +void StaticEphemeris::update(const UpdateData&) {} } // namespace openspace \ No newline at end of file From 523abd65298c4fb1cf55f39ee7c1018c5b9fa898 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 20 May 2015 19:19:10 +0200 Subject: [PATCH 028/329] Modularized the base classes --- CMakeLists.txt | 1 + include/openspace/engine/moduleengine.h | 50 ++++++++++ include/openspace/engine/openspaceengine.h | 3 + include/openspace/util/openspacemodule.h | 50 ++++++++++ modules/base/CMakeLists.txt | 46 +++++++++ modules/base/basemodule.cpp | 88 +++++++++++++++++ modules/base/basemodule.h | 40 ++++++++ .../base/rendering}/modelgeometry.cpp | 4 +- .../base/rendering}/modelgeometry.h | 2 +- .../base/rendering}/planetgeometry.cpp | 2 +- .../base/rendering}/planetgeometry.h | 2 +- .../renderableconstellationbounds.cpp | 2 +- .../renderableconstellationbounds.h | 0 .../rendering/renderablecrawlingline.cpp | 4 +- .../base}/rendering/renderablecrawlingline.h | 0 .../base}/rendering/renderablefieldlines.cpp | 2 +- .../base}/rendering/renderablefieldlines.h | 0 .../base/rendering}/renderablemodel.cpp | 5 +- .../base/rendering}/renderablemodel.h | 0 .../base}/rendering/renderablepath.cpp | 2 +- .../base}/rendering/renderablepath.h | 0 .../base}/rendering/renderableplane.cpp | 2 +- .../base}/rendering/renderableplane.h | 0 .../base/rendering}/renderableplanet.cpp | 4 +- .../base/rendering}/renderableplanet.h | 0 .../base}/rendering/renderablesphere.cpp | 2 +- .../base}/rendering/renderablesphere.h | 0 .../rendering/renderablesphericalgrid.cpp | 2 +- .../base}/rendering/renderablesphericalgrid.h | 0 .../base/rendering}/renderablestars.cpp | 2 +- .../base/rendering}/renderablestars.h | 0 .../base}/rendering/renderabletrail.cpp | 2 +- .../base}/rendering/renderabletrail.h | 0 .../base/rendering}/simplespheregeometry.cpp | 2 +- .../base/rendering}/simplespheregeometry.h | 2 +- .../base/rendering}/wavefrontgeometry.cpp | 2 +- .../base/rendering}/wavefrontgeometry.h | 2 +- .../rendering}/planetgeometryprojection.cpp | 0 .../rendering}/planetgeometryprojection.h | 0 .../projection}/rendering/renderablefov.cpp | 0 .../projection}/rendering/renderablefov.h | 0 .../rendering/renderableplaneprojection.cpp | 0 .../rendering/renderableplaneprojection.h | 0 .../rendering}/renderableplanetprojection.cpp | 0 .../rendering}/renderableplanetprojection.h | 0 .../simplespheregeometryprojection.cpp | 0 .../simplespheregeometryprojection.h | 0 .../projection/rendering}/writeToTexture.h | 0 .../volume}/rendering/renderablevolume.cpp | 0 .../volume}/rendering/renderablevolume.h | 0 .../volume}/rendering/renderablevolumegl.cpp | 0 .../volume}/rendering/renderablevolumegl.h | 0 openspace.cfg | 2 +- src/CMakeLists.txt | 4 + src/engine/moduleengine.cpp | 76 +++++++++++++++ src/engine/openspaceengine.cpp | 22 ++++- src/rendering/renderengine.cpp | 3 +- src/scene/scene.cpp | 2 +- src/scene/scenegraph.cpp | 6 +- src/scene/scenegraphnode.cpp | 2 + src/util/factorymanager.cpp | 95 ++++++------------- 61 files changed, 438 insertions(+), 99 deletions(-) create mode 100644 include/openspace/engine/moduleengine.h create mode 100644 include/openspace/util/openspacemodule.h create mode 100644 modules/base/CMakeLists.txt create mode 100644 modules/base/basemodule.cpp create mode 100644 modules/base/basemodule.h rename {src/rendering/model => modules/base/rendering}/modelgeometry.cpp (98%) rename {include/openspace/rendering/model => modules/base/rendering}/modelgeometry.h (98%) rename {src/rendering/planets => modules/base/rendering}/planetgeometry.cpp (98%) rename {include/openspace/rendering/planets => modules/base/rendering}/planetgeometry.h (97%) rename {src/rendering/stars => modules/base/rendering}/renderableconstellationbounds.cpp (99%) rename {include/openspace/rendering/stars => modules/base/rendering}/renderableconstellationbounds.h (100%) rename {src => modules/base}/rendering/renderablecrawlingline.cpp (98%) rename {include/openspace => modules/base}/rendering/renderablecrawlingline.h (100%) rename {src => modules/base}/rendering/renderablefieldlines.cpp (99%) rename {include/openspace => modules/base}/rendering/renderablefieldlines.h (100%) rename {src/rendering/model => modules/base/rendering}/renderablemodel.cpp (98%) rename {include/openspace/rendering/model => modules/base/rendering}/renderablemodel.h (100%) rename {src => modules/base}/rendering/renderablepath.cpp (99%) rename {include/openspace => modules/base}/rendering/renderablepath.h (100%) rename {src => modules/base}/rendering/renderableplane.cpp (99%) rename {include/openspace => modules/base}/rendering/renderableplane.h (100%) rename {src/rendering/planets => modules/base/rendering}/renderableplanet.cpp (98%) rename {include/openspace/rendering/planets => modules/base/rendering}/renderableplanet.h (100%) rename {src => modules/base}/rendering/renderablesphere.cpp (99%) rename {include/openspace => modules/base}/rendering/renderablesphere.h (100%) rename {src => modules/base}/rendering/renderablesphericalgrid.cpp (99%) rename {include/openspace => modules/base}/rendering/renderablesphericalgrid.h (100%) rename {src/rendering/stars => modules/base/rendering}/renderablestars.cpp (99%) rename {include/openspace/rendering/stars => modules/base/rendering}/renderablestars.h (100%) rename {src => modules/base}/rendering/renderabletrail.cpp (99%) rename {include/openspace => modules/base}/rendering/renderabletrail.h (100%) rename {src/rendering/planets => modules/base/rendering}/simplespheregeometry.cpp (98%) rename {include/openspace/rendering/planets => modules/base/rendering}/simplespheregeometry.h (98%) rename {src/rendering/model => modules/base/rendering}/wavefrontgeometry.cpp (98%) rename {include/openspace/rendering/model => modules/base/rendering}/wavefrontgeometry.h (97%) rename {src/rendering/planets => modules/projection/rendering}/planetgeometryprojection.cpp (100%) rename {include/openspace/rendering/planets => modules/projection/rendering}/planetgeometryprojection.h (100%) rename {src => modules/projection}/rendering/renderablefov.cpp (100%) rename {include/openspace => modules/projection}/rendering/renderablefov.h (100%) rename {src => modules/projection}/rendering/renderableplaneprojection.cpp (100%) rename {include/openspace => modules/projection}/rendering/renderableplaneprojection.h (100%) rename {src/rendering/planets => modules/projection/rendering}/renderableplanetprojection.cpp (100%) rename {include/openspace/rendering/planets => modules/projection/rendering}/renderableplanetprojection.h (100%) rename {src/rendering/planets => modules/projection/rendering}/simplespheregeometryprojection.cpp (100%) rename {include/openspace/rendering/planets => modules/projection/rendering}/simplespheregeometryprojection.h (100%) rename {include/openspace/rendering/planets => modules/projection/rendering}/writeToTexture.h (100%) rename {src => modules/volume}/rendering/renderablevolume.cpp (100%) rename {include/openspace => modules/volume}/rendering/renderablevolume.h (100%) rename {src => modules/volume}/rendering/renderablevolumegl.cpp (100%) rename {include/openspace => modules/volume}/rendering/renderablevolumegl.h (100%) create mode 100644 src/engine/moduleengine.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index be0d4644c7..efa5b8982f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -145,6 +145,7 @@ endif () ######################################################################################### add_subdirectory(src) +add_subdirectory(modules/base) option(BUILD_GUI_APPLICATIONS "Build GUI Applications" OFF) if (BUILD_GUI_APPLICATIONS) diff --git a/include/openspace/engine/moduleengine.h b/include/openspace/engine/moduleengine.h new file mode 100644 index 0000000000..229af21f84 --- /dev/null +++ b/include/openspace/engine/moduleengine.h @@ -0,0 +1,50 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __MODULEENGINE_H__ +#define __MODULEENGINE_H__ + +#include + +namespace openspace { + +class OpenSpaceModule; + +class ModuleEngine { +public: + bool initialize(); + bool deinitialize(); + + void registerModules(std::vector modules); + void registerModule(OpenSpaceModule* module); + const std::vector modules() const; + +protected: + std::vector _modules; +}; + + +} // namespace openspace + +#endif // __MODULEENGINE_H__ diff --git a/include/openspace/engine/openspaceengine.h b/include/openspace/engine/openspaceengine.h index e4b593d97c..78956f5a6a 100644 --- a/include/openspace/engine/openspaceengine.h +++ b/include/openspace/engine/openspaceengine.h @@ -44,6 +44,7 @@ class NetworkEngine; class GUI; class RenderEngine; class SyncBuffer; +class ModuleEngine; namespace interaction { class InteractionHandler; @@ -71,6 +72,7 @@ public: scripting::ScriptEngine* scriptEngine(); NetworkEngine* networkEngine(); LuaConsole* console(); + ModuleEngine* moduleEngine(); gui::GUI* gui(); @@ -116,6 +118,7 @@ private: NetworkEngine* _networkEngine; ghoul::cmdparser::CommandlineParser* _commandlineParser; LuaConsole* _console; + ModuleEngine* _moduleEngine; gui::GUI* _gui; bool _isMaster; diff --git a/include/openspace/util/openspacemodule.h b/include/openspace/util/openspacemodule.h new file mode 100644 index 0000000000..b0a73522ac --- /dev/null +++ b/include/openspace/util/openspacemodule.h @@ -0,0 +1,50 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACEMODULE_H__ +#define __OPENSPACEMODULE_H__ + +#include + +namespace openspace { + +class OpenSpaceModule { +public: + OpenSpaceModule() = default; + virtual ~OpenSpaceModule() = default; + + virtual bool initialize() { return true; } + virtual bool deinitialize() { return true; } + + std::string name() const { return _name; } + +protected: + void setName(std::string name) { _name = std::move(name); } + + std::string _name; +}; + +} // namespace openspace + +#endif // __OPENSPACEMODULE_H__ diff --git a/modules/base/CMakeLists.txt b/modules/base/CMakeLists.txt new file mode 100644 index 0000000000..151f1ca882 --- /dev/null +++ b/modules/base/CMakeLists.txt @@ -0,0 +1,46 @@ +set(HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/modelgeometry.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometry.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableconstellationbounds.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablecrawlingline.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefieldlines.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablemodel.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablepath.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplane.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplanet.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablesphere.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablesphericalgrid.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablestars.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderabletrail.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometry.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/wavefrontgeometry.h +) +source_group("Header Files" FILES ${HEADER_FILES}) + +set(SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/modelgeometry.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometry.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableconstellationbounds.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablecrawlingline.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefieldlines.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablemodel.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablepath.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplane.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplanet.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablesphere.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablesphericalgrid.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablestars.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderabletrail.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometry.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/wavefrontgeometry.cpp +) +source_group("Source Files" FILES ${SOURCE_FILES}) + +set(MODULE_CLASS_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/basemodule.h + ${CMAKE_CURRENT_SOURCE_DIR}/basemodule.cpp +) + +include_directories(${OPENSPACE_BASE_DIR}/include ${OPENSPACE_BASE_DIR}) + +add_library(openspace-module-base ${HEADER_FILES} ${SOURCE_FILES} ${MODULE_CLASS_FILES}) diff --git a/modules/base/basemodule.cpp b/modules/base/basemodule.cpp new file mode 100644 index 0000000000..bcf820d7b9 --- /dev/null +++ b/modules/base/basemodule.cpp @@ -0,0 +1,88 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + const std::string _loggerCat = "BaseModule"; +} + +namespace openspace { + +bool BaseModule::initialize() { + FactoryManager::ref().addFactory(new ghoul::TemplateFactory); + FactoryManager::ref().addFactory(new ghoul::TemplateFactory); + + auto fRenderable = FactoryManager::ref().factory(); + ghoul_assert(fRenderable, "No renderable factory existed"); + + fRenderable->registerClass("RenderablePlanet"); + fRenderable->registerClass("RenderableStars"); + fRenderable->registerClass("RenderableConstellationBounds"); + fRenderable->registerClass("RenderablePath"); + fRenderable->registerClass("RenderableTrail"); + fRenderable->registerClass("RenderableSphere"); + fRenderable->registerClass("RenderableSphericalGrid"); + fRenderable->registerClass("RenderableModel"); + fRenderable->registerClass("RenderablePlane"); + fRenderable->registerClass("RenderableFieldlines"); + fRenderable->registerClass("RenderableCrawlingLine"); + + auto fPlanetGeometry = FactoryManager::ref().factory(); + ghoul_assert(fPlanetGeometry, "No planet geometry factory existed"); + fPlanetGeometry->registerClass("SimpleSphere"); + + auto fModelGeometry = FactoryManager::ref().factory(); + ghoul_assert(fModelGeometry, "No model geometry factory existed"); + fModelGeometry->registerClass("WavefrontGeometry"); + + return true; +} + +bool BaseModule::deinitialize() { + return false; +} + +} // namespace openspace diff --git a/modules/base/basemodule.h b/modules/base/basemodule.h new file mode 100644 index 0000000000..3a2fe48c0b --- /dev/null +++ b/modules/base/basemodule.h @@ -0,0 +1,40 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __BASEMODULE_H__ +#define __BASEMODULE_H__ + +#include + +namespace openspace { + +class BaseModule : public OpenSpaceModule { +public: + bool initialize() override; + bool deinitialize() override; +}; + +} // namespace openspace + +#endif // __BASEMODULE_H__ diff --git a/src/rendering/model/modelgeometry.cpp b/modules/base/rendering/modelgeometry.cpp similarity index 98% rename from src/rendering/model/modelgeometry.cpp rename to modules/base/rendering/modelgeometry.cpp index 5868e8f894..a390bae9a2 100644 --- a/src/rendering/model/modelgeometry.cpp +++ b/modules/base/rendering/modelgeometry.cpp @@ -22,14 +22,14 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include #include #include #include -#include +#include namespace { const std::string _loggerCat = "ModelGeometry"; diff --git a/include/openspace/rendering/model/modelgeometry.h b/modules/base/rendering/modelgeometry.h similarity index 98% rename from include/openspace/rendering/model/modelgeometry.h rename to modules/base/rendering/modelgeometry.h index edebfa8480..b184ee172b 100644 --- a/include/openspace/rendering/model/modelgeometry.h +++ b/modules/base/rendering/modelgeometry.h @@ -26,7 +26,7 @@ #define __MODELGEOMETRY_H__ #include -#include +#include #include namespace openspace { diff --git a/src/rendering/planets/planetgeometry.cpp b/modules/base/rendering/planetgeometry.cpp similarity index 98% rename from src/rendering/planets/planetgeometry.cpp rename to modules/base/rendering/planetgeometry.cpp index 0d9a744820..062fb8db39 100644 --- a/src/rendering/planets/planetgeometry.cpp +++ b/modules/base/rendering/planetgeometry.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include #include diff --git a/include/openspace/rendering/planets/planetgeometry.h b/modules/base/rendering/planetgeometry.h similarity index 97% rename from include/openspace/rendering/planets/planetgeometry.h rename to modules/base/rendering/planetgeometry.h index 329144cb67..58fa3ab3ef 100644 --- a/include/openspace/rendering/planets/planetgeometry.h +++ b/modules/base/rendering/planetgeometry.h @@ -26,7 +26,7 @@ #define __PLANETGEOMETRY_H__ #include -#include +#include #include namespace openspace { diff --git a/src/rendering/stars/renderableconstellationbounds.cpp b/modules/base/rendering/renderableconstellationbounds.cpp similarity index 99% rename from src/rendering/stars/renderableconstellationbounds.cpp rename to modules/base/rendering/renderableconstellationbounds.cpp index 25f87139b4..9837d56656 100644 --- a/src/rendering/stars/renderableconstellationbounds.cpp +++ b/modules/base/rendering/renderableconstellationbounds.cpp @@ -23,7 +23,7 @@ ****************************************************************************************/ // openspace -#include +#include #include #include diff --git a/include/openspace/rendering/stars/renderableconstellationbounds.h b/modules/base/rendering/renderableconstellationbounds.h similarity index 100% rename from include/openspace/rendering/stars/renderableconstellationbounds.h rename to modules/base/rendering/renderableconstellationbounds.h diff --git a/src/rendering/renderablecrawlingline.cpp b/modules/base/rendering/renderablecrawlingline.cpp similarity index 98% rename from src/rendering/renderablecrawlingline.cpp rename to modules/base/rendering/renderablecrawlingline.cpp index b9510bc6cd..58e3da9e85 100644 --- a/src/rendering/renderablecrawlingline.cpp +++ b/modules/base/rendering/renderablecrawlingline.cpp @@ -22,13 +22,13 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include #include #include -#include +//#include namespace { const std::string _loggerCat = "RenderableCrawlingLine"; diff --git a/include/openspace/rendering/renderablecrawlingline.h b/modules/base/rendering/renderablecrawlingline.h similarity index 100% rename from include/openspace/rendering/renderablecrawlingline.h rename to modules/base/rendering/renderablecrawlingline.h diff --git a/src/rendering/renderablefieldlines.cpp b/modules/base/rendering/renderablefieldlines.cpp similarity index 99% rename from src/rendering/renderablefieldlines.cpp rename to modules/base/rendering/renderablefieldlines.cpp index 0df2d05e98..62a6503c67 100644 --- a/src/rendering/renderablefieldlines.cpp +++ b/modules/base/rendering/renderablefieldlines.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include #include diff --git a/include/openspace/rendering/renderablefieldlines.h b/modules/base/rendering/renderablefieldlines.h similarity index 100% rename from include/openspace/rendering/renderablefieldlines.h rename to modules/base/rendering/renderablefieldlines.h diff --git a/src/rendering/model/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp similarity index 98% rename from src/rendering/model/renderablemodel.cpp rename to modules/base/rendering/renderablemodel.cpp index d53f24d42e..ccd6bb70bd 100644 --- a/src/rendering/model/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -26,9 +26,9 @@ // ie after I see model on screen) // open space includes -#include +#include #include -#include +#include #include #include @@ -41,7 +41,6 @@ #include #include -#include "imgui.h" #define _USE_MATH_DEFINES #include diff --git a/include/openspace/rendering/model/renderablemodel.h b/modules/base/rendering/renderablemodel.h similarity index 100% rename from include/openspace/rendering/model/renderablemodel.h rename to modules/base/rendering/renderablemodel.h diff --git a/src/rendering/renderablepath.cpp b/modules/base/rendering/renderablepath.cpp similarity index 99% rename from src/rendering/renderablepath.cpp rename to modules/base/rendering/renderablepath.cpp index 9a3a2eff43..2108a23f6d 100644 --- a/src/rendering/renderablepath.cpp +++ b/modules/base/rendering/renderablepath.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include diff --git a/include/openspace/rendering/renderablepath.h b/modules/base/rendering/renderablepath.h similarity index 100% rename from include/openspace/rendering/renderablepath.h rename to modules/base/rendering/renderablepath.h diff --git a/src/rendering/renderableplane.cpp b/modules/base/rendering/renderableplane.cpp similarity index 99% rename from src/rendering/renderableplane.cpp rename to modules/base/rendering/renderableplane.cpp index c001effc75..96b18ad9cf 100644 --- a/src/rendering/renderableplane.cpp +++ b/modules/base/rendering/renderableplane.cpp @@ -23,7 +23,7 @@ ****************************************************************************************/ #include -#include +#include #include #include #include diff --git a/include/openspace/rendering/renderableplane.h b/modules/base/rendering/renderableplane.h similarity index 100% rename from include/openspace/rendering/renderableplane.h rename to modules/base/rendering/renderableplane.h diff --git a/src/rendering/planets/renderableplanet.cpp b/modules/base/rendering/renderableplanet.cpp similarity index 98% rename from src/rendering/planets/renderableplanet.cpp rename to modules/base/rendering/renderableplanet.cpp index 803ed7c182..f84d7dc599 100644 --- a/src/rendering/planets/renderableplanet.cpp +++ b/modules/base/rendering/renderableplanet.cpp @@ -23,11 +23,11 @@ ****************************************************************************************/ // open space includes -#include +#include #include #include -#include +#include #include #include #include diff --git a/include/openspace/rendering/planets/renderableplanet.h b/modules/base/rendering/renderableplanet.h similarity index 100% rename from include/openspace/rendering/planets/renderableplanet.h rename to modules/base/rendering/renderableplanet.h diff --git a/src/rendering/renderablesphere.cpp b/modules/base/rendering/renderablesphere.cpp similarity index 99% rename from src/rendering/renderablesphere.cpp rename to modules/base/rendering/renderablesphere.cpp index 7606d7180e..996f5e9863 100644 --- a/src/rendering/renderablesphere.cpp +++ b/modules/base/rendering/renderablesphere.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include diff --git a/include/openspace/rendering/renderablesphere.h b/modules/base/rendering/renderablesphere.h similarity index 100% rename from include/openspace/rendering/renderablesphere.h rename to modules/base/rendering/renderablesphere.h diff --git a/src/rendering/renderablesphericalgrid.cpp b/modules/base/rendering/renderablesphericalgrid.cpp similarity index 99% rename from src/rendering/renderablesphericalgrid.cpp rename to modules/base/rendering/renderablesphericalgrid.cpp index 5c6a1e8389..a1d0bce088 100644 --- a/src/rendering/renderablesphericalgrid.cpp +++ b/modules/base/rendering/renderablesphericalgrid.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include diff --git a/include/openspace/rendering/renderablesphericalgrid.h b/modules/base/rendering/renderablesphericalgrid.h similarity index 100% rename from include/openspace/rendering/renderablesphericalgrid.h rename to modules/base/rendering/renderablesphericalgrid.h diff --git a/src/rendering/stars/renderablestars.cpp b/modules/base/rendering/renderablestars.cpp similarity index 99% rename from src/rendering/stars/renderablestars.cpp rename to modules/base/rendering/renderablestars.cpp index 2b3fc5d29c..079645859b 100644 --- a/src/rendering/stars/renderablestars.cpp +++ b/modules/base/rendering/renderablestars.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * *****************************************************************************************/ -#include +#include #include diff --git a/include/openspace/rendering/stars/renderablestars.h b/modules/base/rendering/renderablestars.h similarity index 100% rename from include/openspace/rendering/stars/renderablestars.h rename to modules/base/rendering/renderablestars.h diff --git a/src/rendering/renderabletrail.cpp b/modules/base/rendering/renderabletrail.cpp similarity index 99% rename from src/rendering/renderabletrail.cpp rename to modules/base/rendering/renderabletrail.cpp index b02ec2d3f2..6110b6056a 100644 --- a/src/rendering/renderabletrail.cpp +++ b/modules/base/rendering/renderabletrail.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include diff --git a/include/openspace/rendering/renderabletrail.h b/modules/base/rendering/renderabletrail.h similarity index 100% rename from include/openspace/rendering/renderabletrail.h rename to modules/base/rendering/renderabletrail.h diff --git a/src/rendering/planets/simplespheregeometry.cpp b/modules/base/rendering/simplespheregeometry.cpp similarity index 98% rename from src/rendering/planets/simplespheregeometry.cpp rename to modules/base/rendering/simplespheregeometry.cpp index caa0a7cc19..99dd290833 100644 --- a/src/rendering/planets/simplespheregeometry.cpp +++ b/modules/base/rendering/simplespheregeometry.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include diff --git a/include/openspace/rendering/planets/simplespheregeometry.h b/modules/base/rendering/simplespheregeometry.h similarity index 98% rename from include/openspace/rendering/planets/simplespheregeometry.h rename to modules/base/rendering/simplespheregeometry.h index 6a34d8e138..2da64b8109 100644 --- a/include/openspace/rendering/planets/simplespheregeometry.h +++ b/modules/base/rendering/simplespheregeometry.h @@ -25,7 +25,7 @@ #ifndef __SIMPLESPHEREGEOMETRY_H__ #define __SIMPLESPHEREGEOMETRY_H__ -#include +#include #include #include diff --git a/src/rendering/model/wavefrontgeometry.cpp b/modules/base/rendering/wavefrontgeometry.cpp similarity index 98% rename from src/rendering/model/wavefrontgeometry.cpp rename to modules/base/rendering/wavefrontgeometry.cpp index 4d7df39c73..ae308b01be 100644 --- a/src/rendering/model/wavefrontgeometry.cpp +++ b/modules/base/rendering/wavefrontgeometry.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include "ghoul/logging/logmanager.h" diff --git a/include/openspace/rendering/model/wavefrontgeometry.h b/modules/base/rendering/wavefrontgeometry.h similarity index 97% rename from include/openspace/rendering/model/wavefrontgeometry.h rename to modules/base/rendering/wavefrontgeometry.h index 31720b2b7c..aa61388ac6 100644 --- a/include/openspace/rendering/model/wavefrontgeometry.h +++ b/modules/base/rendering/wavefrontgeometry.h @@ -25,7 +25,7 @@ #ifndef __WAVEFRONTGEOMETRY_H__ #define __WAVEFRONTGEOMETRY_H__ -#include +#include namespace openspace { diff --git a/src/rendering/planets/planetgeometryprojection.cpp b/modules/projection/rendering/planetgeometryprojection.cpp similarity index 100% rename from src/rendering/planets/planetgeometryprojection.cpp rename to modules/projection/rendering/planetgeometryprojection.cpp diff --git a/include/openspace/rendering/planets/planetgeometryprojection.h b/modules/projection/rendering/planetgeometryprojection.h similarity index 100% rename from include/openspace/rendering/planets/planetgeometryprojection.h rename to modules/projection/rendering/planetgeometryprojection.h diff --git a/src/rendering/renderablefov.cpp b/modules/projection/rendering/renderablefov.cpp similarity index 100% rename from src/rendering/renderablefov.cpp rename to modules/projection/rendering/renderablefov.cpp diff --git a/include/openspace/rendering/renderablefov.h b/modules/projection/rendering/renderablefov.h similarity index 100% rename from include/openspace/rendering/renderablefov.h rename to modules/projection/rendering/renderablefov.h diff --git a/src/rendering/renderableplaneprojection.cpp b/modules/projection/rendering/renderableplaneprojection.cpp similarity index 100% rename from src/rendering/renderableplaneprojection.cpp rename to modules/projection/rendering/renderableplaneprojection.cpp diff --git a/include/openspace/rendering/renderableplaneprojection.h b/modules/projection/rendering/renderableplaneprojection.h similarity index 100% rename from include/openspace/rendering/renderableplaneprojection.h rename to modules/projection/rendering/renderableplaneprojection.h diff --git a/src/rendering/planets/renderableplanetprojection.cpp b/modules/projection/rendering/renderableplanetprojection.cpp similarity index 100% rename from src/rendering/planets/renderableplanetprojection.cpp rename to modules/projection/rendering/renderableplanetprojection.cpp diff --git a/include/openspace/rendering/planets/renderableplanetprojection.h b/modules/projection/rendering/renderableplanetprojection.h similarity index 100% rename from include/openspace/rendering/planets/renderableplanetprojection.h rename to modules/projection/rendering/renderableplanetprojection.h diff --git a/src/rendering/planets/simplespheregeometryprojection.cpp b/modules/projection/rendering/simplespheregeometryprojection.cpp similarity index 100% rename from src/rendering/planets/simplespheregeometryprojection.cpp rename to modules/projection/rendering/simplespheregeometryprojection.cpp diff --git a/include/openspace/rendering/planets/simplespheregeometryprojection.h b/modules/projection/rendering/simplespheregeometryprojection.h similarity index 100% rename from include/openspace/rendering/planets/simplespheregeometryprojection.h rename to modules/projection/rendering/simplespheregeometryprojection.h diff --git a/include/openspace/rendering/planets/writeToTexture.h b/modules/projection/rendering/writeToTexture.h similarity index 100% rename from include/openspace/rendering/planets/writeToTexture.h rename to modules/projection/rendering/writeToTexture.h diff --git a/src/rendering/renderablevolume.cpp b/modules/volume/rendering/renderablevolume.cpp similarity index 100% rename from src/rendering/renderablevolume.cpp rename to modules/volume/rendering/renderablevolume.cpp diff --git a/include/openspace/rendering/renderablevolume.h b/modules/volume/rendering/renderablevolume.h similarity index 100% rename from include/openspace/rendering/renderablevolume.h rename to modules/volume/rendering/renderablevolume.h diff --git a/src/rendering/renderablevolumegl.cpp b/modules/volume/rendering/renderablevolumegl.cpp similarity index 100% rename from src/rendering/renderablevolumegl.cpp rename to modules/volume/rendering/renderablevolumegl.cpp diff --git a/include/openspace/rendering/renderablevolumegl.h b/modules/volume/rendering/renderablevolumegl.h similarity index 100% rename from include/openspace/rendering/renderablevolumegl.h rename to modules/volume/rendering/renderablevolumegl.h diff --git a/openspace.cfg b/openspace.cfg index 505bf7812c..dec8375c58 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -7,7 +7,7 @@ return { -- Sets the scene that is to be loaded by OpenSpace. A scene file is a description -- of all entities that will be visible during an instance of OpenSpace - Scene = "${OPENSPACE_DATA}/scene/default_nh.scene", + Scene = "${OPENSPACE_DATA}/scene/default.scene", Paths = { SGCT = "${BASE_PATH}/config/sgct", diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d8f8e10cad..014d3dfe24 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -151,7 +151,11 @@ if (UNIX AND NOT APPLE) endif (UNIX AND NOT APPLE) +set(DEPENDENT_LIBS ${DEPENDENT_LIBS} openspace-module-base) +message(${DEPENDENT_LIBS}) + include_directories("${HEADER_ROOT_DIR}") +include_directories("${OPENSPACE_BASE_DIR}") add_executable(OpenSpace ${SOURCE_ROOT_DIR}/main.cpp ${OPENSPACE_HEADER} ${OPENSPACE_SOURCE}) target_link_libraries(OpenSpace ${DEPENDENT_LIBS}) diff --git a/src/engine/moduleengine.cpp b/src/engine/moduleengine.cpp new file mode 100644 index 0000000000..821b7abe68 --- /dev/null +++ b/src/engine/moduleengine.cpp @@ -0,0 +1,76 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include + +#include + +namespace { + const std::string _loggerCat = "ModuleEngine"; +} + +namespace openspace { + +bool ModuleEngine::initialize() { + LDEBUG("Initializing modules"); + for (OpenSpaceModule* m : _modules) { + bool success = m->initialize(); + if (!success) { + LERROR("Could not initialize module '" << m->name() << "'"); + return false; + } + } + LDEBUG("Finished initializing modules"); + return true; +} + +bool ModuleEngine::deinitialize() { + LDEBUG("Deinitializing modules"); + for (OpenSpaceModule* m : _modules) { + bool success = m->deinitialize(); + if (!success) { + LERROR("Could not deinitialize module '" << m->name() << "'"); + return false; + } + } + LDEBUG("Finished Deinitializing modules"); + return true; +} + +void ModuleEngine::registerModules(std::vector modules) { + _modules.insert(_modules.end(), modules.begin(), modules.end()); +} + +void ModuleEngine::registerModule(OpenSpaceModule* module) { + _modules.push_back(std::move(module)); +} + +const std::vector ModuleEngine::modules() const { + return _modules; +} + + +} // namespace openspace diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index f1277c5d58..02f5ab38e5 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -46,7 +46,7 @@ #include #include #include // testing - +#include #include @@ -89,6 +89,7 @@ namespace { } commandlineArgumentPlaceholders; } +#include namespace openspace { @@ -102,14 +103,19 @@ OpenSpaceEngine::OpenSpaceEngine(std::string programName) , _networkEngine(new NetworkEngine) , _commandlineParser(new ghoul::cmdparser::CommandlineParser(programName, true)) , _console(new LuaConsole) + , _moduleEngine(new ModuleEngine) , _gui(new gui::GUI) , _isMaster(false) , _syncBuffer(nullptr) { - SpiceManager::initialize(); - Time::initialize(); - FactoryManager::initialize(); - ghoul::systemcapabilities::SystemCapabilities::initialize(); + FactoryManager::initialize(); + SpiceManager::initialize(); + Time::initialize(); + ghoul::systemcapabilities::SystemCapabilities::initialize(); + + // Register modules + _moduleEngine->registerModule(new BaseModule); + _moduleEngine->initialize(); } OpenSpaceEngine::~OpenSpaceEngine() { @@ -122,6 +128,7 @@ OpenSpaceEngine::~OpenSpaceEngine() { delete _networkEngine; delete _commandlineParser; delete _console; + delete _moduleEngine; delete _gui; if(_syncBuffer) @@ -259,6 +266,7 @@ bool OpenSpaceEngine::create( } void OpenSpaceEngine::destroy() { + _engine->_moduleEngine->deinitialize(); _engine->_console->deinitialize(); delete _engine; ghoul::systemcapabilities::SystemCapabilities::deinitialize(); @@ -745,4 +753,8 @@ NetworkEngine* OpenSpaceEngine::networkEngine() { return _networkEngine; } +ModuleEngine* OpenSpaceEngine::moduleEngine() { + return _moduleEngine; +} + } // namespace openspace diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 354c40088b..ef601ec52b 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -41,7 +41,8 @@ #include #include #include -#include +//#include +#include #include #include #include diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 4e6546b773..1a07cd914f 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -377,7 +377,7 @@ bool Scene::loadSceneInternal(const std::string& sceneDescriptionFilePath) { // the camera position const SceneGraphNode* fn = OsEng.interactionHandler()->focusNode(); - //psc relative = fn->worldPosition() - c->position(); + // Check crash for when fn == nullptr glm::mat4 la = glm::lookAt(cameraPosition.vec3(), fn->worldPosition().vec3(), c->lookUpVector()); diff --git a/src/scene/scenegraph.cpp b/src/scene/scenegraph.cpp index 8dab57e29a..1e19bb0534 100644 --- a/src/scene/scenegraph.cpp +++ b/src/scene/scenegraph.cpp @@ -47,8 +47,9 @@ namespace { namespace openspace { -SceneGraph::SceneGraph() { -} +SceneGraph::SceneGraph() + : _rootNode(nullptr) +{} void SceneGraph::clear() { // Untested ---abock @@ -58,6 +59,7 @@ void SceneGraph::clear() { } _nodes.clear(); + _rootNode = nullptr; } bool SceneGraph::loadFromFile(const std::string& sceneDescription) { diff --git a/src/scene/scenegraphnode.cpp b/src/scene/scenegraphnode.cpp index 4b6f28ea98..2fb1b3f6ba 100644 --- a/src/scene/scenegraphnode.cpp +++ b/src/scene/scenegraphnode.cpp @@ -79,6 +79,7 @@ SceneGraphNode* SceneGraphNode::createFromDictionary(const ghoul::Dictionary& di if (result->_renderable == nullptr) { LERROR("Failed to create renderable for SceneGraphNode '" << result->name() << "'"); + delete result; return nullptr; } result->addPropertySubOwner(result->_renderable); @@ -92,6 +93,7 @@ SceneGraphNode* SceneGraphNode::createFromDictionary(const ghoul::Dictionary& di if (result->_ephemeris == nullptr) { LERROR("Failed to create ephemeris for SceneGraphNode '" << result->name() << "'"); + delete result; return nullptr; } //result->addPropertySubOwner(result->_ephemeris); diff --git a/src/util/factorymanager.cpp b/src/util/factorymanager.cpp index cffaf3db17..67dfc490ef 100644 --- a/src/util/factorymanager.cpp +++ b/src/util/factorymanager.cpp @@ -25,24 +25,25 @@ #include // renderables -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include // positioninformation #include @@ -50,9 +51,9 @@ #include // projection -#include -#include -#include +//#include +//#include +//#include #include #include @@ -73,37 +74,7 @@ void FactoryManager::initialize() _manager = new FactoryManager; assert(_manager != nullptr); - // TODO: This has to be moved into a sort of module structure (ab) - // Add Renderables _manager->addFactory(new ghoul::TemplateFactory); - _manager->factory()->registerClass("RenderablePlanet"); - _manager->factory()->registerClass("RenderablePlanetProjection"); - _manager->factory()->registerClass("RenderableStars"); - _manager->factory()->registerClass - ("RenderableConstellationBounds"); - //will replace ephemeris class soon... - _manager->factory()->registerClass( - "RenderablePath"); - _manager->factory()->registerClass( - "RenderableTrail"); - _manager->factory()->registerClass( - "RenderableFov"); - _manager->factory()->registerClass( - "RenderableSphere"); - _manager->factory()->registerClass( - "RenderableSphericalGrid"); - _manager->factory()->registerClass( - "RenderableModel"); - _manager->factory()->registerClass( - "RenderablePlane"); - _manager->factory()->registerClass( - "RenderablePlaneProjection"); - _manager->factory()->registerClass( - "RenderableVolumeGL"); - _manager->factory()->registerClass( - "RenderableFieldlines"); - _manager->factory()->registerClass( - "RenderableCrawlingLine"); // Add Ephemerides _manager->addFactory(new ghoul::TemplateFactory); @@ -116,20 +87,14 @@ void FactoryManager::initialize() _manager->factory()->registerClass("Target"); - // Add PlanetGeometry - _manager->addFactory(new ghoul::TemplateFactory); - _manager->factory() - ->registerClass("SimpleSphere"); - - // Add PlanetGeometryProjection - _manager->addFactory(new ghoul::TemplateFactory); - _manager->factory() - ->registerClass("SimpleSphereProjection"); - - // Add ModelGeometry - _manager->addFactory(new ghoul::TemplateFactory); - _manager->factory() - ->registerClass("WavefrontGeometry"); + //_manager->addFactory(new ghoul::TemplateFactory); + //_manager->addFactory(new ghoul::TemplateFactory); + + + //// Add PlanetGeometryProjection + //_manager->addFactory(new ghoul::TemplateFactory); + //_manager->factory() + // ->registerClass("SimpleSphereProjection"); } void FactoryManager::deinitialize() From 59e474d5cd7151acc398a46a428d8ecfa3da4513 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 20 May 2015 21:08:21 +0200 Subject: [PATCH 029/329] Modularize the newhorizons and volume classes --- CMakeLists.txt | 2 + modules/base/basemodule.cpp | 8 --- modules/base/basemodule.h | 1 - modules/newhorizons/CMakeLists.txt | 27 +++++++++ modules/newhorizons/newhorizonsmodule.cpp | 56 +++++++++++++++++++ modules/newhorizons/newhorizonsmodule.h | 39 +++++++++++++ .../rendering/planetgeometryprojection.cpp | 2 +- .../rendering/planetgeometryprojection.h | 2 +- .../rendering/renderablefov.cpp | 2 +- .../rendering/renderablefov.h | 0 .../rendering/renderableplaneprojection.cpp | 2 +- .../rendering/renderableplaneprojection.h | 0 .../rendering/renderableplanetprojection.cpp | 4 +- .../rendering/renderableplanetprojection.h | 0 .../simplespheregeometryprojection.cpp | 2 +- .../simplespheregeometryprojection.h | 2 +- .../rendering/writeToTexture.h | 0 modules/volume/CMakeLists.txt | 20 +++++++ modules/volume/rendering/renderablevolume.cpp | 2 +- .../volume/rendering/renderablevolumegl.cpp | 2 +- modules/volume/rendering/renderablevolumegl.h | 2 +- modules/volume/volumemodule.cpp | 46 +++++++++++++++ modules/volume/volumemodule.h | 39 +++++++++++++ src/CMakeLists.txt | 3 +- src/engine/moduleengine.cpp | 9 +++ src/engine/openspaceengine.cpp | 3 - 26 files changed, 250 insertions(+), 25 deletions(-) create mode 100644 modules/newhorizons/CMakeLists.txt create mode 100644 modules/newhorizons/newhorizonsmodule.cpp create mode 100644 modules/newhorizons/newhorizonsmodule.h rename modules/{projection => newhorizons}/rendering/planetgeometryprojection.cpp (98%) rename modules/{projection => newhorizons}/rendering/planetgeometryprojection.h (97%) rename modules/{projection => newhorizons}/rendering/renderablefov.cpp (99%) rename modules/{projection => newhorizons}/rendering/renderablefov.h (100%) rename modules/{projection => newhorizons}/rendering/renderableplaneprojection.cpp (99%) rename modules/{projection => newhorizons}/rendering/renderableplaneprojection.h (100%) rename modules/{projection => newhorizons}/rendering/renderableplanetprojection.cpp (99%) rename modules/{projection => newhorizons}/rendering/renderableplanetprojection.h (100%) rename modules/{projection => newhorizons}/rendering/simplespheregeometryprojection.cpp (98%) rename modules/{projection => newhorizons}/rendering/simplespheregeometryprojection.h (97%) rename modules/{projection => newhorizons}/rendering/writeToTexture.h (100%) create mode 100644 modules/volume/CMakeLists.txt create mode 100644 modules/volume/volumemodule.cpp create mode 100644 modules/volume/volumemodule.h diff --git a/CMakeLists.txt b/CMakeLists.txt index efa5b8982f..ffe1005913 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,6 +146,8 @@ endif () add_subdirectory(src) add_subdirectory(modules/base) +add_subdirectory(modules/newhorizons) +add_subdirectory(modules/volume) option(BUILD_GUI_APPLICATIONS "Build GUI Applications" OFF) if (BUILD_GUI_APPLICATIONS) diff --git a/modules/base/basemodule.cpp b/modules/base/basemodule.cpp index bcf820d7b9..19e58329ef 100644 --- a/modules/base/basemodule.cpp +++ b/modules/base/basemodule.cpp @@ -45,10 +45,6 @@ #include #include -namespace { - const std::string _loggerCat = "BaseModule"; -} - namespace openspace { bool BaseModule::initialize() { @@ -81,8 +77,4 @@ bool BaseModule::initialize() { return true; } -bool BaseModule::deinitialize() { - return false; -} - } // namespace openspace diff --git a/modules/base/basemodule.h b/modules/base/basemodule.h index 3a2fe48c0b..6561a5cace 100644 --- a/modules/base/basemodule.h +++ b/modules/base/basemodule.h @@ -32,7 +32,6 @@ namespace openspace { class BaseModule : public OpenSpaceModule { public: bool initialize() override; - bool deinitialize() override; }; } // namespace openspace diff --git a/modules/newhorizons/CMakeLists.txt b/modules/newhorizons/CMakeLists.txt new file mode 100644 index 0000000000..fd86c73f88 --- /dev/null +++ b/modules/newhorizons/CMakeLists.txt @@ -0,0 +1,27 @@ +set(HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometryprojection.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefov.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplaneprojection.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplanetprojection.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometryprojection.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/writeToTexture.h +) +source_group("Header Files" FILES ${HEADER_FILES}) + +set(SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometryprojection.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefov.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplaneprojection.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplanetprojection.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometryprojection.cpp +) +source_group("Source Files" FILES ${SOURCE_FILES}) + +set(MODULE_CLASS_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/newhorizonsmodule.h + ${CMAKE_CURRENT_SOURCE_DIR}/newhorizonsmodule.cpp +) + +include_directories(${OPENSPACE_BASE_DIR}/include ${OPENSPACE_BASE_DIR}) + +add_library(openspace-module-newhorizons ${HEADER_FILES} ${SOURCE_FILES} ${MODULE_CLASS_FILES}) diff --git a/modules/newhorizons/newhorizonsmodule.cpp b/modules/newhorizons/newhorizonsmodule.cpp new file mode 100644 index 0000000000..e48556cf44 --- /dev/null +++ b/modules/newhorizons/newhorizonsmodule.cpp @@ -0,0 +1,56 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace openspace { + +bool NewHorizonsModule::initialize() { + FactoryManager::ref().addFactory(new ghoul::TemplateFactory); + + auto fRenderable = FactoryManager::ref().factory(); + ghoul_assert(fRenderable, "No renderable factory existed"); + + fRenderable->registerClass("RenderableFov"); + fRenderable->registerClass("RenderablePlaneProjection"); + fRenderable->registerClass("RenderablePlanetProjection"); + + auto fPlanetGeometryProjection = FactoryManager::ref().factory(); + fPlanetGeometryProjection->registerClass("SimpleSphereProjection"); + + return true; +} + +} // namespace openspace diff --git a/modules/newhorizons/newhorizonsmodule.h b/modules/newhorizons/newhorizonsmodule.h new file mode 100644 index 0000000000..c5d89d8cbd --- /dev/null +++ b/modules/newhorizons/newhorizonsmodule.h @@ -0,0 +1,39 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __NEWHORIZONSMODULE_H__ +#define __NEWHORIZONSMODULE_H__ + +#include + +namespace openspace { + +class NewHorizonsModule : public OpenSpaceModule { +public: + bool initialize() override; +}; + +} // namespace openspace + +#endif // __NEWHORIZONSMODULE_H__ diff --git a/modules/projection/rendering/planetgeometryprojection.cpp b/modules/newhorizons/rendering/planetgeometryprojection.cpp similarity index 98% rename from modules/projection/rendering/planetgeometryprojection.cpp rename to modules/newhorizons/rendering/planetgeometryprojection.cpp index a1507a831c..5df2e2c862 100644 --- a/modules/projection/rendering/planetgeometryprojection.cpp +++ b/modules/newhorizons/rendering/planetgeometryprojection.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include diff --git a/modules/projection/rendering/planetgeometryprojection.h b/modules/newhorizons/rendering/planetgeometryprojection.h similarity index 97% rename from modules/projection/rendering/planetgeometryprojection.h rename to modules/newhorizons/rendering/planetgeometryprojection.h index b843524539..d4fd56e89d 100644 --- a/modules/projection/rendering/planetgeometryprojection.h +++ b/modules/newhorizons/rendering/planetgeometryprojection.h @@ -26,7 +26,7 @@ #define __PlanetGeometryProjection_H__ #include -#include +#include #include namespace openspace { diff --git a/modules/projection/rendering/renderablefov.cpp b/modules/newhorizons/rendering/renderablefov.cpp similarity index 99% rename from modules/projection/rendering/renderablefov.cpp rename to modules/newhorizons/rendering/renderablefov.cpp index fa2d13d451..f1211e5da5 100644 --- a/modules/projection/rendering/renderablefov.cpp +++ b/modules/newhorizons/rendering/renderablefov.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include diff --git a/modules/projection/rendering/renderablefov.h b/modules/newhorizons/rendering/renderablefov.h similarity index 100% rename from modules/projection/rendering/renderablefov.h rename to modules/newhorizons/rendering/renderablefov.h diff --git a/modules/projection/rendering/renderableplaneprojection.cpp b/modules/newhorizons/rendering/renderableplaneprojection.cpp similarity index 99% rename from modules/projection/rendering/renderableplaneprojection.cpp rename to modules/newhorizons/rendering/renderableplaneprojection.cpp index b8d290f156..82f6fc81e2 100644 --- a/modules/projection/rendering/renderableplaneprojection.cpp +++ b/modules/newhorizons/rendering/renderableplaneprojection.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include #include diff --git a/modules/projection/rendering/renderableplaneprojection.h b/modules/newhorizons/rendering/renderableplaneprojection.h similarity index 100% rename from modules/projection/rendering/renderableplaneprojection.h rename to modules/newhorizons/rendering/renderableplaneprojection.h diff --git a/modules/projection/rendering/renderableplanetprojection.cpp b/modules/newhorizons/rendering/renderableplanetprojection.cpp similarity index 99% rename from modules/projection/rendering/renderableplanetprojection.cpp rename to modules/newhorizons/rendering/renderableplanetprojection.cpp index b8506d047e..1a13599eab 100644 --- a/modules/projection/rendering/renderableplanetprojection.cpp +++ b/modules/newhorizons/rendering/renderableplanetprojection.cpp @@ -23,9 +23,9 @@ ****************************************************************************************/ // open space includes -#include +#include #include -#include +#include #include diff --git a/modules/projection/rendering/renderableplanetprojection.h b/modules/newhorizons/rendering/renderableplanetprojection.h similarity index 100% rename from modules/projection/rendering/renderableplanetprojection.h rename to modules/newhorizons/rendering/renderableplanetprojection.h diff --git a/modules/projection/rendering/simplespheregeometryprojection.cpp b/modules/newhorizons/rendering/simplespheregeometryprojection.cpp similarity index 98% rename from modules/projection/rendering/simplespheregeometryprojection.cpp rename to modules/newhorizons/rendering/simplespheregeometryprojection.cpp index a6ef51a9d8..8d840a70a0 100644 --- a/modules/projection/rendering/simplespheregeometryprojection.cpp +++ b/modules/newhorizons/rendering/simplespheregeometryprojection.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include namespace { diff --git a/modules/projection/rendering/simplespheregeometryprojection.h b/modules/newhorizons/rendering/simplespheregeometryprojection.h similarity index 97% rename from modules/projection/rendering/simplespheregeometryprojection.h rename to modules/newhorizons/rendering/simplespheregeometryprojection.h index b64634b9ce..7f6bca6680 100644 --- a/modules/projection/rendering/simplespheregeometryprojection.h +++ b/modules/newhorizons/rendering/simplespheregeometryprojection.h @@ -25,7 +25,7 @@ #ifndef __SIMPLESPHEREGEOMETRYPROJECTION_H__ #define __SIMPLESPHEREGEOMETRYPROJECTION_H__ -#include +#include #include #include #include diff --git a/modules/projection/rendering/writeToTexture.h b/modules/newhorizons/rendering/writeToTexture.h similarity index 100% rename from modules/projection/rendering/writeToTexture.h rename to modules/newhorizons/rendering/writeToTexture.h diff --git a/modules/volume/CMakeLists.txt b/modules/volume/CMakeLists.txt new file mode 100644 index 0000000000..87bea70f10 --- /dev/null +++ b/modules/volume/CMakeLists.txt @@ -0,0 +1,20 @@ +set(HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablevolume.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablevolumegl.h +) +source_group("Header Files" FILES ${HEADER_FILES}) + +set(SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablevolume.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablevolumegl.cpp +) +source_group("Source Files" FILES ${SOURCE_FILES}) + +set(MODULE_CLASS_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/volumemodule.h + ${CMAKE_CURRENT_SOURCE_DIR}/volumemodule.cpp +) + +include_directories(${OPENSPACE_BASE_DIR}/include ${OPENSPACE_BASE_DIR}) + +add_library(openspace-module-volume ${HEADER_FILES} ${SOURCE_FILES} ${MODULE_CLASS_FILES}) diff --git a/modules/volume/rendering/renderablevolume.cpp b/modules/volume/rendering/renderablevolume.cpp index 314c7255e2..4afdd65974 100644 --- a/modules/volume/rendering/renderablevolume.cpp +++ b/modules/volume/rendering/renderablevolume.cpp @@ -23,7 +23,7 @@ ****************************************************************************************/ // open space includes -#include +#include #include #include #include diff --git a/modules/volume/rendering/renderablevolumegl.cpp b/modules/volume/rendering/renderablevolumegl.cpp index 26452d692a..a3fddd75fa 100644 --- a/modules/volume/rendering/renderablevolumegl.cpp +++ b/modules/volume/rendering/renderablevolumegl.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include diff --git a/modules/volume/rendering/renderablevolumegl.h b/modules/volume/rendering/renderablevolumegl.h index 55acc763f4..166bb26d4d 100644 --- a/modules/volume/rendering/renderablevolumegl.h +++ b/modules/volume/rendering/renderablevolumegl.h @@ -25,7 +25,7 @@ #ifndef __RENDERABLEVOLUMEGL_H__ #define __RENDERABLEVOLUMEGL_H__ -#include +#include #include // Forward declare to minimize dependencies diff --git a/modules/volume/volumemodule.cpp b/modules/volume/volumemodule.cpp new file mode 100644 index 0000000000..6811bbb55f --- /dev/null +++ b/modules/volume/volumemodule.cpp @@ -0,0 +1,46 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include + +#include + +#include + +namespace openspace { + +bool VolumeModule::initialize() { + + auto fRenderable = FactoryManager::ref().factory(); + ghoul_assert(fRenderable, "No renderable factory existed"); + + fRenderable->registerClass("RenderableVolumeGL"); + + return true; +} + +} // namespace openspace diff --git a/modules/volume/volumemodule.h b/modules/volume/volumemodule.h new file mode 100644 index 0000000000..5377c1940b --- /dev/null +++ b/modules/volume/volumemodule.h @@ -0,0 +1,39 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __VOLUMEMODULE_H__ +#define __VOLUMEMODULE_H__ + +#include + +namespace openspace { + +class VolumeModule : public OpenSpaceModule { +public: + bool initialize() override; +}; + +} // namespace openspace + +#endif // __VOLUMEMODULE_H__ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 014d3dfe24..f4d9f59798 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -151,8 +151,7 @@ if (UNIX AND NOT APPLE) endif (UNIX AND NOT APPLE) -set(DEPENDENT_LIBS ${DEPENDENT_LIBS} openspace-module-base) -message(${DEPENDENT_LIBS}) +set(DEPENDENT_LIBS ${DEPENDENT_LIBS} openspace-module-base openspace-module-newhorizons openspace-module-volume) include_directories("${HEADER_ROOT_DIR}") include_directories("${OPENSPACE_BASE_DIR}") diff --git a/src/engine/moduleengine.cpp b/src/engine/moduleengine.cpp index 821b7abe68..476f0efd84 100644 --- a/src/engine/moduleengine.cpp +++ b/src/engine/moduleengine.cpp @@ -28,6 +28,10 @@ #include +#include +#include +#include + namespace { const std::string _loggerCat = "ModuleEngine"; } @@ -36,6 +40,11 @@ namespace openspace { bool ModuleEngine::initialize() { LDEBUG("Initializing modules"); + + registerModule(new BaseModule); + registerModule(new NewHorizonsModule); + registerModule(new VolumeModule); + for (OpenSpaceModule* m : _modules) { bool success = m->initialize(); if (!success) { diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 02f5ab38e5..f5a46c5090 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -89,8 +89,6 @@ namespace { } commandlineArgumentPlaceholders; } -#include - namespace openspace { OpenSpaceEngine* OpenSpaceEngine::_engine = nullptr; @@ -114,7 +112,6 @@ OpenSpaceEngine::OpenSpaceEngine(std::string programName) ghoul::systemcapabilities::SystemCapabilities::initialize(); // Register modules - _moduleEngine->registerModule(new BaseModule); _moduleEngine->initialize(); } From e51df4b2a0482bce76a8c7a780a6d46fec194e98 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 20 May 2015 21:40:52 +0200 Subject: [PATCH 030/329] Sorting more classes into the correct modules --- include/openspace/util/factorymanager.h | 4 +- modules/base/CMakeLists.txt | 8 +- modules/base/basemodule.cpp | 18 +- .../base/ephemeris}/dynamicephemeris.cpp | 2 +- .../base/ephemeris}/dynamicephemeris.h | 2 +- .../base/ephemeris}/spiceephemeris.cpp | 3 +- .../base/ephemeris}/spiceephemeris.h | 0 .../base/ephemeris}/staticephemeris.cpp | 2 +- .../base/ephemeris}/staticephemeris.h | 2 +- .../base/rendering/renderablecrawlingline.cpp | 4 +- modules/base/rendering/renderablemodel.cpp | 1 - modules/newhorizons/CMakeLists.txt | 20 ++ modules/newhorizons/newhorizonsmodule.cpp | 12 ++ .../rendering/renderablecrawlingline.cpp | 182 ++++++++++++++++++ .../rendering/renderablecrawlingline.h | 0 .../newhorizons/rendering/renderablefov.cpp | 2 +- .../rendering/renderableplaneprojection.cpp | 2 - .../rendering/renderableplaneprojection.h | 2 +- .../rendering/renderableplanetprojection.h | 10 +- {src => modules/newhorizons}/util/decoder.cpp | 2 +- .../newhorizons}/util/decoder.h | 0 .../newhorizons}/util/hongkangparser.cpp | 6 +- .../newhorizons}/util/hongkangparser.h | 6 +- .../newhorizons}/util/imagesequencer.cpp | 2 +- .../newhorizons}/util/imagesequencer.h | 0 .../newhorizons}/util/imagesequencer2.cpp | 4 +- .../newhorizons}/util/imagesequencer2.h | 2 +- .../newhorizons}/util/instrumentdecoder.cpp | 2 +- .../newhorizons}/util/instrumentdecoder.h | 2 +- .../newhorizons}/util/labelparser.cpp | 4 +- .../newhorizons}/util/labelparser.h | 5 +- .../newhorizons}/util/scannerdecoder.cpp | 2 +- .../newhorizons}/util/scannerdecoder.h | 2 +- .../newhorizons}/util/sequenceparser.cpp | 4 +- .../newhorizons}/util/sequenceparser.h | 0 .../newhorizons}/util/targetdecoder.cpp | 2 +- .../newhorizons}/util/targetdecoder.h | 2 +- openspace.cfg | 2 +- src/engine/openspaceengine.cpp | 2 +- src/rendering/renderengine.cpp | 6 +- src/scene/scenegraphnode.cpp | 2 +- src/util/factorymanager.cpp | 80 +------- 42 files changed, 289 insertions(+), 126 deletions(-) rename {src/scene => modules/base/ephemeris}/dynamicephemeris.cpp (98%) rename {include/openspace/scene => modules/base/ephemeris}/dynamicephemeris.h (98%) rename {src/scene => modules/base/ephemeris}/spiceephemeris.cpp (97%) rename {include/openspace/scene => modules/base/ephemeris}/spiceephemeris.h (100%) rename {src/scene => modules/base/ephemeris}/staticephemeris.cpp (98%) rename {include/openspace/scene => modules/base/ephemeris}/staticephemeris.h (98%) create mode 100644 modules/newhorizons/rendering/renderablecrawlingline.cpp rename modules/{base => newhorizons}/rendering/renderablecrawlingline.h (100%) rename {src => modules/newhorizons}/util/decoder.cpp (98%) rename {include/openspace => modules/newhorizons}/util/decoder.h (100%) rename {src => modules/newhorizons}/util/hongkangparser.cpp (98%) rename {include/openspace => modules/newhorizons}/util/hongkangparser.h (96%) rename {src => modules/newhorizons}/util/imagesequencer.cpp (99%) rename {include/openspace => modules/newhorizons}/util/imagesequencer.h (100%) rename {src => modules/newhorizons}/util/imagesequencer2.cpp (99%) rename {include/openspace => modules/newhorizons}/util/imagesequencer2.h (99%) rename {src => modules/newhorizons}/util/instrumentdecoder.cpp (98%) rename {include/openspace => modules/newhorizons}/util/instrumentdecoder.h (98%) rename {src => modules/newhorizons}/util/labelparser.cpp (99%) rename {include/openspace => modules/newhorizons}/util/labelparser.h (96%) rename {src => modules/newhorizons}/util/scannerdecoder.cpp (98%) rename {include/openspace => modules/newhorizons}/util/scannerdecoder.h (98%) rename {src => modules/newhorizons}/util/sequenceparser.cpp (98%) rename {include/openspace => modules/newhorizons}/util/sequenceparser.h (100%) rename {src => modules/newhorizons}/util/targetdecoder.cpp (98%) rename {include/openspace => modules/newhorizons}/util/targetdecoder.h (98%) diff --git a/include/openspace/util/factorymanager.h b/include/openspace/util/factorymanager.h index 15c4b2d120..d9903c6712 100644 --- a/include/openspace/util/factorymanager.h +++ b/include/openspace/util/factorymanager.h @@ -53,12 +53,12 @@ public: ghoul::TemplateFactory* factory() const; private: - FactoryManager(); + FactoryManager() = default; ~FactoryManager(); FactoryManager(const FactoryManager& c) = delete; - static FactoryManager* _manager; /// _factories; }; diff --git a/modules/base/CMakeLists.txt b/modules/base/CMakeLists.txt index 151f1ca882..c1b679fc71 100644 --- a/modules/base/CMakeLists.txt +++ b/modules/base/CMakeLists.txt @@ -2,7 +2,6 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/modelgeometry.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometry.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableconstellationbounds.h - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablecrawlingline.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefieldlines.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablemodel.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablepath.h @@ -14,6 +13,9 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderabletrail.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometry.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/wavefrontgeometry.h + ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/dynamicephemeris.h + ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/spiceephemeris.h + ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/staticephemeris.h ) source_group("Header Files" FILES ${HEADER_FILES}) @@ -21,7 +23,6 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/modelgeometry.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometry.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableconstellationbounds.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablecrawlingline.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefieldlines.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablemodel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablepath.cpp @@ -33,6 +34,9 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderabletrail.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometry.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/wavefrontgeometry.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/dynamicephemeris.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/spiceephemeris.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/staticephemeris.cpp ) source_group("Source Files" FILES ${SOURCE_FILES}) diff --git a/modules/base/basemodule.cpp b/modules/base/basemodule.cpp index 19e58329ef..bb6b5a59a3 100644 --- a/modules/base/basemodule.cpp +++ b/modules/base/basemodule.cpp @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -45,6 +44,10 @@ #include #include +#include +#include +#include + namespace openspace { bool BaseModule::initialize() { @@ -52,7 +55,7 @@ bool BaseModule::initialize() { FactoryManager::ref().addFactory(new ghoul::TemplateFactory); auto fRenderable = FactoryManager::ref().factory(); - ghoul_assert(fRenderable, "No renderable factory existed"); + ghoul_assert(fRenderable, "Renderable factory was not created"); fRenderable->registerClass("RenderablePlanet"); fRenderable->registerClass("RenderableStars"); @@ -64,14 +67,19 @@ bool BaseModule::initialize() { fRenderable->registerClass("RenderableModel"); fRenderable->registerClass("RenderablePlane"); fRenderable->registerClass("RenderableFieldlines"); - fRenderable->registerClass("RenderableCrawlingLine"); + + auto fEphemeris = FactoryManager::ref().factory(); + ghoul_assert(fEphemeris, "Ephemeris factory was not created"); + fEphemeris->registerClass("Static"); + fEphemeris->registerClass("Dynamic"); + fEphemeris->registerClass("Spice"); auto fPlanetGeometry = FactoryManager::ref().factory(); - ghoul_assert(fPlanetGeometry, "No planet geometry factory existed"); + ghoul_assert(fPlanetGeometry, "Planet geometry factory was not created"); fPlanetGeometry->registerClass("SimpleSphere"); auto fModelGeometry = FactoryManager::ref().factory(); - ghoul_assert(fModelGeometry, "No model geometry factory existed"); + ghoul_assert(fModelGeometry, "Model geometry factory was not created"); fModelGeometry->registerClass("WavefrontGeometry"); return true; diff --git a/src/scene/dynamicephemeris.cpp b/modules/base/ephemeris/dynamicephemeris.cpp similarity index 98% rename from src/scene/dynamicephemeris.cpp rename to modules/base/ephemeris/dynamicephemeris.cpp index 46fe087d55..8e4a708b40 100644 --- a/src/scene/dynamicephemeris.cpp +++ b/modules/base/ephemeris/dynamicephemeris.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include namespace { const std::string KeyPosition = "Position"; diff --git a/include/openspace/scene/dynamicephemeris.h b/modules/base/ephemeris/dynamicephemeris.h similarity index 98% rename from include/openspace/scene/dynamicephemeris.h rename to modules/base/ephemeris/dynamicephemeris.h index 43b73e52db..61387c06bf 100644 --- a/include/openspace/scene/dynamicephemeris.h +++ b/modules/base/ephemeris/dynamicephemeris.h @@ -25,7 +25,7 @@ #ifndef __DYNAMICEPHEMERIS_H__ #define __DYNAMICEPHEMERIS_H__ -#include "ephemeris.h" +#include namespace openspace { diff --git a/src/scene/spiceephemeris.cpp b/modules/base/ephemeris/spiceephemeris.cpp similarity index 97% rename from src/scene/spiceephemeris.cpp rename to modules/base/ephemeris/spiceephemeris.cpp index 1459d490a4..2fb45ddf2a 100644 --- a/src/scene/spiceephemeris.cpp +++ b/modules/base/ephemeris/spiceephemeris.cpp @@ -22,11 +22,10 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include -#include namespace { const std::string _loggerCat = "SpiceEphemeris"; diff --git a/include/openspace/scene/spiceephemeris.h b/modules/base/ephemeris/spiceephemeris.h similarity index 100% rename from include/openspace/scene/spiceephemeris.h rename to modules/base/ephemeris/spiceephemeris.h diff --git a/src/scene/staticephemeris.cpp b/modules/base/ephemeris/staticephemeris.cpp similarity index 98% rename from src/scene/staticephemeris.cpp rename to modules/base/ephemeris/staticephemeris.cpp index e7bb684c51..2fa1a46837 100644 --- a/src/scene/staticephemeris.cpp +++ b/modules/base/ephemeris/staticephemeris.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include namespace { const std::string KeyPosition = "Position"; diff --git a/include/openspace/scene/staticephemeris.h b/modules/base/ephemeris/staticephemeris.h similarity index 98% rename from include/openspace/scene/staticephemeris.h rename to modules/base/ephemeris/staticephemeris.h index 2a2e707489..b04510e357 100644 --- a/include/openspace/scene/staticephemeris.h +++ b/modules/base/ephemeris/staticephemeris.h @@ -25,7 +25,7 @@ #ifndef __STATICEPHEMERIS_H__ #define __STATICEPHEMERIS_H__ -#include "ephemeris.h" +#include namespace openspace { diff --git a/modules/base/rendering/renderablecrawlingline.cpp b/modules/base/rendering/renderablecrawlingline.cpp index 58e3da9e85..58dcdd354d 100644 --- a/modules/base/rendering/renderablecrawlingline.cpp +++ b/modules/base/rendering/renderablecrawlingline.cpp @@ -22,12 +22,12 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include #include -#include +#include //#include namespace { diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index ccd6bb70bd..b48257c4ca 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -37,7 +37,6 @@ #include #include -#include #include #include diff --git a/modules/newhorizons/CMakeLists.txt b/modules/newhorizons/CMakeLists.txt index fd86c73f88..62c1af6ec4 100644 --- a/modules/newhorizons/CMakeLists.txt +++ b/modules/newhorizons/CMakeLists.txt @@ -1,19 +1,39 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometryprojection.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablecrawlingline.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefov.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplaneprojection.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplanetprojection.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometryprojection.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/writeToTexture.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/decoder.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/hongkangparser.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer2.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/instrumentdecoder.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/labelparser.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/scannerdecoder.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/sequenceparser.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/targetdecoder.h ) source_group("Header Files" FILES ${HEADER_FILES}) set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometryprojection.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablecrawlingline.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefov.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplaneprojection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplanetprojection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometryprojection.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/decoder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/hongkangparser.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer2.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/instrumentdecoder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/labelparser.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/scannerdecoder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/sequenceparser.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/targetdecoder.cpp ) source_group("Source Files" FILES ${SOURCE_FILES}) diff --git a/modules/newhorizons/newhorizonsmodule.cpp b/modules/newhorizons/newhorizonsmodule.cpp index e48556cf44..a6e835b595 100644 --- a/modules/newhorizons/newhorizonsmodule.cpp +++ b/modules/newhorizons/newhorizonsmodule.cpp @@ -30,19 +30,27 @@ #include #include +#include #include #include #include #include +#include +#include +#include + + namespace openspace { bool NewHorizonsModule::initialize() { FactoryManager::ref().addFactory(new ghoul::TemplateFactory); + FactoryManager::ref().addFactory(new ghoul::TemplateFactory); auto fRenderable = FactoryManager::ref().factory(); ghoul_assert(fRenderable, "No renderable factory existed"); + fRenderable->registerClass("RenderableCrawlingLine"); fRenderable->registerClass("RenderableFov"); fRenderable->registerClass("RenderablePlaneProjection"); fRenderable->registerClass("RenderablePlanetProjection"); @@ -50,6 +58,10 @@ bool NewHorizonsModule::initialize() { auto fPlanetGeometryProjection = FactoryManager::ref().factory(); fPlanetGeometryProjection->registerClass("SimpleSphereProjection"); + auto fDecoder = FactoryManager::ref().factory(); + fDecoder->registerClass("Instrument"); + fDecoder->registerClass("Target"); + return true; } diff --git a/modules/newhorizons/rendering/renderablecrawlingline.cpp b/modules/newhorizons/rendering/renderablecrawlingline.cpp new file mode 100644 index 0000000000..58dcdd354d --- /dev/null +++ b/modules/newhorizons/rendering/renderablecrawlingline.cpp @@ -0,0 +1,182 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include +#include +//#include + +namespace { + const std::string _loggerCat = "RenderableCrawlingLine"; + + const std::string KeySource = "Source"; + const std::string KeyTarget = "Target"; + const std::string KeyInstrument = "Instrument"; + const std::string KeyReferenceFrame = "Frame"; + const std::string keyColor = "RGB"; + + static const int SourcePosition = 0; + static const int TargetPosition = 1; +} + +namespace openspace { + +RenderableCrawlingLine::RenderableCrawlingLine(const ghoul::Dictionary& dictionary) + : Renderable(dictionary) + , _program(nullptr) + , _imageSequenceTime(-1.f) + , _vao(0) + , _vbo(0) +{ + dictionary.getValue(KeySource, _source); + dictionary.getValue(KeyTarget, _target); + dictionary.getValue(KeyInstrument, _instrumentName); + dictionary.getValue(KeyReferenceFrame, _referenceFrame); + + + if (dictionary.hasKeyAndValue(keyColor)) + dictionary.getValue(keyColor, _lineColor); + else + _lineColor = glm::vec3(1); +} + +bool RenderableCrawlingLine::isReady() const { + bool ready = true; + ready &= !_source.empty(); + ready &= !_target.empty(); + ready &= !_instrumentName.empty(); + ready &= (_program != nullptr); + return ready; +} + +bool RenderableCrawlingLine::initialize() { + _frameCounter = 0; + bool completeSuccess = true; + _program = ghoul::opengl::ProgramObject::Build("RenderableCrawlingLine", + "${SHADERS}/modules/crawlingline/crawlingline_vs.glsl", + "${SHADERS}/modules/crawlingline/crawlingline_fs.glsl" + ); + if (!_program) + return false; + + glGenVertexArrays(1, &_vao); + glGenBuffers(1, &_vbo); + + glBindVertexArray(_vao); + glBindBuffer(GL_ARRAY_BUFFER, _vbo); + glBufferData(GL_ARRAY_BUFFER, 2 * sizeof(psc), NULL, GL_DYNAMIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, (void*)0); + + glBindVertexArray(0); + + return completeSuccess; +} + +bool RenderableCrawlingLine::deinitialize(){ + glDeleteVertexArrays(1, &_vao); + _vao = 0; + glDeleteBuffers(1, &_vbo); + _vbo = 0; + return true; +} + +void RenderableCrawlingLine::render(const RenderData& data) { + if (_drawLine) { + _program->activate(); + _frameCounter++; + // fetch data + psc currentPosition = data.position; + psc campos = data.camera.position(); + glm::mat4 camrot = data.camera.viewRotationMatrix(); + + glm::mat4 transform = glm::mat4(1); + + // setup the data to the shader + _program->setUniform("ViewProjection", data.camera.viewProjectionMatrix()); + _program->setUniform("ModelTransform", transform); + + int frame = _frameCounter % 60; + float fadingFactor = static_cast(sin((frame * pi_c()) / 60)); + float alpha = 0.6f + fadingFactor*0.4f; + + glLineWidth(2.f); + + _program->setUniform("_alpha", alpha); + _program->setUniform("color", _lineColor); + setPscUniforms(_program, &data.camera, data.position); + + glBindVertexArray(_vao); + glBindBuffer(GL_ARRAY_BUFFER, _vbo); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(psc) * 2, _positions); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); + + glDrawArrays(GL_LINES, 0, 2); + glBindVertexArray(0); + + _program->deactivate(); + } +} + +void RenderableCrawlingLine::update(const UpdateData& data) { + if (_program->isDirty()) + _program->rebuildFromFile(); + glm::dmat3 transformMatrix = glm::dmat3(1); + openspace::SpiceManager::ref().getPositionTransformMatrix(_source, _referenceFrame, data.time, transformMatrix); + + glm::mat4 tmp = glm::mat4(1); + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++){ + tmp[i][j] = static_cast(transformMatrix[i][j]); + } + } + + _positions[SourcePosition] = PowerScaledCoordinate::CreatePowerScaledCoordinate(0, 0, 0); + + std::string shape, instrument; + std::vector bounds; + glm::dvec3 boresight; + + bool found = openspace::SpiceManager::ref().getFieldOfView(_source, shape, instrument, boresight, bounds); + if (!found) + LERROR("Could not find field of view for instrument"); + glm::vec4 target(boresight[0], boresight[1], boresight[2], 12); + target = tmp * target; + + _positions[TargetPosition] = target; + + if (ImageSequencer2::ref().isReady()) { + _imageSequenceTime = ImageSequencer2::ref().instrumentActiveTime(_instrumentName); + _drawLine = _imageSequenceTime != -1.f; + } +} + + +} \ No newline at end of file diff --git a/modules/base/rendering/renderablecrawlingline.h b/modules/newhorizons/rendering/renderablecrawlingline.h similarity index 100% rename from modules/base/rendering/renderablecrawlingline.h rename to modules/newhorizons/rendering/renderablecrawlingline.h diff --git a/modules/newhorizons/rendering/renderablefov.cpp b/modules/newhorizons/rendering/renderablefov.cpp index f1211e5da5..6f82510f10 100644 --- a/modules/newhorizons/rendering/renderablefov.cpp +++ b/modules/newhorizons/rendering/renderablefov.cpp @@ -29,7 +29,7 @@ #include #include -#include // testing +#include // testing #include diff --git a/modules/newhorizons/rendering/renderableplaneprojection.cpp b/modules/newhorizons/rendering/renderableplaneprojection.cpp index 82f6fc81e2..ef2def2353 100644 --- a/modules/newhorizons/rendering/renderableplaneprojection.cpp +++ b/modules/newhorizons/rendering/renderableplaneprojection.cpp @@ -29,8 +29,6 @@ #include #include #include -#include -#include #include #include diff --git a/modules/newhorizons/rendering/renderableplaneprojection.h b/modules/newhorizons/rendering/renderableplaneprojection.h index 92c5e479f6..3239a26958 100644 --- a/modules/newhorizons/rendering/renderableplaneprojection.h +++ b/modules/newhorizons/rendering/renderableplaneprojection.h @@ -28,7 +28,7 @@ #include #include -#include +#include #include #include diff --git a/modules/newhorizons/rendering/renderableplanetprojection.h b/modules/newhorizons/rendering/renderableplanetprojection.h index 174fb75c16..01c8971fb3 100644 --- a/modules/newhorizons/rendering/renderableplanetprojection.h +++ b/modules/newhorizons/rendering/renderableplanetprojection.h @@ -29,12 +29,12 @@ // open space includes #include -#include +#include -#include -#include -#include -#include +#include +#include +#include +#include #include diff --git a/src/util/decoder.cpp b/modules/newhorizons/util/decoder.cpp similarity index 98% rename from src/util/decoder.cpp rename to modules/newhorizons/util/decoder.cpp index 6f9e77c742..2c68b90886 100644 --- a/src/util/decoder.cpp +++ b/modules/newhorizons/util/decoder.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include namespace { diff --git a/include/openspace/util/decoder.h b/modules/newhorizons/util/decoder.h similarity index 100% rename from include/openspace/util/decoder.h rename to modules/newhorizons/util/decoder.h diff --git a/src/util/hongkangparser.cpp b/modules/newhorizons/util/hongkangparser.cpp similarity index 98% rename from src/util/hongkangparser.cpp rename to modules/newhorizons/util/hongkangparser.cpp index df8748c7d5..e6b3a0cee3 100644 --- a/src/util/hongkangparser.cpp +++ b/modules/newhorizons/util/hongkangparser.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include #include @@ -32,8 +32,8 @@ #include #include #include -#include -#include +#include +#include namespace { diff --git a/include/openspace/util/hongkangparser.h b/modules/newhorizons/util/hongkangparser.h similarity index 96% rename from include/openspace/util/hongkangparser.h rename to modules/newhorizons/util/hongkangparser.h index 3f5e59e1fb..d74f43db40 100644 --- a/include/openspace/util/hongkangparser.h +++ b/modules/newhorizons/util/hongkangparser.h @@ -22,11 +22,11 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ - #ifndef __HONGKANGPARSER_H__ #define __HONGKANGPARSER_H__ -#include -#include + +#include +#include #include #include diff --git a/src/util/imagesequencer.cpp b/modules/newhorizons/util/imagesequencer.cpp similarity index 99% rename from src/util/imagesequencer.cpp rename to modules/newhorizons/util/imagesequencer.cpp index 80c2ea4b30..dbcc2dc767 100644 --- a/src/util/imagesequencer.cpp +++ b/modules/newhorizons/util/imagesequencer.cpp @@ -23,7 +23,7 @@ ****************************************************************************************/ // open space includes -#include +#include #include #include #include diff --git a/include/openspace/util/imagesequencer.h b/modules/newhorizons/util/imagesequencer.h similarity index 100% rename from include/openspace/util/imagesequencer.h rename to modules/newhorizons/util/imagesequencer.h diff --git a/src/util/imagesequencer2.cpp b/modules/newhorizons/util/imagesequencer2.cpp similarity index 99% rename from src/util/imagesequencer2.cpp rename to modules/newhorizons/util/imagesequencer2.cpp index 46c7434031..aea8dd4238 100644 --- a/src/util/imagesequencer2.cpp +++ b/modules/newhorizons/util/imagesequencer2.cpp @@ -23,13 +23,13 @@ ****************************************************************************************/ // open space includes -#include +#include #include #include #include #include #include -#include +#include #include #include diff --git a/include/openspace/util/imagesequencer2.h b/modules/newhorizons/util/imagesequencer2.h similarity index 99% rename from include/openspace/util/imagesequencer2.h rename to modules/newhorizons/util/imagesequencer2.h index 326fc00c7a..0a30437740 100644 --- a/include/openspace/util/imagesequencer2.h +++ b/modules/newhorizons/util/imagesequencer2.h @@ -33,7 +33,7 @@ #include #include -#include +#include namespace openspace { /** diff --git a/src/util/instrumentdecoder.cpp b/modules/newhorizons/util/instrumentdecoder.cpp similarity index 98% rename from src/util/instrumentdecoder.cpp rename to modules/newhorizons/util/instrumentdecoder.cpp index c062ce0df0..9ad3a20022 100644 --- a/src/util/instrumentdecoder.cpp +++ b/modules/newhorizons/util/instrumentdecoder.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include namespace { const std::string _loggerCat = "InstrumentDecoder"; diff --git a/include/openspace/util/instrumentdecoder.h b/modules/newhorizons/util/instrumentdecoder.h similarity index 98% rename from include/openspace/util/instrumentdecoder.h rename to modules/newhorizons/util/instrumentdecoder.h index d464c3d6ef..cb7958a40e 100644 --- a/include/openspace/util/instrumentdecoder.h +++ b/modules/newhorizons/util/instrumentdecoder.h @@ -25,7 +25,7 @@ #ifndef __INSTRUMENTDECODER_H__ #define __INSTRUMENTDECODER_H__ -#include +#include #include diff --git a/src/util/labelparser.cpp b/modules/newhorizons/util/labelparser.cpp similarity index 99% rename from src/util/labelparser.cpp rename to modules/newhorizons/util/labelparser.cpp index 8398251f44..2331ab586c 100644 --- a/src/util/labelparser.cpp +++ b/modules/newhorizons/util/labelparser.cpp @@ -27,13 +27,13 @@ #include #include #include -#include +#include #include #include #include #include -#include +#include namespace { const std::string _loggerCat = "LabelParser"; diff --git a/include/openspace/util/labelparser.h b/modules/newhorizons/util/labelparser.h similarity index 96% rename from include/openspace/util/labelparser.h rename to modules/newhorizons/util/labelparser.h index e0625d5715..6efa1c77ca 100644 --- a/include/openspace/util/labelparser.h +++ b/modules/newhorizons/util/labelparser.h @@ -24,8 +24,9 @@ #ifndef __LABELPARSER_H__ #define __LABELPARSER_H__ -#include -#include + +#include +#include #include #include diff --git a/src/util/scannerdecoder.cpp b/modules/newhorizons/util/scannerdecoder.cpp similarity index 98% rename from src/util/scannerdecoder.cpp rename to modules/newhorizons/util/scannerdecoder.cpp index 953e183e8f..ceeadd9b7f 100644 --- a/src/util/scannerdecoder.cpp +++ b/modules/newhorizons/util/scannerdecoder.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include namespace { const std::string _loggerCat = "ScannerDecoder"; diff --git a/include/openspace/util/scannerdecoder.h b/modules/newhorizons/util/scannerdecoder.h similarity index 98% rename from include/openspace/util/scannerdecoder.h rename to modules/newhorizons/util/scannerdecoder.h index 4ddff2a935..5d2605e66c 100644 --- a/include/openspace/util/scannerdecoder.h +++ b/modules/newhorizons/util/scannerdecoder.h @@ -25,7 +25,7 @@ #ifndef __SCANNERDECODER_H__ #define __SCANNERDECODER_H__ -#include +#include #include namespace openspace { diff --git a/src/util/sequenceparser.cpp b/modules/newhorizons/util/sequenceparser.cpp similarity index 98% rename from src/util/sequenceparser.cpp rename to modules/newhorizons/util/sequenceparser.cpp index 8d4926b8be..af2842092b 100644 --- a/src/util/sequenceparser.cpp +++ b/modules/newhorizons/util/sequenceparser.cpp @@ -22,11 +22,11 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include -#include +#include namespace { const std::string _loggerCat = "SequenceParser"; diff --git a/include/openspace/util/sequenceparser.h b/modules/newhorizons/util/sequenceparser.h similarity index 100% rename from include/openspace/util/sequenceparser.h rename to modules/newhorizons/util/sequenceparser.h diff --git a/src/util/targetdecoder.cpp b/modules/newhorizons/util/targetdecoder.cpp similarity index 98% rename from src/util/targetdecoder.cpp rename to modules/newhorizons/util/targetdecoder.cpp index e00ea938d7..c44deac698 100644 --- a/src/util/targetdecoder.cpp +++ b/modules/newhorizons/util/targetdecoder.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include namespace { const std::string _loggerCat = "TargetDecoder"; diff --git a/include/openspace/util/targetdecoder.h b/modules/newhorizons/util/targetdecoder.h similarity index 98% rename from include/openspace/util/targetdecoder.h rename to modules/newhorizons/util/targetdecoder.h index 244ba46f7f..ff8157f320 100644 --- a/include/openspace/util/targetdecoder.h +++ b/modules/newhorizons/util/targetdecoder.h @@ -25,7 +25,7 @@ #ifndef __TARGETDECODER_H__ #define __TARGETDECODER_H__ -#include +#include #include diff --git a/openspace.cfg b/openspace.cfg index dec8375c58..505bf7812c 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -7,7 +7,7 @@ return { -- Sets the scene that is to be loaded by OpenSpace. A scene file is a description -- of all entities that will be visible during an instance of OpenSpace - Scene = "${OPENSPACE_DATA}/scene/default.scene", + Scene = "${OPENSPACE_DATA}/scene/default_nh.scene", Paths = { SGCT = "${BASE_PATH}/config/sgct", diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index f5a46c5090..4717af4fec 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -45,7 +45,7 @@ #include #include #include -#include // testing +#include // testing #include diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index ef601ec52b..2125dd150f 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -24,7 +24,7 @@ #include -#include +#include #include @@ -61,8 +61,8 @@ #include // These are temporary ---abock -#include -#include +#include +#include // ABuffer defines #define ABUFFER_FRAMEBUFFER 0 diff --git a/src/scene/scenegraphnode.cpp b/src/scene/scenegraphnode.cpp index 2fb1b3f6ba..4a125b93ff 100644 --- a/src/scene/scenegraphnode.cpp +++ b/src/scene/scenegraphnode.cpp @@ -36,7 +36,7 @@ #include #include -#include +#include #include #include diff --git a/src/util/factorymanager.cpp b/src/util/factorymanager.cpp index 67dfc490ef..2323d2c626 100644 --- a/src/util/factorymanager.cpp +++ b/src/util/factorymanager.cpp @@ -24,97 +24,37 @@ #include -// renderables #include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include +#include -// positioninformation -#include -#include -#include - -// projection -//#include -//#include -//#include - -#include -#include -#include - - -// std -#include +#include namespace openspace { FactoryManager* FactoryManager::_manager = nullptr; -void FactoryManager::initialize() -{ - assert(_manager == nullptr); +void FactoryManager::initialize() { + ghoul_assert(!_manager, "Factory Manager already initialized"); if (_manager == nullptr) _manager = new FactoryManager; - assert(_manager != nullptr); + ghoul_assert(_manager, "Factory Manager was not correctly initialized"); _manager->addFactory(new ghoul::TemplateFactory); - - // Add Ephemerides _manager->addFactory(new ghoul::TemplateFactory); - _manager->factory()->registerClass("Static"); - _manager->factory()->registerClass("Dynamic"); - _manager->factory()->registerClass("Spice"); - - _manager->addFactory(new ghoul::TemplateFactory); - _manager->factory()->registerClass("Instrument"); - _manager->factory()->registerClass("Target"); - - - //_manager->addFactory(new ghoul::TemplateFactory); - //_manager->addFactory(new ghoul::TemplateFactory); - - - //// Add PlanetGeometryProjection - //_manager->addFactory(new ghoul::TemplateFactory); - //_manager->factory() - // ->registerClass("SimpleSphereProjection"); } -void FactoryManager::deinitialize() -{ - assert(_manager != nullptr); +void FactoryManager::deinitialize() { + ghoul_assert(_manager, "No Factory Manager to deinitialize"); delete _manager; _manager = nullptr; } -FactoryManager& FactoryManager::ref() -{ - assert(_manager != nullptr); +FactoryManager& FactoryManager::ref() { + ghoul_assert(_manager, "No Factory Manager to dereference"); return *_manager; } -FactoryManager::FactoryManager() -{ -} -FactoryManager::~FactoryManager() -{ +FactoryManager::~FactoryManager() { for (ghoul::TemplateFactoryBase* factory : _factories) delete factory; _factories.clear(); From ca7dcd4762314aebd33c8763180f17afc79f7b34 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 21 May 2015 15:17:38 +0200 Subject: [PATCH 031/329] Move shaders into their correct modules Add path determination to modules that automatically sets correct path resolve tokens --- ext/ghoul | 2 +- include/openspace/util/openspacemodule.h | 9 +- modules/base/CMakeLists.txt | 25 ++++++ modules/base/basemodule.cpp | 10 +++ modules/base/basemodule.h | 1 + .../renderableconstellationbounds.cpp | 4 +- modules/base/rendering/renderablemodel.cpp | 4 +- modules/base/rendering/renderablepath.cpp | 78 ++++++++--------- modules/base/rendering/renderableplane.cpp | 4 +- modules/base/rendering/renderablesphere.cpp | 4 +- modules/base/rendering/renderablestars.cpp | 6 +- modules/base/rendering/renderabletrail.cpp | 4 +- .../base/shaders}/constellationbounds_fs.glsl | 0 .../base/shaders}/constellationbounds_vs.glsl | 0 .../base/shaders}/ephemeris_fs.glsl | 0 .../base/shaders}/ephemeris_vs.glsl | 0 .../base/shaders}/fieldline_fs.glsl | 0 .../base/shaders}/fieldline_gs.glsl | 0 .../base/shaders}/fieldline_vs.glsl | 0 .../base/shaders}/imageplane_fs.glsl | 0 .../base/shaders}/imageplane_vs.glsl | 0 .../base/shaders}/model_fs.glsl | 0 .../base/shaders}/model_vs.glsl | 0 .../base/shaders}/path_fs.glsl | 0 .../base/shaders}/path_gs.glsl | 0 .../base/shaders}/path_vs.glsl | 0 .../base/shaders}/plane_fs.glsl | 0 .../base/shaders}/plane_vs.glsl | 0 .../base/shaders}/sphere_fs.glsl | 0 .../base/shaders}/sphere_vs.glsl | 0 .../base/shaders}/star_fs.glsl | 0 .../base/shaders}/star_ge.glsl | 0 .../base/shaders}/star_vs.glsl | 0 modules/newhorizons/newhorizonsmodule.cpp | 8 ++ modules/newhorizons/newhorizonsmodule.h | 1 + .../rendering/renderablecrawlingline.cpp | 4 +- .../newhorizons/rendering/renderablefov.cpp | 4 +- .../rendering/renderableplaneprojection.cpp | 4 +- .../rendering/renderableplanetprojection.cpp | 6 +- .../newhorizons/shaders}/crawlingline_fs.glsl | 0 .../newhorizons/shaders}/crawlingline_vs.glsl | 0 .../newhorizons/shaders}/fov_fs.glsl | 0 .../newhorizons/shaders}/fov_vs.glsl | 0 .../shaders}/projectiveTexture_fs.glsl | 0 .../shaders}/projectiveTexture_vs.glsl | 0 modules/volume/volumemodule.cpp | 7 ++ modules/volume/volumemodule.h | 1 + openspace.cfg | 1 + src/abuffer/abuffer.cpp | 2 + src/engine/openspaceengine.cpp | 6 +- src/gui/gui.cpp | 2 + src/util/openspacemodule.cpp | 83 +++++++++++++++++++ 52 files changed, 212 insertions(+), 68 deletions(-) rename {shaders/modules/constellationbounds => modules/base/shaders}/constellationbounds_fs.glsl (100%) rename {shaders/modules/constellationbounds => modules/base/shaders}/constellationbounds_vs.glsl (100%) rename {shaders/modules/trails => modules/base/shaders}/ephemeris_fs.glsl (100%) rename {shaders/modules/trails => modules/base/shaders}/ephemeris_vs.glsl (100%) rename {shaders/modules/fieldlines => modules/base/shaders}/fieldline_fs.glsl (100%) rename {shaders/modules/fieldlines => modules/base/shaders}/fieldline_gs.glsl (100%) rename {shaders/modules/fieldlines => modules/base/shaders}/fieldline_vs.glsl (100%) rename {shaders/modules/imageplane => modules/base/shaders}/imageplane_fs.glsl (100%) rename {shaders/modules/imageplane => modules/base/shaders}/imageplane_vs.glsl (100%) rename {shaders/modules/model => modules/base/shaders}/model_fs.glsl (100%) rename {shaders/modules/model => modules/base/shaders}/model_vs.glsl (100%) rename {shaders/modules/trails => modules/base/shaders}/path_fs.glsl (100%) rename {shaders/modules/trails => modules/base/shaders}/path_gs.glsl (100%) rename {shaders/modules/trails => modules/base/shaders}/path_vs.glsl (100%) rename {shaders/modules/plane => modules/base/shaders}/plane_fs.glsl (100%) rename {shaders/modules/plane => modules/base/shaders}/plane_vs.glsl (100%) rename {shaders/modules/sphere => modules/base/shaders}/sphere_fs.glsl (100%) rename {shaders/modules/sphere => modules/base/shaders}/sphere_vs.glsl (100%) rename {shaders/modules/stars => modules/base/shaders}/star_fs.glsl (100%) rename {shaders/modules/stars => modules/base/shaders}/star_ge.glsl (100%) rename {shaders/modules/stars => modules/base/shaders}/star_vs.glsl (100%) rename {shaders/modules/crawlingline => modules/newhorizons/shaders}/crawlingline_fs.glsl (100%) rename {shaders/modules/crawlingline => modules/newhorizons/shaders}/crawlingline_vs.glsl (100%) rename {shaders/modules/projection => modules/newhorizons/shaders}/fov_fs.glsl (100%) rename {shaders/modules/projection => modules/newhorizons/shaders}/fov_vs.glsl (100%) rename {shaders/modules/projection => modules/newhorizons/shaders}/projectiveTexture_fs.glsl (100%) rename {shaders/modules/projection => modules/newhorizons/shaders}/projectiveTexture_vs.glsl (100%) create mode 100644 src/util/openspacemodule.cpp diff --git a/ext/ghoul b/ext/ghoul index 711fd33d85..f48a0a6420 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 711fd33d8536abc52242ae266566245bdc199a97 +Subproject commit f48a0a6420fced7b5e605bdd5f74685ca0f4b37e diff --git a/include/openspace/util/openspacemodule.h b/include/openspace/util/openspacemodule.h index b0a73522ac..7f2d17c7d7 100644 --- a/include/openspace/util/openspacemodule.h +++ b/include/openspace/util/openspacemodule.h @@ -34,13 +34,14 @@ public: OpenSpaceModule() = default; virtual ~OpenSpaceModule() = default; - virtual bool initialize() { return true; } - virtual bool deinitialize() { return true; } + virtual bool initialize(); + virtual bool deinitialize(); - std::string name() const { return _name; } + std::string name() const; protected: - void setName(std::string name) { _name = std::move(name); } + void setName(std::string name); + std::string modulePath() const; std::string _name; }; diff --git a/modules/base/CMakeLists.txt b/modules/base/CMakeLists.txt index c1b679fc71..2f6af054f8 100644 --- a/modules/base/CMakeLists.txt +++ b/modules/base/CMakeLists.txt @@ -40,6 +40,31 @@ set(SOURCE_FILES ) source_group("Source Files" FILES ${SOURCE_FILES}) +set(SHADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/constellationbounds_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/constellationbounds_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/ephemeris_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/ephemeris_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/fieldline_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/fieldline_gs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/fieldline_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/imageplane_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/imageplane_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/model_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/model_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/path_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/path_gs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/path_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/plane_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/plane_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/sphere_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/sphere_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/star_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/star_ge.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/star_vs.glsl +) +source_group("Shader Files" FILES ${SHADER_FILES}) + set(MODULE_CLASS_FILES ${CMAKE_CURRENT_SOURCE_DIR}/basemodule.h ${CMAKE_CURRENT_SOURCE_DIR}/basemodule.cpp diff --git a/modules/base/basemodule.cpp b/modules/base/basemodule.cpp index bb6b5a59a3..f2edeb85a2 100644 --- a/modules/base/basemodule.cpp +++ b/modules/base/basemodule.cpp @@ -48,9 +48,19 @@ #include #include +#include + namespace openspace { +BaseModule::BaseModule() { + setName("Base"); +} + bool BaseModule::initialize() { + bool success = OpenSpaceModule::initialize(); + if (!success) + return false; + FactoryManager::ref().addFactory(new ghoul::TemplateFactory); FactoryManager::ref().addFactory(new ghoul::TemplateFactory); diff --git a/modules/base/basemodule.h b/modules/base/basemodule.h index 6561a5cace..37dc678bb2 100644 --- a/modules/base/basemodule.h +++ b/modules/base/basemodule.h @@ -31,6 +31,7 @@ namespace openspace { class BaseModule : public OpenSpaceModule { public: + BaseModule(); bool initialize() override; }; diff --git a/modules/base/rendering/renderableconstellationbounds.cpp b/modules/base/rendering/renderableconstellationbounds.cpp index 9837d56656..52fa9785a7 100644 --- a/modules/base/rendering/renderableconstellationbounds.cpp +++ b/modules/base/rendering/renderableconstellationbounds.cpp @@ -91,8 +91,8 @@ RenderableConstellationBounds::~RenderableConstellationBounds() { bool RenderableConstellationBounds::initialize() { _program = ghoul::opengl::ProgramObject::Build("ConstellationBounds", - "${SHADERS}/modules/constellationbounds/constellationbounds_vs.glsl", - "${SHADERS}/modules/constellationbounds/constellationbounds_fs.glsl"); + "${MODULE_BASE}/shaders/constellationbounds_vs.glsl", + "${MODULE_BASE}/shaders/constellationbounds_fs.glsl"); if (!_program) return false; diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index b48257c4ca..487450f7bd 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -128,8 +128,8 @@ bool RenderableModel::initialize() { if (_programObject == nullptr) { // NH shader _programObject = ghoul::opengl::ProgramObject::Build("ModelProgram", - "${SHADERS}/modules/model/model_vs.glsl", - "${SHADERS}/modules/model/model_fs.glsl"); + "${MODULE_BASE}/shaders/model_vs.glsl", + "${MODULE_BASE}/shaders/model_fs.glsl"); if (!_programObject) return false; } diff --git a/modules/base/rendering/renderablepath.cpp b/modules/base/rendering/renderablepath.cpp index 2108a23f6d..8e145f220e 100644 --- a/modules/base/rendering/renderablepath.cpp +++ b/modules/base/rendering/renderablepath.cpp @@ -100,9 +100,8 @@ bool RenderablePath::initialize() { bool completeSuccess = true; _programObject = ghoul::opengl::ProgramObject::Build("PathProgram", - "${SHADERS}/modules/trails/path_vs.glsl", - "${SHADERS}/modules/trails/path_fs.glsl" - //,"${SHADERS}/modules/trails/path_gs.glsl" + "${MODULE_BASE}/shaders/path_vs.glsl", + "${MODULE_BASE}/shaders/path_fs.glsl" ); if (!_programObject) return false; @@ -200,45 +199,48 @@ void RenderablePath::update(const UpdateData& data) { } void RenderablePath::calculatePath(std::string observer) { - - double interval = (_stop - _start); - int segments = static_cast(interval /_increment); - double lightTime; - bool correctPosition = true; + double interval = (_stop - _start); + int segments = static_cast(interval /_increment); - psc pscPos; - double currentTime = _start; - _vertexArray.resize(segments); + if (segments == 0) + return; - //float r, g, b; - //float g = _lineColor[1]; - //float b = _lineColor[2]; - for (int i = 0; i < segments; i++) { - correctPosition = SpiceManager::ref().getTargetPosition(_target, observer, _frame, "NONE", currentTime, pscPos, lightTime); - pscPos[3] += 3; + double lightTime; + bool correctPosition = true; + + psc pscPos; + double currentTime = _start; + _vertexArray.resize(segments); + + //float r, g, b; + //float g = _lineColor[1]; + //float b = _lineColor[2]; + for (int i = 0; i < segments; i++) { + correctPosition = SpiceManager::ref().getTargetPosition(_target, observer, _frame, "NONE", currentTime, pscPos, lightTime); + pscPos[3] += 3; - //if (!correctPosition) { - // r = 1.f; - // g = b = 0.5f; - //} - //else if ((i % 8) == 0) { - // r = _lineColor[0]; - // g = _lineColor[1]; - // b = _lineColor[2]; - //} - //else { - // r = g = b = 0.6f; - //} - //add position - _vertexArray[i] = { pscPos[0], pscPos[1], pscPos[2], pscPos[3] }; - //add color for position - //_vertexArray[i + 1] = { r, g, b, a }; - currentTime += _increment; - } - _lastPosition = pscPos.dvec4(); + //if (!correctPosition) { + // r = 1.f; + // g = b = 0.5f; + //} + //else if ((i % 8) == 0) { + // r = _lineColor[0]; + // g = _lineColor[1]; + // b = _lineColor[2]; + //} + //else { + // r = g = b = 0.6f; + //} + //add position + _vertexArray[i] = { pscPos[0], pscPos[1], pscPos[2], pscPos[3] }; + //add color for position + //_vertexArray[i + 1] = { r, g, b, a }; + currentTime += _increment; + } + _lastPosition = pscPos.dvec4(); - glBindBuffer(GL_ARRAY_BUFFER, _vBufferID); - glBufferSubData(GL_ARRAY_BUFFER, 0, _vertexArray.size() * sizeof(VertexInfo), &_vertexArray[0]); + glBindBuffer(GL_ARRAY_BUFFER, _vBufferID); + glBufferSubData(GL_ARRAY_BUFFER, 0, _vertexArray.size() * sizeof(VertexInfo), &_vertexArray[0]); } void RenderablePath::sendToGPU() { diff --git a/modules/base/rendering/renderableplane.cpp b/modules/base/rendering/renderableplane.cpp index 96b18ad9cf..aac8d1fb84 100644 --- a/modules/base/rendering/renderableplane.cpp +++ b/modules/base/rendering/renderableplane.cpp @@ -128,8 +128,8 @@ bool RenderablePlane::initialize() { if (_shader == nullptr) { // Plane Program _shader = ghoul::opengl::ProgramObject::Build("PlaneProgram", - "${SHADERS}/modules/plane/plane_vs.glsl", - "${SHADERS}/modules/plane/plane_fs.glsl"); + "${MODULE_BASE}/shaders/plane_vs.glsl", + "${MODULE_BASE}/shaders/plane_fs.glsl"); if (!_shader) return false; } diff --git a/modules/base/rendering/renderablesphere.cpp b/modules/base/rendering/renderablesphere.cpp index 996f5e9863..70c6dee647 100644 --- a/modules/base/rendering/renderablesphere.cpp +++ b/modules/base/rendering/renderablesphere.cpp @@ -120,8 +120,8 @@ bool RenderableSphere::initialize() { // pscstandard _shader = ghoul::opengl::ProgramObject::Build("Sphere", - "${SHADERS}/modules/sphere/sphere_vs.glsl", - "${SHADERS}/modules/sphere/sphere_fs.glsl"); + "${MODULES}/base/shaders/sphere_vs.glsl", + "${MODULES}/base/shaders/sphere_fs.glsl"); if (!_shader) return false; diff --git a/modules/base/rendering/renderablestars.cpp b/modules/base/rendering/renderablestars.cpp index 079645859b..1bc08cff79 100644 --- a/modules/base/rendering/renderablestars.cpp +++ b/modules/base/rendering/renderablestars.cpp @@ -147,9 +147,9 @@ bool RenderableStars::initialize() { bool completeSuccess = true; _program = ghoul::opengl::ProgramObject::Build("Star", - "${SHADERS}/modules/stars/star_vs.glsl", - "${SHADERS}/modules/stars/star_fs.glsl", - "${SHADERS}/modules/stars/star_ge.glsl"); + "${MODULE_BASE}/shaders/star_vs.glsl", + "${MODULE_BASE}/shaders/star_fs.glsl", + "${MODULE_BASE}/shaders/star_ge.glsl"); if (!_program) return false; completeSuccess &= loadData(); diff --git a/modules/base/rendering/renderabletrail.cpp b/modules/base/rendering/renderabletrail.cpp index 6110b6056a..045ee51131 100644 --- a/modules/base/rendering/renderabletrail.cpp +++ b/modules/base/rendering/renderabletrail.cpp @@ -114,8 +114,8 @@ bool RenderableTrail::initialize() { bool completeSuccess = true; _programObject = ghoul::opengl::ProgramObject::Build("EphemerisProgram", - "${SHADERS}/modules/trails/ephemeris_vs.glsl", - "${SHADERS}/modules/trails/ephemeris_fs.glsl"); + "${MODULE_BASE}/shaders/ephemeris_vs.glsl", + "${MODULE_BASE}/shaders/ephemeris_fs.glsl"); if (!_programObject) return false; diff --git a/shaders/modules/constellationbounds/constellationbounds_fs.glsl b/modules/base/shaders/constellationbounds_fs.glsl similarity index 100% rename from shaders/modules/constellationbounds/constellationbounds_fs.glsl rename to modules/base/shaders/constellationbounds_fs.glsl diff --git a/shaders/modules/constellationbounds/constellationbounds_vs.glsl b/modules/base/shaders/constellationbounds_vs.glsl similarity index 100% rename from shaders/modules/constellationbounds/constellationbounds_vs.glsl rename to modules/base/shaders/constellationbounds_vs.glsl diff --git a/shaders/modules/trails/ephemeris_fs.glsl b/modules/base/shaders/ephemeris_fs.glsl similarity index 100% rename from shaders/modules/trails/ephemeris_fs.glsl rename to modules/base/shaders/ephemeris_fs.glsl diff --git a/shaders/modules/trails/ephemeris_vs.glsl b/modules/base/shaders/ephemeris_vs.glsl similarity index 100% rename from shaders/modules/trails/ephemeris_vs.glsl rename to modules/base/shaders/ephemeris_vs.glsl diff --git a/shaders/modules/fieldlines/fieldline_fs.glsl b/modules/base/shaders/fieldline_fs.glsl similarity index 100% rename from shaders/modules/fieldlines/fieldline_fs.glsl rename to modules/base/shaders/fieldline_fs.glsl diff --git a/shaders/modules/fieldlines/fieldline_gs.glsl b/modules/base/shaders/fieldline_gs.glsl similarity index 100% rename from shaders/modules/fieldlines/fieldline_gs.glsl rename to modules/base/shaders/fieldline_gs.glsl diff --git a/shaders/modules/fieldlines/fieldline_vs.glsl b/modules/base/shaders/fieldline_vs.glsl similarity index 100% rename from shaders/modules/fieldlines/fieldline_vs.glsl rename to modules/base/shaders/fieldline_vs.glsl diff --git a/shaders/modules/imageplane/imageplane_fs.glsl b/modules/base/shaders/imageplane_fs.glsl similarity index 100% rename from shaders/modules/imageplane/imageplane_fs.glsl rename to modules/base/shaders/imageplane_fs.glsl diff --git a/shaders/modules/imageplane/imageplane_vs.glsl b/modules/base/shaders/imageplane_vs.glsl similarity index 100% rename from shaders/modules/imageplane/imageplane_vs.glsl rename to modules/base/shaders/imageplane_vs.glsl diff --git a/shaders/modules/model/model_fs.glsl b/modules/base/shaders/model_fs.glsl similarity index 100% rename from shaders/modules/model/model_fs.glsl rename to modules/base/shaders/model_fs.glsl diff --git a/shaders/modules/model/model_vs.glsl b/modules/base/shaders/model_vs.glsl similarity index 100% rename from shaders/modules/model/model_vs.glsl rename to modules/base/shaders/model_vs.glsl diff --git a/shaders/modules/trails/path_fs.glsl b/modules/base/shaders/path_fs.glsl similarity index 100% rename from shaders/modules/trails/path_fs.glsl rename to modules/base/shaders/path_fs.glsl diff --git a/shaders/modules/trails/path_gs.glsl b/modules/base/shaders/path_gs.glsl similarity index 100% rename from shaders/modules/trails/path_gs.glsl rename to modules/base/shaders/path_gs.glsl diff --git a/shaders/modules/trails/path_vs.glsl b/modules/base/shaders/path_vs.glsl similarity index 100% rename from shaders/modules/trails/path_vs.glsl rename to modules/base/shaders/path_vs.glsl diff --git a/shaders/modules/plane/plane_fs.glsl b/modules/base/shaders/plane_fs.glsl similarity index 100% rename from shaders/modules/plane/plane_fs.glsl rename to modules/base/shaders/plane_fs.glsl diff --git a/shaders/modules/plane/plane_vs.glsl b/modules/base/shaders/plane_vs.glsl similarity index 100% rename from shaders/modules/plane/plane_vs.glsl rename to modules/base/shaders/plane_vs.glsl diff --git a/shaders/modules/sphere/sphere_fs.glsl b/modules/base/shaders/sphere_fs.glsl similarity index 100% rename from shaders/modules/sphere/sphere_fs.glsl rename to modules/base/shaders/sphere_fs.glsl diff --git a/shaders/modules/sphere/sphere_vs.glsl b/modules/base/shaders/sphere_vs.glsl similarity index 100% rename from shaders/modules/sphere/sphere_vs.glsl rename to modules/base/shaders/sphere_vs.glsl diff --git a/shaders/modules/stars/star_fs.glsl b/modules/base/shaders/star_fs.glsl similarity index 100% rename from shaders/modules/stars/star_fs.glsl rename to modules/base/shaders/star_fs.glsl diff --git a/shaders/modules/stars/star_ge.glsl b/modules/base/shaders/star_ge.glsl similarity index 100% rename from shaders/modules/stars/star_ge.glsl rename to modules/base/shaders/star_ge.glsl diff --git a/shaders/modules/stars/star_vs.glsl b/modules/base/shaders/star_vs.glsl similarity index 100% rename from shaders/modules/stars/star_vs.glsl rename to modules/base/shaders/star_vs.glsl diff --git a/modules/newhorizons/newhorizonsmodule.cpp b/modules/newhorizons/newhorizonsmodule.cpp index a6e835b595..720472deac 100644 --- a/modules/newhorizons/newhorizonsmodule.cpp +++ b/modules/newhorizons/newhorizonsmodule.cpp @@ -43,7 +43,15 @@ namespace openspace { +NewHorizonsModule::NewHorizonsModule() { + setName("NewHorizons"); +} + bool NewHorizonsModule::initialize() { + bool success = OpenSpaceModule::initialize(); + if (!success) + return false; + FactoryManager::ref().addFactory(new ghoul::TemplateFactory); FactoryManager::ref().addFactory(new ghoul::TemplateFactory); diff --git a/modules/newhorizons/newhorizonsmodule.h b/modules/newhorizons/newhorizonsmodule.h index c5d89d8cbd..ebbc4ef3d6 100644 --- a/modules/newhorizons/newhorizonsmodule.h +++ b/modules/newhorizons/newhorizonsmodule.h @@ -31,6 +31,7 @@ namespace openspace { class NewHorizonsModule : public OpenSpaceModule { public: + NewHorizonsModule(); bool initialize() override; }; diff --git a/modules/newhorizons/rendering/renderablecrawlingline.cpp b/modules/newhorizons/rendering/renderablecrawlingline.cpp index 58dcdd354d..8169ce1b33 100644 --- a/modules/newhorizons/rendering/renderablecrawlingline.cpp +++ b/modules/newhorizons/rendering/renderablecrawlingline.cpp @@ -77,8 +77,8 @@ bool RenderableCrawlingLine::initialize() { _frameCounter = 0; bool completeSuccess = true; _program = ghoul::opengl::ProgramObject::Build("RenderableCrawlingLine", - "${SHADERS}/modules/crawlingline/crawlingline_vs.glsl", - "${SHADERS}/modules/crawlingline/crawlingline_fs.glsl" + "${MODULE_NEWHORIZONS}/shaders/crawlingline_vs.glsl", + "${MODULE_NEWHORIZONS}/shaders/crawlingline_fs.glsl" ); if (!_program) return false; diff --git a/modules/newhorizons/rendering/renderablefov.cpp b/modules/newhorizons/rendering/renderablefov.cpp index 6f82510f10..43e8a941a4 100644 --- a/modules/newhorizons/rendering/renderablefov.cpp +++ b/modules/newhorizons/rendering/renderablefov.cpp @@ -145,8 +145,8 @@ bool RenderableFov::initialize() { bool completeSuccess = true; if (_programObject == nullptr) { _programObject = ghoul::opengl::ProgramObject::Build("FovProgram", - "${SHADERS}/modules/projection/fov_vs.glsl", - "${SHADERS}/modules/projection/fov_fs.glsl"); + "${MODULE_NEWHORIZONS}/shaders/fov_vs.glsl", + "${MODULE_NEWHORIZONS}/shaders/fov_fs.glsl"); if (!_programObject) return false; } diff --git a/modules/newhorizons/rendering/renderableplaneprojection.cpp b/modules/newhorizons/rendering/renderableplaneprojection.cpp index ef2def2353..bdcf032200 100644 --- a/modules/newhorizons/rendering/renderableplaneprojection.cpp +++ b/modules/newhorizons/rendering/renderableplaneprojection.cpp @@ -98,8 +98,8 @@ bool RenderablePlaneProjection::initialize() { if (_shader == nullptr) { // Image Plane Program _shader = ghoul::opengl::ProgramObject::Build("ImagePlaneProgram", - "${SHADERS}/modules/imageplane/imageplane_vs.glsl", - "${SHADERS}/modules/imageplane/imageplane_fs.glsl"); + "${MODULE_BASE}/shaders/imageplane_vs.glsl", + "${MODULE_BASE}/shaders/imageplane_fs.glsl"); if (!_shader) return false; } diff --git a/modules/newhorizons/rendering/renderableplanetprojection.cpp b/modules/newhorizons/rendering/renderableplanetprojection.cpp index 1a13599eab..e17d4f81bc 100644 --- a/modules/newhorizons/rendering/renderableplanetprojection.cpp +++ b/modules/newhorizons/rendering/renderableplanetprojection.cpp @@ -212,8 +212,8 @@ bool RenderablePlanetProjection::initialize() { if (_programObject == nullptr) { // projection program _programObject = ghoul::opengl::ProgramObject::Build("projectiveProgram", - "${SHADERS}/modules/projection/projectiveTexture_vs.glsl", - "${SHADERS}/modules/projection/projectiveTexture_fs.glsl"); + "${MODULES}/newhorizons/shaders/projectiveTexture_vs.glsl", + "${MODULES}/newhorizons/shaders/projectiveTexture_fs.glsl"); if (!_programObject) return false; } @@ -289,7 +289,7 @@ bool RenderablePlanetProjection::deinitialize(){ return true; } bool RenderablePlanetProjection::isReady() const { - return (_geometry != nullptr); + return _geometry && _programObject; } void RenderablePlanetProjection::imageProjectGPU(){ diff --git a/shaders/modules/crawlingline/crawlingline_fs.glsl b/modules/newhorizons/shaders/crawlingline_fs.glsl similarity index 100% rename from shaders/modules/crawlingline/crawlingline_fs.glsl rename to modules/newhorizons/shaders/crawlingline_fs.glsl diff --git a/shaders/modules/crawlingline/crawlingline_vs.glsl b/modules/newhorizons/shaders/crawlingline_vs.glsl similarity index 100% rename from shaders/modules/crawlingline/crawlingline_vs.glsl rename to modules/newhorizons/shaders/crawlingline_vs.glsl diff --git a/shaders/modules/projection/fov_fs.glsl b/modules/newhorizons/shaders/fov_fs.glsl similarity index 100% rename from shaders/modules/projection/fov_fs.glsl rename to modules/newhorizons/shaders/fov_fs.glsl diff --git a/shaders/modules/projection/fov_vs.glsl b/modules/newhorizons/shaders/fov_vs.glsl similarity index 100% rename from shaders/modules/projection/fov_vs.glsl rename to modules/newhorizons/shaders/fov_vs.glsl diff --git a/shaders/modules/projection/projectiveTexture_fs.glsl b/modules/newhorizons/shaders/projectiveTexture_fs.glsl similarity index 100% rename from shaders/modules/projection/projectiveTexture_fs.glsl rename to modules/newhorizons/shaders/projectiveTexture_fs.glsl diff --git a/shaders/modules/projection/projectiveTexture_vs.glsl b/modules/newhorizons/shaders/projectiveTexture_vs.glsl similarity index 100% rename from shaders/modules/projection/projectiveTexture_vs.glsl rename to modules/newhorizons/shaders/projectiveTexture_vs.glsl diff --git a/modules/volume/volumemodule.cpp b/modules/volume/volumemodule.cpp index 6811bbb55f..08eba0f856 100644 --- a/modules/volume/volumemodule.cpp +++ b/modules/volume/volumemodule.cpp @@ -33,7 +33,14 @@ namespace openspace { +VolumeModule::VolumeModule() { + setName("Volume"); +} + bool VolumeModule::initialize() { + bool success = OpenSpaceModule::initialize(); + if (!success) + return false; auto fRenderable = FactoryManager::ref().factory(); ghoul_assert(fRenderable, "No renderable factory existed"); diff --git a/modules/volume/volumemodule.h b/modules/volume/volumemodule.h index 5377c1940b..73094ba1d6 100644 --- a/modules/volume/volumemodule.h +++ b/modules/volume/volumemodule.h @@ -31,6 +31,7 @@ namespace openspace { class VolumeModule : public OpenSpaceModule { public: + VolumeModule(); bool initialize() override; }; diff --git a/openspace.cfg b/openspace.cfg index 505bf7812c..1fb1965c3e 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -15,6 +15,7 @@ return { SHADERS = "${BASE_PATH}/shaders", SHADERS_GENERATED = "${SHADERS}/generated", OPENSPACE_DATA = "${BASE_PATH}/openspace-data", + MODULES = "${BASE_PATH}/modules", TESTDIR = "${BASE_PATH}/tests", CONFIG = "${BASE_PATH}/config", CACHE = "${BASE_PATH}/cache", diff --git a/src/abuffer/abuffer.cpp b/src/abuffer/abuffer.cpp index 52ced2a1ce..700413430d 100644 --- a/src/abuffer/abuffer.cpp +++ b/src/abuffer/abuffer.cpp @@ -191,6 +191,8 @@ int ABuffer::addSamplerfile(const std::string& filename) { } bool ABuffer::updateShader() { + if (_resolveShader == nullptr) + return false; bool s = _resolveShader->rebuildFromFile(); if (s) { int startAt = 0; diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 4717af4fec..686ca9c836 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -110,9 +110,6 @@ OpenSpaceEngine::OpenSpaceEngine(std::string programName) SpiceManager::initialize(); Time::initialize(); ghoul::systemcapabilities::SystemCapabilities::initialize(); - - // Register modules - _moduleEngine->initialize(); } OpenSpaceEngine::~OpenSpaceEngine() { @@ -226,6 +223,9 @@ bool OpenSpaceEngine::create( } } + // Register modules + _engine->_moduleEngine->initialize(); + // Create the cachemanager FileSys.createCacheManager(absPath("${" + ConfigurationManager::KeyCache + "}"), CacheVersion); _engine->_console->initialize(); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index f022906ac1..80da9a901f 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -198,6 +198,8 @@ void GUI::initialize() { void GUI::initializeGL() { _program = ghoul::opengl::ProgramObject::Build("GUI", "${SHADERS}/gui_vs.glsl", "${SHADERS}/gui_fs.glsl"); + if (!_program) + return; positionLocation = glGetAttribLocation(*_program, "in_position"); uvLocation = glGetAttribLocation(*_program, "in_uv"); diff --git a/src/util/openspacemodule.cpp b/src/util/openspacemodule.cpp new file mode 100644 index 0000000000..dc944f3fe3 --- /dev/null +++ b/src/util/openspacemodule.cpp @@ -0,0 +1,83 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include + +#include + +namespace { + const std::string _loggerCat = "OpenSpaceModule"; + const std::string ModuleBaseToken = "MODULE_"; +} +//ghoul::filesystem::FileSystem::TokenOpeningBraces +//ghoul::filesystem::FileSystem::TokenClosingBraces +namespace openspace { + +bool OpenSpaceModule::initialize() { + ghoul_assert(!(name().empty()), "Module name must be set before initialize call"); + std::string moduleNameUpper = name(); + std::transform(moduleNameUpper.begin(), moduleNameUpper.end(), moduleNameUpper.begin(), toupper); + std::string moduleToken = + ghoul::filesystem::FileSystem::TokenOpeningBraces + + ModuleBaseToken + + moduleNameUpper + + ghoul::filesystem::FileSystem::TokenClosingBraces; + + std::string path = modulePath(); + LDEBUG("Registering module path: " << moduleToken << ": " << path); + FileSys.registerPathToken(moduleToken, path); + return true; +} + +bool OpenSpaceModule::deinitialize() { + return true; +} + +std::string OpenSpaceModule::name() const { + return _name; +} + +void OpenSpaceModule::setName(std::string name) { + _name = std::move(name); +} + +std::string OpenSpaceModule::modulePath() const { + std::string moduleName = name(); + std::transform(moduleName.begin(), moduleName.end(), moduleName.begin(), tolower); + + if (FileSys.directoryExists("${MODULES}/" + moduleName)) + return absPath("${MODULES}/" + moduleName); + +#ifdef EXTERNAL_MODULES_PATHS + +#endif + LERROR("Could not resolve path for module '" << name() << "'"); + return ""; +} + + +} // namespace openspace From 106cb4541061ea889c88afbcf2c06716030eb6be Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 21 May 2015 22:09:16 +0200 Subject: [PATCH 032/329] Started work on CMakeLists cleanup --- CMakeLists.txt | 4 ++-- ext/ghoul | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ffe1005913..892b14e9ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,11 +69,11 @@ endif () ######################################################################################### # Ghoul -set(GHOUL_ROOT_DIR "${OPENSPACE_EXT_DIR}/ghoul") +#set(GHOUL_ROOT_DIR "${OPENSPACE_EXT_DIR}/ghoul") include_directories("${GHOUL_ROOT_DIR}/include") include_directories("${GHOUL_ROOT_DIR}/ext/tinyobjloader") set(BOOST_ROOT "${GHOUL_ROOT_DIR}/ext/boost") -add_subdirectory(${GHOUL_ROOT_DIR}) +add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul) set(DEPENDENT_LIBS ${DEPENDENT_LIBS} Ghoul) if (GHOUL_USE_FREEIMAGE) add_definitions(-DGHOUL_USE_FREEIMAGE) diff --git a/ext/ghoul b/ext/ghoul index f48a0a6420..f847b4a4be 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit f48a0a6420fced7b5e605bdd5f74685ca0f4b37e +Subproject commit f847b4a4becb913b89ed7220edfe2faafb90efbc From 8f0e1edcdddfd76240451171b27897cb214b4e80 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 22 May 2015 01:17:15 +0200 Subject: [PATCH 033/329] Reorganized CMakeLists file and included new Ghoul format --- CMakeLists.txt | 348 ++++++++++----- ext/ghoul | 2 +- ext/imgui/CMakeLists.txt | 29 ++ modules/base/CMakeLists.txt | 32 +- modules/newhorizons/CMakeLists.txt | 32 +- modules/volume/CMakeLists.txt | 32 +- src/CMakeLists.txt | 410 ++++++++++++------ .../joystickexternalcontrol.cpp.orig | 63 --- .../keyboardexternalcontrol.cpp.orig | 62 --- .../mouseexternalcontrol.cpp.orig | 79 ---- 10 files changed, 633 insertions(+), 456 deletions(-) create mode 100644 ext/imgui/CMakeLists.txt delete mode 100644 src/interaction/externalcontrol/joystickexternalcontrol.cpp.orig delete mode 100644 src/interaction/externalcontrol/keyboardexternalcontrol.cpp.orig delete mode 100644 src/interaction/externalcontrol/mouseexternalcontrol.cpp.orig diff --git a/CMakeLists.txt b/CMakeLists.txt index 892b14e9ad..c1c761a5d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ # # # OpenSpace # # # -# Copyright (c) 2014 # +# Copyright (c) 2014-2015 # # # # 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 # @@ -22,17 +22,19 @@ # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # ######################################################################################### +cmake_minimum_required (VERSION 3.0) + +# Remove MinSizeRel build option +set(CMAKE_CONFIGURATION_TYPES Debug Release RelWithDebInfo CACHE TYPE INTERNAL FORCE) +mark_as_advanced(CMAKE_CONFIGURATION_TYPES) +mark_as_advanced(CMAKE_INSTALL_PREFIX) + +project (OpenSpace) +message(STATUS "Generating OpenSpace project") ######################################################################################### # General Settings ######################################################################################### - -cmake_minimum_required (VERSION 2.8) -project (OpenSpace) - -if (WIN32) - SET(CMAKE_EXE_LINKER_FLAGS /NODEFAULTLIB:\"LIBCMTD.lib;LIBCMT.lib\") -endif () set(OPENSPACE_BASE_DIR "${PROJECT_SOURCE_DIR}") set(OPENSPACE_EXT_DIR "${OPENSPACE_BASE_DIR}/ext") @@ -45,149 +47,257 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OPENSPACE_LIBRARY_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OPENSPACE_LIBRARY_DIR}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OPENSPACE_BINARY_DIR}) +############################# +# Including the source files (with source groups already defined) +############################# +include(src/CMakeLists.txt) -#include(cotire) +############################# +# Declare the application +############################# +add_library(libOpenSpace STATIC ${OPENSPACE_HEADER} ${OPENSPACE_SOURCE}) +target_include_directories(libOpenSpace PUBLIC ${OPENSPACE_BASE_DIR}/include) -# Make sure a build type is set. Default is Debug. -if(NOT CMAKE_BUILD_TYPE) - set( CMAKE_BUILD_TYPE Debug CACHE STRING - "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." - FORCE ) -endif(NOT CMAKE_BUILD_TYPE) +add_executable(OpenSpace ${OPENSPACE_MAIN}) +target_include_directories(OpenSpace PUBLIC ${OPENSPACE_BASE_DIR}/include) +target_link_libraries(OpenSpace libOpenSpace) -if (APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++") -endif () +############################# +# Compile settings +############################# if (MSVC) - # Enable multicore compilation - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + target_compile_options(libOpenSpace PUBLIC "/MP" "/wd4201" "/wd4127") + set_target_properties(OpenSpace PROPERTIES LINK_FLAGS "/NODEFAULTLIB:\"LIBCMTD.lib;LIBCMT.lib\"") +elseif (APPLE) + target_compile_definitions(libOpenSpace PUBLIC "__APPLE__") + target_compile_options(libOpenSpace PUBLIC "-std=c++11" "-stdlib=libc++") + + target_include_directories(libOpenSpace "/Developer/Headers/FlatCarbon") + find_library(COREFOUNDATION_LIBRARY CoreFoundation) + find_library(CARBON_LIBRARY Carbon) + find_library(COCOA_LIBRARY Carbon) + find_library(APP_SERVICES_LIBRARY ApplicationServices) + mark_as_advanced(CARBON_LIBRARY COCOA_LIBRARY APP_SERVICES_LIBRARY) + target_link_libraries(libOpenSpace + ${CARBON_LIBRARY} + ${COREFOUNDATION_LIBRARY} + ${COCOA_LIBRARY} + ${APP_SERVICES_LIBRARY} + ) +elseif (UNIX) + target_compile_definitions(libOpenSpace PUBLIC "-std=c++0x -ggdb") endif () -######################################################################################### -# External Third-party software -######################################################################################### +############################# +# Dependencies +############################# # Ghoul -#set(GHOUL_ROOT_DIR "${OPENSPACE_EXT_DIR}/ghoul") -include_directories("${GHOUL_ROOT_DIR}/include") -include_directories("${GHOUL_ROOT_DIR}/ext/tinyobjloader") -set(BOOST_ROOT "${GHOUL_ROOT_DIR}/ext/boost") add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul) -set(DEPENDENT_LIBS ${DEPENDENT_LIBS} Ghoul) -if (GHOUL_USE_FREEIMAGE) - add_definitions(-DGHOUL_USE_FREEIMAGE) -endif () -if (GHOUL_USE_DEVIL) - add_definitions(-DGHOUL_USE_DEVIL) -endif () +target_link_libraries(libOpenSpace Ghoul) +get_property(GHOUL_INCLUDE_DIR TARGET Ghoul PROPERTY INTERFACE_INCLUDE_DIRECTORIES) +target_include_directories(libOpenSpace PUBLIC ${GHOUL_INCLUDE_DIR}) +get_property(GHOUL_DEFINITIONS TARGET Ghoul PROPERTY INTERFACE_COMPILE_DEFINITIONS) +target_compile_definitions(libOpenSpace PUBLIC ${GHOUL_DEFINITIONS}) -# Add ghoul ext -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${GHOUL_ROOT_DIR}/ext) - -# Boost -include_directories(${GHOUL_ROOT_DIR}/ext/boost) - -# SGCT +# # SGCT find_package(SGCT REQUIRED) -include_directories(${SGCT_INCLUDE_DIRECTORIES}) -set(DEPENDENT_LIBS ${DEPENDENT_LIBS} ${SGCT_LIBRARIES}) +target_include_directories(libOpenSpace SYSTEM PUBLIC ${SGCT_INCLUDE_DIRECTORIES}) +target_link_libraries(libOpenSpace ${SGCT_LIBRARIES}) if (UNIX AND (NOT APPLE)) - set(DEPENDENT_LIBS ${DEPENDENT_LIBS} Xcursor Xinerama) + target_link_libraries(libOpenSpace Xcursor Xinerama) endif () -# GLM -set(GLM_ROOT_DIR "${GHOUL_ROOT_DIR}/ext/glm") -find_package(GLM REQUIRED) -add_definitions(-DGLM_SWIZZLE) -include_directories(${GLM_INCLUDE_DIRS}) - -# GLEW -find_package(GLEW REQUIRED) -include_directories(${GLEW_INCLUDE_DIRECTORIES}) -set(DEPENDENT_LIBS ${DEPENDENT_LIBS} ${GLEW_LIBRARIES}) - -# Lua -set(LUA_ROOT_DIR "${GHOUL_ROOT_DIR}/ext/lua") -include_directories("${LUA_ROOT_DIR}/src") - # Spice set(SPICE_ROOT_DIR "${OPENSPACE_EXT_DIR}/spice") find_package(Spice REQUIRED) -include_directories(${SPICE_INCLUDE_DIRS}) -set(DEPENDENT_LIBS ${DEPENDENT_LIBS} ${SPICE_LIBRARIES}) +target_include_directories(libOpenSpace SYSTEM PUBLIC ${SPICE_INCLUDE_DIRS}) +target_link_libraries(libOpenSpace ${SPICE_LIBRARIES}) -# Kameleon +# # Kameleon option(KAMELEON_LIBRARY_ONLY "Build with Kameleon as library only" ON) -if(WIN32) +if (WIN32) option(BUILD_SHARED_LIBS "Build Shared Libraries" OFF) -else(WIN32) +else () option(BUILD_SHARED_LIBS "Build Shared Libraries" ON) -endif(WIN32) +endif () +mark_as_advanced(BUILD_SHARED_LIBS) # Change to set instead of option option(KAMELEON_USE_HDF5 "Kameleon use HDF5" OFF) set(KAMELEON_ROOT_DIR ${OPENSPACE_EXT_DIR}/kameleon) set(KAMELEON_INCLUDES ${KAMELEON_ROOT_DIR}/src) +set(BOOST_ROOT "${OPENSPACE_EXT_DIR}/ghoul/ext/boost") add_subdirectory(${KAMELEON_ROOT_DIR}) -include_directories(${KAMELEON_INCLUDES}) -set(DEPENDENT_LIBS ${DEPENDENT_LIBS} ccmc) +target_include_directories(libOpenSpace SYSTEM PUBLIC ${KAMELEON_INCLUDES}) +target_link_libraries(libOpenSpace ccmc) -if (APPLE) - include_directories(/Developer/Headers/FlatCarbon) - find_library(CARBON_LIBRARY Carbon) - find_library(COCOA_LIBRARY Cocoa) - find_library(APP_SERVICES_LIBRARY ApplicationServices) - mark_as_advanced(CARBON_LIBRARY COCOA_LIBRARY APP_SERVICES_LIBRARY) - set(DEPENDENT_LIBS ${DEPENDENT_LIBS} ${CARBON_LIBRARY} ${COCOA_LIBRARY} ${APP_SERVICES_LIBRARY}) -endif () - -######################################################################################### -# Executable -######################################################################################### - -add_subdirectory(src) -add_subdirectory(modules/base) -add_subdirectory(modules/newhorizons) -add_subdirectory(modules/volume) +# Imgui +add_subdirectory(${OPENSPACE_EXT_DIR}/imgui) +get_property(IMGUI_INCLUDE_DIR TARGET Imgui PROPERTY INTERFACE_INCLUDE_DIRECTORIES) +target_link_libraries(libOpenSpace Imgui) +target_include_directories(libOpenSpace PUBLIC ${IMGUI_INCLUDE_DIR}) +############################# +# Other applications +############################# option(BUILD_GUI_APPLICATIONS "Build GUI Applications" OFF) if (BUILD_GUI_APPLICATIONS) add_subdirectory(gui) endif () -######################################################################################### -# File Fetch -######################################################################################### -option(DOWNLOAD_FILES "Download large OpenSpace data on configure" OFF) -if(DOWNLOAD_FILES) - function(DownloadFile FILE_PATH FILE_URL) - set(FILE_MD5 ${ARGV2}) - if(NOT EXISTS "${FILE_PATH}") - file(DOWNLOAD ${FILE_URL} ${FILE_PATH} INACTIVITY_TIMEOUT 10 SHOW_PROGRESS) - endif() - file(MD5 ${FILE_PATH} MD5_RESULT) - if( NOT "${FILE_MD5}" STREQUAL "" ) - string(COMPARE EQUAL ${MD5_RESULT} ${FILE_MD5} SUCCESS) - if(NOT ${SUCCESS}) - message(WARNING "${FILE_PATH} not matching MD5") - endif() - endif() - endfunction(DownloadFile) +option(OPENSPACE_HAVE_TESTS "Activate the OpenSpace unit tests" ON) +if (OPENSPACE_HAVE_TESTS) + file(GLOB_RECURSE OPENSPACE_TEST_FILES ${OPENSPACE_BASE_DIR}/tests/*.inl) - function(NewHorizonDownload FILE_PATH ) - set(MD5 "${ARGV1}") - DownloadFile("${OPENSPACE_BASE_DIR}/openspace-data/spice/JupiterNhKernels/${FILE_PATH}" - "http://naif.jpl.nasa.gov/pub/naif/pds/data/nh-j_p_ss-spice-6-v1.0/nhsp_1000/data/${FILE_PATH}" ${MD5}) - endfunction(NewHorizonDownload) + add_executable(OpenSpaceTest ${OPENSPACE_BASE_DIR}/tests/main.cpp ${OPENSPACE_TEST_FILES}) + target_include_directories(OpenSpaceTest PUBLIC "${OPENSPACE_BASE_DIR}/include") + target_link_libraries(OpenSpaceTest gtest libOpenSpace) - NewHorizonDownload("ck/merged_nhpc_2006_v011.bc") - NewHorizonDownload("ck/merged_nhpc_2007_v006.bc") - NewHorizonDownload("fk/nh_v200.tf") - NewHorizonDownload("ik/nh_lorri_v100.ti") - NewHorizonDownload("sclk/new_horizons_413.tsc" "6f7a87c21cb3e37835261ed745f34d4a") - NewHorizonDownload("spk/de413.bsp") - NewHorizonDownload("spk/jup260.bsp") - NewHorizonDownload("spk/nh_nep_ura_000.bsp") - NewHorizonDownload("spk/nh_recon_e2j_v1.bsp") - NewHorizonDownload("spk/nh_recon_j2sep07_prelimv1.bsp") - NewHorizonDownload("spk/sb_2002jf56_2.bsp") -endif(DOWNLOAD_FILES) + + # set(OPENSPACE_TEST_DIR ${OPENSPACE_BASE_DIR}/tests) + + # include_directories("${GHOUL_ROOT_DIR}/ext/gtest/include") + # include_directories("${GHOUL_ROOT_DIR}/include") + # include_directories("${OPENSPACE_TEST_DIR}") + # file(GLOB_RECURSE OPENSPACE_TEST_FILES ${OPENSPACE_BASE_DIR}/tests/*.inl) + + # source_group(Tests FILES ${OPENSPACE_TEST_FILES}) + + +endif (OPENSPACE_HAVE_TESTS) + +############################# +# Modules +############################# +add_subdirectory(modules/base) +add_subdirectory(modules/newhorizons) +add_subdirectory(modules/volume) + +target_link_libraries(libOpenSpace openspace-module-base openspace-module-newhorizons openspace-module-volume) + + + +# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${OPENSPACE_EXT_DIR}/ghoul/ext) + +# #set(GHOUL_ROOT_DIR "${OPENSPACE_EXT_DIR}/ghoul") +# include_directories("${GHOUL_ROOT_DIR}/include") +# include_directories("${GHOUL_ROOT_DIR}/ext/tinyobjloader") +# set(BOOST_ROOT "${GHOUL_ROOT_DIR}/ext/boost") +# add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul) +# set(DEPENDENT_LIBS ${DEPENDENT_LIBS} Ghoul) +# if (GHOUL_USE_FREEIMAGE) +# add_definitions(-DGHOUL_USE_FREEIMAGE) +# endif () +# if (GHOUL_USE_DEVIL) +# add_definitions(-DGHOUL_USE_DEVIL) +# endif () + +# # Add ghoul ext +# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${GHOUL_ROOT_DIR}/ext) + +# # Boost +# include_directories(${GHOUL_ROOT_DIR}/ext/boost) + +# # SGCT +# find_package(SGCT REQUIRED) +# include_directories(${SGCT_INCLUDE_DIRECTORIES}) +# set(DEPENDENT_LIBS ${DEPENDENT_LIBS} ${SGCT_LIBRARIES}) +# if (UNIX AND (NOT APPLE)) +# set(DEPENDENT_LIBS ${DEPENDENT_LIBS} Xcursor Xinerama) +# endif () + +# # GLM +# set(GLM_ROOT_DIR "${GHOUL_ROOT_DIR}/ext/glm") +# find_package(GLM REQUIRED) +# add_definitions(-DGLM_SWIZZLE) +# include_directories(${GLM_INCLUDE_DIRS}) + +# # GLEW +# find_package(GLEW REQUIRED) +# include_directories(${GLEW_INCLUDE_DIRECTORIES}) +# set(DEPENDENT_LIBS ${DEPENDENT_LIBS} ${GLEW_LIBRARIES}) + +# # Lua +# set(LUA_ROOT_DIR "${GHOUL_ROOT_DIR}/ext/lua") +# include_directories("${LUA_ROOT_DIR}/src") + +# # Spice +# set(SPICE_ROOT_DIR "${OPENSPACE_EXT_DIR}/spice") +# find_package(Spice REQUIRED) +# include_directories(${SPICE_INCLUDE_DIRS}) +# set(DEPENDENT_LIBS ${DEPENDENT_LIBS} ${SPICE_LIBRARIES}) + +# # Kameleon +# option(KAMELEON_LIBRARY_ONLY "Build with Kameleon as library only" ON) +# if(WIN32) +# option(BUILD_SHARED_LIBS "Build Shared Libraries" OFF) +# else(WIN32) +# option(BUILD_SHARED_LIBS "Build Shared Libraries" ON) +# endif(WIN32) +# option(KAMELEON_USE_HDF5 "Kameleon use HDF5" OFF) +# set(KAMELEON_ROOT_DIR ${OPENSPACE_EXT_DIR}/kameleon) +# set(KAMELEON_INCLUDES ${KAMELEON_ROOT_DIR}/src) +# add_subdirectory(${KAMELEON_ROOT_DIR}) +# include_directories(${KAMELEON_INCLUDES}) +# set(DEPENDENT_LIBS ${DEPENDENT_LIBS} ccmc) + +# if (APPLE) +# include_directories(/Developer/Headers/FlatCarbon) +# find_library(CARBON_LIBRARY Carbon) +# find_library(COCOA_LIBRARY Cocoa) +# find_library(APP_SERVICES_LIBRARY ApplicationServices) +# mark_as_advanced(CARBON_LIBRARY COCOA_LIBRARY APP_SERVICES_LIBRARY) +# set(DEPENDENT_LIBS ${DEPENDENT_LIBS} ${CARBON_LIBRARY} ${COCOA_LIBRARY} ${APP_SERVICES_LIBRARY}) +# endif () + +# ######################################################################################### +# # Executable +# ######################################################################################### + +# add_subdirectory(src) +# add_subdirectory(modules/base) +# add_subdirectory(modules/newhorizons) +# add_subdirectory(modules/volume) + +# option(BUILD_GUI_APPLICATIONS "Build GUI Applications" OFF) +# if (BUILD_GUI_APPLICATIONS) +# add_subdirectory(gui) +# endif () + +# ######################################################################################### +# # File Fetch +# ######################################################################################### +# option(DOWNLOAD_FILES "Download large OpenSpace data on configure" OFF) +# if(DOWNLOAD_FILES) +# function(DownloadFile FILE_PATH FILE_URL) +# set(FILE_MD5 ${ARGV2}) +# if(NOT EXISTS "${FILE_PATH}") +# file(DOWNLOAD ${FILE_URL} ${FILE_PATH} INACTIVITY_TIMEOUT 10 SHOW_PROGRESS) +# endif() +# file(MD5 ${FILE_PATH} MD5_RESULT) +# if( NOT "${FILE_MD5}" STREQUAL "" ) +# string(COMPARE EQUAL ${MD5_RESULT} ${FILE_MD5} SUCCESS) +# if(NOT ${SUCCESS}) +# message(WARNING "${FILE_PATH} not matching MD5") +# endif() +# endif() +# endfunction(DownloadFile) + +# function(NewHorizonDownload FILE_PATH ) +# set(MD5 "${ARGV1}") +# DownloadFile("${OPENSPACE_BASE_DIR}/openspace-data/spice/JupiterNhKernels/${FILE_PATH}" +# "http://naif.jpl.nasa.gov/pub/naif/pds/data/nh-j_p_ss-spice-6-v1.0/nhsp_1000/data/${FILE_PATH}" ${MD5}) +# endfunction(NewHorizonDownload) + +# NewHorizonDownload("ck/merged_nhpc_2006_v011.bc") +# NewHorizonDownload("ck/merged_nhpc_2007_v006.bc") +# NewHorizonDownload("fk/nh_v200.tf") +# NewHorizonDownload("ik/nh_lorri_v100.ti") +# NewHorizonDownload("sclk/new_horizons_413.tsc" "6f7a87c21cb3e37835261ed745f34d4a") +# NewHorizonDownload("spk/de413.bsp") +# NewHorizonDownload("spk/jup260.bsp") +# NewHorizonDownload("spk/nh_nep_ura_000.bsp") +# NewHorizonDownload("spk/nh_recon_e2j_v1.bsp") +# NewHorizonDownload("spk/nh_recon_j2sep07_prelimv1.bsp") +# NewHorizonDownload("spk/sb_2002jf56_2.bsp") +# endif(DOWNLOAD_FILES) diff --git a/ext/ghoul b/ext/ghoul index f847b4a4be..63b25b5513 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit f847b4a4becb913b89ed7220edfe2faafb90efbc +Subproject commit 63b25b5513c6b536f87a27052325c6a01356a428 diff --git a/ext/imgui/CMakeLists.txt b/ext/imgui/CMakeLists.txt new file mode 100644 index 0000000000..485134bcd6 --- /dev/null +++ b/ext/imgui/CMakeLists.txt @@ -0,0 +1,29 @@ +######################################################################################### +# # +# OpenSpace # +# # +# Copyright (c) 2014-2015 # +# # +# 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. # +######################################################################################### + +project(Imgui) +message(STATUS "Generating Imgui project") + +add_library(Imgui ${PROJECT_SOURCE_DIR}/imgui.cpp) +target_include_directories(Imgui PUBLIC ${PROJECT_SOURCE_DIR}) diff --git a/modules/base/CMakeLists.txt b/modules/base/CMakeLists.txt index 2f6af054f8..3316dfb50c 100644 --- a/modules/base/CMakeLists.txt +++ b/modules/base/CMakeLists.txt @@ -1,3 +1,27 @@ +######################################################################################### +# # +# OpenSpace # +# # +# Copyright (c) 2014-2015 # +# # +# 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. # +######################################################################################### + set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/modelgeometry.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometry.h @@ -70,6 +94,10 @@ set(MODULE_CLASS_FILES ${CMAKE_CURRENT_SOURCE_DIR}/basemodule.cpp ) -include_directories(${OPENSPACE_BASE_DIR}/include ${OPENSPACE_BASE_DIR}) - add_library(openspace-module-base ${HEADER_FILES} ${SOURCE_FILES} ${MODULE_CLASS_FILES}) +target_include_directories(openspace-module-base PUBLIC "${OPENSPACE_BASE_DIR}/include" "${OPENSPACE_BASE_DIR}") + +get_property(OPENSPACE_INCLUDE_DIR TARGET libOpenSpace PROPERTY INTERFACE_INCLUDE_DIRECTORIES) +target_include_directories(openspace-module-base PUBLIC ${OPENSPACE_INCLUDE_DIR}) +get_property(OPENSPACE_DEFINITIONS TARGET libOpenSpace PROPERTY INTERFACE_COMPILE_DEFINITIONS) +target_compile_definitions(openspace-module-base PUBLIC ${OPENSPACE_DEFINITIONS}) \ No newline at end of file diff --git a/modules/newhorizons/CMakeLists.txt b/modules/newhorizons/CMakeLists.txt index 62c1af6ec4..0de42914fc 100644 --- a/modules/newhorizons/CMakeLists.txt +++ b/modules/newhorizons/CMakeLists.txt @@ -1,3 +1,27 @@ +######################################################################################### +# # +# OpenSpace # +# # +# Copyright (c) 2014-2015 # +# # +# 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. # +######################################################################################### + set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometryprojection.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablecrawlingline.h @@ -42,6 +66,10 @@ set(MODULE_CLASS_FILES ${CMAKE_CURRENT_SOURCE_DIR}/newhorizonsmodule.cpp ) -include_directories(${OPENSPACE_BASE_DIR}/include ${OPENSPACE_BASE_DIR}) - add_library(openspace-module-newhorizons ${HEADER_FILES} ${SOURCE_FILES} ${MODULE_CLASS_FILES}) +target_include_directories(openspace-module-newhorizons PUBLIC "${OPENSPACE_BASE_DIR}/include" "${OPENSPACE_BASE_DIR}") + +get_property(OPENSPACE_INCLUDE_DIR TARGET libOpenSpace PROPERTY INTERFACE_INCLUDE_DIRECTORIES) +target_include_directories(openspace-module-newhorizons PUBLIC ${OPENSPACE_INCLUDE_DIR}) +get_property(OPENSPACE_DEFINITIONS TARGET libOpenSpace PROPERTY INTERFACE_COMPILE_DEFINITIONS) +target_compile_definitions(openspace-module-newhorizons PUBLIC ${OPENSPACE_DEFINITIONS}) diff --git a/modules/volume/CMakeLists.txt b/modules/volume/CMakeLists.txt index 87bea70f10..b13d098160 100644 --- a/modules/volume/CMakeLists.txt +++ b/modules/volume/CMakeLists.txt @@ -1,3 +1,27 @@ +######################################################################################### +# # +# OpenSpace # +# # +# Copyright (c) 2014-2015 # +# # +# 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. # +######################################################################################### + set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablevolume.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablevolumegl.h @@ -15,6 +39,10 @@ set(MODULE_CLASS_FILES ${CMAKE_CURRENT_SOURCE_DIR}/volumemodule.cpp ) -include_directories(${OPENSPACE_BASE_DIR}/include ${OPENSPACE_BASE_DIR}) - add_library(openspace-module-volume ${HEADER_FILES} ${SOURCE_FILES} ${MODULE_CLASS_FILES}) +target_include_directories(openspace-module-volume PUBLIC "${OPENSPACE_BASE_DIR}/include" "${OPENSPACE_BASE_DIR}") + +get_property(OPENSPACE_INCLUDE_DIR TARGET libOpenSpace PROPERTY INTERFACE_INCLUDE_DIRECTORIES) +target_include_directories(openspace-module-volume PUBLIC ${OPENSPACE_INCLUDE_DIR}) +get_property(OPENSPACE_DEFINITIONS TARGET libOpenSpace PROPERTY INTERFACE_COMPILE_DEFINITIONS) +target_compile_definitions(openspace-module-volume PUBLIC ${OPENSPACE_DEFINITIONS}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f4d9f59798..39a4eaa827 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,7 +2,7 @@ # # # OpenSpace # # # -# Copyright (c) 2014 # +# Copyright (c) 2014-2015 # # # # 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 # @@ -22,162 +22,320 @@ # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # ######################################################################################### +set(OPENSPACE_SOURCE + ${OPENSPACE_BASE_DIR}/src/abuffer/abuffer.cpp + ${OPENSPACE_BASE_DIR}/src/abuffer/abufferdynamic.cpp + ${OPENSPACE_BASE_DIR}/src/abuffer/abufferfixed.cpp + ${OPENSPACE_BASE_DIR}/src/abuffer/abufferframebuffer.cpp + ${OPENSPACE_BASE_DIR}/src/abuffer/abuffersinglelinked.cpp + ${OPENSPACE_BASE_DIR}/src/abuffer/abuffervisualizer.cpp + ${OPENSPACE_BASE_DIR}/src/engine/configurationmanager.cpp + ${OPENSPACE_BASE_DIR}/src/engine/logfactory.cpp + ${OPENSPACE_BASE_DIR}/src/engine/moduleengine.cpp + ${OPENSPACE_BASE_DIR}/src/engine/openspaceengine.cpp + ${OPENSPACE_BASE_DIR}/src/gui/gui.cpp + ${OPENSPACE_BASE_DIR}/src/gui/guicomponent.cpp + ${OPENSPACE_BASE_DIR}/src/gui/guihelpcomponent.cpp + ${OPENSPACE_BASE_DIR}/src/gui/guiorigincomponent.cpp + ${OPENSPACE_BASE_DIR}/src/gui/guiperformancecomponent.cpp + ${OPENSPACE_BASE_DIR}/src/gui/guipropertycomponent.cpp + ${OPENSPACE_BASE_DIR}/src/gui/guitimecomponent.cpp + ${OPENSPACE_BASE_DIR}/src/interaction/controller.cpp + ${OPENSPACE_BASE_DIR}/src/interaction/deviceidentifier.cpp + ${OPENSPACE_BASE_DIR}/src/interaction/interactionhandler.cpp + ${OPENSPACE_BASE_DIR}/src/interaction/interactionhandler_lua.inl + ${OPENSPACE_BASE_DIR}/src/interaction/keyboardcontroller.cpp + ${OPENSPACE_BASE_DIR}/src/interaction/luaconsole.cpp + ${OPENSPACE_BASE_DIR}/src/interaction/luaconsole_lua.inl + ${OPENSPACE_BASE_DIR}/src/interaction/mousecontroller.cpp + ${OPENSPACE_BASE_DIR}/src/interaction/externalcontrol/externalconnectioncontroller.cpp + ${OPENSPACE_BASE_DIR}/src/interaction/externalcontrol/externalcontrol.cpp + ${OPENSPACE_BASE_DIR}/src/interaction/externalcontrol/joystickexternalcontrol.cpp + ${OPENSPACE_BASE_DIR}/src/interaction/externalcontrol/keyboardexternalcontrol.cpp + ${OPENSPACE_BASE_DIR}/src/interaction/externalcontrol/mouseexternalcontrol.cpp + ${OPENSPACE_BASE_DIR}/src/interaction/externalcontrol/pythonexternalcontrol.cpp + ${OPENSPACE_BASE_DIR}/src/interaction/externalcontrol/randomexternalcontrol.cpp + ${OPENSPACE_BASE_DIR}/src/network/networkengine.cpp + ${OPENSPACE_BASE_DIR}/src/properties/matrixproperty.cpp + ${OPENSPACE_BASE_DIR}/src/properties/optionproperty.cpp + ${OPENSPACE_BASE_DIR}/src/properties/property.cpp + ${OPENSPACE_BASE_DIR}/src/properties/propertyowner.cpp + ${OPENSPACE_BASE_DIR}/src/properties/scalarproperty.cpp + ${OPENSPACE_BASE_DIR}/src/properties/selectionproperty.cpp + ${OPENSPACE_BASE_DIR}/src/properties/stringproperty.cpp + ${OPENSPACE_BASE_DIR}/src/properties/triggerproperty.cpp + ${OPENSPACE_BASE_DIR}/src/properties/vectorproperty.cpp + ${OPENSPACE_BASE_DIR}/src/query/query.cpp + ${OPENSPACE_BASE_DIR}/src/rendering/renderable.cpp + ${OPENSPACE_BASE_DIR}/src/rendering/renderengine.cpp + ${OPENSPACE_BASE_DIR}/src/rendering/renderengine_lua.inl + ${OPENSPACE_BASE_DIR}/src/scene/ephemeris.cpp + ${OPENSPACE_BASE_DIR}/src/scene/scene.cpp + ${OPENSPACE_BASE_DIR}/src/scene/scene_lua.inl + ${OPENSPACE_BASE_DIR}/src/scene/scenegraph.cpp + ${OPENSPACE_BASE_DIR}/src/scene/scenegraphnode.cpp + ${OPENSPACE_BASE_DIR}/src/scripting/scriptengine.cpp + ${OPENSPACE_BASE_DIR}/src/scripting/scriptengine_lua.inl + ${OPENSPACE_BASE_DIR}/src/util/camera.cpp + ${OPENSPACE_BASE_DIR}/src/util/factorymanager.cpp + ${OPENSPACE_BASE_DIR}/src/util/kameleonwrapper.cpp + ${OPENSPACE_BASE_DIR}/src/util/openspacemodule.cpp + ${OPENSPACE_BASE_DIR}/src/util/powerscaledcoordinate.cpp + ${OPENSPACE_BASE_DIR}/src/util/powerscaledscalar.cpp + ${OPENSPACE_BASE_DIR}/src/util/powerscaledsphere.cpp + ${OPENSPACE_BASE_DIR}/src/util/progressbar.cpp + ${OPENSPACE_BASE_DIR}/src/util/screenlog.cpp + ${OPENSPACE_BASE_DIR}/src/util/spicemanager.cpp + ${OPENSPACE_BASE_DIR}/src/util/syncbuffer.cpp + ${OPENSPACE_BASE_DIR}/src/util/time.cpp + ${OPENSPACE_BASE_DIR}/src/util/time_lua.inl +) -#set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) +set(OPENSPACE_MAIN + ${OPENSPACE_BASE_DIR}/src/main.cpp +) -set(SOURCE_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) -set(HEADER_ROOT_DIR ${CMAKE_SOURCE_DIR}/include) +set(OPENSPACE_HEADER + ${OPENSPACE_BASE_DIR}/include/openspace/abuffer/abuffer.h + ${OPENSPACE_BASE_DIR}/include/openspace/abuffer/abufferdynamic.h + ${OPENSPACE_BASE_DIR}/include/openspace/abuffer/abufferfixed.h + ${OPENSPACE_BASE_DIR}/include/openspace/abuffer/abufferframebuffer.h + ${OPENSPACE_BASE_DIR}/include/openspace/abuffer/abuffersinglelinked.h + ${OPENSPACE_BASE_DIR}/include/openspace/abuffer/abuffervisualizer.h + ${OPENSPACE_BASE_DIR}/include/openspace/engine/configurationmanager.h + ${OPENSPACE_BASE_DIR}/include/openspace/engine/logfactory.h + ${OPENSPACE_BASE_DIR}/include/openspace/engine/moduleengine.h + ${OPENSPACE_BASE_DIR}/include/openspace/engine/openspaceengine.h + ${OPENSPACE_BASE_DIR}/include/openspace/gui/gui.h + ${OPENSPACE_BASE_DIR}/include/openspace/gui/guicomponent.h + ${OPENSPACE_BASE_DIR}/include/openspace/gui/guihelpcomponent.h + ${OPENSPACE_BASE_DIR}/include/openspace/gui/guiorigincomponent.h + ${OPENSPACE_BASE_DIR}/include/openspace/gui/guiperformancecomponent.h + ${OPENSPACE_BASE_DIR}/include/openspace/gui/guipropertycomponent.h + ${OPENSPACE_BASE_DIR}/include/openspace/gui/guitimecomponent.h + ${OPENSPACE_BASE_DIR}/include/openspace/interaction/controller.h + ${OPENSPACE_BASE_DIR}/include/openspace/interaction/deviceidentifier.h + ${OPENSPACE_BASE_DIR}/include/openspace/interaction/interactionhandler.h + ${OPENSPACE_BASE_DIR}/include/openspace/interaction/keyboardcontroller.h + ${OPENSPACE_BASE_DIR}/include/openspace/interaction/luaconsole.h + ${OPENSPACE_BASE_DIR}/include/openspace/interaction/mouse.h + ${OPENSPACE_BASE_DIR}/include/openspace/interaction/mousecontroller.h + ${OPENSPACE_BASE_DIR}/include/openspace/interaction/externalcontrol/externalconnectioncontroller.h + ${OPENSPACE_BASE_DIR}/include/openspace/interaction/externalcontrol/externalcontrol.h + ${OPENSPACE_BASE_DIR}/include/openspace/interaction/externalcontrol/joystickexternalcontrol.h + ${OPENSPACE_BASE_DIR}/include/openspace/interaction/externalcontrol/keyboardexternalcontrol.h + ${OPENSPACE_BASE_DIR}/include/openspace/interaction/externalcontrol/mouseexternalcontrol.h + ${OPENSPACE_BASE_DIR}/include/openspace/interaction/externalcontrol/pythonexternalcontrol.h + ${OPENSPACE_BASE_DIR}/include/openspace/interaction/externalcontrol/randomexternalcontrol.h + ${OPENSPACE_BASE_DIR}/include/openspace/network/networkengine.h + ${OPENSPACE_BASE_DIR}/include/openspace/properties/matrixproperty.h + ${OPENSPACE_BASE_DIR}/include/openspace/properties/numericalproperty.h + ${OPENSPACE_BASE_DIR}/include/openspace/properties/numericalproperty.inl + ${OPENSPACE_BASE_DIR}/include/openspace/properties/optionproperty.h + ${OPENSPACE_BASE_DIR}/include/openspace/properties/property.h + ${OPENSPACE_BASE_DIR}/include/openspace/properties/propertydelegate.h + ${OPENSPACE_BASE_DIR}/include/openspace/properties/propertydelegate.inl + ${OPENSPACE_BASE_DIR}/include/openspace/properties/propertyowner.h + ${OPENSPACE_BASE_DIR}/include/openspace/properties/scalarproperty.h + ${OPENSPACE_BASE_DIR}/include/openspace/properties/selectionproperty.h + ${OPENSPACE_BASE_DIR}/include/openspace/properties/stringproperty.h + ${OPENSPACE_BASE_DIR}/include/openspace/properties/templateproperty.h + ${OPENSPACE_BASE_DIR}/include/openspace/properties/templateproperty.inl + ${OPENSPACE_BASE_DIR}/include/openspace/properties/triggerproperty.h + ${OPENSPACE_BASE_DIR}/include/openspace/properties/vectorproperty.h + ${OPENSPACE_BASE_DIR}/include/openspace/query/query.h + ${OPENSPACE_BASE_DIR}/include/openspace/rendering/renderable.h + ${OPENSPACE_BASE_DIR}/include/openspace/rendering/renderengine.h + ${OPENSPACE_BASE_DIR}/include/openspace/scene/ephemeris.h + ${OPENSPACE_BASE_DIR}/include/openspace/scene/scene.h + ${OPENSPACE_BASE_DIR}/include/openspace/scene/scenegraph.h + ${OPENSPACE_BASE_DIR}/include/openspace/scene/scenegraphnode.h + ${OPENSPACE_BASE_DIR}/include/openspace/scripting/script_helper.h + ${OPENSPACE_BASE_DIR}/include/openspace/scripting/scriptengine.h + ${OPENSPACE_BASE_DIR}/include/openspace/util/camera.h + ${OPENSPACE_BASE_DIR}/include/openspace/util/constants.h + ${OPENSPACE_BASE_DIR}/include/openspace/util/factorymanager.h + ${OPENSPACE_BASE_DIR}/include/openspace/util/factorymanager.inl + ${OPENSPACE_BASE_DIR}/include/openspace/util/kameleonwrapper.h + ${OPENSPACE_BASE_DIR}/include/openspace/util/keys.h + ${OPENSPACE_BASE_DIR}/include/openspace/util/openspacemodule.h + ${OPENSPACE_BASE_DIR}/include/openspace/util/powerscaledcoordinate.h + ${OPENSPACE_BASE_DIR}/include/openspace/util/powerscaledscalar.h + ${OPENSPACE_BASE_DIR}/include/openspace/util/powerscaledsphere.h + ${OPENSPACE_BASE_DIR}/include/openspace/util/progressbar.h + ${OPENSPACE_BASE_DIR}/include/openspace/util/screenlog.h + ${OPENSPACE_BASE_DIR}/include/openspace/util/spicemanager.h + ${OPENSPACE_BASE_DIR}/include/openspace/util/syncbuffer.h + ${OPENSPACE_BASE_DIR}/include/openspace/util/time.h + ${OPENSPACE_BASE_DIR}/include/openspace/util/updatestructures.h + ${OPENSPACE_BASE_DIR}/include/openspace/version.h +) -set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${HEADER_ROOT_DIR}/openspace/version.h) +# Place files into source groups +foreach (file ${OPENSPACE_SOURCE} ${OPENSPACE_HEADER}) + # Remove prefixes from the files + set(original_file ${file}) + string(REPLACE "${OPENSPACE_BASE_DIR}/src/" "" file ${file}) + string(REPLACE "${OPENSPACE_BASE_DIR}/include/openspace/" "" file ${file}) + get_filename_component(directory ${file} DIRECTORY) + if (NOT directory STREQUAL "") + string(REPLACE "/" "\\" directory ${directory}) + string(SUBSTRING ${directory} 0 1 FIRST_LETTER) + string(TOUPPER ${FIRST_LETTER} FIRST_LETTER) + string(REGEX REPLACE "^.(.*)" "${FIRST_LETTER}\\1" directory "${directory}") + source_group("${directory}" FILES ${original_file}) + else () + source_group("" FILES ${original_file}) + endif () +endforeach () -file(GLOB CONFIGURATION_SOURCE ${SOURCE_ROOT_DIR}/configuration/*.cpp) -set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${CONFIGURATION_SOURCE}) -file(GLOB CONFIGURATION_HEADER ${HEADER_ROOT_DIR}/openspace/configuration/*.h) -set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${CONFIGURATION_HEADER}) -source_group(Configuration FILES ${CONFIGURATION_SOURCE} ${CONFIGURATION_HEADER}) +# #set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) -file(GLOB ABUFFER_SOURCE ${SOURCE_ROOT_DIR}/abuffer/*.cpp) -set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${ABUFFER_SOURCE}) -file(GLOB ABUFFER_HEADER ${HEADER_ROOT_DIR}/openspace/abuffer/*.h) -set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${ABUFFER_HEADER}) -source_group(ABuffer FILES ${ABUFFER_SOURCE} ${ABUFFER_HEADER}) +# set(SOURCE_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +# set(HEADER_ROOT_DIR ${CMAKE_SOURCE_DIR}/include) -file(GLOB ENGINE_SOURCE ${SOURCE_ROOT_DIR}/engine/*.cpp) -set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${ENGINE_SOURCE}) -file(GLOB ENGINE_HEADER ${HEADER_ROOT_DIR}/openspace/engine/*.h) -set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${ENGINE_HEADER}) -source_group(Engine FILES ${ENGINE_SOURCE} ${ENGINE_HEADER}) +# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${HEADER_ROOT_DIR}/openspace/version.h) -file(GLOB NETWORK_SOURCE ${SOURCE_ROOT_DIR}/network/*.cpp) -set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${NETWORK_SOURCE}) -file(GLOB NETWORK_HEADER ${HEADER_ROOT_DIR}/openspace/network/*.h) -set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${NETWORK_HEADER}) -source_group(Network FILES ${NETWORK_SOURCE} ${NETWORK_HEADER}) +# file(GLOB CONFIGURATION_SOURCE ${SOURCE_ROOT_DIR}/configuration/*.cpp) +# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${CONFIGURATION_SOURCE}) +# file(GLOB CONFIGURATION_HEADER ${HEADER_ROOT_DIR}/openspace/configuration/*.h) +# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${CONFIGURATION_HEADER}) +# source_group(Configuration FILES ${CONFIGURATION_SOURCE} ${CONFIGURATION_HEADER}) -file(GLOB GUI_SOURCE ${SOURCE_ROOT_DIR}/gui/*.cpp) -set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${GUI_SOURCE}) -file(GLOB GUI_HEADER ${HEADER_ROOT_DIR}/openspace/gui/*.h) -set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${GUI_HEADER}) -source_group(GUI FILES ${GUI_SOURCE} ${GUI_HEADER}) +# file(GLOB ABUFFER_SOURCE ${SOURCE_ROOT_DIR}/abuffer/*.cpp) +# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${ABUFFER_SOURCE}) +# file(GLOB ABUFFER_HEADER ${HEADER_ROOT_DIR}/openspace/abuffer/*.h) +# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${ABUFFER_HEADER}) +# source_group(ABuffer FILES ${ABUFFER_SOURCE} ${ABUFFER_HEADER}) -file(GLOB INTERACTION_SOURCE ${SOURCE_ROOT_DIR}/interaction/*.cpp) -set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${INTERACTION_SOURCE}) -file(GLOB INTERACTION_HEADER ${HEADER_ROOT_DIR}/openspace/interaction/*.h) -set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${INTERACTION_HEADER}) -source_group(Interaction FILES ${INTERACTION_SOURCE} ${INTERACTION_HEADER}) +# file(GLOB ENGINE_SOURCE ${SOURCE_ROOT_DIR}/engine/*.cpp) +# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${ENGINE_SOURCE}) +# file(GLOB ENGINE_HEADER ${HEADER_ROOT_DIR}/openspace/engine/*.h) +# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${ENGINE_HEADER}) +# source_group(Engine FILES ${ENGINE_SOURCE} ${ENGINE_HEADER}) -file(GLOB INTERACTION_EXTERNALCONTROL_SOURCE ${SOURCE_ROOT_DIR}/interaction/externalcontrol/*.cpp) -set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${INTERACTION_EXTERNALCONTROL_SOURCE}) -file(GLOB INTERACTION_EXTERNALCONTROL_HEADER ${HEADER_ROOT_DIR}/openspace/interaction/externalcontrol/*.h) -set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${INTERACTION_EXTERNALCONTROL_HEADER}) -source_group(Interaction\\ExternalControl FILES ${INTERACTION_EXTERNALCONTROL_SOURCE} ${INTERACTION_EXTERNALCONTROL_HEADER}) +# file(GLOB NETWORK_SOURCE ${SOURCE_ROOT_DIR}/network/*.cpp) +# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${NETWORK_SOURCE}) +# file(GLOB NETWORK_HEADER ${HEADER_ROOT_DIR}/openspace/network/*.h) +# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${NETWORK_HEADER}) +# source_group(Network FILES ${NETWORK_SOURCE} ${NETWORK_HEADER}) -file(GLOB PROPERTY_SOURCE_CPP ${SOURCE_ROOT_DIR}/properties/*.cpp) -file(GLOB PROPERTY_SOURCE_C ${SOURCE_ROOT_DIR}/properties/*.c) -set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${PROPERTY_SOURCE_CPP} ${PROPERTY_SOURCE_C}) -file(GLOB PROPERTY_HEADER ${HEADER_ROOT_DIR}/openspace/properties/*.h ${HEADER_ROOT_DIR}/openspace/properties/*.inl) -set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${PROPERTY_HEADER}) -source_group(Properties FILES ${PROPERTY_SOURCE_CPP} ${PROPERTY_SOURCE_C} ${PROPERTY_HEADER}) +# file(GLOB GUI_SOURCE ${SOURCE_ROOT_DIR}/gui/*.cpp) +# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${GUI_SOURCE}) +# file(GLOB GUI_HEADER ${HEADER_ROOT_DIR}/openspace/gui/*.h) +# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${GUI_HEADER}) +# source_group(GUI FILES ${GUI_SOURCE} ${GUI_HEADER}) -file(GLOB QUERY_SOURCE_CPP ${SOURCE_ROOT_DIR}/query/*.cpp) -file(GLOB QUERY_SOURCE_C ${SOURCE_ROOT_DIR}/query/*.c) -set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${QUERY_SOURCE_CPP} ${QUERY_SOURCE_C}) -file(GLOB QUERY_HEADER ${HEADER_ROOT_DIR}/openspace/query/*.h) -set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${QUERY_HEADER}) -source_group(Query FILES ${QUERY_SOURCE_CPP} ${QUERY_SOURCE_C} ${QUERY_HEADER}) +# file(GLOB INTERACTION_SOURCE ${SOURCE_ROOT_DIR}/interaction/*.cpp) +# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${INTERACTION_SOURCE}) +# file(GLOB INTERACTION_HEADER ${HEADER_ROOT_DIR}/openspace/interaction/*.h) +# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${INTERACTION_HEADER}) +# source_group(Interaction FILES ${INTERACTION_SOURCE} ${INTERACTION_HEADER}) -file(GLOB RENDERING_SOURCE ${SOURCE_ROOT_DIR}/rendering/*.cpp) -set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${RENDERING_SOURCE}) -file(GLOB RENDERING_HEADER ${HEADER_ROOT_DIR}/openspace/rendering/*.h) -set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${RENDERING_HEADER}) -source_group(Rendering FILES ${RENDERING_SOURCE} ${RENDERING_HEADER}) +# file(GLOB INTERACTION_EXTERNALCONTROL_SOURCE ${SOURCE_ROOT_DIR}/interaction/externalcontrol/*.cpp) +# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${INTERACTION_EXTERNALCONTROL_SOURCE}) +# file(GLOB INTERACTION_EXTERNALCONTROL_HEADER ${HEADER_ROOT_DIR}/openspace/interaction/externalcontrol/*.h) +# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${INTERACTION_EXTERNALCONTROL_HEADER}) +# source_group(Interaction\\ExternalControl FILES ${INTERACTION_EXTERNALCONTROL_SOURCE} ${INTERACTION_EXTERNALCONTROL_HEADER}) -file(GLOB RENDERING_PLANETS_SOURCE ${SOURCE_ROOT_DIR}/rendering/planets/*.cpp) -set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${RENDERING_PLANETS_SOURCE}) -file(GLOB RENDERING_PLANETS_HEADER ${HEADER_ROOT_DIR}/openspace/rendering/planets/*.h) -set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${RENDERING_PLANETS_HEADER}) -source_group(Rendering\\Planets FILES ${RENDERING_PLANETS_SOURCE} ${RENDERING_PLANETS_HEADER}) +# file(GLOB PROPERTY_SOURCE_CPP ${SOURCE_ROOT_DIR}/properties/*.cpp) +# file(GLOB PROPERTY_SOURCE_C ${SOURCE_ROOT_DIR}/properties/*.c) +# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${PROPERTY_SOURCE_CPP} ${PROPERTY_SOURCE_C}) +# file(GLOB PROPERTY_HEADER ${HEADER_ROOT_DIR}/openspace/properties/*.h ${HEADER_ROOT_DIR}/openspace/properties/*.inl) +# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${PROPERTY_HEADER}) +# source_group(Properties FILES ${PROPERTY_SOURCE_CPP} ${PROPERTY_SOURCE_C} ${PROPERTY_HEADER}) -file(GLOB RENDERING_STARS_SOURCE ${SOURCE_ROOT_DIR}/rendering/stars/*.cpp) -set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${RENDERING_STARS_SOURCE}) -file(GLOB RENDERING_STARS_HEADER ${HEADER_ROOT_DIR}/openspace/rendering/stars/*.h) -set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${RENDERING_STARS_HEADER}) -source_group(Rendering\\Stars FILES ${RENDERING_STARS_SOURCE} ${RENDERING_STARS_HEADER}) +# file(GLOB QUERY_SOURCE_CPP ${SOURCE_ROOT_DIR}/query/*.cpp) +# file(GLOB QUERY_SOURCE_C ${SOURCE_ROOT_DIR}/query/*.c) +# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${QUERY_SOURCE_CPP} ${QUERY_SOURCE_C}) +# file(GLOB QUERY_HEADER ${HEADER_ROOT_DIR}/openspace/query/*.h) +# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${QUERY_HEADER}) +# source_group(Query FILES ${QUERY_SOURCE_CPP} ${QUERY_SOURCE_C} ${QUERY_HEADER}) -file(GLOB RENDERING_MODEL_SOURCE ${SOURCE_ROOT_DIR}/rendering/model/*.cpp) -set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${RENDERING_MODEL_SOURCE}) -file(GLOB RENDERING_MODEL_HEADER ${HEADER_ROOT_DIR}/openspace/rendering/model/*.h) -set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${RENDERING_MODEL_HEADER}) -source_group(Rendering\\Model FILES ${RENDERING_MODEL_SOURCE} ${RENDERING_MODEL_HEADER}) +# file(GLOB RENDERING_SOURCE ${SOURCE_ROOT_DIR}/rendering/*.cpp) +# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${RENDERING_SOURCE}) +# file(GLOB RENDERING_HEADER ${HEADER_ROOT_DIR}/openspace/rendering/*.h) +# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${RENDERING_HEADER}) +# source_group(Rendering FILES ${RENDERING_SOURCE} ${RENDERING_HEADER}) -file(GLOB SCENE_SOURCE ${SOURCE_ROOT_DIR}/scene/*.cpp) -set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${SCENE_SOURCE}) -file(GLOB SCENE_HEADER ${HEADER_ROOT_DIR}/openspace/scene/*.h ${HEADER_ROOT_DIR}/openspace/scene/*.inl) -set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${SCENE_HEADER}) -source_group(Scene FILES ${SCENE_SOURCE} ${SCENE_HEADER}) +# file(GLOB RENDERING_PLANETS_SOURCE ${SOURCE_ROOT_DIR}/rendering/planets/*.cpp) +# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${RENDERING_PLANETS_SOURCE}) +# file(GLOB RENDERING_PLANETS_HEADER ${HEADER_ROOT_DIR}/openspace/rendering/planets/*.h) +# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${RENDERING_PLANETS_HEADER}) +# source_group(Rendering\\Planets FILES ${RENDERING_PLANETS_SOURCE} ${RENDERING_PLANETS_HEADER}) -file(GLOB SCRIPTING_SOURCE ${SOURCE_ROOT_DIR}/scripting/*.cpp) -set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${SCRIPTING_SOURCE}) -file(GLOB SCRIPTING_HEADER ${HEADER_ROOT_DIR}/openspace/scripting/*.h) -set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${SCRIPTING_HEADER}) -source_group(Scripting FILES ${SCRIPTING_SOURCE} ${SCRIPTING_HEADER}) +# file(GLOB RENDERING_STARS_SOURCE ${SOURCE_ROOT_DIR}/rendering/stars/*.cpp) +# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${RENDERING_STARS_SOURCE}) +# file(GLOB RENDERING_STARS_HEADER ${HEADER_ROOT_DIR}/openspace/rendering/stars/*.h) +# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${RENDERING_STARS_HEADER}) +# source_group(Rendering\\Stars FILES ${RENDERING_STARS_SOURCE} ${RENDERING_STARS_HEADER}) -file(GLOB UTIL_SOURCE ${SOURCE_ROOT_DIR}/util/*.cpp) -set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${UTIL_SOURCE}) -file(GLOB UTIL_HEADER ${HEADER_ROOT_DIR}/openspace/util/*.h) -set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${UTIL_HEADER}) -source_group(Util FILES ${UTIL_SOURCE} ${UTIL_HEADER}) +# file(GLOB RENDERING_MODEL_SOURCE ${SOURCE_ROOT_DIR}/rendering/model/*.cpp) +# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${RENDERING_MODEL_SOURCE}) +# file(GLOB RENDERING_MODEL_HEADER ${HEADER_ROOT_DIR}/openspace/rendering/model/*.h) +# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${RENDERING_MODEL_HEADER}) +# source_group(Rendering\\Model FILES ${RENDERING_MODEL_SOURCE} ${RENDERING_MODEL_HEADER}) -file(GLOB FLARE_SOURCE ${SOURCE_ROOT_DIR}/flare/*.cpp) -set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${FLARE_SOURCE}) -file(GLOB FLARE_HEADER ${HEADER_ROOT_DIR}/openspace/flare/*.h) -set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${FLARE_HEADER}) -source_group(Flare FILES ${FLARE_SOURCE} ${FLARE_HEADER}) +# file(GLOB SCENE_SOURCE ${SOURCE_ROOT_DIR}/scene/*.cpp) +# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${SCENE_SOURCE}) +# file(GLOB SCENE_HEADER ${HEADER_ROOT_DIR}/openspace/scene/*.h ${HEADER_ROOT_DIR}/openspace/scene/*.inl) +# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${SCENE_HEADER}) +# source_group(Scene FILES ${SCENE_SOURCE} ${SCENE_HEADER}) -include_directories(${HEADER_ROOT_DIR}) -include_directories(${GHOUL_ROOT_DIR}/ext/boost) +# file(GLOB SCRIPTING_SOURCE ${SOURCE_ROOT_DIR}/scripting/*.cpp) +# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${SCRIPTING_SOURCE}) +# file(GLOB SCRIPTING_HEADER ${HEADER_ROOT_DIR}/openspace/scripting/*.h) +# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${SCRIPTING_HEADER}) +# source_group(Scripting FILES ${SCRIPTING_SOURCE} ${SCRIPTING_HEADER}) -include_directories(${CMAKE_SOURCE_DIR}/ext/imgui) -set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${CMAKE_SOURCE_DIR}/ext/imgui/imgui.h) -set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${CMAKE_SOURCE_DIR}/ext/imgui/imgui.cpp) -source_group(ext\\imgui FILES ${CMAKE_SOURCE_DIR}/ext/imgui/imgui.h ${CMAKE_SOURCE_DIR}/ext/imgui/imgui.cpp) +# file(GLOB UTIL_SOURCE ${SOURCE_ROOT_DIR}/util/*.cpp) +# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${UTIL_SOURCE}) +# file(GLOB UTIL_HEADER ${HEADER_ROOT_DIR}/openspace/util/*.h) +# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${UTIL_HEADER}) +# source_group(Util FILES ${UTIL_SOURCE} ${UTIL_HEADER}) -if (APPLE) - add_definitions(-D__APPLE__) - set(CMAKE_CXX_FLAGS "-std=c++11 -stdlib=libc++ ${CMAKE_CXX_FLAGS}") -endif (APPLE) -if (UNIX AND NOT APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -ggdb") -endif (UNIX AND NOT APPLE) +# file(GLOB FLARE_SOURCE ${SOURCE_ROOT_DIR}/flare/*.cpp) +# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${FLARE_SOURCE}) +# file(GLOB FLARE_HEADER ${HEADER_ROOT_DIR}/openspace/flare/*.h) +# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${FLARE_HEADER}) +# source_group(Flare FILES ${FLARE_SOURCE} ${FLARE_HEADER}) + +# include_directories(${HEADER_ROOT_DIR}) +# include_directories(${GHOUL_ROOT_DIR}/ext/boost) + +# include_directories(${CMAKE_SOURCE_DIR}/ext/imgui) +# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${CMAKE_SOURCE_DIR}/ext/imgui/imgui.h) +# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${CMAKE_SOURCE_DIR}/ext/imgui/imgui.cpp) +# source_group(ext\\imgui FILES ${CMAKE_SOURCE_DIR}/ext/imgui/imgui.h ${CMAKE_SOURCE_DIR}/ext/imgui/imgui.cpp) -set(DEPENDENT_LIBS ${DEPENDENT_LIBS} openspace-module-base openspace-module-newhorizons openspace-module-volume) +# set(DEPENDENT_LIBS ${DEPENDENT_LIBS} openspace-module-base openspace-module-newhorizons openspace-module-volume) -include_directories("${HEADER_ROOT_DIR}") -include_directories("${OPENSPACE_BASE_DIR}") +# include_directories("${HEADER_ROOT_DIR}") +# include_directories("${OPENSPACE_BASE_DIR}") -add_executable(OpenSpace ${SOURCE_ROOT_DIR}/main.cpp ${OPENSPACE_HEADER} ${OPENSPACE_SOURCE}) -target_link_libraries(OpenSpace ${DEPENDENT_LIBS}) +# add_executable(OpenSpace ${SOURCE_ROOT_DIR}/main.cpp ${OPENSPACE_HEADER} ${OPENSPACE_SOURCE}) +# target_link_libraries(OpenSpace ${DEPENDENT_LIBS}) -option(OPENSPACE_HAVE_TESTS "Activate the OpenSpace unit tests" ON) -if (OPENSPACE_HAVE_TESTS) - add_definitions(-DOPENSPACE_HAVE_TESTS) +# option(OPENSPACE_HAVE_TESTS "Activate the OpenSpace unit tests" ON) +# if (OPENSPACE_HAVE_TESTS) +# add_definitions(-DOPENSPACE_HAVE_TESTS) - set(OPENSPACE_TEST_DIR ${OPENSPACE_BASE_DIR}/tests) +# set(OPENSPACE_TEST_DIR ${OPENSPACE_BASE_DIR}/tests) - include_directories("${GHOUL_ROOT_DIR}/ext/gtest/include") - include_directories("${GHOUL_ROOT_DIR}/include") - include_directories("${OPENSPACE_TEST_DIR}") - file(GLOB_RECURSE OPENSPACE_TEST_FILES ${OPENSPACE_BASE_DIR}/tests/*.inl) +# include_directories("${GHOUL_ROOT_DIR}/ext/gtest/include") +# include_directories("${GHOUL_ROOT_DIR}/include") +# include_directories("${OPENSPACE_TEST_DIR}") +# file(GLOB_RECURSE OPENSPACE_TEST_FILES ${OPENSPACE_BASE_DIR}/tests/*.inl) - source_group(Tests FILES ${OPENSPACE_TEST_FILES}) +# source_group(Tests FILES ${OPENSPACE_TEST_FILES}) - add_executable(OpenSpaceTest ${OPENSPACE_BASE_DIR}/tests/main.cpp ${OPENSPACE_TEST_FILES} ${OPENSPACE_HEADER} ${OPENSPACE_SOURCE}) - target_link_libraries(OpenSpaceTest gtest ${DEPENDENT_LIBS}) +# add_executable(OpenSpaceTest ${OPENSPACE_BASE_DIR}/tests/main.cpp ${OPENSPACE_TEST_FILES} ${OPENSPACE_HEADER} ${OPENSPACE_SOURCE}) +# target_link_libraries(OpenSpaceTest gtest ${DEPENDENT_LIBS}) -endif (OPENSPACE_HAVE_TESTS) +# endif (OPENSPACE_HAVE_TESTS) -#if (NOT UNIX) - #cotire(OpenSpace) -#endif () -GhoulCopySharedLibraries(OpenSpace) +# #if (NOT UNIX) +# #cotire(OpenSpace) +# #endif () +# GhoulCopySharedLibraries(OpenSpace) diff --git a/src/interaction/externalcontrol/joystickexternalcontrol.cpp.orig b/src/interaction/externalcontrol/joystickexternalcontrol.cpp.orig deleted file mode 100644 index 390295e748..0000000000 --- a/src/interaction/externalcontrol/joystickexternalcontrol.cpp.orig +++ /dev/null @@ -1,63 +0,0 @@ -#include "externalcontrol/joystickexternalcontrol.h" -#include "deviceidentifier.h" - -// workaround since Python debug does not work on windows -#ifdef _DEBUG -#undef _DEBUG -#include -#define _DEBUG -#else -#include -#endif - -namespace openspace { - - -JoystickExternalControl::JoystickExternalControl(const char *filename): PythonExternalControl(filename) { -} - -void JoystickExternalControl::setInputDevice(const int device) { - if(device >= 0 && device <= 16) { - inputDevice_ = device; - numberOfButtons_ = DeviceIdentifier::ref().getButtons(inputDevice_); - numberOfAxes_ = DeviceIdentifier::ref().getAxes(inputDevice_); - clear(); - pyarrSize_ = numberOfButtons_ + numberOfAxes_; - pyarr_ = new PyObject*[pyarrSize_]; - } - -} - -void JoystickExternalControl::update() { - - if(inputDevice_ != -1) { - float *axesPos; - unsigned char *buttons; - DeviceIdentifier::ref().getButtons(inputDevice_, &buttons); - DeviceIdentifier::ref().getAxes(inputDevice_, &axesPos); - - // init array - for(int i = 0; i < numberOfButtons_; ++i){ - pyarr_[i] = PyLong_FromLong(buttons[i]); - } - for(int i = 0; i < numberOfAxes_; ++i){ - pyarr_[i+numberOfButtons_] = PyFloat_FromDouble(axesPos[i]); - } - } - - run(); - - if(inputDevice_ != -1) { - // cleanup - //for(int i = 0; i < pyarrSize_; ++i) { - // Py_XDECREF(pyarr_[i]); - //} - } - -} - -JoystickExternalControl::~JoystickExternalControl() { -} - -} // namespace openspace - diff --git a/src/interaction/externalcontrol/keyboardexternalcontrol.cpp.orig b/src/interaction/externalcontrol/keyboardexternalcontrol.cpp.orig deleted file mode 100644 index 0b90506247..0000000000 --- a/src/interaction/externalcontrol/keyboardexternalcontrol.cpp.orig +++ /dev/null @@ -1,62 +0,0 @@ -#include "externalcontrol/keyboardexternalcontrol.h" - -// workaround since Python debug does not work on windows -#ifdef _DEBUG -#undef _DEBUG -#include -#define _DEBUG -#else -#include -#endif - - -namespace openspace { - - -KeyboardExternalControl::KeyboardExternalControl(const char *filename): PythonExternalControl(filename) { - clear(); - pyarrSize_ = 'Z' - 'A' + 80; // all letters, 69 special keys, space and 10 numbers - pyarr_ = new PyObject*[pyarrSize_]; - for(int i = 0; i < pyarrSize_; ++i) { - pyarr_[i] = PyLong_FromLong(0); - } -} - -void KeyboardExternalControl::keyboardCallback(int key, int action) { - - //printf("key: %i\n",key); - int pos = -1; - if(key >= '0' && key <= '9') { - pos = key - '0'; - //Py_XDECREF(pyarr_[pos]); - pyarr_[pos] = PyLong_FromLong(action); - } else if(key >= 'A' && key <= 'Z') { - pos = key - 'A' + 10; - //Py_XDECREF(pyarr_[pos]); - pyarr_[pos] = PyLong_FromLong(action); - } else if (key > 256 && key < 256+69) { - pos = key - 256 + 'Z'-'A' +10; - //Py_XDECREF(pyarr_[pos]); - pyarr_[pos] = PyLong_FromLong(action); - } else if (key == 32) { - pos = 'Z' - 'A' + 11; - //Py_XDECREF(pyarr_[pos]); - pyarr_[pos] = PyLong_FromLong(action); - } - //printf("pos: %i\n",pos); -} - - - -void KeyboardExternalControl::update() { - run(); -} - -KeyboardExternalControl::~KeyboardExternalControl() { - //for(int i = 0; i < pyarrSize_; ++i) { - // Py_XDECREF(pyarr_[i]); - //} -} - -} // namespace openspace - diff --git a/src/interaction/externalcontrol/mouseexternalcontrol.cpp.orig b/src/interaction/externalcontrol/mouseexternalcontrol.cpp.orig deleted file mode 100644 index b365a02c03..0000000000 --- a/src/interaction/externalcontrol/mouseexternalcontrol.cpp.orig +++ /dev/null @@ -1,79 +0,0 @@ -#include "externalcontrol/mouseexternalcontrol.h" - -// workaround since Python debug does not work on windows -#ifdef _DEBUG -#undef _DEBUG -#include -#define _DEBUG -#else -#include -#endif - - -namespace openspace { - - -MouseExternalControl::MouseExternalControl(const char *filename): PythonExternalControl(filename) { - clear(); - pyarrSize_ = 6*2; - pyarr_ = new PyObject*[pyarrSize_]; - x_ = 0; - y_ = 0; - pos_ = 0; - button1_ = 0; - button2_ = 0; - button3_ = 0; - for(int i = 0; i < pyarrSize_; ++i) { - pyarr_[i] = PyLong_FromLong(0);; - } - -} - -void MouseExternalControl::mouseButtonCallback(int key, int action) { - if(key == 0) - button1_ = action; - if(key == 1) - button2_ = action; - if(key == 2) - button3_ = action; -} - -void MouseExternalControl::mousePosCallback(int x, int y) { - x_ = x; - y_ = y; -} - -void MouseExternalControl::mouseScrollCallback(int pos) { - pos_ = pos; -} - -void MouseExternalControl::update() { - - pyarr_[6] = pyarr_[0]; - pyarr_[7] = pyarr_[1]; - pyarr_[8] = pyarr_[2]; - pyarr_[9] = pyarr_[3]; - pyarr_[10] = pyarr_[4]; - pyarr_[11] = pyarr_[5]; - pyarr_[0] = PyLong_FromLong(button1_); - pyarr_[1] = PyLong_FromLong(button2_); - pyarr_[2] = PyLong_FromLong(button3_); - pyarr_[3] = PyLong_FromLong(pos_); - pyarr_[4] = PyLong_FromLong(x_); - pyarr_[5] = PyLong_FromLong(y_); - - run(); - - // cleanup - //for(int i = pyarrSize_ / 2; i < pyarrSize_; ++i) { - // Py_XDECREF(pyarr_[i]); - //} - -} - -MouseExternalControl::~MouseExternalControl() { - -} - -} // namespace openspace - From fc4a6b68fb5d2490071077150e788378105ea9b3 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 22 May 2015 11:33:45 +0200 Subject: [PATCH 034/329] Remove static version.h and replace by autogenerated file --- .gitignore | 1 + CMakeLists.txt | 13 ++++++++++--- ext/ghoul | 2 +- include/openspace/version.h | 28 ---------------------------- support/cmake/version.template | 11 +++++++++++ 5 files changed, 23 insertions(+), 32 deletions(-) delete mode 100644 include/openspace/version.h create mode 100644 support/cmake/version.template diff --git a/.gitignore b/.gitignore index 51a59b30d1..f5d8d66e0f 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ gui/externaltimecontrol/CMakeLists.txt gui/externaltimecontrol/main.cpp gui/externaltimecontrol/mainwindow.cpp gui/externaltimecontrol/mainwindow.h +include/openspace/version.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c1c761a5d2..83a002fc36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,6 @@ message(STATUS "Generating OpenSpace project") ######################################################################################### # General Settings ######################################################################################### - set(OPENSPACE_BASE_DIR "${PROJECT_SOURCE_DIR}") set(OPENSPACE_EXT_DIR "${OPENSPACE_BASE_DIR}/ext") set(OPENSPACE_CMAKE_EXT_DIR "${OPENSPACE_BASE_DIR}/support/cmake") @@ -47,6 +46,16 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OPENSPACE_LIBRARY_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OPENSPACE_LIBRARY_DIR}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OPENSPACE_BINARY_DIR}) +# Set current OpenSpace Version +set(OPENSPACE_MAJOR_VERSION 0) +set(OPENSPACE_MINOR_VERSION 1) +set(OPENSPACE_PATCH_VERSION 0) +mark_as_advanced(OPENSPACE_MAJOR_VERSION,O configurPENSPACE_MINOR_VERSION, OPENSPACE_PATCH_VERSION) +set(OPENSPACE_VERSION ${OPENSPACE_MAJOR_VERSION}.${OPENSPACE_MINOR_VERSION}.${OPENSPACE_PATCH_VERSION}) +set(OPENSPACE_VERSION_STRING "prerelease-5") +message(STATUS "Version: ${OPENSPACE_VERSION} (${OPENSPACE_VERSION_STRING})") +configure_file(${OPENSPACE_CMAKE_EXT_DIR}/version.template ${OPENSPACE_BASE_DIR}/include/openspace/version.h) + ############################# # Including the source files (with source groups already defined) ############################# @@ -65,7 +74,6 @@ target_link_libraries(OpenSpace libOpenSpace) ############################# # Compile settings ############################# - if (MSVC) target_compile_options(libOpenSpace PUBLIC "/MP" "/wd4201" "/wd4127") set_target_properties(OpenSpace PROPERTIES LINK_FLAGS "/NODEFAULTLIB:\"LIBCMTD.lib;LIBCMT.lib\"") @@ -92,7 +100,6 @@ endif () ############################# # Dependencies ############################# - # Ghoul add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul) target_link_libraries(libOpenSpace Ghoul) diff --git a/ext/ghoul b/ext/ghoul index 63b25b5513..3eb1598c3c 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 63b25b5513c6b536f87a27052325c6a01356a428 +Subproject commit 3eb1598c3c29ecae25541be863f8fa096b39c0bd diff --git a/include/openspace/version.h b/include/openspace/version.h deleted file mode 100644 index 7577c7b993..0000000000 --- a/include/openspace/version.h +++ /dev/null @@ -1,28 +0,0 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2015 * - * * - * 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. * - ****************************************************************************************/ - -#define OPENSPACE_VERSION_MAJOR 0 -#define OPENSPACE_VERSION_MINOR 1 -#define OPENSPACE_VERSION_REVISION 0 -#define OPENSPACE_VERSION_STRING "prerelease-5" diff --git a/support/cmake/version.template b/support/cmake/version.template new file mode 100644 index 0000000000..ca81b3da2d --- /dev/null +++ b/support/cmake/version.template @@ -0,0 +1,11 @@ +/* This is a file that was automatically generated by CMake */ + +#ifndef __VERSION_H__ +#define __VERSION_H__ + +#define OPENSPACE_VERSION_MAJOR @OPENSPACE_MAJOR_VERSION@ +#define OPENSPACE_VERSION_MINOR @OPENSPACE_MINOR_VERSION@ +#define OPENSPACE_VERSION_PATCH @OPENSPACE_PATCH_VERSION@ +#define OPENSPACE_VERSION_STRING "@OPENSPACE_VERSION_STRING@" + +#endif // __VERSION_H__ From d2dec6c9eb0c6b775e802c27ba61d2a458c3d847 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 22 May 2015 12:26:54 +0200 Subject: [PATCH 035/329] More work on cleaning up CMakeLists file Added visual leak detector libraries --- CMakeLists.txt | 95 +++++++-- ext/ghoul | 2 +- ext/vld/lib/vld.lib | Bin 0 -> 6348 bytes ext/vld/vld.h | 343 ++++++++++++++++++++++++++++++ ext/vld/vld_def.h | 46 ++++ src/CMakeLists.txt | 1 - support/cmake/module_macros.cmake | 0 7 files changed, 469 insertions(+), 18 deletions(-) create mode 100644 ext/vld/lib/vld.lib create mode 100644 ext/vld/vld.h create mode 100644 ext/vld/vld_def.h create mode 100644 support/cmake/module_macros.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 83a002fc36..314507136c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,29 +32,33 @@ mark_as_advanced(CMAKE_INSTALL_PREFIX) project (OpenSpace) message(STATUS "Generating OpenSpace project") -######################################################################################### +############################# # General Settings -######################################################################################### +############################# set(OPENSPACE_BASE_DIR "${PROJECT_SOURCE_DIR}") set(OPENSPACE_EXT_DIR "${OPENSPACE_BASE_DIR}/ext") set(OPENSPACE_CMAKE_EXT_DIR "${OPENSPACE_BASE_DIR}/support/cmake") -set(OPENSPACE_BINARY_DIR ${OPENSPACE_BASE_DIR}/bin/openspace) -set(OPENSPACE_LIBRARY_DIR ${OPENSPACE_BASE_DIR}/bin/lib) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${OPENSPACE_CMAKE_EXT_DIR}) -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OPENSPACE_LIBRARY_DIR}) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OPENSPACE_LIBRARY_DIR}) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OPENSPACE_BINARY_DIR}) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OPENSPACE_BASE_DIR}/bin/lib) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OPENSPACE_BASE_DIR}/bin/lib) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OPENSPACE_BASE_DIR}/bin/openspace) +set_property(GLOBAL PROPERTY USE_FOLDERS On) +set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER CMake) + +include (${OPENSPACE_CMAKE_EXT_DIR}/module_macros.cmake) + +############################# # Set current OpenSpace Version +############################# set(OPENSPACE_MAJOR_VERSION 0) set(OPENSPACE_MINOR_VERSION 1) set(OPENSPACE_PATCH_VERSION 0) -mark_as_advanced(OPENSPACE_MAJOR_VERSION,O configurPENSPACE_MINOR_VERSION, OPENSPACE_PATCH_VERSION) -set(OPENSPACE_VERSION ${OPENSPACE_MAJOR_VERSION}.${OPENSPACE_MINOR_VERSION}.${OPENSPACE_PATCH_VERSION}) set(OPENSPACE_VERSION_STRING "prerelease-5") -message(STATUS "Version: ${OPENSPACE_VERSION} (${OPENSPACE_VERSION_STRING})") -configure_file(${OPENSPACE_CMAKE_EXT_DIR}/version.template ${OPENSPACE_BASE_DIR}/include/openspace/version.h) +message(STATUS "Version: ${OPENSPACE_MAJOR_VERSION}.${OPENSPACE_MINOR_VERSION}.${OPENSPACE_PATCH_VERSION} (${OPENSPACE_VERSION_STRING})") +mark_as_advanced(OPENSPACE_MAJOR_VERSION, OPENSPACE_MINOR_VERSION, OPENSPACE_PATCH_VERSION, OPENSPACE_VERSION_STRING) +configure_file(${OPENSPACE_CMAKE_EXT_DIR}/version.template ${CMAKE_BINARY_DIR}/_generated/include/openspace/version.h) ############################# # Including the source files (with source groups already defined) @@ -66,20 +70,49 @@ include(src/CMakeLists.txt) ############################# add_library(libOpenSpace STATIC ${OPENSPACE_HEADER} ${OPENSPACE_SOURCE}) target_include_directories(libOpenSpace PUBLIC ${OPENSPACE_BASE_DIR}/include) +target_include_directories(libOpenSpace PUBLIC ${CMAKE_BINARY_DIR}/_generated/include) add_executable(OpenSpace ${OPENSPACE_MAIN}) target_include_directories(OpenSpace PUBLIC ${OPENSPACE_BASE_DIR}/include) target_link_libraries(OpenSpace libOpenSpace) +############################# +# General Options +############################# +if (MSVC) + option(OPENSPACE_ENABLE_VLD "Enable the Visual Leak Detector" OFF) + if (OPENSPACE_ENABLE_VLD) + target_link_libraries(libOpenSpace ${OPENSPACE_EXT_DIR}/vld/lib/vld.lib) + target_include_directories(libOpenSpace PUBLIC ${OPENSPACE_EXT_DIR}/vld) + endif () +endif () + ############################# # Compile settings ############################# if (MSVC) + if (MSVC_VERSION LESS 1800) + message(FATAL_ERROR "OpenSpace requires at least Visual Studio 2013") + endif () + target_compile_options(libOpenSpace PUBLIC "/MP" "/wd4201" "/wd4127") set_target_properties(OpenSpace PROPERTIES LINK_FLAGS "/NODEFAULTLIB:\"LIBCMTD.lib;LIBCMT.lib\"") elseif (APPLE) target_compile_definitions(libOpenSpace PUBLIC "__APPLE__") - target_compile_options(libOpenSpace PUBLIC "-std=c++11" "-stdlib=libc++") + + include (CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) + CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) + mark_as_advanced(COMPILER_SUPPORTS_CXX11, COMPILER_SUPPORTS_CXX0X) + if (COMPILER_SUPPORTS_CXX11) + target_compile_options(libOpenSpace PUBLIC "-std=c++11") + elseif (COMPILER_SUPPORTS_CXX0X) + target_compile_options(libOpenSpace PUBLIC "-std=c++0x") + else () + message(FATAL_ERROR "Compiler does not have C++11 support") + endif () + + target_compile_options(libOpenSpace PUBLIC"-stdlib=libc++") target_include_directories(libOpenSpace "/Developer/Headers/FlatCarbon") find_library(COREFOUNDATION_LIBRARY CoreFoundation) @@ -94,7 +127,19 @@ elseif (APPLE) ${APP_SERVICES_LIBRARY} ) elseif (UNIX) - target_compile_definitions(libOpenSpace PUBLIC "-std=c++0x -ggdb") + include (CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) + CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) + mark_as_advanced(COMPILER_SUPPORTS_CXX11, COMPILER_SUPPORTS_CXX0X) + if (COMPILER_SUPPORTS_CXX11) + target_compile_options(libOpenSpace PUBLIC "-std=c++11") + elseif (COMPILER_SUPPORTS_CXX0X) + target_compile_options(libOpenSpace PUBLIC "-std=c++0x") + else () + message(FATAL_ERROR "Compiler does not have C++11 support") + endif () + + target_compile_definitions(libOpenSpace PUBLIC "-ggdb") endif () ############################# @@ -147,8 +192,8 @@ target_include_directories(libOpenSpace PUBLIC ${IMGUI_INCLUDE_DIR}) ############################# # Other applications ############################# -option(BUILD_GUI_APPLICATIONS "Build GUI Applications" OFF) -if (BUILD_GUI_APPLICATIONS) +option(OPENSPACE_BUILD_GUI_APPLICATIONS "Build GUI Applications" OFF) +if (OPENSPACE_BUILD_GUI_APPLICATIONS) add_subdirectory(gui) endif () @@ -179,10 +224,28 @@ endif (OPENSPACE_HAVE_TESTS) add_subdirectory(modules/base) add_subdirectory(modules/newhorizons) add_subdirectory(modules/volume) - target_link_libraries(libOpenSpace openspace-module-base openspace-module-newhorizons openspace-module-volume) +############################# +# Project Folder structure (where appropriate) +############################# +macro (os_folder target_name folder_name) + if (TARGET ${target_name}) + set_property(TARGET ${target_name} PROPERTY FOLDER ${folder_name}) + endif () +endmacro () + +os_folder(ccmc "External") +os_folder(cdf "External") +os_folder(gtest "External" "External") +os_folder(Imgui "External") +os_folder(Lua "External") +os_folder(lz4 "External") +os_folder(tinyobjloader "External") + +os_folder(GhoulTest "Unit Tests") +os_folder(OpenSpaceTest "Unit Tests") # set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${OPENSPACE_EXT_DIR}/ghoul/ext) diff --git a/ext/ghoul b/ext/ghoul index 3eb1598c3c..63a61eea62 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 3eb1598c3c29ecae25541be863f8fa096b39c0bd +Subproject commit 63a61eea62550e0b14bb031ea47c71cd8a5f9921 diff --git a/ext/vld/lib/vld.lib b/ext/vld/lib/vld.lib new file mode 100644 index 0000000000000000000000000000000000000000..6325a37c8258c8c56191b60cf7bd547fc3dcc210 GIT binary patch literal 6348 zcmcIo%}*Og6o26(B<9PQU}&I~qedx_DvH5&K|<}i5E>K%ks+L-m<4ZQi-kpAgL;V6 zOO#WUQ;yBC*Bo+dPN>I7m3nDzJ@g-la?T}vvoo{%cD%c>vAfdq?7n$3zj?F2_x6pi zE$OxW%6GAEQvNGFznEJ{FQn5MzkXIcrsw8F&u$C=W&vVT00XN4gO34*-U1|-xJ=v! zU@}tSGM)r*{RU&-7FaT!HQ8g_Ul$n)AF!WSScE| zWu6+#x@p>VegD7~Lj$`|xn4l26z?+c#mo9RHU<4*!KZ-Z^i%~pgbQ1 zxPkH#=bA=+5y#9T)>RmU&tM2fp$`V29}+MQNf?1R48s`a#a@_zOAv$0a1ky*7xc6= zqMPL=xh>wU2rO@R-8-)b8R@4{W3oYw3}l=Dp8{i@5RWsJ**f$g3safH!YnvaA_K57 zr1_Fu-i8ofP2RXfLcze~NjwkAnH)$Oo$IUA+{dSZ{i;IEqQL>BTKQkUJEuYds`q?l z-^@TlQ1b>_c%43SC~9@}Aw+RUh110~QAk7d>;oN`2g%~Sv=&_h_!FE$DHjv<4g2V) z7d7>hEVPOd)y+)VG@aD=shTjeI4`Qh-B|f)H~evy-|D{rn1}c^Q#STa9$nuOkC4Zk zU*v;mHHiN(Q!VRt{ra5jVyW>@lz)Yy8(R%#&xBo0s4))$d_<9UOs_%etP9%&WiRYGxu`)&QL4R;0#pj^hZM470&O;M!z84b!LCkGR5CtOeym&_m?nbUMd+=ev=QP zWlZ^B7|_gn{Tvq&dVFYk*A_&%k@Di}9|YhpIe6D1;wvjRip3|3FM4U9$@t1hJ+u2; zK8Tjtb$oZ5-Pw+1uAFYlvE*K5VSWJKpn)oDB7;@j--Yo>9Od(1S zMG0`JWvqz(>*ZMf;)ei0%Wum~kP0ilK+i6^XxuG+x~FjDzPWvriA6zUrb9F}O_>4Hpu4$mSYF;20_uF`PtEaHenRFBkc`55(WO}L285vwhnsBAsT wag>uAg(LUPu_Pi}Uvbeok*%Zh*qDpNv$gZbFLC7@{0ONTQpo + +#define VLD_OPT_AGGREGATE_DUPLICATES 0x0001 // If set, aggregate duplicate leaks in the leak report. +#define VLD_OPT_MODULE_LIST_INCLUDE 0x0002 // If set, modules in the module list are included, all others are excluded. +#define VLD_OPT_REPORT_TO_DEBUGGER 0x0004 // If set, the memory leak report is sent to the debugger. +#define VLD_OPT_REPORT_TO_FILE 0x0008 // If set, the memory leak report is sent to a file. +#define VLD_OPT_SAFE_STACK_WALK 0x0010 // If set, the stack is walked using the "safe" method (StackWalk64). +#define VLD_OPT_SELF_TEST 0x0020 // If set, perform a self-test to verify memory leak self-checking. +#define VLD_OPT_SLOW_DEBUGGER_DUMP 0x0040 // If set, inserts a slight delay between sending output to the debugger. +#define VLD_OPT_START_DISABLED 0x0080 // If set, memory leak detection will initially disabled. +#define VLD_OPT_TRACE_INTERNAL_FRAMES 0x0100 // If set, include useless frames (e.g. internal to VLD) in call stacks. +#define VLD_OPT_UNICODE_REPORT 0x0200 // If set, the leak report will be encoded UTF-16 instead of ASCII. +#define VLD_OPT_VLDOFF 0x0400 // If set, VLD will be completely deactivated. It will not attach to any modules. +#define VLD_OPT_REPORT_TO_STDOUT 0x0800 // If set, the memory leak report is sent to stdout. +#define VLD_OPT_SKIP_HEAPFREE_LEAKS 0x1000 // If set, VLD skip HeapFree memory leaks. +#define VLD_OPT_VALIDATE_HEAPFREE 0x2000 // If set, VLD verifies and reports heap consistency for HeapFree calls. + +#define VLD_RPTHOOK_INSTALL 0 +#define VLD_RPTHOOK_REMOVE 1 + +typedef int (__cdecl * VLD_REPORT_HOOK)(int reportType, wchar_t *message, int *returnValue); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 39a4eaa827..be7cfc2c38 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -168,7 +168,6 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/util/syncbuffer.h ${OPENSPACE_BASE_DIR}/include/openspace/util/time.h ${OPENSPACE_BASE_DIR}/include/openspace/util/updatestructures.h - ${OPENSPACE_BASE_DIR}/include/openspace/version.h ) # Place files into source groups diff --git a/support/cmake/module_macros.cmake b/support/cmake/module_macros.cmake new file mode 100644 index 0000000000..e69de29bb2 From 98b06af6d1c30305e109e3cdce49aa180103f2a1 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Fri, 22 May 2015 14:24:00 +0200 Subject: [PATCH 036/329] Linker error fixes Adapted OpenSpace Version to different naming for defines --- CMakeLists.txt | 4 +++- src/engine/openspaceengine.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 314507136c..cfdb2dd075 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,7 +96,9 @@ if (MSVC) endif () target_compile_options(libOpenSpace PUBLIC "/MP" "/wd4201" "/wd4127") - set_target_properties(OpenSpace PROPERTIES LINK_FLAGS "/NODEFAULTLIB:\"LIBCMTD.lib;LIBCMT.lib\"") + set_target_properties(OpenSpace PROPERTIES LINK_FLAGS + "/NODEFAULTLIB:LIBCMTD.lib /NODEFAULTLIB:LIBCMT.lib" + ) elseif (APPLE) target_compile_definitions(libOpenSpace PUBLIC "__APPLE__") diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 686ca9c836..426cd568df 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -210,7 +210,7 @@ bool OpenSpaceEngine::create( LINFOC("OpenSpace Version", OPENSPACE_VERSION_MAJOR << "." << OPENSPACE_VERSION_MINOR << "." << - OPENSPACE_VERSION_REVISION << " (" << OPENSPACE_VERSION_STRING << ")"); + OPENSPACE_VERSION_PATCH << " (" << OPENSPACE_VERSION_STRING << ")"); // Create directories that doesn't exist auto tokens = FileSys.tokens(); From 3b15489c26c40efe01dc8da65bda4c75db486c4a Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 22 May 2015 15:16:33 +0200 Subject: [PATCH 037/329] Fix unit test handling --- CMakeLists.txt | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cfdb2dd075..aae634f516 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -201,23 +201,23 @@ endif () option(OPENSPACE_HAVE_TESTS "Activate the OpenSpace unit tests" ON) if (OPENSPACE_HAVE_TESTS) + if (NOT TARGET gtest) + add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul/ext/gtest) + endif () + file(GLOB_RECURSE OPENSPACE_TEST_FILES ${OPENSPACE_BASE_DIR}/tests/*.inl) add_executable(OpenSpaceTest ${OPENSPACE_BASE_DIR}/tests/main.cpp ${OPENSPACE_TEST_FILES}) - target_include_directories(OpenSpaceTest PUBLIC "${OPENSPACE_BASE_DIR}/include") + target_include_directories(OpenSpaceTest PUBLIC + "${OPENSPACE_BASE_DIR}/include" + "${OPENSPACE_BASE_DIR}/tests" + "${OPENSPACE_EXT_DIR}/ghoul/ext/gtest/include" + ) target_link_libraries(OpenSpaceTest gtest libOpenSpace) - - # set(OPENSPACE_TEST_DIR ${OPENSPACE_BASE_DIR}/tests) - - # include_directories("${GHOUL_ROOT_DIR}/ext/gtest/include") - # include_directories("${GHOUL_ROOT_DIR}/include") - # include_directories("${OPENSPACE_TEST_DIR}") - # file(GLOB_RECURSE OPENSPACE_TEST_FILES ${OPENSPACE_BASE_DIR}/tests/*.inl) - - # source_group(Tests FILES ${OPENSPACE_TEST_FILES}) - - + set_target_properties(OpenSpaceTest PROPERTIES LINK_FLAGS + "/NODEFAULTLIB:LIBCMTD.lib /NODEFAULTLIB:LIBCMT.lib" + ) endif (OPENSPACE_HAVE_TESTS) ############################# From 394964425a0090178b75e5f321f6011726284bd7 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 22 May 2015 19:09:30 +0200 Subject: [PATCH 038/329] Fix automatic copying of DLL files --- CMakeLists.txt | 138 +++++-------------------------------------------- ext/ghoul | 2 +- 2 files changed, 14 insertions(+), 126 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index aae634f516..26340274ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ message(STATUS "Generating OpenSpace project") ############################# set(OPENSPACE_BASE_DIR "${PROJECT_SOURCE_DIR}") set(OPENSPACE_EXT_DIR "${OPENSPACE_BASE_DIR}/ext") +set(OPENSPACE_MODULE_DIR "${OPENSPACE_BASE_DIR}/modules") set(OPENSPACE_CMAKE_EXT_DIR "${OPENSPACE_BASE_DIR}/support/cmake") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${OPENSPACE_CMAKE_EXT_DIR}) @@ -47,8 +48,8 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OPENSPACE_BASE_DIR}/bin/openspace) set_property(GLOBAL PROPERTY USE_FOLDERS On) set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER CMake) -include (${OPENSPACE_CMAKE_EXT_DIR}/module_macros.cmake) - +include(${OPENSPACE_CMAKE_EXT_DIR}/module_macros.cmake) +include(${OPENSPACE_EXT_DIR}/ghoul/ext/CopySharedLibraries.cmake) ############################# # Set current OpenSpace Version ############################# @@ -249,127 +250,14 @@ os_folder(tinyobjloader "External") os_folder(GhoulTest "Unit Tests") os_folder(OpenSpaceTest "Unit Tests") -# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${OPENSPACE_EXT_DIR}/ghoul/ext) +############################# +# Copy dynamic libraries +############################# +if (WIN32) + # Copy DLLs needed by Ghoul into the executable directory + ghl_copy_shared_libraries(OpenSpace ${OPENSPACE_EXT_DIR}/ghoul) -# #set(GHOUL_ROOT_DIR "${OPENSPACE_EXT_DIR}/ghoul") -# include_directories("${GHOUL_ROOT_DIR}/include") -# include_directories("${GHOUL_ROOT_DIR}/ext/tinyobjloader") -# set(BOOST_ROOT "${GHOUL_ROOT_DIR}/ext/boost") -# add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul) -# set(DEPENDENT_LIBS ${DEPENDENT_LIBS} Ghoul) -# if (GHOUL_USE_FREEIMAGE) -# add_definitions(-DGHOUL_USE_FREEIMAGE) -# endif () -# if (GHOUL_USE_DEVIL) -# add_definitions(-DGHOUL_USE_DEVIL) -# endif () - -# # Add ghoul ext -# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${GHOUL_ROOT_DIR}/ext) - -# # Boost -# include_directories(${GHOUL_ROOT_DIR}/ext/boost) - -# # SGCT -# find_package(SGCT REQUIRED) -# include_directories(${SGCT_INCLUDE_DIRECTORIES}) -# set(DEPENDENT_LIBS ${DEPENDENT_LIBS} ${SGCT_LIBRARIES}) -# if (UNIX AND (NOT APPLE)) -# set(DEPENDENT_LIBS ${DEPENDENT_LIBS} Xcursor Xinerama) -# endif () - -# # GLM -# set(GLM_ROOT_DIR "${GHOUL_ROOT_DIR}/ext/glm") -# find_package(GLM REQUIRED) -# add_definitions(-DGLM_SWIZZLE) -# include_directories(${GLM_INCLUDE_DIRS}) - -# # GLEW -# find_package(GLEW REQUIRED) -# include_directories(${GLEW_INCLUDE_DIRECTORIES}) -# set(DEPENDENT_LIBS ${DEPENDENT_LIBS} ${GLEW_LIBRARIES}) - -# # Lua -# set(LUA_ROOT_DIR "${GHOUL_ROOT_DIR}/ext/lua") -# include_directories("${LUA_ROOT_DIR}/src") - -# # Spice -# set(SPICE_ROOT_DIR "${OPENSPACE_EXT_DIR}/spice") -# find_package(Spice REQUIRED) -# include_directories(${SPICE_INCLUDE_DIRS}) -# set(DEPENDENT_LIBS ${DEPENDENT_LIBS} ${SPICE_LIBRARIES}) - -# # Kameleon -# option(KAMELEON_LIBRARY_ONLY "Build with Kameleon as library only" ON) -# if(WIN32) -# option(BUILD_SHARED_LIBS "Build Shared Libraries" OFF) -# else(WIN32) -# option(BUILD_SHARED_LIBS "Build Shared Libraries" ON) -# endif(WIN32) -# option(KAMELEON_USE_HDF5 "Kameleon use HDF5" OFF) -# set(KAMELEON_ROOT_DIR ${OPENSPACE_EXT_DIR}/kameleon) -# set(KAMELEON_INCLUDES ${KAMELEON_ROOT_DIR}/src) -# add_subdirectory(${KAMELEON_ROOT_DIR}) -# include_directories(${KAMELEON_INCLUDES}) -# set(DEPENDENT_LIBS ${DEPENDENT_LIBS} ccmc) - -# if (APPLE) -# include_directories(/Developer/Headers/FlatCarbon) -# find_library(CARBON_LIBRARY Carbon) -# find_library(COCOA_LIBRARY Cocoa) -# find_library(APP_SERVICES_LIBRARY ApplicationServices) -# mark_as_advanced(CARBON_LIBRARY COCOA_LIBRARY APP_SERVICES_LIBRARY) -# set(DEPENDENT_LIBS ${DEPENDENT_LIBS} ${CARBON_LIBRARY} ${COCOA_LIBRARY} ${APP_SERVICES_LIBRARY}) -# endif () - -# ######################################################################################### -# # Executable -# ######################################################################################### - -# add_subdirectory(src) -# add_subdirectory(modules/base) -# add_subdirectory(modules/newhorizons) -# add_subdirectory(modules/volume) - -# option(BUILD_GUI_APPLICATIONS "Build GUI Applications" OFF) -# if (BUILD_GUI_APPLICATIONS) -# add_subdirectory(gui) -# endif () - -# ######################################################################################### -# # File Fetch -# ######################################################################################### -# option(DOWNLOAD_FILES "Download large OpenSpace data on configure" OFF) -# if(DOWNLOAD_FILES) -# function(DownloadFile FILE_PATH FILE_URL) -# set(FILE_MD5 ${ARGV2}) -# if(NOT EXISTS "${FILE_PATH}") -# file(DOWNLOAD ${FILE_URL} ${FILE_PATH} INACTIVITY_TIMEOUT 10 SHOW_PROGRESS) -# endif() -# file(MD5 ${FILE_PATH} MD5_RESULT) -# if( NOT "${FILE_MD5}" STREQUAL "" ) -# string(COMPARE EQUAL ${MD5_RESULT} ${FILE_MD5} SUCCESS) -# if(NOT ${SUCCESS}) -# message(WARNING "${FILE_PATH} not matching MD5") -# endif() -# endif() -# endfunction(DownloadFile) - -# function(NewHorizonDownload FILE_PATH ) -# set(MD5 "${ARGV1}") -# DownloadFile("${OPENSPACE_BASE_DIR}/openspace-data/spice/JupiterNhKernels/${FILE_PATH}" -# "http://naif.jpl.nasa.gov/pub/naif/pds/data/nh-j_p_ss-spice-6-v1.0/nhsp_1000/data/${FILE_PATH}" ${MD5}) -# endfunction(NewHorizonDownload) - -# NewHorizonDownload("ck/merged_nhpc_2006_v011.bc") -# NewHorizonDownload("ck/merged_nhpc_2007_v006.bc") -# NewHorizonDownload("fk/nh_v200.tf") -# NewHorizonDownload("ik/nh_lorri_v100.ti") -# NewHorizonDownload("sclk/new_horizons_413.tsc" "6f7a87c21cb3e37835261ed745f34d4a") -# NewHorizonDownload("spk/de413.bsp") -# NewHorizonDownload("spk/jup260.bsp") -# NewHorizonDownload("spk/nh_nep_ura_000.bsp") -# NewHorizonDownload("spk/nh_recon_e2j_v1.bsp") -# NewHorizonDownload("spk/nh_recon_j2sep07_prelimv1.bsp") -# NewHorizonDownload("spk/sb_2002jf56_2.bsp") -# endif(DOWNLOAD_FILES) + if (TARGET OpenSpaceTest) + ghl_copy_shared_libraries(OpenSpaceTest ${OPENSPACE_EXT_DIR}/ghoul) + endif () +endif () diff --git a/ext/ghoul b/ext/ghoul index 63a61eea62..b256ae209a 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 63a61eea62550e0b14bb031ea47c71cd8a5f9921 +Subproject commit b256ae209a521dd8c079090f18ec032adab6f8ce From aab60be7f17637736e23a5fb15b63b3640d547b9 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 22 May 2015 21:41:20 +0200 Subject: [PATCH 039/329] Making modules selectable in CMake Only include selected modules --- CMakeLists.txt | 18 +++-- support/cmake/module_macros.cmake | 109 ++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 26340274ed..ea1d56b714 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -224,10 +224,20 @@ endif (OPENSPACE_HAVE_TESTS) ############################# # Modules ############################# -add_subdirectory(modules/base) -add_subdirectory(modules/newhorizons) -add_subdirectory(modules/volume) -target_link_libraries(libOpenSpace openspace-module-base openspace-module-newhorizons openspace-module-volume) + +#add_internal_modules +generate_unset_mod_options_and_depend_sort(${OPENSPACE_MODULE_DIR} OPENSPACE_SORTED_MODULES) +#Resolve dependencies for selected modules +resolve_module_dependencies(${OPENSPACE_MODULE_DIR} ${OPENSPACE_SORTED_MODULES}) + +foreach (moduleDir ${OPENSPACE_SORTED_MODULES}) + openspace_dir_to_mod_prefix(module ${moduleDir}) + if (${module}) + add_subdirectory(modules/${moduleDir}) + target_link_libraries(libOpenSpace openspace-module-${moduleDir}) + endif () +endforeach () + ############################# # Project Folder structure (where appropriate) diff --git a/support/cmake/module_macros.cmake b/support/cmake/module_macros.cmake index e69de29bb2..4950d9c815 100644 --- a/support/cmake/module_macros.cmake +++ b/support/cmake/module_macros.cmake @@ -0,0 +1,109 @@ +######################################################################################### +# # +# OpenSpace # +# # +# Copyright (c) 2014-2015 # +# # +# 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. # +######################################################################################### + +macro(first_case_upper retval in_value) + string(TOLOWER ${in_value} value) + string(SUBSTRING ${value} 0 1 first_letter) + string(TOUPPER ${first_letter} first_letter) + string(REGEX REPLACE "^.(.*)" "${first_letter}\\1" result "${value}") + set(${retval} ${result}) +endmacro() + +macro(openspace_mod_name_to_dir retval) + set(the_list "") + foreach(item ${ARGN}) + string(REGEX MATCH "(^OpenSpace.*.Module$)" found_item ${item}) + if(found_item) + string(REGEX REPLACE "(^OpenSpace)|(Module$)" "" new_item ${item}) + string(TOLOWER ${new_item} l_new_item) + list(APPEND the_list ${l_new_item}) + endif() + endforeach() + set(${retval} ${the_list}) +endmacro() + +macro(openspace_dir_to_mod_prefix retval) + set(the_list "") + foreach(item ${ARGN}) + string(TOUPPER ${item} u_item) + list(APPEND the_list OPENSPACE_MODULE_${u_item}) + endforeach() + set(${retval} ${the_list}) +endmacro() + + +macro(generate_unset_mod_options_and_depend_sort module_root_path retval) + file(GLOB sub-dir RELATIVE ${module_root_path} ${module_root_path}/*) + set(sorted_dirs ${sub-dir}) + foreach(dir ${sub-dir}) + if(IS_DIRECTORY ${module_root_path}/${dir}) + if(EXISTS "${module_root_path}/${dir}/depends.cmake") + include(${module_root_path}/${dir}/depends.cmake) + foreach(dependency ${dependencies}) + list(FIND OPENSPACE_MODULE_PACKAGE_NAMES ${dependency} module_index) + if(NOT module_index EQUAL -1) + list(REMOVE_ITEM dependencies ${dependency}) + endif() + endforeach() + openspace_mod_name_to_dir(depend_folders ${dependencies}) + list(APPEND depend_folders ${dir}) + list(FIND sorted_dirs ${dir} dir_index) + list(INSERT sorted_dirs ${dir_index} ${depend_folders}) + list(REMOVE_DUPLICATES sorted_dirs) + endif() + openspace_dir_to_mod_prefix(mod_name ${dir}) + if(NOT DEFINED ${mod_name}) + first_case_upper(dir_name_cap ${dir}) + option(${mod_name} "Build ${dir_name_cap} Module" OFF) + endif() + else() + list(REMOVE_ITEM sorted_dirs ${dir}) + endif() + endforeach() + set(${retval} ${sorted_dirs}) +endmacro() + +macro(resolve_module_dependencies module_root_path) + #Reverse list (as it is depend sorted) and go over dependencies one more time + #If build is ON, then switch dependencies ON + set(dir_list ${ARGN}) + list(REVERSE dir_list) + foreach(dir ${dir_list}) + openspace_dir_to_mod_prefix(mod_name ${dir}) + if(${mod_name}) + if(EXISTS "${module_root_path}/${dir}/depends.cmake") + include(${module_root_path}/${dir}/depends.cmake) + openspace_mod_name_to_dir(depend_folders ${dependencies}) + foreach(depend_folder ${depend_folders}) + openspace_dir_to_mod_prefix(depend_mod_name ${depend_folder}) + first_case_upper(depend_name_cap ${depend_folder}) + if(NOT ${depend_mod_name}) + set(${depend_mod_name} ON CACHE BOOL "Build ${depend_name_cap} Module" FORCE) + message(STATUS "${depend_mod_name} was set to build, due to dependency towards ${mod_name}") + endif() + endforeach() + endif() + endif() + endforeach() +endmacro() \ No newline at end of file From f2a7ea9630ef40368fc0987c8eb015780b8f2d1f Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 23 May 2015 01:39:23 +0200 Subject: [PATCH 040/329] Fixing compile warning --- include/openspace/util/updatestructures.h | 3 +++ modules/base/rendering/renderablemodel.cpp | 1 - modules/base/rendering/renderablepath.cpp | 2 +- modules/base/rendering/renderabletrail.cpp | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/openspace/util/updatestructures.h b/include/openspace/util/updatestructures.h index faa10c9a4a..92286f02a7 100644 --- a/include/openspace/util/updatestructures.h +++ b/include/openspace/util/updatestructures.h @@ -42,6 +42,9 @@ struct UpdateData { }; struct RenderData { + RenderData() = delete; + RenderData& operator=(const RenderData& rhs) = delete; + const Camera& camera; psc position; bool doPerformanceMeasurement; diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index 487450f7bd..ff22486f0d 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -222,7 +222,6 @@ void RenderableModel::update(const UpdateData& data) { _programObject->rebuildFromFile(); double _time = data.time; - double futureTime; //if (_isGhost){ // futureTime = openspace::ImageSequencer2::ref().getNextCaptureTime(); // double remaining = openspace::ImageSequencer2::ref().getNextCaptureTime() - data.time; diff --git a/modules/base/rendering/renderablepath.cpp b/modules/base/rendering/renderablepath.cpp index 8e145f220e..49b214c3b3 100644 --- a/modules/base/rendering/renderablepath.cpp +++ b/modules/base/rendering/renderablepath.cpp @@ -120,8 +120,8 @@ bool RenderablePath::initialize() { bool RenderablePath::deinitialize() { glDeleteVertexArrays(1, &_vaoID); glDeleteBuffers(1, &_vBufferID); + Renderable::deinitialize(); return true; - deinitialize(); } bool RenderablePath::isReady() const { diff --git a/modules/base/rendering/renderabletrail.cpp b/modules/base/rendering/renderabletrail.cpp index 045ee51131..28938be76e 100644 --- a/modules/base/rendering/renderabletrail.cpp +++ b/modules/base/rendering/renderabletrail.cpp @@ -125,8 +125,8 @@ bool RenderableTrail::initialize() { bool RenderableTrail::deinitialize() { glDeleteVertexArrays(1, &_vaoID); glDeleteBuffers(1, &_vBufferID); + Renderable::deinitialize(); return true; - deinitialize(); } bool RenderableTrail::isReady() const { From b882ba60780cbf9524d6b8dc75f3aead8a50c691 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 23 May 2015 02:03:06 +0200 Subject: [PATCH 041/329] Getting a running version of modularized cmakelists up and running --- CMakeLists.txt | 89 ++++++++++++-- modules/base/CMakeLists.txt | 16 +-- modules/base/include.cmake | 1 + modules/base/rendering/renderablepath.cpp | 2 +- modules/base/rendering/renderabletrail.cpp | 2 +- modules/newhorizons/CMakeLists.txt | 86 +++++++------- modules/volume/CMakeLists.txt | 16 +-- modules/volume/include.cmake | 1 + support/cmake/module_common.cmake | 33 ++++++ support/cmake/module_definition.cmake | 131 +++++++++++++++++++++ support/cmake/module_macros.cmake | 109 ----------------- 11 files changed, 300 insertions(+), 186 deletions(-) create mode 100644 modules/base/include.cmake create mode 100644 modules/volume/include.cmake create mode 100644 support/cmake/module_common.cmake create mode 100644 support/cmake/module_definition.cmake delete mode 100644 support/cmake/module_macros.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index ea1d56b714..88111e4dfe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,7 +48,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OPENSPACE_BASE_DIR}/bin/openspace) set_property(GLOBAL PROPERTY USE_FOLDERS On) set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER CMake) -include(${OPENSPACE_CMAKE_EXT_DIR}/module_macros.cmake) +include(${OPENSPACE_CMAKE_EXT_DIR}/module_common.cmake) include(${OPENSPACE_EXT_DIR}/ghoul/ext/CopySharedLibraries.cmake) ############################# # Set current OpenSpace Version @@ -80,6 +80,7 @@ target_link_libraries(OpenSpace libOpenSpace) ############################# # General Options ############################# +option(OPENSPACE_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF) if (MSVC) option(OPENSPACE_ENABLE_VLD "Enable the Visual Leak Detector" OFF) if (OPENSPACE_ENABLE_VLD) @@ -97,9 +98,14 @@ if (MSVC) endif () target_compile_options(libOpenSpace PUBLIC "/MP" "/wd4201" "/wd4127") + if (OPENSPACE_WARNINGS_AS_ERRORS) + target_compile_options(libOpenSpace PUBLIC "/WX") + target_compile_options(OpenSpace PUBLIC "/WX") + endif () + set_target_properties(OpenSpace PROPERTIES LINK_FLAGS - "/NODEFAULTLIB:LIBCMTD.lib /NODEFAULTLIB:LIBCMT.lib" - ) + "/NODEFAULTLIB:LIBCMTD.lib /NODEFAULTLIB:LIBCMT.lib" + ) elseif (APPLE) target_compile_definitions(libOpenSpace PUBLIC "__APPLE__") @@ -225,19 +231,78 @@ endif (OPENSPACE_HAVE_TESTS) # Modules ############################# -#add_internal_modules -generate_unset_mod_options_and_depend_sort(${OPENSPACE_MODULE_DIR} OPENSPACE_SORTED_MODULES) -#Resolve dependencies for selected modules -resolve_module_dependencies(${OPENSPACE_MODULE_DIR} ${OPENSPACE_SORTED_MODULES}) +# Get all modules in the correct order based on their dependencies +file(GLOB moduleDirs RELATIVE ${OPENSPACE_MODULE_DIR} ${OPENSPACE_MODULE_DIR}/*) +set(sortedModules ${moduleDirs}) +foreach (dir ${moduleDirs}) + if(IS_DIRECTORY ${OPENSPACE_MODULE_DIR}/${dir}) + set(defaultModule OFF) + if (EXISTS "${OPENSPACE_MODULE_DIR}/${dir}/include.cmake") + unset(OPENSPACE_DEPENDENCIES) + unset(DEFAULT_MODULE) + include(${OPENSPACE_MODULE_DIR}/${dir}/include.cmake) -foreach (moduleDir ${OPENSPACE_SORTED_MODULES}) - openspace_dir_to_mod_prefix(module ${moduleDir}) - if (${module}) - add_subdirectory(modules/${moduleDir}) - target_link_libraries(libOpenSpace openspace-module-${moduleDir}) + if (DEFINED DEFAULT_MODULE) + set(defaultModule ${DEFAULT_MODULE}) + endif () + if (OPENSPACE_DEPENDENCIES) + foreach (dependency ${OPENSPACE_DEPENDENCIES}) + create_library_name(${dependency} library) + if (TARGET ${library}) + # already registered + list(REMOVE_ITEM OPENSPACE_DEPENDENCIES ${dependency}) + endif () + endforeach () + + list(APPEND OPENSPACE_DEPENDENCIES ${dir}) + list(FIND sortedModules ${dir} dir_index) + # if (NOT dir STREQUAL "base") + # list(INSERT OPENSPACE_DEPENDENCIES 0 "base") + # endif () + list(INSERT sortedModules ${dir_index} ${OPENSPACE_DEPENDENCIES}) + list(REMOVE_DUPLICATES sortedModules) + endif () + endif () + create_option_name(${dir} optionName) + option(${optionName} "Build ${dir} Module" ${defaultModule}) + # create_library_name(${module} ${library}) + else () + list(REMOVE_ITEM sortedModules ${dir}) endif () endforeach () +# Automatically set dependent modules to ON +set(dir_list ${sortedModules}) +list(REVERSE dir_list) +foreach (dir ${dir_list}) + create_option_name(${dir} optionName) + if (${optionName}) + if (EXISTS "${OPENSPACE_MODULE_DIR}/${dir}/include.cmake") + unset(OPENSPACE_DEPENDENCIES) + unset(DEFAULT_MODULE) + include(${OPENSPACE_MODULE_DIR}/${dir}/include.cmake) + + if (OPENSPACE_DEPENDENCIES) + foreach (dependency ${OPENSPACE_DEPENDENCIES}) + create_option_name(${dependency} dependencyOptionName) + set(${dependencyOptionName} ON CACHE BOOL "ff" FORCE) + message(STATUS "${dependencyOptionName} was set to build, due to dependency towards ${optionName}") + endforeach () + endif () + endif () + endif () +endforeach () + + +# Add subdirectories in the correct order +foreach (module ${sortedModules}) + create_option_name(${module} optionName) + if (${optionName}) + create_library_name(${module} libraryName) + add_subdirectory(${OPENSPACE_MODULE_DIR}/${module}) + target_link_libraries(libOpenSpace ${libraryName}) + endif () +endforeach () ############################# # Project Folder structure (where appropriate) diff --git a/modules/base/CMakeLists.txt b/modules/base/CMakeLists.txt index 3316dfb50c..f4cffdc774 100644 --- a/modules/base/CMakeLists.txt +++ b/modules/base/CMakeLists.txt @@ -22,6 +22,8 @@ # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # ######################################################################################### +include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) + set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/modelgeometry.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometry.h @@ -89,15 +91,7 @@ set(SHADER_FILES ) source_group("Shader Files" FILES ${SHADER_FILES}) -set(MODULE_CLASS_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/basemodule.h - ${CMAKE_CURRENT_SOURCE_DIR}/basemodule.cpp +create_new_module( + "Base" + ${HEADER_FILES} ${SOURCE_FILES} ${SHADER_FILES} ) - -add_library(openspace-module-base ${HEADER_FILES} ${SOURCE_FILES} ${MODULE_CLASS_FILES}) -target_include_directories(openspace-module-base PUBLIC "${OPENSPACE_BASE_DIR}/include" "${OPENSPACE_BASE_DIR}") - -get_property(OPENSPACE_INCLUDE_DIR TARGET libOpenSpace PROPERTY INTERFACE_INCLUDE_DIRECTORIES) -target_include_directories(openspace-module-base PUBLIC ${OPENSPACE_INCLUDE_DIR}) -get_property(OPENSPACE_DEFINITIONS TARGET libOpenSpace PROPERTY INTERFACE_COMPILE_DEFINITIONS) -target_compile_definitions(openspace-module-base PUBLIC ${OPENSPACE_DEFINITIONS}) \ No newline at end of file diff --git a/modules/base/include.cmake b/modules/base/include.cmake new file mode 100644 index 0000000000..ffea0ac430 --- /dev/null +++ b/modules/base/include.cmake @@ -0,0 +1 @@ +set(DEFAULT_MODULE ON) diff --git a/modules/base/rendering/renderablepath.cpp b/modules/base/rendering/renderablepath.cpp index 49b214c3b3..5ea998c475 100644 --- a/modules/base/rendering/renderablepath.cpp +++ b/modules/base/rendering/renderablepath.cpp @@ -120,7 +120,7 @@ bool RenderablePath::initialize() { bool RenderablePath::deinitialize() { glDeleteVertexArrays(1, &_vaoID); glDeleteBuffers(1, &_vBufferID); - Renderable::deinitialize(); + //Renderable::deinitialize(); return true; } diff --git a/modules/base/rendering/renderabletrail.cpp b/modules/base/rendering/renderabletrail.cpp index 28938be76e..d1d652a7c2 100644 --- a/modules/base/rendering/renderabletrail.cpp +++ b/modules/base/rendering/renderabletrail.cpp @@ -125,7 +125,7 @@ bool RenderableTrail::initialize() { bool RenderableTrail::deinitialize() { glDeleteVertexArrays(1, &_vaoID); glDeleteBuffers(1, &_vBufferID); - Renderable::deinitialize(); + //Renderable::deinitialize(); return true; } diff --git a/modules/newhorizons/CMakeLists.txt b/modules/newhorizons/CMakeLists.txt index 0de42914fc..bd0df96ffb 100644 --- a/modules/newhorizons/CMakeLists.txt +++ b/modules/newhorizons/CMakeLists.txt @@ -22,54 +22,58 @@ # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # ######################################################################################### +include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) + set(HEADER_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometryprojection.h - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablecrawlingline.h - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefov.h - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplaneprojection.h - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplanetprojection.h - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometryprojection.h - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/writeToTexture.h - ${CMAKE_CURRENT_SOURCE_DIR}/util/decoder.h - ${CMAKE_CURRENT_SOURCE_DIR}/util/hongkangparser.h - ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer.h - ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer2.h - ${CMAKE_CURRENT_SOURCE_DIR}/util/instrumentdecoder.h - ${CMAKE_CURRENT_SOURCE_DIR}/util/labelparser.h - ${CMAKE_CURRENT_SOURCE_DIR}/util/scannerdecoder.h - ${CMAKE_CURRENT_SOURCE_DIR}/util/sequenceparser.h - ${CMAKE_CURRENT_SOURCE_DIR}/util/targetdecoder.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometryprojection.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablecrawlingline.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefov.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplaneprojection.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplanetprojection.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometryprojection.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/writeToTexture.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/decoder.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/hongkangparser.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer2.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/instrumentdecoder.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/labelparser.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/scannerdecoder.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/sequenceparser.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/targetdecoder.h ) source_group("Header Files" FILES ${HEADER_FILES}) set(SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometryprojection.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablecrawlingline.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefov.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplaneprojection.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplanetprojection.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometryprojection.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/util/decoder.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/util/hongkangparser.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer2.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/util/instrumentdecoder.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/util/labelparser.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/util/scannerdecoder.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/util/sequenceparser.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/util/targetdecoder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometryprojection.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablecrawlingline.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefov.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplaneprojection.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplanetprojection.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometryprojection.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/decoder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/hongkangparser.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer2.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/instrumentdecoder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/labelparser.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/scannerdecoder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/sequenceparser.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/targetdecoder.cpp ) source_group("Source Files" FILES ${SOURCE_FILES}) -set(MODULE_CLASS_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/newhorizonsmodule.h - ${CMAKE_CURRENT_SOURCE_DIR}/newhorizonsmodule.cpp +set(SHADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/crawlingline_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/crawlingline_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/fov_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/fov_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/projectiveTexture_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/projectiveTexture_vs.glsl ) +source_group("Shader Files" FILES ${SHADER_FILES}) -add_library(openspace-module-newhorizons ${HEADER_FILES} ${SOURCE_FILES} ${MODULE_CLASS_FILES}) -target_include_directories(openspace-module-newhorizons PUBLIC "${OPENSPACE_BASE_DIR}/include" "${OPENSPACE_BASE_DIR}") - -get_property(OPENSPACE_INCLUDE_DIR TARGET libOpenSpace PROPERTY INTERFACE_INCLUDE_DIRECTORIES) -target_include_directories(openspace-module-newhorizons PUBLIC ${OPENSPACE_INCLUDE_DIR}) -get_property(OPENSPACE_DEFINITIONS TARGET libOpenSpace PROPERTY INTERFACE_COMPILE_DEFINITIONS) -target_compile_definitions(openspace-module-newhorizons PUBLIC ${OPENSPACE_DEFINITIONS}) +create_new_module( + "NewHorizons" + ${HEADER_FILES} ${SOURCE_FILES} ${SHADER_FILES} +) diff --git a/modules/volume/CMakeLists.txt b/modules/volume/CMakeLists.txt index b13d098160..23ce705156 100644 --- a/modules/volume/CMakeLists.txt +++ b/modules/volume/CMakeLists.txt @@ -22,6 +22,8 @@ # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # ######################################################################################### +include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) + set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablevolume.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablevolumegl.h @@ -34,15 +36,7 @@ set(SOURCE_FILES ) source_group("Source Files" FILES ${SOURCE_FILES}) -set(MODULE_CLASS_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/volumemodule.h - ${CMAKE_CURRENT_SOURCE_DIR}/volumemodule.cpp +create_new_module( + "Volume" + ${HEADER_FILES} ${SOURCE_FILES} ${SHADER_FILES} ) - -add_library(openspace-module-volume ${HEADER_FILES} ${SOURCE_FILES} ${MODULE_CLASS_FILES}) -target_include_directories(openspace-module-volume PUBLIC "${OPENSPACE_BASE_DIR}/include" "${OPENSPACE_BASE_DIR}") - -get_property(OPENSPACE_INCLUDE_DIR TARGET libOpenSpace PROPERTY INTERFACE_INCLUDE_DIRECTORIES) -target_include_directories(openspace-module-volume PUBLIC ${OPENSPACE_INCLUDE_DIR}) -get_property(OPENSPACE_DEFINITIONS TARGET libOpenSpace PROPERTY INTERFACE_COMPILE_DEFINITIONS) -target_compile_definitions(openspace-module-volume PUBLIC ${OPENSPACE_DEFINITIONS}) diff --git a/modules/volume/include.cmake b/modules/volume/include.cmake new file mode 100644 index 0000000000..ffea0ac430 --- /dev/null +++ b/modules/volume/include.cmake @@ -0,0 +1 @@ +set(DEFAULT_MODULE ON) diff --git a/support/cmake/module_common.cmake b/support/cmake/module_common.cmake new file mode 100644 index 0000000000..683600e7ca --- /dev/null +++ b/support/cmake/module_common.cmake @@ -0,0 +1,33 @@ +######################################################################################### +# # +# OpenSpace # +# # +# Copyright (c) 2014-2015 # +# # +# 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. # +######################################################################################### + +function (create_library_name module_name library_name) + string(TOLOWER ${module_name} module_name) + set(${library_name} "openspace-module-${module_name}" PARENT_SCOPE) +endfunction () + +function (create_option_name module_name option_name) + string(TOUPPER ${module_name} module_name) + set(${option_name} "OPENSPACE_MODULE_${module_name}" PARENT_SCOPE) +endfunction () diff --git a/support/cmake/module_definition.cmake b/support/cmake/module_definition.cmake new file mode 100644 index 0000000000..62032ec2cd --- /dev/null +++ b/support/cmake/module_definition.cmake @@ -0,0 +1,131 @@ +######################################################################################### +# # +# OpenSpace # +# # +# Copyright (c) 2014-2015 # +# # +# Permission is hereby granted, free of charge, to any person obtaining a copy of this # +# software and associated documentation files (the "Software"), to deal in the Software # +# without restriction, including without limitation the rights to use, copy, modify, # +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # +# permit persons to whom the Software is furnished to do so, subject to the following # +# conditions: # +# # +# The above copyright notice and this permission notice shall be included in all copies # +# or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF # +# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # +# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # +######################################################################################### + +include (${OPENSPACE_CMAKE_EXT_DIR}/module_common.cmake) + +function (create_new_module + module_name + # Sources + ) + set(sources ${ARGN}) + project(${module_name}) + # Create a library name of the style: openspace-module-${name} + create_library_name(${module_name} library_name) + + message(STATUS "Configuring module ${MODULE_NAME}: ${library_name}") + + add_module_files() + + add_library(${library_name} ${sources}) + set_common_compile_settings(${library_name}) + set_openspace_includes(${library_name}) + + handle_dependencies(${library_name} ${module_name}) +endfunction () + +# I couldn't make adding the module files to the ${sources} variable to work with a function ---abock +# Adds the module.h and module.cpp files to the list of sources and provides +# Them with a source group +macro (add_module_files) + string(TOLOWER ${module_name} module_name_lower) + set(module_files + ${CMAKE_CURRENT_SOURCE_DIR}/${module_name_lower}module.h + ${CMAKE_CURRENT_SOURCE_DIR}/${module_name_lower}module.cpp + ) + source_group("Module Files" FILES ${module_files}) + list(APPEND sources ${module_files}) +endmacro () + +# Set the compiler settings that are common to all modules +function (set_common_compile_settings target_name) + if (MSVC) + target_compile_options(${library_name} PUBLIC + "/MP" # Enabling multi-threaded compilation + "/wd4100" # Unreferenced formal parameter [too frequent in external libs] + "/wd4127" # constant conditional expression [used for do/while semicolon swallowing] + "/wd4201" # nameless struct/union [standard is ubiquitous] + "/wd4505" # Unreferenced function was removed + "/W4" + ) + if (OPENSPACE_WARNINGS_AS_ERRORS) + target_compile_options(${library_name} PUBLIC "/Wx") + endif () + endif () +endfunction () + +# Propagate the include directives from the libOpenSpace target into this module +function (set_openspace_includes target_name) + get_property( + OPENSPACE_INCLUDE_DIR + TARGET libOpenSpace + PROPERTY INTERFACE_INCLUDE_DIRECTORIES + ) + target_include_directories(${target_name} PUBLIC + "${OPENSPACE_BASE_DIR}" + ${OPENSPACE_INCLUDE_DIR} + ) + + get_property( + OPENSPACE_DEFINES + TARGET libOpenSpace + PROPERTY INTERFACE_COMPILE_DEFINITIONS + ) + target_compile_definitions(${target_name} PUBLIC ${OPENSPACE_DEFINES}) + + target_link_libraries(${target_name} libOpenSpace) +endfunction () + +# Loads the dependencies from 'include.cmake' and deals with them +function (handle_dependencies target_name module_name) + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/include.cmake") + include(${CMAKE_CURRENT_SOURCE_DIR}/include.cmake) + + # if (NOT ${module_name} STREQUAL "Base") + # list(APPEND OPENSPACE_DEPENDENCIES "base") + # endif () + + # Handle OpenSpace dependencies + foreach (dep ${OPENSPACE_DEPENDENCIES}) + create_library_name(${dep} dep_library) + target_link_libraries(${target_name} ${dep_library}) + + get_property( + DEP_INCLUDE_DIR + TARGET ${dep_library} + PROPERTY INTERFACE_INCLUDE_DIRECTORIES + ) + target_include_directories(${target_name} PUBLIC ${DEP_INCLUDE_DIR}) + endforeach () + + # Handle extenal dependencies + foreach (dep ${EXTERNAL_DEPENDENCIES}) + string(TOUPPER ${dep} dep_upper) + find_package(${dep} REQUIRED) + target_include_directories(${target_name} PUBLIC + ${${dep_upper}_INCLUDE_DIR} ${${dep_upper}_INCLUDE_DIRS} + ) + target_link_libraries(${target_name} ${${dep_upper}_LIBRARIES}) + endforeach () + endif () +endfunction () diff --git a/support/cmake/module_macros.cmake b/support/cmake/module_macros.cmake deleted file mode 100644 index 4950d9c815..0000000000 --- a/support/cmake/module_macros.cmake +++ /dev/null @@ -1,109 +0,0 @@ -######################################################################################### -# # -# OpenSpace # -# # -# Copyright (c) 2014-2015 # -# # -# 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. # -######################################################################################### - -macro(first_case_upper retval in_value) - string(TOLOWER ${in_value} value) - string(SUBSTRING ${value} 0 1 first_letter) - string(TOUPPER ${first_letter} first_letter) - string(REGEX REPLACE "^.(.*)" "${first_letter}\\1" result "${value}") - set(${retval} ${result}) -endmacro() - -macro(openspace_mod_name_to_dir retval) - set(the_list "") - foreach(item ${ARGN}) - string(REGEX MATCH "(^OpenSpace.*.Module$)" found_item ${item}) - if(found_item) - string(REGEX REPLACE "(^OpenSpace)|(Module$)" "" new_item ${item}) - string(TOLOWER ${new_item} l_new_item) - list(APPEND the_list ${l_new_item}) - endif() - endforeach() - set(${retval} ${the_list}) -endmacro() - -macro(openspace_dir_to_mod_prefix retval) - set(the_list "") - foreach(item ${ARGN}) - string(TOUPPER ${item} u_item) - list(APPEND the_list OPENSPACE_MODULE_${u_item}) - endforeach() - set(${retval} ${the_list}) -endmacro() - - -macro(generate_unset_mod_options_and_depend_sort module_root_path retval) - file(GLOB sub-dir RELATIVE ${module_root_path} ${module_root_path}/*) - set(sorted_dirs ${sub-dir}) - foreach(dir ${sub-dir}) - if(IS_DIRECTORY ${module_root_path}/${dir}) - if(EXISTS "${module_root_path}/${dir}/depends.cmake") - include(${module_root_path}/${dir}/depends.cmake) - foreach(dependency ${dependencies}) - list(FIND OPENSPACE_MODULE_PACKAGE_NAMES ${dependency} module_index) - if(NOT module_index EQUAL -1) - list(REMOVE_ITEM dependencies ${dependency}) - endif() - endforeach() - openspace_mod_name_to_dir(depend_folders ${dependencies}) - list(APPEND depend_folders ${dir}) - list(FIND sorted_dirs ${dir} dir_index) - list(INSERT sorted_dirs ${dir_index} ${depend_folders}) - list(REMOVE_DUPLICATES sorted_dirs) - endif() - openspace_dir_to_mod_prefix(mod_name ${dir}) - if(NOT DEFINED ${mod_name}) - first_case_upper(dir_name_cap ${dir}) - option(${mod_name} "Build ${dir_name_cap} Module" OFF) - endif() - else() - list(REMOVE_ITEM sorted_dirs ${dir}) - endif() - endforeach() - set(${retval} ${sorted_dirs}) -endmacro() - -macro(resolve_module_dependencies module_root_path) - #Reverse list (as it is depend sorted) and go over dependencies one more time - #If build is ON, then switch dependencies ON - set(dir_list ${ARGN}) - list(REVERSE dir_list) - foreach(dir ${dir_list}) - openspace_dir_to_mod_prefix(mod_name ${dir}) - if(${mod_name}) - if(EXISTS "${module_root_path}/${dir}/depends.cmake") - include(${module_root_path}/${dir}/depends.cmake) - openspace_mod_name_to_dir(depend_folders ${dependencies}) - foreach(depend_folder ${depend_folders}) - openspace_dir_to_mod_prefix(depend_mod_name ${depend_folder}) - first_case_upper(depend_name_cap ${depend_folder}) - if(NOT ${depend_mod_name}) - set(${depend_mod_name} ON CACHE BOOL "Build ${depend_name_cap} Module" FORCE) - message(STATUS "${depend_mod_name} was set to build, due to dependency towards ${mod_name}") - endif() - endforeach() - endif() - endif() - endforeach() -endmacro() \ No newline at end of file From 7ee57a83b9647cf4b7f1c226bc9e9ee8ba011ff4 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 23 May 2015 02:40:44 +0200 Subject: [PATCH 042/329] Deal with automatic registration of modules in moduleengine --- CMakeLists.txt | 34 ++++++++++++++++++++++ src/engine/moduleengine.cpp | 8 ++--- support/cmake/module_definition.cmake | 11 +++++++ support/cmake/module_registration.template | 15 ++++++++++ 4 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 support/cmake/module_registration.template diff --git a/CMakeLists.txt b/CMakeLists.txt index 88111e4dfe..53f1c82be8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -293,6 +293,8 @@ foreach (dir ${dir_list}) endif () endforeach () +set(MODULE_HEADERS "") +set(MODULE_CLASSES "") # Add subdirectories in the correct order foreach (module ${sortedModules}) @@ -301,9 +303,41 @@ foreach (module ${sortedModules}) create_library_name(${module} libraryName) add_subdirectory(${OPENSPACE_MODULE_DIR}/${module}) target_link_libraries(libOpenSpace ${libraryName}) + + # Create registration file + string(TOUPPER ${module} module_upper) + unset(MODULE_NAME) + unset(MODULE_PATH) + include(${CMAKE_BINARY_DIR}/modules/${module}/modulename.cmake) + + list(APPEND MODULE_HEADERS + #"#ifdef REGISTRATION_OPENSPACE${module_upper}MODULE\n" + "#include <${MODULE_PATH}>\n" + #"#endif\n\n" + ) + + list(APPEND MODULE_CLASSES + "new ${MODULE_NAME},\n" + ) + endif () endforeach () +function(JOIN VALUES GLUE OUTPUT) + string (REGEX REPLACE "([^\\]|^);" "\\1${GLUE}" _TMP_STR "${VALUES}") + string (REGEX REPLACE "[\\](.)" "\\1" _TMP_STR "${_TMP_STR}") #fixes escaping + set (${OUTPUT} "${_TMP_STR}" PARENT_SCOPE) +endfunction() + +string(REPLACE ";" "" MODULE_HEADERS ${MODULE_HEADERS}) +string(REPLACE ";" "" MODULE_CLASSES ${MODULE_CLASSES}) + +configure_file( + ${OPENSPACE_CMAKE_EXT_DIR}/module_registration.template + ${CMAKE_BINARY_DIR}/_generated/include/openspace/moduleregistration.h +) + + ############################# # Project Folder structure (where appropriate) ############################# diff --git a/src/engine/moduleengine.cpp b/src/engine/moduleengine.cpp index 476f0efd84..4dafe9d882 100644 --- a/src/engine/moduleengine.cpp +++ b/src/engine/moduleengine.cpp @@ -32,6 +32,8 @@ #include #include +#include + namespace { const std::string _loggerCat = "ModuleEngine"; } @@ -40,10 +42,8 @@ namespace openspace { bool ModuleEngine::initialize() { LDEBUG("Initializing modules"); - - registerModule(new BaseModule); - registerModule(new NewHorizonsModule); - registerModule(new VolumeModule); + + registerModules(AllModules); for (OpenSpaceModule* m : _modules) { bool success = m->initialize(); diff --git a/support/cmake/module_definition.cmake b/support/cmake/module_definition.cmake index 62032ec2cd..7554548b25 100644 --- a/support/cmake/module_definition.cmake +++ b/support/cmake/module_definition.cmake @@ -42,6 +42,8 @@ function (create_new_module set_openspace_includes(${library_name}) handle_dependencies(${library_name} ${module_name}) + + write_module_name(${module_name}) endfunction () # I couldn't make adding the module files to the ${sources} variable to work with a function ---abock @@ -129,3 +131,12 @@ function (handle_dependencies target_name module_name) endforeach () endif () endfunction () + +function (write_module_name module_name) + string(TOLOWER ${module_name} module_name_lower) + + file(WRITE ${CMAKE_BINARY_DIR}/modules/${module_name}/modulename.cmake + "set(MODULE_NAME ${module_name}Module)\n" + "set(MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/${module_name_lower}module.h)" + ) +endfunction () \ No newline at end of file diff --git a/support/cmake/module_registration.template b/support/cmake/module_registration.template new file mode 100644 index 0000000000..c173417f9b --- /dev/null +++ b/support/cmake/module_registration.template @@ -0,0 +1,15 @@ +// This file has been auto-generated by CMake. Do not change + +#ifndef __MODULE_REGISTRATION_H__ +#define __MODULE_REGISTRATION_H__ + +@MODULE_HEADERS@ + +namespace openspace { +std::vector AllModules = { +@MODULE_CLASSES@ +}; + +} // namespace openspace + +#endif // __MODULE_REGISTRATION_H__ From 4585ee1422b7811df8e0ac75f8ed5c38f4295663 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 23 May 2015 12:57:14 +0200 Subject: [PATCH 043/329] More cleanup of CMakeLists --- CMakeLists.txt | 337 +------------------- modules/base/CMakeLists.txt | 1 + modules/base/rendering/renderablepath.cpp | 1 - modules/base/rendering/renderabletrail.cpp | 1 - modules/newhorizons/CMakeLists.txt | 1 + modules/volume/CMakeLists.txt | 1 + src/CMakeLists.txt | 151 --------- src/engine/moduleengine.cpp | 4 - support/cmake/module_definition.cmake | 40 ++- support/cmake/module_handling.cmake | 24 ++ support/cmake/module_registration.template | 5 +- support/cmake/support_macros.cmake | 347 +++++++++++++++++++++ 12 files changed, 419 insertions(+), 494 deletions(-) create mode 100644 support/cmake/module_handling.cmake create mode 100644 support/cmake/support_macros.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 53f1c82be8..25752717c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,349 +24,38 @@ cmake_minimum_required (VERSION 3.0) -# Remove MinSizeRel build option -set(CMAKE_CONFIGURATION_TYPES Debug Release RelWithDebInfo CACHE TYPE INTERNAL FORCE) -mark_as_advanced(CMAKE_CONFIGURATION_TYPES) -mark_as_advanced(CMAKE_INSTALL_PREFIX) - project (OpenSpace) message(STATUS "Generating OpenSpace project") -############################# -# General Settings -############################# set(OPENSPACE_BASE_DIR "${PROJECT_SOURCE_DIR}") set(OPENSPACE_EXT_DIR "${OPENSPACE_BASE_DIR}/ext") set(OPENSPACE_MODULE_DIR "${OPENSPACE_BASE_DIR}/modules") set(OPENSPACE_CMAKE_EXT_DIR "${OPENSPACE_BASE_DIR}/support/cmake") -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${OPENSPACE_CMAKE_EXT_DIR}) -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OPENSPACE_BASE_DIR}/bin/lib) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OPENSPACE_BASE_DIR}/bin/lib) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OPENSPACE_BASE_DIR}/bin/openspace) - -set_property(GLOBAL PROPERTY USE_FOLDERS On) -set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER CMake) - +include(${OPENSPACE_CMAKE_EXT_DIR}/support_macros.cmake) include(${OPENSPACE_CMAKE_EXT_DIR}/module_common.cmake) include(${OPENSPACE_EXT_DIR}/ghoul/ext/CopySharedLibraries.cmake) -############################# -# Set current OpenSpace Version -############################# -set(OPENSPACE_MAJOR_VERSION 0) -set(OPENSPACE_MINOR_VERSION 1) -set(OPENSPACE_PATCH_VERSION 0) -set(OPENSPACE_VERSION_STRING "prerelease-5") -message(STATUS "Version: ${OPENSPACE_MAJOR_VERSION}.${OPENSPACE_MINOR_VERSION}.${OPENSPACE_PATCH_VERSION} (${OPENSPACE_VERSION_STRING})") -mark_as_advanced(OPENSPACE_MAJOR_VERSION, OPENSPACE_MINOR_VERSION, OPENSPACE_PATCH_VERSION, OPENSPACE_VERSION_STRING) -configure_file(${OPENSPACE_CMAKE_EXT_DIR}/version.template ${CMAKE_BINARY_DIR}/_generated/include/openspace/version.h) -############################# -# Including the source files (with source groups already defined) -############################# +test_compiler_compatibility() +cleanup_project() +set_build_output_directories() +configure_openspace_version(0 1 0 "prerelease-5") + include(src/CMakeLists.txt) +create_openspace_targets() +set_compile_settings() +add_external_dependencies() -############################# -# Declare the application -############################# -add_library(libOpenSpace STATIC ${OPENSPACE_HEADER} ${OPENSPACE_SOURCE}) -target_include_directories(libOpenSpace PUBLIC ${OPENSPACE_BASE_DIR}/include) -target_include_directories(libOpenSpace PUBLIC ${CMAKE_BINARY_DIR}/_generated/include) - -add_executable(OpenSpace ${OPENSPACE_MAIN}) -target_include_directories(OpenSpace PUBLIC ${OPENSPACE_BASE_DIR}/include) -target_link_libraries(OpenSpace libOpenSpace) - -############################# -# General Options -############################# option(OPENSPACE_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF) if (MSVC) option(OPENSPACE_ENABLE_VLD "Enable the Visual Leak Detector" OFF) - if (OPENSPACE_ENABLE_VLD) - target_link_libraries(libOpenSpace ${OPENSPACE_EXT_DIR}/vld/lib/vld.lib) - target_include_directories(libOpenSpace PUBLIC ${OPENSPACE_EXT_DIR}/vld) - endif () + handle_option_vld() endif () -############################# -# Compile settings -############################# -if (MSVC) - if (MSVC_VERSION LESS 1800) - message(FATAL_ERROR "OpenSpace requires at least Visual Studio 2013") - endif () - - target_compile_options(libOpenSpace PUBLIC "/MP" "/wd4201" "/wd4127") - if (OPENSPACE_WARNINGS_AS_ERRORS) - target_compile_options(libOpenSpace PUBLIC "/WX") - target_compile_options(OpenSpace PUBLIC "/WX") - endif () - - set_target_properties(OpenSpace PROPERTIES LINK_FLAGS - "/NODEFAULTLIB:LIBCMTD.lib /NODEFAULTLIB:LIBCMT.lib" - ) -elseif (APPLE) - target_compile_definitions(libOpenSpace PUBLIC "__APPLE__") - - include (CheckCXXCompilerFlag) - CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) - CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) - mark_as_advanced(COMPILER_SUPPORTS_CXX11, COMPILER_SUPPORTS_CXX0X) - if (COMPILER_SUPPORTS_CXX11) - target_compile_options(libOpenSpace PUBLIC "-std=c++11") - elseif (COMPILER_SUPPORTS_CXX0X) - target_compile_options(libOpenSpace PUBLIC "-std=c++0x") - else () - message(FATAL_ERROR "Compiler does not have C++11 support") - endif () - - target_compile_options(libOpenSpace PUBLIC"-stdlib=libc++") - - target_include_directories(libOpenSpace "/Developer/Headers/FlatCarbon") - find_library(COREFOUNDATION_LIBRARY CoreFoundation) - find_library(CARBON_LIBRARY Carbon) - find_library(COCOA_LIBRARY Carbon) - find_library(APP_SERVICES_LIBRARY ApplicationServices) - mark_as_advanced(CARBON_LIBRARY COCOA_LIBRARY APP_SERVICES_LIBRARY) - target_link_libraries(libOpenSpace - ${CARBON_LIBRARY} - ${COREFOUNDATION_LIBRARY} - ${COCOA_LIBRARY} - ${APP_SERVICES_LIBRARY} - ) -elseif (UNIX) - include (CheckCXXCompilerFlag) - CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) - CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) - mark_as_advanced(COMPILER_SUPPORTS_CXX11, COMPILER_SUPPORTS_CXX0X) - if (COMPILER_SUPPORTS_CXX11) - target_compile_options(libOpenSpace PUBLIC "-std=c++11") - elseif (COMPILER_SUPPORTS_CXX0X) - target_compile_options(libOpenSpace PUBLIC "-std=c++0x") - else () - message(FATAL_ERROR "Compiler does not have C++11 support") - endif () - - target_compile_definitions(libOpenSpace PUBLIC "-ggdb") -endif () - -############################# -# Dependencies -############################# -# Ghoul -add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul) -target_link_libraries(libOpenSpace Ghoul) -get_property(GHOUL_INCLUDE_DIR TARGET Ghoul PROPERTY INTERFACE_INCLUDE_DIRECTORIES) -target_include_directories(libOpenSpace PUBLIC ${GHOUL_INCLUDE_DIR}) -get_property(GHOUL_DEFINITIONS TARGET Ghoul PROPERTY INTERFACE_COMPILE_DEFINITIONS) -target_compile_definitions(libOpenSpace PUBLIC ${GHOUL_DEFINITIONS}) - -# # SGCT -find_package(SGCT REQUIRED) -target_include_directories(libOpenSpace SYSTEM PUBLIC ${SGCT_INCLUDE_DIRECTORIES}) -target_link_libraries(libOpenSpace ${SGCT_LIBRARIES}) -if (UNIX AND (NOT APPLE)) - target_link_libraries(libOpenSpace Xcursor Xinerama) -endif () - -# Spice -set(SPICE_ROOT_DIR "${OPENSPACE_EXT_DIR}/spice") -find_package(Spice REQUIRED) -target_include_directories(libOpenSpace SYSTEM PUBLIC ${SPICE_INCLUDE_DIRS}) -target_link_libraries(libOpenSpace ${SPICE_LIBRARIES}) - -# # Kameleon -option(KAMELEON_LIBRARY_ONLY "Build with Kameleon as library only" ON) -if (WIN32) - option(BUILD_SHARED_LIBS "Build Shared Libraries" OFF) -else () - option(BUILD_SHARED_LIBS "Build Shared Libraries" ON) -endif () -mark_as_advanced(BUILD_SHARED_LIBS) # Change to set instead of option -option(KAMELEON_USE_HDF5 "Kameleon use HDF5" OFF) -set(KAMELEON_ROOT_DIR ${OPENSPACE_EXT_DIR}/kameleon) -set(KAMELEON_INCLUDES ${KAMELEON_ROOT_DIR}/src) -set(BOOST_ROOT "${OPENSPACE_EXT_DIR}/ghoul/ext/boost") -add_subdirectory(${KAMELEON_ROOT_DIR}) -target_include_directories(libOpenSpace SYSTEM PUBLIC ${KAMELEON_INCLUDES}) -target_link_libraries(libOpenSpace ccmc) - -# Imgui -add_subdirectory(${OPENSPACE_EXT_DIR}/imgui) -get_property(IMGUI_INCLUDE_DIR TARGET Imgui PROPERTY INTERFACE_INCLUDE_DIRECTORIES) -target_link_libraries(libOpenSpace Imgui) -target_include_directories(libOpenSpace PUBLIC ${IMGUI_INCLUDE_DIR}) - -############################# -# Other applications -############################# option(OPENSPACE_BUILD_GUI_APPLICATIONS "Build GUI Applications" OFF) -if (OPENSPACE_BUILD_GUI_APPLICATIONS) - add_subdirectory(gui) -endif () option(OPENSPACE_HAVE_TESTS "Activate the OpenSpace unit tests" ON) -if (OPENSPACE_HAVE_TESTS) - if (NOT TARGET gtest) - add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul/ext/gtest) - endif () +handle_option_tests() - file(GLOB_RECURSE OPENSPACE_TEST_FILES ${OPENSPACE_BASE_DIR}/tests/*.inl) - - add_executable(OpenSpaceTest ${OPENSPACE_BASE_DIR}/tests/main.cpp ${OPENSPACE_TEST_FILES}) - target_include_directories(OpenSpaceTest PUBLIC - "${OPENSPACE_BASE_DIR}/include" - "${OPENSPACE_BASE_DIR}/tests" - "${OPENSPACE_EXT_DIR}/ghoul/ext/gtest/include" - ) - target_link_libraries(OpenSpaceTest gtest libOpenSpace) - - set_target_properties(OpenSpaceTest PROPERTIES LINK_FLAGS - "/NODEFAULTLIB:LIBCMTD.lib /NODEFAULTLIB:LIBCMT.lib" - ) -endif (OPENSPACE_HAVE_TESTS) - -############################# -# Modules -############################# - -# Get all modules in the correct order based on their dependencies -file(GLOB moduleDirs RELATIVE ${OPENSPACE_MODULE_DIR} ${OPENSPACE_MODULE_DIR}/*) -set(sortedModules ${moduleDirs}) -foreach (dir ${moduleDirs}) - if(IS_DIRECTORY ${OPENSPACE_MODULE_DIR}/${dir}) - set(defaultModule OFF) - if (EXISTS "${OPENSPACE_MODULE_DIR}/${dir}/include.cmake") - unset(OPENSPACE_DEPENDENCIES) - unset(DEFAULT_MODULE) - include(${OPENSPACE_MODULE_DIR}/${dir}/include.cmake) - - if (DEFINED DEFAULT_MODULE) - set(defaultModule ${DEFAULT_MODULE}) - endif () - if (OPENSPACE_DEPENDENCIES) - foreach (dependency ${OPENSPACE_DEPENDENCIES}) - create_library_name(${dependency} library) - if (TARGET ${library}) - # already registered - list(REMOVE_ITEM OPENSPACE_DEPENDENCIES ${dependency}) - endif () - endforeach () - - list(APPEND OPENSPACE_DEPENDENCIES ${dir}) - list(FIND sortedModules ${dir} dir_index) - # if (NOT dir STREQUAL "base") - # list(INSERT OPENSPACE_DEPENDENCIES 0 "base") - # endif () - list(INSERT sortedModules ${dir_index} ${OPENSPACE_DEPENDENCIES}) - list(REMOVE_DUPLICATES sortedModules) - endif () - endif () - create_option_name(${dir} optionName) - option(${optionName} "Build ${dir} Module" ${defaultModule}) - # create_library_name(${module} ${library}) - else () - list(REMOVE_ITEM sortedModules ${dir}) - endif () -endforeach () - -# Automatically set dependent modules to ON -set(dir_list ${sortedModules}) -list(REVERSE dir_list) -foreach (dir ${dir_list}) - create_option_name(${dir} optionName) - if (${optionName}) - if (EXISTS "${OPENSPACE_MODULE_DIR}/${dir}/include.cmake") - unset(OPENSPACE_DEPENDENCIES) - unset(DEFAULT_MODULE) - include(${OPENSPACE_MODULE_DIR}/${dir}/include.cmake) - - if (OPENSPACE_DEPENDENCIES) - foreach (dependency ${OPENSPACE_DEPENDENCIES}) - create_option_name(${dependency} dependencyOptionName) - set(${dependencyOptionName} ON CACHE BOOL "ff" FORCE) - message(STATUS "${dependencyOptionName} was set to build, due to dependency towards ${optionName}") - endforeach () - endif () - endif () - endif () -endforeach () - -set(MODULE_HEADERS "") -set(MODULE_CLASSES "") - -# Add subdirectories in the correct order -foreach (module ${sortedModules}) - create_option_name(${module} optionName) - if (${optionName}) - create_library_name(${module} libraryName) - add_subdirectory(${OPENSPACE_MODULE_DIR}/${module}) - target_link_libraries(libOpenSpace ${libraryName}) - - # Create registration file - string(TOUPPER ${module} module_upper) - unset(MODULE_NAME) - unset(MODULE_PATH) - include(${CMAKE_BINARY_DIR}/modules/${module}/modulename.cmake) - - list(APPEND MODULE_HEADERS - #"#ifdef REGISTRATION_OPENSPACE${module_upper}MODULE\n" - "#include <${MODULE_PATH}>\n" - #"#endif\n\n" - ) - - list(APPEND MODULE_CLASSES - "new ${MODULE_NAME},\n" - ) - - endif () -endforeach () - -function(JOIN VALUES GLUE OUTPUT) - string (REGEX REPLACE "([^\\]|^);" "\\1${GLUE}" _TMP_STR "${VALUES}") - string (REGEX REPLACE "[\\](.)" "\\1" _TMP_STR "${_TMP_STR}") #fixes escaping - set (${OUTPUT} "${_TMP_STR}" PARENT_SCOPE) -endfunction() - -string(REPLACE ";" "" MODULE_HEADERS ${MODULE_HEADERS}) -string(REPLACE ";" "" MODULE_CLASSES ${MODULE_CLASSES}) - -configure_file( - ${OPENSPACE_CMAKE_EXT_DIR}/module_registration.template - ${CMAKE_BINARY_DIR}/_generated/include/openspace/moduleregistration.h -) - - -############################# -# Project Folder structure (where appropriate) -############################# - -macro (os_folder target_name folder_name) - if (TARGET ${target_name}) - set_property(TARGET ${target_name} PROPERTY FOLDER ${folder_name}) - endif () -endmacro () - -os_folder(ccmc "External") -os_folder(cdf "External") -os_folder(gtest "External" "External") -os_folder(Imgui "External") -os_folder(Lua "External") -os_folder(lz4 "External") -os_folder(tinyobjloader "External") - -os_folder(GhoulTest "Unit Tests") -os_folder(OpenSpaceTest "Unit Tests") - -############################# -# Copy dynamic libraries -############################# -if (WIN32) - # Copy DLLs needed by Ghoul into the executable directory - ghl_copy_shared_libraries(OpenSpace ${OPENSPACE_EXT_DIR}/ghoul) - - if (TARGET OpenSpaceTest) - ghl_copy_shared_libraries(OpenSpaceTest ${OPENSPACE_EXT_DIR}/ghoul) - endif () -endif () +handle_internal_modules() +copy_dynamic_libraries() diff --git a/modules/base/CMakeLists.txt b/modules/base/CMakeLists.txt index f4cffdc774..cd2491b6af 100644 --- a/modules/base/CMakeLists.txt +++ b/modules/base/CMakeLists.txt @@ -93,5 +93,6 @@ source_group("Shader Files" FILES ${SHADER_FILES}) create_new_module( "Base" + base_module ${HEADER_FILES} ${SOURCE_FILES} ${SHADER_FILES} ) diff --git a/modules/base/rendering/renderablepath.cpp b/modules/base/rendering/renderablepath.cpp index 5ea998c475..0654ecc36c 100644 --- a/modules/base/rendering/renderablepath.cpp +++ b/modules/base/rendering/renderablepath.cpp @@ -120,7 +120,6 @@ bool RenderablePath::initialize() { bool RenderablePath::deinitialize() { glDeleteVertexArrays(1, &_vaoID); glDeleteBuffers(1, &_vBufferID); - //Renderable::deinitialize(); return true; } diff --git a/modules/base/rendering/renderabletrail.cpp b/modules/base/rendering/renderabletrail.cpp index d1d652a7c2..a6dbd75e5f 100644 --- a/modules/base/rendering/renderabletrail.cpp +++ b/modules/base/rendering/renderabletrail.cpp @@ -125,7 +125,6 @@ bool RenderableTrail::initialize() { bool RenderableTrail::deinitialize() { glDeleteVertexArrays(1, &_vaoID); glDeleteBuffers(1, &_vBufferID); - //Renderable::deinitialize(); return true; } diff --git a/modules/newhorizons/CMakeLists.txt b/modules/newhorizons/CMakeLists.txt index bd0df96ffb..d25a88fd44 100644 --- a/modules/newhorizons/CMakeLists.txt +++ b/modules/newhorizons/CMakeLists.txt @@ -75,5 +75,6 @@ source_group("Shader Files" FILES ${SHADER_FILES}) create_new_module( "NewHorizons" + newhorizons_module ${HEADER_FILES} ${SOURCE_FILES} ${SHADER_FILES} ) diff --git a/modules/volume/CMakeLists.txt b/modules/volume/CMakeLists.txt index 23ce705156..b73a2b40e7 100644 --- a/modules/volume/CMakeLists.txt +++ b/modules/volume/CMakeLists.txt @@ -38,5 +38,6 @@ source_group("Source Files" FILES ${SOURCE_FILES}) create_new_module( "Volume" + volume_module ${HEADER_FILES} ${SOURCE_FILES} ${SHADER_FILES} ) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index be7cfc2c38..96a6c83e8b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -187,154 +187,3 @@ foreach (file ${OPENSPACE_SOURCE} ${OPENSPACE_HEADER}) source_group("" FILES ${original_file}) endif () endforeach () - -# #set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) - -# set(SOURCE_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) -# set(HEADER_ROOT_DIR ${CMAKE_SOURCE_DIR}/include) - -# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${HEADER_ROOT_DIR}/openspace/version.h) - -# file(GLOB CONFIGURATION_SOURCE ${SOURCE_ROOT_DIR}/configuration/*.cpp) -# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${CONFIGURATION_SOURCE}) -# file(GLOB CONFIGURATION_HEADER ${HEADER_ROOT_DIR}/openspace/configuration/*.h) -# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${CONFIGURATION_HEADER}) -# source_group(Configuration FILES ${CONFIGURATION_SOURCE} ${CONFIGURATION_HEADER}) - -# file(GLOB ABUFFER_SOURCE ${SOURCE_ROOT_DIR}/abuffer/*.cpp) -# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${ABUFFER_SOURCE}) -# file(GLOB ABUFFER_HEADER ${HEADER_ROOT_DIR}/openspace/abuffer/*.h) -# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${ABUFFER_HEADER}) -# source_group(ABuffer FILES ${ABUFFER_SOURCE} ${ABUFFER_HEADER}) - -# file(GLOB ENGINE_SOURCE ${SOURCE_ROOT_DIR}/engine/*.cpp) -# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${ENGINE_SOURCE}) -# file(GLOB ENGINE_HEADER ${HEADER_ROOT_DIR}/openspace/engine/*.h) -# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${ENGINE_HEADER}) -# source_group(Engine FILES ${ENGINE_SOURCE} ${ENGINE_HEADER}) - -# file(GLOB NETWORK_SOURCE ${SOURCE_ROOT_DIR}/network/*.cpp) -# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${NETWORK_SOURCE}) -# file(GLOB NETWORK_HEADER ${HEADER_ROOT_DIR}/openspace/network/*.h) -# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${NETWORK_HEADER}) -# source_group(Network FILES ${NETWORK_SOURCE} ${NETWORK_HEADER}) - -# file(GLOB GUI_SOURCE ${SOURCE_ROOT_DIR}/gui/*.cpp) -# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${GUI_SOURCE}) -# file(GLOB GUI_HEADER ${HEADER_ROOT_DIR}/openspace/gui/*.h) -# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${GUI_HEADER}) -# source_group(GUI FILES ${GUI_SOURCE} ${GUI_HEADER}) - -# file(GLOB INTERACTION_SOURCE ${SOURCE_ROOT_DIR}/interaction/*.cpp) -# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${INTERACTION_SOURCE}) -# file(GLOB INTERACTION_HEADER ${HEADER_ROOT_DIR}/openspace/interaction/*.h) -# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${INTERACTION_HEADER}) -# source_group(Interaction FILES ${INTERACTION_SOURCE} ${INTERACTION_HEADER}) - -# file(GLOB INTERACTION_EXTERNALCONTROL_SOURCE ${SOURCE_ROOT_DIR}/interaction/externalcontrol/*.cpp) -# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${INTERACTION_EXTERNALCONTROL_SOURCE}) -# file(GLOB INTERACTION_EXTERNALCONTROL_HEADER ${HEADER_ROOT_DIR}/openspace/interaction/externalcontrol/*.h) -# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${INTERACTION_EXTERNALCONTROL_HEADER}) -# source_group(Interaction\\ExternalControl FILES ${INTERACTION_EXTERNALCONTROL_SOURCE} ${INTERACTION_EXTERNALCONTROL_HEADER}) - -# file(GLOB PROPERTY_SOURCE_CPP ${SOURCE_ROOT_DIR}/properties/*.cpp) -# file(GLOB PROPERTY_SOURCE_C ${SOURCE_ROOT_DIR}/properties/*.c) -# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${PROPERTY_SOURCE_CPP} ${PROPERTY_SOURCE_C}) -# file(GLOB PROPERTY_HEADER ${HEADER_ROOT_DIR}/openspace/properties/*.h ${HEADER_ROOT_DIR}/openspace/properties/*.inl) -# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${PROPERTY_HEADER}) -# source_group(Properties FILES ${PROPERTY_SOURCE_CPP} ${PROPERTY_SOURCE_C} ${PROPERTY_HEADER}) - -# file(GLOB QUERY_SOURCE_CPP ${SOURCE_ROOT_DIR}/query/*.cpp) -# file(GLOB QUERY_SOURCE_C ${SOURCE_ROOT_DIR}/query/*.c) -# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${QUERY_SOURCE_CPP} ${QUERY_SOURCE_C}) -# file(GLOB QUERY_HEADER ${HEADER_ROOT_DIR}/openspace/query/*.h) -# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${QUERY_HEADER}) -# source_group(Query FILES ${QUERY_SOURCE_CPP} ${QUERY_SOURCE_C} ${QUERY_HEADER}) - -# file(GLOB RENDERING_SOURCE ${SOURCE_ROOT_DIR}/rendering/*.cpp) -# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${RENDERING_SOURCE}) -# file(GLOB RENDERING_HEADER ${HEADER_ROOT_DIR}/openspace/rendering/*.h) -# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${RENDERING_HEADER}) -# source_group(Rendering FILES ${RENDERING_SOURCE} ${RENDERING_HEADER}) - -# file(GLOB RENDERING_PLANETS_SOURCE ${SOURCE_ROOT_DIR}/rendering/planets/*.cpp) -# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${RENDERING_PLANETS_SOURCE}) -# file(GLOB RENDERING_PLANETS_HEADER ${HEADER_ROOT_DIR}/openspace/rendering/planets/*.h) -# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${RENDERING_PLANETS_HEADER}) -# source_group(Rendering\\Planets FILES ${RENDERING_PLANETS_SOURCE} ${RENDERING_PLANETS_HEADER}) - -# file(GLOB RENDERING_STARS_SOURCE ${SOURCE_ROOT_DIR}/rendering/stars/*.cpp) -# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${RENDERING_STARS_SOURCE}) -# file(GLOB RENDERING_STARS_HEADER ${HEADER_ROOT_DIR}/openspace/rendering/stars/*.h) -# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${RENDERING_STARS_HEADER}) -# source_group(Rendering\\Stars FILES ${RENDERING_STARS_SOURCE} ${RENDERING_STARS_HEADER}) - -# file(GLOB RENDERING_MODEL_SOURCE ${SOURCE_ROOT_DIR}/rendering/model/*.cpp) -# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${RENDERING_MODEL_SOURCE}) -# file(GLOB RENDERING_MODEL_HEADER ${HEADER_ROOT_DIR}/openspace/rendering/model/*.h) -# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${RENDERING_MODEL_HEADER}) -# source_group(Rendering\\Model FILES ${RENDERING_MODEL_SOURCE} ${RENDERING_MODEL_HEADER}) - -# file(GLOB SCENE_SOURCE ${SOURCE_ROOT_DIR}/scene/*.cpp) -# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${SCENE_SOURCE}) -# file(GLOB SCENE_HEADER ${HEADER_ROOT_DIR}/openspace/scene/*.h ${HEADER_ROOT_DIR}/openspace/scene/*.inl) -# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${SCENE_HEADER}) -# source_group(Scene FILES ${SCENE_SOURCE} ${SCENE_HEADER}) - -# file(GLOB SCRIPTING_SOURCE ${SOURCE_ROOT_DIR}/scripting/*.cpp) -# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${SCRIPTING_SOURCE}) -# file(GLOB SCRIPTING_HEADER ${HEADER_ROOT_DIR}/openspace/scripting/*.h) -# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${SCRIPTING_HEADER}) -# source_group(Scripting FILES ${SCRIPTING_SOURCE} ${SCRIPTING_HEADER}) - -# file(GLOB UTIL_SOURCE ${SOURCE_ROOT_DIR}/util/*.cpp) -# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${UTIL_SOURCE}) -# file(GLOB UTIL_HEADER ${HEADER_ROOT_DIR}/openspace/util/*.h) -# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${UTIL_HEADER}) -# source_group(Util FILES ${UTIL_SOURCE} ${UTIL_HEADER}) - -# file(GLOB FLARE_SOURCE ${SOURCE_ROOT_DIR}/flare/*.cpp) -# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${FLARE_SOURCE}) -# file(GLOB FLARE_HEADER ${HEADER_ROOT_DIR}/openspace/flare/*.h) -# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${FLARE_HEADER}) -# source_group(Flare FILES ${FLARE_SOURCE} ${FLARE_HEADER}) - -# include_directories(${HEADER_ROOT_DIR}) -# include_directories(${GHOUL_ROOT_DIR}/ext/boost) - -# include_directories(${CMAKE_SOURCE_DIR}/ext/imgui) -# set(OPENSPACE_HEADER ${OPENSPACE_HEADER} ${CMAKE_SOURCE_DIR}/ext/imgui/imgui.h) -# set(OPENSPACE_SOURCE ${OPENSPACE_SOURCE} ${CMAKE_SOURCE_DIR}/ext/imgui/imgui.cpp) -# source_group(ext\\imgui FILES ${CMAKE_SOURCE_DIR}/ext/imgui/imgui.h ${CMAKE_SOURCE_DIR}/ext/imgui/imgui.cpp) - - -# set(DEPENDENT_LIBS ${DEPENDENT_LIBS} openspace-module-base openspace-module-newhorizons openspace-module-volume) - -# include_directories("${HEADER_ROOT_DIR}") -# include_directories("${OPENSPACE_BASE_DIR}") - -# add_executable(OpenSpace ${SOURCE_ROOT_DIR}/main.cpp ${OPENSPACE_HEADER} ${OPENSPACE_SOURCE}) -# target_link_libraries(OpenSpace ${DEPENDENT_LIBS}) - -# option(OPENSPACE_HAVE_TESTS "Activate the OpenSpace unit tests" ON) -# if (OPENSPACE_HAVE_TESTS) -# add_definitions(-DOPENSPACE_HAVE_TESTS) - -# set(OPENSPACE_TEST_DIR ${OPENSPACE_BASE_DIR}/tests) - -# include_directories("${GHOUL_ROOT_DIR}/ext/gtest/include") -# include_directories("${GHOUL_ROOT_DIR}/include") -# include_directories("${OPENSPACE_TEST_DIR}") -# file(GLOB_RECURSE OPENSPACE_TEST_FILES ${OPENSPACE_BASE_DIR}/tests/*.inl) - -# source_group(Tests FILES ${OPENSPACE_TEST_FILES}) - -# add_executable(OpenSpaceTest ${OPENSPACE_BASE_DIR}/tests/main.cpp ${OPENSPACE_TEST_FILES} ${OPENSPACE_HEADER} ${OPENSPACE_SOURCE}) -# target_link_libraries(OpenSpaceTest gtest ${DEPENDENT_LIBS}) - -# endif (OPENSPACE_HAVE_TESTS) - -# #if (NOT UNIX) -# #cotire(OpenSpace) -# #endif () -# GhoulCopySharedLibraries(OpenSpace) diff --git a/src/engine/moduleengine.cpp b/src/engine/moduleengine.cpp index 4dafe9d882..b6b541156b 100644 --- a/src/engine/moduleengine.cpp +++ b/src/engine/moduleengine.cpp @@ -28,10 +28,6 @@ #include -#include -#include -#include - #include namespace { diff --git a/support/cmake/module_definition.cmake b/support/cmake/module_definition.cmake index 7554548b25..ac6a2a033f 100644 --- a/support/cmake/module_definition.cmake +++ b/support/cmake/module_definition.cmake @@ -24,28 +24,40 @@ include (${OPENSPACE_CMAKE_EXT_DIR}/module_common.cmake) -function (create_new_module - module_name - # Sources - ) +# Creates a new project and a library for the module with name . The name of +# the library is returned in for outside configuration +# The library will have the name openspace-module- and has all of their +# dependencies set correctly. +# Dependencies will have to be set in a file called "include.cmake" +function (create_new_module module_name output_library_name) set(sources ${ARGN}) project(${module_name}) # Create a library name of the style: openspace-module-${name} create_library_name(${module_name} library_name) - message(STATUS "Configuring module ${MODULE_NAME}: ${library_name}") + message(STATUS "Configuring module ${module_name}: ${library_name}") + # Add the module files to the list of sources add_module_files() + # Create the library add_library(${library_name} ${sources}) + + # Set compile settings that are common to all modules set_common_compile_settings(${library_name}) - set_openspace_includes(${library_name}) + + # Propagate the includes and compiler definitions that are set in the libOpenSpace target + set_openspace_settings(${library_name}) handle_dependencies(${library_name} ${module_name}) write_module_name(${module_name}) + + set(${output_library_name} ${library_name}) endfunction () + + # I couldn't make adding the module files to the ${sources} variable to work with a function ---abock # Adds the module.h and module.cpp files to the list of sources and provides # Them with a source group @@ -59,6 +71,8 @@ macro (add_module_files) list(APPEND sources ${module_files}) endmacro () + + # Set the compiler settings that are common to all modules function (set_common_compile_settings target_name) if (MSVC) @@ -76,8 +90,10 @@ function (set_common_compile_settings target_name) endif () endfunction () + + # Propagate the include directives from the libOpenSpace target into this module -function (set_openspace_includes target_name) +function (set_openspace_settings target_name) get_property( OPENSPACE_INCLUDE_DIR TARGET libOpenSpace @@ -98,15 +114,13 @@ function (set_openspace_includes target_name) target_link_libraries(${target_name} libOpenSpace) endfunction () + + # Loads the dependencies from 'include.cmake' and deals with them function (handle_dependencies target_name module_name) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/include.cmake") include(${CMAKE_CURRENT_SOURCE_DIR}/include.cmake) - # if (NOT ${module_name} STREQUAL "Base") - # list(APPEND OPENSPACE_DEPENDENCIES "base") - # endif () - # Handle OpenSpace dependencies foreach (dep ${OPENSPACE_DEPENDENCIES}) create_library_name(${dep} dep_library) @@ -132,6 +146,8 @@ function (handle_dependencies target_name module_name) endif () endfunction () + + function (write_module_name module_name) string(TOLOWER ${module_name} module_name_lower) @@ -139,4 +155,4 @@ function (write_module_name module_name) "set(MODULE_NAME ${module_name}Module)\n" "set(MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/${module_name_lower}module.h)" ) -endfunction () \ No newline at end of file +endfunction () diff --git a/support/cmake/module_handling.cmake b/support/cmake/module_handling.cmake new file mode 100644 index 0000000000..e410061a3b --- /dev/null +++ b/support/cmake/module_handling.cmake @@ -0,0 +1,24 @@ +######################################################################################### +# # +# OpenSpace # +# # +# Copyright (c) 2014-2015 # +# # +# Permission is hereby granted, free of charge, to any person obtaining a copy of this # +# software and associated documentation files (the "Software"), to deal in the Software # +# without restriction, including without limitation the rights to use, copy, modify, # +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # +# permit persons to whom the Software is furnished to do so, subject to the following # +# conditions: # +# # +# The above copyright notice and this permission notice shall be included in all copies # +# or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF # +# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # +# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # +######################################################################################### + diff --git a/support/cmake/module_registration.template b/support/cmake/module_registration.template index c173417f9b..58d73f69a7 100644 --- a/support/cmake/module_registration.template +++ b/support/cmake/module_registration.template @@ -1,4 +1,6 @@ -// This file has been auto-generated by CMake. Do not change +// This file has been auto-generated by CMake based on +// support/cmake/module_registration.template +// Do not change this file manually #ifndef __MODULE_REGISTRATION_H__ #define __MODULE_REGISTRATION_H__ @@ -6,6 +8,7 @@ @MODULE_HEADERS@ namespace openspace { + std::vector AllModules = { @MODULE_CLASSES@ }; diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake new file mode 100644 index 0000000000..15c93f9a75 --- /dev/null +++ b/support/cmake/support_macros.cmake @@ -0,0 +1,347 @@ +######################################################################################### +# # +# OpenSpace # +# # +# Copyright (c) 2014-2015 # +# # +# 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. # +######################################################################################### + +function (test_compiler_compatibility) + if (MSVC) + if (MSVC_VERSION LESS 1800) + message(FATAL_ERROR "OpenSpace requires at least Visual Studio 2013") + endif () + endif () +endfunction () + + + +macro (cleanup_project) + # Remove MinSizeRel build option + set(CMAKE_CONFIGURATION_TYPES Debug Release RelWithDebInfo CACHE TYPE INTERNAL FORCE) + mark_as_advanced(CMAKE_CONFIGURATION_TYPES) + mark_as_advanced(CMAKE_INSTALL_PREFIX) + + set_property(GLOBAL PROPERTY USE_FOLDERS On) + set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER CMake) +endmacro () + + +macro (set_build_output_directories) + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${OPENSPACE_CMAKE_EXT_DIR}) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OPENSPACE_BASE_DIR}/bin/lib) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OPENSPACE_BASE_DIR}/bin/lib) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OPENSPACE_BASE_DIR}/bin/openspace) +endmacro () + + + +function (configure_openspace_version major_version minor_version patch_version string_version) + set(OPENSPACE_MAJOR_VERSION ${major_version}) + set(OPENSPACE_MINOR_VERSION ${minor_version}) + set(OPENSPACE_PATCH_VERSION ${patch_version}) + set(OPENSPACE_VERSION_STRING ${string_version}) + message(STATUS "Version: ${OPENSPACE_MAJOR_VERSION}.${OPENSPACE_MINOR_VERSION}.${OPENSPACE_PATCH_VERSION} (${OPENSPACE_VERSION_STRING})") + mark_as_advanced(OPENSPACE_MAJOR_VERSION, OPENSPACE_MINOR_VERSION, OPENSPACE_PATCH_VERSION, OPENSPACE_VERSION_STRING) + configure_file(${OPENSPACE_CMAKE_EXT_DIR}/version.template ${CMAKE_BINARY_DIR}/_generated/include/openspace/version.h) +endfunction () + + + +function (create_openspace_targets) + add_library(libOpenSpace STATIC ${OPENSPACE_HEADER} ${OPENSPACE_SOURCE}) + target_include_directories(libOpenSpace PUBLIC ${OPENSPACE_BASE_DIR}/include) + target_include_directories(libOpenSpace PUBLIC ${CMAKE_BINARY_DIR}/_generated/include) + + add_executable(OpenSpace ${OPENSPACE_MAIN}) + target_include_directories(OpenSpace PUBLIC ${OPENSPACE_BASE_DIR}/include) + target_link_libraries(OpenSpace libOpenSpace) +endfunction () + + + +function (set_compile_settings) + if (MSVC) + target_compile_options(libOpenSpace PUBLIC "/MP" "/wd4201" "/wd4127") + if (OPENSPACE_WARNINGS_AS_ERRORS) + target_compile_options(libOpenSpace PUBLIC "/WX") + target_compile_options(OpenSpace PUBLIC "/WX") + endif () + + set_target_properties(OpenSpace PROPERTIES LINK_FLAGS + "/NODEFAULTLIB:LIBCMTD.lib /NODEFAULTLIB:LIBCMT.lib" + ) + elseif (APPLE) + target_compile_definitions(libOpenSpace PUBLIC "__APPLE__") + + include (CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) + CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) + mark_as_advanced(COMPILER_SUPPORTS_CXX11, COMPILER_SUPPORTS_CXX0X) + if (COMPILER_SUPPORTS_CXX11) + target_compile_options(libOpenSpace PUBLIC "-std=c++11") + elseif (COMPILER_SUPPORTS_CXX0X) + target_compile_options(libOpenSpace PUBLIC "-std=c++0x") + else () + message(FATAL_ERROR "Compiler does not have C++11 support") + endif () + + target_compile_options(libOpenSpace PUBLIC"-stdlib=libc++") + + target_include_directories(libOpenSpace "/Developer/Headers/FlatCarbon") + find_library(COREFOUNDATION_LIBRARY CoreFoundation) + find_library(CARBON_LIBRARY Carbon) + find_library(COCOA_LIBRARY Carbon) + find_library(APP_SERVICES_LIBRARY ApplicationServices) + mark_as_advanced(CARBON_LIBRARY COCOA_LIBRARY APP_SERVICES_LIBRARY) + target_link_libraries(libOpenSpace + ${CARBON_LIBRARY} + ${COREFOUNDATION_LIBRARY} + ${COCOA_LIBRARY} + ${APP_SERVICES_LIBRARY} + ) + elseif (UNIX) + include (CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) + CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) + mark_as_advanced(COMPILER_SUPPORTS_CXX11, COMPILER_SUPPORTS_CXX0X) + if (COMPILER_SUPPORTS_CXX11) + target_compile_options(libOpenSpace PUBLIC "-std=c++11") + elseif (COMPILER_SUPPORTS_CXX0X) + target_compile_options(libOpenSpace PUBLIC "-std=c++0x") + else () + message(FATAL_ERROR "Compiler does not have C++11 support") + endif () + + target_compile_definitions(libOpenSpace PUBLIC "-ggdb") + endif () +endfunction () + + + +function (add_external_dependencies) + # Ghoul + add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul) + target_link_libraries(libOpenSpace Ghoul) + get_property(GHOUL_INCLUDE_DIR TARGET Ghoul PROPERTY INTERFACE_INCLUDE_DIRECTORIES) + target_include_directories(libOpenSpace PUBLIC ${GHOUL_INCLUDE_DIR}) + get_property(GHOUL_DEFINITIONS TARGET Ghoul PROPERTY INTERFACE_COMPILE_DEFINITIONS) + target_compile_definitions(libOpenSpace PUBLIC ${GHOUL_DEFINITIONS}) + set_property(TARGET Lua PROPERTY FOLDER "External") + set_property(TARGET lz4 PROPERTY FOLDER "External") + set_property(TARGET tinyobjloader PROPERTY FOLDER "External") + + # # SGCT + find_package(SGCT REQUIRED) + target_include_directories(libOpenSpace SYSTEM PUBLIC ${SGCT_INCLUDE_DIRECTORIES}) + target_link_libraries(libOpenSpace ${SGCT_LIBRARIES}) + if (UNIX AND (NOT APPLE)) + target_link_libraries(libOpenSpace Xcursor Xinerama) + endif () + + # Spice + set(SPICE_ROOT_DIR "${OPENSPACE_EXT_DIR}/spice") + find_package(Spice REQUIRED) + target_include_directories(libOpenSpace SYSTEM PUBLIC ${SPICE_INCLUDE_DIRS}) + target_link_libraries(libOpenSpace ${SPICE_LIBRARIES}) + + # # Kameleon + option(KAMELEON_LIBRARY_ONLY "Build with Kameleon as library only" ON) + if (WIN32) + option(BUILD_SHARED_LIBS "Build Shared Libraries" OFF) + else () + option(BUILD_SHARED_LIBS "Build Shared Libraries" ON) + endif () + mark_as_advanced(BUILD_SHARED_LIBS) # Change to set instead of option + option(KAMELEON_USE_HDF5 "Kameleon use HDF5" OFF) + set(KAMELEON_ROOT_DIR ${OPENSPACE_EXT_DIR}/kameleon) + set(KAMELEON_INCLUDES ${KAMELEON_ROOT_DIR}/src) + set(BOOST_ROOT "${OPENSPACE_EXT_DIR}/ghoul/ext/boost") + add_subdirectory(${KAMELEON_ROOT_DIR}) + target_include_directories(libOpenSpace SYSTEM PUBLIC ${KAMELEON_INCLUDES}) + target_link_libraries(libOpenSpace ccmc) + set_property(TARGET ccmc PROPERTY FOLDER "External") + set_property(TARGET cdf PROPERTY FOLDER "External") + + # Imgui + add_subdirectory(${OPENSPACE_EXT_DIR}/imgui) + get_property(IMGUI_INCLUDE_DIR TARGET Imgui PROPERTY INTERFACE_INCLUDE_DIRECTORIES) + target_link_libraries(libOpenSpace Imgui) + target_include_directories(libOpenSpace PUBLIC ${IMGUI_INCLUDE_DIR}) + set_property(TARGET Imgui PROPERTY FOLDER "External") +endfunction () + + + +function (handle_option_vld) + if (OPENSPACE_ENABLE_VLD) + target_link_libraries(libOpenSpace ${OPENSPACE_EXT_DIR}/vld/lib/vld.lib) + target_include_directories(libOpenSpace PUBLIC ${OPENSPACE_EXT_DIR}/vld) + endif () +endfunction () + + + +function(handle_option_gui) + if (OPENSPACE_BUILD_GUI_APPLICATIONS) + add_subdirectory(gui) + endif () +endfunction () + + + +function (handle_option_tests) + if (OPENSPACE_HAVE_TESTS) + if (NOT TARGET gtest) + add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul/ext/gtest) + endif () + + file(GLOB_RECURSE OPENSPACE_TEST_FILES ${OPENSPACE_BASE_DIR}/tests/*.inl) + + add_executable(OpenSpaceTest ${OPENSPACE_BASE_DIR}/tests/main.cpp ${OPENSPACE_TEST_FILES}) + target_include_directories(OpenSpaceTest PUBLIC + "${OPENSPACE_BASE_DIR}/include" + "${OPENSPACE_BASE_DIR}/tests" + "${OPENSPACE_EXT_DIR}/ghoul/ext/gtest/include" + ) + target_link_libraries(OpenSpaceTest gtest libOpenSpace) + + set_target_properties(OpenSpaceTest PROPERTIES LINK_FLAGS + "/NODEFAULTLIB:LIBCMTD.lib /NODEFAULTLIB:LIBCMT.lib" + set_property(TARGET OpenSpaceTest PROPERTY FOLDER "Unit Tests") + ) + endif (OPENSPACE_HAVE_TESTS) + if (TARGET GhoulTest) + set_property(TARGET GhoulTest PROPERTY FOLDER "Unit Tests") + endif () +endfunction () + + +function (handle_internal_modules) +# Get all modules in the correct order based on their dependencies + file(GLOB moduleDirs RELATIVE ${OPENSPACE_MODULE_DIR} ${OPENSPACE_MODULE_DIR}/*) + set(sortedModules ${moduleDirs}) + foreach (dir ${moduleDirs}) + if(IS_DIRECTORY ${OPENSPACE_MODULE_DIR}/${dir}) + set(defaultModule OFF) + if (EXISTS "${OPENSPACE_MODULE_DIR}/${dir}/include.cmake") + unset(OPENSPACE_DEPENDENCIES) + unset(DEFAULT_MODULE) + include(${OPENSPACE_MODULE_DIR}/${dir}/include.cmake) + + if (DEFINED DEFAULT_MODULE) + set(defaultModule ${DEFAULT_MODULE}) + endif () + if (OPENSPACE_DEPENDENCIES) + foreach (dependency ${OPENSPACE_DEPENDENCIES}) + create_library_name(${dependency} library) + if (TARGET ${library}) + # already registered + list(REMOVE_ITEM OPENSPACE_DEPENDENCIES ${dependency}) + endif () + endforeach () + + list(APPEND OPENSPACE_DEPENDENCIES ${dir}) + list(FIND sortedModules ${dir} dir_index) + # if (NOT dir STREQUAL "base") + # list(INSERT OPENSPACE_DEPENDENCIES 0 "base") + # endif () + list(INSERT sortedModules ${dir_index} ${OPENSPACE_DEPENDENCIES}) + list(REMOVE_DUPLICATES sortedModules) + endif () + endif () + create_option_name(${dir} optionName) + option(${optionName} "Build ${dir} Module" ${defaultModule}) + # create_library_name(${module} ${library}) + else () + list(REMOVE_ITEM sortedModules ${dir}) + endif () + endforeach () + + # Automatically set dependent modules to ON + set(dir_list ${sortedModules}) + list(REVERSE dir_list) + foreach (dir ${dir_list}) + create_option_name(${dir} optionName) + if (${optionName}) + if (EXISTS "${OPENSPACE_MODULE_DIR}/${dir}/include.cmake") + unset(OPENSPACE_DEPENDENCIES) + unset(DEFAULT_MODULE) + include(${OPENSPACE_MODULE_DIR}/${dir}/include.cmake) + + if (OPENSPACE_DEPENDENCIES) + foreach (dependency ${OPENSPACE_DEPENDENCIES}) + create_option_name(${dependency} dependencyOptionName) + set(${dependencyOptionName} ON CACHE BOOL "ff" FORCE) + message(STATUS "${dependencyOptionName} was set to build, due to dependency towards ${optionName}") + endforeach () + endif () + endif () + endif () + endforeach () + + set(MODULE_HEADERS "") + set(MODULE_CLASSES "") + + # Add subdirectories in the correct order + foreach (module ${sortedModules}) + create_option_name(${module} optionName) + if (${optionName}) + create_library_name(${module} libraryName) + add_subdirectory(${OPENSPACE_MODULE_DIR}/${module}) + target_link_libraries(libOpenSpace ${libraryName}) + + # Create registration file + string(TOUPPER ${module} module_upper) + unset(MODULE_NAME) + unset(MODULE_PATH) + include(${CMAKE_BINARY_DIR}/modules/${module}/modulename.cmake) + + list(APPEND MODULE_HEADERS + #"#ifdef REGISTRATION_OPENSPACE${module_upper}MODULE\n" + "#include <${MODULE_PATH}>\n" + #"#endif\n\n" + ) + + list(APPEND MODULE_CLASSES + "new ${MODULE_NAME},\n" + ) + + endif () + endforeach () + + string(REPLACE ";" "" MODULE_HEADERS ${MODULE_HEADERS}) + string(REPLACE ";" "" MODULE_CLASSES ${MODULE_CLASSES}) + + configure_file( + ${OPENSPACE_CMAKE_EXT_DIR}/module_registration.template + ${CMAKE_BINARY_DIR}/_generated/include/openspace/moduleregistration.h + ) +endfunction () + +function (copy_dynamic_libraries) + if (WIN32) + # Copy DLLs needed by Ghoul into the executable directory + ghl_copy_shared_libraries(OpenSpace ${OPENSPACE_EXT_DIR}/ghoul) + + if (TARGET OpenSpaceTest) + ghl_copy_shared_libraries(OpenSpaceTest ${OPENSPACE_EXT_DIR}/ghoul) + endif () + endif () +endfunction () \ No newline at end of file From 433d0ba3a6d9c025b6c02638190dacceed5e5eb5 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 23 May 2015 19:50:03 +0200 Subject: [PATCH 044/329] Fixing CMake for Mac OS --- ext/ghoul | 2 +- include/openspace/util/syncbuffer.h | 1 + src/engine/openspaceengine.cpp | 2 ++ src/gui/gui.cpp | 2 ++ src/interaction/luaconsole.cpp | 1 + src/main.cpp | 1 + src/network/networkengine.cpp | 2 ++ src/util/screenlog.cpp | 1 + support/cmake/module_definition.cmake | 35 +++++++++++++++++++++++++-- support/cmake/support_macros.cmake | 11 ++++++--- 10 files changed, 51 insertions(+), 7 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index b256ae209a..9df5fdae81 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit b256ae209a521dd8c079090f18ec032adab6f8ce +Subproject commit 9df5fdae818d5c2abb9458e4ccf8f4b8c88b308d diff --git a/include/openspace/util/syncbuffer.h b/include/openspace/util/syncbuffer.h index bee0bbd046..48fda29fb8 100644 --- a/include/openspace/util/syncbuffer.h +++ b/include/openspace/util/syncbuffer.h @@ -26,6 +26,7 @@ #define SYNCBUFFER_H #include +#include #include #include diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 426cd568df..da48d4b12c 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -24,6 +24,8 @@ #include +#include + #define SGCT_WINDOWS_INCLUDE #include #include diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 80da9a901f..ae5fb99fc4 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -24,6 +24,8 @@ #include +#include + // This needs to be included first due to Windows.h / winsock2.h complications #define SGCT_WINDOWS_INCLUDE #include diff --git a/src/interaction/luaconsole.cpp b/src/interaction/luaconsole.cpp index 920d7a7f24..66d9afa687 100644 --- a/src/interaction/luaconsole.cpp +++ b/src/interaction/luaconsole.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include diff --git a/src/main.cpp b/src/main.cpp index af5dc9325b..a7c161752e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include sgct::Engine* _sgctEngine; diff --git a/src/network/networkengine.cpp b/src/network/networkengine.cpp index 7a29e38879..2d802e69c9 100644 --- a/src/network/networkengine.cpp +++ b/src/network/networkengine.cpp @@ -31,6 +31,8 @@ #include #include +#include + #include "sgct.h" namespace { diff --git a/src/util/screenlog.cpp b/src/util/screenlog.cpp index 18ff2be4f8..efe454bd1e 100644 --- a/src/util/screenlog.cpp +++ b/src/util/screenlog.cpp @@ -24,6 +24,7 @@ #include +#include #include // sgct::Engine::instance()->getTime() namespace openspace { diff --git a/support/cmake/module_definition.cmake b/support/cmake/module_definition.cmake index ac6a2a033f..a75445cc17 100644 --- a/support/cmake/module_definition.cmake +++ b/support/cmake/module_definition.cmake @@ -41,7 +41,7 @@ function (create_new_module module_name output_library_name) add_module_files() # Create the library - add_library(${library_name} ${sources}) + add_library(${library_name} STATIC ${sources}) # Set compile settings that are common to all modules set_common_compile_settings(${library_name}) @@ -87,7 +87,37 @@ function (set_common_compile_settings target_name) if (OPENSPACE_WARNINGS_AS_ERRORS) target_compile_options(${library_name} PUBLIC "/Wx") endif () - endif () + elseif (APPLE) + target_compile_definitions(${library_name} PUBLIC "__APPLE__") + + include (CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) + CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) + mark_as_advanced(COMPILER_SUPPORTS_CXX11, COMPILER_SUPPORTS_CXX0X) + if (COMPILER_SUPPORTS_CXX11) + target_compile_options(${library_name} PUBLIC "-std=c++11") + elseif (COMPILER_SUPPORTS_CXX0X) + target_compile_options(${library_name} PUBLIC "-std=c++0x") + else () + message(FATAL_ERROR "Compiler does not have C++11 support") + endif () + + target_compile_options(${library_name} PUBLIC "-stdlib=libc++") + elseif (UNIX) + include (CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) + CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) + mark_as_advanced(COMPILER_SUPPORTS_CXX11, COMPILER_SUPPORTS_CXX0X) + if (COMPILER_SUPPORTS_CXX11) + target_compile_options(${library_name} PUBLIC "-std=c++11") + elseif (COMPILER_SUPPORTS_CXX0X) + target_compile_options(${library_name} PUBLIC "-std=c++0x") + else () + message(FATAL_ERROR "Compiler does not have C++11 support") + endif () + + target_compile_definitions(${library_name} PUBLIC "-ggdb") + endif () endfunction () @@ -111,6 +141,7 @@ function (set_openspace_settings target_name) ) target_compile_definitions(${target_name} PUBLIC ${OPENSPACE_DEFINES}) + target_link_libraries(${target_name} Ghoul) target_link_libraries(${target_name} libOpenSpace) endfunction () diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 15c93f9a75..55a1719914 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -67,6 +67,7 @@ endfunction () function (create_openspace_targets) add_library(libOpenSpace STATIC ${OPENSPACE_HEADER} ${OPENSPACE_SOURCE}) target_include_directories(libOpenSpace PUBLIC ${OPENSPACE_BASE_DIR}/include) + target_include_directories(libOpenSpace PUBLIC ${OPENSPACE_BASE_DIR}) target_include_directories(libOpenSpace PUBLIC ${CMAKE_BINARY_DIR}/_generated/include) add_executable(OpenSpace ${OPENSPACE_MAIN}) @@ -102,9 +103,9 @@ function (set_compile_settings) message(FATAL_ERROR "Compiler does not have C++11 support") endif () - target_compile_options(libOpenSpace PUBLIC"-stdlib=libc++") + target_compile_options(libOpenSpace PUBLIC "-stdlib=libc++") - target_include_directories(libOpenSpace "/Developer/Headers/FlatCarbon") + target_include_directories(libOpenSpace PUBLIC "/Developer/Headers/FlatCarbon") find_library(COREFOUNDATION_LIBRARY CoreFoundation) find_library(CARBON_LIBRARY Carbon) find_library(COCOA_LIBRARY Carbon) @@ -177,7 +178,9 @@ function (add_external_dependencies) target_include_directories(libOpenSpace SYSTEM PUBLIC ${KAMELEON_INCLUDES}) target_link_libraries(libOpenSpace ccmc) set_property(TARGET ccmc PROPERTY FOLDER "External") - set_property(TARGET cdf PROPERTY FOLDER "External") + if (TARGET cdf) + set_property(TARGET cdf PROPERTY FOLDER "External") + endif () # Imgui add_subdirectory(${OPENSPACE_EXT_DIR}/imgui) @@ -305,7 +308,7 @@ function (handle_internal_modules) if (${optionName}) create_library_name(${module} libraryName) add_subdirectory(${OPENSPACE_MODULE_DIR}/${module}) - target_link_libraries(libOpenSpace ${libraryName}) + target_link_libraries(OpenSpace ${libraryName}) # Create registration file string(TOUPPER ${module} module_upper) From ef58668b813381fc5c7113afbca42bd3c855e6f9 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 23 May 2015 20:47:06 +0200 Subject: [PATCH 045/329] Making OpenSpace compile and run when the NewHorizons module is not loaded --- modules/newhorizons/newhorizonsmodule.cpp | 5 +++++ src/engine/openspaceengine.cpp | 3 --- src/rendering/renderengine.cpp | 7 +++++-- src/scene/scene.cpp | 6 ++++-- src/scene/scenegraph.cpp | 14 ++++++++++---- 5 files changed, 24 insertions(+), 11 deletions(-) diff --git a/modules/newhorizons/newhorizonsmodule.cpp b/modules/newhorizons/newhorizonsmodule.cpp index 720472deac..4ec0ff04d6 100644 --- a/modules/newhorizons/newhorizonsmodule.cpp +++ b/modules/newhorizons/newhorizonsmodule.cpp @@ -40,6 +40,8 @@ #include #include +#include + namespace openspace { @@ -52,6 +54,9 @@ bool NewHorizonsModule::initialize() { if (!success) return false; + ImageSequencer2::initialize(); + + FactoryManager::ref().addFactory(new ghoul::TemplateFactory); FactoryManager::ref().addFactory(new ghoul::TemplateFactory); diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index da48d4b12c..2534fe497c 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -47,7 +47,6 @@ #include #include #include -#include // testing #include @@ -232,8 +231,6 @@ bool OpenSpaceEngine::create( FileSys.createCacheManager(absPath("${" + ConfigurationManager::KeyCache + "}"), CacheVersion); _engine->_console->initialize(); - ImageSequencer2::initialize(); - // Register the provided shader directories ghoul::opengl::ShaderObject::addIncludePath("${SHADERS}"); diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 2125dd150f..650cc92d4b 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -24,8 +24,9 @@ #include +#ifdef OPENSPACE_MODULE_NEWHORIZONS_ENABLED #include - +#endif #include #include @@ -440,6 +441,7 @@ void RenderEngine::render(const glm::mat4 &projectionMatrix, const glm::mat4 &vi //PrintText(i++, "Cam->origin: (% .15f, % .4f)", pssl[0], pssl[1]); //PrintText(i++, "Scaling: (% .5f, % .5f)", scaling[0], scaling[1]); +#ifdef OPENSPACE_MODULE_NEWHORIZONS_ENABLED if (openspace::ImageSequencer2::ref().isReady()) { double remaining = openspace::ImageSequencer2::ref().getNextCaptureTime() - currentTime; double t = 1.0 - remaining / openspace::ImageSequencer2::ref().getIntervalLength(); @@ -523,7 +525,8 @@ void RenderEngine::render(const glm::mat4 &projectionMatrix, const glm::mat4 &vi } } } - +#endif + #undef PrintText } diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 1a07cd914f..9ac182c09c 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -56,7 +56,7 @@ #include "scene_lua.inl" namespace { - const std::string _loggerCat = "SceneGraph"; + const std::string _loggerCat = "Scene"; const std::string _moduleExtension = ".mod"; const std::string _defaultCommonDirectory = "common"; const std::string _commonModuleToken = "${COMMON_MODULE}"; @@ -281,8 +281,10 @@ bool Scene::loadSceneInternal(const std::string& sceneDescriptionFilePath) { _focus = focus; LDEBUG("Setting camera focus to '" << _focus << "'"); } - else + else { LERROR("Could not find focus object '" << focus << "'"); + _focus = "Root"; + } } } diff --git a/src/scene/scenegraph.cpp b/src/scene/scenegraph.cpp index 1e19bb0534..09f932aa37 100644 --- a/src/scene/scenegraph.cpp +++ b/src/scene/scenegraph.cpp @@ -195,8 +195,9 @@ bool SceneGraph::loadFromFile(const std::string& sceneDescription) { SceneGraphNode* node = SceneGraphNode::createFromDictionary(element); if (node == nullptr) { LERROR("Error loading SceneGraphNode '" << nodeName << "' in module '" << moduleName << "'"); - clear(); - return false; + continue; + //clear(); + //return false; } dependencies[nodeName].push_back(parentName); @@ -254,14 +255,19 @@ bool SceneGraph::loadFromFile(const std::string& sceneDescription) { } } + std::vector nodesToDelete; for (SceneGraphNodeInternal* node : _nodes) { if (!nodeIsDependentOnRoot(node)) { LERROR("Node '" << node->node->name() << "' has no direct connection to Root."); - //clear(); - return false; + nodesToDelete.push_back(node); } } + for (SceneGraphNodeInternal* node : nodesToDelete) { + _nodes.erase(std::find(_nodes.begin(), _nodes.end(), node)); + delete node; + } + bool s = sortTopologically(); if (!s) { LERROR("Topological sort failed"); From d127726e3ce9b9a28f54b87b69f0906750d97606 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 23 May 2015 20:48:49 +0200 Subject: [PATCH 046/329] Automatically declare defines for all included modules --- support/cmake/module_common.cmake | 5 +++++ support/cmake/module_handling.cmake | 24 ------------------------ support/cmake/support_macros.cmake | 2 ++ 3 files changed, 7 insertions(+), 24 deletions(-) delete mode 100644 support/cmake/module_handling.cmake diff --git a/support/cmake/module_common.cmake b/support/cmake/module_common.cmake index 683600e7ca..522d892a7c 100644 --- a/support/cmake/module_common.cmake +++ b/support/cmake/module_common.cmake @@ -31,3 +31,8 @@ function (create_option_name module_name option_name) string(TOUPPER ${module_name} module_name) set(${option_name} "OPENSPACE_MODULE_${module_name}" PARENT_SCOPE) endfunction () + +function (create_define_name module_name define_name) + string(TOUPPER ${module_name} module_name) + set(${define_name} "OPENSPACE_MODULE_${module_name}_ENABLED" PARENT_SCOPE) +endfunction () diff --git a/support/cmake/module_handling.cmake b/support/cmake/module_handling.cmake deleted file mode 100644 index e410061a3b..0000000000 --- a/support/cmake/module_handling.cmake +++ /dev/null @@ -1,24 +0,0 @@ -######################################################################################### -# # -# OpenSpace # -# # -# Copyright (c) 2014-2015 # -# # -# Permission is hereby granted, free of charge, to any person obtaining a copy of this # -# software and associated documentation files (the "Software"), to deal in the Software # -# without restriction, including without limitation the rights to use, copy, modify, # -# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # -# permit persons to whom the Software is furnished to do so, subject to the following # -# conditions: # -# # -# The above copyright notice and this permission notice shall be included in all copies # -# or substantial portions of the Software. # -# # -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # -# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # -# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # -# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF # -# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # -# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -######################################################################################### - diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 55a1719914..bc2ad32338 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -309,6 +309,8 @@ function (handle_internal_modules) create_library_name(${module} libraryName) add_subdirectory(${OPENSPACE_MODULE_DIR}/${module}) target_link_libraries(OpenSpace ${libraryName}) + create_define_name(${module} defineName) + target_compile_definitions(libOpenSpace PUBLIC "${defineName}") # Create registration file string(TOUPPER ${module} module_upper) From 7f390ef8e5d5eb3cd356bd12c0ff9d87a800f5ba Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 23 May 2015 22:20:26 +0200 Subject: [PATCH 047/329] Added option to inhibit all warnings from external projects --- CMakeLists.txt | 12 +++++++----- ext/ghoul | 2 +- support/cmake/support_macros.cmake | 22 ++++++++++++++++++++++ 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 25752717c9..e2d2bd6dec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,19 +41,21 @@ cleanup_project() set_build_output_directories() configure_openspace_version(0 1 0 "prerelease-5") -include(src/CMakeLists.txt) -create_openspace_targets() -set_compile_settings() -add_external_dependencies() - option(OPENSPACE_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF) if (MSVC) option(OPENSPACE_ENABLE_VLD "Enable the Visual Leak Detector" OFF) handle_option_vld() endif () +option(OPENSPACE_DISABLE_EXTERNAL_WARNINGS "Disable warnings in external libraries" ON) option(OPENSPACE_BUILD_GUI_APPLICATIONS "Build GUI Applications" OFF) + +include(src/CMakeLists.txt) +create_openspace_targets() +set_compile_settings() +add_external_dependencies() + option(OPENSPACE_HAVE_TESTS "Activate the OpenSpace unit tests" ON) handle_option_tests() diff --git a/ext/ghoul b/ext/ghoul index 9df5fdae81..12aba7d7dc 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 9df5fdae818d5c2abb9458e4ccf8f4b8c88b308d +Subproject commit 12aba7d7dc13e2cb33d6bf70466ea9d7b24f5704 diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index bc2ad32338..82ba9bcb80 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -177,8 +177,23 @@ function (add_external_dependencies) add_subdirectory(${KAMELEON_ROOT_DIR}) target_include_directories(libOpenSpace SYSTEM PUBLIC ${KAMELEON_INCLUDES}) target_link_libraries(libOpenSpace ccmc) + if (OPENSPACE_DISABLE_EXTERNAL_WARNINGS) + if (MSVC) + target_compile_options(ccmc PUBLIC "/W0" "/MP") + else () + message("Fill me will values ---abock") + endif () + target_compile_definitions(ccmc PUBLIC "_SCL_SECURE_NO_WARNINGS") + endif () set_property(TARGET ccmc PROPERTY FOLDER "External") if (TARGET cdf) + if (OPENSPACE_DISABLE_EXTERNAL_WARNINGS) + if (MSVC) + target_compile_options(cdf PUBLIC "/W0" "/MP") + else () + message("Fill me will values ---abock") + endif () + endif () set_property(TARGET cdf PROPERTY FOLDER "External") endif () @@ -188,6 +203,13 @@ function (add_external_dependencies) target_link_libraries(libOpenSpace Imgui) target_include_directories(libOpenSpace PUBLIC ${IMGUI_INCLUDE_DIR}) set_property(TARGET Imgui PROPERTY FOLDER "External") + if (OPENSPACE_DISABLE_EXTERNAL_WARNINGS) + if (MSVC) + target_compile_options(Imgui PUBLIC "/W0" "/MP") + else () + message("Fill me will values ---abock") + endif () + endif () endfunction () From f1e6ad6f3d8b3d47a2580e9f58057ddedf8fc9d6 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 23 May 2015 22:42:51 +0200 Subject: [PATCH 048/329] Remove more warnings --- include/openspace/gui/gui.h | 2 +- include/openspace/interaction/interactionhandler.h | 4 ++-- src/engine/openspaceengine.cpp | 2 +- src/gui/gui.cpp | 2 +- src/gui/guipropertycomponent.cpp | 4 ++++ src/interaction/interactionhandler.cpp | 8 ++++---- support/cmake/support_macros.cmake | 1 + 7 files changed, 14 insertions(+), 9 deletions(-) diff --git a/include/openspace/gui/gui.h b/include/openspace/gui/gui.h index 73555d488e..a874b35147 100644 --- a/include/openspace/gui/gui.h +++ b/include/openspace/gui/gui.h @@ -50,7 +50,7 @@ public: void deinitializeGL(); bool mouseButtonCallback(int key, int action); - bool mouseWheelCallback(int position); + bool mouseWheelCallback(double position); bool keyCallback(int key, int action); bool charCallback(unsigned int character); diff --git a/include/openspace/interaction/interactionhandler.h b/include/openspace/interaction/interactionhandler.h index ac0995334b..2b56ebe881 100644 --- a/include/openspace/interaction/interactionhandler.h +++ b/include/openspace/interaction/interactionhandler.h @@ -108,8 +108,8 @@ public: void keyboardCallback(int key, int action); void mouseButtonCallback(int button, int action); - void mousePositionCallback(int x, int y); - void mouseScrollWheelCallback(int pos); + void mousePositionCallback(double x, double y); + void mouseScrollWheelCallback(double pos); double deltaTime() const; diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 2534fe497c..6a689f4400 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -650,7 +650,7 @@ void OpenSpaceEngine::keyboardCallback(int key, int action) { return; } - if (key == _console->commandInputButton() && (action == SGCT_PRESS || action == SGCT_REPEAT)) + if (static_cast(key) == _console->commandInputButton() && (action == SGCT_PRESS || action == SGCT_REPEAT)) _console->toggleVisibility(); if (!_console->isVisible()) { diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index ae5fb99fc4..110b80052c 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -294,7 +294,7 @@ bool GUI::mouseButtonCallback(int key, int action) { return consumeEvent; } -bool GUI::mouseWheelCallback(int position) { +bool GUI::mouseWheelCallback(double position) { ImGuiIO& io = ImGui::GetIO(); bool consumeEvent = io.WantCaptureMouse; if (consumeEvent) diff --git a/src/gui/guipropertycomponent.cpp b/src/gui/guipropertycomponent.cpp index 29643fc4ba..e8b34a2cff 100644 --- a/src/gui/guipropertycomponent.cpp +++ b/src/gui/guipropertycomponent.cpp @@ -112,7 +112,11 @@ namespace { static const int bufferSize = 256; static char buffer[bufferSize]; +#ifdef _MSC_VER + strcpy_s(buffer, p->value().length() + 1, p->value().c_str()); +#else strcpy(buffer, p->value().c_str()); +#endif ImGui::InputText((ownerName + "." + name).c_str(), buffer, bufferSize); std::string newValue(buffer); diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index f3d702cb4c..76668e95e0 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -405,15 +405,15 @@ void InteractionHandler::mouseButtonCallback(int button, int action) { _mouseController->button(MouseAction(action), MouseButton(button)); } -void InteractionHandler::mousePositionCallback(int x, int y) { +void InteractionHandler::mousePositionCallback(double x, double y) { if (_mouseController) // TODO Remap screen coordinates to [0,1] - _mouseController->move(float(x), float(y)); + _mouseController->move(static_cast(x), static_cast(y)); } -void InteractionHandler::mouseScrollWheelCallback(int pos) { +void InteractionHandler::mouseScrollWheelCallback(double pos) { if (_mouseController) - _mouseController->scrollWheel(pos); + _mouseController->scrollWheel(static_cast(pos)); } void InteractionHandler::orbit(const float &dx, const float &dy, const float &dz, const float &dist){ diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 82ba9bcb80..c333d1ee29 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -331,6 +331,7 @@ function (handle_internal_modules) create_library_name(${module} libraryName) add_subdirectory(${OPENSPACE_MODULE_DIR}/${module}) target_link_libraries(OpenSpace ${libraryName}) + target_link_libraries(libOpenSpace ${libraryName}) create_define_name(${module} defineName) target_compile_definitions(libOpenSpace PUBLIC "${defineName}") From e9b82d27af1c8d1f80aa2a3e340ca1be82ffdbf9 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 23 May 2015 22:58:04 +0200 Subject: [PATCH 049/329] Fix folder organization of OpenSpaceTest --- support/cmake/support_macros.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index c333d1ee29..990b560b1e 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -249,8 +249,8 @@ function (handle_option_tests) set_target_properties(OpenSpaceTest PROPERTIES LINK_FLAGS "/NODEFAULTLIB:LIBCMTD.lib /NODEFAULTLIB:LIBCMT.lib" - set_property(TARGET OpenSpaceTest PROPERTY FOLDER "Unit Tests") ) + set_property(TARGET OpenSpaceTest PROPERTY FOLDER "Unit Tests") endif (OPENSPACE_HAVE_TESTS) if (TARGET GhoulTest) set_property(TARGET GhoulTest PROPERTY FOLDER "Unit Tests") From fed009519676253e73449a9c7ea83ad2009e9e6c Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 23 May 2015 22:58:36 +0200 Subject: [PATCH 050/329] Updated Imgui to version 1.37 --- ext/imgui/LICENSE | 2 +- ext/imgui/README.md | 100 +- ext/imgui/imconfig.h | 19 +- ext/imgui/imgui.cpp | 9147 +++++++++++++++++++++++++++---------- ext/imgui/imgui.h | 1407 +++--- ext/imgui/stb_image.h | 4744 ------------------- ext/imgui/stb_rect_pack.h | 547 +++ ext/imgui/stb_truetype.h | 2658 +++++++++++ src/gui/gui.cpp | 16 +- 9 files changed, 10765 insertions(+), 7875 deletions(-) delete mode 100644 ext/imgui/stb_image.h create mode 100644 ext/imgui/stb_rect_pack.h create mode 100644 ext/imgui/stb_truetype.h diff --git a/ext/imgui/LICENSE b/ext/imgui/LICENSE index fa8630078c..b28ef22539 100644 --- a/ext/imgui/LICENSE +++ b/ext/imgui/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014 Omar Cornut +Copyright (c) 2014-2015 Omar Cornut and ImGui contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/ext/imgui/README.md b/ext/imgui/README.md index 433f0a6de8..f8ef61d449 100644 --- a/ext/imgui/README.md +++ b/ext/imgui/README.md @@ -1,13 +1,26 @@ ImGui ===== +[![Build Status](https://travis-ci.org/ocornut/imgui.svg?branch=master)](https://travis-ci.org/ocornut/imgui) +[![Coverity Status](https://scan.coverity.com/projects/4720/badge.svg)](https://scan.coverity.com/projects/4720) -ImGui is a bloat-free graphical user interface library for C++. It outputs vertex buffers that you can render in your 3D-pipeline enabled application. It is portable, renderer agnostic and carries minimal amount of dependencies (only 3 files are needed). It is based on an "immediate" graphical user interface paradigm which allows you to build user interfaces with ease. +[![Patreon](https://cloud.githubusercontent.com/assets/8225057/5990484/70413560-a9ab-11e4-8942-1a63607c0b00.png)](http://www.patreon.com/imgui) [![PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=5Q73FPZ9C526U) + +ImGui is a bloat-free graphical user interface library for C++. It outputs vertex buffers that you can render in your 3D-pipeline enabled application. It is portable, renderer agnostic and carries minimal amount of dependencies. It is based on an "immediate" graphical user interface paradigm which allows you to build user interfaces with ease. ImGui is designed to enable fast iteration and allow programmers to create "content creation" or "debug" tools (as opposed to UI for the average end-user). It favors simplicity and productivity toward this goal, and thus lacks certain features normally found in more high-level libraries. ImGui is particularly suited to integration in 3D applications, fullscreen applications, embedded applications, games, or any applications on consoles platforms where operating system features are non-standard. -After ImGui is setup in your engine, you can use it like in this example: +ImGui is self-contained within 6 files that you can easily copy and compile into your application/engine: + + - imgui.cpp + - imgui.h + - imconfig.h (empty by default, user-editable) + - stb_rect_pack.h + - stb_textedit.h + - stb_truetype.h + +Your code passes mouse/keyboard inputs and settings to ImGui (see example applications for more details). After ImGui is setup, you can use it like in this example: ![screenshot of sample code alongside its output with ImGui](/web/code_sample_01.png?raw=true) @@ -15,6 +28,13 @@ ImGui outputs vertex buffers and simple command-lists that you can render in you ImGui allows you create elaborate tools as well as very short-lived ones. On the extreme side of short-liveness: using the Edit&Continue feature of compilers you can add a few widgets to tweaks variables while your application is running, and remove the code a minute later! ImGui is not just for tweaking values. You can use it to trace a running algorithm by just emitting text commands. You can use it along with your own reflection data to browse your dataset live. You can use it to expose the internals of a subsystem in your engine, to create a logger, an inspection tool, a profiler, a debugger, etc. +Demo +---- + +You should be able to build the examples from sources (tested on Windows/Mac/Linux). If you don't, let me know! If you want to have a quick look at the features of ImGui, you can download binaries of the demo app here. +- [imgui-demo-binaries-20150321.zip](http://www.miracleworld.net/imgui/binaries/imgui-demo-binaries-20150321.zip) (Windows binaries, ImGui 1.37 WIP 2015/03/31, 4 executables, 391 KB) + + Gallery ------- @@ -22,10 +42,18 @@ Gallery ![screenshot 2](/web/test_window_02.png?raw=true) ![screenshot 3](/web/test_window_03.png?raw=true) ![screenshot 4](/web/test_window_04.png?raw=true) +![screenshot 4](/web/examples_02.png?raw=true) -UTF-8 is supported for text display and input. Here using M+ font to display Japanese: - -![utf-8 screenshot](/web/utf8_sample_01.png?raw=true) +ImGui can load TTF fonts. UTF-8 is supported for text display and input. Here using Arial Unicode font to display Japanese. Initialize custom font with: +``` +ImGuiIO& io = ImGui::GetIO(); +io.Fonts->AddFontFromFileTTF("ArialUni.ttf", 18.0f, io.Fonts->GetGlyphRangesJapanese()); +``` +For Microsoft IME, pass your HWND to enable IME positioning: +``` +io.ImeWindowHandle = my_hwnd; +``` +![Japanese screenshot](/web/code_sample_01_jp.png?raw=true) References ---------- @@ -39,63 +67,75 @@ The Immediate Mode GUI paradigm may at first appear unusual to some users. This Frequently Asked Question ------------------------- +Where is the documentation? + +- The documentation is at the top of imgui.cpp + effectively imgui.h. +- Example code is in the ImGui::ShowTestWindow() function. It covers most features of ImGui so you can read the code and call the function itself to see its output. +- Standalone example applications using OpenGL/DirectX are provided in the examples/ folder. + How do you use ImGui on a platform that may not have a mouse or keyboard? -I recommend using [Synergy](http://synergy-project.org). With the uSynergy.c micro client running you can seamlessly use your PC input devices from a video game console or a tablet. ImGui was also designed to function with touch inputs if you increase the padding of widgets to compensate for the lack of precision of touch devices, but it is recommended you use a mouse to allow optimising for screen real-estate. +I recommend using [Synergy](http://synergy-project.org) ([sources](https://github.com/synergy/synergy)). In particular, the _src/micro/uSynergy.c_ file contains a small client that you can use on any platform to connect to your host PC. You can seamlessly use your PC input devices from a video game console or a tablet. ImGui allows to increase the hit box of widgets (via the _TouchPadding_ setting) to accomodate a little for the lack of precision of touch inputs, but it is recommended you use a mouse to allow optimising for screen real-estate. I integrated ImGui in my engine and the text or lines are blurry.. -- Try adjusting ImGui::GetIO().PixelCenterOffset to 0.0f or 0.5f. -- In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f). +In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f). Can you create elaborate/serious tools with ImGui? -Yes. I have written data browsers, debuggers, profilers and all sort of non-trivial tools with the library. There's no reason you cannot, and in my experience the simplicity of the API is very empowering. However note that ImGui is programmer centric and the immediate-mode GUI paradigm might requires a bit of adaptation before you can realize its full potential. +Yes. I have written data browsers, debuggers, profilers and all sort of non-trivial tools with the library. In my experience the simplicity of the API is very empowering. However note that ImGui is programmer centric and the immediate-mode GUI paradigm might requires a bit of adaptation before you can realize its full potential. Is ImGui fast? -Down the fundation of its visual design, ImGui is engineered to be fairly performant both in term of CPU and GPU usage. Running elaborate code and creating elaborate UI will of course have a cost but ImGui aims to minimize it. +Down to the fundation of its visual design, ImGui is engineered to be fairly performant both in term of CPU and GPU usage. Running elaborate code and creating elaborate UI will of course have a cost but ImGui aims to minimize it. -Mileage may vary but the following screenshot should give you an idea of the cost of running and rendering UI code (In the case of a trivial demo application like this one, your driver/os setup may be a bottleneck and cause higher variation or throttled framerate. Testing performance as part of a real application is recommended). +Mileage may vary but the following screenshot can give you a rough idea of the cost of running and rendering UI code (In the case of a trivial demo application like this one, your driver/os setup are likely to be the bottleneck. Testing performance as part of a real application is recommended). -![performance screenshot](/web/performance_01_close_up.png?raw=true) +![performance screenshot](/web/performance_01.png?raw=true) -This is showing framerate on my 2011 iMac running Windows 7, OpenGL, AMD Radeon HD 6700M. ([click here for the full-size picture](/web/performance_01.png)). -In contrast, librairies featuring higher-quality rendering and layouting techniques may have a higher resources footprint. +This is showing framerate for the full application loop on my 2011 iMac running Windows 7, OpenGL, AMD Radeon HD 6700M with an optimized executable. In contrast, librairies featuring higher-quality rendering and layouting techniques may have a higher resources footprint. + +If you intend to display large lists of items (say, 1000+) it can be beneficial for your code to perform clipping manually - using helpers such as CalcListClipping() - in order to avoid submitting them to ImGui in the first place. Even though ImGui will discard your clipped items it still needs to calculate their size and that overhead will add up if you have thousands of items. If you can handle clipping and height positionning yourself then browsing a list with millions of items isn't a problem. Can you reskin the look of ImGui? -Yes, you can alter the look of the interface to some degree: changing colors, sizes and padding, font. However, as ImGui is designed and optimised to create debug tools, the amount of skinning you can apply is limited. There is only so much you can stray away from the default look and feel of the interface. The example below uses modified settings to create a very compact UI with different colors: - -![skinning screenshot 1](/web/skinning_sample_01.png?raw=true) +You can alter the look of the interface to some degree: changing colors, sizes, padding, rounding, fonts. However, as ImGui is designed and optimised to create debug tools, the amount of skinning you can apply is limited. There is only so much you can stray away from the default look and feel of the interface. Why using C++ (as opposed to C)? -ImGui takes advantage of a few C++ features for convenience but nothing in the realm of Boost-insanity/quagmire. In particular, function overloading and default parameters are used to make the API easier to use and code more terse. Doing so I believe the API is sitting on a sweet spot and giving up on those features would make the API more cumbersome. Other features such as namespace, constructors and templates (in the case of the ImVector<> class) are also relied on as a convenience but could be removed. +ImGui takes advantage of a few C++ features for convenience but nothing anywhere Boost-insanity/quagmire. In particular, function overloading and default parameters are used to make the API easier to use and code more terse. Doing so I believe the API is sitting on a sweet spot and giving up on those features would make the API more cumbersome. Other features such as namespace, constructors and templates (in the case of the ImVector<> class) are also relied on as a convenience but could be removed. Shall someone wants to use ImGui from another language, it should be possible to wrap ImGui to be used from a raw C API in the future. -Support -------- - -Can you develop features xxxx for ImGui? - -Please use GitHub 'Issues' facilities to suggest and discuss improvements. Your questions are often helpul to the community of users. If you represent an organization and would like specific non-trivial features to be implemented, I am available for hire to work on or with ImGui. +Donate +------ Can I donate to support the development of ImGui? -If you have the mean to help, I have setup a [**Patreon page**](http://www.patreon.com/imgui) to enable me to spend more time on the development of the library. One-off donations are also greatly appreciated. Thanks! +[![Patreon](https://cloud.githubusercontent.com/assets/8225057/5990484/70413560-a9ab-11e4-8942-1a63607c0b00.png)](http://www.patreon.com/imgui) [![PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=5Q73FPZ9C526U) + +I'm currently an independant developer and your contributions are very meaningful to me. I have setup an [**ImGui Patreon page**](http://www.patreon.com/imgui) if you want to donate and enable me to spend more time improving the library. If your company uses ImGui please consider making a contribution. One-off donations are also greatly appreciated (PayPal link above). I am also available for hire to work on or with ImGui. Thanks! Credits ------- -Developed by [Omar Cornut](http://www.miracleworld.net). The library was developed with the support of [Media Molecule](http://www.mediamolecule.com) and first used internally on the game [Tearaway](http://tearaway.mediamolecule.com). +Developed by [Omar Cornut](http://www.miracleworld.net) and every direct or indirect contributors to the GitHub. The early version of this library was developed with the support of [Media Molecule](http://www.mediamolecule.com) and first used internally on the game [Tearaway](http://tearaway.mediamolecule.com). -Embeds [proggy_clean](http://upperbounds.net) font by Tristan Grimmer (MIT license). -Embeds [M+ fonts](http://mplus-fonts.sourceforge.jp/mplus-outline-fonts/index-en.html) font by Coji Morishita (free software license). -Embeds [stb_textedit.h](https://github.com/nothings/stb/) by Sean Barrett. +Embeds [ProggyClean.ttf](http://upperbounds.net) font by Tristan Grimmer (MIT license). -Inspiration, feedback, and testing: Casey Muratori, Atman Binstock, Mikko Mononen, Emmanuel Briney, Stefan Kamoda, Anton Mikhailov, Matt Willis. Thanks! +Embeds [stb_textedit.h, stb_truetype.h, stb_rectpack.h](https://github.com/nothings/stb/) by Sean Barrett (public domain). + +Inspiration, feedback, and testing for early versions: Casey Muratori, Atman Binstock, Mikko Mononen, Emmanuel Briney, Stefan Kamoda, Anton Mikhailov, Matt Willis. And everybody posting feedback, questions and patches on the GitHub. + +ImGui is financially supported on [**Patreon**](http://www.patreon.com/imgui). + +Special supporters +- Jetha Chan, MÄrtiņš Možeiko, Alex Evans, Pastagames, Wild Sheep Studio + +And +- Dale Kim, Michel Courtine, Paul Patrashcu, Rui Figueira + +And other supporters; thanks! License ------- diff --git a/ext/imgui/imconfig.h b/ext/imgui/imconfig.h index 02e70da9aa..24d61148d7 100644 --- a/ext/imgui/imconfig.h +++ b/ext/imgui/imconfig.h @@ -14,8 +14,9 @@ //---- Define assertion handler. Defaults to calling assert(). //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) -//---- Don't implement default clipboard handlers for Windows (so as not to link with OpenClipboard() and others Win32 functions) -//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS +//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows. +//#define IMGUI_API __declspec( dllexport ) +//#define IMGUI_API __declspec( dllimport ) //---- Include imgui_user.inl at the end of imgui.cpp so you can include code that extends ImGui using its private data/functions. //#define IMGUI_INCLUDE_IMGUI_USER_INL @@ -23,7 +24,17 @@ //---- Include imgui_user.h at the end of imgui.h //#define IMGUI_INCLUDE_IMGUI_USER_H -//---- Define implicit cast operators to convert back<>forth from your math types and ImVec2/ImVec4. +//---- Don't implement default handlers for Windows (so as not to link with OpenClipboard() and others Win32 functions) +//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS +//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS + +//---- Don't implement help and test window functionality (ShowUserGuide()/ShowStyleEditor()/ShowTestWindow() methods will be empty) +//#define IMGUI_DISABLE_TEST_WINDOWS + +//---- Implement STB libraries in a namespace to avoid conflicts +//#define IMGUI_STB_NAMESPACE ImStb + +//---- Define constructor and implicit cast operators to convert back<>forth from your math types and ImVec2/ImVec4. /* #define IM_VEC2_CLASS_EXTRA \ ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \ @@ -36,7 +47,7 @@ //---- Freely implement extra functions within the ImGui:: namespace. //---- Declare helpers or widgets implemented in imgui_user.inl or elsewhere, so end-user doesn't need to include multiple files. -//---- e.g. you can create variants of the ImGui::Value() helper for your low-level math types. +//---- e.g. you can create variants of the ImGui::Value() helper for your low-level math types, or your own widgets/helpers. /* namespace ImGui { diff --git a/ext/imgui/imgui.cpp b/ext/imgui/imgui.cpp index 4db1a5b6fb..b405e5293e 100644 --- a/ext/imgui/imgui.cpp +++ b/ext/imgui/imgui.cpp @@ -1,17 +1,23 @@ -// ImGui library v1.18 wip +// ImGui library v1.39 WIP // See ImGui::ShowTestWindow() for sample code. // Read 'Programmer guide' below for notes on how to setup ImGui in your codebase. // Get latest version at https://github.com/ocornut/imgui -// Developed by Omar Cornut and contributors. +// Developed by Omar Cornut and ImGui contributors. /* Index - MISSION STATEMENT - END-USER GUIDE - - PROGRAMMER GUIDE - - API BREAKING CHANGES - - TROUBLESHOOTING & FREQUENTLY ASKED QUESTIONS + - PROGRAMMER GUIDE (read me!) + - API BREAKING CHANGES (read me when you update!) + - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS + - How do I update to a newer version of ImGui? + - Can I have multiple widgets with the same label? Can I have widget without a label? (Yes) + - Why is my text output blurry? + - How can I load a different font than the default? + - How can I load multiple fonts? + - How can I display and input Chinese, Japanese, Korean characters? - ISSUES & TODO-LIST - CODE - SAMPLE CODE @@ -27,21 +33,21 @@ - minimize screen real-estate usage - minimize setup and maintenance - minimize state storage on user side - - portable, minimize dependencies, run on target (consoles, etc.) + - portable, minimize dependencies, run on target (consoles, phones, etc.) - efficient runtime (NB- we do allocate when "growing" content - creating a window / opening a tree node for the first time, etc. - but a typical frame won't allocate anything) - - read about immediate-mode GUI principles @ http://mollyrocket.com/861, http://mollyrocket.com/forums/index.html + - read about immediate-mode gui principles @ http://mollyrocket.com/861, http://mollyrocket.com/forums/index.html Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes: - doesn't look fancy, doesn't animate - limited layout features, intricate layouts are typically crafted in code - - occasionally use statically sized buffers for string manipulations - won't crash, but some long text may be clipped - + - occasionally uses statically sized buffers for string manipulations - won't crash, but some very long pieces of text may be clipped. functions like ImGui::TextUnformatted() don't have such restriction. + END-USER GUIDE ============== - double-click title bar to collapse window - - click upper right corner to close a window, available when 'bool* open' is passed to ImGui::Begin() + - click upper right corner to close a window, available when 'bool* p_opened' is passed to ImGui::Begin() - click and drag on lower right corner to resize window - click and drag on any empty space to move window - double-click/double-tap on lower right corner grip to auto-fit to content @@ -63,171 +69,319 @@ PROGRAMMER GUIDE ================ - - your code creates the UI, if your code doesn't run the UI is gone! == dynamic UI, no construction step, less data retention on your side, no state duplication, less sync, less errors. - - see ImGui::ShowTestWindow() for user-side sample code - - see examples/ folder for standalone sample applications. + - read the FAQ below this section! + - your code creates the UI, if your code doesn't run the UI is gone! == very dynamic UI, no construction/destructions steps, less data retention on your side, no state duplication, less sync, less bugs. + - call and read ImGui::ShowTestWindow() for sample code demonstrating most features. + - see examples/ folder for standalone sample applications. e.g. examples/opengl_example/ + - customization: PushStyleColor()/PushStyleVar() or the style editor to tweak the look of the interface (e.g. if you want a more compact UI or a different color scheme). - getting started: - - initialisation: call ImGui::GetIO() and fill the 'Settings' data. + - initialisation: call ImGui::GetIO() to retrieve the ImGuiIO structure and fill the 'Settings' data. - every frame: 1/ in your mainloop or right after you got your keyboard/mouse info, call ImGui::GetIO() and fill the 'Input' data, then call ImGui::NewFrame(). 2/ use any ImGui function you want between NewFrame() and Render() 3/ ImGui::Render() to render all the accumulated command-lists. it will call your RenderDrawListFn handler that you set in the IO structure. - - all rendering information are stored into command-lists until ImGui::Render() is called. + - all rendering information are stored into command-lists until ImGui::Render() is called. + - ImGui never touches or know about your GPU state. the only function that knows about GPU is the RenderDrawListFn handler that you must provide. - effectively it means you can create widgets at any time in your code, regardless of "update" vs "render" considerations. - refer to the examples applications in the examples/ folder for instruction on how to setup your code. - a typical application skeleton may be: // Application init - // TODO: Fill all settings fields of the io structure ImGuiIO& io = ImGui::GetIO(); io.DisplaySize.x = 1920.0f; io.DisplaySize.y = 1280.0f; io.DeltaTime = 1.0f/60.0f; io.IniFilename = "imgui.ini"; + // TODO: Fill others settings of the io structure + + // Load texture + unsigned char* pixels; + int width, height, bytes_per_pixels; + io.Fonts->GetTexDataAsRGBA32(pixels, &width, &height, &bytes_per_pixels); + // TODO: copy texture to graphics memory. + // TODO: store your texture pointer/identifier in 'io.Fonts->TexID' // Application main loop while (true) { - // 1/ get low-level input + // 1) get low-level input // e.g. on Win32, GetKeyboardState(), or poll your events, etc. - // 2/ TODO: Fill all 'Input' fields of io structure and call NewFrame + // 2) TODO: fill all fields of IO structure and call NewFrame ImGuiIO& io = ImGui::GetIO(); - io.MousePos = ... + io.MousePos = mouse_pos; + io.MouseDown[0] = mouse_button_0; io.KeysDown[i] = ... ImGui::NewFrame(); - // 3/ most of your application code here - you can use any of ImGui::* functions between NewFrame() and Render() calls + // 3) most of your application code here - you can use any of ImGui::* functions at any point in the frame + ImGui::Begin("My window"); + ImGui::Text("Hello, world."); + ImGui::End(); GameUpdate(); GameRender(); - // 4/ render & swap video buffers + // 4) render & swap video buffers ImGui::Render(); // swap video buffer, etc. } - - after calling ImGui::NewFrame() you can read back 'ImGui::GetIO().WantCaptureMouse' and 'ImGui::GetIO().WantCaptureKeyboard' to tell - if ImGui wants to use your inputs. so typically can hide the mouse inputs from the rest of your application if ImGui is using it. - + - after calling ImGui::NewFrame() you can read back 'io.WantCaptureMouse' and 'io.WantCaptureKeyboard' to tell if ImGui + wants to use your inputs. if it does you can discard/hide the inputs from the rest of your application. API BREAKING CHANGES ==================== Occasionally introducing changes that are breaking the API. The breakage are generally minor and easy to fix. Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code. - - - 2014/11/28 (1.17) moved IO.Font*** options to inside the IO.Font-> structure. - - 2014/11/26 (1.17) reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility - - 2014/11/07 (1.15) renamed IsHovered() to IsItemHovered() - - 2014/10/02 (1.14) renamed IMGUI_INCLUDE_IMGUI_USER_CPP to IMGUI_INCLUDE_IMGUI_USER_INL and imgui_user.cpp to imgui_user.inl (more IDE friendly) - - 2014/09/25 (1.13) removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity) - - 2014/09/24 (1.12) renamed SetFontScale() to SetWindowFontScale() - - 2014/09/24 (1.12) moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn - - 2014/08/30 (1.09) removed IO.FontHeight (now computed automatically) - - 2014/08/30 (1.09) moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite - - 2014/08/28 (1.09) changed the behavior of IO.PixelCenterOffset following various rendering fixes + + - 2015/05/03 (1.39) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same). + - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function (will obsolete). + - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API + - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive. + - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead. + - 2015/03/17 (1.36) - renamed GetItemRectMin()/GetItemRectMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function (will obsolete). + - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing + - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function (will obsolete). + - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth + - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function (will obsolete). + - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once. + - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now. + - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior + - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing() + - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused) + - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions. + - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader. + (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels. + this sequence: + const void* png_data; + unsigned int png_size; + ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); + // + became: + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); + // + io.Fonts->TexID = (your_texture_identifier); + you now have much more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs. + it is now recommended your sample the font texture with bilinear interpolation. + (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID. + (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix) + (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets + - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver) + - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph) + - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility + - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered() + - 2014/10/02 (1.14) - renamed IMGUI_INCLUDE_IMGUI_USER_CPP to IMGUI_INCLUDE_IMGUI_USER_INL and imgui_user.cpp to imgui_user.inl (more IDE friendly) + - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity) + - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale() + - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn + - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically) + - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite + - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes - TROUBLESHOOTING & FREQUENTLY ASKED QUESTIONS - ============================================ + FREQUENTLY ASKED QUESTIONS (FAQ), TIPS + ====================================== - If text or lines are blurry when integrating ImGui in your engine: - - in your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f) - - try adjusting ImGui::GetIO().PixelCenterOffset to 0.5f or 0.375f + Q: How do I update to a newer version of ImGui? + A: Overwrite the following files: + imgui.cpp + imgui.h + stb_rect_pack.h + stb_textedit.h + stb_truetype.h + Check the "API BREAKING CHANGES" sections for a list of occasional API breaking changes. If you have a problem with a function, search for its name + in the code, there will likely be a comment about it. Please report any issue to the GitHub page! - If you are confused about the meaning or use of ID in ImGui: - - some widgets requires state to be carried over multiple frames (most typically ImGui often wants remember what is the "active" widget). - to do so they need an unique ID. unique ID are typically derived from a string label, an indice or a pointer. - when you call Button("OK") the button shows "OK" and also use "OK" as an ID. - - ID are uniquely scoped within Windows so no conflict can happen if you have two buttons called "OK" in two different Windows. - within a same Window, use PushID() / PopID() to easily create scopes and avoid ID conflicts. - so if you have a loop creating "multiple" items, you can use PushID() / PopID() with the index of each item, or their pointer, etc. - some functions like TreeNode() implicitly creates a scope for you by calling PushID() - - when dealing with trees, ID are important because you want to preserve the opened/closed state of tree nodes. - depending on your use cases you may want to use strings, indices or pointers as ID. experiment and see what makes more sense! - e.g. When displaying a single object, using a static string as ID will preserve your node open/closed state when the targeted object change - e.g. When displaying a list of objects, using indices or pointers as ID will preserve the node open/closed state per object - - when passing a label you can optionally specify extra unique ID information within the same string using "##". This helps solving the simpler collision cases. - e.g. "Label" display "Label" and uses "Label" as ID - e.g. "Label##Foobar" display "Label" and uses "Label##Foobar" as ID - e.g. "##Foobar" display an empty label and uses "##Foobar" as ID - - read articles about the imgui principles (see web links) to understand the requirement and use of ID. + Q: Can I have multiple widgets with the same label? Can I have widget without a label? (Yes) + A: Yes. A primer on the use of labels/IDs in ImGui.. + + - Elements that are not clickable, such as Text() items don't need an ID. - If you want to use a different font than the default: - - read extra_fonts/README.txt for instructions. Examples fonts are also provided. - - if you can only see text but no solid shapes or lines, make sure io.Font->TexUvForWhite is set to the texture coordinates of a pure white pixel in your texture! + - Interactive widgets require state to be carried over multiple frames (most typically ImGui often needs to remember what is the "active" widget). + to do so they need an unique ID. unique ID are typically derived from a string label, an integer index or a pointer. + + Button("OK"); // Label = "OK", ID = hash of "OK" + Button("Cancel"); // Label = "Cancel", ID = hash of "Cancel" - If you want to input Japanese/Chinese/Korean in the text input widget: - - make sure you are using a font that can display the glyphs you want (see above paragraph about fonts) - - to have the Microsoft IME cursor appears at the right location in the screen, setup a handler for the io.ImeSetInputScreenPosFn function: + - ID are uniquely scoped within windows, tree nodes, etc. so no conflict can happen if you have two buttons called "OK" in two different windows + or in two different locations of a tree. - #include - #include - static void ImImpl_ImeSetInputScreenPosFn(int x, int y) - { - // Notify OS Input Method Editor of text input position - HWND hwnd = glfwGetWin32Window(window); - if (HIMC himc = ImmGetContext(hwnd)) - { - COMPOSITIONFORM cf; - cf.ptCurrentPos.x = x; - cf.ptCurrentPos.y = y; - cf.dwStyle = CFS_FORCE_POSITION; - ImmSetCompositionWindow(himc, &cf); - } - } + - if you have a same ID twice in the same location, you'll have a conflict: - // Set pointer to handler in ImGuiIO structure - io.ImeSetInputScreenPosFn = ImImpl_ImeSetInputScreenPosFn; + Button("OK"); + Button("OK"); // ID collision! Both buttons will be treated as the same. - - tip: the construct 'IMGUI_ONCE_UPON_A_FRAME { ... }' will evaluate to a block of code only once a frame. You can use it to quickly add custom UI in the middle of a deep nested inner loop in your code. - - tip: you can call Render() multiple times (e.g for VR renders), up to you to communicate the extra state to your RenderDrawListFn function. + Fear not! this is easy to solve and there are many ways to solve it! + + - when passing a label you can optionally specify extra unique ID information within string itself. This helps solving the simpler collision cases. + use "##" to pass a complement to the ID that won't be visible to the end-user: + + Button("Play##0"); // Label = "Play", ID = hash of "Play##0" + Button("Play##1"); // Label = "Play", ID = hash of "Play##1" (different from above) + + - so if you want to hide the label but need an ID: + + Checkbox("##On", &b); // Label = "", ID = hash of "##On" + + - occasionally (rarely) you might want change a label while preserving a constant ID. This allows you to animate labels. + use "###" to pass a label that isn't part of ID: + + Button("Hello###ID"; // Label = "Hello", ID = hash of "ID" + Button("World###ID"; // Label = "World", ID = hash of "ID" (same as above) + + - use PushID() / PopID() to create scopes and avoid ID conflicts within the same Window. + this is the most convenient way of distinguish ID if you are iterating and creating many UI elements. + you can push a pointer, a string or an integer value. remember that ID are formed from the addition of everything in the ID stack! + + for (int i = 0; i < 100; i++) + { + PushID(i); + Button("Click"); // Label = "Click", ID = hash of integer + "label" (unique) + PopID(); + } + + for (int i = 0; i < 100; i++) + { + MyObject* obj = Objects[i]; + PushID(obj); + Button("Click"); // Label = "Click", ID = hash of pointer + "label" (unique) + PopID(); + } + + for (int i = 0; i < 100; i++) + { + MyObject* obj = Objects[i]; + PushID(obj->Name); + Button("Click"); // Label = "Click", ID = hash of string + "label" (unique) + PopID(); + } + + - more example showing that you can stack multiple prefixes into the ID stack: + + Button("Click"); // Label = "Click", ID = hash of "Click" + PushID("node"); + Button("Click"); // Label = "Click", ID = hash of "node" + "Click" + PushID(my_ptr); + Button("Click"); // Label = "Click", ID = hash of "node" + ptr + "Click" + PopID(); + PopID(); + + - tree nodes implicitly creates a scope for you by calling PushID(). + + Button("Click"); // Label = "Click", ID = hash of "Click" + if (TreeNode("node")) + { + Button("Click"); // Label = "Click", ID = hash of "node" + "Click" + TreePop(); + } + + - when working with trees, ID are used to preserve the opened/closed state of each tree node. + depending on your use cases you may want to use strings, indices or pointers as ID. + e.g. when displaying a single object that may change over time (1-1 relationship), using a static string as ID will preserve your node open/closed state when the targeted object change. + e.g. when displaying a list of objects, using indices or pointers as ID will preserve the node open/closed state differently. experiment and see what makes more sense! + + Q: Why is my text output blurry? + A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f) + + Q: How can I load a different font than the default? (default is an embedded version of ProggyClean.ttf, rendered at size 13) + A: Use the font atlas to load the TTF file you want: + + io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels); + io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8() + + Q: How can I load multiple fonts? + A: Use the font atlas to pack them into a single texture: + + ImFont* font0 = io.Fonts->AddFontDefault(); + ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels); + ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels); + io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8() + // the first loaded font gets used by default + // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime + + Q: How can I render and input Chinese, Japanese, Korean characters? + A: When loading a font, pass custom Unicode ranges to specify the glyphs to load. ImGui will support UTF-8 encoding across the board. + Character input depends on you passing the right character code to io.AddInputCharacter(). The example applications do that. + + io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, io.Fonts->GetGlyphRangesJapanese()); // Load Japanese characters + io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8() + io.ImeWindowHandle = MY_HWND; // To input using Microsoft IME, give ImGui the hwnd of your application + + - tip: the construct 'IMGUI_ONCE_UPON_A_FRAME { ... }' will run the block of code only once a frame. You can use it to quickly add custom UI in the middle of a deep nested inner loop in your code. - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug" - - tip: read the ShowTestWindow() code for more example of how to use ImGui! + - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window. + - tip: you can call Render() multiple times (e.g for VR renders). + - tip: call and read the ShowTestWindow() code for more example of how to use ImGui! ISSUES & TODO-LIST ================== - - misc: merge or clarify ImVec4 / ImGuiAabb, they are essentially duplicate containers - - window: autofit is losing its purpose when user relies on any dynamic layout (window width multiplier, column). maybe just clearly discard autofit? + - misc: merge or clarify ImVec4 vs ImRect? - window: add horizontal scroll - window: fix resize grip rendering scaling along with Rounding style setting - - window: better helpers to set pos/size/collapsed with different options (first-run, session only, current value) (github issue #89) - - widgets: switching from "widget-label" to "label-widget" would make it more convenient to integrate widgets in trees - - widgets: clip text? hover clipped text shows it in a tooltip or in-place overlay - - main: make IsHovered() more consistent for various type of widgets, widgets with multiple components, etc. also effectively IsHovered() region sometimes differs from hot region, e.g tree nodes - - main: make IsHovered() info stored in a stack? so that 'if TreeNode() { Text; TreePop; } if IsHovered' return the hover state of the TreeNode? - - scrollbar: use relative mouse movement when first-clicking inside of scroll grab box. - - scrollbar: make the grab visible and a minimum size for long scroll regions -!- input number: very large int not reliably supported because of int<>float conversions. + - window: autofit feedback loop when user relies on any dynamic layout (window width multiplier, column). maybe just clearly drop manual autofit? + - window: add a way for very transient windows (non-saved, temporary overlay over hundreds of objects) to "clean" up from the global window list. + - window: allow resizing of child windows (possibly given min/max for each axis?) + - window: background options for child windows, border option (disable rounding) + - window: resizing from any sides? + mouse cursor directives for app. + - widgets: display mode: widget-label, label-widget (aligned on column or using fixed size), label-newline-tab-widget etc. + - widgets: clean up widgets internal toward exposing everything. + - main: considering adding EndFrame()/Init(). some constructs are awkward in the implementation because of the lack of them. + - main: IsItemHovered() make it more consistent for various type of widgets, widgets with multiple components, etc. also effectively IsHovered() region sometimes differs from hot region, e.g tree nodes + - main: IsItemHovered() info stored in a stack? so that 'if TreeNode() { Text; TreePop; } if IsHovered' return the hover state of the TreeNode? + - input text: add ImGuiInputTextFlags_EnterToApply? (off github issue #218) +!- input number: large int not reliably supported because of int<>float conversions. - input number: optional range min/max for Input*() functions - - input number: holding [-]/[+] buttons should increase the step non-linearly + - input number: holding [-]/[+] buttons could increase the step speed non-linearly (or user-controlled) - input number: use mouse wheel to step up/down - - layout: clean up the InputFloatN/SliderFloatN/ColorEdit4 horrible layout code. item width should include frame padding, then we can have a generic horizontal layout helper. - - columns: declare column set (each column: fixed size, %, fill, distribute default size among fills) - - columns: columns header to act as button (~sort op) and allow resize/reorder - - columns: user specify columns size + - input number: non-decimal input. + - text: proper alignment options + - layout: horizontal layout helper (github issue #97) + - layout: more generic alignment state (left/right/centered) for single items? + - layout: clean up the InputFloatN/SliderFloatN/ColorEdit4 layout code. item width should include frame padding. + - columns: separator function or parameter that works within the column (currently Separator() bypass all columns) (github issue #125) + - columns: declare column set (each column: fixed size, %, fill, distribute default size among fills) (github issue #125) + - columns: columns header to act as button (~sort op) and allow resize/reorder (github issue #125) + - columns: user specify columns size (github issue #125) + - popup: border options. richer api like BeginChild() perhaps? (github issue #197) + - combo: sparse combo boxes (via function call?) - combo: turn child handling code into pop up helper - - list selection, concept of a selectable "block" (that can be multiple widgets) - - menubar, menus - - plot: make it easier for user to draw into the graph (e.g: draw basis, highlight certain points, 2d plots, multiple plots) - - plot: "smooth" automatic scale, user give an input 0.0(full user scale) 1.0(full derived from value) + - combo: contents should extends to fit label if combo widget is small + - combo/listbox: keyboard control. need inputtext like non-active focus + key handling. considering keybord for custom listbox (see github pr #203) + - listbox: multiple selection + - listbox: user may want to initial scroll to focus on the one selected value? + ! menubar, menus (github issue #126) + - tabs + - gauge: various forms of gauge/loading bars widgets + - color: better color editor. + - plot: make it easier for user to draw extra stuff into the graph (e.g: draw basis, highlight certain points, 2d plots, multiple plots) + - plot: "smooth" automatic scale over time, user give an input 0.0(full user scale) 1.0(full derived from value) - plot: add a helper e.g. Plot(char* label, float value, float time_span=2.0f) that stores values and Plot them for you - probably another function name. and/or automatically allow to plot ANY displayed value (more reliance on stable ID) - - file selection widget -> build the tool in our codebase to improve model-dialog idioms (may or not lead to ImGui changes) + - file selection widget -> build the tool in our codebase to improve model-dialog idioms - slider: allow using the [-]/[+] buttons used by InputFloat()/InputInt() - - slider: initial absolute click is imprecise. change to relative movement slider? hide mouse cursor, allow more precise input using less screen-space. - - text edit: clean up the horrible mess caused by converting UTF-8 <> wchar - - text edit: centered text for slider or input text to it matches typical positioning. + - slider: initial absolute click is imprecise. change to relative movement slider (same as scrollbar). + - slider: add dragging-based widgets to edit values with mouse (on 2 axises), saving screen real-estate. + - text edit: clean up the mess caused by converting UTF-8 <> wchar. the code is rather inefficient right now. + - text edit: centered text for slider as input text so it matches typical positioning. - text edit: flag to disable live update of the user buffer. - text edit: field resize behavior - field could stretch when being edited? hover tooltip shows more text? - - text edit: pasting text into a number box should filter the characters the same way direct input does - text edit: add multi-line text edit + - tree: add treenode/treepush int variants? because (void*) cast from int warns on some platforms/settings - settings: write more decent code to allow saving/loading new fields - - settings: api for per-tool simple persistent data (bool,int,float) in .ini file + - settings: api for per-tool simple persistent data (bool,int,float,columns sizes,etc.) in .ini file + ! style: store rounded corners in texture to use 1 quad per corner (filled and wireframe). so rounding have minor cost. + - style: checkbox: padding for "active" color should be a multiplier of the + - style: colorbox not always square? + - text: simple markup language for color change? + - log: LogButtons() options for specifying depth and/or hiding depth slider - log: have more control over the log scope (e.g. stop logging when leaving current tree node scope) - - log: be able to right-click and log a window or tree-node into tty/file/clipboard? + - log: be able to log anything (e.g. right-click on a window/tree-node, shows context menu? log into tty/file/clipboard) + - log: let user copy any window content to clipboard easily (CTRL+C on windows? while moving it? context menu?). code is commented because it fails with multiple Begin/End pairs. - filters: set a current filter that tree node can automatically query to hide themselves - filters: handle wildcards (with implicit leading/trailing *), regexps - shortcuts: add a shortcut api, e.g. parse "&Save" and/or "Save (CTRL+S)", pass in to widgets or provide simple ways to use (button=activate, input=focus) @@ -235,23 +389,16 @@ - keyboard: full keyboard navigation and focus. - input: rework IO to be able to pass actual events to fix temporal aliasing issues. - input: support track pad style scrolling & slider edit. - - tooltip: move to fit within screen (e.g. when mouse cursor is right of the screen). - - clipboard: automatically transform \n into \n\r or equivalent for higher compatibility on windows - portability: big-endian test/support (github issue #81) - - misc: provide a way to compile out the entire implementation while providing a dummy API (e.g. #define IMGUI_DUMMY_IMPL - - misc: not thread-safe + - misc: mark printf compiler attributes on relevant functions + - misc: provide a way to compile out the entire implementation while providing a dummy API (e.g. #define IMGUI_DUMMY_IMPL) - misc: double-clicking on title bar to minimize isn't consistent, perhaps move to single-click on left-most collapse icon? - - misc: CalcTextSize() could benefit from having 'hide_text_after_double_hash' false by default for external use? - - style editor: add a button to output C code. - - examples: integrate dx11 example. - - examples: integrate opengl 3/4 programmable pipeline example. - - optimization/render: use indexed rendering - - optimization/render: move clip-rect to vertex data? would allow merging all commands + - style editor: have a more global HSV setter (e.g. alter hue on all elements). consider replacing active/hovered by offset in HSV space? + - style editor: color child window height expressed in multiple of line height. + - optimization/render: use indexed rendering to reduce vertex data cost (e.g. for remote/networked imgui) - optimization/render: merge command-lists with same clip-rect into one even if they aren't sequential? (as long as in-between clip rectangle don't overlap)? - - optimization/render: font exported by bmfont is not tight fit on vertical axis, incur unneeded pixel-shading cost. - optimization: turn some the various stack vectors into statically-sized arrays - optimization: better clipping for multi-component widgets - - optimization: specialize for height based clipping first (assume widgets never go up + height tests before width tests?) */ #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) @@ -259,56 +406,156 @@ #endif #include "imgui.h" -#include // toupper -#include // sqrtf -#include // intptr_t -#include // vsnprintf -#include // memset +#include // toupper, isprint +#include // sqrtf, fabsf, fmodf, powf, cosf, sinf, floorf, ceilf +#include // vsnprintf, sscanf #include // new (ptr) +#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier +#include // intptr_t +#else +#include // intptr_t +#endif #ifdef _MSC_VER +#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen #endif +// Clang warnings with -Weverything #ifdef __clang__ -#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse and not scary looking. +#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse. #pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants ok. -#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning : format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code, thank you. +#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning : format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code. #pragma clang diagnostic ignored "-Wexit-time-destructors" // warning : declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals. #pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it. +#pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness // +#pragma clang diagnostic ignored "-Wmissing-noreturn" // warning : function xx could be declared with attribute 'noreturn' warning // GetDefaultFontData() asserts which some implementation makes it never return. +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used +#endif + +//------------------------------------------------------------------------- +// STB libraries implementation +//------------------------------------------------------------------------- + +struct ImGuiTextEditState; + +//#define IMGUI_STB_NAMESPACE ImStb +//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION +//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION + +#ifdef IMGUI_STB_NAMESPACE +namespace IMGUI_STB_NAMESPACE +{ +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#endif + +#define STBRP_ASSERT(x) IM_ASSERT(x) +#ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION +#define STBRP_STATIC +#define STB_RECT_PACK_IMPLEMENTATION +#endif +#include "stb_rect_pack.h" + +#define STBTT_malloc(x,u) ((void)(u), ImGui::MemAlloc(x)) +#define STBTT_free(x,u) ((void)(u), ImGui::MemFree(x)) +#define STBTT_assert(x) IM_ASSERT(x) +#ifndef IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION +#define STBTT_STATIC +#define STB_TRUETYPE_IMPLEMENTATION +#endif +#include "stb_truetype.h" + +#undef STB_TEXTEDIT_STRING +#undef STB_TEXTEDIT_CHARTYPE +#define STB_TEXTEDIT_STRING ImGuiTextEditState +#define STB_TEXTEDIT_CHARTYPE ImWchar +#include "stb_textedit.h" + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#ifdef IMGUI_STB_NAMESPACE +} // namespace ImStb +using namespace IMGUI_STB_NAMESPACE; #endif //------------------------------------------------------------------------- // Forward Declarations //------------------------------------------------------------------------- -static bool ButtonBehaviour(const ImGuiAabb& bb, const ImGuiID& id, bool* out_hovered, bool* out_held, bool allow_key_modifiers, bool repeat = false); +struct ImRect; +struct ImGuiColMod; +struct ImGuiStyleMod; +struct ImGuiDrawContext; +struct ImGuiTextEditState; +struct ImGuiIniData; +struct ImGuiState; +struct ImGuiWindow; +typedef int ImGuiButtonFlags; // enum ImGuiButtonFlags_ + +static bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, bool allow_key_modifiers, ImGuiButtonFlags flags = 0); static void LogText(const ImVec2& ref_pos, const char* text, const char* text_end = NULL); -static void RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true, float wrap_width = 0.0f); +static void RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true); +static void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width); +static void RenderTextClipped(ImVec2 pos, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& pos_max, const ImVec2* clip_max = NULL, ImGuiAlign align = ImGuiAlign_Default); static void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f); -static void RenderCollapseTriangle(ImVec2 p_min, bool open, float scale = 1.0f, bool shadow = false); +static void RenderCollapseTriangle(ImVec2 p_min, bool opened, float scale = 1.0f, bool shadow = false); +static void RenderCheckMark(ImVec2 pos, ImU32 col); -static void ItemSize(ImVec2 size, ImVec2* adjust_start_offset = NULL); -static void ItemSize(const ImGuiAabb& aabb, ImVec2* adjust_start_offset = NULL); +static void SetFont(ImFont* font); +static bool ItemAdd(const ImRect& bb, const ImGuiID* id); +static void ItemSize(ImVec2 size, float text_offset_y = 0.0f); +static void ItemSize(const ImRect& bb, float text_offset_y = 0.0f); static void PushColumnClipRect(int column_index = -1); -static bool IsClipped(const ImGuiAabb& aabb); -static bool ClipAdvance(const ImGuiAabb& aabb); +static bool IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when_logged); -static bool IsMouseHoveringBox(const ImGuiAabb& box); +static bool IsMouseHoveringRect(const ImRect& bb); static bool IsKeyPressedMap(ImGuiKey key, bool repeat = true); -static bool CloseWindowButton(bool* open = NULL); +static void Scrollbar(ImGuiWindow* window); +static bool CloseWindowButton(bool* p_opened = NULL); static void FocusWindow(ImGuiWindow* window); -static ImGuiWindow* FindWindow(const char* name); static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs); +// Helpers: String +static int ImStricmp(const char* str1, const char* str2); +static int ImStrnicmp(const char* str1, const char* str2, int count); +static char* ImStrdup(const char *str); +static size_t ImStrlenW(const ImWchar* str); +static const char* ImStristr(const char* haystack, const char* needle, const char* needle_end); +static size_t ImFormatString(char* buf, size_t buf_size, const char* fmt, ...); +static size_t ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args); + +// Helpers: Misc +static ImU32 ImHash(const void* data, size_t data_size, ImU32 seed); +static bool ImLoadFileToMemory(const char* filename, const char* file_open_mode, void** out_file_data, size_t* out_file_size, size_t padding_bytes = 0); +static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } +static inline bool ImCharIsSpace(int c) { return c == ' ' || c == '\t' || c == 0x3000; } + +// Helpers: UTF-8 <> wchar +static int ImTextCharToUtf8(char* buf, size_t buf_size, unsigned int in_char); // return output UTF-8 bytes count +static ptrdiff_t ImTextStrToUtf8(char* buf, size_t buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count +static int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // return input UTF-8 bytes count +static ptrdiff_t ImTextStrFromUtf8(ImWchar* buf, size_t buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL); // return input UTF-8 bytes count +static int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count) +static int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string as UTF-8 code-points + //----------------------------------------------------------------------------- // Platform dependent default implementations //----------------------------------------------------------------------------- static const char* GetClipboardTextFn_DefaultImpl(); static void SetClipboardTextFn_DefaultImpl(const char* text); +static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y); //----------------------------------------------------------------------------- // User facing structures @@ -318,23 +565,32 @@ ImGuiStyle::ImGuiStyle() { Alpha = 1.0f; // Global alpha applies to everything in ImGui WindowPadding = ImVec2(8,8); // Padding within a window - WindowMinSize = ImVec2(48,48); // Minimum window size - FramePadding = ImVec2(5,4); // Padding within a framed rectangle (used by most widgets) - ItemSpacing = ImVec2(10,5); // Horizontal and vertical spacing between widgets/lines - ItemInnerSpacing = ImVec2(5,5); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label) - TouchExtraPadding = ImVec2(0,0); // Expand bounding box for touch-based system where touch position is not accurate enough (unnecessary for mouse inputs). Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget running. So dont grow this too much! - AutoFitPadding = ImVec2(8,8); // Extra space after auto-fit (double-clicking on resize grip) + WindowMinSize = ImVec2(32,32); // Minimum window size + WindowRounding = 9.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows + WindowTitleAlign = ImGuiAlign_Left; // Alignment for title bar text + ChildWindowRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular windows + FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets) + FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets). + ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines + ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label) + TouchExtraPadding = ImVec2(0,0); // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! WindowFillAlphaDefault = 0.70f; // Default alpha of window background, if not specified in ImGui::Begin() - WindowRounding = 10.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows - TreeNodeSpacing = 22.0f; // Horizontal spacing when entering a tree node + IndentSpacing = 22.0f; // Horizontal spacing when e.g. entering a tree node ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns - ScrollBarWidth = 16.0f; // Width of the vertical scroll bar + ScrollbarWidth = 16.0f; // Width of the vertical scrollbar + ScrollbarRounding = 0.0f; // Radius of grab corners rounding for scrollbar + GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar + DisplayWindowPadding = ImVec2(22,22); // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows. + DisplaySafeAreaPadding = ImVec2(4,4); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. Colors[ImGuiCol_Text] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); Colors[ImGuiCol_WindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - Colors[ImGuiCol_Border] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); + Colors[ImGuiCol_ChildWindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); + Colors[ImGuiCol_Border] = ImVec4(0.70f, 0.70f, 0.70f, 0.65f); Colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.60f); Colors[ImGuiCol_FrameBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.30f); // Background of checkbox, radio button, plot, slider, text input + Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.90f, 0.80f, 0.80f, 0.40f); + Colors[ImGuiCol_FrameBgActive] = ImVec4(0.90f, 0.65f, 0.65f, 0.45f); Colors[ImGuiCol_TitleBg] = ImVec4(0.50f, 0.50f, 1.00f, 0.45f); Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.40f, 0.40f, 0.80f, 0.20f); Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.40f, 0.40f, 0.80f, 0.15f); @@ -342,8 +598,7 @@ ImGuiStyle::ImGuiStyle() Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.80f, 0.40f); Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.80f, 0.50f, 0.50f, 0.40f); Colors[ImGuiCol_ComboBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.99f); - Colors[ImGuiCol_CheckHovered] = ImVec4(0.60f, 0.40f, 0.40f, 0.45f); - Colors[ImGuiCol_CheckActive] = ImVec4(0.90f, 0.90f, 0.90f, 0.50f); + Colors[ImGuiCol_CheckMark] = ImVec4(0.90f, 0.90f, 0.90f, 0.50f); Colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f); Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.80f, 0.50f, 0.50f, 1.00f); Colors[ImGuiCol_Button] = ImVec4(0.67f, 0.40f, 0.40f, 0.60f); @@ -351,10 +606,10 @@ ImGuiStyle::ImGuiStyle() Colors[ImGuiCol_ButtonActive] = ImVec4(0.80f, 0.50f, 0.50f, 1.00f); Colors[ImGuiCol_Header] = ImVec4(0.40f, 0.40f, 0.90f, 0.45f); Colors[ImGuiCol_HeaderHovered] = ImVec4(0.45f, 0.45f, 0.90f, 0.80f); - Colors[ImGuiCol_HeaderActive] = ImVec4(0.60f, 0.60f, 0.80f, 1.00f); - Colors[ImGuiCol_Column] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - Colors[ImGuiCol_ColumnHovered] = ImVec4(0.60f, 0.40f, 0.40f, 1.00f); - Colors[ImGuiCol_ColumnActive] = ImVec4(0.80f, 0.50f, 0.50f, 1.00f); + Colors[ImGuiCol_HeaderActive] = ImVec4(0.53f, 0.53f, 0.87f, 0.80f); + Colors[ImGuiCol_Column] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); + Colors[ImGuiCol_ColumnHovered] = ImVec4(0.70f, 0.60f, 0.60f, 1.00f); + Colors[ImGuiCol_ColumnActive] = ImVec4(0.90f, 0.70f, 0.70f, 1.00f); Colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f); Colors[ImGuiCol_ResizeGripHovered] = ImVec4(1.00f, 1.00f, 1.00f, 0.60f); Colors[ImGuiCol_ResizeGripActive] = ImVec4(1.00f, 1.00f, 1.00f, 0.90f); @@ -369,37 +624,41 @@ ImGuiStyle::ImGuiStyle() Colors[ImGuiCol_TooltipBg] = ImVec4(0.05f, 0.05f, 0.10f, 0.90f); } +// Statically allocated font atlas. This is merely a maneuver to keep ImFontAtlas definition at the bottom of the .h file (otherwise it'd be inside ImGuiIO) +// Also we wouldn't be able to new() one at this point, before users may define IO.MemAllocFn. +static ImFontAtlas GDefaultFontAtlas; + ImGuiIO::ImGuiIO() { + // Most fields are initialized with zero memset(this, 0, sizeof(*this)); + + DisplaySize = ImVec2(-1.0f, -1.0f); DeltaTime = 1.0f/60.0f; IniSavingRate = 5.0f; IniFilename = "imgui.ini"; LogFilename = "imgui_log.txt"; - Font = NULL; + Fonts = &GDefaultFontAtlas; FontGlobalScale = 1.0f; - FontAllowUserScaling = false; - PixelCenterOffset = 0.0f; MousePos = ImVec2(-1,-1); MousePosPrev = ImVec2(-1,-1); MouseDoubleClickTime = 0.30f; MouseDoubleClickMaxDist = 6.0f; + MouseDragThreshold = 6.0f; UserData = NULL; // User functions RenderDrawListsFn = NULL; MemAllocFn = malloc; - MemReallocFn = realloc; MemFreeFn = free; - GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependant default implementations + GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; - ImeSetInputScreenPosFn = NULL; + ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl; } // Pass in translated ASCII characters for text input. // - with glfw you can get those from the callback set in glfwSetCharCallback() -// - on Windows you can get those using ToAscii+keyboard state, or via the VM_CHAR message -static size_t ImStrlenW(const ImWchar* str); +// - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message void ImGuiIO::AddInputCharacter(ImWchar c) { const size_t n = ImStrlenW(InputCharacters); @@ -420,9 +679,18 @@ void ImGuiIO::AddInputCharacter(ImWchar c) const float PI = 3.14159265358979323846f; #ifdef INT_MAX -#define IM_INT_MAX INT_MAX +#define IM_INT_MIN INT_MIN +#define IM_INT_MAX INT_MAX #else -#define IM_INT_MAX 2147483647 +#define IM_INT_MIN (-2147483647-1) +#define IM_INT_MAX (2147483647) +#endif + +// Play it nice with Windows users. Notepad in 2015 still doesn't display text data with Unix-style \n. +#ifdef _MSC_VER +#define STR_NEWLINE "\r\n" +#else +#define STR_NEWLINE "\n" #endif // Math bits @@ -437,6 +705,7 @@ static inline ImVec2& operator+=(ImVec2& lhs, const ImVec2& rhs) static inline ImVec2& operator-=(ImVec2& lhs, const ImVec2& rhs) { lhs.x -= rhs.x; lhs.y -= rhs.y; return lhs; } static inline ImVec2& operator*=(ImVec2& lhs, const float rhs) { lhs.x *= rhs; lhs.y *= rhs; return lhs; } //static inline ImVec2& operator/=(ImVec2& lhs, const float rhs) { lhs.x /= rhs; lhs.y /= rhs; return lhs; } +static inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x-rhs.x, lhs.y-rhs.y, lhs.z-rhs.z, lhs.w-lhs.w); } static inline int ImMin(int lhs, int rhs) { return lhs < rhs ? lhs : rhs; } static inline int ImMax(int lhs, int rhs) { return lhs >= rhs ? lhs : rhs; } @@ -444,20 +713,14 @@ static inline float ImMin(float lhs, float rhs) static inline float ImMax(float lhs, float rhs) { return lhs >= rhs ? lhs : rhs; } static inline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(ImMin(lhs.x,rhs.x), ImMin(lhs.y,rhs.y)); } static inline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(ImMax(lhs.x,rhs.x), ImMax(lhs.y,rhs.y)); } -static inline float ImClamp(float f, float mn, float mx) { return (f < mn) ? mn : (f > mx) ? mx : f; } +static inline int ImClamp(int v, int mn, int mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } +static inline float ImClamp(float v, float mn, float mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } static inline ImVec2 ImClamp(const ImVec2& f, const ImVec2& mn, ImVec2 mx) { return ImVec2(ImClamp(f.x,mn.x,mx.x), ImClamp(f.y,mn.y,mx.y)); } static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; } static inline float ImLerp(float a, float b, float t) { return a + (b - a) * t; } -//static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, float t) { return a + (b - a) * t; } static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); } -static inline float ImLength(const ImVec2& lhs) { return sqrtf(lhs.x*lhs.x + lhs.y*lhs.y); } - -static int ImTextCharToUtf8(char* buf, size_t buf_size, unsigned int in_char); // return output UTF-8 bytes count -static ptrdiff_t ImTextStrToUtf8(char* buf, size_t buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count -static int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // return input UTF-8 bytes count -static ptrdiff_t ImTextStrFromUtf8(ImWchar* buf, size_t buf_size, const char* in_text, const char* in_text_end); // return input UTF-8 bytes count -static int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count) -static int ImTextCountUtf8BytesFromWchar(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string as UTF-8 code-points +static inline float ImLengthSqr(const ImVec2& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y; } +static inline float ImLengthSqr(const ImVec4& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y + lhs.z*lhs.z + lhs.w*lhs.w; } static int ImStricmp(const char* str1, const char* str2) { @@ -484,8 +747,7 @@ static char* ImStrdup(const char *str) static size_t ImStrlenW(const ImWchar* str) { size_t n = 0; - while (*str++) - n++; + while (*str++) n++; return n; } @@ -511,7 +773,9 @@ static const char* ImStristr(const char* haystack, const char* needle, const cha return NULL; } -static ImU32 crc32(const void* data, size_t data_size, ImU32 seed = 0) +// Pass data_size==0 for zero-terminated string +// Try to replace with FNV1a hash? +static ImU32 ImHash(const void* data, size_t data_size, ImU32 seed = 0) { static ImU32 crc32_lut[256] = { 0 }; if (!crc32_lut[1]) @@ -525,10 +789,32 @@ static ImU32 crc32(const void* data, size_t data_size, ImU32 seed = 0) crc32_lut[i] = crc; } } - ImU32 crc = ~seed; - const unsigned char* current = (const unsigned char*)data; - while (data_size--) - crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++]; + + seed = ~seed; + ImU32 crc = seed; + const unsigned char* current = (const unsigned char*)data; + + if (data_size > 0) + { + // Known size + while (data_size--) + crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++]; + } + else + { + // Zero-terminated string + while (unsigned char c = *current++) + { + // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed. + // Because this syntax is rarely used we are optimizing for the common case. + // - If we reach ### in the string we discard the hash so far and reset to the seed. + // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller. + if (c == '#' && current[0] == '#' && current[1] == '#') + crc = seed; + + crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; + } + } return ~crc; } @@ -549,7 +835,7 @@ static size_t ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_li return (w == -1) ? buf_size : (size_t)w; } -static ImU32 ImConvertColorFloat4ToU32(const ImVec4& in) +ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in) { ImU32 out = ((ImU32)(ImSaturate(in.x)*255.f)); out |= ((ImU32)(ImSaturate(in.y)*255.f) << 8); @@ -560,7 +846,7 @@ static ImU32 ImConvertColorFloat4ToU32(const ImVec4& in) // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592 // Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv -static void ImConvertColorRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v) +void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v) { float K = 0.f; if (g < b) @@ -582,7 +868,7 @@ static void ImConvertColorRGBtoHSV(float r, float g, float b, float& out_h, floa // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593 // also http://en.wikipedia.org/wiki/HSL_and_HSV -static void ImConvertColorHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b) +void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b) { if (s == 0.0f) { @@ -609,8 +895,57 @@ static void ImConvertColorHSVtoRGB(float h, float s, float v, float& out_r, floa } } +// Load file content into memory +// Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree() +static bool ImLoadFileToMemory(const char* filename, const char* file_open_mode, void** out_file_data, size_t* out_file_size, size_t padding_bytes) +{ + IM_ASSERT(filename && file_open_mode && out_file_data && out_file_size); + *out_file_data = NULL; + *out_file_size = 0; + + FILE* f; + if ((f = fopen(filename, file_open_mode)) == NULL) + return false; + + long file_size_signed; + if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET)) + { + fclose(f); + return false; + } + + size_t file_size = (size_t)file_size_signed; + void* file_data = ImGui::MemAlloc(file_size + padding_bytes); + if (file_data == NULL) + { + fclose(f); + return false; + } + if (fread(file_data, 1, file_size, f) != file_size) + { + fclose(f); + ImGui::MemFree(file_data); + return false; + } + if (padding_bytes > 0) + memset((void *)(((char*)file_data) + file_size), 0, padding_bytes); + + fclose(f); + *out_file_data = file_data; + *out_file_size = file_size; + + return true; +} + //----------------------------------------------------------------------------- +enum ImGuiButtonFlags_ +{ + ImGuiButtonFlags_Repeat = (1 << 0), + ImGuiButtonFlags_PressedOnClick = (1 << 1), + ImGuiButtonFlags_FlattenChilds = (1 << 2) +}; + struct ImGuiColMod // Color modifier, backup of modified data so we can restore it { ImGuiCol Col; @@ -623,29 +958,54 @@ struct ImGuiStyleMod // Style modifier, backup of modified data so we can res ImVec2 PreviousValue; }; -struct ImGuiAabb // 2D axis aligned bounding-box +struct ImRect // 2D axis aligned bounding-box { ImVec2 Min; ImVec2 Max; - ImGuiAabb() { Min = ImVec2(FLT_MAX,FLT_MAX); Max = ImVec2(-FLT_MAX,-FLT_MAX); } - ImGuiAabb(const ImVec2& min, const ImVec2& max) { Min = min; Max = max; } - ImGuiAabb(const ImVec4& v) { Min.x = v.x; Min.y = v.y; Max.x = v.z; Max.y = v.w; } - ImGuiAabb(float x1, float y1, float x2, float y2) { Min.x = x1; Min.y = y1; Max.x = x2; Max.y = y2; } + ImRect() { Min = ImVec2(FLT_MAX,FLT_MAX); Max = ImVec2(-FLT_MAX,-FLT_MAX); } + ImRect(const ImVec2& min, const ImVec2& max) { Min = min; Max = max; } + ImRect(const ImVec4& v) { Min.x = v.x; Min.y = v.y; Max.x = v.z; Max.y = v.w; } + ImRect(float x1, float y1, float x2, float y2) { Min.x = x1; Min.y = y1; Max.x = x2; Max.y = y2; } - ImVec2 GetCenter() const { return Min + (Max-Min)*0.5f; } - ImVec2 GetSize() const { return Max-Min; } - float GetWidth() const { return (Max-Min).x; } - float GetHeight() const { return (Max-Min).y; } - ImVec2 GetTL() const { return Min; } - ImVec2 GetTR() const { return ImVec2(Max.x,Min.y); } - ImVec2 GetBL() const { return ImVec2(Min.x,Max.y); } - ImVec2 GetBR() const { return Max; } - bool Contains(ImVec2 p) const { return p.x >= Min.x && p.y >= Min.y && p.x <= Max.x && p.y <= Max.y; } - bool Contains(const ImGuiAabb& r) const { return r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x <= Max.x && r.Max.y <= Max.y; } - bool Overlaps(const ImGuiAabb& r) const { return r.Min.y <= Max.y && r.Max.y >= Min.y && r.Min.x <= Max.x && r.Max.x >= Min.x; } - void Expand(ImVec2 sz) { Min -= sz; Max += sz; } - void Clip(const ImGuiAabb& clip) { Min.x = ImMax(Min.x, clip.Min.x); Min.y = ImMax(Min.y, clip.Min.y); Max.x = ImMin(Max.x, clip.Max.x); Max.y = ImMin(Max.y, clip.Max.y); } + ImVec2 GetCenter() const { return Min + (Max-Min)*0.5f; } + ImVec2 GetSize() const { return Max-Min; } + float GetWidth() const { return (Max-Min).x; } + float GetHeight() const { return (Max-Min).y; } + ImVec2 GetTL() const { return Min; } + ImVec2 GetTR() const { return ImVec2(Max.x,Min.y); } + ImVec2 GetBL() const { return ImVec2(Min.x,Max.y); } + ImVec2 GetBR() const { return Max; } + bool Contains(const ImVec2& p) const { return p.x >= Min.x && p.y >= Min.y && p.x < Max.x && p.y < Max.y; } + bool Contains(const ImRect& r) const { return r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x < Max.x && r.Max.y < Max.y; } + bool Overlaps(const ImRect& r) const { return r.Min.y < Max.y && r.Max.y > Min.y && r.Min.x < Max.x && r.Max.x > Min.x; } + void Add(const ImVec2& rhs) { Min.x = ImMin(Min.x, rhs.x); Min.y = ImMin(Min.y, rhs.y); Max.x = ImMax(Max.x, rhs.x); Max.y = ImMax(Max.x, rhs.x); } + void Add(const ImRect& rhs) { Min.x = ImMin(Min.x, rhs.Min.x); Min.y = ImMin(Min.y, rhs.Min.y); Max.x = ImMax(Max.x, rhs.Max.x); Max.y = ImMax(Max.y, rhs.Max.y); } + void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; } + void Expand(const ImVec2& amount) { Min -= amount; Max += amount; } + void Reduce(const ImVec2& amount) { Min += amount; Max -= amount; } + void Clip(const ImRect& clip) { Min.x = ImMax(Min.x, clip.Min.x); Min.y = ImMax(Min.y, clip.Min.y); Max.x = ImMin(Max.x, clip.Max.x); Max.y = ImMin(Max.y, clip.Max.y); } + ImVec2 GetClosestPoint(ImVec2 p, bool on_edge) const + { + if (!on_edge && Contains(p)) + return p; + if (p.x > Max.x) p.x = Max.x; + else if (p.x < Min.x) p.x = Min.x; + if (p.y > Max.y) p.y = Max.y; + else if (p.y < Min.y) p.y = Min.y; + return p; + } +}; +typedef ImRect ImGuiAabb; // FIXME-OBSOLETE + +struct ImGuiGroupData +{ + ImVec2 BackupCursorPos; + ImVec2 BackupCursorMaxPos; + float BackupColumnsStartX; + float BackupCurrentLineHeight; + float BackupCurrentLineTextBaseOffset; + float BackupLogLinePosY; }; // Temporary per-window data, reset at the beginning of the frame @@ -654,22 +1014,27 @@ struct ImGuiDrawContext ImVec2 CursorPos; ImVec2 CursorPosPrevLine; ImVec2 CursorStartPos; + ImVec2 CursorMaxPos; // Implicitly calculate the size of our contents, always extending. Saved into window->SizeContents at the end of the frame float CurrentLineHeight; + float CurrentLineTextBaseOffset; float PrevLineHeight; - float LogLineHeight; + float PrevLineTextBaseOffset; + float LogLinePosY; int TreeDepth; - ImGuiAabb LastItemAabb; - bool LastItemHovered; - bool LastItemFocused; + ImGuiID LastItemID; + ImRect LastItemRect; + bool LastItemHoveredAndUsable; + bool LastItemHoveredRect; ImVector ChildWindows; ImVector AllowKeyboardFocus; - ImVector ItemWidth; + ImVector ItemWidth; // 0.0: default, >0.0: width in pixels, <0.0: align xx pixels to the right of window ImVector TextWrapPos; + ImVector GroupStack; ImGuiColorEditMode ColorEditMode; ImGuiStorage* StateStorage; - int OpenNextNode; + int StackSizesBackup[5]; // store size of various stacks for asserting - float ColumnsStartX; // Start position from left of window (increased by TreePush/TreePop, etc.) + float ColumnsStartX; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) float ColumnsOffsetX; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API. int ColumnsCurrent; int ColumnsCount; @@ -678,18 +1043,21 @@ struct ImGuiDrawContext float ColumnsCellMaxY; bool ColumnsShowBorders; ImGuiID ColumnsSetID; + ImVector ColumnsOffsetsT; // Columns offset normalized 0.0 (far left) -> 1.0 (far right) ImGuiDrawContext() { - CursorPos = CursorPosPrevLine = CursorStartPos = ImVec2(0.0f, 0.0f); + CursorPos = CursorPosPrevLine = CursorStartPos = CursorMaxPos = ImVec2(0.0f, 0.0f); CurrentLineHeight = PrevLineHeight = 0.0f; - LogLineHeight = -1.0f; + CurrentLineTextBaseOffset = PrevLineTextBaseOffset = 0.0f; + LogLinePosY = -1.0f; TreeDepth = 0; - LastItemAabb = ImGuiAabb(0.0f,0.0f,0.0f,0.0f); - LastItemHovered = false; - LastItemFocused = true; + LastItemID = 0; + LastItemRect = ImRect(0.0f,0.0f,0.0f,0.0f); + LastItemHoveredAndUsable = LastItemHoveredRect = false; + ColorEditMode = ImGuiColorEditMode_RGB; StateStorage = NULL; - OpenNextNode = -1; + memset(StackSizesBackup, 0, sizeof(StackSizesBackup)); ColumnsStartX = 0.0f; ColumnsOffsetX = 0.0f; @@ -702,22 +1070,19 @@ struct ImGuiDrawContext } }; -struct ImGuiTextEditState; -#define STB_TEXTEDIT_STRING ImGuiTextEditState -#define STB_TEXTEDIT_CHARTYPE ImWchar -#include "stb_textedit.h" - // Internal state of the currently focused/edited text input box struct ImGuiTextEditState { + ImGuiID Id; // widget id owning the text state ImWchar Text[1024]; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. - char InitialText[1024*3+1]; // backup of end-user buffer at the time of focus (in UTF-8, unconverted) - size_t BufSize; // end-user buffer size, <= 1024 (or increase above) + char InitialText[1024*3+1]; // backup of end-user buffer at the time of focus (in UTF-8, unaltered) + size_t CurLenA, CurLenW; // we need to maintain our buffer length in both UTF-8 and wchar format. + size_t BufSizeA; // end-user buffer size, <= 1024 (or increase above) float Width; // widget width float ScrollX; STB_TexteditState StbState; float CursorAnim; - ImVec2 LastCursorPos; // Cursor position in screen space to be used by IME callback. + ImVec2 InputCursorScreenPos; // Cursor position in screen space to be used by IME callback. bool SelectedAllMouseLock; ImFont* Font; float FontSize; @@ -739,9 +1104,11 @@ struct ImGuiTextEditState static void RenderTextScrolledClipped(ImFont* font, float font_size, const char* text, ImVec2 pos_base, float width, float scroll_x); }; +// Data saved in imgui.ini file struct ImGuiIniData { char* Name; + ImGuiID ID; ImVec2 Pos; ImVec2 Size; bool Collapsed; @@ -750,41 +1117,81 @@ struct ImGuiIniData ~ImGuiIniData() { if (Name) { ImGui::MemFree(Name); Name = NULL; } } }; +struct ImGuiMouseCursorData +{ + ImGuiMouseCursor Type; + ImVec2 Offset; + ImVec2 Size; + ImVec2 TexUvMin[2]; + ImVec2 TexUvMax[2]; +}; + +// Main state for ImGui struct ImGuiState { bool Initialized; ImGuiIO IO; ImGuiStyle Style; - float FontSize; // == IO.FontGlobalScale * IO.Font->Scale * IO.Font->Info->FontSize. Vertical distance between two lines of text, aka == CalcTextSize(" ").y - ImVec2 FontTexUvForWhite; // == IO.Font->FontTexUvForWhite (cached copy) + ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back() + float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize() + float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Size of characters. + ImVec2 FontTexUvWhitePixel; // (Shortcut) == Font->TexUvForWhite float Time; int FrameCount; int FrameCountRendered; ImVector Windows; + ImVector WindowsSortBuffer; ImGuiWindow* CurrentWindow; // Being drawn into ImVector CurrentWindowStack; + int CurrentPopupStackSize; ImGuiWindow* FocusedWindow; // Will catch keyboard inputs ImGuiWindow* HoveredWindow; // Will catch mouse inputs ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only) - ImGuiID HoveredId; - ImGuiID ActiveId; + ImGuiID HoveredId; // Hovered widget + ImGuiID ActiveId; // Active widget ImGuiID ActiveIdPreviousFrame; bool ActiveIdIsAlive; + bool ActiveIdIsJustActivated; // Set when + bool ActiveIdIsFocusedOnly; // Set only by active widget. Denote focus but no active interaction. + ImGuiWindow* MovedWindow; // Track the child window we clicked on to move a window. Only valid if ActiveID is the "#MOVE" identifier of a window. float SettingsDirtyTimer; ImVector Settings; - ImVec2 NewWindowDefaultPos; + int DisableHideTextAfterDoubleHash; ImVector ColorModifiers; ImVector StyleModifiers; + ImVector FontStack; + + ImVec2 SetNextWindowPosVal; + ImGuiSetCond SetNextWindowPosCond; + ImVec2 SetNextWindowSizeVal; + ImGuiSetCond SetNextWindowSizeCond; + bool SetNextWindowCollapsedVal; + ImGuiSetCond SetNextWindowCollapsedCond; + bool SetNextWindowFocus; + bool SetNextTreeNodeOpenedVal; + ImGuiSetCond SetNextTreeNodeOpenedCond; // Render - ImVector RenderDrawLists; + ImVector RenderDrawLists[3]; + + // Mouse cursor + ImGuiMouseCursor MouseCursor; + ImDrawList MouseCursorDrawList; // Optional software render of mouse cursors, if io.MouseDrawCursor is set + ImGuiMouseCursorData MouseCursorData[ImGuiMouseCursor_Count_]; // Widget state ImGuiTextEditState InputTextState; - ImGuiID SliderAsInputTextId; + ImGuiID ScalarAsInputTextId; // Temporary text input when CTRL+clicking on a slider, etc. ImGuiStorage ColorEditModeStorage; // for user selection ImGuiID ActiveComboID; + ImVec2 ActiveClickDeltaToCenter; + float DragCurrentValue; // current dragged value, always float, not rounded by end-user precision settings + ImVec2 DragLastMouseDelta; + float DragSpeedDefaultRatio; // if speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio + float DragSpeedScaleSlow; + float DragSpeedScaleFast; + float ScrollbarClickDeltaToGrabCenter; // distance between mouse and center of grab box, normalized in parent space char Tooltip[1024]; char* PrivateClipboard; // if no custom clipboard handler is defined @@ -792,33 +1199,78 @@ struct ImGuiState bool LogEnabled; FILE* LogFile; ImGuiTextBuffer* LogClipboard; // pointer so our GImGui static constructor doesn't call heap allocators. + int LogStartDepth; int LogAutoExpandMaxDepth; + // Misc + float FramerateSecPerFrame[120]; // calculate estimate of framerate for user + int FramerateSecPerFrameIdx; + float FramerateSecPerFrameAccum; + char TempBuffer[1024*3+1]; // temporary text buffer + ImGuiState() { Initialized = false; + Font = NULL; + FontBaseSize = FontSize = 0.0f; + FontTexUvWhitePixel = ImVec2(0.0f, 0.0f); + Time = 0.0f; FrameCount = 0; FrameCountRendered = -1; CurrentWindow = NULL; + CurrentPopupStackSize = 0; FocusedWindow = NULL; HoveredWindow = NULL; HoveredRootWindow = NULL; + HoveredId = 0; + ActiveId = 0; + ActiveIdPreviousFrame = 0; ActiveIdIsAlive = false; + ActiveIdIsJustActivated = false; + ActiveIdIsFocusedOnly = false; + MovedWindow = NULL; SettingsDirtyTimer = 0.0f; - NewWindowDefaultPos = ImVec2(60, 60); - SliderAsInputTextId = 0; + DisableHideTextAfterDoubleHash = 0; + + SetNextWindowPosVal = ImVec2(0.0f, 0.0f); + SetNextWindowPosCond = 0; + SetNextWindowSizeVal = ImVec2(0.0f, 0.0f); + SetNextWindowSizeCond = 0; + SetNextWindowCollapsedVal = false; + SetNextWindowCollapsedCond = 0; + SetNextWindowFocus = false; + SetNextTreeNodeOpenedVal = false; + SetNextTreeNodeOpenedCond = 0; + + ScalarAsInputTextId = 0; ActiveComboID = 0; + ActiveClickDeltaToCenter = ImVec2(0.0f, 0.0f); + DragCurrentValue = 0.0f; + DragLastMouseDelta = ImVec2(0.0f, 0.0f); + DragSpeedDefaultRatio = 0.01f; + DragSpeedScaleSlow = 0.01f; + DragSpeedScaleFast = 10.0f; + ScrollbarClickDeltaToGrabCenter = 0.0f; memset(Tooltip, 0, sizeof(Tooltip)); PrivateClipboard = NULL; + + MouseCursor = ImGuiMouseCursor_Arrow; + LogEnabled = false; LogFile = NULL; - LogAutoExpandMaxDepth = 2; LogClipboard = NULL; + LogStartDepth = 0; + LogAutoExpandMaxDepth = 2; + + memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); + FramerateSecPerFrameIdx = 0; + FramerateSecPerFrameAccum = 0.0f; } }; -static ImGuiState GImGui; +static ImGuiState GImDefaultState; // Internal state storage +static ImGuiState* GImGui = &GImDefaultState; // We access everything through this pointer. NB: this pointer is always assumed to be != NULL struct ImGuiWindow { @@ -829,20 +1281,29 @@ struct ImGuiWindow ImVec2 Pos; // Position rounded-up to nearest pixel ImVec2 Size; // Current size (==SizeFull or collapsed title bar size) ImVec2 SizeFull; // Size when non collapsed - ImVec2 SizeContentsFit; // Size of contents (extents reach by the drawing cursor) - may not fit within Size. + ImVec2 SizeContents; // Size of contents (== extents reach of the drawing cursor) from previous frame + ImGuiID MoveID; // == window->GetID("#MOVE") float ScrollY; float NextScrollY; bool ScrollbarY; - bool Visible; // Set to true on Begin() + bool Active; // Set to true on Begin() + bool WasActive; bool Accessed; // Set to true when any widget access the current window bool Collapsed; // Set when collapsing window to become only title-bar bool SkipItems; // == Visible && !Collapsed + int BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) int AutoFitFrames; bool AutoFitOnlyGrows; + int AutoPosLastDirection; + int HiddenFrames; + int SetWindowPosAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowPos() call will succeed with this particular flag. + int SetWindowSizeAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowSize() call will succeed with this particular flag. + int SetWindowCollapsedAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowCollapsed() call will succeed with this particular flag. - ImGuiDrawContext DC; - ImVector IDStack; - ImVector ClipRectStack; + ImGuiDrawContext DC; // Temporary per-window data, reset at the beginning of the frame + ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack + ImVector ClipRectStack; // Scissoring / clipping rectangle. x1, y1, x2, y2. + ImRect ClippedRect; // = ClipRectStack.front() after setup in Begin() int LastFrameDrawn; float ItemWidthDefault; ImGuiStorage StateStorage; @@ -859,38 +1320,60 @@ struct ImGuiWindow int FocusIdxTabRequestNext; // " public: - ImGuiWindow(const char* name, ImVec2 default_pos, ImVec2 default_size); + ImGuiWindow(const char* name); ~ImGuiWindow(); - ImGuiID GetID(const char* str); + ImGuiID GetID(const char* str, const char* str_end = NULL); ImGuiID GetID(const void* ptr); - void AddToRenderList(); bool FocusItemRegister(bool is_active, bool tab_stop = true); // Return true if focus is requested void FocusItemUnregister(); - ImGuiAabb Aabb() const { return ImGuiAabb(Pos, Pos+Size); } - ImFont* Font() const { return GImGui.IO.Font; } - float FontSize() const { return GImGui.FontSize * FontWindowScale; } - ImVec2 CursorPos() const { return DC.CursorPos; } - float TitleBarHeight() const { return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0 : FontSize() + GImGui.Style.FramePadding.y * 2.0f; } - ImGuiAabb TitleBarAabb() const { return ImGuiAabb(Pos, Pos + ImVec2(SizeFull.x, TitleBarHeight())); } - ImVec2 WindowPadding() const { return ((Flags & ImGuiWindowFlags_ChildWindow) && !(Flags & ImGuiWindowFlags_ShowBorders)) ? ImVec2(1,1) : GImGui.Style.WindowPadding; } - ImU32 Color(ImGuiCol idx, float a=1.f) const { ImVec4 c = GImGui.Style.Colors[idx]; c.w *= GImGui.Style.Alpha * a; return ImConvertColorFloat4ToU32(c); } - ImU32 Color(const ImVec4& col) const { ImVec4 c = col; c.w *= GImGui.Style.Alpha; return ImConvertColorFloat4ToU32(c); } + ImRect Rect() const { return ImRect(Pos, Pos+Size); } + float CalcFontSize() const { return GImGui->FontBaseSize * FontWindowScale; } + float TitleBarHeight() const { return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0 : CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f; } + ImRect TitleBarRect() const { return ImRect(Pos, Pos + ImVec2(SizeFull.x, TitleBarHeight())); } + ImVec2 WindowPadding() const { return ((Flags & ImGuiWindowFlags_ChildWindow) && !(Flags & ImGuiWindowFlags_ShowBorders) && !(Flags & ImGuiWindowFlags_ComboBox)) ? ImVec2(0,0) : GImGui->Style.WindowPadding; } + ImU32 Color(ImGuiCol idx, float a=1.f) const { ImVec4 c = GImGui->Style.Colors[idx]; c.w *= GImGui->Style.Alpha * a; return ImGui::ColorConvertFloat4ToU32(c); } + ImU32 Color(const ImVec4& col) const { ImVec4 c = col; c.w *= GImGui->Style.Alpha; return ImGui::ColorConvertFloat4ToU32(c); } }; -static ImGuiWindow* GetCurrentWindow() +static inline ImGuiWindow* GetCurrentWindow() { - IM_ASSERT(GImGui.CurrentWindow != NULL); // ImGui::NewFrame() hasn't been called yet? - GImGui.CurrentWindow->Accessed = true; - return GImGui.CurrentWindow; + // If this ever crash it probably means that ImGui::NewFrame() hasn't been called. We should always have a CurrentWindow in the stack (there is an implicit "Debug" window) + ImGuiState& g = *GImGui; + g.CurrentWindow->Accessed = true; + return g.CurrentWindow; } -static void RegisterAliveId(const ImGuiID& id) +static inline void SetCurrentWindow(ImGuiWindow* window) { - if (GImGui.ActiveId == id) - GImGui.ActiveIdIsAlive = true; + ImGuiState& g = *GImGui; + g.CurrentWindow = window; + if (window) + g.FontSize = window->CalcFontSize(); +} + +static inline ImGuiWindow* GetParentWindow() +{ + ImGuiState& g = *GImGui; + IM_ASSERT(g.CurrentWindowStack.size() >= 2); + return g.CurrentWindowStack[g.CurrentWindowStack.size() - 2]; +} + +static void SetActiveId(ImGuiID id) +{ + ImGuiState& g = *GImGui; + g.ActiveId = id; + g.ActiveIdIsFocusedOnly = false; + g.ActiveIdIsJustActivated = true; +} + +static void RegisterAliveId(ImGuiID id) +{ + ImGuiState& g = *GImGui; + if (g.ActiveId == id) + g.ActiveIdIsAlive = true; } //----------------------------------------------------------------------------- @@ -924,68 +1407,120 @@ static ImVector::iterator LowerBound(ImVector::iterator it = LowerBound(const_cast&>(Data), key); + if (it == Data.end() || it->key != key) + return default_val; + return it->val_i; +} + +float ImGuiStorage::GetFloat(ImU32 key, float default_val) const +{ + ImVector::iterator it = LowerBound(const_cast&>(Data), key); + if (it == Data.end() || it->key != key) + return default_val; + return it->val_f; +} + +void* ImGuiStorage::GetVoidPtr(ImGuiID key) const +{ + ImVector::iterator it = LowerBound(const_cast&>(Data), key); + if (it == Data.end() || it->key != key) + return NULL; + return it->val_p; +} + +// References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer. +int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val) { ImVector::iterator it = LowerBound(Data, key); - if (it == Data.end()) - return NULL; - if (it->key != key) - return NULL; - return &it->val; + if (it == Data.end() || it->key != key) + it = Data.insert(it, Pair(key, default_val)); + return &it->val_i; } -int ImGuiStorage::GetInt(ImU32 key, int default_val) +float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val) { - int* pval = Find(key); - if (!pval) - return default_val; - return *pval; + ImVector::iterator it = LowerBound(Data, key); + if (it == Data.end() || it->key != key) + it = Data.insert(it, Pair(key, default_val)); + return &it->val_f; } -// FIXME-OPT: We are wasting time because all SetInt() are preceeded by GetInt() calls so we should have the result from lower_bound already in place. +void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val) +{ + ImVector::iterator it = LowerBound(Data, key); + if (it == Data.end() || it->key != key) + it = Data.insert(it, Pair(key, default_val)); + return &it->val_p; +} + +// FIXME-OPT: Wasting CPU because all SetInt() are preceeded by GetInt() calls so we should have the result from lower_bound already in place. // However we only use SetInt() on explicit user action (so that's maximum once a frame) so the optimisation isn't much needed. void ImGuiStorage::SetInt(ImU32 key, int val) { ImVector::iterator it = LowerBound(Data, key); - if (it != Data.end() && it->key == key) + if (it == Data.end() || it->key != key) { - it->val = val; + Data.insert(it, Pair(key, val)); + return; } - else + it->val_i = val; +} + +void ImGuiStorage::SetFloat(ImU32 key, float val) +{ + ImVector::iterator it = LowerBound(Data, key); + if (it == Data.end() || it->key != key) { - Pair pair_key; - pair_key.key = key; - pair_key.val = val; - Data.insert(it, pair_key); + Data.insert(it, Pair(key, val)); + return; } + it->val_f = val; +} + +void ImGuiStorage::SetVoidPtr(ImU32 key, void* val) +{ + ImVector::iterator it = LowerBound(Data, key); + if (it == Data.end() || it->key != key) + { + Data.insert(it, Pair(key, val)); + return; + } + it->val_p = val; } void ImGuiStorage::SetAllInt(int v) { for (size_t i = 0; i < Data.size(); i++) - Data[i].val = v; + Data[i].val_i = v; } //----------------------------------------------------------------------------- // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" -ImGuiTextFilter::ImGuiTextFilter() +ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) { - InputBuf[0] = 0; - CountGrep = 0; + if (default_filter) + { + ImFormatString(InputBuf, IM_ARRAYSIZE(InputBuf), "%s", default_filter); + Build(); + } + else + { + InputBuf[0] = 0; + CountGrep = 0; + } } void ImGuiTextFilter::Draw(const char* label, float width) { - ImGuiWindow* window = GetCurrentWindow(); - if (width < 0.0f) - { - ImVec2 label_size = ImGui::CalcTextSize(label, NULL); - width = ImMax(window->Pos.x + ImGui::GetContentRegionMax().x - window->DC.CursorPos.x - (label_size.x + GImGui.Style.ItemSpacing.x*4), 10.0f); - } - ImGui::PushItemWidth(width); + if (width > 0.0f) + ImGui::PushItemWidth(width); ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf)); - ImGui::PopItemWidth(); + if (width > 0.0f) + ImGui::PopItemWidth(); Build(); } @@ -1060,60 +1595,76 @@ bool ImGuiTextFilter::PassFilter(const char* val) const //----------------------------------------------------------------------------- +// On some platform vsnprintf() takes va_list by reference and modifies it. +// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it. +#ifndef va_copy +#define va_copy(dest, src) (dest = src) +#endif + // Helper: Text buffer for logging/accumulating text -void ImGuiTextBuffer::append(const char* fmt, ...) +void ImGuiTextBuffer::appendv(const char* fmt, va_list args) { - va_list args; - va_start(args, fmt); - int len = vsnprintf(NULL, 0, fmt, args); - va_end(args); + va_list args_copy; + va_copy(args_copy, args); + + int len = vsnprintf(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass. if (len <= 0) return; const size_t write_off = Buf.size(); + const size_t needed_sz = write_off + (size_t)len; if (write_off + (size_t)len >= Buf.capacity()) - Buf.reserve(Buf.capacity() * 2); + { + const size_t double_capacity = Buf.capacity() * 2; + Buf.reserve(needed_sz > double_capacity ? needed_sz : double_capacity); + } - Buf.resize(write_off + (size_t)len); + Buf.resize(needed_sz); + ImFormatStringV(&Buf[write_off] - 1, (size_t)len+1, fmt, args_copy); +} +void ImGuiTextBuffer::append(const char* fmt, ...) +{ + va_list args; va_start(args, fmt); - ImFormatStringV(&Buf[write_off] - 1, (size_t)len+1, fmt, args); + appendv(fmt, args); va_end(args); } //----------------------------------------------------------------------------- -ImGuiWindow::ImGuiWindow(const char* name, ImVec2 default_pos, ImVec2 default_size) +ImGuiWindow::ImGuiWindow(const char* name) { Name = ImStrdup(name); - ID = GetID(name); + ID = ImHash(name, 0); IDStack.push_back(ID); + MoveID = GetID("#MOVE"); - PosFloat = default_pos; - Pos = ImVec2((float)(int)PosFloat.x, (float)(int)PosFloat.y); - Size = SizeFull = default_size; - SizeContentsFit = ImVec2(0.0f, 0.0f); + Flags = 0; + PosFloat = Pos = ImVec2(0.0f, 0.0f); + Size = SizeFull = ImVec2(0.0f, 0.0f); + SizeContents = ImVec2(0.0f, 0.0f); ScrollY = 0.0f; NextScrollY = 0.0f; ScrollbarY = false; - Visible = false; + Active = WasActive = false; Accessed = false; Collapsed = false; SkipItems = false; + BeginCount = 0; AutoFitFrames = -1; AutoFitOnlyGrows = false; + AutoPosLastDirection = -1; + HiddenFrames = 0; + SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiSetCond_Always | ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing; + LastFrameDrawn = -1; ItemWidthDefault = 0.0f; FontWindowScale = 1.0f; - if (ImLength(Size) < 0.001f) - { - AutoFitFrames = 2; - AutoFitOnlyGrows = true; - } - DrawList = (ImDrawList*)ImGui::MemAlloc(sizeof(ImDrawList)); new(DrawList) ImDrawList(); + RootWindow = NULL; FocusIdxAllCounter = FocusIdxTabCounter = -1; FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = IM_INT_MAX; @@ -1129,25 +1680,25 @@ ImGuiWindow::~ImGuiWindow() Name = NULL; } -ImGuiID ImGuiWindow::GetID(const char* str) +ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) { - const ImGuiID seed = IDStack.empty() ? 0 : IDStack.back(); - const ImGuiID id = crc32(str, strlen(str), seed); // FIXME-OPT: crc32 function/variant should handle zero-terminated strings + ImGuiID seed = IDStack.back(); + const ImGuiID id = ImHash(str, str_end ? str_end - str : 0, seed); RegisterAliveId(id); return id; } ImGuiID ImGuiWindow::GetID(const void* ptr) { - const ImGuiID seed = IDStack.empty() ? 0 : IDStack.back(); - const ImGuiID id = crc32(&ptr, sizeof(void*), seed); + ImGuiID seed = IDStack.back(); + const ImGuiID id = ImHash(&ptr, sizeof(void*), seed); RegisterAliveId(id); return id; } bool ImGuiWindow::FocusItemRegister(bool is_active, bool tab_stop) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); const bool allow_keyboard_focus = window->DC.AllowKeyboardFocus.back(); @@ -1155,9 +1706,6 @@ bool ImGuiWindow::FocusItemRegister(bool is_active, bool tab_stop) if (allow_keyboard_focus) FocusIdxTabCounter++; - if (is_active) - window->DC.LastItemFocused = true; - // Process keyboard input at this point: TAB, Shift-TAB switch focus // We can always TAB out of a widget that doesn't allow tabbing in. if (tab_stop && FocusIdxAllRequestNext == IM_INT_MAX && FocusIdxTabRequestNext == IM_INT_MAX && is_active && IsKeyPressedMap(ImGuiKey_Tab)) @@ -1182,21 +1730,25 @@ void ImGuiWindow::FocusItemUnregister() FocusIdxTabCounter--; } -void ImGuiWindow::AddToRenderList() +static inline void AddDrawListToRenderList(ImVector& out_render_list, ImDrawList* draw_list) { - ImGuiState& g = GImGui; - - if (!DrawList->commands.empty() && !DrawList->vtx_buffer.empty()) + if (!draw_list->commands.empty() && !draw_list->vtx_buffer.empty()) { - if (DrawList->commands.back().vtx_count == 0) - DrawList->commands.pop_back(); - g.RenderDrawLists.push_back(DrawList); + if (draw_list->commands.back().vtx_count == 0) + draw_list->commands.pop_back(); + out_render_list.push_back(draw_list); + GImGui->IO.MetricsRenderVertices += (int)draw_list->vtx_buffer.size(); } - for (size_t i = 0; i < DC.ChildWindows.size(); i++) +} + +static void AddWindowToRenderList(ImVector& out_render_list, ImGuiWindow* window) +{ + AddDrawListToRenderList(out_render_list, window->DrawList); + for (size_t i = 0; i < window->DC.ChildWindows.size(); i++) { - ImGuiWindow* child = DC.ChildWindows[i]; - if (child->Visible) // clipped childs may have been marked not Visible - child->AddToRenderList(); + ImGuiWindow* child = window->DC.ChildWindows[i]; + if (child->Active) // clipped children may have been marked not active + AddWindowToRenderList(out_render_list, child); } } @@ -1204,36 +1756,37 @@ void ImGuiWindow::AddToRenderList() void* ImGui::MemAlloc(size_t sz) { - return GImGui.IO.MemAllocFn(sz); + return GImGui->IO.MemAllocFn(sz); } void ImGui::MemFree(void* ptr) { - return GImGui.IO.MemFreeFn(ptr); -} - -void* ImGui::MemRealloc(void* ptr, size_t sz) -{ - return GImGui.IO.MemReallocFn(ptr, sz); + return GImGui->IO.MemFreeFn(ptr); } static ImGuiIniData* FindWindowSettings(const char* name) { - ImGuiState& g = GImGui; - + ImGuiState& g = *GImGui; + ImGuiID id = ImHash(name, 0); for (size_t i = 0; i != g.Settings.size(); i++) { ImGuiIniData* ini = g.Settings[i]; - if (ImStricmp(ini->Name, name) == 0) + if (ini->ID == id) return ini; } + return NULL; +} + +static ImGuiIniData* AddWindowSettings(const char* name) +{ ImGuiIniData* ini = (ImGuiIniData*)ImGui::MemAlloc(sizeof(ImGuiIniData)); new(ini) ImGuiIniData(); ini->Name = ImStrdup(name); + ini->ID = ImHash(name, 0); ini->Collapsed = false; ini->Pos = ImVec2(FLT_MAX,FLT_MAX); ini->Size = ImVec2(0,0); - g.Settings.push_back(ini); + GImGui->Settings.push_back(ini); return ini; } @@ -1241,45 +1794,19 @@ static ImGuiIniData* FindWindowSettings(const char* name) // FIXME: Write something less rubbish static void LoadSettings() { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; const char* filename = g.IO.IniFilename; if (!filename) return; - // Load file - FILE* f; - if ((f = fopen(filename, "rt")) == NULL) + char* file_data; + size_t file_size; + if (!ImLoadFileToMemory(filename, "rb", (void**)&file_data, &file_size, 1)) return; - if (fseek(f, 0, SEEK_END)) - { - fclose(f); - return; - } - const long f_size_signed = ftell(f); - if (f_size_signed == -1) - { - fclose(f); - return; - } - size_t f_size = (size_t)f_size_signed; - if (fseek(f, 0, SEEK_SET)) - { - fclose(f); - return; - } - char* f_data = (char*)ImGui::MemAlloc(f_size+1); - f_size = fread(f_data, 1, f_size, f); // Text conversion alter read size so let's not be fussy about return value - fclose(f); - if (f_size == 0) - { - ImGui::MemFree(f_data); - return; - } - f_data[f_size] = 0; ImGuiIniData* settings = NULL; - const char* buf_end = f_data + f_size; - for (const char* line_start = f_data; line_start < buf_end; ) + const char* buf_end = file_data + file_size; + for (const char* line_start = file_data; line_start < buf_end; ) { const char* line_end = line_start; while (line_end < buf_end && *line_end != '\n' && *line_end != '\r') @@ -1290,6 +1817,8 @@ static void LoadSettings() char name[64]; ImFormatString(name, IM_ARRAYSIZE(name), "%.*s", line_end-line_start-2, line_start+1); settings = FindWindowSettings(name); + if (!settings) + settings = AddWindowSettings(name); } else if (settings) { @@ -1306,12 +1835,12 @@ static void LoadSettings() line_start = line_end+1; } - ImGui::MemFree(f_data); + ImGui::MemFree(file_data); } static void SaveSettings() { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; const char* filename = g.IO.IniFilename; if (!filename) return; @@ -1320,7 +1849,7 @@ static void SaveSettings() for (size_t i = 0; i != g.Windows.size(); i++) { ImGuiWindow* window = g.Windows[i]; - if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) + if (window->Flags & ImGuiWindowFlags_NoSavedSettings) continue; ImGuiIniData* settings = FindWindowSettings(window->Name); settings->Pos = window->Pos; @@ -1338,7 +1867,10 @@ static void SaveSettings() const ImGuiIniData* settings = g.Settings[i]; if (settings->Pos.x == FLT_MAX) continue; - fprintf(f, "[%s]\n", settings->Name); + const char* name = settings->Name; + if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() + name = p; + fprintf(f, "[%s]\n", name); fprintf(f, "Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y); fprintf(f, "Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y); fprintf(f, "Collapsed=%d\n", settings->Collapsed); @@ -1350,30 +1882,56 @@ static void SaveSettings() static void MarkSettingsDirty() { - ImGuiState& g = GImGui; - + ImGuiState& g = *GImGui; if (g.SettingsDirtyTimer <= 0.0f) g.SettingsDirtyTimer = g.IO.IniSavingRate; } +const char* ImGui::GetVersion() +{ + return IMGUI_VERSION; +} + +// Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself +// Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module +void* ImGui::GetInternalState() +{ + return GImGui; +} + +size_t ImGui::GetInternalStateSize() +{ + return sizeof(ImGuiState); +} + +void ImGui::SetInternalState(void* state, bool construct) +{ + if (construct) + new (state) ImGuiState(); + + GImGui = (ImGuiState*)state; +} + ImGuiIO& ImGui::GetIO() { - return GImGui.IO; + return GImGui->IO; } ImGuiStyle& ImGui::GetStyle() { - return GImGui.Style; + return GImGui->Style; } void ImGui::NewFrame() { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; - // Check user inputs + // Check user data IM_ASSERT(g.IO.DeltaTime > 0.0f); - IM_ASSERT(g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f); - IM_ASSERT(g.IO.RenderDrawListsFn != NULL); // Must be implemented + IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f); + IM_ASSERT(g.IO.RenderDrawListsFn != NULL); // Must be implemented + IM_ASSERT(g.IO.Fonts->Fonts.size() > 0); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? + IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? if (!g.Initialized) { @@ -1383,26 +1941,10 @@ void ImGui::NewFrame() IM_ASSERT(g.Settings.empty()); LoadSettings(); - if (!g.IO.Font) - { - // Default font - const void* fnt_data; - unsigned int fnt_size; - ImGui::GetDefaultFontData(&fnt_data, &fnt_size, NULL, NULL); - g.IO.Font = (ImFont*)ImGui::MemAlloc(sizeof(ImFont)); - new(g.IO.Font) ImFont(); - g.IO.Font->LoadFromMemory(fnt_data, fnt_size); - IM_ASSERT(g.IO.Font->IsLoaded()); // Font failed to load - g.IO.Font->DisplayOffset = ImVec2(0.0f, +1.0f); - } g.Initialized = true; } - IM_ASSERT(g.IO.Font && g.IO.Font->IsLoaded()); // Font not loaded - IM_ASSERT(g.IO.Font->Scale > 0.0f); - g.FontSize = g.IO.FontGlobalScale * (float)g.IO.Font->Info->FontSize * g.IO.Font->Scale; - g.FontTexUvForWhite = g.IO.Font->TexUvForWhite; - g.IO.Font->FallbackGlyph = g.IO.Font->FindGlyph(g.IO.Font->FallbackChar); + SetFont(g.IO.Fonts->Fonts[0]); g.Time += g.IO.DeltaTime; g.FrameCount += 1; @@ -1425,26 +1967,40 @@ void ImGui::NewFrame() { if (g.Time - g.IO.MouseClickedTime[i] < g.IO.MouseDoubleClickTime) { - if (ImLength(g.IO.MousePos - g.IO.MouseClickedPos[i]) < g.IO.MouseDoubleClickMaxDist) + if (ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist) g.IO.MouseDoubleClicked[i] = true; g.IO.MouseClickedTime[i] = -FLT_MAX; // so the third click isn't turned into a double-click } else { g.IO.MouseClickedTime[i] = g.Time; - g.IO.MouseClickedPos[i] = g.IO.MousePos; } + g.IO.MouseClickedPos[i] = g.IO.MousePos; + g.IO.MouseDragMaxDistanceSqr[i] = 0.0f; + } + else if (g.IO.MouseDown[i]) + { + g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i])); } } for (size_t i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++) g.IO.KeysDownTime[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownTime[i] < 0.0f ? 0.0f : g.IO.KeysDownTime[i] + g.IO.DeltaTime) : -1.0f; + // Calculate frame-rate for the user, as a purely luxurious feature + g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx]; + g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime; + g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); + g.IO.Framerate = 1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame)); + // Clear reference to active widget if the widget isn't alive anymore g.HoveredId = 0; if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0) - g.ActiveId = 0; + SetActiveId(0); g.ActiveIdPreviousFrame = g.ActiveId; g.ActiveIdIsAlive = false; + g.ActiveIdIsJustActivated = false; + if (!g.ActiveId) + g.MovedWindow = NULL; // Delay saving settings so we don't spam disk too much if (g.SettingsDirtyTimer > 0.0f) @@ -1454,12 +2010,32 @@ void ImGui::NewFrame() SaveSettings(); } + // Find the window we are hovering. Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow g.HoveredWindow = FindHoveredWindow(g.IO.MousePos, false); - g.HoveredRootWindow = FindHoveredWindow(g.IO.MousePos, true); + if (g.HoveredWindow && (g.HoveredWindow->Flags & ImGuiWindowFlags_ChildWindow)) + g.HoveredRootWindow = g.HoveredWindow->RootWindow; + else + g.HoveredRootWindow = FindHoveredWindow(g.IO.MousePos, true); - // Are we using inputs? Tell user so they can capture/discard them. - g.IO.WantCaptureMouse = (g.HoveredWindow != NULL) || (g.ActiveId != 0); + // Are we using inputs? Tell user so they can capture/discard the inputs away from the rest of their application. + // When clicking outside of a window we assume the click is owned by the application and won't request capture. + int mouse_earliest_button_down = -1; + for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) + { + if (g.IO.MouseClicked[i]) + g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL); + if (g.IO.MouseDown[i]) + if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[mouse_earliest_button_down] > g.IO.MouseClickedTime[i]) + mouse_earliest_button_down = i; + } + bool mouse_owned_by_application = mouse_earliest_button_down != -1 && !g.IO.MouseDownOwned[mouse_earliest_button_down]; + g.IO.WantCaptureMouse = (!mouse_owned_by_application && g.HoveredWindow != NULL) || (g.ActiveId != 0); g.IO.WantCaptureKeyboard = (g.ActiveId != 0); + g.MouseCursor = ImGuiMouseCursor_Arrow; + + // If mouse was first clicked outside of ImGui bounds we also cancel out hovering. + if (mouse_owned_by_application) + g.HoveredWindow = g.HoveredRootWindow = NULL; // Scale & Scrolling if (g.HoveredWindow && g.IO.MouseWheel != 0.0f) @@ -1487,14 +2063,14 @@ void ImGui::NewFrame() if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse)) { const int scroll_lines = (window->Flags & ImGuiWindowFlags_ComboBox) ? 3 : 5; - window->NextScrollY -= g.IO.MouseWheel * window->FontSize() * scroll_lines; + window->NextScrollY -= g.IO.MouseWheel * window->CalcFontSize() * scroll_lines; } } } // Pressing TAB activate widget focus // NB: Don't discard FocusedWindow if it isn't active, so that a window that go on/off programatically won't lose its keyboard focus. - if (g.ActiveId == 0 && g.FocusedWindow != NULL && g.FocusedWindow->Visible && IsKeyPressedMap(ImGuiKey_Tab, false)) + if (g.ActiveId == 0 && g.FocusedWindow != NULL && g.FocusedWindow->Active && IsKeyPressedMap(ImGuiKey_Tab, false)) { g.FocusedWindow->FocusIdxTabRequestNext = 0; } @@ -1503,22 +2079,24 @@ void ImGui::NewFrame() for (size_t i = 0; i != g.Windows.size(); i++) { ImGuiWindow* window = g.Windows[i]; - window->Visible = false; + window->WasActive = window->Active; + window->Active = false; window->Accessed = false; } // No window should be open at the beginning of the frame. // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. - g.CurrentWindowStack.clear(); + g.CurrentWindowStack.resize(0); // Create implicit window - we will only render it if the user has added something to it. - ImGui::Begin("Debug", NULL, ImVec2(400,400)); + ImGui::SetNextWindowSize(ImVec2(400,400), ImGuiSetCond_FirstUseEver); + ImGui::Begin("Debug"); } -// NB: behaviour of ImGui after Shutdown() is not tested/guaranteed at the moment. This function is merely here to free heap allocations. +// NB: behavior of ImGui after Shutdown() is not tested/guaranteed at the moment. This function is merely here to free heap allocations. void ImGui::Shutdown() { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; if (!g.Initialized) return; @@ -1530,8 +2108,8 @@ void ImGui::Shutdown() ImGui::MemFree(g.Windows[i]); } g.Windows.clear(); + g.WindowsSortBuffer.clear(); g.CurrentWindowStack.clear(); - g.RenderDrawLists.clear(); g.FocusedWindow = NULL; g.HoveredWindow = NULL; g.HoveredRootWindow = NULL; @@ -1541,44 +2119,63 @@ void ImGui::Shutdown() ImGui::MemFree(g.Settings[i]); } g.Settings.clear(); + g.ColorModifiers.clear(); + g.StyleModifiers.clear(); + g.FontStack.clear(); + for (size_t i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++) + g.RenderDrawLists[i].clear(); + g.MouseCursorDrawList.ClearFreeMemory(); g.ColorEditModeStorage.Clear(); - if (g.LogFile && g.LogFile != stdout) - { - fclose(g.LogFile); - g.LogFile = NULL; - } - if (g.IO.Font) - { - g.IO.Font->~ImFont(); - ImGui::MemFree(g.IO.Font); - g.IO.Font = NULL; - } - if (g.PrivateClipboard) { ImGui::MemFree(g.PrivateClipboard); g.PrivateClipboard = NULL; } + if (g.LogFile && g.LogFile != stdout) + { + fclose(g.LogFile); + g.LogFile = NULL; + } if (g.LogClipboard) { g.LogClipboard->~ImGuiTextBuffer(); ImGui::MemFree(g.LogClipboard); } + if (g.IO.Fonts) // Testing for NULL to allow user to NULLify in case of running Shutdown() on multiple contexts. Bit hacky. + g.IO.Fonts->Clear(); + g.Initialized = false; } -static void AddWindowToSortedBuffer(ImGuiWindow* window, ImVector& sorted_windows) +// FIXME: Add a more explicit sort order in the window structure. +static int ChildWindowComparer(const void* lhs, const void* rhs) { - sorted_windows.push_back(window); - if (window->Visible) + const ImGuiWindow* a = *(const ImGuiWindow**)lhs; + const ImGuiWindow* b = *(const ImGuiWindow**)rhs; + if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup)) + return d; + if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip)) + return d; + if (int d = (a->Flags & ImGuiWindowFlags_ComboBox) - (b->Flags & ImGuiWindowFlags_ComboBox)) + return d; + return 0; +} + +static void AddWindowToSortedBuffer(ImVector& out_sorted_windows, ImGuiWindow* window) +{ + out_sorted_windows.push_back(window); + if (window->Active) { - for (size_t i = 0; i < window->DC.ChildWindows.size(); i++) + const size_t count = window->DC.ChildWindows.size(); + if (count > 1) + qsort(window->DC.ChildWindows.begin(), count, sizeof(ImGuiWindow*), ChildWindowComparer); + for (size_t i = 0; i < count; i++) { ImGuiWindow* child = window->DC.ChildWindows[i]; - if (child->Visible) - AddWindowToSortedBuffer(child, sorted_windows); + if (child->Active) + AddWindowToSortedBuffer(out_sorted_windows, child); } } } @@ -1590,11 +2187,14 @@ static void PushClipRect(const ImVec4& clip_rect, bool clipped = true) ImVec4 cr = clip_rect; if (clipped && !window->ClipRectStack.empty()) { - // Clip to new clip rect + // Clip with existing clip rect const ImVec4 cur_cr = window->ClipRectStack.back(); cr = ImVec4(ImMax(cr.x, cur_cr.x), ImMax(cr.y, cur_cr.y), ImMin(cr.z, cur_cr.z), ImMin(cr.w, cur_cr.w)); } + cr.z = ImMax(cr.x, cr.z); + cr.w = ImMax(cr.y, cr.w); + IM_ASSERT(cr.x <= cr.z && cr.y <= cr.w); window->ClipRectStack.push_back(cr); window->DrawList->PushClipRect(cr); } @@ -1608,72 +2208,124 @@ static void PopClipRect() void ImGui::Render() { - ImGuiState& g = GImGui; - IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame() + ImGuiState& g = *GImGui; + IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame() const bool first_render_of_the_frame = (g.FrameCountRendered != g.FrameCount); g.FrameCountRendered = g.FrameCount; if (first_render_of_the_frame) { - // Hide implicit window if it hasn't been used + // Hide implicit "Debug" window if it hasn't been used IM_ASSERT(g.CurrentWindowStack.size() == 1); // Mismatched Begin/End if (g.CurrentWindow && !g.CurrentWindow->Accessed) - g.CurrentWindow->Visible = false; + g.CurrentWindow->Active = false; ImGui::End(); + // Click to focus window and start moving (after we're done with all our widgets) + if (g.ActiveId == 0 && g.HoveredId == 0 && g.IO.MouseClicked[0]) + { + if (g.HoveredRootWindow != NULL) + { + IM_ASSERT(g.MovedWindow == NULL); + g.MovedWindow = g.HoveredWindow; + SetActiveId(g.HoveredRootWindow->MoveID); + } + else if (g.FocusedWindow != NULL) + { + // Clicking on void disable focus + FocusWindow(NULL); + } + } + // Sort the window list so that all child windows are after their parent // We cannot do that on FocusWindow() because childs may not exist yet - ImVector sorted_windows; - sorted_windows.reserve(g.Windows.size()); + g.WindowsSortBuffer.resize(0); + g.WindowsSortBuffer.reserve(g.Windows.size()); for (size_t i = 0; i != g.Windows.size(); i++) { ImGuiWindow* window = g.Windows[i]; - if (window->Flags & ImGuiWindowFlags_ChildWindow) // if a child is visible its parent will add it - if (window->Visible) + if (window->Flags & ImGuiWindowFlags_ChildWindow) // if a child is active its parent will add it + if (window->Active) continue; - AddWindowToSortedBuffer(window, sorted_windows); + AddWindowToSortedBuffer(g.WindowsSortBuffer, window); } - IM_ASSERT(g.Windows.size() == sorted_windows.size()); // We done something wrong - g.Windows.swap(sorted_windows); + IM_ASSERT(g.Windows.size() == g.WindowsSortBuffer.size()); // we done something wrong + g.Windows.swap(g.WindowsSortBuffer); - // Clear data for next frame + // Clear Input data for next frame g.IO.MouseWheel = 0.0f; memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters)); } // Skip render altogether if alpha is 0.0 - // Note that vertex buffers have been created, so it is best practice that you don't call Begin/End in the first place. + // Note that vertex buffers have been created, so it is best practice that you don't create windows in the first place, or respond to Begin() returning false if (g.Style.Alpha > 0.0f) { // Render tooltip if (g.Tooltip[0]) { - // Use a dummy window to render the tooltip ImGui::BeginTooltip(); ImGui::TextUnformatted(g.Tooltip); ImGui::EndTooltip(); } // Gather windows to render - g.RenderDrawLists.resize(0); + g.IO.MetricsRenderVertices = 0; + g.IO.MetricsActiveWindows = 0; + for (size_t i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++) + g.RenderDrawLists[i].resize(0); for (size_t i = 0; i != g.Windows.size(); i++) { ImGuiWindow* window = g.Windows[i]; - if (window->Visible && (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0) - window->AddToRenderList(); + if (window->Active && window->HiddenFrames <= 0 && (window->Flags & (ImGuiWindowFlags_ChildWindow)) == 0) + { + // FIXME: Generalize this with a proper layering system so we can stack. + g.IO.MetricsActiveWindows++; + if (window->Flags & ImGuiWindowFlags_Popup) + AddWindowToRenderList(g.RenderDrawLists[1], window); + else if (window->Flags & ImGuiWindowFlags_Tooltip) + AddWindowToRenderList(g.RenderDrawLists[2], window); + else + AddWindowToRenderList(g.RenderDrawLists[0], window); + } } - for (size_t i = 0; i != g.Windows.size(); i++) + + // Flatten layers + size_t n = g.RenderDrawLists[0].size(); + size_t flattened_size = n; + for (size_t i = 1; i < IM_ARRAYSIZE(g.RenderDrawLists); i++) + flattened_size += g.RenderDrawLists[i].size(); + g.RenderDrawLists[0].resize(flattened_size); + for (size_t i = 1; i < IM_ARRAYSIZE(g.RenderDrawLists); i++) { - ImGuiWindow* window = g.Windows[i]; - if (window->Visible && (window->Flags & ImGuiWindowFlags_Tooltip)) - window->AddToRenderList(); + ImVector& layer = g.RenderDrawLists[i]; + if (!layer.empty()) + { + memcpy(&g.RenderDrawLists[0][n], &layer[0], layer.size() * sizeof(ImDrawList*)); + n += layer.size(); + } + } + + if (g.IO.MouseDrawCursor) + { + const ImGuiMouseCursorData& cursor_data = g.MouseCursorData[g.MouseCursor]; + const ImVec2 pos = g.IO.MousePos - cursor_data.Offset; + const ImVec2 size = cursor_data.Size; + const ImTextureID tex_id = g.IO.Fonts->TexID; + g.MouseCursorDrawList.Clear(); + g.MouseCursorDrawList.PushTextureID(tex_id); + g.MouseCursorDrawList.AddImage(tex_id, pos+ImVec2(1,0), pos+ImVec2(1,0) + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], 0x30000000); // Shadow + g.MouseCursorDrawList.AddImage(tex_id, pos+ImVec2(2,0), pos+ImVec2(2,0) + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], 0x30000000); // Shadow + g.MouseCursorDrawList.AddImage(tex_id, pos, pos + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], 0xFF000000); // Black border + g.MouseCursorDrawList.AddImage(tex_id, pos, pos + size, cursor_data.TexUvMin[0], cursor_data.TexUvMax[0], 0xFFFFFFFF); // White fill + g.MouseCursorDrawList.PopTextureID(); + AddDrawListToRenderList(g.RenderDrawLists[0], &g.MouseCursorDrawList); } // Render - if (!g.RenderDrawLists.empty()) - g.IO.RenderDrawListsFn(&g.RenderDrawLists[0], (int)g.RenderDrawLists.size()); - g.RenderDrawLists.resize(0); + if (!g.RenderDrawLists[0].empty()) + g.IO.RenderDrawListsFn(&g.RenderDrawLists[0][0], (int)g.RenderDrawLists[0].size()); } } @@ -1681,27 +2333,63 @@ void ImGui::Render() static const char* FindTextDisplayEnd(const char* text, const char* text_end = NULL) { const char* text_display_end = text; - while ((!text_end || text_display_end < text_end) && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#')) - text_display_end++; + if (!text_end) + text_end = (const char*)-1; + + ImGuiState& g = *GImGui; + if (g.DisableHideTextAfterDoubleHash > 0) + { + while (text_display_end < text_end && *text_display_end != '\0') + text_display_end++; + } + else + { + while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#')) + text_display_end++; + } return text_display_end; } -// Log ImGui display into text output (tty or file or clipboard) +// Pass text data straight to log (without being displayed) +void ImGui::LogText(const char* fmt, ...) +{ + ImGuiState& g = *GImGui; + if (!g.LogEnabled) + return; + + va_list args; + va_start(args, fmt); + if (g.LogFile) + { + vfprintf(g.LogFile, fmt, args); + } + else + { + g.LogClipboard->appendv(fmt, args); + } + va_end(args); +} + +// Internal version that takes a position to decide on newline placement and pad items according to their depth. +// We split text into individual lines to add current tree level padding static void LogText(const ImVec2& ref_pos, const char* text, const char* text_end) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (!text_end) text_end = FindTextDisplayEnd(text, text_end); - const bool log_new_line = ref_pos.y > window->DC.LogLineHeight+1; - window->DC.LogLineHeight = ref_pos.y; + const bool log_new_line = ref_pos.y > window->DC.LogLinePosY+1; + window->DC.LogLinePosY = ref_pos.y; const char* text_remaining = text; - const int tree_depth = window->DC.TreeDepth; - while (true) + if (g.LogStartDepth > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth + g.LogStartDepth = window->DC.TreeDepth; + const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth); + for (;;) { + // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry. const char* line_end = text_remaining; while (line_end < text_end) if (*line_end == '\n') @@ -1721,20 +2409,10 @@ static void LogText(const ImVec2& ref_pos, const char* text, const char* text_en if (line_end != NULL && !(is_last_line && (line_end - text_remaining)==0)) { const int char_count = (int)(line_end - text_remaining); - if (g.LogFile) - { - if (log_new_line || !is_first_line) - fprintf(g.LogFile, "\n%*s%.*s", tree_depth*4, "", char_count, text_remaining); - else - fprintf(g.LogFile, " %.*s", char_count, text_remaining); - } + if (log_new_line || !is_first_line) + ImGui::LogText(STR_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, text_remaining); else - { - if (log_new_line || !is_first_line) - g.LogClipboard->append("\n%*s%.*s", tree_depth*4, "", char_count, text_remaining); - else - g.LogClipboard->append(" %.*s", char_count, text_remaining); - } + ImGui::LogText(" %.*s", char_count, text_remaining); } if (is_last_line) @@ -1758,11 +2436,11 @@ static float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) return wrap_width; } -// Internal ImGui function to render text (called from ImGui::Text(), ImGui::TextUnformatted(), etc.) -// RenderText() calls ImDrawList::AddText() calls ImBitmapFont::RenderText() -static void RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash, float wrap_width) +// Internal ImGui functions to render text +// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText() +static void RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); // Hide anything after a '##' string @@ -1782,14 +2460,57 @@ static void RenderText(ImVec2 pos, const char* text, const char* text_end, bool if (text_len > 0) { // Render - window->DrawList->AddText(window->Font(), window->FontSize(), pos, window->Color(ImGuiCol_Text), text, text + text_len, wrap_width); + window->DrawList->AddText(g.Font, g.FontSize, pos, window->Color(ImGuiCol_Text), text, text_display_end); - // Log as text. We split text into individual lines to add current tree level padding + // Log as text if (g.LogEnabled) LogText(pos, text, text_display_end); } } +static void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width) +{ + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + if (!text_end) + text_end = text + strlen(text); // FIXME-OPT + + const int text_len = (int)(text_end - text); + if (text_len > 0) + { + window->DrawList->AddText(g.Font, g.FontSize, pos, window->Color(ImGuiCol_Text), text, text_end, wrap_width); + if (g.LogEnabled) + LogText(pos, text, text_end); + } +} + +static void RenderTextClipped(ImVec2 pos, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& pos_max, const ImVec2* clip_max, ImGuiAlign align) +{ + // Hide anything after a '##' string + const char* text_display_end = FindTextDisplayEnd(text, text_end); + const int text_len = (int)(text_display_end - text); + if (text_len == 0) + return; + + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + // Perform CPU side clipping for single clipped element to avoid using scissor state + const ImVec2 text_size = text_size_if_known ? *text_size_if_known : ImGui::CalcTextSize(text, text_display_end, false, 0.0f); + if (!clip_max) clip_max = &pos_max; + const bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y); + + // Align + if (align & ImGuiAlign_Center) pos.x = ImMax(pos.x, (pos.x + pos_max.x - text_size.x) * 0.5f); + else if (align & ImGuiAlign_Right) pos.x = ImMax(pos.x, pos_max.x - text_size.x); + + // Render + window->DrawList->AddText(g.Font, g.FontSize, pos, window->Color(ImGuiCol_Text), text, text_display_end, 0.0f, need_clipping ? clip_max : NULL); + if (g.LogEnabled) + LogText(pos, text, text_display_end); +} + // Render a rectangle shaped with optional rounding and borders static void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding) { @@ -1798,24 +2519,24 @@ static void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding); if (border && (window->Flags & ImGuiWindowFlags_ShowBorders)) { - // FIXME: This is the best I've found that works on multiple renderer/back ends. Rather dodgy. - const float offset = GImGui.IO.PixelCenterOffset; - window->DrawList->AddRect(p_min+ImVec2(1.5f-offset,1.5f-offset), p_max+ImVec2(1.0f-offset*2,1.0f-offset*2), window->Color(ImGuiCol_BorderShadow), rounding); - window->DrawList->AddRect(p_min+ImVec2(0.5f-offset,0.5f-offset), p_max+ImVec2(0.0f-offset*2,0.0f-offset*2), window->Color(ImGuiCol_Border), rounding); + // FIXME: This is the best I've found that works on multiple renderer/back ends. Bit dodgy. + window->DrawList->AddRect(p_min+ImVec2(1.5f,1.5f), p_max+ImVec2(1,1), window->Color(ImGuiCol_BorderShadow), rounding); + window->DrawList->AddRect(p_min+ImVec2(0.5f,0.5f), p_max+ImVec2(0,0), window->Color(ImGuiCol_Border), rounding); } } // Render a triangle to denote expanded/collapsed state -static void RenderCollapseTriangle(ImVec2 p_min, bool open, float scale, bool shadow) +static void RenderCollapseTriangle(ImVec2 p_min, bool opened, float scale, bool shadow) { + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); - const float h = window->FontSize() * 1.00f; + const float h = g.FontSize * 1.00f; const float r = h * 0.40f * scale; ImVec2 center = p_min + ImVec2(h*0.50f, h*0.50f*scale); ImVec2 a, b, c; - if (open) + if (opened) { center.y -= r*0.25f; a = center + ImVec2(0,1)*r; @@ -1831,14 +2552,33 @@ static void RenderCollapseTriangle(ImVec2 p_min, bool open, float scale, bool sh if (shadow && (window->Flags & ImGuiWindowFlags_ShowBorders) != 0) window->DrawList->AddTriangleFilled(a+ImVec2(2,2), b+ImVec2(2,2), c+ImVec2(2,2), window->Color(ImGuiCol_BorderShadow)); - window->DrawList->AddTriangleFilled(a, b, c, window->Color(ImGuiCol_Border)); + window->DrawList->AddTriangleFilled(a, b, c, window->Color(ImGuiCol_Text)); +} + +static void RenderCheckMark(ImVec2 pos, ImU32 col) +{ + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + ImVec2 a, b, c; + float start_x = (float)(int)(g.FontSize * 0.307f + 0.5f); + float rem_third = (float)(int)((g.FontSize - start_x) / 3.0f); + a.x = pos.x + start_x; + b.x = a.x + rem_third; + c.x = a.x + rem_third * 3.0f; + b.y = pos.y + (float)(int)(g.Font->BaseLine * (g.FontSize / g.Font->FontSize) + 0.5f) + (float)(int)(g.Font->DisplayOffset.y); + a.y = b.y - rem_third; + c.y = b.y - rem_third * 2.0f; + + window->DrawList->AddLine(a, b, col); + window->DrawList->AddLine(b, c, col); } // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker. -// CalcTextSize("") should return ImVec2(0.0f, GImGui.FontSize) +// CalcTextSize("") should return ImVec2(0.0f, GImGui->FontSize) ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width) { - ImGuiWindow* window = GetCurrentWindow(); + ImGuiState& g = *GImGui; const char* text_display_end; if (hide_text_after_double_hash) @@ -1846,64 +2586,113 @@ ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_tex else text_display_end = text_end; - const ImVec2 text_size = window->Font()->CalcTextSizeA(window->FontSize(), FLT_MAX, wrap_width, text, text_display_end, NULL); + ImFont* font = g.Font; + const float font_size = g.FontSize; + ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL); + + // Cancel out character spacing for the last character of a line (it is baked into glyph->XAdvance field) + const float font_scale = font_size / font->FontSize; + const float character_spacing_x = 1.0f * font_scale; + if (text_size.x > 0.0f) + text_size.x -= character_spacing_x; + return text_size; } +// Helper to calculate coarse clipping of large list of evenly sized items. +// If you are displaying thousands of items and you have a random access to the list, you can perform clipping yourself to save on CPU. +// { +// float item_height = ImGui::GetTextLineHeightWithSpacing(); +// int display_start, display_end; +// ImGui::CalcListClipping(count, item_height, &display_start, &display_end); // calculate how many to clip/display +// ImGui::SetCursorPosY(ImGui::GetCursorPosY() + (display_start) * item_height); // advance cursor +// for (int i = display_start; i < display_end; i++) // display only visible items +// // TODO: display visible item +// ImGui::SetCursorPosY(ImGui::GetCursorPosY() + (count - display_end) * item_height); // advance cursor +// } +void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end) +{ + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + if (g.LogEnabled) + { + // If logging is active, do not perform any clipping + *out_items_display_start = 0; + *out_items_display_end = items_count; + return; + } + + const ImVec2 pos = window->DC.CursorPos; + const ImVec4 clip_rect = window->ClipRectStack.back(); + const float clip_y1 = clip_rect.y; + const float clip_y2 = clip_rect.w; + + int start = (int)((clip_y1 - pos.y) / items_height); + int end = (int)((clip_y2 - pos.y) / items_height); + start = ImClamp(start, 0, items_count); + end = ImClamp(end + 1, start, items_count); + + *out_items_display_start = start; + *out_items_display_end = end; +} + // Find window given position, search front-to-back static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; for (int i = (int)g.Windows.size()-1; i >= 0; i--) { ImGuiWindow* window = g.Windows[(size_t)i]; - if (!window->Visible) + if (!window->Active) continue; if (excluding_childs && (window->Flags & ImGuiWindowFlags_ChildWindow) != 0) continue; - ImGuiAabb bb(window->Pos - g.Style.TouchExtraPadding, window->Pos+window->Size + g.Style.TouchExtraPadding); + + // Using the clipped AABB so a child window will typically be clipped by its parent. + ImRect bb(window->ClippedRect.Min - g.Style.TouchExtraPadding, window->ClippedRect.Max + g.Style.TouchExtraPadding); if (bb.Contains(pos)) return window; } return NULL; } -// Test if mouse cursor is hovering given aabb -// NB- Box is clipped by our current clip setting -// NB- Expand the aabb to be generous on imprecise inputs systems (g.Style.TouchExtraPadding) -static bool IsMouseHoveringBox(const ImGuiAabb& box) +// Test if mouse cursor is hovering given rectangle +// NB- Rectangle is clipped by our current clip setting +// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding) +static bool IsMouseHoveringRect(const ImRect& rect) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); // Clip - ImGuiAabb box_clipped = box; + ImRect rect_clipped = rect; if (!window->ClipRectStack.empty()) { const ImVec4 clip_rect = window->ClipRectStack.back(); - box_clipped.Clip(ImGuiAabb(ImVec2(clip_rect.x, clip_rect.y), ImVec2(clip_rect.z, clip_rect.w))); + rect_clipped.Clip(ImRect(ImVec2(clip_rect.x, clip_rect.y), ImVec2(clip_rect.z, clip_rect.w))); } // Expand for touch input - const ImGuiAabb box_for_touch(box_clipped.Min - g.Style.TouchExtraPadding, box_clipped.Max + g.Style.TouchExtraPadding); - return box_for_touch.Contains(g.IO.MousePos); + const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); + return rect_for_touch.Contains(g.IO.MousePos); } -bool ImGui::IsMouseHoveringBox(const ImVec2& box_min, const ImVec2& box_max) +bool ImGui::IsMouseHoveringRect(const ImVec2& rect_min, const ImVec2& rect_max) { - return IsMouseHoveringBox(ImGuiAabb(box_min, box_max)); + return IsMouseHoveringRect(ImRect(rect_min, rect_max)); } bool ImGui::IsMouseHoveringWindow() { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); return g.HoveredWindow == window; } bool ImGui::IsMouseHoveringAnyWindow() { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; return g.HoveredWindow != NULL; } @@ -1914,14 +2703,21 @@ bool ImGui::IsPosHoveringAnyWindow(const ImVec2& pos) static bool IsKeyPressedMap(ImGuiKey key, bool repeat) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; const int key_index = g.IO.KeyMap[key]; return ImGui::IsKeyPressed(key_index, repeat); } +bool ImGui::IsKeyDown(int key_index) +{ + ImGuiState& g = *GImGui; + IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown)); + return g.IO.KeysDown[key_index]; +} + bool ImGui::IsKeyPressed(int key_index, bool repeat) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown)); const float t = g.IO.KeysDownTime[key_index]; if (t == 0.0f) @@ -1937,9 +2733,16 @@ bool ImGui::IsKeyPressed(int key_index, bool repeat) return false; } +bool ImGui::IsMouseDown(int button) +{ + ImGuiState& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseDown[button]; +} + bool ImGui::IsMouseClicked(int button, bool repeat) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); const float t = g.IO.MouseDownTime[button]; if (t == 0.0f) @@ -1957,44 +2760,121 @@ bool ImGui::IsMouseClicked(int button, bool repeat) bool ImGui::IsMouseDoubleClicked(int button) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); return g.IO.MouseDoubleClicked[button]; } +bool ImGui::IsMouseDragging(int button, float lock_threshold) +{ + ImGuiState& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + if (lock_threshold < 0.0f) + lock_threshold = g.IO.MouseDragThreshold; + return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold; +} + ImVec2 ImGui::GetMousePos() { - return GImGui.IO.MousePos; + return GImGui->IO.MousePos; +} + +ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold) +{ + ImGuiState& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + if (lock_threshold < 0.0f) + lock_threshold = g.IO.MouseDragThreshold; + if (g.IO.MouseDown[button]) + if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold) + return g.IO.MousePos - g.IO.MouseClickedPos[button]; // Assume we can only get active with left-mouse button (at the moment). + return ImVec2(0.0f, 0.0f); +} + +void ImGui::ResetMouseDragDelta(int button) +{ + ImGuiState& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr + g.IO.MouseClickedPos[button] = g.IO.MousePos; +} + +ImGuiMouseCursor ImGui::GetMouseCursor() +{ + return GImGui->MouseCursor; +} + +void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type) +{ + GImGui->MouseCursor = cursor_type; } bool ImGui::IsItemHovered() { ImGuiWindow* window = GetCurrentWindow(); - return window->DC.LastItemHovered; + return window->DC.LastItemHoveredAndUsable; } -bool ImGui::IsItemFocused() +bool ImGui::IsItemHoveredRect() { ImGuiWindow* window = GetCurrentWindow(); - return window->DC.LastItemFocused; + return window->DC.LastItemHoveredRect; } -ImVec2 ImGui::GetItemBoxMin() +bool ImGui::IsItemActive() { - ImGuiWindow* window = GetCurrentWindow(); - return window->DC.LastItemAabb.Min; + ImGuiState& g = *GImGui; + if (g.ActiveId) + { + ImGuiWindow* window = GetCurrentWindow(); + return g.ActiveId == window->DC.LastItemID; + } + return false; } -ImVec2 ImGui::GetItemBoxMax() +bool ImGui::IsAnyItemActive() +{ + ImGuiState& g = *GImGui; + return g.ActiveId != 0; +} + +bool ImGui::IsItemVisible() { ImGuiWindow* window = GetCurrentWindow(); - return window->DC.LastItemAabb.Max; + ImRect r(window->ClipRectStack.back()); + return r.Overlaps(window->DC.LastItemRect); +} + +ImVec2 ImGui::GetItemRectMin() +{ + ImGuiWindow* window = GetCurrentWindow(); + return window->DC.LastItemRect.Min; +} + +ImVec2 ImGui::GetItemRectMax() +{ + ImGuiWindow* window = GetCurrentWindow(); + return window->DC.LastItemRect.Max; +} + +ImVec2 ImGui::GetItemRectSize() +{ + ImGuiWindow* window = GetCurrentWindow(); + return window->DC.LastItemRect.GetSize(); +} + +ImVec2 ImGui::CalcItemRectClosestPoint(const ImVec2& pos, bool on_edge, float outward) +{ + ImGuiWindow* window = GetCurrentWindow(); + ImRect rect = window->DC.LastItemRect; + rect.Expand(outward); + return rect.GetClosestPoint(pos, on_edge); } // Tooltip is stored and turned into a BeginTooltip()/EndTooltip() sequence at the end of the frame. Each call override previous value. void ImGui::SetTooltipV(const char* fmt, va_list args) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImFormatStringV(g.Tooltip, IM_ARRAYSIZE(g.Tooltip), fmt, args); } @@ -2006,35 +2886,29 @@ void ImGui::SetTooltip(const char* fmt, ...) va_end(args); } -// Position new window if they don't have position setting in the .ini file. Rarely useful (used by the sample applications). -void ImGui::SetNewWindowDefaultPos(const ImVec2& pos) -{ - ImGuiState& g = GImGui; - g.NewWindowDefaultPos = pos; -} - float ImGui::GetTime() { - return GImGui.Time; + return GImGui->Time; } int ImGui::GetFrameCount() { - return GImGui.FrameCount; + return GImGui->FrameCount; } -static ImGuiWindow* FindWindow(const char* name) +static ImVec4 GetVisibleRect() { - ImGuiState& g = GImGui; - for (size_t i = 0; i != g.Windows.size(); i++) - if (strcmp(g.Windows[i]->Name, name) == 0) - return g.Windows[i]; - return NULL; + ImGuiState& g = *GImGui; + if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y) + return ImVec4(g.IO.DisplayVisibleMin.x, g.IO.DisplayVisibleMin.y, g.IO.DisplayVisibleMax.x, g.IO.DisplayVisibleMax.y); + return ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); } void ImGui::BeginTooltip() { - ImGui::Begin("##Tooltip", NULL, ImVec2(0,0), 0.9f, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_Tooltip); + ImGuiState& g = *GImGui; + ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize; + ImGui::Begin("##Tooltip", NULL, ImVec2(0,0), g.Style.Colors[ImGuiCol_TooltipBg].w, flags); } void ImGui::EndTooltip() @@ -2043,15 +2917,44 @@ void ImGui::EndTooltip() ImGui::End(); } -void ImGui::BeginChild(const char* str_id, ImVec2 size, bool border, ImGuiWindowFlags extra_flags) +void ImGui::BeginPopup(bool* p_opened) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + IM_ASSERT(p_opened != NULL); // Must provide a bool at the moment + + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGuiWindowFlags flags = ImGuiWindowFlags_Popup|ImGuiWindowFlags_ShowBorders|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize; + float alpha = 1.0f; + + char name[20]; + ImFormatString(name, 20, "##Popup%02d", g.CurrentPopupStackSize++); + ImGui::Begin(name, p_opened, ImVec2(0.0f, 0.0f), alpha, flags); + + if (!(window->Flags & ImGuiWindowFlags_ShowBorders)) + GetCurrentWindow()->Flags &= ~ImGuiWindowFlags_ShowBorders; +} + +void ImGui::EndPopup() +{ + ImGuiState& g = *GImGui; + IM_ASSERT(GetCurrentWindow()->Flags & ImGuiWindowFlags_Popup); + IM_ASSERT(g.CurrentPopupStackSize > 0); + g.CurrentPopupStackSize--; + ImGui::End(); + ImGui::PopStyleVar(); +} + +bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) +{ + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); - ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_ChildWindow; + ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow; const ImVec2 content_max = window->Pos + ImGui::GetContentRegionMax(); const ImVec2 cursor_pos = window->Pos + ImGui::GetCursorPos(); + ImVec2 size = size_arg; if (size.x <= 0.0f) { if (size.x == 0.0f) @@ -2071,77 +2974,247 @@ void ImGui::BeginChild(const char* str_id, ImVec2 size, bool border, ImGuiWindow char title[256]; ImFormatString(title, IM_ARRAYSIZE(title), "%s.%s", window->Name, str_id); - const float alpha = (flags & ImGuiWindowFlags_ComboBox) ? 1.0f : 0.0f; - ImGui::Begin(title, NULL, size, alpha, flags); + const float alpha = 1.0f; + bool ret = ImGui::Begin(title, NULL, size, alpha, flags); if (!(window->Flags & ImGuiWindowFlags_ShowBorders)) - g.CurrentWindow->Flags &= ~ImGuiWindowFlags_ShowBorders; + GetCurrentWindow()->Flags &= ~ImGuiWindowFlags_ShowBorders; + + return ret; +} + +bool ImGui::BeginChild(ImGuiID id, const ImVec2& size, bool border, ImGuiWindowFlags extra_flags) +{ + char str_id[32]; + ImFormatString(str_id, IM_ARRAYSIZE(str_id), "child_%x", id); + bool ret = ImGui::BeginChild(str_id, size, border, extra_flags); + return ret; } void ImGui::EndChild() { ImGuiWindow* window = GetCurrentWindow(); - if (window->Flags & ImGuiWindowFlags_ComboBox) + IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); + if ((window->Flags & ImGuiWindowFlags_ComboBox) || window->BeginCount > 1) { ImGui::End(); } else { - // When using auto-filling child window, we don't provide the width/height to ItemSize so that it doesn't feed back into automatic size-fitting. + // When using auto-filling child window, we don't provide full width/height to ItemSize so that it doesn't feed back into automatic size-fitting. + ImGuiState& g = *GImGui; ImVec2 sz = ImGui::GetWindowSize(); if (window->Flags & ImGuiWindowFlags_ChildWindowAutoFitX) - sz.x = 0; + sz.x = ImMax(g.Style.WindowMinSize.x, sz.x - g.Style.WindowPadding.x); if (window->Flags & ImGuiWindowFlags_ChildWindowAutoFitY) - sz.y = 0; + sz.y = ImMax(g.Style.WindowMinSize.y, sz.y - g.Style.WindowPadding.y); ImGui::End(); + + window = GetCurrentWindow(); + ImRect bb(window->DC.CursorPos, window->DC.CursorPos + sz); ItemSize(sz); + ItemAdd(bb, NULL); } } -// Push a new ImGui window to add widgets to. This can be called multiple times with the same window to append contents -bool ImGui::Begin(const char* name, bool* open, ImVec2 size, float fill_alpha, ImGuiWindowFlags flags) +// Helper to create a child window / scrolling region that looks like a normal widget frame. +void ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; const ImGuiStyle& style = g.Style; - IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame() + ImGui::PushStyleColor(ImGuiCol_ChildWindowBg, style.Colors[ImGuiCol_FrameBg]); + ImGui::PushStyleVar(ImGuiStyleVar_ChildWindowRounding, style.FrameRounding); + ImGui::BeginChild(id, size); +} - ImGuiWindow* window = FindWindow(name); - if (!window) +void ImGui::EndChildFrame() +{ + ImGui::EndChild(); + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); +} + +// Save and compare stack sizes on Begin()/End() to detect usage errors +static void CheckStacksSize(ImGuiWindow* window, bool write) +{ + // NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin) + ImGuiState& g = *GImGui; + int* p_backup = &window->DC.StackSizesBackup[0]; + { int current = (int)window->IDStack.size(); if (write) *p_backup = current; else IM_ASSERT(*p_backup == current); p_backup++; } // User forgot PopID() + { int current = (int)window->DC.GroupStack.size(); if (write) *p_backup = current; else IM_ASSERT(*p_backup == current); p_backup++; } // User forgot EndGroup() + { int current = (int)g.ColorModifiers.size(); if (write) *p_backup = current; else IM_ASSERT(*p_backup == current); p_backup++; } // User forgot PopStyleColor() + { int current = (int)g.StyleModifiers.size(); if (write) *p_backup = current; else IM_ASSERT(*p_backup == current); p_backup++; } // User forgot PopStyleVar() + { int current = (int)g.FontStack.size(); if (write) *p_backup = current; else IM_ASSERT(*p_backup == current); p_backup++; } // User forgot PopFont() + IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup)); +} + +static ImVec2 FindBestWindowPos(const ImVec2& mouse_pos, const ImVec2& size, int* last_dir, const ImRect& r_inner) +{ + const ImGuiStyle& style = GImGui->Style; + + // Clamp into visible area while not overlapping the cursor + ImRect r_outer(GetVisibleRect()); + r_outer.Reduce(style.DisplaySafeAreaPadding); + ImVec2 mouse_pos_clamped = ImClamp(mouse_pos, r_outer.Min, r_outer.Max - size); + + for (int n = (*last_dir != -1) ? -1 : 0; n < 4; n++) // Right, down, up, left. Favor last used direction. { - // Create window the first time, and load settings - if (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) + const int dir = (n == -1) ? *last_dir : n; + ImRect rect(dir == 0 ? r_inner.Max.x : r_outer.Min.x, dir == 1 ? r_inner.Max.y : r_outer.Min.y, dir == 3 ? r_inner.Min.x : r_outer.Max.x, dir == 2 ? r_inner.Min.y : r_outer.Max.y); + if (rect.GetWidth() < size.x || rect.GetHeight() < size.y) + continue; + *last_dir = dir; + return ImVec2(dir == 0 ? r_inner.Max.x : dir == 3 ? r_inner.Min.x - size.x : mouse_pos_clamped.x, dir == 1 ? r_inner.Max.y : dir == 2 ? r_inner.Min.y - size.y : mouse_pos_clamped.y); + } + + // Fallback + *last_dir = -1; + return mouse_pos + ImVec2(2,2); +} + +static ImGuiWindow* FindWindowByName(const char* name) +{ + // FIXME-OPT: Store sorted hashes -> pointers. + ImGuiState& g = *GImGui; + ImGuiID id = ImHash(name, 0); + for (size_t i = 0; i < g.Windows.size(); i++) + if (g.Windows[i]->ID == id) + return g.Windows[i]; + return NULL; +} + +static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags) +{ + ImGuiState& g = *GImGui; + + // Create window the first time + ImGuiWindow* window = (ImGuiWindow*)ImGui::MemAlloc(sizeof(ImGuiWindow)); + new(window) ImGuiWindow(name); + window->Flags = flags; + + if (flags & ImGuiWindowFlags_NoSavedSettings) + { + // User can disable loading and saving of settings. Tooltip and child windows also don't store settings. + window->Size = window->SizeFull = size; + } + else + { + // Retrieve settings from .ini file + // Use SetWindowPos() or SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. + window->PosFloat = ImVec2(60, 60); + window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y); + + ImGuiIniData* settings = FindWindowSettings(name); + if (!settings) { - // Tooltip and child windows don't store settings - window = (ImGuiWindow*)ImGui::MemAlloc(sizeof(ImGuiWindow)); - new(window) ImGuiWindow(name, ImVec2(0,0), size); + settings = AddWindowSettings(name); } else { - // Normal windows store settings in .ini file - ImGuiIniData* settings = FindWindowSettings(name); - if (settings && ImLength(settings->Size) > 0.0f && !(flags & ImGuiWindowFlags_NoResize))// && ImLengthsize) == 0.0f) - size = settings->Size; - - window = (ImGuiWindow*)ImGui::MemAlloc(sizeof(ImGuiWindow)); - new(window) ImGuiWindow(name, g.NewWindowDefaultPos, size); - - if (settings->Pos.x != FLT_MAX) - { - window->PosFloat = settings->Pos; - window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y); - window->Collapsed = settings->Collapsed; - } + window->SetWindowPosAllowFlags &= ~ImGuiSetCond_FirstUseEver; + window->SetWindowSizeAllowFlags &= ~ImGuiSetCond_FirstUseEver; + window->SetWindowCollapsedAllowFlags &= ~ImGuiSetCond_FirstUseEver; } - g.Windows.push_back(window); + + if (settings->Pos.x != FLT_MAX) + { + window->PosFloat = settings->Pos; + window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y); + window->Collapsed = settings->Collapsed; + } + + if (ImLengthSqr(settings->Size) > 0.00001f && !(flags & ImGuiWindowFlags_NoResize)) + size = settings->Size; + window->Size = window->SizeFull = size; + } + + if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0) + { + window->AutoFitFrames = 2; + window->AutoFitOnlyGrows = false; + } + else if (ImLengthSqr(window->Size) < 0.00001f) + { + window->AutoFitFrames = 2; + window->AutoFitOnlyGrows = true; + } + + g.Windows.push_back(window); + return window; +} + +// Push a new ImGui window to add widgets to. +// - 'size' for a regular window denote the initial size for first-time creation (no saved data) and isn't that useful. Use SetNextWindowSize() prior to calling Begin() for more flexible window manipulation. +// - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair. +// - Begin/End can be called multiple times during the frame with the same window name to append content. +// - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file). +// You can use the "##" or "###" markers to use the same label with different id, or same id with different label. See documentation at the top of this file. +// - Return false when window is collapsed, so you can early out in your code. You always need to call ImGui::End() even if false is returned. +// - Passing 'bool* p_opened' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed. +// - Passing non-zero 'size' is roughly equivalent to calling SetNextWindowSize(size, ImGuiSetCond_FirstUseEver) prior to calling Begin(). +bool ImGui::Begin(const char* name, bool* p_opened, ImGuiWindowFlags flags) +{ + return ImGui::Begin(name, p_opened, ImVec2(0.f, 0.f), -1.0f, flags); +} + +bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_use, float bg_alpha, ImGuiWindowFlags flags) +{ + ImGuiState& g = *GImGui; + const ImGuiStyle& style = g.Style; + IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame() + IM_ASSERT(name != NULL); // Must pass a name + + // Find or create + bool window_is_new = false; + ImGuiWindow* window = FindWindowByName(name); + if (!window) + { + window = CreateNewWindow(name, size_on_first_use, flags); + window_is_new = true; } window->Flags = (ImGuiWindowFlags)flags; - g.CurrentWindowStack.push_back(window); - g.CurrentWindow = window; + const int current_frame = ImGui::GetFrameCount(); + const bool window_was_visible = (window->LastFrameDrawn == current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on - // Find root + // Add to stack + ImGuiWindow* parent_window = !g.CurrentWindowStack.empty() ? g.CurrentWindowStack.back() : NULL; + g.CurrentWindowStack.push_back(window); + SetCurrentWindow(window); + CheckStacksSize(window, true); + + // Process SetNextWindow***() calls + bool window_pos_set_by_api = false; + if (g.SetNextWindowPosCond) + { + const ImVec2 backup_cursor_pos = window->DC.CursorPos; // FIXME: not sure of the exact reason of this anymore :( need to look into that. + if (!window_was_visible) window->SetWindowPosAllowFlags |= ImGuiSetCond_Appearing; + window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.SetNextWindowPosCond) != 0; + ImGui::SetWindowPos(g.SetNextWindowPosVal, g.SetNextWindowPosCond); + window->DC.CursorPos = backup_cursor_pos; + g.SetNextWindowPosCond = 0; + } + if (g.SetNextWindowSizeCond) + { + if (!window_was_visible) window->SetWindowSizeAllowFlags |= ImGuiSetCond_Appearing; + ImGui::SetWindowSize(g.SetNextWindowSizeVal, g.SetNextWindowSizeCond); + g.SetNextWindowSizeCond = 0; + } + if (g.SetNextWindowCollapsedCond) + { + if (!window_was_visible) window->SetWindowCollapsedAllowFlags |= ImGuiSetCond_Appearing; + ImGui::SetWindowCollapsed(g.SetNextWindowCollapsedVal, g.SetNextWindowCollapsedCond); + g.SetNextWindowCollapsedCond = 0; + } + if (g.SetNextWindowFocus) + { + ImGui::SetWindowFocus(); + g.SetNextWindowFocus = false; + } + + // Update known root window (if we are a child window, otherwise window == window->RootWindow) size_t root_idx = g.CurrentWindowStack.size() - 1; while (root_idx > 0) { @@ -2152,89 +3225,180 @@ bool ImGui::Begin(const char* name, bool* open, ImVec2 size, float fill_alpha, I window->RootWindow = g.CurrentWindowStack[root_idx]; // Default alpha - if (fill_alpha < 0.0f) - fill_alpha = style.WindowFillAlphaDefault; + if (bg_alpha < 0.0f) + bg_alpha = style.WindowFillAlphaDefault; // When reusing window again multiple times a frame, just append content (don't need to setup again) - const int current_frame = ImGui::GetFrameCount(); const bool first_begin_of_the_frame = (window->LastFrameDrawn != current_frame); if (first_begin_of_the_frame) { + window->Active = true; + window->BeginCount = 0; window->DrawList->Clear(); - window->Visible = true; + window->ClipRectStack.resize(0); + window->LastFrameDrawn = current_frame; + window->IDStack.resize(1); + + // Setup texture, outer clipping rectangle + window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID); + if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ComboBox)) + PushClipRect(parent_window->ClipRectStack.back()); + else + PushClipRect(GetVisibleRect()); // New windows appears in front - if (window->LastFrameDrawn < current_frame - 1) + if (!window_was_visible) { - FocusWindow(window); - if ((window->Flags & ImGuiWindowFlags_Tooltip) != 0) + window->AutoPosLastDirection = -1; + + if (!(flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) { - // Hide for 1 frame while resizing - window->AutoFitFrames = 2; - window->AutoFitOnlyGrows = false; - window->Visible = false; + FocusWindow(window); + + // Popup first latch mouse position, will position itself when it appears next frame + if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api) + window->PosFloat = g.IO.MousePos; } } - window->LastFrameDrawn = current_frame; - window->ClipRectStack.resize(0); - - if (flags & ImGuiWindowFlags_ChildWindow) + // Collapse window by double-clicking on title bar + // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing + if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse)) { - ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.size()-2]; - parent_window->DC.ChildWindows.push_back(window); - window->Pos = window->PosFloat = parent_window->DC.CursorPos; - window->SizeFull = size; + if (g.HoveredWindow == window && IsMouseHoveringRect(window->TitleBarRect()) && g.IO.MouseDoubleClicked[0]) + { + window->Collapsed = !window->Collapsed; + if (!(flags & ImGuiWindowFlags_NoSavedSettings)) + MarkSettingsDirty(); + FocusWindow(window); + } + } + else + { + window->Collapsed = false; } - // Outer clipping rectangle - if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ComboBox)) - PushClipRect(g.CurrentWindowStack[g.CurrentWindowStack.size()-2]->ClipRectStack.back()); + const bool window_appearing_after_being_hidden = (window->HiddenFrames == 1); + if (window->HiddenFrames > 0) + window->HiddenFrames--; + + // SIZE + + // Save contents size from last frame for auto-fitting + window->SizeContents = window_is_new ? ImVec2(0.0f, 0.0f) : window->DC.CursorMaxPos - window->Pos; + window->SizeContents.y += window->ScrollY; + + // Hide popup/tooltip window when first appearing while we measure size (because we recycle them) + if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0 && !window->WasActive) + { + window->HiddenFrames = 1; + window->Size = window->SizeFull = window->SizeContents = ImVec2(0.f, 0.f); // TODO: We don't support SetNextWindowSize() for tooltips or popups yet + } + + // Calculate auto-fit size + ImVec2 size_auto_fit; + if ((flags & ImGuiWindowFlags_Tooltip) != 0) + { + // Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose. + size_auto_fit = window->SizeContents + style.WindowPadding - ImVec2(0.0f, style.ItemSpacing.y); + } else - PushClipRect(ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y)); + { + size_auto_fit = ImClamp(window->SizeContents + style.WindowPadding, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - style.WindowPadding)); + if (size_auto_fit.y < window->SizeContents.y + style.WindowPadding.y) + size_auto_fit.x += style.ScrollbarWidth; + size_auto_fit.y -= style.ItemSpacing.y; + } - // Seed ID stack with our window pointer - window->IDStack.resize(0); - ImGui::PushID(window); + // Handle automatic resize + if (window->Collapsed) + { + // We still process initial auto-fit on collapsed windows to get a window width, + // But otherwise we don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed. + if (window->AutoFitFrames > 0) + window->SizeFull = window->AutoFitOnlyGrows ? ImMax(window->SizeFull, size_auto_fit) : size_auto_fit; + window->Size = window->TitleBarRect().GetSize(); + } + else + { + if (flags & ImGuiWindowFlags_AlwaysAutoResize) + { + window->SizeFull = size_auto_fit; + } + else if (window->AutoFitFrames > 0) + { + // Auto-fit only grows during the first few frames + window->SizeFull = window->AutoFitOnlyGrows ? ImMax(window->SizeFull, size_auto_fit) : size_auto_fit; + if (!(flags & ImGuiWindowFlags_NoSavedSettings)) + MarkSettingsDirty(); + } + window->Size = window->SizeFull; + } - // Move window (at the beginning of the frame to avoid lag) - const ImGuiID move_id = window->GetID("#MOVE"); - RegisterAliveId(move_id); - if (g.ActiveId == move_id) + // Minimum window size + if (!(flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) + window->SizeFull = ImMax(window->SizeFull, style.WindowMinSize); + + // POSITION + + // Position child window + if (flags & ImGuiWindowFlags_ChildWindow) + { + parent_window->DC.ChildWindows.push_back(window); + window->Pos = window->PosFloat = parent_window->DC.CursorPos; + window->SizeFull = size_on_first_use; // NB: argument name 'size_on_first_use' misleading here, it's really just 'size' as provided by user. + } + + // Position popup + if ((flags & ImGuiWindowFlags_Popup) != 0 && window_appearing_after_being_hidden && !window_pos_set_by_api) + { + ImRect rect_to_avoid(window->PosFloat.x - 1, window->PosFloat.y - 1, window->PosFloat.x + 1, window->PosFloat.y + 1); + window->PosFloat = FindBestWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid); + } + + // Position tooltip (always follows mouse) + if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api) + { + ImRect rect_to_avoid(g.IO.MousePos.x - 16, g.IO.MousePos.y - 8, g.IO.MousePos.x + 24, g.IO.MousePos.y + 24); // FIXME: Completely hard-coded. Perhaps center on cursor hit-point instead? + window->PosFloat = FindBestWindowPos(g.IO.MousePos, window->Size, &window->AutoPosLastDirection, rect_to_avoid); + } + + // User moving window (at the beginning of the frame to avoid input lag or sheering). Only valid for root windows. + RegisterAliveId(window->MoveID); + if (g.ActiveId == window->MoveID) { if (g.IO.MouseDown[0]) { - if (!(window->Flags & ImGuiWindowFlags_NoMove)) + if (!(flags & ImGuiWindowFlags_NoMove)) { window->PosFloat += g.IO.MouseDelta; - MarkSettingsDirty(); + if (!(flags & ImGuiWindowFlags_NoSavedSettings)) + MarkSettingsDirty(); } - FocusWindow(window); + IM_ASSERT(g.MovedWindow != NULL); + FocusWindow(g.MovedWindow); } else { - g.ActiveId = 0; + SetActiveId(0); + g.MovedWindow = NULL; // Not strictly necessary but doing it for sanity. } } - // Tooltips always follow mouse - if ((window->Flags & ImGuiWindowFlags_Tooltip) != 0) + // Clamp into display + if (!(flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) { - window->PosFloat = g.IO.MousePos + ImVec2(32,16) - g.Style.FramePadding*2; - } - - // Clamp into view - if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) - { - const ImVec2 pad = ImVec2(window->FontSize()*2.0f, window->FontSize()*2.0f); - window->PosFloat = ImMax(window->PosFloat + window->Size, pad) - window->Size; - window->PosFloat = ImMin(window->PosFloat, ImVec2(g.IO.DisplaySize.x, g.IO.DisplaySize.y) - pad); - window->SizeFull = ImMax(window->SizeFull, pad); + if (window->AutoFitFrames <= 0 && g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. + { + ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); + window->PosFloat = ImMax(window->PosFloat + window->Size, padding) - window->Size; + window->PosFloat = ImMin(window->PosFloat, g.IO.DisplaySize - padding); + } } window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y); - // Default item width - if (window->Size.x > 0.0f && !(window->Flags & ImGuiWindowFlags_Tooltip) && !(window->Flags & ImGuiWindowFlags_AlwaysAutoResize)) + // Default item width. Make it proportional to window size if window manually resizes + if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize)) window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f); else window->ItemWidthDefault = 200.0f; @@ -2251,173 +3415,107 @@ bool ImGui::Begin(const char* name, bool* open, ImVec2 size, float fill_alpha, I window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1; window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = IM_INT_MAX; - ImGuiAabb title_bar_aabb = window->TitleBarAabb(); - - // Apply and ImClamp scrolling + // Apply scrolling window->ScrollY = window->NextScrollY; window->ScrollY = ImMax(window->ScrollY, 0.0f); if (!window->Collapsed && !window->SkipItems) - window->ScrollY = ImMin(window->ScrollY, ImMax(0.0f, (float)window->SizeContentsFit.y - window->SizeFull.y)); + window->ScrollY = ImMin(window->ScrollY, ImMax(0.0f, window->SizeContents.y - window->SizeFull.y)); window->NextScrollY = window->ScrollY; - // At this point we don't have a clipping rectangle setup yet, so we can test and draw in title bar - // Collapse window by double-clicking on title bar - if (!(window->Flags & ImGuiWindowFlags_NoTitleBar)) - { - if (g.HoveredWindow == window && IsMouseHoveringBox(title_bar_aabb) && g.IO.MouseDoubleClicked[0]) - { - window->Collapsed = !window->Collapsed; - MarkSettingsDirty(); - FocusWindow(window); - } - } - else - { - window->Collapsed = false; - } - + // Draw window + handle manual resize + ImRect title_bar_rect = window->TitleBarRect(); + const float window_rounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding; if (window->Collapsed) { // Draw title bar only - window->Size = title_bar_aabb.GetSize(); - window->DrawList->AddRectFilled(title_bar_aabb.GetTL(), title_bar_aabb.GetBR(), window->Color(ImGuiCol_TitleBgCollapsed), g.Style.WindowRounding); - if (window->Flags & ImGuiWindowFlags_ShowBorders) + window->DrawList->AddRectFilled(title_bar_rect.GetTL(), title_bar_rect.GetBR(), window->Color(ImGuiCol_TitleBgCollapsed), window_rounding); + if (flags & ImGuiWindowFlags_ShowBorders) { - window->DrawList->AddRect(title_bar_aabb.GetTL()+ImVec2(1,1), title_bar_aabb.GetBR()+ImVec2(1,1), window->Color(ImGuiCol_BorderShadow), g.Style.WindowRounding); - window->DrawList->AddRect(title_bar_aabb.GetTL(), title_bar_aabb.GetBR(), window->Color(ImGuiCol_Border), g.Style.WindowRounding); + window->DrawList->AddRect(title_bar_rect.GetTL()+ImVec2(1,1), title_bar_rect.GetBR()+ImVec2(1,1), window->Color(ImGuiCol_BorderShadow), window_rounding); + window->DrawList->AddRect(title_bar_rect.GetTL(), title_bar_rect.GetBR(), window->Color(ImGuiCol_Border), window_rounding); } } else { - window->Size = window->SizeFull; - ImU32 resize_col = 0; - if ((window->Flags & ImGuiWindowFlags_Tooltip) != 0) + if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFrames <= 0 && !(flags & ImGuiWindowFlags_NoResize)) { - // Tooltip always resize - if (window->AutoFitFrames > 0) + // Manual resize + const ImRect resize_rect(window->Rect().GetBR()-ImVec2(14,14), window->Rect().GetBR()); + const ImGuiID resize_id = window->GetID("#RESIZE"); + bool hovered, held; + ButtonBehavior(resize_rect, resize_id, &hovered, &held, true, ImGuiButtonFlags_FlattenChilds); + resize_col = window->Color(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); + + if (hovered || held) + g.MouseCursor = ImGuiMouseCursor_ResizeNWSE; + + if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0]) { - window->SizeFull = window->SizeContentsFit + g.Style.WindowPadding - ImVec2(0.0f, g.Style.ItemSpacing.y); - } - } - else - { - const ImVec2 size_auto_fit = ImClamp(window->SizeContentsFit + style.AutoFitPadding, style.WindowMinSize, g.IO.DisplaySize - style.AutoFitPadding); - if ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0) - { - // Don't continuously mark settings as dirty, the size of the window doesn't need to be stored. + // Manual auto-fit when double-clicking window->SizeFull = size_auto_fit; - } - else if (window->AutoFitFrames > 0) - { - // Auto-fit only grows during the first few frames - if (window->AutoFitOnlyGrows) - window->SizeFull = ImMax(window->SizeFull, size_auto_fit); - else - window->SizeFull = size_auto_fit; - MarkSettingsDirty(); - } - else if (!(window->Flags & ImGuiWindowFlags_NoResize)) - { - // Resize grip - const ImGuiAabb resize_aabb(window->Aabb().GetBR()-ImVec2(18,18), window->Aabb().GetBR()); - const ImGuiID resize_id = window->GetID("##RESIZE"); - bool hovered, held; - ButtonBehaviour(resize_aabb, resize_id, &hovered, &held, true); - resize_col = window->Color(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); - - if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0]) - { - // Manual auto-fit - window->SizeFull = size_auto_fit; - window->Size = window->SizeFull; + if (!(flags & ImGuiWindowFlags_NoSavedSettings)) MarkSettingsDirty(); - } - else if (held) - { - // Resize - window->SizeFull = ImMax(window->SizeFull + g.IO.MouseDelta, style.WindowMinSize); - window->Size = window->SizeFull; + SetActiveId(0); + } + else if (held) + { + window->SizeFull = ImMax(window->SizeFull + g.IO.MouseDelta, style.WindowMinSize); + if (!(flags & ImGuiWindowFlags_NoSavedSettings)) MarkSettingsDirty(); - } } - // Update aabb immediately so that the rendering below isn't one frame late - title_bar_aabb = window->TitleBarAabb(); - } - - // Title bar + Window box - if (fill_alpha > 0.0f) - { - if ((window->Flags & ImGuiWindowFlags_ComboBox) != 0) - window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size, window->Color(ImGuiCol_ComboBg, fill_alpha), 0); - else - window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size, window->Color(ImGuiCol_WindowBg, fill_alpha), g.Style.WindowRounding); - } - - if (!(window->Flags & ImGuiWindowFlags_NoTitleBar)) - window->DrawList->AddRectFilled(title_bar_aabb.GetTL(), title_bar_aabb.GetBR(), window->Color(ImGuiCol_TitleBg), g.Style.WindowRounding, 1|2); - - // Borders - if (window->Flags & ImGuiWindowFlags_ShowBorders) - { - const float rounding = (window->Flags & ImGuiWindowFlags_ComboBox) ? 0.0f : g.Style.WindowRounding; - window->DrawList->AddRect(window->Pos+ImVec2(1,1), window->Pos+window->Size+ImVec2(1,1), window->Color(ImGuiCol_BorderShadow), rounding); - window->DrawList->AddRect(window->Pos, window->Pos+window->Size, window->Color(ImGuiCol_Border), rounding); - if (!(window->Flags & ImGuiWindowFlags_NoTitleBar)) - window->DrawList->AddLine(title_bar_aabb.GetBL(), title_bar_aabb.GetBR(), window->Color(ImGuiCol_Border)); + window->Size = window->SizeFull; + title_bar_rect = window->TitleBarRect(); } // Scrollbar - window->ScrollbarY = (window->SizeContentsFit.y > window->Size.y) && !(window->Flags & ImGuiWindowFlags_NoScrollbar); - if (window->ScrollbarY) + window->ScrollbarY = (window->SizeContents.y > window->Size.y) && !(flags & ImGuiWindowFlags_NoScrollbar); + + // Window background + if (bg_alpha > 0.0f) { - ImGuiAabb scrollbar_bb(window->Aabb().Max.x - style.ScrollBarWidth, title_bar_aabb.Max.y+1, window->Aabb().Max.x, window->Aabb().Max.y-1); - //window->DrawList->AddLine(scrollbar_bb.GetTL(), scrollbar_bb.GetBL(), g.Colors[ImGuiCol_Border]); - window->DrawList->AddRectFilled(scrollbar_bb.Min, scrollbar_bb.Max, window->Color(ImGuiCol_ScrollbarBg)); - scrollbar_bb.Expand(ImVec2(-3,-3)); - - const float grab_size_y_norm = ImSaturate(window->Size.y / ImMax(window->SizeContentsFit.y, window->Size.y)); - const float grab_size_y = scrollbar_bb.GetHeight() * grab_size_y_norm; - - // Handle input right away (none of the code above is relying on scrolling position) - bool held = false; - bool hovered = false; - if (grab_size_y_norm < 1.0f) - { - const ImGuiID scrollbar_id = window->GetID("#SCROLLY"); - ButtonBehaviour(scrollbar_bb, scrollbar_id, &hovered, &held, true); - if (held) - { - g.HoveredId = scrollbar_id; - const float pos_y_norm = ImSaturate((g.IO.MousePos.y - (scrollbar_bb.Min.y + grab_size_y*0.5f)) / (scrollbar_bb.GetHeight() - grab_size_y)) * (1.0f - grab_size_y_norm); - window->ScrollY = pos_y_norm * window->SizeContentsFit.y; - window->NextScrollY = window->ScrollY; - } - } - - // Normalized height of the grab - const float pos_y_norm = ImSaturate(window->ScrollY / ImMax(0.0f, window->SizeContentsFit.y)); - const ImU32 grab_col = window->Color(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab); - window->DrawList->AddRectFilled( - ImVec2(scrollbar_bb.Min.x, ImLerp(scrollbar_bb.Min.y, scrollbar_bb.Max.y, pos_y_norm)), - ImVec2(scrollbar_bb.Max.x, ImLerp(scrollbar_bb.Min.y, scrollbar_bb.Max.y, pos_y_norm + grab_size_y_norm)), grab_col); + if ((flags & ImGuiWindowFlags_ComboBox) != 0) + window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size, window->Color(ImGuiCol_ComboBg, bg_alpha), window_rounding); + else if ((flags & ImGuiWindowFlags_Tooltip) != 0) + window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size, window->Color(ImGuiCol_TooltipBg, bg_alpha), window_rounding); + else if ((flags & ImGuiWindowFlags_ChildWindow) != 0) + window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size-ImVec2(window->ScrollbarY?style.ScrollbarWidth:0.0f,0.0f), window->Color(ImGuiCol_ChildWindowBg, bg_alpha), window_rounding, window->ScrollbarY ? (1|8) : (0xF)); + else + window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size, window->Color(ImGuiCol_WindowBg, bg_alpha), window_rounding); } + // Title bar + if (!(flags & ImGuiWindowFlags_NoTitleBar)) + window->DrawList->AddRectFilled(title_bar_rect.GetTL(), title_bar_rect.GetBR(), window->Color(ImGuiCol_TitleBg), window_rounding, 1|2); + + // Borders + if (flags & ImGuiWindowFlags_ShowBorders) + { + window->DrawList->AddRect(window->Pos+ImVec2(1,1), window->Pos+window->Size+ImVec2(1,1), window->Color(ImGuiCol_BorderShadow), window_rounding); + window->DrawList->AddRect(window->Pos, window->Pos+window->Size, window->Color(ImGuiCol_Border), window_rounding); + if (!(flags & ImGuiWindowFlags_NoTitleBar)) + window->DrawList->AddLine(title_bar_rect.GetBL(), title_bar_rect.GetBR(), window->Color(ImGuiCol_Border)); + } + + // Scrollbar + if (window->ScrollbarY) + Scrollbar(window); + // Render resize grip // (after the input handling so we don't have a frame of latency) - if (!(window->Flags & ImGuiWindowFlags_NoResize)) + if (!(flags & ImGuiWindowFlags_NoResize)) { - const float r = style.WindowRounding; - const ImVec2 br = window->Aabb().GetBR(); + const float r = window_rounding; + const ImVec2 br = window->Rect().GetBR(); if (r == 0.0f) { window->DrawList->AddTriangleFilled(br, br-ImVec2(0,14), br-ImVec2(14,0), resize_col); } else { - // FIXME: We should draw 4 triangles and decide on a size that's not dependant on the rounding size (previously used 18) - window->DrawList->AddArc(br - ImVec2(r,r), r, resize_col, 6, 9, true); + // FIXME: We should draw 4 triangles and decide on a size that's not dependent on the rounding size (previously used 18) + window->DrawList->AddArcFast(br - ImVec2(r,r), r, resize_col, 6, 9, true); window->DrawList->AddTriangleFilled(br+ImVec2(0,-2*r),br+ImVec2(0,-r),br+ImVec2(-r,-r), resize_col); window->DrawList->AddTriangleFilled(br+ImVec2(-r,-r), br+ImVec2(-r,0),br+ImVec2(-2*r,0), resize_col); } @@ -2430,8 +3528,10 @@ bool ImGui::Begin(const char* name, bool* open, ImVec2 size, float fill_alpha, I window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.ColumnsStartX + window->DC.ColumnsOffsetX, window->TitleBarHeight() + window->WindowPadding().y) - ImVec2(0.0f, window->ScrollY); window->DC.CursorPos = window->DC.CursorStartPos; window->DC.CursorPosPrevLine = window->DC.CursorPos; + window->DC.CursorMaxPos = window->DC.CursorStartPos; window->DC.CurrentLineHeight = window->DC.PrevLineHeight = 0.0f; - window->DC.LogLineHeight = window->DC.CursorPos.y - 9999.0f; + window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; + window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; window->DC.ChildWindows.resize(0); window->DC.ItemWidth.resize(0); window->DC.ItemWidth.push_back(window->ItemWidthDefault); @@ -2446,51 +3546,66 @@ bool ImGui::Begin(const char* name, bool* open, ImVec2 size, float fill_alpha, I window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.ColumnsStartPos.y; window->DC.TreeDepth = 0; window->DC.StateStorage = &window->StateStorage; - window->DC.OpenNextNode = -1; + window->DC.GroupStack.resize(0); - // Reset contents size for auto-fitting - window->SizeContentsFit = ImVec2(0.0f, 0.0f); if (window->AutoFitFrames > 0) window->AutoFitFrames--; // Title bar - if (!(window->Flags & ImGuiWindowFlags_NoTitleBar)) + if (!(flags & ImGuiWindowFlags_NoTitleBar)) { - RenderCollapseTriangle(window->Pos + style.FramePadding, !window->Collapsed, 1.0f, true); - if (open) - CloseWindowButton(open); + if (p_opened != NULL) + CloseWindowButton(p_opened); - const ImVec2 text_size = CalcTextSize(name); - const ImVec2 text_min = window->Pos + style.FramePadding + ImVec2(window->FontSize() + style.ItemInnerSpacing.x, 0.0f); - const ImVec2 text_max = window->Pos + ImVec2(window->Size.x - (open ? (title_bar_aabb.GetHeight()-3) : style.FramePadding.x), style.FramePadding.y + text_size.y); - const bool clip_title = text_size.x > (text_max.x - text_min.x); // only push a clip rectangle if we need to, because it may turn into a separate draw call - if (clip_title) - PushClipRect(ImVec4(text_min.x, text_min.y, text_max.x, text_max.y)); - RenderText(text_min, name); - if (clip_title) - PopClipRect(); + const ImVec2 text_size = CalcTextSize(name, NULL, true); + if (!(flags & ImGuiWindowFlags_NoCollapse)) + RenderCollapseTriangle(window->Pos + style.FramePadding, !window->Collapsed, 1.0f, true); + + ImVec2 text_min = window->Pos + style.FramePadding; + ImVec2 text_max = window->Pos + ImVec2(window->Size.x - style.FramePadding.x, style.FramePadding.y*2 + text_size.y); + ImVec2 clip_max = ImVec2(window->Pos.x + window->Size.x - (p_opened ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x), text_max.y); // Match the size of CloseWindowButton() + bool pad_left = (flags & ImGuiWindowFlags_NoCollapse) == 0; + bool pad_right = (p_opened != NULL); + if (style.WindowTitleAlign & ImGuiAlign_Center) pad_right = pad_left; + if (pad_left) text_min.x += g.FontSize + style.ItemInnerSpacing.x; + if (pad_right) text_max.x -= g.FontSize + style.ItemInnerSpacing.x; + RenderTextClipped(text_min, name, NULL, &text_size, text_max, &clip_max, style.WindowTitleAlign); } - } - else - { - // Outer clipping rectangle - if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ComboBox)) + if (flags & ImGuiWindowFlags_Popup) { - ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.size()-2]; - PushClipRect(parent_window->ClipRectStack.back()); - } - else - { - PushClipRect(ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y)); + if (p_opened) + { + if (g.IO.MouseClicked[0] && (!g.HoveredWindow || g.HoveredWindow->RootWindow != window)) + *p_opened = false; + else if (!g.FocusedWindow) + *p_opened = false; + else if (g.FocusedWindow->RootWindow != window)// && !(g.FocusedWindow->RootWindow->Flags & ImGuiWindowFlags_Tooltip)) + *p_opened = false; + } } + + // Save clipped aabb so we can access it in constant-time in FindHoveredWindow() + window->ClippedRect = window->Rect(); + window->ClippedRect.Clip(window->ClipRectStack.front()); + + // Pressing CTRL+C while holding on a window copy its content to the clipboard + // This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope. + // Maybe we can support CTRL+C on every element? + /* + if (g.ActiveId == move_id) + if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C)) + ImGui::LogToClipboard(); + */ } + window->BeginCount++; // Inner clipping rectangle // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame - const ImGuiAabb title_bar_aabb = window->TitleBarAabb(); - ImVec4 clip_rect(title_bar_aabb.Min.x+0.5f+window->WindowPadding().x*0.5f, title_bar_aabb.Max.y+0.5f, window->Aabb().Max.x+0.5f-window->WindowPadding().x*0.5f, window->Aabb().Max.y-1.5f); + // Note that if our window is collapsed we will end up with a null clipping rectangle which is the correct behavior. + const ImRect title_bar_rect = window->TitleBarRect(); + ImVec4 clip_rect(title_bar_rect.Min.x+0.5f+window->WindowPadding().x*0.5f, title_bar_rect.Max.y+0.5f, window->Rect().Max.x+0.5f-window->WindowPadding().x*0.5f, window->Rect().Max.y-1.5f); if (window->ScrollbarY) - clip_rect.z -= g.Style.ScrollBarWidth; + clip_rect.z -= style.ScrollbarWidth; PushClipRect(clip_rect); // Clear 'accessed' flag last thing @@ -2502,70 +3617,131 @@ bool ImGui::Begin(const char* name, bool* open, ImVec2 size, float fill_alpha, I if (flags & ImGuiWindowFlags_ChildWindow) { IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); + window->Collapsed = parent_window && parent_window->Collapsed; + const ImVec4 clip_rect_t = window->ClipRectStack.back(); - window->Collapsed = (clip_rect_t.x >= clip_rect_t.z || clip_rect_t.y >= clip_rect_t.w); + window->Collapsed |= (clip_rect_t.x >= clip_rect_t.z || clip_rect_t.y >= clip_rect_t.w); // We also hide the window from rendering because we've already added its border to the command list. // (we could perform the check earlier in the function but it is simpler at this point) if (window->Collapsed) - window->Visible = false; + window->Active = false; } - if (g.Style.Alpha <= 0.0f) - window->Visible = false; + if (style.Alpha <= 0.0f) + window->Active = false; - // Return false if we don't intend to display anything to allow user to perform an early out optimisation - window->SkipItems = window->Collapsed || (!window->Visible && window->AutoFitFrames == 0); + // Return false if we don't intend to display anything to allow user to perform an early out optimization + window->SkipItems = (window->Collapsed || !window->Active) && window->AutoFitFrames <= 0; return !window->SkipItems; } void ImGui::End() { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImGui::Columns(1, "#CloseColumns"); PopClipRect(); // inner window clip rectangle - PopClipRect(); // outer window clip rectangle - - // Select window for move/focus when we're done with all our widgets (we only consider non-childs windows here) - const ImGuiAabb bb(window->Pos, window->Pos+window->Size); - if (g.ActiveId == 0 && g.HoveredId == 0 && g.HoveredRootWindow == window && IsMouseHoveringBox(bb) && g.IO.MouseClicked[0]) - g.ActiveId = window->GetID("#MOVE"); // Stop logging if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging - { - g.LogEnabled = false; - if (g.LogFile != NULL) - { - fprintf(g.LogFile, "\n"); - if (g.LogFile == stdout) - fflush(g.LogFile); - else - fclose(g.LogFile); - g.LogFile = NULL; - } - if (g.LogClipboard->size() > 1) - { - g.LogClipboard->append("\n"); - if (g.IO.SetClipboardTextFn) - g.IO.SetClipboardTextFn(g.LogClipboard->begin()); - g.LogClipboard->clear(); - } - } + ImGui::LogFinish(); // Pop - window->RootWindow = NULL; + // NB: we don't clear 'window->RootWindow'. The pointer is allowed to live until the next call to Begin(). + CheckStacksSize(window, false); g.CurrentWindowStack.pop_back(); - g.CurrentWindow = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back(); + SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back()); } -// Moving window to front +// Vertical scrollbar +// The entire piece of code below is rather confusing because: +// - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab) +// - We store values as ratio and in a form that allows the window content to change while we are holding on a scrollbar +static void Scrollbar(ImGuiWindow* window) +{ + ImGuiState& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID("#SCROLLY"); + + // Render background + ImRect bb(window->Rect().Max.x - style.ScrollbarWidth, window->Pos.y + window->TitleBarHeight()+1, window->Rect().Max.x, window->Rect().Max.y-1); + window->DrawList->AddRectFilled(bb.Min, bb.Max, window->Color(ImGuiCol_ScrollbarBg)); + bb.Expand(-3.0f); + const float scrollbar_height = bb.GetHeight(); + + // The grabable box size generally represent the amount visible (vs the total scrollable amount) + // But we maintain a minimum size in pixel to allow for the user to still aim inside. + const float grab_h_pixels = ImMin(ImMax(scrollbar_height * ImSaturate(window->Size.y / ImMax(window->SizeContents.y, window->Size.y)), style.GrabMinSize), scrollbar_height); + const float grab_h_norm = grab_h_pixels / scrollbar_height; + + // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar(). + bool held = false; + bool hovered = false; + const bool previously_held = (g.ActiveId == id); + ButtonBehavior(bb, id, &hovered, &held, true); + + const float scroll_max = ImMax(1.0f, window->SizeContents.y - window->Size.y); + float scroll_ratio = ImSaturate(window->ScrollY / scroll_max); + float grab_y_norm = scroll_ratio * (scrollbar_height - grab_h_pixels) / scrollbar_height; + if (held) + { + const float clicked_y_norm = ImSaturate((g.IO.MousePos.y - bb.Min.y) / scrollbar_height); // Click position in scrollbar space (0.0f->1.0f) + g.HoveredId = id; + + bool seek_absolute = false; + if (!previously_held) + { + // On initial click calculate the distance between mouse and the center of the grab + if (clicked_y_norm >= grab_y_norm && clicked_y_norm <= grab_y_norm + grab_h_norm) + { + g.ScrollbarClickDeltaToGrabCenter = clicked_y_norm - grab_y_norm - grab_h_norm*0.5f; + } + else + { + seek_absolute = true; + g.ScrollbarClickDeltaToGrabCenter = 0; + } + } + + // Apply scroll + const float scroll_y_norm = ImSaturate((clicked_y_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm*0.5f) / (1.0f - grab_h_norm)); + window->ScrollY = (float)(int)(0.5f + scroll_y_norm * (window->SizeContents.y - window->Size.y)); + window->NextScrollY = window->ScrollY; + + // Update values for rendering + scroll_ratio = ImSaturate(window->ScrollY / scroll_max); + grab_y_norm = scroll_ratio * (scrollbar_height - grab_h_pixels) / scrollbar_height; + + // Update distance to grab now that we have seeked and saturated + if (seek_absolute) + g.ScrollbarClickDeltaToGrabCenter = clicked_y_norm - grab_y_norm - grab_h_norm*0.5f; + } + + // Render + const ImU32 grab_col = window->Color(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab); + window->DrawList->AddRectFilled(ImVec2(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_y_norm)), ImVec2(bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_y_norm) + grab_h_pixels), grab_col, style.ScrollbarRounding); +} + +// Moving window to front of display (which happens to be back of our sorted list) static void FocusWindow(ImGuiWindow* window) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; + + // Always mark the window we passed as focused. This is used for keyboard interactions such as tabbing. g.FocusedWindow = window; + // Passing NULL allow to disable keyboard focus + if (!window) + return; + + // And move its root window to the top of the pile + if (window->RootWindow) + window = window->RootWindow; + + if (g.Windows.back() == window) + return; + for (size_t i = 0; i < g.Windows.size(); i++) if (g.Windows[i] == window) { @@ -2578,8 +3754,20 @@ static void FocusWindow(ImGuiWindow* window) void ImGui::PushItemWidth(float item_width) { ImGuiWindow* window = GetCurrentWindow(); - item_width = (float)(int)item_width; - window->DC.ItemWidth.push_back(item_width > 0.0f ? item_width : window->ItemWidthDefault); + window->DC.ItemWidth.push_back(item_width == 0.0f ? window->ItemWidthDefault : item_width); +} + +static void PushMultiItemsWidths(int components, float w_full = 0.0f) +{ + ImGuiWindow* window = GetCurrentWindow(); + const ImGuiStyle& style = GImGui->Style; + if (w_full <= 0.0f) + w_full = ImGui::CalcItemWidth(); + const float w_item_one = ImMax(1.0f, (float)(int)((w_full - (style.FramePadding.x*2.0f + style.ItemInnerSpacing.x) * (components-1)) / (float)components)); + const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.FramePadding.x*2.0f + style.ItemInnerSpacing.x) * (components-1))); + window->DC.ItemWidth.push_back(w_item_last); + for (int i = 0; i < components-1; i++) + window->DC.ItemWidth.push_back(w_item_one); } void ImGui::PopItemWidth() @@ -2588,10 +3776,48 @@ void ImGui::PopItemWidth() window->DC.ItemWidth.pop_back(); } -float ImGui::GetItemWidth() +float ImGui::CalcItemWidth() { ImGuiWindow* window = GetCurrentWindow(); - return window->DC.ItemWidth.back(); + float w = window->DC.ItemWidth.back(); + if (w < 0.0f) + { + // Align to a right-side limit. We include 1 frame padding in the calculation because this is how the width is always used (we add 2 frame padding to it), but we could move that responsibility to the widget as well. + ImGuiState& g = *GImGui; + float width_to_right_edge = window->Pos.x + ImGui::GetContentRegionMax().x - window->DC.CursorPos.x; + w = ImMax(1.0f, width_to_right_edge + w - g.Style.FramePadding.x * 2.0f); + } + w = (float)(int)w; + return w; +} + +static void SetFont(ImFont* font) +{ + ImGuiState& g = *GImGui; + IM_ASSERT(font && font->IsLoaded()); + IM_ASSERT(font->Scale > 0.0f); + g.Font = font; + g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale; + g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f; + g.FontTexUvWhitePixel = g.Font->ContainerAtlas->TexUvWhitePixel; +} + +void ImGui::PushFont(ImFont* font) +{ + ImGuiState& g = *GImGui; + if (!font) + font = g.IO.Fonts->Fonts[0]; + SetFont(font); + g.FontStack.push_back(font); + g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID); +} + +void ImGui::PopFont() +{ + ImGuiState& g = *GImGui; + g.CurrentWindow->DrawList->PopTextureID(); + g.FontStack.pop_back(); + SetFont(g.FontStack.empty() ? g.IO.Fonts->Fonts[0] : g.FontStack.back()); } void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus) @@ -2620,8 +3846,7 @@ void ImGui::PopTextWrapPos() void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col) { - ImGuiState& g = GImGui; - + ImGuiState& g = *GImGui; ImGuiColMod backup; backup.Col = idx; backup.PreviousValue = g.Style.Colors[idx]; @@ -2631,8 +3856,7 @@ void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col) void ImGui::PopStyleColor(int count) { - ImGuiState& g = GImGui; - + ImGuiState& g = *GImGui; while (count > 0) { ImGuiColMod& backup = g.ColorModifiers.back(); @@ -2644,19 +3868,22 @@ void ImGui::PopStyleColor(int count) static float* GetStyleVarFloatAddr(ImGuiStyleVar idx) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; switch (idx) { case ImGuiStyleVar_Alpha: return &g.Style.Alpha; - case ImGuiStyleVar_TreeNodeSpacing: return &g.Style.TreeNodeSpacing; - case ImGuiStyleVar_ColumnsMinSpacing: return &g.Style.ColumnsMinSpacing; + case ImGuiStyleVar_WindowRounding: return &g.Style.WindowRounding; + case ImGuiStyleVar_ChildWindowRounding: return &g.Style.ChildWindowRounding; + case ImGuiStyleVar_FrameRounding: return &g.Style.FrameRounding; + case ImGuiStyleVar_IndentSpacing: return &g.Style.IndentSpacing; + case ImGuiStyleVar_GrabMinSize: return &g.Style.GrabMinSize; } return NULL; } static ImVec2* GetStyleVarVec2Addr(ImGuiStyleVar idx) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; switch (idx) { case ImGuiStyleVar_WindowPadding: return &g.Style.WindowPadding; @@ -2669,10 +3896,9 @@ static ImVec2* GetStyleVarVec2Addr(ImGuiStyleVar idx) void ImGui::PushStyleVar(ImGuiStyleVar idx, float val) { - ImGuiState& g = GImGui; - + ImGuiState& g = *GImGui; float* pvar = GetStyleVarFloatAddr(idx); - IM_ASSERT(pvar != NULL); // Called wrong function? + IM_ASSERT(pvar != NULL); // Called function with wrong-type? Variable is not a float. ImGuiStyleMod backup; backup.Var = idx; backup.PreviousValue = ImVec2(*pvar, 0.0f); @@ -2683,10 +3909,9 @@ void ImGui::PushStyleVar(ImGuiStyleVar idx, float val) void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) { - ImGuiState& g = GImGui; - + ImGuiState& g = *GImGui; ImVec2* pvar = GetStyleVarVec2Addr(idx); - IM_ASSERT(pvar != NULL); // Called wrong function? + IM_ASSERT(pvar != NULL); // Called function with wrong-type? Variable is not a ImVec2. ImGuiStyleMod backup; backup.Var = idx; backup.PreviousValue = *pvar; @@ -2696,8 +3921,7 @@ void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) void ImGui::PopStyleVar(int count) { - ImGuiState& g = GImGui; - + ImGuiState& g = *GImGui; while (count > 0) { ImGuiStyleMod& backup = g.StyleModifiers.back(); @@ -2710,16 +3934,19 @@ void ImGui::PopStyleVar(int count) } } -const char* ImGui::GetStyleColorName(ImGuiCol idx) +const char* ImGui::GetStyleColName(ImGuiCol idx) { // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1"; switch (idx) { case ImGuiCol_Text: return "Text"; case ImGuiCol_WindowBg: return "WindowBg"; + case ImGuiCol_ChildWindowBg: return "ChildWindowBg"; case ImGuiCol_Border: return "Border"; case ImGuiCol_BorderShadow: return "BorderShadow"; case ImGuiCol_FrameBg: return "FrameBg"; + case ImGuiCol_FrameBgHovered: return "FrameBgHovered"; + case ImGuiCol_FrameBgActive: return "FrameBgActive"; case ImGuiCol_TitleBg: return "TitleBg"; case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed"; case ImGuiCol_ScrollbarBg: return "ScrollbarBg"; @@ -2727,8 +3954,7 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx) case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered"; case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive"; case ImGuiCol_ComboBg: return "ComboBg"; - case ImGuiCol_CheckHovered: return "CheckHovered"; - case ImGuiCol_CheckActive: return "CheckActive"; + case ImGuiCol_CheckMark: return "CheckMark"; case ImGuiCol_SliderGrab: return "SliderGrab"; case ImGuiCol_SliderGrabActive: return "SliderGrabActive"; case ImGuiCol_Button: return "Button"; @@ -2757,13 +3983,27 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx) return "Unknown"; } -bool ImGui::GetWindowIsFocused() +bool ImGui::IsWindowFocused() { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); return g.FocusedWindow == window; } +bool ImGui::IsRootWindowFocused() +{ + ImGuiState& g = *GImGui; + ImGuiWindow* root_window = GetCurrentWindow()->RootWindow; + return g.FocusedWindow == root_window; +} + +bool ImGui::IsRootWindowOrAnyChildFocused() +{ + ImGuiState& g = *GImGui; + ImGuiWindow* root_window = GetCurrentWindow()->RootWindow; + return g.FocusedWindow && g.FocusedWindow->RootWindow == root_window; +} + float ImGui::GetWindowWidth() { ImGuiWindow* window = GetCurrentWindow(); @@ -2776,15 +4016,32 @@ ImVec2 ImGui::GetWindowPos() return window->Pos; } -void ImGui::SetWindowPos(const ImVec2& pos) +static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiSetCond cond) { - ImGuiWindow* window = GetCurrentWindow(); + // Test condition (NB: bit 0 is always true) and clear flags for next time + if (cond && (window->SetWindowPosAllowFlags & cond) == 0) + return; + window->SetWindowPosAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing); + + // Set const ImVec2 old_pos = window->Pos; window->PosFloat = pos; window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y); + window->DC.CursorPos += (window->Pos - old_pos); // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor + window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected. +} - // If we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor - window->DC.CursorPos += (window->Pos - old_pos); +void ImGui::SetWindowPos(const ImVec2& pos, ImGuiSetCond cond) +{ + ImGuiWindow* window = GetCurrentWindow(); + SetWindowPos(window, pos, cond); +} + +void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiSetCond cond) +{ + ImGuiWindow* window = FindWindowByName(name); + if (window) + SetWindowPos(window, pos, cond); } ImVec2 ImGui::GetWindowSize() @@ -2793,37 +4050,133 @@ ImVec2 ImGui::GetWindowSize() return window->Size; } -void ImGui::SetWindowSize(const ImVec2& size) +static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiSetCond cond) { - ImGuiWindow* window = GetCurrentWindow(); - if (ImLength(size) < 0.001f) + // Test condition (NB: bit 0 is always true) and clear flags for next time + if (cond && (window->SetWindowSizeAllowFlags & cond) == 0) + return; + window->SetWindowSizeAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing); + + // Set + if (ImLengthSqr(size) > 0.00001f) { - window->AutoFitFrames = 2; - window->AutoFitOnlyGrows = false; + window->SizeFull = size; + window->AutoFitFrames = 0; } else { - window->SizeFull = size; + // Autofit + window->AutoFitFrames = 2; + window->AutoFitOnlyGrows = false; } } +void ImGui::SetWindowSize(const ImVec2& size, ImGuiSetCond cond) +{ + ImGuiWindow* window = GetCurrentWindow(); + SetWindowSize(window, size, cond); +} + +void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiSetCond cond) +{ + ImGuiWindow* window = FindWindowByName(name); + if (window) + SetWindowSize(window, size, cond); +} + +static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiSetCond cond) +{ + // Test condition (NB: bit 0 is always true) and clear flags for next time + if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0) + return; + window->SetWindowCollapsedAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing); + + // Set + window->Collapsed = collapsed; +} + +void ImGui::SetWindowCollapsed(bool collapsed, ImGuiSetCond cond) +{ + ImGuiWindow* window = GetCurrentWindow(); + SetWindowCollapsed(window, collapsed, cond); +} + +bool ImGui::GetWindowCollapsed() +{ + ImGuiWindow* window = GetCurrentWindow(); + return window->Collapsed; +} + +void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiSetCond cond) +{ + ImGuiWindow* window = FindWindowByName(name); + if (window) + SetWindowCollapsed(window, collapsed, cond); +} + +void ImGui::SetWindowFocus() +{ + ImGuiWindow* window = GetCurrentWindow(); + FocusWindow(window); +} + +void ImGui::SetWindowFocus(const char* name) +{ + if (name) + { + ImGuiWindow* window = FindWindowByName(name); + if (window) + FocusWindow(window); + } + else + { + FocusWindow(NULL); + } +} + +void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiSetCond cond) +{ + ImGuiState& g = *GImGui; + g.SetNextWindowPosVal = pos; + g.SetNextWindowPosCond = cond ? cond : ImGuiSetCond_Always; +} + +void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiSetCond cond) +{ + ImGuiState& g = *GImGui; + g.SetNextWindowSizeVal = size; + g.SetNextWindowSizeCond = cond ? cond : ImGuiSetCond_Always; +} + +void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiSetCond cond) +{ + ImGuiState& g = *GImGui; + g.SetNextWindowCollapsedVal = collapsed; + g.SetNextWindowCollapsedCond = cond ? cond : ImGuiSetCond_Always; +} + +void ImGui::SetNextWindowFocus() +{ + ImGuiState& g = *GImGui; + g.SetNextWindowFocus = true; +} + ImVec2 ImGui::GetContentRegionMax() { ImGuiWindow* window = GetCurrentWindow(); - - ImVec2 m = window->Size - window->WindowPadding(); + ImVec2 window_padding = window->WindowPadding(); + ImVec2 mx = window->Size - window_padding; if (window->DC.ColumnsCount != 1) { - m.x = GetColumnOffset(window->DC.ColumnsCurrent + 1); - m.x -= GImGui.Style.WindowPadding.x; + mx.x = ImGui::GetColumnOffset(window->DC.ColumnsCurrent + 1); + mx.x -= window_padding.x; } else { if (window->ScrollbarY) - m.x -= GImGui.Style.ScrollBarWidth; + mx.x -= GImGui->Style.ScrollbarWidth; } - - return m; + return mx; } ImVec2 ImGui::GetWindowContentRegionMin() @@ -2837,21 +4190,20 @@ ImVec2 ImGui::GetWindowContentRegionMax() ImGuiWindow* window = GetCurrentWindow(); ImVec2 m = window->Size - window->WindowPadding(); if (window->ScrollbarY) - m.x -= GImGui.Style.ScrollBarWidth; + m.x -= GImGui->Style.ScrollbarWidth; return m; } float ImGui::GetTextLineHeight() { - ImGuiWindow* window = GetCurrentWindow(); - return window->FontSize(); + ImGuiState& g = *GImGui; + return g.FontSize; } -float ImGui::GetTextLineSpacing() +float ImGui::GetTextLineHeightWithSpacing() { - ImGuiState& g = GImGui; - ImGuiWindow* window = GetCurrentWindow(); - return window->FontSize() + g.Style.ItemSpacing.y; + ImGuiState& g = *GImGui; + return g.FontSize + g.Style.ItemSpacing.y; } ImDrawList* ImGui::GetWindowDrawList() @@ -2862,44 +4214,61 @@ ImDrawList* ImGui::GetWindowDrawList() ImFont* ImGui::GetWindowFont() { - ImGuiWindow* window = GetCurrentWindow(); - return window->Font(); + ImGuiState& g = *GImGui; + return g.Font; } float ImGui::GetWindowFontSize() { - ImGuiWindow* window = GetCurrentWindow(); - return window->FontSize(); + ImGuiState& g = *GImGui; + return g.FontSize; } void ImGui::SetWindowFontScale(float scale) { + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); window->FontWindowScale = scale; + g.FontSize = window->CalcFontSize(); } +// NB: internally we store CursorPos in absolute screen coordinates because it is more convenient. +// Conversion happens as we pass the value to user, but it makes our naming convention dodgy. May want to rename 'DC.CursorPos'. ImVec2 ImGui::GetCursorPos() { ImGuiWindow* window = GetCurrentWindow(); return window->DC.CursorPos - window->Pos; } +float ImGui::GetCursorPosX() +{ + return ImGui::GetCursorPos().x; +} + +float ImGui::GetCursorPosY() +{ + return ImGui::GetCursorPos().y; +} + void ImGui::SetCursorPos(const ImVec2& pos) { ImGuiWindow* window = GetCurrentWindow(); window->DC.CursorPos = window->Pos + pos; + window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); } void ImGui::SetCursorPosX(float x) { ImGuiWindow* window = GetCurrentWindow(); window->DC.CursorPos.x = window->Pos.x + x; + window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x); } void ImGui::SetCursorPosY(float y) { ImGuiWindow* window = GetCurrentWindow(); window->DC.CursorPos.y = window->Pos.y + y; + window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y); } ImVec2 ImGui::GetCursorScreenPos() @@ -2908,6 +4277,24 @@ ImVec2 ImGui::GetCursorScreenPos() return window->DC.CursorPos; } +void ImGui::SetCursorScreenPos(const ImVec2& screen_pos) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.CursorPos = screen_pos; +} + +float ImGui::GetScrollPosY() +{ + ImGuiWindow* window = GetCurrentWindow(); + return window->ScrollY; +} + +float ImGui::GetScrollMaxY() +{ + ImGuiWindow* window = GetCurrentWindow(); + return window->SizeContents.y - window->SizeFull.y; +} + void ImGui::SetScrollPosHere() { ImGuiWindow* window = GetCurrentWindow(); @@ -2921,13 +4308,13 @@ void ImGui::SetKeyboardFocusHere(int offset) window->FocusIdxTabRequestNext = IM_INT_MAX; } -void ImGui::SetTreeStateStorage(ImGuiStorage* tree) +void ImGui::SetStateStorage(ImGuiStorage* tree) { ImGuiWindow* window = GetCurrentWindow(); window->DC.StateStorage = tree ? tree : &window->StateStorage; } -ImGuiStorage* ImGui::GetTreeStateStorage() +ImGuiStorage* ImGui::GetStateStorage() { ImGuiWindow* window = GetCurrentWindow(); return window->DC.StateStorage; @@ -2935,13 +4322,13 @@ ImGuiStorage* ImGui::GetTreeStateStorage() void ImGui::TextV(const char* fmt, va_list args) { + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; - static char buf[1024]; - const char* text_end = buf + ImFormatStringV(buf, IM_ARRAYSIZE(buf), fmt, args); - TextUnformatted(buf, text_end); + const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + TextUnformatted(g.TempBuffer, text_end); } void ImGui::Text(const char* fmt, ...) @@ -2984,7 +4371,7 @@ void ImGui::TextWrapped(const char* fmt, ...) void ImGui::TextUnformatted(const char* text, const char* text_end) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; @@ -3000,22 +4387,22 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) { // Long text! // Perform manual coarse clipping to optimize for long multi-line text - // From this point we will only compute the width of lines that are visible. - // Optimization only available when word-wrapping is disabled. + // From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled. + // We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line. const char* line = text; const float line_height = ImGui::GetTextLineHeight(); - const ImVec2 start_pos = window->DC.CursorPos; + const ImVec2 text_pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrentLineTextBaseOffset); const ImVec4 clip_rect = window->ClipRectStack.back(); ImVec2 text_size(0,0); - if (start_pos.y <= clip_rect.w) + if (text_pos.y <= clip_rect.w) { - ImVec2 pos = start_pos; + ImVec2 pos = text_pos; // Lines to skip (can't skip when logging text) if (!g.LogEnabled) { - int lines_skippable = (int)((clip_rect.y - start_pos.y) / line_height) - 1; + int lines_skippable = (int)((clip_rect.y - text_pos.y) / line_height) - 1; if (lines_skippable > 0) { int lines_skipped = 0; @@ -3032,11 +4419,11 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) // Lines to render if (line < text_end) { - ImGuiAabb line_box(pos, pos + ImVec2(ImGui::GetWindowWidth(), line_height)); + ImRect line_rect(pos, pos + ImVec2(ImGui::GetWindowWidth(), line_height)); while (line < text_end) { const char* line_end = strchr(line, '\n'); - if (IsClipped(line_box)) + if (IsClippedEx(line_rect, NULL, false)) break; const ImVec2 line_size = CalcTextSize(line, line_end, false); @@ -3045,8 +4432,8 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) if (!line_end) line_end = text_end; line = line_end + 1; - line_box.Min.y += line_height; - line_box.Max.y += line_height; + line_rect.Min.y += line_height; + line_rect.Max.y += line_height; pos.y += line_height; } @@ -3063,65 +4450,68 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) pos.y += lines_skipped * line_height; } - text_size.y += (pos - start_pos).y; + text_size.y += (pos - text_pos).y; } - const ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos + text_size); + + ImRect bb(text_pos, text_pos + text_size); ItemSize(bb); - ClipAdvance(bb); + ItemAdd(bb, NULL); } else { const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f; const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width); - ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos + text_size); - ItemSize(bb.GetSize(), &bb.Min); - if (ClipAdvance(bb)) + // Account of baseline offset + ImVec2 text_pos = window->DC.CursorPos; + text_pos.y += window->DC.CurrentLineTextBaseOffset; + + ImRect bb(text_pos, text_pos + text_size); + ItemSize(bb.GetSize()); + if (!ItemAdd(bb, NULL)) return; - // Render - // We don't hide text after ## in this end-user function. - RenderText(bb.Min, text_begin, text_end, false, wrap_width); + // Render (we don't hide text after ## in this end-user function) + RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width); } } void ImGui::AlignFirstTextHeightToWidgets() { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; // Declare a dummy item size to that upcoming items that are smaller will center-align on the newly expanded line height. - ItemSize(ImVec2(0, window->FontSize() + g.Style.FramePadding.y*2)); + ItemSize(ImVec2(0, g.FontSize + g.Style.FramePadding.y*2), g.Style.FramePadding.y); ImGui::SameLine(0, 0); } // Add a label+text combo aligned to other label+value widgets void ImGui::LabelTextV(const char* label, const char* fmt, va_list args) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; + const ImGuiStyle& style = g.Style; - const float w = window->DC.ItemWidth.back(); + const float w = ImGui::CalcItemWidth(); - static char buf[1024]; - const char* text_begin = &buf[0]; - const char* text_end = text_begin + ImFormatStringV(buf, IM_ARRAYSIZE(buf), fmt, args); + const char* value_text_begin = &g.TempBuffer[0]; + const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - const ImVec2 text_size = CalcTextSize(label); - const ImGuiAabb value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + style.FramePadding.x*2, text_size.y)); - const ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + style.FramePadding.x*2 + style.ItemInnerSpacing.x, 0.0f) + text_size); - ItemSize(bb); - - if (ClipAdvance(value_bb)) + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + style.FramePadding.x*2, label_size.y + style.FramePadding.y*2)); + const ImRect total_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + style.FramePadding.x*2 + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), style.FramePadding.y*2) + label_size); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, NULL)) return; // Render - RenderText(value_bb.Min, text_begin, text_end); - RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y), label); + RenderTextClipped(ImVec2(value_bb.Min.x, value_bb.Min.y + style.FramePadding.y), value_text_begin, value_text_end, NULL, value_bb.Max); + RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label); } void ImGui::LabelText(const char* label, const char* fmt, ...) @@ -3132,23 +4522,58 @@ void ImGui::LabelText(const char* label, const char* fmt, ...) va_end(args); } -static bool ButtonBehaviour(const ImGuiAabb& bb, const ImGuiID& id, bool* out_hovered, bool* out_held, bool allow_key_modifiers, bool repeat) +static inline bool IsWindowContentHoverable(ImGuiWindow* window) { - ImGuiState& g = GImGui; + // An active popup disable hovering on other windows (apart from its own children) + ImGuiState& g = *GImGui; + if (ImGuiWindow* focused_window = g.FocusedWindow) + if (ImGuiWindow* focused_root_window = focused_window->RootWindow) + if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) != 0 && focused_root_window->WasActive && focused_root_window != window->RootWindow) + return false; + + return true; +} + +static bool IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs = false) +{ + ImGuiState& g = *GImGui; + if (g.HoveredId == 0) + { + ImGuiWindow* window = GetCurrentWindow(); + if (g.HoveredWindow == window || (flatten_childs && g.HoveredRootWindow == window->RootWindow)) + if ((g.ActiveId == 0 || g.ActiveId == id || g.ActiveIdIsFocusedOnly) && IsMouseHoveringRect(bb)) + if (IsWindowContentHoverable(g.HoveredRootWindow)) + return true; + } + return false; +} + +static bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, bool allow_key_modifiers, ImGuiButtonFlags flags) +{ + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); - const bool hovered = (g.HoveredRootWindow == window->RootWindow) && (g.HoveredId == 0) && IsMouseHoveringBox(bb); bool pressed = false; + const bool hovered = IsHovered(bb, id, (flags & ImGuiButtonFlags_FlattenChilds) != 0); if (hovered) { g.HoveredId = id; - if (allow_key_modifiers || (!g.IO.KeyCtrl && !g.IO.KeyShift)) + if (allow_key_modifiers || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt)) { if (g.IO.MouseClicked[0]) { - g.ActiveId = id; + if (flags & ImGuiButtonFlags_PressedOnClick) + { + pressed = true; + SetActiveId(0); + } + else + { + SetActiveId(id); + } + FocusWindow(window); } - else if (repeat && g.ActiveId && ImGui::IsMouseClicked(0, true)) + else if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && ImGui::IsMouseClicked(0, true)) { pressed = true; } @@ -3166,7 +4591,7 @@ static bool ButtonBehaviour(const ImGuiAabb& bb, const ImGuiID& id, bool* out_ho { if (hovered) pressed = true; - g.ActiveId = 0; + SetActiveId(0); } } @@ -3176,41 +4601,32 @@ static bool ButtonBehaviour(const ImGuiAabb& bb, const ImGuiID& id, bool* out_ho return pressed; } -bool ImGui::Button(const char* label, ImVec2 size, bool repeat_when_held) +bool ImGui::Button(const char* label, const ImVec2& size_arg, bool repeat_when_held) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImVec2 text_size = CalcTextSize(label); - if (size.x == 0.0f) - size.x = text_size.x; - if (size.y == 0.0f) - size.y = text_size.y; - - const ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos+size + style.FramePadding*2.0f); - ItemSize(bb); - - if (ClipAdvance(bb)) + const ImVec2 size(size_arg.x != 0.0f ? size_arg.x : (label_size.x + style.FramePadding.x*2), size_arg.y != 0.0f ? size_arg.y : (label_size.y + style.FramePadding.y*2)); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + ItemSize(bb, style.FramePadding.y); + if (!ItemAdd(bb, &id)) return false; bool hovered, held; - bool pressed = ButtonBehaviour(bb, id, &hovered, &held, true, repeat_when_held); + bool pressed = ButtonBehavior(bb, id, &hovered, &held, true, repeat_when_held ? ImGuiButtonFlags_Repeat : 0); // Render const ImU32 col = window->Color((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); - RenderFrame(bb.Min, bb.Max, col); + RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); - if (size.x < text_size.x || size.y < text_size.y) - PushClipRect(ImVec4(bb.Min.x+style.FramePadding.x, bb.Min.y+style.FramePadding.y, bb.Max.x, bb.Max.y-style.FramePadding.y)); // Allow extra to draw over the horizontal padding to make it visible that text doesn't fit - const ImVec2 off = ImVec2(ImMax(0.0f, size.x - text_size.x) * 0.5f, ImMax(0.0f, size.y - text_size.y) * 0.5f); - RenderText(bb.Min + style.FramePadding + off, label); - if (size.x < text_size.x || size.y < text_size.y) - PopClipRect(); + const ImVec2 off = ImVec2(ImMax(0.0f, size.x - label_size.x) * 0.5f, ImMax(0.0f, size.y - label_size.y) * 0.5f); // Center (only applies if we explicitly gave a size bigger than the text size, which isn't the common path) + RenderTextClipped(bb.Min + off, label, NULL, &label_size, bb.Max); // Render clip (only applies if we explicitly gave a size smaller than the text size, which isn't the commmon path) return pressed; } @@ -3218,22 +4634,24 @@ bool ImGui::Button(const char* label, ImVec2 size, bool repeat_when_held) // Small buttons fits within text without additional spacing. bool ImGui::SmallButton(const char* label) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos+CalcTextSize(label) + ImVec2(style.FramePadding.x*2,0)); + ImVec2 text_pos = window->DC.CursorPos; + text_pos.y += window->DC.CurrentLineTextBaseOffset; + ImRect bb(text_pos, text_pos + label_size + ImVec2(style.FramePadding.x*2,0)); ItemSize(bb); - - if (ClipAdvance(bb)) + if (!ItemAdd(bb, &id)) return false; bool hovered, held; - bool pressed = ButtonBehaviour(bb, id, &hovered, &held, true); + bool pressed = ButtonBehavior(bb, id, &hovered, &held, true); // Render const ImU32 col = window->Color((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); @@ -3243,17 +4661,37 @@ bool ImGui::SmallButton(const char* label) return pressed; } +// Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack. +// Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id) +bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + const ImGuiID id = window->GetID(str_id); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + ItemSize(bb); + if (!ItemAdd(bb, &id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, true); + + return pressed; +} + // Upper-right button to close a window. -static bool CloseWindowButton(bool* open) +static bool CloseWindowButton(bool* p_opened) { ImGuiWindow* window = GetCurrentWindow(); - const ImGuiID id = window->GetID("##CLOSE"); + const ImGuiID id = window->GetID("#CLOSE"); const float size = window->TitleBarHeight() - 4.0f; - const ImGuiAabb bb(window->Aabb().GetTR() + ImVec2(-3.0f-size,2.0f), window->Aabb().GetTR() + ImVec2(-3.0f,2.0f+size)); + const ImRect bb(window->Rect().GetTR() + ImVec2(-3.0f-size,2.0f), window->Rect().GetTR() + ImVec2(-3.0f,2.0f+size)); bool hovered, held; - bool pressed = ButtonBehaviour(bb, id, &hovered, &held, true); + bool pressed = ButtonBehavior(bb, id, &hovered, &held, true); // Render const ImU32 col = window->Color((held && hovered) ? ImGuiCol_CloseButtonActive : hovered ? ImGuiCol_CloseButtonHovered : ImGuiCol_CloseButton); @@ -3267,8 +4705,72 @@ static bool CloseWindowButton(bool* open) window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), window->Color(ImGuiCol_Text)); } - if (open != NULL && pressed) - *open = !*open; + if (p_opened != NULL && pressed) + *p_opened = !*p_opened; + + return pressed; +} + +void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + if (border_col.w > 0.0f) + bb.Max += ImVec2(2,2); + ItemSize(bb); + if (!ItemAdd(bb, NULL)) + return; + + if (border_col.w > 0.0f) + { + window->DrawList->AddRect(bb.Min, bb.Max, window->Color(border_col), 0.0f); + window->DrawList->AddImage(user_texture_id, bb.Min+ImVec2(1,1), bb.Max-ImVec2(1,1), uv0, uv1, window->Color(tint_col)); + } + else + { + window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, window->Color(tint_col)); + } +} + +// frame_padding < 0: uses FramePadding from style (default) +// frame_padding = 0: no framing +// frame_padding > 0: set framing size +// The color used are the button colors. +bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col) +{ + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + const ImGuiStyle& style = g.Style; + + // Default to using texture ID as ID. User can still push string/integer prefixes. + // We could hash the size/uv to create a unique ID but that would prevent the user from animating buttons. + ImGui::PushID((void *)user_texture_id); + const ImGuiID id = window->GetID("#image"); + ImGui::PopID(); + + const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding; + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding*2); + const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size); + ItemSize(bb); + if (!ItemAdd(bb, &id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, true); + + // Render + const ImU32 col = window->Color((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + if (padding.x > 0.0f || padding.y > 0.0f) + RenderFrame(bb.Min, bb.Max, col); + if (bg_col.w > 0.0f) + window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, window->Color(bg_col)); + window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, window->Color(tint_col)); return pressed; } @@ -3276,11 +4778,14 @@ static bool CloseWindowButton(bool* open) // Start logging ImGui output to TTY void ImGui::LogToTTY(int max_depth) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); if (g.LogEnabled) return; + g.LogEnabled = true; g.LogFile = stdout; + g.LogStartDepth = window->DC.TreeDepth; if (max_depth >= 0) g.LogAutoExpandMaxDepth = max_depth; } @@ -3288,13 +4793,25 @@ void ImGui::LogToTTY(int max_depth) // Start logging ImGui output to given file void ImGui::LogToFile(int max_depth, const char* filename) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); if (g.LogEnabled) return; if (!filename) + { filename = g.IO.LogFilename; + if (!filename) + return; + } + + g.LogFile = fopen(filename, "ab"); + if (!g.LogFile) + { + IM_ASSERT(g.LogFile != NULL); // Consider this an error + return; + } g.LogEnabled = true; - g.LogFile = fopen(filename, "at"); + g.LogStartDepth = window->DC.TreeDepth; if (max_depth >= 0) g.LogAutoExpandMaxDepth = max_depth; } @@ -3302,19 +4819,46 @@ void ImGui::LogToFile(int max_depth, const char* filename) // Start logging ImGui output to clipboard void ImGui::LogToClipboard(int max_depth) { - ImGuiState& g = GImGui; + ImGuiWindow* window = GetCurrentWindow(); + ImGuiState& g = *GImGui; if (g.LogEnabled) return; + g.LogEnabled = true; g.LogFile = NULL; + g.LogStartDepth = window->DC.TreeDepth; if (max_depth >= 0) g.LogAutoExpandMaxDepth = max_depth; } +void ImGui::LogFinish() +{ + ImGuiState& g = *GImGui; + if (!g.LogEnabled) + return; + + ImGui::LogText(STR_NEWLINE); + g.LogEnabled = false; + if (g.LogFile != NULL) + { + if (g.LogFile == stdout) + fflush(g.LogFile); + else + fclose(g.LogFile); + g.LogFile = NULL; + } + if (g.LogClipboard->size() > 1) + { + if (g.IO.SetClipboardTextFn) + g.IO.SetClipboardTextFn(g.LogClipboard->begin()); + g.LogClipboard->clear(); + } +} + // Helper to display logging buttons void ImGui::LogButtons() { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGui::PushID("LogButtons"); const bool log_to_tty = ImGui::Button("Log To TTY"); @@ -3340,9 +4884,9 @@ void ImGui::LogButtons() LogToClipboard(g.LogAutoExpandMaxDepth); } -bool ImGui::CollapsingHeader(const char* label, const char* str_id, const bool display_frame, const bool default_open) +bool ImGui::CollapsingHeader(const char* label, const char* str_id, bool display_frame, bool default_open) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; @@ -3356,26 +4900,43 @@ bool ImGui::CollapsingHeader(const char* label, const char* str_id, const bool d label = str_id; const ImGuiID id = window->GetID(str_id); - // We only write to the tree storage if the user clicks - ImGuiStorage* tree = window->DC.StateStorage; + // We only write to the tree storage if the user clicks (or explicitely use SetNextTreeNode*** functions) + ImGuiStorage* storage = window->DC.StateStorage; bool opened; - if (window->DC.OpenNextNode != -1) + if (g.SetNextTreeNodeOpenedCond != 0) { - opened = window->DC.OpenNextNode > 0; - tree->SetInt(id, opened); - window->DC.OpenNextNode = -1; + if (g.SetNextTreeNodeOpenedCond & ImGuiSetCond_Always) + { + opened = g.SetNextTreeNodeOpenedVal; + storage->SetInt(id, opened); + } + else + { + // We treat ImGuiSetCondition_Once and ImGuiSetCondition_FirstUseEver the same because tree node state are not saved persistently. + const int stored_value = storage->GetInt(id, -1); + if (stored_value == -1) + { + opened = g.SetNextTreeNodeOpenedVal; + storage->SetInt(id, opened); + } + else + { + opened = stored_value != 0; + } + } + g.SetNextTreeNodeOpenedCond = 0; } else { - opened = tree->GetInt(id, default_open) != 0; + opened = storage->GetInt(id, default_open) != 0; } // Framed header expand a little outside the default padding const ImVec2 window_padding = window->WindowPadding(); - const ImVec2 text_size = CalcTextSize(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImVec2 pos_min = window->DC.CursorPos; const ImVec2 pos_max = window->Pos + GetContentRegionMax(); - ImGuiAabb bb = ImGuiAabb(pos_min, ImVec2(pos_max.x, pos_min.y + text_size.y)); + ImRect bb = ImRect(pos_min, ImVec2(pos_max.x, pos_min.y + label_size.y)); if (display_frame) { bb.Min.x -= window_padding.x*0.5f - 1; @@ -3383,24 +4944,24 @@ bool ImGui::CollapsingHeader(const char* label, const char* str_id, const bool d bb.Max.y += style.FramePadding.y * 2; } - const ImGuiAabb text_bb(bb.Min, bb.Min + ImVec2(window->FontSize() + style.FramePadding.x*2*2,0) + text_size); - ItemSize(ImVec2(text_bb.GetSize().x, bb.GetSize().y)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit + // FIXME: we don't provide our width so that it doesn't get feed back into AutoFit. Should manage that better so we can still hover without extending ContentsSize + const ImRect text_bb(bb.Min, bb.Min + ImVec2(g.FontSize + style.FramePadding.x*2*2,0) + label_size); + ItemSize(ImVec2(text_bb.GetSize().x, bb.GetSize().y), display_frame ? style.FramePadding.y : 0.0f); - // When logging is enabled, if automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behaviour). + // When logging is enabled, if automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior). // NB- If we are above max depth we still allow manually opened nodes to be logged. - if (!display_frame) - if (g.LogEnabled && window->DC.TreeDepth < g.LogAutoExpandMaxDepth) - opened = true; + if (g.LogEnabled && !display_frame && window->DC.TreeDepth < g.LogAutoExpandMaxDepth) + opened = true; - if (ClipAdvance(bb)) + if (!ItemAdd(bb, &id)) return opened; bool hovered, held; - bool pressed = ButtonBehaviour(display_frame ? bb : text_bb, id, &hovered, &held, false); + bool pressed = ButtonBehavior(display_frame ? bb : text_bb, id, &hovered, &held, false); if (pressed) { opened = !opened; - tree->SetInt(id, opened); + storage->SetInt(id, opened); } // Render @@ -3408,46 +4969,80 @@ bool ImGui::CollapsingHeader(const char* label, const char* str_id, const bool d if (display_frame) { // Framed type - RenderFrame(bb.Min, bb.Max, col, true); + RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); RenderCollapseTriangle(bb.Min + style.FramePadding, opened, 1.0f, true); - RenderText(bb.Min + style.FramePadding + ImVec2(window->FontSize() + style.FramePadding.x*2,0), label); + if (g.LogEnabled) + { + // NB: '##' is normally used to hide text (as a library-wide feature), so we need to specify the text range to make sure the ## aren't stripped out here. + const char log_prefix[] = "\n##"; + LogText(bb.Min + style.FramePadding, log_prefix, log_prefix+3); + } + RenderText(bb.Min + style.FramePadding + ImVec2(g.FontSize + style.FramePadding.x*2,0), label); + if (g.LogEnabled) + { + const char log_suffix[] = "##"; + LogText(bb.Min + style.FramePadding, log_suffix, log_suffix+2); + } } else { // Unframed typed for tree nodes if ((held && hovered) || hovered) RenderFrame(bb.Min, bb.Max, col, false); - RenderCollapseTriangle(bb.Min + ImVec2(style.FramePadding.x, window->FontSize()*0.15f), opened, 0.70f, false); - RenderText(bb.Min + ImVec2(window->FontSize() + style.FramePadding.x*2,0), label); + RenderCollapseTriangle(bb.Min + ImVec2(style.FramePadding.x, g.FontSize*0.15f), opened, 0.70f, false); + if (g.LogEnabled) + LogText(bb.Min, ">"); + RenderText(bb.Min + ImVec2(g.FontSize + style.FramePadding.x*2,0), label); } return opened; } -// Text with a little bullet aligned to the typical tree node. -void ImGui::BulletTextV(const char* fmt, va_list args) +void ImGui::Bullet() { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; - static char buf[1024]; - const char* text_begin = buf; - const char* text_end = text_begin + ImFormatStringV(buf, IM_ARRAYSIZE(buf), fmt, args); - - const float line_height = window->FontSize(); - const ImVec2 text_size = CalcTextSize(text_begin, text_end); - const ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(line_height + (text_size.x > 0.0f ? (g.Style.FramePadding.x*2) : 0.0f),0) + text_size); // Empty text doesn't add padding + const ImGuiStyle& style = g.Style; + const float line_height = g.FontSize; + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(line_height, line_height)); ItemSize(bb); - - if (ClipAdvance(bb)) + if (!ItemAdd(bb, NULL)) return; // Render const float bullet_size = line_height*0.15f; - window->DrawList->AddCircleFilled(bb.Min + ImVec2(g.Style.FramePadding.x + line_height*0.5f, line_height*0.5f), bullet_size, window->Color(ImGuiCol_Text)); - RenderText(bb.Min+ImVec2(window->FontSize()+g.Style.FramePadding.x*2,0), text_begin, text_end); + window->DrawList->AddCircleFilled(bb.Min + ImVec2(style.FramePadding.x + line_height*0.5f, line_height*0.5f), bullet_size, window->Color(ImGuiCol_Text)); + + // Stay on same line + ImGui::SameLine(0, -1); +} + +// Text with a little bullet aligned to the typical tree node. +void ImGui::BulletTextV(const char* fmt, va_list args) +{ + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + const char* text_begin = g.TempBuffer; + const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + + const ImGuiStyle& style = g.Style; + const float line_height = g.FontSize; + const ImVec2 label_size = CalcTextSize(text_begin, text_end, true); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(line_height + (label_size.x > 0.0f ? (style.FramePadding.x*2) : 0.0f),0) + label_size); // Empty text doesn't add padding + ItemSize(bb); + if (!ItemAdd(bb, NULL)) + return; + + // Render + const float bullet_size = line_height*0.15f; + window->DrawList->AddCircleFilled(bb.Min + ImVec2(style.FramePadding.x + line_height*0.5f, line_height*0.5f), bullet_size, window->Color(ImGuiCol_Text)); + RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2,0), text_begin, text_end); } void ImGui::BulletText(const char* fmt, ...) @@ -3461,14 +5056,18 @@ void ImGui::BulletText(const char* fmt, ...) // If returning 'true' the node is open and the user is responsible for calling TreePop bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args) { - static char buf[1024]; - ImFormatStringV(buf, IM_ARRAYSIZE(buf), fmt, args); + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); if (!str_id || !str_id[0]) str_id = fmt; ImGui::PushID(str_id); - const bool opened = ImGui::CollapsingHeader(buf, "", false); // do not add to the ID so that TreeNodeSetOpen can access + const bool opened = ImGui::CollapsingHeader(g.TempBuffer, "", false); ImGui::PopID(); if (opened) @@ -3489,14 +5088,18 @@ bool ImGui::TreeNode(const char* str_id, const char* fmt, ...) // If returning 'true' the node is open and the user is responsible for calling TreePop bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args) { - static char buf[1024]; - ImFormatStringV(buf, IM_ARRAYSIZE(buf), fmt, args); + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); if (!ptr_id) ptr_id = fmt; ImGui::PushID(ptr_id); - const bool opened = ImGui::CollapsingHeader(buf, "", false); + const bool opened = ImGui::CollapsingHeader(g.TempBuffer, "", false); ImGui::PopID(); if (opened) @@ -3519,10 +5122,11 @@ bool ImGui::TreeNode(const char* str_label_id) return TreeNode(str_label_id, "%s", str_label_id); } -void ImGui::OpenNextNode(bool open) +void ImGui::SetNextTreeNodeOpened(bool opened, ImGuiSetCond cond) { - ImGuiWindow* window = GetCurrentWindow(); - window->DC.OpenNextNode = open ? 1 : 0; + ImGuiState& g = *GImGui; + g.SetNextTreeNodeOpenedVal = opened; + g.SetNextTreeNodeOpenedCond = cond ? cond : ImGuiSetCond_Always; } void ImGui::PushID(const char* str_id) @@ -3531,6 +5135,12 @@ void ImGui::PushID(const char* str_id) window->IDStack.push_back(window->GetID(str_id)); } +void ImGui::PushID(const char* str_id_begin, const char* str_id_end) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->IDStack.push_back(window->GetID(str_id_begin, str_id_end)); +} + void ImGui::PushID(const void* ptr_id) { ImGuiWindow* window = GetCurrentWindow(); @@ -3550,11 +5160,29 @@ void ImGui::PopID() window->IDStack.pop_back(); } +ImGuiID ImGui::GetID(const char* str_id) +{ + ImGuiWindow* window = GetCurrentWindow(); + return window->GetID(str_id); +} + +ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end) +{ + ImGuiWindow* window = GetCurrentWindow(); + return window->GetID(str_id_begin, str_id_end); +} + +ImGuiID ImGui::GetID(const void* ptr_id) +{ + ImGuiWindow* window = GetCurrentWindow(); + return window->GetID(ptr_id); +} + // User can input math operators (e.g. +100) to edit a numerical values. // NB: only call right after InputText because we are using its InitialValue storage static void ApplyNumericalTextInput(const char* buf, float *v) { - while (*buf == ' ' || *buf == '\t') + while (ImCharIsSpace(*buf)) buf++; // We don't support '-' op because it would conflict with inputing negative value. @@ -3563,7 +5191,7 @@ static void ApplyNumericalTextInput(const char* buf, float *v) if (op == '+' || op == '*' || op == '/') { buf++; - while (*buf == ' ' || *buf == '\t') + while (ImCharIsSpace(*buf)) buf++; } else @@ -3575,7 +5203,7 @@ static void ApplyNumericalTextInput(const char* buf, float *v) float ref_v = *v; if (op) - if (sscanf(GImGui.InputTextState.InitialText, "%f", &ref_v) < 1) + if (sscanf(GImGui->InputTextState.InitialText, "%f", &ref_v) < 1) return; float op_v = 0.0f; @@ -3596,154 +5224,134 @@ static void ApplyNumericalTextInput(const char* buf, float *v) *v = op_v; } -// Use power!=1.0 for logarithmic sliders. -// Adjust display_format to decorate the value with a prefix or a suffix. -bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format, float power) +// Create text input in place of a slider (when CTRL+Clicking on slider) +static bool SliderFloatAsInputText(const char* label, float* v, ImGuiID id, int decimal_precision) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const float w = window->DC.ItemWidth.back(); + char text_buf[64]; + ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), "%.*f", decimal_precision, *v); - if (!display_format) - display_format = "%.3f"; + SetActiveId(g.ScalarAsInputTextId); + g.HoveredId = 0; - // Parse display precision back from the display format string - int decimal_precision = 3; - if (const char* p = strchr(display_format, '%')) + // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen) + window->FocusItemUnregister(); + + bool value_changed = ImGui::InputText(label, text_buf, IM_ARRAYSIZE(text_buf), ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll); + if (g.ScalarAsInputTextId == 0) { - p++; - while (*p >= '0' && *p <= '9') - p++; - if (*p == '.') + // First frame + IM_ASSERT(g.ActiveId == id); // InputText ID expected to match the Slider ID (else we'd need to store them both, which is also possible) + g.ScalarAsInputTextId = g.ActiveId; + g.HoveredId = id; + } + else if (g.ActiveId != g.ScalarAsInputTextId) + { + // Release + g.ScalarAsInputTextId = 0; + } + if (value_changed) + { + ApplyNumericalTextInput(text_buf, v); + } + return value_changed; +} + +// Parse display precision back from the display format string +static inline void ParseFormat(const char* fmt, int& decimal_precision) +{ + while ((fmt = strchr(fmt, '%')) != NULL) + { + fmt++; + if (fmt[0] == '%') { fmt++; continue; } // Ignore "%%" + while (*fmt >= '0' && *fmt <= '9') + fmt++; + if (*fmt == '.') { - decimal_precision = atoi(p+1); + decimal_precision = atoi(fmt + 1); if (decimal_precision < 0 || decimal_precision > 10) decimal_precision = 3; } + break; } +} - const ImVec2 text_size = CalcTextSize(label); - const ImGuiAabb frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, text_size.y) + style.FramePadding*2.0f); - const ImGuiAabb slider_bb(frame_bb.Min+g.Style.FramePadding, frame_bb.Max-g.Style.FramePadding); - const ImGuiAabb bb(frame_bb.Min, frame_bb.Max + ImVec2(style.ItemInnerSpacing.x + text_size.x, 0.0f)); - - if (IsClipped(slider_bb)) - { - // NB- we don't use ClipAdvance() in the if() statement because we don't want to submit ItemSize() because we may change into a text edit later which may submit an ItemSize itself - ItemSize(bb); - return false; - } - - const bool tab_focus_requested = window->FocusItemRegister(g.ActiveId == id); - - const bool is_unbound = v_min == -FLT_MAX || v_min == FLT_MAX || v_max == -FLT_MAX || v_max == FLT_MAX; - - const float grab_size_in_units = 1.0f; // In 'v' units. Probably needs to be parametrized, based on a 'v_step' value? decimal precision? - float grab_size_in_pixels; - if (decimal_precision > 0 || is_unbound) - grab_size_in_pixels = 10.0f; +static inline float RoundScalar(float value, int decimal_precision) +{ + // Round past decimal precision + // 0: 1, 1: 0.1, 2: 0.01, etc. + // So when our value is 1.99999 with a precision of 0.001 we'll end up rounding to 2.0 + // FIXME: Investigate better rounding methods + const float min_step = 1.0f / powf(10.0f, (float)decimal_precision); + const float remainder = fmodf(value, min_step); + if (remainder <= min_step*0.5f) + value -= remainder; else - grab_size_in_pixels = ImMax(grab_size_in_units * (w / (v_max-v_min+1.0f)), 8.0f); // Integer sliders - const float slider_effective_w = slider_bb.GetWidth() - grab_size_in_pixels; - const float slider_effective_x1 = slider_bb.Min.x + grab_size_in_pixels*0.5f; - const float slider_effective_x2 = slider_bb.Max.x - grab_size_in_pixels*0.5f; + value += (min_step - remainder); + return value; +} + +static bool SliderScalarBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, bool horizontal) +{ + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + const ImGuiStyle& style = g.Style; + + // Draw frame + RenderFrame(frame_bb.Min, frame_bb.Max, window->Color(ImGuiCol_FrameBg), true, style.FrameRounding); + + const bool is_non_linear = fabsf(power - 1.0f) > 0.0001f; + + const float padding = horizontal ? style.FramePadding.x : style.FramePadding.y; + const float slider_sz = horizontal ? (frame_bb.GetWidth() - padding * 2.0f) : (frame_bb.GetHeight() - padding * 2.0f); + float grab_sz; + if (decimal_precision > 0) + grab_sz = ImMin(style.GrabMinSize, slider_sz); + else + grab_sz = ImMin(ImMax(1.0f * (slider_sz / (v_max-v_min+1.0f)), style.GrabMinSize), slider_sz); // Integer sliders, if possible have the grab size represent 1 unit + const float slider_usable_sz = slider_sz - grab_sz; + const float slider_usable_pos_min = (horizontal ? frame_bb.Min.x : frame_bb.Min.y) + padding + grab_sz*0.5f; + const float slider_usable_pos_max = (horizontal ? frame_bb.Max.x : frame_bb.Max.y) - padding - grab_sz*0.5f; + + bool value_changed = false; // For logarithmic sliders that cross over sign boundary we want the exponential increase to be symmetric around 0.0f float linear_zero_pos = 0.0f; // 0.0->1.0f - if (!is_unbound) + if (v_min * v_max < 0.0f) { - if (v_min * v_max < 0.0f) - { - // Different sign - const float linear_dist_min_to_0 = powf(fabsf(0.0f - v_min), 1.0f/power); - const float linear_dist_max_to_0 = powf(fabsf(v_max - 0.0f), 1.0f/power); - linear_zero_pos = linear_dist_min_to_0 / (linear_dist_min_to_0+linear_dist_max_to_0); - } - else - { - // Same sign - linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f; - } + // Different sign + const float linear_dist_min_to_0 = powf(fabsf(0.0f - v_min), 1.0f/power); + const float linear_dist_max_to_0 = powf(fabsf(v_max - 0.0f), 1.0f/power); + linear_zero_pos = linear_dist_min_to_0 / (linear_dist_min_to_0+linear_dist_max_to_0); } - - const bool hovered = (g.HoveredWindow == window) && (g.HoveredId == 0) && IsMouseHoveringBox(slider_bb); - if (hovered) - g.HoveredId = id; - - bool start_text_input = false; - if (tab_focus_requested || (hovered && g.IO.MouseClicked[0])) + else { - g.ActiveId = id; - - const bool is_ctrl_down = g.IO.KeyCtrl; - if (tab_focus_requested || is_ctrl_down || is_unbound) - { - start_text_input = true; - g.SliderAsInputTextId = 0; - } + // Same sign + linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f; } - // Tabbing or CTRL-clicking through slider turns into an input box - bool value_changed = false; - if (start_text_input || (g.ActiveId == id && id == g.SliderAsInputTextId)) - { - char text_buf[64]; - ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), "%.*f", decimal_precision, *v); - - g.ActiveId = g.SliderAsInputTextId; - g.HoveredId = 0; - window->FocusItemUnregister(); // Our replacement slider will override the focus ID (registered previously to allow for a TAB focus to happen) - value_changed = ImGui::InputText(label, text_buf, IM_ARRAYSIZE(text_buf), ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll); - if (g.SliderAsInputTextId == 0) - { - // First frame - IM_ASSERT(g.ActiveId == id); // InputText ID should match the Slider ID (else we'd need to store them both which is also possible) - g.SliderAsInputTextId = g.ActiveId; - g.ActiveId = id; - g.HoveredId = id; - } - else - { - if (g.ActiveId == g.SliderAsInputTextId) - g.ActiveId = id; - else - g.ActiveId = g.SliderAsInputTextId = 0; - } - if (value_changed) - { - ApplyNumericalTextInput(text_buf, v); - } - return value_changed; - } - - ItemSize(bb); - RenderFrame(frame_bb.Min, frame_bb.Max, window->Color(ImGuiCol_FrameBg)); - // Process clicking on the slider if (g.ActiveId == id) { if (g.IO.MouseDown[0]) { - if (!is_unbound) - { - const float normalized_pos = ImClamp((g.IO.MousePos.x - slider_effective_x1) / slider_effective_w, 0.0f, 1.0f); - - // Linear slider - //float new_value = ImLerp(v_min, v_max, normalized_pos); + const float mouse_abs_pos = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; + float normalized_pos = ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f); + if (!horizontal) + normalized_pos = 1.0f - normalized_pos; + float new_value; + if (is_non_linear) + { // Account for logarithmic scale on both sides of the zero - float new_value; if (normalized_pos < linear_zero_pos) { // Negative: rescale to the negative range before powering float a = 1.0f - (normalized_pos / linear_zero_pos); a = powf(a, power); - new_value = ImLerp(ImMin(v_max,0.f), v_min, a); + new_value = ImLerp(ImMin(v_max,0.0f), v_min, a); } else { @@ -3756,37 +5364,32 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c a = powf(a, power); new_value = ImLerp(ImMax(v_min,0.0f), v_max, a); } + } + else + { + // Linear slider + new_value = ImLerp(v_min, v_max, normalized_pos); + } - // Round past decimal precision - // 0->1, 1->0.1, 2->0.01, etc. - // So when our value is 1.99999 with a precision of 0.001 we'll end up rounding to 2.0 - const float min_step = 1.0f / powf(10.0f, (float)decimal_precision); - const float remainder = fmodf(new_value, min_step); - if (remainder <= min_step*0.5f) - new_value -= remainder; - else - new_value += (min_step - remainder); + // Round past decimal precision + new_value = RoundScalar(new_value, decimal_precision); - if (*v != new_value) - { - *v = new_value; - value_changed = true; - } + if (*v != new_value) + { + *v = new_value; + value_changed = true; } } else { - g.ActiveId = 0; + SetActiveId(0); } } - if (!is_unbound) + // Calculate slider grab positioning + float grab_t; + if (is_non_linear) { - // Linear slider - // const float grab_t = (ImClamp(*v, v_min, v_max) - v_min) / (v_max - v_min); - - // Calculate slider grab positioning - float grab_t; float v_clamped = ImClamp(*v, v_min, v_max); if (v_clamped < 0.0f) { @@ -3798,28 +5401,152 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c const float f = (v_clamped - ImMax(0.0f,v_min)) / (v_max - ImMax(0.0f,v_min)); grab_t = linear_zero_pos + powf(f, 1.0f/power) * (1.0f - linear_zero_pos); } - - // Draw - const float grab_x = ImLerp(slider_effective_x1, slider_effective_x2, grab_t); - const ImGuiAabb grab_bb(ImVec2(grab_x-grab_size_in_pixels*0.5f,frame_bb.Min.y+2.0f), ImVec2(grab_x+grab_size_in_pixels*0.5f,frame_bb.Max.y-2.0f)); - window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, window->Color(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab)); + } + else + { + // Linear slider + grab_t = (ImClamp(*v, v_min, v_max) - v_min) / (v_max - v_min); } - // Draw value using user-provided display format so user can add prefix/suffix/decorations to the value. - char value_buf[64]; - ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v); - RenderText(ImVec2(slider_bb.GetCenter().x-CalcTextSize(value_buf).x*0.5f, frame_bb.Min.y + style.FramePadding.y), value_buf); - - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, slider_bb.Min.y), label); + // Draw + if (!horizontal) + grab_t = 1.0f - grab_t; + const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); + ImRect grab_bb; + if (horizontal) + grab_bb = ImRect(ImVec2(grab_pos - grab_sz*0.5f, frame_bb.Min.y + 2.0f), ImVec2(grab_pos + grab_sz*0.5f, frame_bb.Max.y - 2.0f)); + else + grab_bb = ImRect(ImVec2(frame_bb.Min.x + 2.0f, grab_pos - grab_sz*0.5f), ImVec2(frame_bb.Max.x - 2.0f, grab_pos + grab_sz*0.5f)); + window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, window->Color(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab)); return value_changed; } -bool ImGui::SliderAngle(const char* label, float* v, float v_degrees_min, float v_degrees_max) +// Use power!=1.0 for logarithmic sliders. +// Adjust display_format to decorate the value with a prefix or a suffix. +// "%.3f" 1.234 +// "%5.2f secs" 01.23 secs +// "Gold: %.0f" Gold: 1 +bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format, float power) { - float v_deg = *v * 360.0f / (2*PI); + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const float w = ImGui::CalcItemWidth(); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y) + style.FramePadding*2.0f); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + + // NB- we don't call ItemSize() yet because we may turn into a text edit box below + if (!ItemAdd(total_bb, &id)) + { + ItemSize(total_bb, style.FramePadding.y); + return false; + } + + const bool hovered = IsHovered(frame_bb, id); + if (hovered) + g.HoveredId = id; + + if (!display_format) + display_format = "%.3f"; + int decimal_precision = 3; + ParseFormat(display_format, decimal_precision); + + // Tabbing or CTRL-clicking on Slider turns it into an input box + bool start_text_input = false; + const bool tab_focus_requested = window->FocusItemRegister(g.ActiveId == id); + if (tab_focus_requested || (hovered && g.IO.MouseClicked[0])) + { + SetActiveId(id); + FocusWindow(window); + + const bool is_ctrl_down = g.IO.KeyCtrl; + if (tab_focus_requested || is_ctrl_down) + { + start_text_input = true; + g.ScalarAsInputTextId = 0; + } + } + if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) + return SliderFloatAsInputText(label, v, id, decimal_precision); + + ItemSize(total_bb, style.FramePadding.y); + + // Actual slider behavior + render grab + const bool value_changed = SliderScalarBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision, true); + + // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. + char value_buf[64]; + const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v); + const ImVec2 value_text_size = CalcTextSize(value_buf, value_buf_end, true); + RenderTextClipped(ImVec2(ImMax(frame_bb.Min.x + style.FramePadding.x, frame_bb.GetCenter().x - value_text_size.x*0.5f), frame_bb.Min.y + style.FramePadding.y), value_buf, value_buf_end, &value_text_size, frame_bb.Max); + + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + return value_changed; +} + +bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format, float power) +{ + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); + const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + + ItemSize(bb, style.FramePadding.y); + if (!ItemAdd(frame_bb, &id)) + return false; + + const bool hovered = IsHovered(frame_bb, id); + if (hovered) + g.HoveredId = id; + + if (!display_format) + display_format = "%.3f"; + int decimal_precision = 3; + ParseFormat(display_format, decimal_precision); + + if (hovered && g.IO.MouseClicked[0]) + { + SetActiveId(id); + FocusWindow(window); + } + + // Actual slider behavior + render grab + bool value_changed = SliderScalarBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision, false); + + // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. + // For the vertical slider we allow centered text to overlap the frame padding + char value_buf[64]; + char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v); + const ImVec2 value_text_size = CalcTextSize(value_buf, value_buf_end, true); + RenderTextClipped(ImVec2(ImMax(frame_bb.Min.x, frame_bb.GetCenter().x - value_text_size.x*0.5f), frame_bb.Min.y + style.FramePadding.y), value_buf, value_buf_end, &value_text_size, frame_bb.Max); + + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + return value_changed; +} + +bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max) +{ + float v_deg = (*v_rad) * 360.0f / (2*PI); bool value_changed = ImGui::SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, "%.0f deg", 1.0f); - *v = v_deg * (2*PI) / 360.0f; + *v_rad = v_deg * (2*PI) / 360.0f; return value_changed; } @@ -3833,38 +5560,40 @@ bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const cha return value_changed; } -// Add multiple sliders on 1 line for compact edition of multiple components -static bool SliderFloatN(const char* label, float v[3], int components, float v_min, float v_max, const char* display_format, float power) +bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format) { - ImGuiState& g = GImGui; + if (!display_format) + display_format = "%.0f"; + float v_f = (float)*v; + bool value_changed = ImGui::VSliderFloat(label, size, &v_f, (float)v_min, (float)v_max, display_format, 1.0f); + *v = (int)v_f; + return value_changed; +} + +// Add multiple sliders on 1 line for compact edition of multiple components +static bool SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power) +{ + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; - const ImGuiStyle& style = g.Style; - const float w_full = window->DC.ItemWidth.back(); - const float w_item_one = ImMax(1.0f, (float)(int)((w_full - (style.FramePadding.x*2.0f+style.ItemInnerSpacing.x)*(components-1)) / (float)components)); - const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one+style.FramePadding.x*2.0f+style.ItemInnerSpacing.x)*(components-1))); - bool value_changed = false; + ImGui::BeginGroup(); ImGui::PushID(label); - ImGui::PushItemWidth(w_item_one); + PushMultiItemsWidths(components); for (int i = 0; i < components; i++) { ImGui::PushID(i); - if (i + 1 == components) - { - ImGui::PopItemWidth(); - ImGui::PushItemWidth(w_item_last); - } value_changed |= ImGui::SliderFloat("##v", &v[i], v_min, v_max, display_format, power); - ImGui::SameLine(0, 0); + ImGui::SameLine(0, (int)g.Style.ItemInnerSpacing.x); ImGui::PopID(); + ImGui::PopItemWidth(); } - ImGui::PopItemWidth(); ImGui::PopID(); ImGui::TextUnformatted(label, FindTextDisplayEnd(label)); + ImGui::EndGroup(); return value_changed; } @@ -3884,6 +5613,286 @@ bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max return SliderFloatN(label, v, 4, v_min, v_max, display_format, power); } +static bool SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format) +{ + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + bool value_changed = false; + ImGui::BeginGroup(); + ImGui::PushID(label); + PushMultiItemsWidths(components); + for (int i = 0; i < components; i++) + { + ImGui::PushID(i); + value_changed |= ImGui::SliderInt("##v", &v[i], v_min, v_max, display_format); + ImGui::SameLine(0, (int)g.Style.ItemInnerSpacing.x); + ImGui::PopID(); + ImGui::PopItemWidth(); + } + ImGui::PopID(); + + ImGui::TextUnformatted(label, FindTextDisplayEnd(label)); + ImGui::EndGroup(); + + return value_changed; +} + +bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format) +{ + return SliderIntN(label, v, 2, v_min, v_max, display_format); +} + +bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format) +{ + return SliderIntN(label, v, 3, v_min, v_max, display_format); +} + +bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format) +{ + return SliderIntN(label, v, 4, v_min, v_max, display_format); +} + +// FIXME-WIP: Work in progress. May change API / behavior. +static bool DragScalarBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power) +{ + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + const ImGuiStyle& style = g.Style; + + // Draw frame + const ImU32 frame_col = window->Color(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); + + bool value_changed = false; + + // Process clicking on the drag + if (g.ActiveId == id) + { + if (g.IO.MouseDown[0]) + { + if (g.ActiveIdIsJustActivated) + { + // Lock current value on click + g.DragCurrentValue = *v; + g.DragLastMouseDelta = ImVec2(0.f, 0.f); + } + + const ImVec2 mouse_drag_delta = ImGui::GetMouseDragDelta(0, 1.0f); + if (fabsf(mouse_drag_delta.x - g.DragLastMouseDelta.x) > 0.0f) + { + float speed = v_speed; + if (speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX) + speed = (v_max - v_min) * g.DragSpeedDefaultRatio; + if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f) + speed = speed * g.DragSpeedScaleFast; + if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f) + speed = speed * g.DragSpeedScaleSlow; + + float v_cur = g.DragCurrentValue; + float delta = (mouse_drag_delta.x - g.DragLastMouseDelta.x) * speed; + if (fabsf(power - 1.0f) > 0.001f) + { + // Logarithmic curve on both side of 0.0 + float v0_abs = v_cur >= 0.0f ? v_cur : -v_cur; + float v0_sign = v_cur >= 0.0f ? 1.0f : -1.0f; + float v1 = powf(v0_abs, 1.0f / power) + (delta * v0_sign); + float v1_abs = v1 >= 0.0f ? v1 : -v1; + float v1_sign = v1 >= 0.0f ? 1.0f : -1.0f; // Crossed sign line + v_cur = powf(v1_abs, power) * v0_sign * v1_sign; // Reapply sign + } + else + { + v_cur += delta; + } + g.DragLastMouseDelta.x = mouse_drag_delta.x; + + // Clamp + if (v_min < v_max) + v_cur = ImClamp(v_cur, v_min, v_max); + g.DragCurrentValue = v_cur; + + // Round to user desired precision, then apply + v_cur = RoundScalar(v_cur, decimal_precision); + if (*v != v_cur) + { + *v = v_cur; + value_changed = true; + } + } + } + else + { + SetActiveId(0); + } + } + + return value_changed; +} + +bool ImGui::DragFloat(const char* label, float *v, float v_speed, float v_min, float v_max, const char* display_format, float power) +{ + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const float w = ImGui::CalcItemWidth(); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y) + style.FramePadding*2.0f); + const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + + // NB- we don't call ItemSize() yet because we may turn into a text edit box below + if (!ItemAdd(total_bb, &id)) + { + ItemSize(total_bb, style.FramePadding.y); + return false; + } + + const bool hovered = IsHovered(frame_bb, id); + if (hovered) + g.HoveredId = id; + + if (!display_format) + display_format = "%.3f"; + int decimal_precision = 3; + ParseFormat(display_format, decimal_precision); + + // Tabbing or CTRL-clicking on Drag turns it into an input box + bool start_text_input = false; + const bool tab_focus_requested = window->FocusItemRegister(g.ActiveId == id); + if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] | g.IO.MouseDoubleClicked[0]))) + { + SetActiveId(id); + FocusWindow(window); + + if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0]) + { + start_text_input = true; + g.ScalarAsInputTextId = 0; + } + } + if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) + return SliderFloatAsInputText(label, v, id, decimal_precision); + + ItemSize(total_bb, style.FramePadding.y); + + // Actual drag behavior + const bool value_changed = DragScalarBehavior(frame_bb, id, v, v_speed, v_min, v_max, decimal_precision, power); + + // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. + char value_buf[64]; + const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v); + const ImVec2 value_text_size = CalcTextSize(value_buf, value_buf_end, true); + RenderTextClipped(ImVec2(ImMax(frame_bb.Min.x + style.FramePadding.x, inner_bb.GetCenter().x - value_text_size.x*0.5f), frame_bb.Min.y + style.FramePadding.y), value_buf, value_buf_end, &value_text_size, frame_bb.Max); + + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); + + return value_changed; +} + +static bool DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* display_format, float power) +{ + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + bool value_changed = false; + ImGui::BeginGroup(); + ImGui::PushID(label); + PushMultiItemsWidths(components); + for (int i = 0; i < components; i++) + { + ImGui::PushID(i); + value_changed |= ImGui::DragFloat("##v", &v[i], v_speed, v_min, v_max, display_format, power); + ImGui::SameLine(0, (int)g.Style.ItemInnerSpacing.x); + ImGui::PopID(); + ImGui::PopItemWidth(); + } + ImGui::PopID(); + + ImGui::TextUnformatted(label, FindTextDisplayEnd(label)); + ImGui::EndGroup(); + + return value_changed; +} + +bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* display_format, float power) +{ + return DragFloatN(label, v, 2, v_speed, v_min, v_max, display_format, power); +} + +bool ImGui::DragFloat3(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* display_format, float power) +{ + return DragFloatN(label, v, 3, v_speed, v_min, v_max, display_format, power); +} + +bool ImGui::DragFloat4(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* display_format, float power) +{ + return DragFloatN(label, v, 4, v_speed, v_min, v_max, display_format, power); +} + +// NB: v_speed is float to allow adjusting the drag speed with more precision +bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* display_format) +{ + if (!display_format) + display_format = "%.0f"; + float v_f = (float)*v; + bool value_changed = ImGui::DragFloat(label, &v_f, v_speed, (float)v_min, (float)v_max, display_format); + *v = (int)v_f; + return value_changed; +} + +static bool DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format) +{ + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + bool value_changed = false; + ImGui::BeginGroup(); + ImGui::PushID(label); + PushMultiItemsWidths(components); + for (int i = 0; i < components; i++) + { + ImGui::PushID(i); + value_changed |= ImGui::DragInt("##v", &v[i], v_speed, v_min, v_max, display_format); + ImGui::SameLine(0, (int)g.Style.ItemInnerSpacing.x); + ImGui::PopID(); + ImGui::PopItemWidth(); + } + ImGui::PopID(); + + ImGui::TextUnformatted(label, FindTextDisplayEnd(label)); + ImGui::EndGroup(); + + return value_changed; +} + +bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* display_format) +{ + return DragIntN(label, v, 2, v_speed, v_min, v_max, display_format); +} + +bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* display_format) +{ + return DragIntN(label, v, 3, v_speed, v_min, v_max, display_format); +} + +bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* display_format) +{ + return DragIntN(label, v, 4, v_speed, v_min, v_max, display_format); +} + enum ImGuiPlotType { ImGuiPlotType_Lines, @@ -3892,25 +5901,24 @@ enum ImGuiPlotType static void Plot(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; const ImGuiStyle& style = g.Style; - const ImVec2 text_size = ImGui::CalcTextSize(label); + const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); if (graph_size.x == 0.0f) - graph_size.x = window->DC.ItemWidth.back(); + graph_size.x = ImGui::CalcItemWidth() + (style.FramePadding.x * 2); if (graph_size.y == 0.0f) - graph_size.y = text_size.y; + graph_size.y = label_size.y + (style.FramePadding.y * 2); - const ImGuiAabb frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y) + style.FramePadding*2.0f); - const ImGuiAabb graph_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); - const ImGuiAabb bb(frame_bb.Min, frame_bb.Max + ImVec2(style.ItemInnerSpacing.x + text_size.x,0)); - ItemSize(bb); - - if (ClipAdvance(bb)) + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y)); + const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0)); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, NULL)) return; // Determine scale from values if not specified @@ -3930,7 +5938,7 @@ static void Plot(ImGuiPlotType plot_type, const char* label, float (*values_gett scale_max = v_max; } - RenderFrame(frame_bb.Min, frame_bb.Max, window->Color(ImGuiCol_FrameBg)); + RenderFrame(frame_bb.Min, frame_bb.Max, window->Color(ImGuiCol_FrameBg), true, style.FrameRounding); int res_w = ImMin((int)graph_size.x, values_count); if (plot_type == ImGuiPlotType_Lines) @@ -3938,9 +5946,9 @@ static void Plot(ImGuiPlotType plot_type, const char* label, float (*values_gett // Tooltip on hover int v_hovered = -1; - if (IsMouseHoveringBox(graph_bb)) + if (IsMouseHoveringRect(inner_bb)) { - const float t = ImClamp((g.IO.MousePos.x - graph_bb.Min.x) / (graph_bb.Max.x - graph_bb.Min.x), 0.0f, 0.9999f); + const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f); const int v_idx = (int)(t * (values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0))); IM_ASSERT(v_idx >= 0 && v_idx < values_count); @@ -3972,9 +5980,9 @@ static void Plot(ImGuiPlotType plot_type, const char* label, float (*values_gett // NB- Draw calls are merged together by the DrawList system. if (plot_type == ImGuiPlotType_Lines) - window->DrawList->AddLine(ImLerp(graph_bb.Min, graph_bb.Max, p0), ImLerp(graph_bb.Min, graph_bb.Max, p1), v_hovered == v_idx ? col_hovered : col_base); + window->DrawList->AddLine(ImLerp(inner_bb.Min, inner_bb.Max, p0), ImLerp(inner_bb.Min, inner_bb.Max, p1), v_hovered == v_idx ? col_hovered : col_base); else if (plot_type == ImGuiPlotType_Histogram) - window->DrawList->AddRectFilled(ImLerp(graph_bb.Min, graph_bb.Max, p0), ImLerp(graph_bb.Min, graph_bb.Max, ImVec2(p1.x, 1.0f))+ImVec2(-1,0), v_hovered == v_idx ? col_hovered : col_base); + window->DrawList->AddRectFilled(ImLerp(inner_bb.Min, inner_bb.Max, p0), ImLerp(inner_bb.Min, inner_bb.Max, ImVec2(p1.x, 1.0f))+ImVec2(-1,0), v_hovered == v_idx ? col_hovered : col_base); t0 = t1; p0 = p1; @@ -3982,9 +5990,9 @@ static void Plot(ImGuiPlotType plot_type, const char* label, float (*values_gett // Text overlay if (overlay_text) - RenderText(ImVec2(graph_bb.GetCenter().x - ImGui::CalcTextSize(overlay_text).x*0.5f, frame_bb.Min.y + style.FramePadding.y), overlay_text); + RenderTextClipped(ImVec2(ImMax(inner_bb.Min.x, inner_bb.GetCenter().x - ImGui::CalcTextSize(overlay_text, NULL, true).x*0.5f), frame_bb.Min.y + style.FramePadding.y), overlay_text, NULL, NULL, frame_bb.Max); - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, graph_bb.Min.y), label); + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); } struct ImGuiPlotArrayGetterData @@ -4026,43 +6034,42 @@ void ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, bool ImGui::Checkbox(const char* label, bool* v) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImVec2 text_size = CalcTextSize(label); + const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2, label_size.y + style.FramePadding.y*2)); + ItemSize(check_bb, style.FramePadding.y); - const ImGuiAabb check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(text_size.y + style.FramePadding.y*2, text_size.y + style.FramePadding.y*2)); - ItemSize(check_bb); - SameLine(0, (int)g.Style.ItemInnerSpacing.x); - - const ImGuiAabb text_bb(window->DC.CursorPos + ImVec2(0,style.FramePadding.y), window->DC.CursorPos + ImVec2(0,style.FramePadding.y) + text_size); - ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight())); - const ImGuiAabb total_bb(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max)); - - if (ClipAdvance(total_bb)) - return false; - - const bool hovered = (g.HoveredWindow == window) && (g.HoveredId == 0) && IsMouseHoveringBox(total_bb); - const bool pressed = hovered && g.IO.MouseClicked[0]; - if (hovered) - g.HoveredId = id; - if (pressed) + ImRect total_bb = check_bb; + if (label_size.x > 0) + SameLine(0, (int)style.ItemInnerSpacing.x); + const ImRect text_bb(window->DC.CursorPos + ImVec2(0,style.FramePadding.y), window->DC.CursorPos + ImVec2(0,style.FramePadding.y) + label_size); + if (label_size.x > 0) { - *v = !(*v); - g.ActiveId = 0; // Clear focus + ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y); + total_bb = ImRect(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max)); } - RenderFrame(check_bb.Min, check_bb.Max, window->Color(hovered ? ImGuiCol_CheckHovered : ImGuiCol_FrameBg)); + if (!ItemAdd(total_bb, &id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(total_bb, id, &hovered, &held, true); + if (pressed) + *v = !(*v); + + RenderFrame(check_bb.Min, check_bb.Max, window->Color((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); if (*v) { const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight()); const float pad = check_sz < 8.0f ? 1.0f : check_sz < 13.0f ? 2.0f : 3.0f; - window->DrawList->AddRectFilled(check_bb.Min+ImVec2(pad,pad), check_bb.Max-ImVec2(pad,pad), window->Color(ImGuiCol_CheckActive)); + window->DrawList->AddRectFilled(check_bb.Min+ImVec2(pad,pad), check_bb.Max-ImVec2(pad,pad), window->Color(ImGuiCol_CheckMark), style.FrameRounding); } if (g.LogEnabled) @@ -4085,25 +6092,29 @@ bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int f bool ImGui::RadioButton(const char* label, bool active) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImVec2 text_size = CalcTextSize(label); + const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2-1, label_size.y + style.FramePadding.y*2-1)); + ItemSize(check_bb, style.FramePadding.y); - const ImGuiAabb check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(text_size.y + style.FramePadding.y*2-1, text_size.y + style.FramePadding.y*2-1)); - ItemSize(check_bb); - SameLine(0, (int)style.ItemInnerSpacing.x); + ImRect total_bb = check_bb; + if (label_size.x > 0) + SameLine(0, (int)style.ItemInnerSpacing.x); + const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size); + if (label_size.x > 0) + { + ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y); + total_bb.Add(text_bb); + } - const ImGuiAabb text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + text_size); - ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight())); - const ImGuiAabb total_bb(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max)); - - if (ClipAdvance(total_bb)) + if (!ItemAdd(total_bb, &id)) return false; ImVec2 center = check_bb.GetCenter(); @@ -4111,17 +6122,15 @@ bool ImGui::RadioButton(const char* label, bool active) center.y = (float)(int)center.y + 0.5f; const float radius = check_bb.GetHeight() * 0.5f; - const bool hovered = (g.HoveredWindow == window) && (g.HoveredId == 0) && IsMouseHoveringBox(total_bb); - const bool pressed = hovered && g.IO.MouseClicked[0]; - if (hovered) - g.HoveredId = id; + bool hovered, held; + bool pressed = ButtonBehavior(total_bb, id, &hovered, &held, true); - window->DrawList->AddCircleFilled(center, radius, window->Color(hovered ? ImGuiCol_CheckHovered : ImGuiCol_FrameBg), 16); + window->DrawList->AddCircleFilled(center, radius, window->Color((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16); if (active) { const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight()); const float pad = check_sz < 8.0f ? 1.0f : check_sz < 13.0f ? 2.0f : 3.0f; - window->DrawList->AddCircleFilled(center, radius-pad, window->Color(ImGuiCol_CheckActive), 16); + window->DrawList->AddCircleFilled(center, radius-pad, window->Color(ImGuiCol_CheckMark), 16); } if (window->Flags & ImGuiWindowFlags_ShowBorders) @@ -4165,45 +6174,70 @@ static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* ob r->num_chars = (int)(text_remaining - (obj->Text + line_start_idx)); } -static bool is_white(unsigned int c) { return c==0 || c==' ' || c=='\t' || c=='\r' || c=='\n'; } static bool is_separator(unsigned int c) { return c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; } -#define STB_TEXTEDIT_IS_SPACE(CH) ( is_white((unsigned int)CH) || is_separator((unsigned int)CH) ) -static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) { ImWchar* dst = obj->Text+pos; const ImWchar* src = obj->Text+pos+n; while (ImWchar c = *src++) *dst++ = c; *dst = '\0'; } +#define STB_TEXTEDIT_IS_SPACE(CH) ( ImCharIsSpace((unsigned int)CH) || is_separator((unsigned int)CH) ) +static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) +{ + ImWchar* dst = obj->Text + pos; + + // We maintain our buffer length in both UTF-8 and wchar formats + obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n); + obj->CurLenW -= n; + + // Offset remaining text + const ImWchar* src = obj->Text + pos + n; + while (ImWchar c = *src++) + *dst++ = c; + *dst = '\0'; +} + static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len) { - const size_t text_len = ImStrlenW(obj->Text); - if ((size_t)new_text_len + text_len + 1 >= obj->BufSize) + const size_t text_len = obj->CurLenW; + if ((size_t)new_text_len + text_len + 1 > IM_ARRAYSIZE(obj->Text)) + return false; + + const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len); + if ((size_t)new_text_len_utf8 + obj->CurLenA + 1 > obj->BufSizeA) return false; if (pos != (int)text_len) memmove(obj->Text + (size_t)pos + new_text_len, obj->Text + (size_t)pos, (text_len - (size_t)pos) * sizeof(ImWchar)); memcpy(obj->Text + (size_t)pos, new_text, (size_t)new_text_len * sizeof(ImWchar)); - obj->Text[text_len + (size_t)new_text_len] = '\0'; + + obj->CurLenW += new_text_len; + obj->CurLenA += new_text_len_utf8; + obj->Text[obj->CurLenW] = '\0'; return true; } -enum -{ - STB_TEXTEDIT_K_LEFT = 1 << 16, // keyboard input to move cursor left - STB_TEXTEDIT_K_RIGHT, // keyboard input to move cursor right - STB_TEXTEDIT_K_UP, // keyboard input to move cursor up - STB_TEXTEDIT_K_DOWN, // keyboard input to move cursor down - STB_TEXTEDIT_K_LINESTART, // keyboard input to move cursor to start of line - STB_TEXTEDIT_K_LINEEND, // keyboard input to move cursor to end of line - STB_TEXTEDIT_K_TEXTSTART, // keyboard input to move cursor to start of text - STB_TEXTEDIT_K_TEXTEND, // keyboard input to move cursor to end of text - STB_TEXTEDIT_K_DELETE, // keyboard input to delete selection or character under cursor - STB_TEXTEDIT_K_BACKSPACE, // keyboard input to delete selection or character left of cursor - STB_TEXTEDIT_K_UNDO, // keyboard input to perform undo - STB_TEXTEDIT_K_REDO, // keyboard input to perform redo - STB_TEXTEDIT_K_WORDLEFT, // keyboard input to move cursor left one word - STB_TEXTEDIT_K_WORDRIGHT, // keyboard input to move cursor right one word - STB_TEXTEDIT_K_SHIFT = 1 << 17 -}; +// We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols) +#define STB_TEXTEDIT_K_LEFT 0x10000 // keyboard input to move cursor left +#define STB_TEXTEDIT_K_RIGHT 0x10001 // keyboard input to move cursor right +#define STB_TEXTEDIT_K_UP 0x10002 // keyboard input to move cursor up +#define STB_TEXTEDIT_K_DOWN 0x10003 // keyboard input to move cursor down +#define STB_TEXTEDIT_K_LINESTART 0x10004 // keyboard input to move cursor to start of line +#define STB_TEXTEDIT_K_LINEEND 0x10005 // keyboard input to move cursor to end of line +#define STB_TEXTEDIT_K_TEXTSTART 0x10006 // keyboard input to move cursor to start of text +#define STB_TEXTEDIT_K_TEXTEND 0x10007 // keyboard input to move cursor to end of text +#define STB_TEXTEDIT_K_DELETE 0x10008 // keyboard input to delete selection or character under cursor +#define STB_TEXTEDIT_K_BACKSPACE 0x10009 // keyboard input to delete selection or character left of cursor +#define STB_TEXTEDIT_K_UNDO 0x1000A // keyboard input to perform undo +#define STB_TEXTEDIT_K_REDO 0x1000B // keyboard input to perform redo +#define STB_TEXTEDIT_K_WORDLEFT 0x1000C // keyboard input to move cursor left one word +#define STB_TEXTEDIT_K_WORDRIGHT 0x1000D // keyboard input to move cursor right one word +#define STB_TEXTEDIT_K_SHIFT 0x20000 +#ifdef IMGUI_STB_NAMESPACE +namespace IMGUI_STB_NAMESPACE +{ +#endif #define STB_TEXTEDIT_IMPLEMENTATION #include "stb_textedit.h" +#ifdef IMGUI_STB_NAMESPACE +} +#endif void ImGuiTextEditState::OnKeyPressed(int key) { @@ -4216,9 +6250,21 @@ void ImGuiTextEditState::UpdateScrollOffset() // Scroll in chunks of quarter width const float scroll_x_increment = Width * 0.25f; const float cursor_offset_x = Font->CalcTextSizeW(FontSize, FLT_MAX, Text, Text+StbState.cursor, NULL).x; - if (ScrollX > cursor_offset_x) + + // If widget became bigger than text (because of a resize), reset horizontal scrolling + if (ScrollX > 0.0f) + { + const float text_width = cursor_offset_x + Font->CalcTextSizeW(FontSize, FLT_MAX, Text+StbState.cursor, NULL, NULL).x; + if (text_width < Width) + { + ScrollX = 0.0f; + return; + } + } + + if (cursor_offset_x < ScrollX) ScrollX = ImMax(0.0f, cursor_offset_x - scroll_x_increment); - else if (ScrollX < cursor_offset_x - Width) + else if (cursor_offset_x - Width >= ScrollX) ScrollX = cursor_offset_x - Width + scroll_x_increment; } @@ -4228,7 +6274,7 @@ ImVec2 ImGuiTextEditState::CalcDisplayOffsetFromCharIdx(int i) const const ImWchar* text_end = (Text+i >= text_start) ? Text+i : text_start; // Clip if requested character is outside of display IM_ASSERT(text_end >= text_start); - const ImVec2 offset = Font->CalcTextSizeW(FontSize, Width, text_start, text_end, NULL); + const ImVec2 offset = Font->CalcTextSizeW(FontSize, Width+1, text_start, text_end, NULL); return offset; } @@ -4261,44 +6307,38 @@ const ImWchar* ImGuiTextEditState::GetTextPointerClippedW(ImFont* font, float fo // [Static] void ImGuiTextEditState::RenderTextScrolledClipped(ImFont* font, float font_size, const char* buf, ImVec2 pos, float width, float scroll_x) { - // NB- We start drawing at character boundary - ImVec2 text_size; - const char* text_start = GetTextPointerClippedA(font, font_size, buf, scroll_x, NULL); - const char* text_end = GetTextPointerClippedA(font, font_size, text_start, width, &text_size); + ImGuiWindow* window = GetCurrentWindow(); + const ImU32 font_color = window->Color(ImGuiCol_Text); + //window->DrawList->AddLine(pos, pos+ImVec2(width,0), 0xFF00FFFF); - // Draw a little clip symbol if we've got text on either left or right of the box - const char symbol_c = '~'; - const float symbol_w = font_size*0.40f; // FIXME: compute correct width - const float clip_begin = (text_start > buf && text_start < text_end) ? symbol_w : 0.0f; - const float clip_end = (text_end[0] != '\0' && text_end > text_start) ? symbol_w : 0.0f; + // Determine start and end of visible string + // FIXME-OPT: This is pretty slow for what it does. + const char* text_start = scroll_x <= 0.0f ? buf : GetTextPointerClippedA(font, font_size, buf, scroll_x, NULL); + const char* text_end = GetTextPointerClippedA(font, font_size, text_start, width + 1, NULL); // +1 to allow character spacing to fit outside the allowed width + window->DrawList->AddText(font, font_size, pos, font_color, text_start, text_end); - // Draw text - RenderText(pos+ImVec2(clip_begin,0), text_start+(clip_begin>0.0f?1:0), text_end-(clip_end>0.0f?1:0), false);//, &text_params_with_clipping); - - // Draw the clip symbol - const char s[2] = {symbol_c,'\0'}; - if (clip_begin > 0.0f) - RenderText(pos, s); - if (clip_end > 0.0f) - RenderText(pos+ImVec2(width-clip_end,0.0f), s); + // Log as text + if (GImGui->LogEnabled) + LogText(pos, buf, NULL); } bool ImGui::InputFloat(const char* label, float *v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; const ImGuiStyle& style = g.Style; - const float w = window->DC.ItemWidth.back(); - const ImVec2 text_size = CalcTextSize(label); - const ImGuiAabb frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, text_size.y) + style.FramePadding*2.0f); + const float w = ImGui::CalcItemWidth(); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y) + style.FramePadding*2.0f); + ImGui::BeginGroup(); ImGui::PushID(label); - const float button_sz = window->FontSize(); + const ImVec2 button_sz = ImVec2(g.FontSize, g.FontSize) + style.FramePadding * 2; if (step > 0.0f) - ImGui::PushItemWidth(ImMax(1.0f, window->DC.ItemWidth.back() - (button_sz+g.Style.FramePadding.x*2.0f+g.Style.ItemInnerSpacing.x)*2)); + ImGui::PushItemWidth(ImMax(1.0f, w - (button_sz.x + style.ItemInnerSpacing.x)*2)); char buf[64]; if (decimal_precision < 0) @@ -4317,14 +6357,14 @@ bool ImGui::InputFloat(const char* label, float *v, float step, float step_fast, if (step > 0.0f) { ImGui::PopItemWidth(); - ImGui::SameLine(0, 0); - if (ImGui::Button("-", ImVec2(button_sz,button_sz), true)) + ImGui::SameLine(0, (int)style.ItemInnerSpacing.x); + if (ImGui::Button("-", button_sz, true)) { *v -= g.IO.KeyCtrl && step_fast > 0.0f ? step_fast : step; value_changed = true; } - ImGui::SameLine(0, (int)g.Style.ItemInnerSpacing.x); - if (ImGui::Button("+", ImVec2(button_sz,button_sz), true)) + ImGui::SameLine(0, (int)style.ItemInnerSpacing.x); + if (ImGui::Button("+", button_sz, true)) { *v += g.IO.KeyCtrl && step_fast > 0.0f ? step_fast : step; value_changed = true; @@ -4333,7 +6373,14 @@ bool ImGui::InputFloat(const char* label, float *v, float step, float step_fast, ImGui::PopID(); - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + g.Style.FramePadding.y), label); + if (label_size.x > 0) + { + ImGui::SameLine(0, (int)style.ItemInnerSpacing.x); + RenderText(ImVec2(window->DC.CursorPos.x, window->DC.CursorPos.y + style.FramePadding.y), label); + ItemSize(label_size, style.FramePadding.y); + } + + ImGui::EndGroup(); return value_changed; } @@ -4387,10 +6434,58 @@ void ImGuiTextEditCallbackData::InsertChars(int pos, const char* new_text, const SelectionStart = SelectionEnd = CursorPos; } -// Edit a string of text -bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, void (*callback)(ImGuiTextEditCallbackData*), void* user_data) +// Return false to discard a character. +static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) { - ImGuiState& g = GImGui; + unsigned int c = *p_char; + + if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF))) + return false; + + if (c >= 0xE000 && c <= 0xF8FF) // Filter private Unicode range. I don't imagine anybody would want to input them. GLFW on OSX seems to send private characters for special keys like arrow keys. + return false; + + if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank)) + { + if (flags & ImGuiInputTextFlags_CharsDecimal) + if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/')) + return false; + + if (flags & ImGuiInputTextFlags_CharsHexadecimal) + if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F')) + return false; + + if (flags & ImGuiInputTextFlags_CharsUppercase) + if (c >= 'a' && c <= 'z') + *p_char = (c += (unsigned int)('A'-'a')); + + if (flags & ImGuiInputTextFlags_CharsNoBlank) + if (ImCharIsSpace(c)) + return false; + } + + if (flags & ImGuiInputTextFlags_CallbackCharFilter) + { + ImGuiTextEditCallbackData callback_data; + memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData)); + callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter; + callback_data.EventChar = (ImWchar)c; + callback_data.Flags = flags; + callback_data.UserData = user_data; + if (callback(&callback_data) != 0) + return false; + *p_char = callback_data.EventChar; + if (!callback_data.EventChar) + return false; + } + + return true; +} + +// Edit a string of text +bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) +{ + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; @@ -4399,72 +6494,104 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); - const float w = window->DC.ItemWidth.back(); + const float w = ImGui::CalcItemWidth(); - const ImVec2 text_size = CalcTextSize(label); - const ImGuiAabb frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, text_size.y) + style.FramePadding*2.0f); - const ImGuiAabb bb(frame_bb.Min, frame_bb.Max + ImVec2(style.ItemInnerSpacing.x + text_size.x, 0.0f)); - ItemSize(bb); - - if (ClipAdvance(frame_bb)) + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y) + style.FramePadding*2.0f); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? (style.ItemInnerSpacing.x + label_size.x) : 0.0f, 0.0f)); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, &id)) return false; - // NB: we are only allowed to access it if we are the active widget. + // NB: we are only allowed to access 'edit_state' if we are the active widget. ImGuiTextEditState& edit_state = g.InputTextState; const bool is_ctrl_down = io.KeyCtrl; const bool is_shift_down = io.KeyShift; - const bool tab_focus_requested = window->FocusItemRegister(g.ActiveId == id, (flags & ImGuiInputTextFlags_CallbackCompletion) == 0); // Using completion callback disable keyboard tabbing - //const bool align_center = (bool)(flags & ImGuiInputTextFlags_AlignCenter); // FIXME: Unsupported + const bool is_alt_down = io.KeyAlt; + const bool focus_requested = window->FocusItemRegister(g.ActiveId == id, (flags & ImGuiInputTextFlags_CallbackCompletion) == 0); // Using completion callback disable keyboard tabbing + const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent); + const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; - const bool hovered = (g.HoveredWindow == window) && (g.HoveredId == 0) && IsMouseHoveringBox(frame_bb); + const bool hovered = IsHovered(frame_bb, id); if (hovered) + { g.HoveredId = id; + g.MouseCursor = ImGuiMouseCursor_TextInput; + } + const bool user_clicked = hovered && io.MouseClicked[0]; bool select_all = (g.ActiveId != id) && (flags & ImGuiInputTextFlags_AutoSelectAll) != 0; - if (tab_focus_requested || (hovered && io.MouseClicked[0])) + if (focus_requested || user_clicked) { if (g.ActiveId != id) { // Start edition // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar) + // From the moment we focused we are ignoring the content of 'buf' ImFormatString(edit_state.InitialText, IM_ARRAYSIZE(edit_state.InitialText), "%s", buf); - ImTextStrFromUtf8(edit_state.Text, IM_ARRAYSIZE(edit_state.Text), buf, NULL); - edit_state.ScrollX = 0.0f; - edit_state.Width = w; - stb_textedit_initialize_state(&edit_state.StbState, true); + const char* buf_end = NULL; + edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text, IM_ARRAYSIZE(edit_state.Text), buf, NULL, &buf_end); + edit_state.CurLenA = buf_end - buf; // We can't get the result from ImFormatString() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. + edit_state.Width = w + style.FramePadding.x; + edit_state.InputCursorScreenPos = ImVec2(-1.f,-1.f); edit_state.CursorAnimReset(); - edit_state.LastCursorPos = ImVec2(-1.f,-1.f); - if (tab_focus_requested || is_ctrl_down) + if (edit_state.Id != id) + { + edit_state.Id = id; + edit_state.ScrollX = 0.0f; + stb_textedit_initialize_state(&edit_state.StbState, true); + if (focus_requested_by_code) + select_all = true; + } + else + { + // Recycle existing cursor/selection/undo stack but clamp position + // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler. + edit_state.StbState.cursor = ImMin(edit_state.StbState.cursor, (int)edit_state.CurLenW); + edit_state.StbState.select_start = ImMin(edit_state.StbState.select_start, (int)edit_state.CurLenW); + edit_state.StbState.select_end = ImMin(edit_state.StbState.select_end, (int)edit_state.CurLenW); + } + if (focus_requested_by_tab || (user_clicked && is_ctrl_down)) select_all = true; } - g.ActiveId = id; + SetActiveId(id); + FocusWindow(window); } else if (io.MouseClicked[0]) { // Release focus when we click outside if (g.ActiveId == id) { - g.ActiveId = 0; + SetActiveId(0); } } + // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. + // Down the line we should have a cleaner concept of focused vs active in the library. + if (g.ActiveId == id) + g.ActiveIdIsFocusedOnly = !io.MouseDown[0]; + bool value_changed = false; bool cancel_edit = false; bool enter_pressed = false; - static char text_tmp_utf8[IM_ARRAYSIZE(edit_state.InitialText)]; + if (g.ActiveId == id) + //if (edit_state.Id == id) // Works, but double-click to select-all sets cursors to end which in turn tends to scroll toward the right when shrinking widget. + { + // Update some data if we are active or last active + edit_state.Width = w + style.FramePadding.x; + edit_state.BufSizeA = buf_size; + edit_state.Font = g.Font; + edit_state.FontSize = g.FontSize; + edit_state.UpdateScrollOffset(); + } if (g.ActiveId == id) { // Edit in progress - edit_state.BufSize = buf_size < IM_ARRAYSIZE(edit_state.Text) ? buf_size : IM_ARRAYSIZE(edit_state.Text); - edit_state.Font = window->Font(); - edit_state.FontSize = window->FontSize(); - const float mx = g.IO.MousePos.x - frame_bb.Min.x - style.FramePadding.x; - const float my = window->FontSize()*0.5f; // Flatten mouse because we are doing a single-line edit + const float my = g.FontSize*0.5f; // Flatten mouse because we are doing a single-line edit - edit_state.UpdateScrollOffset(); if (select_all || (hovered && io.MouseDoubleClicked[0])) { edit_state.SelectAll(); @@ -4474,7 +6601,6 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT { stb_textedit_click(&edit_state, &edit_state.StbState, mx + edit_state.ScrollX, my); edit_state.CursorAnimReset(); - } else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock) { @@ -4482,21 +6608,41 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT edit_state.CursorAnimReset(); } if (edit_state.SelectedAllMouseLock && !io.MouseDown[0]) - edit_state.SelectedAllMouseLock = false; + edit_state.SelectedAllMouseLock = false; + + if (g.IO.InputCharacters[0]) + { + // Process text input (before we check for Return because using some IME will effectively send a Return?) + for (int n = 0; n < IM_ARRAYSIZE(g.IO.InputCharacters) && g.IO.InputCharacters[n]; n++) + { + unsigned int c = (unsigned int)g.IO.InputCharacters[n]; + if (c) + { + // Insert character if they pass filtering + if (!InputTextFilterCharacter(&c, flags, callback, user_data)) + continue; + edit_state.OnKeyPressed((int)c); + } + } + + // Consume characters + memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters)); + } const int k_mask = (is_shift_down ? STB_TEXTEDIT_K_SHIFT : 0); + const bool is_ctrl_only = is_ctrl_down && !is_alt_down && !is_shift_down; if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { edit_state.OnKeyPressed(is_ctrl_down ? STB_TEXTEDIT_K_WORDLEFT | k_mask : STB_TEXTEDIT_K_LEFT | k_mask); } else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { edit_state.OnKeyPressed(is_ctrl_down ? STB_TEXTEDIT_K_WORDRIGHT | k_mask : STB_TEXTEDIT_K_RIGHT | k_mask); } else if (IsKeyPressedMap(ImGuiKey_Home)) { edit_state.OnKeyPressed(is_ctrl_down ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } else if (IsKeyPressedMap(ImGuiKey_End)) { edit_state.OnKeyPressed(is_ctrl_down ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } else if (IsKeyPressedMap(ImGuiKey_Delete)) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } else if (IsKeyPressedMap(ImGuiKey_Backspace)) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Enter)) { g.ActiveId = 0; enter_pressed = true; } - else if (IsKeyPressedMap(ImGuiKey_Escape)) { g.ActiveId = 0; cancel_edit = true; } - else if (is_ctrl_down && IsKeyPressedMap(ImGuiKey_Z)) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); } - else if (is_ctrl_down && IsKeyPressedMap(ImGuiKey_Y)) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO); } - else if (is_ctrl_down && IsKeyPressedMap(ImGuiKey_A)) { edit_state.SelectAll(); } - else if (is_ctrl_down && (IsKeyPressedMap(ImGuiKey_X) || IsKeyPressedMap(ImGuiKey_C))) + else if (IsKeyPressedMap(ImGuiKey_Enter)) { SetActiveId(0); enter_pressed = true; } + else if (IsKeyPressedMap(ImGuiKey_Escape)) { SetActiveId(0); cancel_edit = true; } + else if (is_ctrl_only && IsKeyPressedMap(ImGuiKey_Z)) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); } + else if (is_ctrl_only && IsKeyPressedMap(ImGuiKey_Y)) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO); } + else if (is_ctrl_only && IsKeyPressedMap(ImGuiKey_A)) { edit_state.SelectAll(); } + else if (is_ctrl_only && (IsKeyPressedMap(ImGuiKey_X) || IsKeyPressedMap(ImGuiKey_C))) { // Cut, Copy const bool cut = IsKeyPressedMap(ImGuiKey_X); @@ -4506,15 +6652,15 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT if (g.IO.SetClipboardTextFn) { const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0; - const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : (int)ImStrlenW(edit_state.Text); - ImTextStrToUtf8(text_tmp_utf8, IM_ARRAYSIZE(text_tmp_utf8), edit_state.Text+ib, edit_state.Text+ie); - g.IO.SetClipboardTextFn(text_tmp_utf8); + const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : (int)edit_state.CurLenW; + ImTextStrToUtf8(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), edit_state.Text+ib, edit_state.Text+ie); + g.IO.SetClipboardTextFn(g.TempBuffer); } if (cut) stb_textedit_cut(&edit_state, &edit_state.StbState); } - else if (is_ctrl_down && IsKeyPressedMap(ImGuiKey_V)) + else if (is_ctrl_only && IsKeyPressedMap(ImGuiKey_V)) { // Paste if (g.IO.GetClipboardTextFn) @@ -4522,51 +6668,28 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT if (const char* clipboard = g.IO.GetClipboardTextFn()) { // Remove new-line from pasted buffer - size_t clipboard_len = strlen(clipboard); + const size_t clipboard_len = strlen(clipboard); ImWchar* clipboard_filtered = (ImWchar*)ImGui::MemAlloc((clipboard_len+1) * sizeof(ImWchar)); int clipboard_filtered_len = 0; for (const char* s = clipboard; *s; ) { unsigned int c; - const int bytes_count = ImTextCharFromUtf8(&c, s, NULL); - if (bytes_count <= 0) + s += ImTextCharFromUtf8(&c, s, NULL); + if (c == 0) break; - s += bytes_count; - if (c == '\n' || c == '\r') - continue; if (c >= 0x10000) continue; + if (!InputTextFilterCharacter(&c, flags, callback, user_data)) + continue; clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c; } clipboard_filtered[clipboard_filtered_len] = 0; - stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len); + if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation + stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len); ImGui::MemFree(clipboard_filtered); } } } - else if (g.IO.InputCharacters[0]) - { - // Text input - for (int n = 0; n < IM_ARRAYSIZE(g.IO.InputCharacters) && g.IO.InputCharacters[n]; n++) - { - const ImWchar c = g.IO.InputCharacters[n]; - if (c) - { - // Filter - if (c < 256 && !isprint((char)(c & 0xFF)) && c != ' ') - continue; - if (flags & ImGuiInputTextFlags_CharsDecimal) - if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/')) - continue; - if (flags & ImGuiInputTextFlags_CharsHexadecimal) - if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F')) - continue; - - // Insert character! - edit_state.OnKeyPressed(c); - } - } - } edit_state.CursorAnim += g.IO.DeltaTime; edit_state.UpdateScrollOffset(); @@ -4583,7 +6706,7 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT // Note that as soon as we can focus into the input box, the in-widget value gets priority over any underlying modification of the input buffer // FIXME: We actually always render 'buf' in RenderTextScrolledClipped // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks - ImTextStrToUtf8(text_tmp_utf8, IM_ARRAYSIZE(text_tmp_utf8), edit_state.Text, NULL); + ImTextStrToUtf8(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), edit_state.Text, NULL); // User callback if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0) @@ -4591,59 +6714,71 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT IM_ASSERT(callback != NULL); // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment. + ImGuiInputTextFlags event_flag = 0; ImGuiKey event_key = ImGuiKey_COUNT; if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab)) + { + event_flag = ImGuiInputTextFlags_CallbackCompletion; event_key = ImGuiKey_Tab; + } else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow)) + { + event_flag = ImGuiInputTextFlags_CallbackHistory; event_key = ImGuiKey_UpArrow; + } else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow)) + { + event_flag = ImGuiInputTextFlags_CallbackHistory; event_key = ImGuiKey_DownArrow; + } if (event_key != ImGuiKey_COUNT || (flags & ImGuiInputTextFlags_CallbackAlways) != 0) { ImGuiTextEditCallbackData callback_data; + callback_data.EventFlag = event_flag; callback_data.EventKey = event_key; - callback_data.Buf = text_tmp_utf8; - callback_data.BufSize = edit_state.BufSize; + callback_data.Buf = g.TempBuffer; + callback_data.BufSize = edit_state.BufSizeA; callback_data.BufDirty = false; callback_data.Flags = flags; callback_data.UserData = user_data; // We have to convert from position from wchar to UTF-8 positions - const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromWchar(edit_state.Text, edit_state.Text + edit_state.StbState.cursor); - const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromWchar(edit_state.Text, edit_state.Text + edit_state.StbState.select_start); - const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromWchar(edit_state.Text, edit_state.Text + edit_state.StbState.select_end); + const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(edit_state.Text, edit_state.Text + edit_state.StbState.cursor); + const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(edit_state.Text, edit_state.Text + edit_state.StbState.select_start); + const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(edit_state.Text, edit_state.Text + edit_state.StbState.select_end); // Call user code callback(&callback_data); // Read back what user may have modified - IM_ASSERT(callback_data.Buf == text_tmp_utf8); // Invalid to modify those fields - IM_ASSERT(callback_data.BufSize == edit_state.BufSize); + IM_ASSERT(callback_data.Buf == g.TempBuffer); // Invalid to modify those fields + IM_ASSERT(callback_data.BufSize == edit_state.BufSizeA); IM_ASSERT(callback_data.Flags == flags); if (callback_data.CursorPos != utf8_cursor_pos) edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); if (callback_data.SelectionStart != utf8_selection_start) edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); if (callback_data.SelectionEnd != utf8_selection_end) edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); if (callback_data.BufDirty) { - ImTextStrFromUtf8(edit_state.Text, IM_ARRAYSIZE(edit_state.Text), text_tmp_utf8, NULL); + edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text, IM_ARRAYSIZE(edit_state.Text), g.TempBuffer, NULL); + edit_state.CurLenA = strlen(g.TempBuffer); edit_state.CursorAnimReset(); } } } - if (strcmp(text_tmp_utf8, buf) != 0) + if (strcmp(g.TempBuffer, buf) != 0) { - ImFormatString(buf, buf_size, "%s", text_tmp_utf8); + ImFormatString(buf, buf_size, "%s", g.TempBuffer); value_changed = true; } } } - RenderFrame(frame_bb.Min, frame_bb.Max, window->Color(ImGuiCol_FrameBg), true); + RenderFrame(frame_bb.Min, frame_bb.Max, window->Color(ImGuiCol_FrameBg), true, style.FrameRounding); - const ImVec2 font_off_up = ImVec2(0.0f,window->FontSize()+1.0f); // FIXME: those offsets are part of the style or font API - const ImVec2 font_off_dn = ImVec2(0.0f,2.0f); + const ImVec2 font_off_up = ImVec2(0.0f, g.FontSize+1.0f); // FIXME: those offsets are part of the style or font API + const ImVec2 font_off_dn = ImVec2(0.0f, 2.0f); if (g.ActiveId == id) { @@ -4658,8 +6793,9 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT } } - // FIXME: 'align_center' unsupported - ImGuiTextEditState::RenderTextScrolledClipped(window->Font(), window->FontSize(), buf, frame_bb.Min + style.FramePadding, w, (g.ActiveId == id) ? edit_state.ScrollX : 0.0f); + //const float render_scroll_x = (g.ActiveId == id) ? edit_state.ScrollX : 0.0f; + const float render_scroll_x = (edit_state.Id == id) ? edit_state.ScrollX : 0.0f; + ImGuiTextEditState::RenderTextScrolledClipped(g.Font, g.FontSize, buf, frame_bb.Min + style.FramePadding, w + style.FramePadding.x, render_scroll_x); if (g.ActiveId == id) { @@ -4667,16 +6803,17 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT // Draw blinking cursor if (g.InputTextState.CursorIsVisible()) - window->DrawList->AddRect(cursor_pos - font_off_up + ImVec2(0,2), cursor_pos + font_off_dn - ImVec2(0,3), window->Color(ImGuiCol_Text)); + window->DrawList->AddLine(cursor_pos - font_off_up + ImVec2(0,2), cursor_pos + font_off_dn - ImVec2(0,3), window->Color(ImGuiCol_Text)); - // Notify OS of text input position - if (io.ImeSetInputScreenPosFn && ImLength(edit_state.LastCursorPos - cursor_pos) > 0.01f) - io.ImeSetInputScreenPosFn((int)cursor_pos.x - 1, (int)(cursor_pos.y - window->FontSize())); // -1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety. + // Notify OS of text input position for advanced IME + if (io.ImeSetInputScreenPosFn && ImLengthSqr(edit_state.InputCursorScreenPos - cursor_pos) > 0.0001f) + io.ImeSetInputScreenPosFn((int)cursor_pos.x - 1, (int)(cursor_pos.y - g.FontSize)); // -1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety. - edit_state.LastCursorPos = cursor_pos; + edit_state.InputCursorScreenPos = cursor_pos; } - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + if (label_size.x > 0) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0) return enter_pressed; @@ -4684,57 +6821,93 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT return value_changed; } -static bool InputFloatN(const char* label, float* v, int components, int decimal_precision) +static bool InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; - const ImGuiStyle& style = g.Style; - const float w_full = window->DC.ItemWidth.back(); - const float w_item_one = ImMax(1.0f, (float)(int)((w_full - (style.FramePadding.x*2.0f+style.ItemInnerSpacing.x) * (components-1)) / (float)components)); - const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one+style.FramePadding.x*2.0f+style.ItemInnerSpacing.x) * (components-1))); - bool value_changed = false; + ImGui::BeginGroup(); ImGui::PushID(label); - ImGui::PushItemWidth(w_item_one); + PushMultiItemsWidths(components); for (int i = 0; i < components; i++) { ImGui::PushID(i); - if (i + 1 == components) - { - ImGui::PopItemWidth(); - ImGui::PushItemWidth(w_item_last); - } - value_changed |= ImGui::InputFloat("##v", &v[i], 0, 0, decimal_precision); - ImGui::SameLine(0, 0); + value_changed |= ImGui::InputFloat("##v", &v[i], 0, 0, decimal_precision, extra_flags); + ImGui::SameLine(0, (int)g.Style.ItemInnerSpacing.x); ImGui::PopID(); + ImGui::PopItemWidth(); } - ImGui::PopItemWidth(); ImGui::PopID(); + window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y); ImGui::TextUnformatted(label, FindTextDisplayEnd(label)); + ImGui::EndGroup(); return value_changed; } -bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision) +bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags) { - return InputFloatN(label, v, 2, decimal_precision); + return InputFloatN(label, v, 2, decimal_precision, extra_flags); } -bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision) +bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags) { - return InputFloatN(label, v, 3, decimal_precision); + return InputFloatN(label, v, 3, decimal_precision, extra_flags); } -bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision) +bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags) { - return InputFloatN(label, v, 4, decimal_precision); + return InputFloatN(label, v, 4, decimal_precision, extra_flags); } -static bool Combo_ArrayGetter(void* data, int idx, const char** out_text) +static bool InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags) +{ + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + bool value_changed = false; + ImGui::BeginGroup(); + ImGui::PushID(label); + PushMultiItemsWidths(components); + for (int i = 0; i < components; i++) + { + ImGui::PushID(i); + value_changed |= ImGui::InputInt("##v", &v[i], 0, 0, extra_flags); + ImGui::SameLine(0, (int)g.Style.ItemInnerSpacing.x); + ImGui::PopID(); + ImGui::PopItemWidth(); + } + ImGui::PopID(); + + window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y); + ImGui::TextUnformatted(label, FindTextDisplayEnd(label)); + ImGui::EndGroup(); + + return value_changed; +} + +bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags) +{ + return InputIntN(label, v, 2, extra_flags); +} + +bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags) +{ + return InputIntN(label, v, 3, extra_flags); +} + +bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags) +{ + return InputIntN(label, v, 4, extra_flags); +} + +static bool Items_ArrayGetter(void* data, int idx, const char** out_text) { const char** items = (const char**)data; if (out_text) @@ -4742,16 +6915,9 @@ static bool Combo_ArrayGetter(void* data, int idx, const char** out_text) return true; } -// Combo box helper allowing to pass an array of strings. -bool ImGui::Combo(const char* label, int* current_item, const char** items, int items_count, int popup_height_items) +static bool Items_SingleStringGetter(void* data, int idx, const char** out_text) { - const bool value_changed = Combo(label, current_item, Combo_ArrayGetter, (void*)items, items_count, popup_height_items); - return value_changed; -} - -static bool Combo_StringListGetter(void* data, int idx, const char** out_text) -{ - // FIXME-OPT: we could precompute the indices to fasten this. But only 1 active combo means the waste is limited. + // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited. const char* items_separated_by_zeros = (const char*)data; int items_count = 0; const char* p = items_separated_by_zeros; @@ -4769,57 +6935,65 @@ static bool Combo_StringListGetter(void* data, int idx, const char** out_text) return true; } +// Combo box helper allowing to pass an array of strings. +bool ImGui::Combo(const char* label, int* current_item, const char** items, int items_count, int height_in_items) +{ + const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items); + return value_changed; +} + // Combo box helper allowing to pass all items in a single string. -bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int popup_height_items) +bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items) { int items_count = 0; - const char* p = items_separated_by_zeros; + const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this while (*p) { p += strlen(p) + 1; items_count++; } - bool value_changed = Combo(label, current_item, Combo_StringListGetter, (void*)items_separated_by_zeros, items_count, popup_height_items); + bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items); return value_changed; } // Combo box function. -bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int popup_height_items) +bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); + const float w = ImGui::CalcItemWidth(); - const ImVec2 text_size = CalcTextSize(label); - const float arrow_size = (window->FontSize() + style.FramePadding.x * 2.0f); - const ImGuiAabb frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(window->DC.ItemWidth.back(), text_size.y) + style.FramePadding*2.0f); - const ImGuiAabb bb(frame_bb.Min, frame_bb.Max + ImVec2(style.ItemInnerSpacing.x + text_size.x,0)); - - if (ClipAdvance(frame_bb)) + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y) + style.FramePadding*2.0f); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(style.ItemInnerSpacing.x + label_size.x,0)); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, &id)) return false; - const bool hovered = (g.HoveredWindow == window) && (g.HoveredId == 0) && IsMouseHoveringBox(bb); + const float arrow_size = (g.FontSize + style.FramePadding.x * 2.0f); + const bool hovered = IsHovered(frame_bb, id); bool value_changed = false; - ItemSize(frame_bb); - RenderFrame(frame_bb.Min, frame_bb.Max, window->Color(ImGuiCol_FrameBg)); - RenderFrame(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y), frame_bb.Max, window->Color(hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button)); + const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f)); + RenderFrame(frame_bb.Min, frame_bb.Max, window->Color(ImGuiCol_FrameBg), true, style.FrameRounding); + RenderFrame(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y), frame_bb.Max, window->Color(hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button), true, style.FrameRounding); // FIXME-ROUNDING RenderCollapseTriangle(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y) + style.FramePadding, true); if (*current_item >= 0 && *current_item < items_count) { const char* item_text; if (items_getter(data, *current_item, &item_text)) - RenderText(frame_bb.Min + style.FramePadding, item_text, NULL, false); + RenderTextClipped(frame_bb.Min + style.FramePadding, item_text, NULL, NULL, value_bb.Max); } - ImGui::SameLine(0, (int)g.Style.ItemInnerSpacing.x); - ImGui::TextUnformatted(label, FindTextDisplayEnd(label)); - + if (label_size.x > 0) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + ImGui::PushID((int)id); bool menu_toggled = false; if (hovered) @@ -4829,65 +7003,53 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi { menu_toggled = true; g.ActiveComboID = (g.ActiveComboID == id) ? 0 : id; + if (g.ActiveComboID) + FocusWindow(window); } } if (g.ActiveComboID == id) { + // Size default to hold ~7 items + if (height_in_items < 0) + height_in_items = 7; + const ImVec2 backup_pos = ImGui::GetCursorPos(); - const float popup_off_x = 0.0f;//g.Style.ItemInnerSpacing.x; - const float popup_height = (text_size.y + g.Style.ItemSpacing.y) * ImMin(items_count, popup_height_items) + g.Style.WindowPadding.y; - const ImGuiAabb popup_aabb(ImVec2(frame_bb.Min.x+popup_off_x, frame_bb.Max.y), ImVec2(frame_bb.Max.x+popup_off_x, frame_bb.Max.y + popup_height)); - ImGui::SetCursorPos(popup_aabb.Min - window->Pos); + const float popup_off_x = 0.0f;//style.ItemInnerSpacing.x; + const float popup_height = (label_size.y + style.ItemSpacing.y) * ImMin(items_count, height_in_items) + (style.FramePadding.y * 3); + const ImRect popup_rect(ImVec2(frame_bb.Min.x+popup_off_x, frame_bb.Max.y), ImVec2(frame_bb.Max.x+popup_off_x, frame_bb.Max.y + popup_height)); + ImGui::SetCursorPos(popup_rect.Min - window->Pos); const ImGuiWindowFlags flags = ImGuiWindowFlags_ComboBox | ((window->Flags & ImGuiWindowFlags_ShowBorders) ? ImGuiWindowFlags_ShowBorders : 0); - ImGui::BeginChild("#ComboBox", popup_aabb.GetSize(), false, flags); - ImGuiWindow* child_window = GetCurrentWindow(); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding); + ImGui::BeginChild("#ComboBox", popup_rect.GetSize(), false, flags); ImGui::Spacing(); bool combo_item_active = false; - combo_item_active |= (g.ActiveId == child_window->GetID("#SCROLLY")); + combo_item_active |= (g.ActiveId == GetCurrentWindow()->GetID("#SCROLLY")); // Display items - for (int item_idx = 0; item_idx < items_count; item_idx++) + for (int i = 0; i < items_count; i++) { - const float item_h = child_window->FontSize(); - const float spacing_up = (float)(int)(g.Style.ItemSpacing.y/2); - const float spacing_dn = g.Style.ItemSpacing.y - spacing_up; - const ImGuiAabb item_aabb(ImVec2(popup_aabb.Min.x, child_window->DC.CursorPos.y - spacing_up), ImVec2(popup_aabb.Max.x, child_window->DC.CursorPos.y + item_h + spacing_dn)); - const ImGuiID item_id = child_window->GetID((void*)(intptr_t)item_idx); - - bool item_hovered, item_held; - bool item_pressed = ButtonBehaviour(item_aabb, item_id, &item_hovered, &item_held, true); - bool item_selected = (item_idx == *current_item); - - if (item_hovered || item_selected) - { - const ImU32 col = window->Color((item_held && item_hovered) ? ImGuiCol_HeaderActive : item_hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - RenderFrame(item_aabb.Min, item_aabb.Max, col, false); - } - + ImGui::PushID((void*)(intptr_t)i); + const bool item_selected = (i == *current_item); const char* item_text; - if (!items_getter(data, item_idx, &item_text)) + if (!items_getter(data, i, &item_text)) item_text = "*Unknown item*"; - ImGui::Text("%s", item_text); - - if (item_selected) + if (ImGui::Selectable(item_text, item_selected)) { - if (menu_toggled) - ImGui::SetScrollPosHere(); - } - if (item_pressed) - { - g.ActiveId = 0; + SetActiveId(0); g.ActiveComboID = 0; value_changed = true; - *current_item = item_idx; + *current_item = i; } - - combo_item_active |= (g.ActiveId == item_id); + if (item_selected && menu_toggled) + ImGui::SetScrollPosHere(); + combo_item_active |= ImGui::IsItemActive(); + ImGui::PopID(); } ImGui::EndChild(); + ImGui::PopStyleVar(); ImGui::SetCursorPos(backup_pos); if (!combo_item_active && g.ActiveId != 0) @@ -4899,25 +7061,214 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi return value_changed; } -// A little colored square. Return true when clicked. -bool ImGui::ColorButton(const ImVec4& col, bool small_height, bool outline_border) +// Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image. +// But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID. +bool ImGui::Selectable(const char* label, bool selected, const ImVec2& size_arg) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; const ImGuiStyle& style = g.Style; - const float square_size = window->FontSize(); - const ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(square_size + style.FramePadding.x*2, square_size + (small_height ? 0 : style.FramePadding.y*2))); + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + const float w = ImMax(label_size.x, window->Pos.x + ImGui::GetContentRegionMax().x - style.WindowPadding.x - window->DC.CursorPos.x); + const ImVec2 size(size_arg.x != 0.0f ? size_arg.x : w, size_arg.y != 0.0f ? size_arg.y : label_size.y); + ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); ItemSize(bb); + if (size_arg.x == 0.0f) + bb.Max.x += style.WindowPadding.x; - if (ClipAdvance(bb)) + // Selectables are meant to be tightly packed together. So for both rendering and collision we extend to compensate for spacing. + ImRect bb_with_spacing = bb; + const float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f); + const float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f); + const float spacing_R = style.ItemSpacing.x - spacing_L; + const float spacing_D = style.ItemSpacing.y - spacing_U; + bb_with_spacing.Min.x -= spacing_L; + bb_with_spacing.Min.y -= spacing_U; + bb_with_spacing.Max.x += spacing_R; + bb_with_spacing.Max.y += spacing_D; + if (!ItemAdd(bb_with_spacing, &id)) return false; - const bool hovered = (g.HoveredWindow == window) && (g.HoveredId == 0) && IsMouseHoveringBox(bb); - const bool pressed = hovered && g.IO.MouseClicked[0]; - RenderFrame(bb.Min, bb.Max, window->Color(col), outline_border); + bool hovered, held; + bool pressed = ButtonBehavior(bb_with_spacing, id, &hovered, &held, true); + + // Render + if (hovered || selected) + { + const ImU32 col = window->Color((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); + RenderFrame(bb_with_spacing.Min, bb_with_spacing.Max, col, false, style.FrameRounding); + } + + //const ImVec2 off = ImVec2(ImMax(0.0f, size.x - text_size.x) * 0.5f, ImMax(0.0f, size.y - text_size.y) * 0.5f); + RenderTextClipped(bb.Min, label, NULL, &label_size, bb_with_spacing.Max); + + return pressed; +} + +bool ImGui::Selectable(const char* label, bool* p_selected, const ImVec2& size_arg) +{ + if (ImGui::Selectable(label, *p_selected, size_arg)) + { + *p_selected = !*p_selected; + return true; + } + return false; +} + +// Helper to calculate the size of a listbox and display a label on the right. +// Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an empty label "##empty" +bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg) +{ + ImGuiWindow* window = GetCurrentWindow(); + + const ImGuiStyle& style = ImGui::GetStyle(); + const ImGuiID id = ImGui::GetID(label); + const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); + + // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. + ImVec2 size; + size.x = (size_arg.x != 0.0f) ? (size_arg.x) : ImGui::CalcItemWidth() + style.FramePadding.x * 2.0f; + size.y = (size_arg.y != 0.0f) ? (size_arg.y) : ImGui::GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y; + const ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y)); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); + const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + window->DC.LastItemRect = bb; + + ImGui::BeginGroup(); + if (label_size.x > 0) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + ImGui::BeginChildFrame(id, frame_bb.GetSize()); + return true; +} + +bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items) +{ + // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. + // However we don't add +0.40f if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size. + // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution. + if (height_in_items < 0) + height_in_items = ImMin(items_count, 7); + float height_in_items_f = height_in_items < items_count ? (height_in_items + 0.40f) : (height_in_items + 0.00f); + + // We include ItemSpacing.y so that a list sized for the exact number of items doesn't make a scrollbar appears. We could also enforce that by passing a flag to BeginChild(). + ImVec2 size; + size.x = 0.0f; + size.y = ImGui::GetTextLineHeightWithSpacing() * height_in_items_f + ImGui::GetStyle().ItemSpacing.y; + return ImGui::ListBoxHeader(label, size); +} + +void ImGui::ListBoxFooter() +{ + ImGuiWindow* parent_window = GetParentWindow(); + const ImRect bb = parent_window->DC.LastItemRect; + const ImGuiStyle& style = ImGui::GetStyle(); + + ImGui::EndChildFrame(); + + // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect) + // We call SameLine() to restore DC.CurrentLine* data + ImGui::SameLine(); + parent_window->DC.CursorPos = bb.Min; + ItemSize(bb, style.FramePadding.y); + ImGui::EndGroup(); +} + +bool ImGui::ListBox(const char* label, int* current_item, const char** items, int items_count, int height_items) +{ + const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items); + return value_changed; +} + +bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + if (!ImGui::ListBoxHeader(label, items_count, height_in_items)) + return false; + + bool value_changed = false; + for (int i = 0; i < items_count; i++) + { + const bool item_selected = (i == *current_item); + const char* item_text; + if (!items_getter(data, i, &item_text)) + item_text = "*Unknown item*"; + + ImGui::PushID(i); + if (ImGui::Selectable(item_text, item_selected)) + { + *current_item = i; + value_changed = true; + } + ImGui::PopID(); + } + + ImGui::ListBoxFooter(); + return value_changed; +} + +bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected) +{ + (void)shortcut; // FIXME-MENU: Shortcut are not supported yet. Argument is reserved. + + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImVec2 pos = ImGui::GetCursorScreenPos(); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const float symbol_spacing = (float)(int)(g.FontSize * 1.50f + 0.5f); + const float w = ImMax(label_size.x + symbol_spacing, window->Pos.x + ImGui::GetContentRegionMax().x - window->DC.CursorPos.x); // Feedback to next frame + bool ret = ImGui::Selectable(label, false, ImVec2(w, 0.0f)); + + if (selected) + { + pos.x = window->Pos.x + ImGui::GetContentRegionMax().x - g.FontSize; + RenderCheckMark(pos, window->Color(ImGuiCol_Text)); + } + + return ret; +} + +bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected) +{ + if (ImGui::MenuItem(label, shortcut, p_selected ? *p_selected : false)) + { + if (p_selected) + *p_selected = !*p_selected; + return true; + } + return false; +} + +// A little colored square. Return true when clicked. +bool ImGui::ColorButton(const ImVec4& col, bool small_height, bool outline_border) +{ + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID("#colorbutton"); + const float square_size = g.FontSize; + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(square_size + style.FramePadding.x*2, square_size + (small_height ? 0 : style.FramePadding.y*2))); + ItemSize(bb, small_height ? 0.0f : style.FramePadding.y); + if (!ItemAdd(bb, &id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, true); + RenderFrame(bb.Min, bb.Max, window->Color(col), outline_border, style.FrameRounding); if (hovered) { @@ -4949,67 +7300,62 @@ bool ImGui::ColorEdit3(const char* label, float col[3]) // Use CTRL-Click to input value and TAB to go to next item. bool ImGui::ColorEdit4(const char* label, float col[4], bool alpha) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); - const float w_full = window->DC.ItemWidth.back(); - const float square_sz = (window->FontSize() + style.FramePadding.x * 2.0f); + const float w_full = ImGui::CalcItemWidth(); + const float square_sz = (g.FontSize + style.FramePadding.x * 2.0f); ImGuiColorEditMode edit_mode = window->DC.ColorEditMode; - if (edit_mode == ImGuiColorEditMode_UserSelect) + if (edit_mode == ImGuiColorEditMode_UserSelect || edit_mode == ImGuiColorEditMode_UserSelectShowButton) edit_mode = g.ColorEditModeStorage.GetInt(id, 0) % 3; - float fx = col[0]; - float fy = col[1]; - float fz = col[2]; - float fw = col[3]; - const ImVec4 col_display(fx, fy, fz, 1.0f); + float f[4] = { col[0], col[1], col[2], col[3] }; if (edit_mode == ImGuiColorEditMode_HSV) - ImConvertColorRGBtoHSV(fx, fy, fz, fx, fy, fz); + ImGui::ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); - int ix = (int)(fx * 255.0f + 0.5f); - int iy = (int)(fy * 255.0f + 0.5f); - int iz = (int)(fz * 255.0f + 0.5f); - int iw = (int)(fw * 255.0f + 0.5f); + int i[4] = { (int)(f[0] * 255.0f + 0.5f), (int)(f[1] * 255.0f + 0.5f), (int)(f[2] * 255.0f + 0.5f), (int)(f[3] * 255.0f + 0.5f) }; int components = alpha ? 4 : 3; bool value_changed = false; + ImGui::BeginGroup(); ImGui::PushID(label); - bool hsv = (edit_mode == 1); + const bool hsv = (edit_mode == 1); switch (edit_mode) { case ImGuiColorEditMode_RGB: case ImGuiColorEditMode_HSV: { - // 0: RGB 0..255 - // 1: HSV 0.255 Sliders + // RGB/HSV 0..255 Sliders const float w_items_all = w_full - (square_sz + style.ItemInnerSpacing.x); - const float w_item_one = ImMax(1.0f, (float)(int)((w_items_all - (style.FramePadding.x*2.0f+style.ItemInnerSpacing.x) * (components-1)) / (float)components)); - const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one+style.FramePadding.x*2.0f+style.ItemInnerSpacing.x) * (components-1))); + const float w_item_one = ImMax(1.0f, (float)(int)((w_items_all - (style.FramePadding.x*2.0f + style.ItemInnerSpacing.x) * (components-1)) / (float)components)); + const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.FramePadding.x*2.0f + style.ItemInnerSpacing.x) * (components-1))); + + const bool hide_prefix = (w_item_one <= CalcTextSize("M:999").x); + const char* ids[4] = { "##X", "##Y", "##Z", "##W" }; + const char* fmt_table[3][4] = + { + { "%3.0f", "%3.0f", "%3.0f", "%3.0f" }, + { "R:%3.0f", "G:%3.0f", "B:%3.0f", "A:%3.0f" }, + { "H:%3.0f", "S:%3.0f", "V:%3.0f", "A:%3.0f" } + }; + const char** fmt = hide_prefix ? fmt_table[0] : hsv ? fmt_table[2] : fmt_table[1]; ImGui::PushItemWidth(w_item_one); - value_changed |= ImGui::SliderInt("##X", &ix, 0, 255, hsv ? "H:%3.0f" : "R:%3.0f"); - ImGui::SameLine(0, 0); - value_changed |= ImGui::SliderInt("##Y", &iy, 0, 255, hsv ? "S:%3.0f" : "G:%3.0f"); - ImGui::SameLine(0, 0); - if (alpha) + for (int n = 0; n < components; n++) { - value_changed |= ImGui::SliderInt("##Z", &iz, 0, 255, hsv ? "V:%3.0f" : "B:%3.0f"); - ImGui::SameLine(0, 0); - ImGui::PushItemWidth(w_item_last); - value_changed |= ImGui::SliderInt("##W", &iw, 0, 255, "A:%3.0f"); - } - else - { - ImGui::PushItemWidth(w_item_last); - value_changed |= ImGui::SliderInt("##Z", &iz, 0, 255, hsv ? "V:%3.0f" : "B:%3.0f"); + if (n > 0) + ImGui::SameLine(0, (int)style.ItemInnerSpacing.x); + if (n + 1 == components) + ImGui::PushItemWidth(w_item_last); + value_changed |= ImGui::DragInt(ids[n], &i[n], 1.0f, 0, 255, fmt[n]); } ImGui::PopItemWidth(); ImGui::PopItemWidth(); @@ -5017,65 +7363,68 @@ bool ImGui::ColorEdit4(const char* label, float col[4], bool alpha) break; case ImGuiColorEditMode_HEX: { - // 2: RGB Hexadecimal + // RGB Hexadecimal Input const float w_slider_all = w_full - square_sz; char buf[64]; if (alpha) - sprintf(buf, "#%02X%02X%02X%02X", ix, iy, iz, iw); + ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", i[0], i[1], i[2], i[3]); else - sprintf(buf, "#%02X%02X%02X", ix, iy, iz); - ImGui::PushItemWidth(w_slider_all - g.Style.ItemInnerSpacing.x); - value_changed |= ImGui::InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal); + ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", i[0], i[1], i[2]); + ImGui::PushItemWidth(w_slider_all - style.ItemInnerSpacing.x); + value_changed |= ImGui::InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); ImGui::PopItemWidth(); char* p = buf; - while (*p == '#' || *p == ' ' || *p == '\t') + while (*p == '#' || ImCharIsSpace(*p)) p++; // Treat at unsigned (%X is unsigned) - ix = iy = iz = iw = 0; + i[0] = i[1] = i[2] = i[3] = 0; if (alpha) - sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&ix, (unsigned int*)&iy, (unsigned int*)&iz, (unsigned int*)&iw); + sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); else - sscanf(p, "%02X%02X%02X", (unsigned int*)&ix, (unsigned int*)&iy, (unsigned int*)&iz); + sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); } break; } - ImGui::SameLine(0, 0); - ImGui::ColorButton(col_display); + ImGui::SameLine(0, (int)style.ItemInnerSpacing.x); - if (window->DC.ColorEditMode == ImGuiColorEditMode_UserSelect) + const ImVec4 col_display(col[0], col[1], col[2], 1.0f); + if (ImGui::ColorButton(col_display)) + g.ColorEditModeStorage.SetInt(id, (edit_mode + 1) % 3); // Don't set local copy of 'edit_mode' right away! + + if (window->DC.ColorEditMode == ImGuiColorEditMode_UserSelectShowButton) { ImGui::SameLine(0, (int)style.ItemInnerSpacing.x); const char* button_titles[3] = { "RGB", "HSV", "HEX" }; if (ImGui::Button(button_titles[edit_mode])) - { - // Don't set local copy of 'edit_mode' right away! - g.ColorEditModeStorage.SetInt(id, (edit_mode + 1) % 3); - } + g.ColorEditModeStorage.SetInt(id, (edit_mode + 1) % 3); // Don't set local copy of 'edit_mode' right away! + ImGui::SameLine(); + } + else + { + ImGui::SameLine(0, (int)style.ItemInnerSpacing.x); } - ImGui::SameLine(); ImGui::TextUnformatted(label, FindTextDisplayEnd(label)); // Convert back - fx = ix / 255.0f; - fy = iy / 255.0f; - fz = iz / 255.0f; - fw = iw / 255.0f; + for (int n = 0; n < 4; n++) + f[n] = i[n] / 255.0f; if (edit_mode == 1) - ImConvertColorHSVtoRGB(fx, fy, fz, fx, fy, fz); + ImGui::ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); if (value_changed) { - col[0] = fx; - col[1] = fy; - col[2] = fz; + col[0] = f[0]; + col[1] = f[1]; + col[2] = f[2]; if (alpha) - col[3] = fw; + col[3] = f[3]; } ImGui::PopID(); + ImGui::EndGroup(); return value_changed; } @@ -5096,10 +7445,14 @@ void ImGui::Separator() if (window->DC.ColumnsCount > 1) PopClipRect(); - const ImGuiAabb bb(ImVec2(window->Pos.x, window->DC.CursorPos.y), ImVec2(window->Pos.x + window->Size.x, window->DC.CursorPos.y)); - ItemSize(ImVec2(0.0f, bb.GetSize().y)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit + float x1 = window->Pos.x; + float x2 = window->Pos.x + window->Size.x; + if (!window->DC.GroupStack.empty()) + x1 += window->DC.ColumnsStartX; - if (ClipAdvance(bb)) + const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y)); + ItemSize(ImVec2(0.0f, bb.GetSize().y)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit + if (!ItemAdd(bb, NULL)) { if (window->DC.ColumnsCount > 1) PushColumnClipRect(); @@ -5108,8 +7461,15 @@ void ImGui::Separator() window->DrawList->AddLine(bb.Min, bb.Max, window->Color(ImGuiCol_Border)); + ImGuiState& g = *GImGui; + if (g.LogEnabled) + ImGui::LogText(STR_NEWLINE "--------------------------------"); + if (window->DC.ColumnsCount > 1) + { PushColumnClipRect(); + window->DC.ColumnsCellMinY = window->DC.CursorPos.y; + } } // A little vertical spacing. @@ -5123,35 +7483,165 @@ void ImGui::Spacing() } // Advance cursor given item size. -static void ItemSize(ImVec2 size, ImVec2* adjust_start_offset) +static void ItemSize(ImVec2 size, float text_offset_y) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; - const float line_height = ImMax(window->DC.CurrentLineHeight, size.y); - if (adjust_start_offset) - adjust_start_offset->y = adjust_start_offset->y + (line_height - size.y) * 0.5f; - // Always align ourselves on pixel boundaries + const float line_height = ImMax(window->DC.CurrentLineHeight, size.y); + const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y); window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y); window->DC.CursorPos = ImVec2((float)(int)(window->Pos.x + window->DC.ColumnsStartX + window->DC.ColumnsOffsetX), (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y)); + window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x); + window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y); - window->SizeContentsFit = ImMax(window->SizeContentsFit, ImVec2(window->DC.CursorPosPrevLine.x, window->DC.CursorPos.y) - window->Pos + ImVec2(0.0f, window->ScrollY)); + //window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, 0xFF0000FF, 4); // Debug window->DC.PrevLineHeight = line_height; - window->DC.CurrentLineHeight = 0.0f; + window->DC.PrevLineTextBaseOffset = text_base_offset; + window->DC.CurrentLineHeight = window->DC.CurrentLineTextBaseOffset = 0.0f; } -static void ItemSize(const ImGuiAabb& aabb, ImVec2* adjust_start_offset) +static inline void ItemSize(const ImRect& bb, float text_offset_y) { - ItemSize(aabb.GetSize(), adjust_start_offset); + ItemSize(bb.GetSize(), text_offset_y); +} + +static bool IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when_logged) +{ + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + if (!bb.Overlaps(ImRect(window->ClipRectStack.back()))) + { + if (!id || *id != GImGui->ActiveId) + if (clip_even_when_logged || !g.LogEnabled) + return true; + } + return false; +} + +bool ImGui::IsRectClipped(const ImVec2& size) +{ + ImGuiWindow* window = GetCurrentWindow(); + return IsClippedEx(ImRect(window->DC.CursorPos, window->DC.CursorPos + size), NULL, true); +} + +static bool ItemAdd(const ImRect& bb, const ImGuiID* id) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.LastItemID = id ? *id : 0; + window->DC.LastItemRect = bb; + if (IsClippedEx(bb, id, false)) + { + window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = false; + return false; + } + + // This is a sensible default, but widgets are free to override it after calling ItemAdd() + ImGuiState& g = *GImGui; + if (IsMouseHoveringRect(bb)) + { + // Matching the behavior of IsHovered() but ignore if ActiveId==window->MoveID (we clicked on the window background) + // So that clicking on items with no active id such as Text() still returns true with IsItemHovered() + window->DC.LastItemHoveredRect = true; + window->DC.LastItemHoveredAndUsable = false; + if (g.HoveredRootWindow == window->RootWindow) + if (g.ActiveId == 0 || (id && g.ActiveId == *id) || g.ActiveIdIsFocusedOnly || (g.ActiveId == window->MoveID)) + if (IsWindowContentHoverable(window)) + window->DC.LastItemHoveredAndUsable = true; + } + else + { + window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = false; + } + + return true; +} + +void ImGui::BeginGroup() +{ + ImGuiWindow* window = GetCurrentWindow(); + + window->DC.GroupStack.resize(window->DC.GroupStack.size() + 1); + ImGuiGroupData& group_data = window->DC.GroupStack.back(); + group_data.BackupCursorPos = window->DC.CursorPos; + group_data.BackupCursorMaxPos = window->DC.CursorMaxPos; + group_data.BackupColumnsStartX = window->DC.ColumnsStartX; + group_data.BackupCurrentLineHeight = window->DC.CurrentLineHeight; + group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset; + group_data.BackupLogLinePosY = window->DC.LogLinePosY; + + window->DC.ColumnsStartX = window->DC.CursorPos.x - window->Pos.x; + window->DC.CursorMaxPos = window->DC.CursorPos; + window->DC.CurrentLineHeight = 0.0f; + window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; +} + +void ImGui::EndGroup() +{ + ImGuiWindow* window = GetCurrentWindow(); + ImGuiStyle& style = ImGui::GetStyle(); + + IM_ASSERT(!window->DC.GroupStack.empty()); + + ImGuiGroupData& group_data = window->DC.GroupStack.back(); + + ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos); + group_bb.Max.y -= style.ItemSpacing.y; // Cancel out last vertical spacing because we are adding one ourselves. + group_bb.Max = ImMax(group_bb.Min, group_bb.Max); + + window->DC.CursorPos = group_data.BackupCursorPos; + window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos); + window->DC.CurrentLineHeight = group_data.BackupCurrentLineHeight; + window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset; // FIXME: Ideally we'll grab the base offset from the first line of the group. + window->DC.ColumnsStartX = group_data.BackupColumnsStartX; + window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; + + ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset); + ItemAdd(group_bb, NULL); + + window->DC.GroupStack.pop_back(); + + //window->DrawList->AddRect(group_bb.Min, group_bb.Max, 0xFFFF00FF); // Debug +} + +// Gets back to previous line and continue with horizontal layout +// column_x == 0 : follow on previous item +// columm_x != 0 : align to specified column +// spacing_w < 0 : use default spacing if column_x==0, no spacing if column_x!=0 +// spacing_w >= 0 : enforce spacing +void ImGui::SameLine(int column_x, int spacing_w) +{ + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + float x, y; + if (column_x != 0) + { + if (spacing_w < 0) spacing_w = 0; + x = window->Pos.x + (float)column_x + (float)spacing_w; + y = window->DC.CursorPosPrevLine.y; + } + else + { + if (spacing_w < 0) spacing_w = (int)g.Style.ItemSpacing.x; + x = window->DC.CursorPosPrevLine.x + (float)spacing_w; + y = window->DC.CursorPosPrevLine.y; + } + window->DC.CurrentLineHeight = window->DC.PrevLineHeight; + window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset; + window->DC.CursorPos = ImVec2(x, y); } void ImGui::NextColumn() { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; @@ -5175,97 +7665,80 @@ void ImGui::NextColumn() window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.ColumnsStartX + window->DC.ColumnsOffsetX); window->DC.CursorPos.y = window->DC.ColumnsCellMinY; window->DC.CurrentLineHeight = 0.0f; + window->DC.CurrentLineTextBaseOffset = 0.0f; PushColumnClipRect(); - ImGui::PushItemWidth(ImGui::GetColumnWidth() * 0.65f); + ImGui::PushItemWidth(ImGui::GetColumnWidth() * 0.65f); // FIXME } } -static bool IsClipped(const ImGuiAabb& bb) +int ImGui::GetColumnIndex() { - ImGuiState& g = GImGui; ImGuiWindow* window = GetCurrentWindow(); - - if (!bb.Overlaps(ImGuiAabb(window->ClipRectStack.back())) && !g.LogEnabled) - return true; - return false; + return window->DC.ColumnsCurrent; } -bool ImGui::IsClipped(const ImVec2& item_size) +int ImGui::GetColumnsCount() { ImGuiWindow* window = GetCurrentWindow(); - return IsClipped(ImGuiAabb(window->DC.CursorPos, window->DC.CursorPos + item_size)); + return window->DC.ColumnsCount; } -static bool ClipAdvance(const ImGuiAabb& bb) +static float GetDraggedColumnOffset(int column_index) { + // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing + // window creates a feedback loop because we store normalized positions/ So while dragging we enforce absolute positioning + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); - window->DC.LastItemAabb = bb; - window->DC.LastItemFocused = false; - if (IsClipped(bb)) - { - window->DC.LastItemHovered = false; - return true; - } - window->DC.LastItemHovered = IsMouseHoveringBox(bb); // this is a sensible default but widgets are free to override it after calling ClipAdvance - return false; -} + IM_ASSERT(column_index > 0); // We cannot drag column 0. If you get this assert you may have a conflict between the ID of your columns and another widgets. + IM_ASSERT(g.ActiveId == window->DC.ColumnsSetID + ImGuiID(column_index)); -// Gets back to previous line and continue with horizontal layout -// column_x == 0 : follow on previous item -// columm_x != 0 : align to specified column -// spacing_w < 0 : use default spacing if column_x==0, no spacing if column_x!=0 -// spacing_w >= 0 : enforce spacing -void ImGui::SameLine(int column_x, int spacing_w) -{ - ImGuiState& g = GImGui; - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - float x, y; - if (column_x != 0) - { - if (spacing_w < 0) spacing_w = 0; - x = window->Pos.x + (float)column_x + (float)spacing_w; - y = window->DC.CursorPosPrevLine.y; - } - else - { - if (spacing_w < 0) spacing_w = (int)g.Style.ItemSpacing.x; - x = window->DC.CursorPosPrevLine.x + (float)spacing_w; - y = window->DC.CursorPosPrevLine.y; - } - window->DC.CurrentLineHeight = window->DC.PrevLineHeight; - window->DC.CursorPos = ImVec2(x, y); + float x = g.IO.MousePos.x + g.ActiveClickDeltaToCenter.x; + x -= window->Pos.x; + x = ImClamp(x, ImGui::GetColumnOffset(column_index-1)+g.Style.ColumnsMinSpacing, ImGui::GetColumnOffset(column_index+1)-g.Style.ColumnsMinSpacing); + + return x; } float ImGui::GetColumnOffset(int column_index) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (column_index < 0) column_index = window->DC.ColumnsCurrent; - const ImGuiID column_id = window->DC.ColumnsSetID + ImGuiID(column_index); - RegisterAliveId(column_id); - const float default_t = column_index / (float)window->DC.ColumnsCount; - const float t = (float)window->StateStorage.GetInt(column_id, (int)(default_t * 8192)) / 8192; // Cheaply store our floating point value inside the integer (could store an union into the map?) + if (g.ActiveId) + { + const ImGuiID column_id = window->DC.ColumnsSetID + ImGuiID(column_index); + if (g.ActiveId == column_id) + return GetDraggedColumnOffset(column_index); + } - const float offset = window->DC.ColumnsStartX + t * (window->Size.x - g.Style.ScrollBarWidth - window->DC.ColumnsStartX); + // Read from cache + IM_ASSERT(column_index < (int)window->DC.ColumnsOffsetsT.size()); + const float t = window->DC.ColumnsOffsetsT[column_index]; + + const float min_x = window->DC.ColumnsStartX; + const float max_x = window->Size.x - (g.Style.ScrollbarWidth);// - window->WindowPadding().x; + const float offset = min_x + t * (max_x - min_x); return offset; } void ImGui::SetColumnOffset(int column_index, float offset) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (column_index < 0) column_index = window->DC.ColumnsCurrent; + IM_ASSERT(column_index < (int)window->DC.ColumnsOffsetsT.size()); const ImGuiID column_id = window->DC.ColumnsSetID + ImGuiID(column_index); - const float t = (offset - window->DC.ColumnsStartX) / (window->Size.x - g.Style.ScrollBarWidth - window->DC.ColumnsStartX); - window->StateStorage.SetInt(column_id, (int)(t*8192)); + + const float min_x = window->DC.ColumnsStartX; + const float max_x = window->Size.x - (g.Style.ScrollbarWidth);// - window->WindowPadding().x; + const float t = (offset - min_x) / (max_x - min_x); + window->DC.StateStorage->SetFloat(column_id, t); + window->DC.ColumnsOffsetsT[column_index] = t; } float ImGui::GetColumnWidth(int column_index) @@ -5291,10 +7764,8 @@ static void PushColumnClipRect(int column_index) void ImGui::Columns(int columns_count, const char* id, bool border) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; if (window->DC.ColumnsCount != 1) { @@ -5308,7 +7779,7 @@ void ImGui::Columns(int columns_count, const char* id, bool border) } // Draw columns borders and handle resize at the time of "closing" a columns set - if (window->DC.ColumnsCount != columns_count && window->DC.ColumnsCount != 1 && window->DC.ColumnsShowBorders) + if (window->DC.ColumnsCount != columns_count && window->DC.ColumnsCount != 1 && window->DC.ColumnsShowBorders && !window->SkipItems) { const float y1 = window->DC.ColumnsStartPos.y; const float y2 = window->DC.CursorPos.y; @@ -5317,13 +7788,15 @@ void ImGui::Columns(int columns_count, const char* id, bool border) float x = window->Pos.x + GetColumnOffset(i); const ImGuiID column_id = window->DC.ColumnsSetID + ImGuiID(i); - const ImGuiAabb column_aabb(ImVec2(x-4,y1),ImVec2(x+4,y2)); + const ImRect column_rect(ImVec2(x-4,y1),ImVec2(x+4,y2)); - if (IsClipped(column_aabb)) + if (IsClippedEx(column_rect, &column_id, false)) continue; bool hovered, held; - ButtonBehaviour(column_aabb, column_id, &hovered, &held, true); + ButtonBehavior(column_rect, column_id, &hovered, &held, true); + if (hovered || held) + g.MouseCursor = ImGuiMouseCursor_ResizeEW; // Draw before resize so our items positioning are in sync with the line being drawn const ImU32 col = window->Color(held ? ImGuiCol_ColumnActive : hovered ? ImGuiCol_ColumnHovered : ImGuiCol_Column); @@ -5332,16 +7805,19 @@ void ImGui::Columns(int columns_count, const char* id, bool border) if (held) { - x -= window->Pos.x; - x = ImClamp(x + g.IO.MouseDelta.x, ImGui::GetColumnOffset(i-1)+g.Style.ColumnsMinSpacing, ImGui::GetColumnOffset(i+1)-g.Style.ColumnsMinSpacing); + if (g.ActiveIdIsJustActivated) + g.ActiveClickDeltaToCenter.x = x - g.IO.MousePos.x; + + x = GetDraggedColumnOffset(i); SetColumnOffset(i, x); - x += window->Pos.x; } } } // Set state for first column + ImGui::PushID(0x11223344); // Differentiate column ID with an arbitrary/random prefix for cases where users name their columns set the same as another non-scope widget window->DC.ColumnsSetID = window->GetID(id ? id : ""); + ImGui::PopID(); window->DC.ColumnsCurrent = 0; window->DC.ColumnsCount = columns_count; window->DC.ColumnsShowBorders = border; @@ -5352,37 +7828,64 @@ void ImGui::Columns(int columns_count, const char* id, bool border) if (window->DC.ColumnsCount != 1) { + // Cache column offsets + window->DC.ColumnsOffsetsT.resize((size_t)columns_count + 1); + for (int column_index = 0; column_index < columns_count + 1; column_index++) + { + const ImGuiID column_id = window->DC.ColumnsSetID + ImGuiID(column_index); + RegisterAliveId(column_id); + const float default_t = column_index / (float)window->DC.ColumnsCount; + const float t = window->DC.StateStorage->GetFloat(column_id, default_t); // Cheaply store our floating point value inside the integer (could store an union into the map?) + window->DC.ColumnsOffsetsT[column_index] = t; + } + PushColumnClipRect(); ImGui::PushItemWidth(ImGui::GetColumnWidth() * 0.65f); } + else + { + window->DC.ColumnsOffsetsT.resize(2); + window->DC.ColumnsOffsetsT[0] = 0.0f; + window->DC.ColumnsOffsetsT[1] = 1.0f; + } +} + +void ImGui::Indent() +{ + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + window->DC.ColumnsStartX += g.Style.IndentSpacing; + window->DC.CursorPos.x = window->Pos.x + window->DC.ColumnsStartX + window->DC.ColumnsOffsetX; +} + +void ImGui::Unindent() +{ + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + window->DC.ColumnsStartX -= g.Style.IndentSpacing; + window->DC.CursorPos.x = window->Pos.x + window->DC.ColumnsStartX + window->DC.ColumnsOffsetX; } void ImGui::TreePush(const char* str_id) { - ImGuiState& g = GImGui; ImGuiWindow* window = GetCurrentWindow(); - window->DC.ColumnsStartX += g.Style.TreeNodeSpacing; - window->DC.CursorPos.x = window->Pos.x + window->DC.ColumnsStartX + window->DC.ColumnsOffsetX; + ImGui::Indent(); window->DC.TreeDepth++; PushID(str_id ? str_id : "#TreePush"); } void ImGui::TreePush(const void* ptr_id) { - ImGuiState& g = GImGui; ImGuiWindow* window = GetCurrentWindow(); - window->DC.ColumnsStartX += g.Style.TreeNodeSpacing; - window->DC.CursorPos.x = window->Pos.x + window->DC.ColumnsStartX + window->DC.ColumnsOffsetX; + ImGui::Indent(); window->DC.TreeDepth++; PushID(ptr_id ? ptr_id : (const void*)"#TreePush"); } void ImGui::TreePop() { - ImGuiState& g = GImGui; ImGuiWindow* window = GetCurrentWindow(); - window->DC.ColumnsStartX -= g.Style.TreeNodeSpacing; - window->DC.CursorPos.x = window->Pos.x + window->DC.ColumnsStartX + window->DC.ColumnsOffsetX; + ImGui::Unindent(); window->DC.TreeDepth--; PopID(); } @@ -5407,7 +7910,7 @@ void ImGui::Value(const char* prefix, float v, const char* float_format) if (float_format) { char fmt[64]; - sprintf(fmt, "%%s: %s", float_format); + ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format); ImGui::Text(fmt, prefix, v); } else @@ -5440,126 +7943,244 @@ void ImGui::Color(const char* prefix, unsigned int v) // ImDrawList //----------------------------------------------------------------------------- +static ImVec4 GNullClipRect(-9999.0f,-9999.0f, +9999.0f, +9999.0f); + void ImDrawList::Clear() { commands.resize(0); vtx_buffer.resize(0); vtx_write = NULL; clip_rect_stack.resize(0); + texture_id_stack.resize(0); } -void ImDrawList::PushClipRect(const ImVec4& clip_rect) +void ImDrawList::ClearFreeMemory() { - if (!commands.empty() && commands.back().vtx_count == 0) + commands.clear(); + vtx_buffer.clear(); + vtx_write = NULL; + clip_rect_stack.clear(); + texture_id_stack.clear(); +} + +void ImDrawList::AddDrawCmd() +{ + ImDrawCmd draw_cmd; + draw_cmd.vtx_count = 0; + draw_cmd.clip_rect = clip_rect_stack.empty() ? GNullClipRect : clip_rect_stack.back(); + draw_cmd.texture_id = texture_id_stack.empty() ? NULL : texture_id_stack.back(); + draw_cmd.user_callback = NULL; + draw_cmd.user_callback_data = NULL; + + IM_ASSERT(draw_cmd.clip_rect.x <= draw_cmd.clip_rect.z && draw_cmd.clip_rect.y <= draw_cmd.clip_rect.w); + commands.push_back(draw_cmd); +} + +void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) +{ + ImDrawCmd* current_cmd = commands.empty() ? NULL : &commands.back(); + if (!current_cmd || current_cmd->vtx_count != 0 || current_cmd->user_callback != NULL) { - // Reuse empty command because high-level clipping may have discarded the other vertices already - commands.back().clip_rect = clip_rect; + AddDrawCmd(); + current_cmd = &commands.back(); + } + current_cmd->user_callback = callback; + current_cmd->user_callback_data = callback_data; + + // Force a new command after us + // We function this way so that the most common calls (AddLine, AddRect..) always have a command to add to without doing any check. + AddDrawCmd(); +} + +void ImDrawList::UpdateClipRect() +{ + ImDrawCmd* current_cmd = commands.empty() ? NULL : &commands.back(); + if (!current_cmd || (current_cmd->vtx_count != 0) || current_cmd->user_callback != NULL) + { + AddDrawCmd(); } else { - ImDrawCmd draw_cmd; - draw_cmd.vtx_count = 0; - draw_cmd.clip_rect = clip_rect; - commands.push_back(draw_cmd); + ImVec4 current_clip_rect = clip_rect_stack.empty() ? GNullClipRect : clip_rect_stack.back(); + if (commands.size() > 2 && ImLengthSqr(commands[commands.size()-2].clip_rect - current_clip_rect) < 0.00001f) + commands.pop_back(); + else + current_cmd->clip_rect = current_clip_rect; } +} + +// Scissoring. The values in clip_rect are x1, y1, x2, y2. +void ImDrawList::PushClipRect(const ImVec4& clip_rect) +{ clip_rect_stack.push_back(clip_rect); + UpdateClipRect(); +} + +void ImDrawList::PushClipRectFullScreen() +{ + PushClipRect(GNullClipRect); + + // This would be more correct but we're not supposed to access ImGuiState from here? + //ImGuiState& g = *GImGui; + //PushClipRect(GetVisibleRect()); } void ImDrawList::PopClipRect() { + IM_ASSERT(clip_rect_stack.size() > 0); clip_rect_stack.pop_back(); - const ImVec4 clip_rect = clip_rect_stack.empty() ? ImVec4(-9999.0f,-9999.0f, +9999.0f, +9999.0f) : clip_rect_stack.back(); - if (!commands.empty() && commands.back().vtx_count == 0) + UpdateClipRect(); +} + +void ImDrawList::UpdateTextureID() +{ + ImDrawCmd* current_cmd = commands.empty() ? NULL : &commands.back(); + const ImTextureID texture_id = texture_id_stack.empty() ? NULL : texture_id_stack.back(); + if (!current_cmd || (current_cmd->vtx_count != 0 && current_cmd->texture_id != texture_id) || current_cmd->user_callback != NULL) { - // Reuse empty command because high-level clipping may have discarded the other vertices already - commands.back().clip_rect = clip_rect; + AddDrawCmd(); } else { - ImDrawCmd draw_cmd; - draw_cmd.vtx_count = 0; - draw_cmd.clip_rect = clip_rect; - commands.push_back(draw_cmd); + current_cmd->texture_id = texture_id; } } -void ImDrawList::ReserveVertices(unsigned int vtx_count) +void ImDrawList::PushTextureID(const ImTextureID& texture_id) { - if (vtx_count > 0) - { - ImDrawCmd& draw_cmd = commands.back(); - draw_cmd.vtx_count += vtx_count; - vtx_buffer.resize(vtx_buffer.size() + vtx_count); - vtx_write = &vtx_buffer[vtx_buffer.size() - vtx_count]; - } + texture_id_stack.push_back(texture_id); + UpdateTextureID(); } -void ImDrawList::AddVtx(const ImVec2& pos, ImU32 col) +void ImDrawList::PopTextureID() { - vtx_write->pos = pos; - vtx_write->col = col; - vtx_write->uv = GImGui.FontTexUvForWhite; - vtx_write++; + IM_ASSERT(texture_id_stack.size() > 0); + texture_id_stack.pop_back(); + UpdateTextureID(); } -void ImDrawList::AddVtxLine(const ImVec2& a, const ImVec2& b, ImU32 col) +void ImDrawList::PrimReserve(unsigned int vtx_count) { - const float offset = GImGui.IO.PixelCenterOffset; - const ImVec2 hn = (b - a) * (0.50f / ImLength(b - a)); // half normal - const ImVec2 hp0 = ImVec2(offset + hn.y, offset - hn.x); // half perpendiculars + user offset - const ImVec2 hp1 = ImVec2(offset - hn.y, offset + hn.x); + ImDrawCmd& draw_cmd = commands.back(); + draw_cmd.vtx_count += vtx_count; - // Two triangles makes up one line. Using triangles allows us to reduce amount of draw calls. - AddVtx(a + hp0, col); - AddVtx(b + hp0, col); - AddVtx(a + hp1, col); - AddVtx(b + hp0, col); - AddVtx(b + hp1, col); - AddVtx(a + hp1, col); + size_t vtx_buffer_size = vtx_buffer.size(); + vtx_buffer.resize(vtx_buffer_size + vtx_count); + vtx_write = &vtx_buffer[vtx_buffer_size]; } -void ImDrawList::AddLine(const ImVec2& a, const ImVec2& b, ImU32 col) +void ImDrawList::PrimTriangle(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col) +{ + const ImVec2 uv = GImGui->FontTexUvWhitePixel; + vtx_write[0].pos = a; vtx_write[0].uv = uv; vtx_write[0].col = col; + vtx_write[1].pos = b; vtx_write[1].uv = uv; vtx_write[1].col = col; + vtx_write[2].pos = c; vtx_write[2].uv = uv; vtx_write[2].col = col; + vtx_write += 3; +} + +void ImDrawList::PrimRect(const ImVec2& a, const ImVec2& c, ImU32 col) +{ + const ImVec2 uv = GImGui->FontTexUvWhitePixel; + const ImVec2 b(c.x, a.y); + const ImVec2 d(a.x, c.y); + vtx_write[0].pos = a; vtx_write[0].uv = uv; vtx_write[0].col = col; + vtx_write[1].pos = b; vtx_write[1].uv = uv; vtx_write[1].col = col; + vtx_write[2].pos = c; vtx_write[2].uv = uv; vtx_write[2].col = col; + vtx_write[3].pos = a; vtx_write[3].uv = uv; vtx_write[3].col = col; + vtx_write[4].pos = c; vtx_write[4].uv = uv; vtx_write[4].col = col; + vtx_write[5].pos = d; vtx_write[5].uv = uv; vtx_write[5].col = col; + vtx_write += 6; +} + +void ImDrawList::PrimRectUV(const ImVec2& a, const ImVec2& c, const ImVec2& uv_a, const ImVec2& uv_c, ImU32 col) +{ + const ImVec2 b(c.x, a.y); + const ImVec2 d(a.x, c.y); + const ImVec2 uv_b(uv_c.x, uv_a.y); + const ImVec2 uv_d(uv_a.x, uv_c.y); + vtx_write[0].pos = a; vtx_write[0].uv = uv_a; vtx_write[0].col = col; + vtx_write[1].pos = b; vtx_write[1].uv = uv_b; vtx_write[1].col = col; + vtx_write[2].pos = c; vtx_write[2].uv = uv_c; vtx_write[2].col = col; + vtx_write[3].pos = a; vtx_write[3].uv = uv_a; vtx_write[3].col = col; + vtx_write[4].pos = c; vtx_write[4].uv = uv_c; vtx_write[4].col = col; + vtx_write[5].pos = d; vtx_write[5].uv = uv_d; vtx_write[5].col = col; + vtx_write += 6; +} + +void ImDrawList::PrimQuad(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col) +{ + const ImVec2 uv = GImGui->FontTexUvWhitePixel; + vtx_write[0].pos = a; vtx_write[0].uv = uv; vtx_write[0].col = col; + vtx_write[1].pos = b; vtx_write[1].uv = uv; vtx_write[1].col = col; + vtx_write[2].pos = c; vtx_write[2].uv = uv; vtx_write[2].col = col; + vtx_write[3].pos = a; vtx_write[3].uv = uv; vtx_write[3].col = col; + vtx_write[4].pos = c; vtx_write[4].uv = uv; vtx_write[4].col = col; + vtx_write[5].pos = d; vtx_write[5].uv = uv; vtx_write[5].col = col; + vtx_write += 6; +} + +// FIXME-OPT: In many instances the caller could provide a normal. +void ImDrawList::PrimLine(const ImVec2& a, const ImVec2& b, ImU32 col, float thickness) +{ + const float inv_length = 1.0f / sqrtf(ImLengthSqr(b - a)); + const float dx = (b.x - a.x) * (thickness * 0.5f * inv_length); // line direction, halved + const float dy = (b.y - a.y) * (thickness * 0.5f * inv_length); // line direction, halved + + const ImVec2 pa(a.x + dy, a.y - dx); + const ImVec2 pb(b.x + dy, b.y - dx); + const ImVec2 pc(b.x - dy, b.y + dx); + const ImVec2 pd(a.x - dy, a.y + dx); + PrimQuad(pa, pb, pc, pd, col); +} + +void ImDrawList::AddLine(const ImVec2& a, const ImVec2& b, ImU32 col, float thickness) { if ((col >> 24) == 0) return; - ReserveVertices(6); - AddVtxLine(a, b, col); + PrimReserve(6); + PrimLine(a, b, col, thickness); } -void ImDrawList::AddArc(const ImVec2& center, float rad, ImU32 col, int a_min, int a_max, bool tris, const ImVec2& third_point_offset) +void ImDrawList::AddArcFast(const ImVec2& center, float radius, ImU32 col, int a_min, int a_max, bool filled, const ImVec2& third_point_offset) { if ((col >> 24) == 0) return; - static ImVec2 circle_vtx[12]; + const int SAMPLES = 12; + static ImVec2 circle_vtx[SAMPLES]; static bool circle_vtx_builds = false; if (!circle_vtx_builds) { - for (int i = 0; i < IM_ARRAYSIZE(circle_vtx); i++) + for (int i = 0; i < SAMPLES; i++) { - const float a = ((float)i / (float)IM_ARRAYSIZE(circle_vtx)) * 2*PI; + const float a = ((float)i / (float)SAMPLES) * 2*PI; circle_vtx[i].x = cosf(a + PI); circle_vtx[i].y = sinf(a + PI); } circle_vtx_builds = true; } - if (tris) + const ImVec2 uv = GImGui->FontTexUvWhitePixel; + if (filled) { - ReserveVertices((unsigned int)(a_max-a_min) * 3); - for (int a = a_min; a < a_max; a++) + PrimReserve((unsigned int)(a_max-a_min) * 3); + for (int a0 = a_min; a0 < a_max; a0++) { - AddVtx(center + circle_vtx[a % IM_ARRAYSIZE(circle_vtx)] * rad, col); - AddVtx(center + circle_vtx[(a+1) % IM_ARRAYSIZE(circle_vtx)] * rad, col); - AddVtx(center + third_point_offset, col); + int a1 = (a0 + 1 == SAMPLES) ? 0 : a0 + 1; + PrimVtx(center + circle_vtx[a0] * radius, uv, col); + PrimVtx(center + circle_vtx[a1] * radius, uv, col); + PrimVtx(center + third_point_offset, uv, col); } } else { - ReserveVertices((unsigned int)(a_max-a_min) * 6); - for (int a = a_min; a < a_max; a++) - AddVtxLine(center + circle_vtx[a % IM_ARRAYSIZE(circle_vtx)] * rad, center + circle_vtx[(a+1) % IM_ARRAYSIZE(circle_vtx)] * rad, col); + PrimReserve((unsigned int)(a_max-a_min) * 6); + for (int a0 = a_min; a0 < a_max; a0++) + { + int a1 = (a0 + 1 == SAMPLES) ? 0 : a0 + 1; + PrimLine(center + circle_vtx[a0] * radius, center + circle_vtx[a1] * radius, col); + } } } @@ -5574,24 +8195,24 @@ void ImDrawList::AddRect(const ImVec2& a, const ImVec2& b, ImU32 col, float roun if (r == 0.0f || rounding_corners == 0) { - ReserveVertices(4*6); - AddVtxLine(ImVec2(a.x,a.y), ImVec2(b.x,a.y), col); - AddVtxLine(ImVec2(b.x,a.y), ImVec2(b.x,b.y), col); - AddVtxLine(ImVec2(b.x,b.y), ImVec2(a.x,b.y), col); - AddVtxLine(ImVec2(a.x,b.y), ImVec2(a.x,a.y), col); + PrimReserve(4*6); + PrimLine(ImVec2(a.x,a.y), ImVec2(b.x,a.y), col); + PrimLine(ImVec2(b.x,a.y), ImVec2(b.x,b.y), col); + PrimLine(ImVec2(b.x,b.y), ImVec2(a.x,b.y), col); + PrimLine(ImVec2(a.x,b.y), ImVec2(a.x,a.y), col); } else { - ReserveVertices(4*6); - AddVtxLine(ImVec2(a.x + ((rounding_corners & 1)?r:0), a.y), ImVec2(b.x - ((rounding_corners & 2)?r:0), a.y), col); - AddVtxLine(ImVec2(b.x, a.y + ((rounding_corners & 2)?r:0)), ImVec2(b.x, b.y - ((rounding_corners & 4)?r:0)), col); - AddVtxLine(ImVec2(b.x - ((rounding_corners & 4)?r:0), b.y), ImVec2(a.x + ((rounding_corners & 8)?r:0), b.y), col); - AddVtxLine(ImVec2(a.x, b.y - ((rounding_corners & 8)?r:0)), ImVec2(a.x, a.y + ((rounding_corners & 1)?r:0)), col); + PrimReserve(4*6); + PrimLine(ImVec2(a.x + ((rounding_corners & 1)?r:0), a.y), ImVec2(b.x - ((rounding_corners & 2)?r:0), a.y), col); + PrimLine(ImVec2(b.x, a.y + ((rounding_corners & 2)?r:0)), ImVec2(b.x, b.y - ((rounding_corners & 4)?r:0)), col); + PrimLine(ImVec2(b.x - ((rounding_corners & 4)?r:0), b.y), ImVec2(a.x + ((rounding_corners & 8)?r:0), b.y), col); + PrimLine(ImVec2(a.x, b.y - ((rounding_corners & 8)?r:0)), ImVec2(a.x, a.y + ((rounding_corners & 1)?r:0)), col); - if (rounding_corners & 1) AddArc(ImVec2(a.x+r,a.y+r), r, col, 0, 3); - if (rounding_corners & 2) AddArc(ImVec2(b.x-r,a.y+r), r, col, 3, 6); - if (rounding_corners & 4) AddArc(ImVec2(b.x-r,b.y-r), r, col, 6, 9); - if (rounding_corners & 8) AddArc(ImVec2(a.x+r,b.y-r), r, col, 9, 12); + if (rounding_corners & 1) AddArcFast(ImVec2(a.x+r,a.y+r), r, col, 0, 3); + if (rounding_corners & 2) AddArcFast(ImVec2(b.x-r,a.y+r), r, col, 3, 6); + if (rounding_corners & 4) AddArcFast(ImVec2(b.x-r,b.y-r), r, col, 6, 9); + if (rounding_corners & 8) AddArcFast(ImVec2(a.x+r,b.y-r), r, col, 9, 12); } } @@ -5607,46 +8228,26 @@ void ImDrawList::AddRectFilled(const ImVec2& a, const ImVec2& b, ImU32 col, floa if (r == 0.0f || rounding_corners == 0) { // Use triangle so we can merge more draw calls together (at the cost of extra vertices) - ReserveVertices(6); - AddVtx(ImVec2(a.x,a.y), col); - AddVtx(ImVec2(b.x,a.y), col); - AddVtx(ImVec2(b.x,b.y), col); - AddVtx(ImVec2(a.x,a.y), col); - AddVtx(ImVec2(b.x,b.y), col); - AddVtx(ImVec2(a.x,b.y), col); + PrimReserve(6); + PrimRect(a, b, col); } else { - ReserveVertices(6+6*2); - AddVtx(ImVec2(a.x+r,a.y), col); - AddVtx(ImVec2(b.x-r,a.y), col); - AddVtx(ImVec2(b.x-r,b.y), col); - AddVtx(ImVec2(a.x+r,a.y), col); - AddVtx(ImVec2(b.x-r,b.y), col); - AddVtx(ImVec2(a.x+r,b.y), col); + PrimReserve(6+6*2); + PrimRect(ImVec2(a.x+r,a.y), ImVec2(b.x-r,b.y), col); float top_y = (rounding_corners & 1) ? a.y+r : a.y; float bot_y = (rounding_corners & 8) ? b.y-r : b.y; - AddVtx(ImVec2(a.x,top_y), col); - AddVtx(ImVec2(a.x+r,top_y), col); - AddVtx(ImVec2(a.x+r,bot_y), col); - AddVtx(ImVec2(a.x,top_y), col); - AddVtx(ImVec2(a.x+r,bot_y), col); - AddVtx(ImVec2(a.x,bot_y), col); + PrimRect(ImVec2(a.x,top_y), ImVec2(a.x+r,bot_y), col); top_y = (rounding_corners & 2) ? a.y+r : a.y; bot_y = (rounding_corners & 4) ? b.y-r : b.y; - AddVtx(ImVec2(b.x-r,top_y), col); - AddVtx(ImVec2(b.x,top_y), col); - AddVtx(ImVec2(b.x,bot_y), col); - AddVtx(ImVec2(b.x-r,top_y), col); - AddVtx(ImVec2(b.x,bot_y), col); - AddVtx(ImVec2(b.x-r,bot_y), col); + PrimRect(ImVec2(b.x-r,top_y), ImVec2(b.x,bot_y), col); - if (rounding_corners & 1) AddArc(ImVec2(a.x+r,a.y+r), r, col, 0, 3, true); - if (rounding_corners & 2) AddArc(ImVec2(b.x-r,a.y+r), r, col, 3, 6, true); - if (rounding_corners & 4) AddArc(ImVec2(b.x-r,b.y-r), r, col, 6, 9, true); - if (rounding_corners & 8) AddArc(ImVec2(a.x+r,b.y-r), r, col, 9, 12,true); + if (rounding_corners & 1) AddArcFast(ImVec2(a.x+r,a.y+r), r, col, 0, 3, true); + if (rounding_corners & 2) AddArcFast(ImVec2(b.x-r,a.y+r), r, col, 3, 6, true); + if (rounding_corners & 4) AddArcFast(ImVec2(b.x-r,b.y-r), r, col, 6, 9, true); + if (rounding_corners & 8) AddArcFast(ImVec2(a.x+r,b.y-r), r, col, 9, 12, true); } } @@ -5655,12 +8256,8 @@ void ImDrawList::AddTriangleFilled(const ImVec2& a, const ImVec2& b, const ImVec if ((col >> 24) == 0) return; - const ImVec2 offset(GImGui.IO.PixelCenterOffset,GImGui.IO.PixelCenterOffset); - - ReserveVertices(3); - AddVtx(a + offset, col); - AddVtx(b + offset, col); - AddVtx(c + offset, col); + PrimReserve(3); + PrimTriangle(a, b, c, col); } void ImDrawList::AddCircle(const ImVec2& centre, float radius, ImU32 col, int num_segments) @@ -5668,15 +8265,13 @@ void ImDrawList::AddCircle(const ImVec2& centre, float radius, ImU32 col, int nu if ((col >> 24) == 0) return; - const ImVec2 offset(GImGui.IO.PixelCenterOffset,GImGui.IO.PixelCenterOffset); - - ReserveVertices((unsigned int)num_segments*6); + PrimReserve((unsigned int)num_segments*6); const float a_step = 2*PI/(float)num_segments; float a0 = 0.0f; for (int i = 0; i < num_segments; i++) { const float a1 = (i + 1) == num_segments ? 0.0f : a0 + a_step; - AddVtxLine(centre + offset + ImVec2(cosf(a0), sinf(a0))*radius, centre + ImVec2(cosf(a1), sinf(a1))*radius, col); + PrimLine(centre + ImVec2(cosf(a0), sinf(a0))*radius, centre + ImVec2(cosf(a1), sinf(a1))*radius, col); a0 = a1; } } @@ -5686,36 +8281,39 @@ void ImDrawList::AddCircleFilled(const ImVec2& centre, float radius, ImU32 col, if ((col >> 24) == 0) return; - const ImVec2 offset(GImGui.IO.PixelCenterOffset,GImGui.IO.PixelCenterOffset); - - ReserveVertices((unsigned int)num_segments*3); + const ImVec2 uv = GImGui->FontTexUvWhitePixel; + PrimReserve((unsigned int)num_segments*3); const float a_step = 2*PI/(float)num_segments; float a0 = 0.0f; for (int i = 0; i < num_segments; i++) { const float a1 = (i + 1) == num_segments ? 0.0f : a0 + a_step; - AddVtx(centre + offset + ImVec2(cosf(a0), sinf(a0))*radius, col); - AddVtx(centre + offset + ImVec2(cosf(a1), sinf(a1))*radius, col); - AddVtx(centre + offset, col); + PrimVtx(centre + ImVec2(cosf(a0), sinf(a0))*radius, uv, col); + PrimVtx(centre + ImVec2(cosf(a1), sinf(a1))*radius, uv, col); + PrimVtx(centre, uv, col); a0 = a1; } } -void ImDrawList::AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width) +void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec2* cpu_clip_max) { if ((col >> 24) == 0) return; if (text_end == NULL) - text_end = text_begin + strlen(text_begin); // FIXME-OPT + text_end = text_begin + strlen(text_begin); + if (text_begin == text_end) + return; + + IM_ASSERT(font->ContainerAtlas->TexID == texture_id_stack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. // reserve vertices for worse case const unsigned int char_count = (unsigned int)(text_end - text_begin); const unsigned int vtx_count_max = char_count * 6; const size_t vtx_begin = vtx_buffer.size(); - ReserveVertices(vtx_count_max); + PrimReserve(vtx_count_max); - font->RenderText(font_size, pos, col, clip_rect_stack.back(), text_begin, text_end, vtx_write, wrap_width); + font->RenderText(font_size, pos, col, clip_rect_stack.back(), text_begin, text_end, this, wrap_width, cpu_clip_max); // give back unused vertices vtx_buffer.resize((size_t)(vtx_write - &vtx_buffer.front())); @@ -5724,156 +8322,648 @@ void ImDrawList::AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 vtx_write -= (vtx_count_max - vtx_count); } +void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv0, const ImVec2& uv1, ImU32 col) +{ + if ((col >> 24) == 0) + return; + + // FIXME-OPT: This is wasting draw calls. + const bool push_texture_id = texture_id_stack.empty() || user_texture_id != texture_id_stack.back(); + if (push_texture_id) + PushTextureID(user_texture_id); + + PrimReserve(6); + PrimRectUV(a, b, uv0, uv1, col); + + if (push_texture_id) + PopTextureID(); +} + //----------------------------------------------------------------------------- -// ImBitmapFont +// ImFontAtlias +//----------------------------------------------------------------------------- + +struct ImFontAtlas::ImFontAtlasData +{ + // Input + ImFont* OutFont; // Load into this font + void* TTFData; // TTF data, we own the memory + size_t TTFDataSize; // TTF data size, in bytes + float SizePixels; // Desired output size, in pixels + const ImWchar* GlyphRanges; // List of Unicode range (2 value per range, values are inclusive, zero-terminated list) + int FontNo; // Index of font within .TTF file (0) + + // Temporary Build Data + stbtt_fontinfo FontInfo; + stbrp_rect* Rects; + stbtt_pack_range* Ranges; + int RangesCount; +}; + +ImFontAtlas::ImFontAtlas() +{ + TexID = NULL; + TexPixelsAlpha8 = NULL; + TexPixelsRGBA32 = NULL; + TexWidth = TexHeight = 0; + TexUvWhitePixel = ImVec2(0, 0); +} + +ImFontAtlas::~ImFontAtlas() +{ + Clear(); +} + +void ImFontAtlas::ClearInputData() +{ + for (size_t i = 0; i < InputData.size(); i++) + { + if (InputData[i]->TTFData) + ImGui::MemFree(InputData[i]->TTFData); + ImGui::MemFree(InputData[i]); + } + InputData.clear(); +} + +void ImFontAtlas::ClearTexData() +{ + if (TexPixelsAlpha8) + ImGui::MemFree(TexPixelsAlpha8); + if (TexPixelsRGBA32) + ImGui::MemFree(TexPixelsRGBA32); + TexPixelsAlpha8 = NULL; + TexPixelsRGBA32 = NULL; +} + +void ImFontAtlas::ClearFonts() +{ + for (size_t i = 0; i < Fonts.size(); i++) + { + Fonts[i]->~ImFont(); + ImGui::MemFree(Fonts[i]); + } + Fonts.clear(); +} + +void ImFontAtlas::Clear() +{ + ClearInputData(); + ClearTexData(); + ClearFonts(); +} + +void ImGui::GetDefaultFontData(const void** fnt_data, unsigned int* fnt_size, const void** png_data, unsigned int* png_size) +{ + printf("GetDefaultFontData() is obsoleted in ImGui 1.30.\n"); + printf("Please use ImGui::GetIO().Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8() functions to retrieve uncompressed texture data.\n"); + if (fnt_data) *fnt_data = NULL; + if (fnt_size) *fnt_size = 0; + if (png_data) *png_data = NULL; + if (png_size) *png_size = 0; + IM_ASSERT(false); +} + +void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel) +{ + // Lazily build + if (TexPixelsAlpha8 == NULL) + { + if (InputData.empty()) + AddFontDefault(); + Build(); + } + + *out_pixels = TexPixelsAlpha8; + if (out_width) *out_width = TexWidth; + if (out_height) *out_height = TexHeight; + if (out_bytes_per_pixel) *out_bytes_per_pixel = 1; +} + +void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel) +{ + // Lazily convert to RGBA32 format + // Although it is likely to be the most commonly used format, our font rendering is 8 bpp + if (!TexPixelsRGBA32) + { + unsigned char* pixels; + GetTexDataAsAlpha8(&pixels, NULL, NULL); + TexPixelsRGBA32 = (unsigned int*)ImGui::MemAlloc((size_t)(TexWidth * TexHeight * 4)); + const unsigned char* src = pixels; + unsigned int* dst = TexPixelsRGBA32; + for (int n = TexWidth * TexHeight; n > 0; n--) + *dst++ = ((unsigned int)(*src++) << 24) | 0x00FFFFFF; + } + + *out_pixels = (unsigned char*)TexPixelsRGBA32; + if (out_width) *out_width = TexWidth; + if (out_height) *out_height = TexHeight; + if (out_bytes_per_pixel) *out_bytes_per_pixel = 4; +} + +static void GetDefaultCompressedFontDataTTF(const void** ttf_compressed_data, unsigned int* ttf_compressed_size); +static unsigned int stb_decompress_length(unsigned char *input); +static unsigned int stb_decompress(unsigned char *output, unsigned char *i, unsigned int length); + +// Load embedded ProggyClean.ttf at size 13 +ImFont* ImFontAtlas::AddFontDefault() +{ + unsigned int ttf_compressed_size; + const void* ttf_compressed; + GetDefaultCompressedFontDataTTF(&ttf_compressed, &ttf_compressed_size); + ImFont* font = AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, 13.0f, GetGlyphRangesDefault(), 0); + font->DisplayOffset.y += 1; + return font; +} + +ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, const ImWchar* glyph_ranges, int font_no) +{ + void* data = NULL; + size_t data_size = 0; + if (!ImLoadFileToMemory(filename, "rb", (void**)&data, &data_size)) + { + IM_ASSERT(0); // Could not load file. + return NULL; + } + + ImFont* font = AddFontFromMemoryTTF(data, (unsigned int)data_size, size_pixels, glyph_ranges, font_no); + return font; +} + +// Transfer ownership of 'ttf_data' to ImFontAtlas, will be deleted after Build() +ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImWchar* glyph_ranges, int font_no) +{ + // Create new font + ImFont* font = (ImFont*)ImGui::MemAlloc(sizeof(ImFont)); + new (font) ImFont(); + Fonts.push_back(font); + + // Add to build list + ImFontAtlasData* data = (ImFontAtlasData*)ImGui::MemAlloc(sizeof(ImFontAtlasData)); + memset(data, 0, sizeof(ImFontAtlasData)); + data->OutFont = font; + data->TTFData = ttf_data; + data->TTFDataSize = (size_t)ttf_size; + data->SizePixels = size_pixels; + data->GlyphRanges = glyph_ranges; + data->FontNo = font_no; + InputData.push_back(data); + + // Invalidate texture + ClearTexData(); + + return font; +} + +ImFont* ImFontAtlas::AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImWchar* glyph_ranges, int font_no) +{ + // Decompress + const size_t buf_decompressed_size = stb_decompress_length((unsigned char*)compressed_ttf_data); + unsigned char* buf_decompressed_data = (unsigned char *)ImGui::MemAlloc(buf_decompressed_size); + stb_decompress(buf_decompressed_data, (unsigned char*)compressed_ttf_data, (unsigned int)compressed_ttf_size); + + // Add + ImFont* font = AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, size_pixels, glyph_ranges, font_no); + return font; +} + +bool ImFontAtlas::Build() +{ + IM_ASSERT(InputData.size() > 0); + + TexID = NULL; + TexWidth = TexHeight = 0; + TexUvWhitePixel = ImVec2(0, 0); + ClearTexData(); + + // Initialize font information early (so we can error without any cleanup) + count glyphs + int total_glyph_count = 0; + int total_glyph_range_count = 0; + for (size_t input_i = 0; input_i < InputData.size(); input_i++) + { + ImFontAtlasData& data = *InputData[input_i]; + IM_ASSERT(data.OutFont && (!data.OutFont->IsLoaded() || data.OutFont->ContainerAtlas == this)); + const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)data.TTFData, data.FontNo); + IM_ASSERT(font_offset >= 0); + if (!stbtt_InitFont(&data.FontInfo, (unsigned char*)data.TTFData, font_offset)) + return false; + + if (!data.GlyphRanges) + data.GlyphRanges = GetGlyphRangesDefault(); + for (const ImWchar* in_range = data.GlyphRanges; in_range[0] && in_range[1]; in_range += 2) + { + total_glyph_count += (in_range[1] - in_range[0]) + 1; + total_glyph_range_count++; + } + } + + // Start packing + TexWidth = (total_glyph_count > 1000) ? 1024 : 512; // Width doesn't actually matters. + TexHeight = 0; + const int max_tex_height = 1024*32; + stbtt_pack_context spc; + int ret = stbtt_PackBegin(&spc, NULL, TexWidth, max_tex_height, 0, 1, NULL); + IM_ASSERT(ret); + stbtt_PackSetOversampling(&spc, 1, 1); + + // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values). + ImVector extra_rects; + RenderCustomTexData(0, &extra_rects); + stbrp_pack_rects((stbrp_context*)spc.pack_info, &extra_rects[0], (int)extra_rects.size()); + for (size_t i = 0; i < extra_rects.size(); i++) + if (extra_rects[i].was_packed) + TexHeight = ImMax(TexHeight, extra_rects[i].y + extra_rects[i].h); + + // Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0) + int buf_packedchars_n = 0, buf_rects_n = 0, buf_ranges_n = 0; + stbtt_packedchar* buf_packedchars = (stbtt_packedchar*)ImGui::MemAlloc(total_glyph_count * sizeof(stbtt_packedchar)); + stbrp_rect* buf_rects = (stbrp_rect*)ImGui::MemAlloc(total_glyph_count * sizeof(stbrp_rect)); + stbtt_pack_range* buf_ranges = (stbtt_pack_range*)ImGui::MemAlloc(total_glyph_range_count * sizeof(stbtt_pack_range)); + memset(buf_packedchars, 0, total_glyph_count * sizeof(stbtt_packedchar)); + memset(buf_rects, 0, total_glyph_count * sizeof(stbrp_rect)); // Unnecessary but let's clear this for the sake of sanity. + memset(buf_ranges, 0, total_glyph_range_count * sizeof(stbtt_pack_range)); + + // First font pass: pack all glyphs (no rendering at this point, we are working with glyph sizes only) + for (size_t input_i = 0; input_i < InputData.size(); input_i++) + { + ImFontAtlasData& data = *InputData[input_i]; + + // Setup ranges + int glyph_count = 0; + int glyph_ranges_count = 0; + for (const ImWchar* in_range = data.GlyphRanges; in_range[0] && in_range[1]; in_range += 2) + { + glyph_count += (in_range[1] - in_range[0]) + 1; + glyph_ranges_count++; + } + data.Ranges = buf_ranges + buf_ranges_n; + data.RangesCount = glyph_ranges_count; + buf_ranges_n += glyph_ranges_count; + for (int i = 0; i < glyph_ranges_count; i++) + { + const ImWchar* in_range = &data.GlyphRanges[i * 2]; + stbtt_pack_range& range = data.Ranges[i]; + range.font_size = data.SizePixels; + range.first_unicode_char_in_range = in_range[0]; + range.num_chars_in_range = (in_range[1] - in_range[0]) + 1; + range.chardata_for_range = buf_packedchars + buf_packedchars_n; + buf_packedchars_n += range.num_chars_in_range; + } + + // Pack + data.Rects = buf_rects + buf_rects_n; + buf_rects_n += glyph_count; + const int n = stbtt_PackFontRangesGatherRects(&spc, &data.FontInfo, data.Ranges, data.RangesCount, data.Rects); + stbrp_pack_rects((stbrp_context*)spc.pack_info, data.Rects, n); + + // Extend texture height + for (int i = 0; i < n; i++) + if (data.Rects[i].was_packed) + TexHeight = ImMax(TexHeight, data.Rects[i].y + data.Rects[i].h); + } + IM_ASSERT(buf_rects_n == total_glyph_count); + IM_ASSERT(buf_packedchars_n == total_glyph_count); + IM_ASSERT(buf_ranges_n == total_glyph_range_count); + + // Create texture + TexHeight = ImUpperPowerOfTwo(TexHeight); + TexPixelsAlpha8 = (unsigned char*)ImGui::MemAlloc(TexWidth * TexHeight); + memset(TexPixelsAlpha8, 0, TexWidth * TexHeight); + spc.pixels = TexPixelsAlpha8; + spc.height = TexHeight; + + // Second pass: render characters + for (size_t input_i = 0; input_i < InputData.size(); input_i++) + { + ImFontAtlasData& data = *InputData[input_i]; + ret = stbtt_PackFontRangesRenderIntoRects(&spc, &data.FontInfo, data.Ranges, data.RangesCount, data.Rects); + data.Rects = NULL; + } + + // End packing + stbtt_PackEnd(&spc); + ImGui::MemFree(buf_rects); + buf_rects = NULL; + + // Third pass: setup ImFont and glyphs for runtime + for (size_t input_i = 0; input_i < InputData.size(); input_i++) + { + ImFontAtlasData& data = *InputData[input_i]; + data.OutFont->ContainerAtlas = this; + data.OutFont->FontSize = data.SizePixels; + + const float font_scale = stbtt_ScaleForPixelHeight(&data.FontInfo, data.SizePixels); + int font_ascent, font_descent, font_line_gap; + stbtt_GetFontVMetrics(&data.FontInfo, &font_ascent, &font_descent, &font_line_gap); + data.OutFont->BaseLine = (font_ascent * font_scale); + data.OutFont->Glyphs.resize(0); + + const float uv_scale_x = 1.0f / TexWidth; + const float uv_scale_y = 1.0f / TexHeight; + const int character_spacing_x = 1; + for (int i = 0; i < data.RangesCount; i++) + { + stbtt_pack_range& range = data.Ranges[i]; + for (int char_idx = 0; char_idx < range.num_chars_in_range; char_idx += 1) + { + const int codepoint = range.first_unicode_char_in_range + char_idx; + const stbtt_packedchar& pc = range.chardata_for_range[char_idx]; + if (!pc.x0 && !pc.x1 && !pc.y0 && !pc.y1) + continue; + + data.OutFont->Glyphs.resize(data.OutFont->Glyphs.size() + 1); + ImFont::Glyph& glyph = data.OutFont->Glyphs.back(); + glyph.Codepoint = (ImWchar)codepoint; + glyph.Width = (signed short)pc.x1 - pc.x0 + 1; + glyph.Height = (signed short)pc.y1 - pc.y0 + 1; + glyph.XOffset = (signed short)(pc.xoff); + glyph.YOffset = (signed short)(pc.yoff + (int)(font_ascent * font_scale)); + glyph.XAdvance = (signed short)(pc.xadvance + character_spacing_x); // Bake spacing into XAdvance + glyph.U0 = ((float)pc.x0 - 0.5f) * uv_scale_x; + glyph.V0 = ((float)pc.y0 - 0.5f) * uv_scale_y; + glyph.U1 = ((float)pc.x0 - 0.5f + glyph.Width) * uv_scale_x; + glyph.V1 = ((float)pc.y0 - 0.5f + glyph.Height) * uv_scale_y; + } + } + + data.OutFont->BuildLookupTable(); + } + + // Cleanup temporaries + ImGui::MemFree(buf_packedchars); + ImGui::MemFree(buf_ranges); + buf_packedchars = NULL; + buf_ranges = NULL; + + // Render into our custom data block + RenderCustomTexData(1, &extra_rects); + + return true; +} + +void ImFontAtlas::RenderCustomTexData(int pass, void* p_rects) +{ + // . = white layer, X = black layer, others are blank + const int TEX_DATA_W = 90; + const int TEX_DATA_H = 27; + const char texture_data[TEX_DATA_W*TEX_DATA_H+1] = + { + "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX" + "..- -X.....X- X.X - X.X -X.....X - X.....X" + "--- -XXX.XXX- X...X - X...X -X....X - X....X" + "X - X.X - X.....X - X.....X -X...X - X...X" + "XX - X.X -X.......X- X.......X -X..X.X - X.X..X" + "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X" + "X..X - X.X - X.X - X.X -XX X.X - X.X XX" + "X...X - X.X - X.X - XX X.X XX - X.X - X.X " + "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X " + "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X " + "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X " + "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X " + "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X " + "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X " + "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X " + "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X " + "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX " + "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------" + "X.X X..X - -X.......X- X.......X - XX XX - " + "XX X..X - - X.....X - X.....X - X.X X.X - " + " X..X - X...X - X...X - X..X X..X - " + " XX - X.X - X.X - X...XXXXXXXXXXXXX...X - " + "------------ - X - X -X.....................X- " + " ----------------------------------- X...XXXXXXXXXXXXX...X - " + " - X..X X..X - " + " - X.X X.X - " + " - XX XX - " + }; + + ImVector& rects = *(ImVector*)p_rects; + if (pass == 0) + { + stbrp_rect r; + memset(&r, 0, sizeof(r)); + r.w = (TEX_DATA_W*2)+1; + r.h = TEX_DATA_H+1; + rects.push_back(r); + } + else if (pass == 1) + { + // Copy pixels + const stbrp_rect& r = rects[0]; + for (int y = 0, n = 0; y < TEX_DATA_H; y++) + for (int x = 0; x < TEX_DATA_W; x++, n++) + { + const int offset0 = (int)(r.x + x) + (int)(r.y + y) * TexWidth; + const int offset1 = offset0 + 1 + TEX_DATA_W; + TexPixelsAlpha8[offset0] = texture_data[n] == '.' ? 0xFF : 0x00; + TexPixelsAlpha8[offset1] = texture_data[n] == 'X' ? 0xFF : 0x00; + } + const ImVec2 tex_uv_scale(1.0f / TexWidth, 1.0f / TexHeight); + TexUvWhitePixel = ImVec2(r.x + 0.5f, r.y + 0.5f) * tex_uv_scale; + + const ImVec2 cursor_datas[ImGuiMouseCursor_Count_][3] = + { + // Pos ........ Size ......... Offset ...... + { ImVec2(0,3), ImVec2(12,19), ImVec2( 0, 0) }, // ImGuiMouseCursor_Arrow + { ImVec2(13,0), ImVec2(7,16), ImVec2( 4, 8) }, // ImGuiMouseCursor_TextInput + { ImVec2(31,0), ImVec2(23,23), ImVec2(11,11) }, // ImGuiMouseCursor_Move + { ImVec2(21,0), ImVec2( 9,23), ImVec2( 5,11) }, // ImGuiMouseCursor_ResizeNS + { ImVec2(55,18),ImVec2(23, 9), ImVec2(11, 5) }, // ImGuiMouseCursor_ResizeEW + { ImVec2(73,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNESW + { ImVec2(55,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNWSE + }; + + for (int type = 0; type < ImGuiMouseCursor_Count_; type++) + { + ImGuiMouseCursorData& cursor_data = GImGui->MouseCursorData[type]; + ImVec2 pos = cursor_datas[type][0] + ImVec2((float)r.x, (float)r.y); + const ImVec2 size = cursor_datas[type][1]; + cursor_data.Type = type; + cursor_data.Size = size; + cursor_data.Offset = cursor_datas[type][2]; + cursor_data.TexUvMin[0] = (pos) * tex_uv_scale; + cursor_data.TexUvMax[0] = (pos + size) * tex_uv_scale; + pos.x += TEX_DATA_W+1; + cursor_data.TexUvMin[1] = (pos) * tex_uv_scale; + cursor_data.TexUvMax[1] = (pos + size) * tex_uv_scale; + } + } +} + +//----------------------------------------------------------------------------- +// ImFont //----------------------------------------------------------------------------- ImFont::ImFont() { Scale = 1.0f; - DisplayOffset = ImVec2(0.0f,0.0f); - TexUvForWhite = ImVec2(0.0f,0.0f); FallbackChar = (ImWchar)'?'; + Clear(); +} - Data = NULL; - DataSize = 0; - DataOwned = false; - Info = NULL; - Common = NULL; - Glyphs = NULL; - GlyphsCount = 0; - Kerning = NULL; - KerningCount = 0; +ImFont::~ImFont() +{ + // Invalidate active font so that the user gets a clear crash instead of a dangling pointer. + // If you want to delete fonts you need to do it between Render() and NewFrame(). + ImGuiState& g = *GImGui; + if (g.Font == this) + g.Font = NULL; + Clear(); } void ImFont::Clear() { - if (Data && DataOwned) - ImGui::MemFree(Data); - Data = NULL; - DataOwned = false; - Info = NULL; - Common = NULL; - Glyphs = NULL; - GlyphsCount = 0; - Filenames.clear(); + FontSize = 0.0f; + DisplayOffset = ImVec2(-0.5f, 0.5f); + BaseLine = 0.0f; + ContainerAtlas = NULL; + Glyphs.clear(); + FallbackGlyph = NULL; + FallbackXAdvance = 0.0f; + IndexXAdvance.clear(); IndexLookup.clear(); } -bool ImFont::LoadFromFile(const char* filename) +// Retrieve list of range (2 int per range, values are inclusive) +const ImWchar* ImFontAtlas::GetGlyphRangesDefault() { - IM_ASSERT(!IsLoaded()); // Call Clear() - - // Load file - FILE* f; - if ((f = fopen(filename, "rb")) == NULL) - return false; - if (fseek(f, 0, SEEK_END)) + static const ImWchar ranges[] = { - fclose(f); - return false; - } - const long f_size = ftell(f); - if (f_size == -1) - { - fclose(f); - return false; - } - DataSize = (size_t)f_size; - if (fseek(f, 0, SEEK_SET)) - { - fclose(f); - return false; - } - if ((Data = (unsigned char*)ImGui::MemAlloc(DataSize)) == NULL) - { - fclose(f); - return false; - } - if (fread(Data, 1, DataSize, f) != DataSize) - { - fclose(f); - ImGui::MemFree(Data); - return false; - } - fclose(f); - DataOwned = true; - return LoadFromMemory(Data, DataSize); + 0x0020, 0x00FF, // Basic Latin + Latin Supplement + 0, + }; + return &ranges[0]; } -bool ImFont::LoadFromMemory(const void* data, size_t data_size) +const ImWchar* ImFontAtlas::GetGlyphRangesChinese() { - IM_ASSERT(!IsLoaded()); // Call Clear() - - Data = (unsigned char*)data; - DataSize = data_size; - - // Parse data - if (DataSize < 4 || Data[0] != 'B' || Data[1] != 'M' || Data[2] != 'F' || Data[3] != 0x03) - return false; - for (const unsigned char* p = Data+4; p < Data + DataSize; ) + static const ImWchar ranges[] = { - const unsigned char block_type = *(unsigned char*)p; - p += sizeof(unsigned char); - ImU32 block_size; // use memcpy to read 4-byte because they may be unaligned. This seems to break when compiling for Emscripten. - memcpy(&block_size, p, sizeof(ImU32)); - p += sizeof(ImU32); + 0x0020, 0x00FF, // Basic Latin + Latin Supplement + 0x3000, 0x30FF, // Punctuations, Hiragana, Katakana + 0x31F0, 0x31FF, // Katakana Phonetic Extensions + 0xFF00, 0xFFEF, // Half-width characters + 0x4e00, 0x9FAF, // CJK Ideograms + 0, + }; + return &ranges[0]; +} - switch (block_type) - { - case 1: - IM_ASSERT(Info == NULL); - Info = (FntInfo*)p; - break; - case 2: - IM_ASSERT(Common == NULL); - Common = (FntCommon*)p; - break; - case 3: - for (const unsigned char* s = p; s < p+block_size && s < Data+DataSize; s = s + strlen((const char*)s) + 1) - Filenames.push_back((const char*)s); - break; - case 4: - IM_ASSERT(Glyphs == NULL && GlyphsCount == 0); - Glyphs = (FntGlyph*)p; - GlyphsCount = block_size / sizeof(FntGlyph); - break; - case 5: - IM_ASSERT(Kerning == NULL && KerningCount == 0); - Kerning = (FntKerning*)p; - KerningCount = block_size / sizeof(FntKerning); - break; - default: - break; - } - p += block_size; +const ImWchar* ImFontAtlas::GetGlyphRangesJapanese() +{ + // Store the 1946 ideograms code points as successive offsets from the initial unicode codepoint 0x4E00. Each offset has an implicit +1. + // This encoding helps us reduce the source code size. + static const short offsets_from_0x4E00[] = + { + -1,0,1,3,0,0,0,0,1,0,5,1,1,0,7,4,6,10,0,1,9,9,7,1,3,19,1,10,7,1,0,1,0,5,1,0,6,4,2,6,0,0,12,6,8,0,3,5,0,1,0,9,0,0,8,1,1,3,4,5,13,0,0,8,2,17, + 4,3,1,1,9,6,0,0,0,2,1,3,2,22,1,9,11,1,13,1,3,12,0,5,9,2,0,6,12,5,3,12,4,1,2,16,1,1,4,6,5,3,0,6,13,15,5,12,8,14,0,0,6,15,3,6,0,18,8,1,6,14,1, + 5,4,12,24,3,13,12,10,24,0,0,0,1,0,1,1,2,9,10,2,2,0,0,3,3,1,0,3,8,0,3,2,4,4,1,6,11,10,14,6,15,3,4,15,1,0,0,5,2,2,0,0,1,6,5,5,6,0,3,6,5,0,0,1,0, + 11,2,2,8,4,7,0,10,0,1,2,17,19,3,0,2,5,0,6,2,4,4,6,1,1,11,2,0,3,1,2,1,2,10,7,6,3,16,0,8,24,0,0,3,1,1,3,0,1,6,0,0,0,2,0,1,5,15,0,1,0,0,2,11,19, + 1,4,19,7,6,5,1,0,0,0,0,5,1,0,1,9,0,0,5,0,2,0,1,0,3,0,11,3,0,2,0,0,0,0,0,9,3,6,4,12,0,14,0,0,29,10,8,0,14,37,13,0,31,16,19,0,8,30,1,20,8,3,48, + 21,1,0,12,0,10,44,34,42,54,11,18,82,0,2,1,2,12,1,0,6,2,17,2,12,7,0,7,17,4,2,6,24,23,8,23,39,2,16,23,1,0,5,1,2,15,14,5,6,2,11,0,8,6,2,2,2,14, + 20,4,15,3,4,11,10,10,2,5,2,1,30,2,1,0,0,22,5,5,0,3,1,5,4,1,0,0,2,2,21,1,5,1,2,16,2,1,3,4,0,8,4,0,0,5,14,11,2,16,1,13,1,7,0,22,15,3,1,22,7,14, + 22,19,11,24,18,46,10,20,64,45,3,2,0,4,5,0,1,4,25,1,0,0,2,10,0,0,0,1,0,1,2,0,0,9,1,2,0,0,0,2,5,2,1,1,5,5,8,1,1,1,5,1,4,9,1,3,0,1,0,1,1,2,0,0, + 2,0,1,8,22,8,1,0,0,0,0,4,2,1,0,9,8,5,0,9,1,30,24,2,6,4,39,0,14,5,16,6,26,179,0,2,1,1,0,0,0,5,2,9,6,0,2,5,16,7,5,1,1,0,2,4,4,7,15,13,14,0,0, + 3,0,1,0,0,0,2,1,6,4,5,1,4,9,0,3,1,8,0,0,10,5,0,43,0,2,6,8,4,0,2,0,0,9,6,0,9,3,1,6,20,14,6,1,4,0,7,2,3,0,2,0,5,0,3,1,0,3,9,7,0,3,4,0,4,9,1,6,0, + 9,0,0,2,3,10,9,28,3,6,2,4,1,2,32,4,1,18,2,0,3,1,5,30,10,0,2,2,2,0,7,9,8,11,10,11,7,2,13,7,5,10,0,3,40,2,0,1,6,12,0,4,5,1,5,11,11,21,4,8,3,7, + 8,8,33,5,23,0,0,19,8,8,2,3,0,6,1,1,1,5,1,27,4,2,5,0,3,5,6,3,1,0,3,1,12,5,3,3,2,0,7,7,2,1,0,4,0,1,1,2,0,10,10,6,2,5,9,7,5,15,15,21,6,11,5,20, + 4,3,5,5,2,5,0,2,1,0,1,7,28,0,9,0,5,12,5,5,18,30,0,12,3,3,21,16,25,32,9,3,14,11,24,5,66,9,1,2,0,5,9,1,5,1,8,0,8,3,3,0,1,15,1,4,8,1,2,7,0,7,2, + 8,3,7,5,3,7,10,2,1,0,0,2,25,0,6,4,0,10,0,4,2,4,1,12,5,38,4,0,4,1,10,5,9,4,0,14,4,2,5,18,20,21,1,3,0,5,0,7,0,3,7,1,3,1,1,8,1,0,0,0,3,2,5,2,11, + 6,0,13,1,3,9,1,12,0,16,6,2,1,0,2,1,12,6,13,11,2,0,28,1,7,8,14,13,8,13,0,2,0,5,4,8,10,2,37,42,19,6,6,7,4,14,11,18,14,80,7,6,0,4,72,12,36,27, + 7,7,0,14,17,19,164,27,0,5,10,7,3,13,6,14,0,2,2,5,3,0,6,13,0,0,10,29,0,4,0,3,13,0,3,1,6,51,1,5,28,2,0,8,0,20,2,4,0,25,2,10,13,10,0,16,4,0,1,0, + 2,1,7,0,1,8,11,0,0,1,2,7,2,23,11,6,6,4,16,2,2,2,0,22,9,3,3,5,2,0,15,16,21,2,9,20,15,15,5,3,9,1,0,0,1,7,7,5,4,2,2,2,38,24,14,0,0,15,5,6,24,14, + 5,5,11,0,21,12,0,3,8,4,11,1,8,0,11,27,7,2,4,9,21,59,0,1,39,3,60,62,3,0,12,11,0,3,30,11,0,13,88,4,15,5,28,13,1,4,48,17,17,4,28,32,46,0,16,0, + 18,11,1,8,6,38,11,2,6,11,38,2,0,45,3,11,2,7,8,4,30,14,17,2,1,1,65,18,12,16,4,2,45,123,12,56,33,1,4,3,4,7,0,0,0,3,2,0,16,4,2,4,2,0,7,4,5,2,26, + 2,25,6,11,6,1,16,2,6,17,77,15,3,35,0,1,0,5,1,0,38,16,6,3,12,3,3,3,0,9,3,1,3,5,2,9,0,18,0,25,1,3,32,1,72,46,6,2,7,1,3,14,17,0,28,1,40,13,0,20, + 15,40,6,38,24,12,43,1,1,9,0,12,6,0,6,2,4,19,3,7,1,48,0,9,5,0,5,6,9,6,10,15,2,11,19,3,9,2,0,1,10,1,27,8,1,3,6,1,14,0,26,0,27,16,3,4,9,6,2,23, + 9,10,5,25,2,1,6,1,1,48,15,9,15,14,3,4,26,60,29,13,37,21,1,6,4,0,2,11,22,23,16,16,2,2,1,3,0,5,1,6,4,0,0,4,0,0,8,3,0,2,5,0,7,1,7,3,13,2,4,10, + 3,0,2,31,0,18,3,0,12,10,4,1,0,7,5,7,0,5,4,12,2,22,10,4,2,15,2,8,9,0,23,2,197,51,3,1,1,4,13,4,3,21,4,19,3,10,5,40,0,4,1,1,10,4,1,27,34,7,21, + 2,17,2,9,6,4,2,3,0,4,2,7,8,2,5,1,15,21,3,4,4,2,2,17,22,1,5,22,4,26,7,0,32,1,11,42,15,4,1,2,5,0,19,3,1,8,6,0,10,1,9,2,13,30,8,2,24,17,19,1,4, + 4,25,13,0,10,16,11,39,18,8,5,30,82,1,6,8,18,77,11,13,20,75,11,112,78,33,3,0,0,60,17,84,9,1,1,12,30,10,49,5,32,158,178,5,5,6,3,3,1,3,1,4,7,6, + 19,31,21,0,2,9,5,6,27,4,9,8,1,76,18,12,1,4,0,3,3,6,3,12,2,8,30,16,2,25,1,5,5,4,3,0,6,10,2,3,1,0,5,1,19,3,0,8,1,5,2,6,0,0,0,19,1,2,0,5,1,2,5, + 1,3,7,0,4,12,7,3,10,22,0,9,5,1,0,2,20,1,1,3,23,30,3,9,9,1,4,191,14,3,15,6,8,50,0,1,0,0,4,0,0,1,0,2,4,2,0,2,3,0,2,0,2,2,8,7,0,1,1,1,3,3,17,11, + 91,1,9,3,2,13,4,24,15,41,3,13,3,1,20,4,125,29,30,1,0,4,12,2,21,4,5,5,19,11,0,13,11,86,2,18,0,7,1,8,8,2,2,22,1,2,6,5,2,0,1,2,8,0,2,0,5,2,1,0, + 2,10,2,0,5,9,2,1,2,0,1,0,4,0,0,10,2,5,3,0,6,1,0,1,4,4,33,3,13,17,3,18,6,4,7,1,5,78,0,4,1,13,7,1,8,1,0,35,27,15,3,0,0,0,1,11,5,41,38,15,22,6, + 14,14,2,1,11,6,20,63,5,8,27,7,11,2,2,40,58,23,50,54,56,293,8,8,1,5,1,14,0,1,12,37,89,8,8,8,2,10,6,0,0,0,4,5,2,1,0,1,1,2,7,0,3,3,0,4,6,0,3,2, + 19,3,8,0,0,0,4,4,16,0,4,1,5,1,3,0,3,4,6,2,17,10,10,31,6,4,3,6,10,126,7,3,2,2,0,9,0,0,5,20,13,0,15,0,6,0,2,5,8,64,50,3,2,12,2,9,0,0,11,8,20, + 109,2,18,23,0,0,9,61,3,0,28,41,77,27,19,17,81,5,2,14,5,83,57,252,14,154,263,14,20,8,13,6,57,39,38, + }; + static int ranges_unpacked = false; + static ImWchar ranges[8 + IM_ARRAYSIZE(offsets_from_0x4E00)*2 + 1] = + { + 0x0020, 0x00FF, // Basic Latin + Latin Supplement + 0x3000, 0x30FF, // Punctuations, Hiragana, Katakana + 0x31F0, 0x31FF, // Katakana Phonetic Extensions + 0xFF00, 0xFFEF, // Half-width characters + }; + if (!ranges_unpacked) + { + // Unpack + int codepoint = 0x4e00; + ImWchar* dst = &ranges[8]; + for (int n = 0; n < IM_ARRAYSIZE(offsets_from_0x4E00); n++, dst += 2) + dst[0] = dst[1] = (ImWchar)(codepoint += (offsets_from_0x4E00[n] + 1)); + dst[0] = 0; + ranges_unpacked = true; } - - BuildLookupTable(); - return true; + return &ranges[0]; } void ImFont::BuildLookupTable() { - ImU32 max_c = 0; - for (size_t i = 0; i != GlyphsCount; i++) - if (max_c < Glyphs[i].Id) - max_c = Glyphs[i].Id; + int max_codepoint = 0; + for (size_t i = 0; i != Glyphs.size(); i++) + max_codepoint = ImMax(max_codepoint, (int)Glyphs[i].Codepoint); + IndexXAdvance.clear(); + IndexXAdvance.resize((size_t)max_codepoint + 1); IndexLookup.clear(); - IndexLookup.resize(max_c + 1); - for (size_t i = 0; i < IndexLookup.size(); i++) + IndexLookup.resize((size_t)max_codepoint + 1); + for (size_t i = 0; i < (size_t)max_codepoint + 1; i++) + { + IndexXAdvance[i] = -1.0f; IndexLookup[i] = -1; - for (size_t i = 0; i < GlyphsCount; i++) - IndexLookup[Glyphs[i].Id] = (int)i; + } + for (size_t i = 0; i < Glyphs.size(); i++) + { + const size_t codepoint = (int)Glyphs[i].Codepoint; + IndexXAdvance[codepoint] = Glyphs[i].XAdvance; + IndexLookup[codepoint] = (int)i; + } + + // Create a glyph to handle TAB + // FIXME: Needs proper TAB handling but it needs to be contextualized (can arbitrary say that each string starts at "column 0" + if (FindGlyph((unsigned short)' ')) + { + if (Glyphs.back().Codepoint != '\t') // So we can call this function multiple times + Glyphs.resize(Glyphs.size() + 1); + ImFont::Glyph& tab_glyph = Glyphs.back(); + tab_glyph = *FindGlyph((unsigned short)' '); + tab_glyph.Codepoint = '\t'; + tab_glyph.XAdvance *= 4; + IndexXAdvance[(size_t)tab_glyph.Codepoint] = (float)tab_glyph.XAdvance; + IndexLookup[(size_t)tab_glyph.Codepoint] = (int)(Glyphs.size()-1); + } + + FallbackGlyph = NULL; + FallbackGlyph = FindGlyph(FallbackChar); + FallbackXAdvance = FallbackGlyph ? FallbackGlyph->XAdvance : 0.0f; + for (size_t i = 0; i < (size_t)max_codepoint + 1; i++) + if (IndexXAdvance[i] < 0.0f) + IndexXAdvance[i] = FallbackXAdvance; } -const ImFont::FntGlyph* ImFont::FindGlyph(unsigned short c) const +void ImFont::SetFallbackChar(ImWchar c) +{ + FallbackChar = c; + BuildLookupTable(); +} + +const ImFont::Glyph* ImFont::FindGlyph(unsigned short c) const { if (c < (int)IndexLookup.size()) { const int i = IndexLookup[c]; - if (i >= 0 && i < (int)GlyphsCount) + if (i != -1) return &Glyphs[i]; } return FallbackGlyph; @@ -5881,65 +8971,66 @@ const ImFont::FntGlyph* ImFont::FindGlyph(unsigned short c) const // Convert UTF-8 to 32-bits character, process single character input. // Based on stb_from_utf8() from github.com/nothings/stb/ +// We handle UTF-8 decoding error by skipping forward. static int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end) { - if (*in_text != 0) + unsigned int c = (unsigned int)-1; + const unsigned char* str = (const unsigned char*)in_text; + if (!(*str & 0x80)) { - unsigned int c = (unsigned int)-1; - const unsigned char* str = (const unsigned char*)in_text; - if (!(*str & 0x80)) - { - c = (unsigned int)(*str++); - *out_char = c; - return 1; - } - if ((*str & 0xe0) == 0xc0) - { - if (in_text_end && in_text_end - (const char*)str < 2) return -1; - if (*str < 0xc2) return -1; - c = (unsigned int)((*str++ & 0x1f) << 6); - if ((*str & 0xc0) != 0x80) return -1; - c += (*str++ & 0x3f); - *out_char = c; - return 2; - } - if ((*str & 0xf0) == 0xe0) - { - if (in_text_end && in_text_end - (const char*)str < 3) return -1; - if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return -1; - if (*str == 0xed && str[1] > 0x9f) return -1; // str[1] < 0x80 is checked below - c = (unsigned int)((*str++ & 0x0f) << 12); - if ((*str & 0xc0) != 0x80) return -1; - c += (unsigned int)((*str++ & 0x3f) << 6); - if ((*str & 0xc0) != 0x80) return -1; - c += (*str++ & 0x3f); - *out_char = c; - return 3; - } - if ((*str & 0xf8) == 0xf0) - { - if (in_text_end && in_text_end - (const char*)str < 4) return -1; - if (*str > 0xf4) return -1; - if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return -1; - if (*str == 0xf4 && str[1] > 0x8f) return -1; // str[1] < 0x80 is checked below - c = (unsigned int)((*str++ & 0x07) << 18); - if ((*str & 0xc0) != 0x80) return -1; - c += (unsigned int)((*str++ & 0x3f) << 12); - if ((*str & 0xc0) != 0x80) return -1; - c += (unsigned int)((*str++ & 0x3f) << 6); - if ((*str & 0xc0) != 0x80) return -1; - c += (*str++ & 0x3f); - // utf-8 encodings of values used in surrogate pairs are invalid - if ((c & 0xFFFFF800) == 0xD800) return -1; - *out_char = c; - return 4; - } + c = (unsigned int)(*str++); + *out_char = c; + return 1; + } + if ((*str & 0xe0) == 0xc0) + { + *out_char = 0; + if (in_text_end && in_text_end - (const char*)str < 2) return 0; + if (*str < 0xc2) return 0; + c = (unsigned int)((*str++ & 0x1f) << 6); + if ((*str & 0xc0) != 0x80) return 0; + c += (*str++ & 0x3f); + *out_char = c; + return 2; + } + if ((*str & 0xf0) == 0xe0) + { + *out_char = 0; + if (in_text_end && in_text_end - (const char*)str < 3) return 0; + if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 0; + if (*str == 0xed && str[1] > 0x9f) return 0; // str[1] < 0x80 is checked below + c = (unsigned int)((*str++ & 0x0f) << 12); + if ((*str & 0xc0) != 0x80) return 0; + c += (unsigned int)((*str++ & 0x3f) << 6); + if ((*str & 0xc0) != 0x80) return 0; + c += (*str++ & 0x3f); + *out_char = c; + return 3; + } + if ((*str & 0xf8) == 0xf0) + { + *out_char = 0; + if (in_text_end && in_text_end - (const char*)str < 4) return 0; + if (*str > 0xf4) return 0; + if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 0; + if (*str == 0xf4 && str[1] > 0x8f) return 0; // str[1] < 0x80 is checked below + c = (unsigned int)((*str++ & 0x07) << 18); + if ((*str & 0xc0) != 0x80) return 0; + c += (unsigned int)((*str++ & 0x3f) << 12); + if ((*str & 0xc0) != 0x80) return 0; + c += (unsigned int)((*str++ & 0x3f) << 6); + if ((*str & 0xc0) != 0x80) return 0; + c += (*str++ & 0x3f); + // utf-8 encodings of values used in surrogate pairs are invalid + if ((c & 0xFFFFF800) == 0xD800) return 0; + *out_char = c; + return 4; } *out_char = 0; return 0; } -static ptrdiff_t ImTextStrFromUtf8(ImWchar* buf, size_t buf_size, const char* in_text, const char* in_text_end) +static ptrdiff_t ImTextStrFromUtf8(ImWchar* buf, size_t buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining) { ImWchar* buf_out = buf; ImWchar* buf_end = buf + buf_size; @@ -5947,10 +9038,14 @@ static ptrdiff_t ImTextStrFromUtf8(ImWchar* buf, size_t buf_size, const char* in { unsigned int c; in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); + if (c == 0) + break; if (c < 0x10000) // FIXME: Losing characters that don't fit in 2 bytes *buf_out++ = (ImWchar)c; } *buf_out = 0; + if (in_text_remaining) + *in_text_remaining = in_text; return buf_out - buf; } @@ -5961,6 +9056,8 @@ static int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end { unsigned int c; in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); + if (c == 0) + break; if (c < 0x10000) char_count++; } @@ -6025,7 +9122,7 @@ static ptrdiff_t ImTextStrToUtf8(char* buf, size_t buf_size, const ImWchar* in_t return buf_out - buf; } -static int ImTextCountUtf8BytesFromWchar(const ImWchar* in_text, const ImWchar* in_text_end) +static int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end) { int bytes_count = 0; while ((!in_text_end || in_text < in_text_end) && *in_text) @@ -6049,17 +9146,10 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c // List of hardcoded separators: .,;!?'" // Skip extra blanks after a line returns (that includes not counting them in width computation) - // e.g. "Hello world" - // --> - // "Hello" - // "world" + // e.g. "Hello world" --> "Hello" "World" // Cut words that cannot possibly fit within one line. - // e.g.: "The tropical fish" with ~5 characters worth of width - // --> - // "The tr" - // "opical" - // "fish" + // e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish" float line_width = 0.0f; float word_width = 0.0f; @@ -6072,9 +9162,14 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c const char* s = text; while (s < text_end) { - unsigned int c; - const int bytes_count = ImTextCharFromUtf8(&c, s, text_end); - const char* next_s = s + (bytes_count > 0 ? bytes_count : 1); + unsigned int c = (unsigned int)*s; + const char* next_s; + if (c < 0x80) + next_s = s + 1; + else + next_s = s + ImTextCharFromUtf8(&c, s, text_end); + if (c == 0) + break; if (c == '\n') { @@ -6084,19 +9179,8 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c continue; } - float char_width = 0.0f; - if (c == '\t') - { - if (const FntGlyph* glyph = FindGlyph((unsigned short)' ')) - char_width = (glyph->XAdvance + Info->SpacingHoriz) * 4 * scale; - } - else - { - if (const FntGlyph* glyph = FindGlyph((unsigned short)c)) - char_width = (glyph->XAdvance + Info->SpacingHoriz) * scale; - } - - if (c == ' ' || c == '\t') + const float char_width = ((size_t)c < IndexXAdvance.size()) ? IndexXAdvance[(size_t)c] * scale : FallbackXAdvance; + if (ImCharIsSpace(c)) { if (inside_word) { @@ -6142,10 +9226,10 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** remaining) const { if (!text_end) - text_end = text_begin + strlen(text_begin); // FIXME-OPT + text_end = text_begin + strlen(text_begin); // FIXME-OPT: Need to avoid this. - const float scale = size / (float)Info->FontSize; - const float line_height = (float)Info->FontSize * scale; + const float scale = size / FontSize; + const float line_height = FontSize * scale; ImVec2 text_size = ImVec2(0,0); float line_width = 0.0f; @@ -6178,40 +9262,40 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons while (s < text_end) { const char c = *s; - if (c == ' ' || c == '\t') { s++; } else if (c == '\n') { s++; break; } else { break; } + if (ImCharIsSpace(c)) { s++; } else if (c == '\n') { s++; break; } else { break; } } continue; } } // Decode and advance source (handle unlikely UTF-8 decoding failure by skipping to the next byte) - unsigned int c; - const int bytes_count = ImTextCharFromUtf8(&c, s, text_end); - s += bytes_count > 0 ? bytes_count : 1; + const char* prev_s = s; + unsigned int c = (unsigned int)*s; + if (c < 0x80) + { + s += 1; + } + else + { + s += ImTextCharFromUtf8(&c, s, text_end); + if (c == 0) + break; + } if (c == '\n') { - if (text_size.x < line_width) - text_size.x = line_width; + text_size.x = ImMax(text_size.x, line_width); text_size.y += line_height; line_width = 0.0f; continue; } - float char_width = 0.0f; - if (c == '\t') - { - // FIXME: Better TAB handling - if (const FntGlyph* glyph = FindGlyph((unsigned short)' ')) - char_width = (glyph->XAdvance + Info->SpacingHoriz) * 4 * scale; - } - else if (const FntGlyph* glyph = FindGlyph((unsigned short)c)) - { - char_width = (glyph->XAdvance + Info->SpacingHoriz) * scale; - } - + const float char_width = ((size_t)c < IndexXAdvance.size()) ? IndexXAdvance[(size_t)c] * scale : FallbackXAdvance; if (line_width + char_width >= max_width) + { + s = prev_s; break; + } line_width += char_width; } @@ -6234,8 +9318,8 @@ ImVec2 ImFont::CalcTextSizeW(float size, float max_width, const ImWchar* text_be if (!text_end) text_end = text_begin + ImStrlenW(text_begin); - const float scale = size / (float)Info->FontSize; - const float line_height = (float)Info->FontSize * scale; + const float scale = size / FontSize; + const float line_height = FontSize * scale; ImVec2 text_size = ImVec2(0,0); float line_width = 0.0f; @@ -6247,28 +9331,18 @@ ImVec2 ImFont::CalcTextSizeW(float size, float max_width, const ImWchar* text_be if (c == '\n') { - if (text_size.x < line_width) - text_size.x = line_width; + text_size.x = ImMax(text_size.x, line_width); text_size.y += line_height; line_width = 0.0f; continue; } - float char_width = 0.0f; - if (c == '\t') - { - // FIXME: Better TAB handling - if (const FntGlyph* glyph = FindGlyph((unsigned short)' ')) - char_width = (glyph->XAdvance + Info->SpacingHoriz) * 4 * scale; - } - else - { - if (const FntGlyph* glyph = FindGlyph((unsigned short)c)) - char_width = (glyph->XAdvance + Info->SpacingHoriz) * scale; - } - + const float char_width = ((size_t)c < IndexXAdvance.size()) ? IndexXAdvance[(size_t)c] * scale : FallbackXAdvance; if (line_width + char_width >= max_width) + { + s--; break; + } line_width += char_width; } @@ -6286,16 +9360,13 @@ ImVec2 ImFont::CalcTextSizeW(float size, float max_width, const ImWchar* text_be return text_size; } -void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect_ref, const char* text_begin, const char* text_end, ImDrawVert*& out_vertices, float wrap_width) const +void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect_ref, const char* text_begin, const char* text_end, ImDrawList* draw_list, float wrap_width, const ImVec2* cpu_clip_max) const { if (!text_end) text_end = text_begin + strlen(text_begin); - const float line_height = (float)Info->FontSize; - const float scale = size / (float)Info->FontSize; - const float tex_scale_x = 1.0f / (float)Common->ScaleW; - const float tex_scale_y = 1.0f / (float)(Common->ScaleH); - const float outline = (float)Info->Outline; + const float scale = size / FontSize; + const float line_height = FontSize * scale; // Align to be pixel perfect pos.x = (float)(int)pos.x + DisplayOffset.x; @@ -6304,10 +9375,17 @@ void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_re const bool word_wrap_enabled = (wrap_width > 0.0f); const char* word_wrap_eol = NULL; - const ImVec4 clip_rect = clip_rect_ref; + ImVec4 clip_rect = clip_rect_ref; + if (cpu_clip_max) + { + clip_rect.z = ImMin(clip_rect.z, cpu_clip_max->x); + clip_rect.w = ImMin(clip_rect.w, cpu_clip_max->y); + } float x = pos.x; float y = pos.y; + ImDrawVert* out_vertices = draw_list->vtx_write; + const char* s = text_begin; while (s < text_end) { @@ -6324,75 +9402,95 @@ void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_re if (s >= word_wrap_eol) { x = pos.x; - y += line_height * scale; + y += line_height; word_wrap_eol = NULL; // Wrapping skips upcoming blanks while (s < text_end) { const char c = *s; - if (c == ' ' || c == '\t') { s++; } else if (c == '\n') { s++; break; } else { break; } + if (ImCharIsSpace(c)) { s++; } else if (c == '\n') { s++; break; } else { break; } } continue; } } // Decode and advance source (handle unlikely UTF-8 decoding failure by skipping to the next byte) - unsigned int c; - const int bytes_count = ImTextCharFromUtf8(&c, s, text_end); - s += bytes_count > 0 ? bytes_count : 1; + unsigned int c = (unsigned int)*s; + if (c < 0x80) + { + s += 1; + } + else + { + s += ImTextCharFromUtf8(&c, s, text_end); + if (c == 0) + break; + } if (c == '\n') { x = pos.x; - y += line_height * scale; + y += line_height; continue; } float char_width = 0.0f; - if (c == '\t') + if (const Glyph* glyph = FindGlyph((unsigned short)c)) { - // FIXME: Better TAB handling - if (const FntGlyph* glyph = FindGlyph((unsigned short)' ')) - char_width += (glyph->XAdvance + Info->SpacingHoriz) * 4 * scale; - } - else if (const FntGlyph* glyph = FindGlyph((unsigned short)c)) - { - char_width = (glyph->XAdvance + Info->SpacingHoriz) * scale; - if (c != ' ') + char_width = glyph->XAdvance * scale; + if (c != ' ' && c != '\t') { // Clipping on Y is more likely - const float y1 = (float)(y + (glyph->YOffset + outline*2) * scale); - const float y2 = (float)(y1 + glyph->Height * scale); + float y1 = (float)(y + glyph->YOffset * scale); + float y2 = (float)(y1 + glyph->Height * scale); if (y1 <= clip_rect.w && y2 >= clip_rect.y) { - const float x1 = (float)(x + (glyph->XOffset + outline) * scale); - const float x2 = (float)(x1 + glyph->Width * scale); + float x1 = (float)(x + glyph->XOffset * scale); + float x2 = (float)(x1 + glyph->Width * scale); if (x1 <= clip_rect.z && x2 >= clip_rect.x) { // Render a character - const float s1 = (glyph->X) * tex_scale_x; - const float t1 = (glyph->Y) * tex_scale_y; - const float s2 = (glyph->X + glyph->Width) * tex_scale_x; - const float t2 = (glyph->Y + glyph->Height) * tex_scale_y; + float u1 = glyph->U0; + float v1 = glyph->V0; + float u2 = glyph->U1; + float v2 = glyph->V1; + // CPU side clipping used to fit text in their frame when the frame is too small. Only does clipping for axis aligned quad and in the "max" direction (bottom-right) + if (cpu_clip_max) + { + if (x2 > cpu_clip_max->x) + { + const float clip_tx = (cpu_clip_max->x - x1) / (x2 - x1); + x2 = cpu_clip_max->x; + u2 = u1 + clip_tx * (u2 - u1); + } + if (y2 > cpu_clip_max->y) + { + const float clip_ty = (cpu_clip_max->y - y1) / (y2 - y1); + y2 = cpu_clip_max->y; + v2 = v1 + clip_ty * (v2 - v1); + } + } + + // NB: we are not calling PrimRectUV() here because non-inlined causes too much overhead in a debug build. out_vertices[0].pos = ImVec2(x1, y1); - out_vertices[0].uv = ImVec2(s1, t1); + out_vertices[0].uv = ImVec2(u1, v1); out_vertices[0].col = col; out_vertices[1].pos = ImVec2(x2, y1); - out_vertices[1].uv = ImVec2(s2, t1); + out_vertices[1].uv = ImVec2(u2, v1); out_vertices[1].col = col; out_vertices[2].pos = ImVec2(x2, y2); - out_vertices[2].uv = ImVec2(s2, t2); + out_vertices[2].uv = ImVec2(u2, v2); out_vertices[2].col = col; out_vertices[3] = out_vertices[0]; out_vertices[4] = out_vertices[2]; out_vertices[5].pos = ImVec2(x1, y2); - out_vertices[5].uv = ImVec2(s1, t2); + out_vertices[5].uv = ImVec2(u1, v2); out_vertices[5].col = col; out_vertices += 6; @@ -6403,6 +9501,8 @@ void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_re x += char_width; } + + draw_list->vtx_write = out_vertices; } //----------------------------------------------------------------------------- @@ -6411,11 +9511,13 @@ void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_re #if defined(_MSC_VER) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS) +#ifndef _WINDOWS_ #define WIN32_LEAN_AND_MEAN #include +#endif // Win32 API clipboard implementation -static const char* GetClipboardTextFn_DefaultImpl() +static const char* GetClipboardTextFn_DefaultImpl() { static char* buf_local = NULL; if (buf_local) @@ -6425,12 +9527,16 @@ static const char* GetClipboardTextFn_DefaultImpl() } if (!OpenClipboard(NULL)) return NULL; - HANDLE buf_handle = GetClipboardData(CF_TEXT); - if (buf_handle == NULL) + HANDLE wbuf_handle = GetClipboardData(CF_UNICODETEXT); + if (wbuf_handle == NULL) return NULL; - if (char* buf_global = (char*)GlobalLock(buf_handle)) - buf_local = ImStrdup(buf_global); - GlobalUnlock(buf_handle); + if (ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle)) + { + int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1; + buf_local = (char*)ImGui::MemAlloc(buf_len * sizeof(char)); + ImTextStrToUtf8(buf_local, buf_len, wbuf_global, NULL); + } + GlobalUnlock(wbuf_handle); CloseClipboard(); return buf_local; } @@ -6440,51 +9546,91 @@ static void SetClipboardTextFn_DefaultImpl(const char* text) { if (!OpenClipboard(NULL)) return; - const char* text_end = text + strlen(text); - const int buf_length = (int)(text_end - text) + 1; - HGLOBAL buf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)buf_length * sizeof(char)); - if (buf_handle == NULL) + + const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1; + HGLOBAL wbuf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar)); + if (wbuf_handle == NULL) return; - char* buf_global = (char *)GlobalLock(buf_handle); - memcpy(buf_global, text, (size_t)(text_end - text)); - buf_global[text_end - text] = 0; - GlobalUnlock(buf_handle); + ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle); + ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL); + GlobalUnlock(wbuf_handle); EmptyClipboard(); - SetClipboardData(CF_TEXT, buf_handle); + SetClipboardData(CF_UNICODETEXT, wbuf_handle); CloseClipboard(); } #else // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers -static const char* GetClipboardTextFn_DefaultImpl() +static const char* GetClipboardTextFn_DefaultImpl() { - return GImGui.PrivateClipboard; + return GImGui->PrivateClipboard; } // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers static void SetClipboardTextFn_DefaultImpl(const char* text) { - if (GImGui.PrivateClipboard) + ImGuiState& g = *GImGui; + if (g.PrivateClipboard) { - ImGui::MemFree(GImGui.PrivateClipboard); - GImGui.PrivateClipboard = NULL; + ImGui::MemFree(g.PrivateClipboard); + g.PrivateClipboard = NULL; } const char* text_end = text + strlen(text); - GImGui.PrivateClipboard = (char*)ImGui::MemAlloc((size_t)(text_end - text) + 1); - memcpy(GImGui.PrivateClipboard, text, (size_t)(text_end - text)); - GImGui.PrivateClipboard[(size_t)(text_end - text)] = 0; + g.PrivateClipboard = (char*)ImGui::MemAlloc((size_t)(text_end - text) + 1); + memcpy(g.PrivateClipboard, text, (size_t)(text_end - text)); + g.PrivateClipboard[(size_t)(text_end - text)] = 0; } #endif +#if defined(_MSC_VER) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS) + +#ifndef _WINDOWS_ +#define WIN32_LEAN_AND_MEAN +#include +#endif +#include +#pragma comment(lib, "imm32") + +static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y) +{ + // Notify OS Input Method Editor of text input position + if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle) + if (HIMC himc = ImmGetContext(hwnd)) + { + COMPOSITIONFORM cf; + cf.ptCurrentPos.x = x; + cf.ptCurrentPos.y = y; + cf.dwStyle = CFS_FORCE_POSITION; + ImmSetCompositionWindow(himc, &cf); + } +} + +#else + +static void ImeSetInputScreenPosFn_DefaultImpl(int, int) +{ +} + +#endif + +#ifdef IMGUI_DISABLE_TEST_WINDOWS + +void ImGui::ShowUserGuide() {} +void ImGui::ShowStyleEditor(ImGuiStyle*) {} +void ImGui::ShowTestWindow(bool*) {} +void ImGui::ShowMetricsWindow(bool*) {} + +#else + //----------------------------------------------------------------------------- // HELP //----------------------------------------------------------------------------- void ImGui::ShowUserGuide() { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGui::BulletText("Double-click on title bar to collapse window."); ImGui::BulletText("Click and drag on lower right corner to resize window."); @@ -6508,10 +9654,10 @@ void ImGui::ShowUserGuide() void ImGui::ShowStyleEditor(ImGuiStyle* ref) { - ImGuiState& g = GImGui; + ImGuiState& g = *GImGui; ImGuiStyle& style = g.Style; - const ImGuiStyle def; + const ImGuiStyle def; // Default style if (ImGui::Button("Revert Style")) g.Style = ref ? *ref : def; @@ -6522,30 +9668,56 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) *ref = g.Style; } - ImGui::PushItemWidth(ImGui::GetWindowWidth()*0.55f); + ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.55f); if (ImGui::TreeNode("Sizes")) { ImGui::SliderFloat("Alpha", &style.Alpha, 0.20f, 1.0f, "%.2f"); // Not exposing zero here so user doesn't "lose" the UI. But application code could have a toggle to switch between zero and non-zero. ImGui::SliderFloat2("WindowPadding", (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 16.0f, "%.0f"); + ImGui::SliderFloat("ChildWindowRounding", &style.ChildWindowRounding, 0.0f, 16.0f, "%.0f"); ImGui::SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f"); + ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 16.0f, "%.0f"); ImGui::SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat("TreeNodeSpacing", &style.TreeNodeSpacing, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat("ScrollBarWidth", &style.ScrollBarWidth, 0.0f, 20.0f, "%.0f"); + ImGui::SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f"); + ImGui::SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 20.0f, "%.0f"); + ImGui::SliderFloat("ScrollbarWidth", &style.ScrollbarWidth, 1.0f, 20.0f, "%.0f"); + ImGui::SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 16.0f, "%.0f"); + ImGui::SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f"); ImGui::TreePop(); } if (ImGui::TreeNode("Colors")) { + static int output_dest = 0; + static bool output_only_modified = false; + if (ImGui::Button("Output Colors")) + { + if (output_dest == 0) + ImGui::LogToClipboard(); + else + ImGui::LogToTTY(); + ImGui::LogText("ImGuiStyle& style = ImGui::GetStyle();" STR_NEWLINE); + for (int i = 0; i < ImGuiCol_COUNT; i++) + { + const ImVec4& col = style.Colors[i]; + const char* name = ImGui::GetStyleColName(i); + if (!output_only_modified || memcmp(&col, (ref ? &ref->Colors[i] : &def.Colors[i]), sizeof(ImVec4)) != 0) + ImGui::LogText("style.Colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);" STR_NEWLINE, name, 22 - strlen(name), "", col.x, col.y, col.z, col.w); + } + ImGui::LogFinish(); + } + ImGui::SameLine(); ImGui::PushItemWidth(150); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY"); ImGui::PopItemWidth(); + ImGui::SameLine(); ImGui::Checkbox("Only Modified Fields", &output_only_modified); + static ImGuiColorEditMode edit_mode = ImGuiColorEditMode_RGB; ImGui::RadioButton("RGB", &edit_mode, ImGuiColorEditMode_RGB); ImGui::SameLine(); ImGui::RadioButton("HSV", &edit_mode, ImGuiColorEditMode_HSV); ImGui::SameLine(); ImGui::RadioButton("HEX", &edit_mode, ImGuiColorEditMode_HEX); + //ImGui::Text("Tip: Click on colored square to change edit mode."); static ImGuiTextFilter filter; filter.Draw("Filter colors", 200); @@ -6555,7 +9727,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::ColorEditMode(edit_mode); for (int i = 0; i < ImGuiCol_COUNT; i++) { - const char* name = GetStyleColorName(i); + const char* name = ImGui::GetStyleColName(i); if (!filter.PassFilter(name)) continue; ImGui::PushID(i); @@ -6572,20 +9744,6 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::TreePop(); } - /* - // Font scaling options - // Note that those are not actually part of the style. - if (ImGui::TreeNode("Font")) - { - static float window_scale = 1.0f; - ImGui::SliderFloat("window scale", &window_scale, 0.3f, 2.0f, "%.1f"); // scale only this window - ImGui::SliderFloat("font scale", &ImGui::GetIO().Font->Scale, 0.3f, 2.0f, "%.1f"); // scale only this font - ImGui::SliderFloat("global scale", &ImGui::GetIO().FontGlobalScale, 0.3f, 2.0f, "%.1f"); // scale everything - ImGui::SetWindowFontScale(window_scale); - ImGui::TreePop(); - } - */ - ImGui::PopItemWidth(); } @@ -6593,27 +9751,71 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) // SAMPLE CODE //----------------------------------------------------------------------------- -static void ShowExampleAppConsole(bool* open); -static void ShowExampleAppLongText(bool* open); -static void ShowExampleAppAutoResize(bool* open); +static void ShowExampleAppConsole(bool* opened); +static void ShowExampleAppLongText(bool* opened); +static void ShowExampleAppAutoResize(bool* opened); +static void ShowExampleAppFixedOverlay(bool* opened); +static void ShowExampleAppManipulatingWindowTitle(bool* opened); +static void ShowExampleAppCustomRendering(bool* opened); // Demonstrate ImGui features (unfortunately this makes this function a little bloated!) -void ImGui::ShowTestWindow(bool* open) +void ImGui::ShowTestWindow(bool* opened) { + // Examples apps + static bool show_app_metrics = false; + static bool show_app_console = false; + static bool show_app_long_text = false; + static bool show_app_auto_resize = false; + static bool show_app_fixed_overlay = false; + static bool show_app_custom_rendering = false; + static bool show_app_manipulating_window_title = false; + if (show_app_metrics) + ImGui::ShowMetricsWindow(&show_app_metrics); + if (show_app_console) + ShowExampleAppConsole(&show_app_console); + if (show_app_long_text) + ShowExampleAppLongText(&show_app_long_text); + if (show_app_auto_resize) + ShowExampleAppAutoResize(&show_app_auto_resize); + if (show_app_fixed_overlay) + ShowExampleAppFixedOverlay(&show_app_fixed_overlay); + if (show_app_manipulating_window_title) + ShowExampleAppManipulatingWindowTitle(&show_app_manipulating_window_title); + if (show_app_custom_rendering) + ShowExampleAppCustomRendering(&show_app_custom_rendering); + static bool no_titlebar = false; static bool no_border = true; static bool no_resize = false; static bool no_move = false; static bool no_scrollbar = false; - static float fill_alpha = 0.65f; + static bool no_collapse = false; + static float bg_alpha = 0.65f; - const ImGuiWindowFlags layout_flags = (no_titlebar ? ImGuiWindowFlags_NoTitleBar : 0) | (no_border ? 0 : ImGuiWindowFlags_ShowBorders) | (no_resize ? ImGuiWindowFlags_NoResize : 0) | (no_move ? ImGuiWindowFlags_NoMove : 0) | (no_scrollbar ? ImGuiWindowFlags_NoScrollbar : 0); - ImGui::Begin("ImGui Test", open, ImVec2(550,680), fill_alpha, layout_flags); - ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.65f); + // Demonstrate the various window flags. Typically you would just use the default. + ImGuiWindowFlags window_flags = 0; + if (no_titlebar) window_flags |= ImGuiWindowFlags_NoTitleBar; + if (!no_border) window_flags |= ImGuiWindowFlags_ShowBorders; + if (no_resize) window_flags |= ImGuiWindowFlags_NoResize; + if (no_move) window_flags |= ImGuiWindowFlags_NoMove; + if (no_scrollbar) window_flags |= ImGuiWindowFlags_NoScrollbar; + if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse; + if (!ImGui::Begin("ImGui Test", opened, ImVec2(550,680), bg_alpha, window_flags)) + { + // Early out if the window is collapsed, as an optimization. + ImGui::End(); + return; + } + + //ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.65f); // 2/3 of the space for widget and 1/3 for labels + ImGui::PushItemWidth(-140); // Right align, keep 140 pixels for labels ImGui::Text("ImGui says hello."); //ImGui::Text("MousePos (%g, %g)", ImGui::GetIO().MousePos.x, ImGui::GetIO().MousePos.y); //ImGui::Text("MouseWheel %d", ImGui::GetIO().MouseWheel); + //ImGui::Text("KeyMods %s%s%s", ImGui::GetIO().KeyCtrl ? "CTRL" : "", ImGui::GetIO().KeyShift ? "SHIFT" : "", ImGui::GetIO().KeyAlt? "ALT" : ""); + //ImGui::Text("WantCaptureMouse: %d", ImGui::GetIO().WantCaptureMouse); + //ImGui::Text("WantCaptureKeyboard: %d", ImGui::GetIO().WantCaptureKeyboard); ImGui::Spacing(); if (ImGui::CollapsingHeader("Help")) @@ -6628,15 +9830,42 @@ void ImGui::ShowTestWindow(bool* open) ImGui::Checkbox("no border", &no_border); ImGui::SameLine(300); ImGui::Checkbox("no resize", &no_resize); ImGui::Checkbox("no move", &no_move); ImGui::SameLine(150); - ImGui::Checkbox("no scrollbar", &no_scrollbar); - ImGui::SliderFloat("fill alpha", &fill_alpha, 0.0f, 1.0f); + ImGui::Checkbox("no scrollbar", &no_scrollbar); ImGui::SameLine(300); + ImGui::Checkbox("no collapse", &no_collapse); + ImGui::SliderFloat("bg alpha", &bg_alpha, 0.0f, 1.0f); - if (ImGui::TreeNode("Style Editor")) + if (ImGui::TreeNode("Style")) { ImGui::ShowStyleEditor(); ImGui::TreePop(); } + if (ImGui::TreeNode("Fonts", "Fonts (%d)", (int)ImGui::GetIO().Fonts->Fonts.size())) + { + ImGui::TextWrapped("Tip: Load fonts with GetIO().Fonts->AddFontFromFileTTF()."); + for (size_t i = 0; i < ImGui::GetIO().Fonts->Fonts.size(); i++) + { + ImFont* font = ImGui::GetIO().Fonts->Fonts[i]; + ImGui::BulletText("Font %d: %.2f pixels, %d glyphs", i, font->FontSize, font->Glyphs.size()); + ImGui::TreePush((void*)i); + ImGui::PushFont(font); + ImGui::Text("The quick brown fox jumps over the lazy dog"); + ImGui::PopFont(); + if (i > 0 && ImGui::Button("Set as default")) + { + ImGui::GetIO().Fonts->Fonts[i] = ImGui::GetIO().Fonts->Fonts[0]; + ImGui::GetIO().Fonts->Fonts[0] = font; + } + ImGui::SliderFloat("font scale", &font->Scale, 0.3f, 2.0f, "%.1f"); // scale only this font + ImGui::TreePop(); + } + static float window_scale = 1.0f; + ImGui::SliderFloat("this window scale", &window_scale, 0.3f, 2.0f, "%.1f"); // scale only this window + ImGui::SliderFloat("global scale", &ImGui::GetIO().FontGlobalScale, 0.3f, 2.0f, "%.1f"); // scale everything + ImGui::SetWindowFontScale(window_scale); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Logging")) { ImGui::LogButtons(); @@ -6674,7 +9903,9 @@ void ImGui::ShowTestWindow(bool* open) { ImGui::BulletText("Bullet point 1"); ImGui::BulletText("Bullet point 2\nOn multiple lines"); - ImGui::BulletText("Bullet point 3"); + ImGui::Bullet(); ImGui::Text("Bullet point 3 (two calls)"); + ImGui::Bullet(); ImGui::SmallButton("Button 1"); + ImGui::Bullet(); ImGui::SmallButton("Button 2"); ImGui::TreePop(); } @@ -6689,7 +9920,7 @@ void ImGui::ShowTestWindow(bool* open) if (ImGui::TreeNode("Word Wrapping")) { // Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility. - ImGui::TextWrapped("This is a long paragraph. The text should automatically wrap on the edge of the window. The current implementation follows simple rules that works for English and possibly other languages."); + ImGui::TextWrapped("This text should automatically wrap on the edge of the window. The current implementation for text wrapping follows simple rules that works for English and possibly other languages."); ImGui::Spacing(); static float wrap_width = 200.0f; @@ -6699,14 +9930,14 @@ void ImGui::ShowTestWindow(bool* open) ImGui::GetWindowDrawList()->AddRectFilled(ImGui::GetCursorScreenPos() + ImVec2(wrap_width, 0.0f), ImGui::GetCursorScreenPos() + ImVec2(wrap_width+10, ImGui::GetTextLineHeight()), 0xFFFF00FF); ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width); ImGui::Text("lazy dog. This paragraph is made to fit within %.0f pixels. The quick brown fox jumps over the lazy dog.", wrap_width); - ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemBoxMin(), ImGui::GetItemBoxMax(), 0xFF00FFFF); + ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), 0xFF00FFFF); ImGui::PopTextWrapPos(); ImGui::Text("Test paragraph 2:"); ImGui::GetWindowDrawList()->AddRectFilled(ImGui::GetCursorScreenPos() + ImVec2(wrap_width, 0.0f), ImGui::GetCursorScreenPos() + ImVec2(wrap_width+10, ImGui::GetTextLineHeight()), 0xFFFF00FF); ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width); ImGui::Text("aaaaaaaa bbbbbbbb, cccccccc,dddddddd. eeeeeeee ffffffff. gggggggg!hhhhhhhh"); - ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemBoxMin(), ImGui::GetItemBoxMax(), 0xFF00FFFF); + ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), 0xFF00FFFF); ImGui::PopTextWrapPos(); ImGui::TreePop(); @@ -6714,11 +9945,13 @@ void ImGui::ShowTestWindow(bool* open) if (ImGui::TreeNode("UTF-8 Text")) { - // UTF-8 test (need a suitable font, try extra_fonts/mplus* files for example) + // UTF-8 test with Japanese characters + // (needs a suitable font, try Arial Unicode or M+ fonts http://mplus-fonts.sourceforge.jp/mplus-outline-fonts/index-en.html) // Most compiler appears to support UTF-8 in source code (with Visual Studio you need to save your file as 'UTF-8 without signature') // However for the sake for maximum portability here we are *not* including raw UTF-8 character in this source file, instead we encode the string with hexadecimal constants. - // In your own application please be reasonable and use UTF-8 in the source or get the data from external files! :) - ImGui::TextWrapped("(CJK text will only appears if the font supports it. Please check in the extra_fonts/ folder if you intend to use non-ASCII characters. Note that characters values are preserved even if the font cannot be displayed, so you can safely copy & paste garbled characters.)"); + // In your own application be reasonable and use UTF-8 in source or retrieve the data from file system! + // Note that characters values are preserved even if the font cannot be displayed, so you can safely copy & paste garbled characters into another application. + ImGui::TextWrapped("CJK text will only appears if the font was loaded with the appropriate CJK character ranges. Call io.Font->LoadFromFileTTF() manually to load extra character ranges."); ImGui::Text("Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)"); ImGui::Text("Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)"); static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"; @@ -6726,6 +9959,157 @@ void ImGui::ShowTestWindow(bool* open) ImGui::TreePop(); } + if (ImGui::TreeNode("Clipping")) + { + static ImVec2 size(80, 20); + ImGui::TextWrapped("On a per-widget basis we are occasionally clipping text if it won't fit in its frame."); + ImGui::SliderFloat2("size", (float*)&size, 5.0f, 200.0f); + ImGui::Button("Line 1 hello\nLine 2 clip me!", size); + ImGui::TextWrapped("Otherwise we are doing coarser clipping + passing a scissor rectangle to the renderer. The system is designed to try minimizing both execution and rendering cost."); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Images")) + { + ImGui::TextWrapped("Below we are displaying the font texture (which is the only texture we have access to in this demo). Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. Hover the texture for a zoomed view!"); + ImVec2 tex_screen_pos = ImGui::GetCursorScreenPos(); + float tex_w = (float)ImGui::GetIO().Fonts->TexWidth; + float tex_h = (float)ImGui::GetIO().Fonts->TexHeight; + ImTextureID tex_id = ImGui::GetIO().Fonts->TexID; + ImGui::Image(tex_id, ImVec2(tex_w, tex_h), ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(255,255,255,128)); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + float focus_sz = 32.0f; + float focus_x = ImClamp(ImGui::GetMousePos().x - tex_screen_pos.x - focus_sz * 0.5f, 0.0f, tex_w - focus_sz); + float focus_y = ImClamp(ImGui::GetMousePos().y - tex_screen_pos.y - focus_sz * 0.5f, 0.0f, tex_h - focus_sz); + ImGui::Text("Min: (%.2f, %.2f)", focus_x, focus_y); + ImGui::Text("Max: (%.2f, %.2f)", focus_x + focus_sz, focus_y + focus_sz); + ImVec2 uv0 = ImVec2((focus_x) / tex_w, (focus_y) / tex_h); + ImVec2 uv1 = ImVec2((focus_x + focus_sz) / tex_w, (focus_y + focus_sz) / tex_h); + ImGui::Image(tex_id, ImVec2(128,128), uv0, uv1, ImColor(255,255,255,255), ImColor(255,255,255,128)); + ImGui::EndTooltip(); + } + ImGui::TextWrapped("And now some textured buttons.."); + static int pressed_count = 0; + for (int i = 0; i < 8; i++) + { + if (i > 0) + ImGui::SameLine(); + ImGui::PushID(i); + int frame_padding = -1 + i; // -1 padding uses default padding + if (ImGui::ImageButton(tex_id, ImVec2(32,32), ImVec2(0,0), ImVec2(32.0f/tex_w,32/tex_h), frame_padding)) + pressed_count += 1; + ImGui::PopID(); + } + ImGui::Text("Pressed %d times.", pressed_count); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Selectables")) + { + if (ImGui::TreeNode("Basic")) + { + static bool selected[3] = { false, true, false }; + ImGui::Selectable("1. I am selectable", &selected[0]); + ImGui::Selectable("2. I am selectable", &selected[1]); + ImGui::Text("3. I am not selectable"); + ImGui::Selectable("4. I am selectable", &selected[2]); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Rendering more text into the same block")) + { + static bool selected[3] = { false, false, false }; + ImGui::Selectable("main.c", &selected[0]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); + ImGui::Selectable("Hello.cpp", &selected[1]); ImGui::SameLine(300); ImGui::Text("12,345 bytes"); + ImGui::Selectable("Hello.h", &selected[2]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Grid")) + { + static bool selected[16] = { true, false, false, false, false, true, false, false, false, false, true, false, false, false, false, true }; + for (int i = 0; i < 16; i++) + { + ImGui::PushID(i); + if (ImGui::Selectable("Me", &selected[i], ImVec2(50,50))) + { + int x = i % 4, y = i / 4; + if (x > 0) selected[i - 1] ^= 1; + if (x < 3) selected[i + 1] ^= 1; + if (y > 0) selected[i - 4] ^= 1; + if (y < 3) selected[i + 4] ^= 1; + } + if ((i % 4) < 3) ImGui::SameLine(); + ImGui::PopID(); + } + ImGui::TreePop(); + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Popup, Menus")) + { + static int selected_fish = -1; + const char* names[] = { "Bream", "Haddock", "Mackerel", "Pollock", "Tilefish" }; + static bool toggles[] = { true, false, false, false, false }; + + { + static bool popup_open = false; + if (ImGui::Button("Select..")) + popup_open = true; + ImGui::SameLine(); + ImGui::Text(selected_fish == -1 ? "" : names[selected_fish]); + if (popup_open) + { + ImGui::BeginPopup(&popup_open); + ImGui::Text("Aquarium"); + ImGui::Separator(); + for (int i = 0; i < IM_ARRAYSIZE(names); i++) + { + if (ImGui::Selectable(names[i])) + { + selected_fish = i; + popup_open = false; + } + } + ImGui::EndPopup(); + } + } + { + static bool popup_open = false; + if (ImGui::Button("Toggle..")) + popup_open = true; + if (popup_open) + { + ImGui::BeginPopup(&popup_open); + for (int i = 0; i < IM_ARRAYSIZE(names); i++) + if (ImGui::MenuItem(names[i], "", &toggles[i])) + popup_open = false; + + ImGui::Separator(); + ImGui::Text("Tooltip here"); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("I am a tooltip over a popup"); + + ImGui::EndPopup(); + } + } + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Filtered Text Input")) + { + static char buf1[64] = ""; ImGui::InputText("default", buf1, 64); + static char buf2[64] = ""; ImGui::InputText("decimal", buf2, 64, ImGuiInputTextFlags_CharsDecimal); + static char buf3[64] = ""; ImGui::InputText("hexadecimal", buf3, 64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); + static char buf4[64] = ""; ImGui::InputText("uppercase", buf4, 64, ImGuiInputTextFlags_CharsUppercase); + static char buf5[64] = ""; ImGui::InputText("no blank", buf5, 64, ImGuiInputTextFlags_CharsNoBlank); + struct TextFilters { static int FilterImGuiLetters(ImGuiTextEditCallbackData* data) { if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar)) return 0; return 1; } }; + static char buf6[64] = ""; ImGui::InputText("\"imgui\" letters", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); + ImGui::TreePop(); + } + static bool check = true; ImGui::Checkbox("checkbox", &check); @@ -6734,7 +10118,20 @@ void ImGui::ShowTestWindow(bool* open) ImGui::RadioButton("radio b", &e, 1); ImGui::SameLine(); ImGui::RadioButton("radio c", &e, 2); - ImGui::Text("Hover me"); + // Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style. + for (int i = 0; i < 7; i++) + { + if (i > 0) ImGui::SameLine(); + ImGui::PushID(i); + ImGui::PushStyleColor(ImGuiCol_Button, ImColor::HSV(i/7.0f, 0.6f, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImColor::HSV(i/7.0f, 0.7f, 0.7f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImColor::HSV(i/7.0f, 0.8f, 0.8f)); + ImGui::Button("Click"); + ImGui::PopStyleColor(3); + ImGui::PopID(); + } + + ImGui::Text("Hover over me"); if (ImGui::IsItemHovered()) ImGui::SetTooltip("I am a tooltip"); @@ -6760,6 +10157,8 @@ void ImGui::ShowTestWindow(bool* open) ImGui::Separator(); + ImGui::LabelText("label", "Value"); + static int item = 1; ImGui::Combo("combo", &item, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); @@ -6767,51 +10166,183 @@ void ImGui::ShowTestWindow(bool* open) static int item2 = -1; ImGui::Combo("combo scroll", &item2, items, IM_ARRAYSIZE(items)); - static char str0[128] = "Hello, world!"; - static int i0=123; - static float f0=0.001f; - ImGui::InputText("string", str0, IM_ARRAYSIZE(str0)); - ImGui::InputInt("input int", &i0); - ImGui::InputFloat("input float", &f0, 0.01f, 1.0f); + { + static char str0[128] = "Hello, world!"; + static int i0=123; + static float f0=0.001f; + ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0)); + ImGui::InputInt("input int", &i0); + ImGui::InputFloat("input float", &f0, 0.01f, 1.0f); - //static float vec2a[3] = { 0.10f, 0.20f }; - //ImGui::InputFloat2("input float2", vec2a); + static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; + ImGui::InputFloat3("input float3", vec4a); + } - static float vec3a[3] = { 0.10f, 0.20f, 0.30f }; - ImGui::InputFloat3("input float3", vec3a); + { + static int i1=50; + static int i2=42; + ImGui::DragInt("drag int", &i1, 1); + ImGui::SameLine(); + ImGui::TextColored(ImColor(170,170,170,255), "(?)"); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Click and drag to edit value.\nHold SHIFT/ALT for faster/slower edit.\nDouble-click or CTRL+click to input text"); - //static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; - //ImGui::InputFloat4("input float4", vec4a); + ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%.0f%%"); - static int i1=0; - static int i2=42; - ImGui::SliderInt("int 0..3", &i1, 0, 3); - ImGui::SliderInt("int -100..100", &i2, -100, 100); + static float f1=1.00f; + static float f2=0.0067f; + ImGui::DragFloat("drag float", &f1, 1.0f); + ImGui::DragFloat("drag small float", &f2, 0.0001f, 0.0f, 0.0f, "%.06f ns"); + } - static float f1=1.123f; - static float f2=0; - static float f3=0; - static float f4=123456789.0f; - ImGui::SliderFloat("float", &f1, 0.0f, 2.0f); - ImGui::SliderFloat("log float", &f2, 0.0f, 10.0f, "%.4f", 2.0f); - ImGui::SliderFloat("signed log float", &f3, -10.0f, 10.0f, "%.4f", 3.0f); - ImGui::SliderFloat("unbound float", &f4, -FLT_MAX, FLT_MAX, "%.4f"); - static float angle = 0.0f; - ImGui::SliderAngle("angle", &angle); + { + static int i1=0; + //static int i2=42; + ImGui::SliderInt("slider int 0..3", &i1, 0, 3); + //ImGui::SliderInt("slider int -100..100", &i2, -100, 100); - //static float vec2b[3] = { 0.10f, 0.20f }; - //ImGui::SliderFloat2("slider float2", vec2b, 0.0f, 1.0f); - - static float vec3b[3] = { 0.10f, 0.20f, 0.30f }; - ImGui::SliderFloat3("slider float3", vec3b, 0.0f, 1.0f); - - //static float vec4b[4] = { 0.10f, 0.20f, 0.30f, 0.40f }; - //ImGui::SliderFloat4("slider float4", vec4b, 0.0f, 1.0f); + static float f1=0.123f; + static float f2=0.0f; + ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f"); + ImGui::SliderFloat("slider log float", &f2, -10.0f, 10.0f, "%.4f", 3.0f); + static float angle = 0.0f; + ImGui::SliderAngle("slider angle", &angle); + } static float col1[3] = { 1.0f,0.0f,0.2f }; static float col2[4] = { 0.4f,0.7f,0.0f,0.5f }; ImGui::ColorEdit3("color 1", col1); ImGui::ColorEdit4("color 2", col2); + + const char* listbox_items[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; + static int listbox_item_current = 1; + ImGui::ListBox("listbox\n(single select)", &listbox_item_current, listbox_items, IM_ARRAYSIZE(listbox_items), 4); + + //static int listbox_item_current2 = 2; + //ImGui::PushItemWidth(-1); + //ImGui::ListBox("##listbox2", &listbox_item_current2, listbox_items, IM_ARRAYSIZE(listbox_items), 4); + //ImGui::PopItemWidth(); + + if (ImGui::TreeNode("Multi-component Widgets")) + { + ImGui::Unindent(); + + static float vec4f[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; + static int vec4i[4] = { 1, 5, 100, 255 }; + + ImGui::InputFloat2("input float2", vec4f); + ImGui::DragFloat2("drag float2", vec4f, 0.01f, 0.0f, 1.0f); + ImGui::SliderFloat2("slider float2", vec4f, 0.0f, 1.0f); + ImGui::DragInt2("drag int2", vec4i, 1, 0, 255); + ImGui::InputInt2("input int2", vec4i); + ImGui::SliderInt2("slider int2", vec4i, 0, 255); + + ImGui::InputFloat3("input float3", vec4f); + ImGui::DragFloat3("drag float3", vec4f, 0.01f, 0.0f, 1.0f); + ImGui::SliderFloat3("slider float3", vec4f, 0.0f, 1.0f); + ImGui::DragInt3("drag int3", vec4i, 1, 0, 255); + ImGui::InputInt3("input int3", vec4i); + ImGui::SliderInt3("slider int3", vec4i, 0, 255); + + ImGui::InputFloat4("input float4", vec4f); + ImGui::DragFloat4("drag float4", vec4f, 0.01f, 0.0f, 1.0f); + ImGui::SliderFloat4("slider float4", vec4f, 0.0f, 1.0f); + ImGui::InputInt4("input int4", vec4i); + ImGui::DragInt4("drag int4", vec4i, 1, 0, 255); + ImGui::SliderInt4("slider int4", vec4i, 0, 255); + + ImGui::Indent(); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Vertical Sliders")) + { + ImGui::Unindent(); + const float spacing = 4; + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing)); + + static int int_value = 0; + ImGui::VSliderInt("##int", ImVec2(18,160), &int_value, 0, 5); + ImGui::SameLine(); + + static float values[7] = { 0.0f, 0.60f, 0.35f, 0.9f, 0.70f, 0.20f, 0.0f }; + ImGui::PushID("set1"); + for (int i = 0; i < 7; i++) + { + if (i > 0) ImGui::SameLine(); + ImGui::PushID(i); + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImColor::HSV(i/7.0f, 0.5f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImColor::HSV(i/7.0f, 0.6f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImColor::HSV(i/7.0f, 0.7f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImColor::HSV(i/7.0f, 0.9f, 0.9f)); + ImGui::VSliderFloat("##v", ImVec2(18,160), &values[i], 0.0f, 1.0f, ""); + if (ImGui::IsItemActive() || ImGui::IsItemHovered()) + ImGui::SetTooltip("%.3f", values[i]); + ImGui::PopStyleColor(4); + ImGui::PopID(); + } + ImGui::PopID(); + + ImGui::SameLine(); + ImGui::PushID("set2"); + static float values2[4] = { 0.20f, 0.80f, 0.40f, 0.25f }; + const int rows = 3; + const ImVec2 small_slider_size(18, (160.0f-(rows-1)*spacing)/rows); + for (int nx = 0; nx < 4; nx++) + { + if (nx > 0) ImGui::SameLine(); + ImGui::BeginGroup(); + for (int ny = 0; ny < rows; ny++) + { + ImGui::PushID(nx*rows+ny); + ImGui::VSliderFloat("##v", small_slider_size, &values2[nx], 0.0f, 1.0f, ""); + if (ImGui::IsItemActive() || ImGui::IsItemHovered()) + ImGui::SetTooltip("%.3f", values2[nx]); + ImGui::PopID(); + } + ImGui::EndGroup(); + } + ImGui::PopID(); + + ImGui::SameLine(); + ImGui::PushID("set3"); + for (int i = 0; i < 4; i++) + { + if (i > 0) ImGui::SameLine(); + ImGui::PushID(i); + ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40); + ImGui::VSliderFloat("##v", ImVec2(40,160), &values[i], 0.0f, 1.0f, "%.2f"); + ImGui::PopStyleVar(); + ImGui::PopID(); + } + ImGui::PopID(); + ImGui::PopStyleVar(); + + ImGui::Indent(); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Dragging")) + { + // You can use ImGui::GetItemActiveDragDelta() to query for the dragged amount on any widget. + static ImVec2 value_raw(0.0f, 0.0f); + static ImVec2 value_with_lock_threshold(0.0f, 0.0f); + ImGui::Button("Drag Me"); + if (ImGui::IsItemActive()) + { + value_raw = ImGui::GetMouseDragDelta(0, 0.0f); + value_with_lock_threshold = ImGui::GetMouseDragDelta(0); + //ImGui::SetTooltip("Delta: %.1f, %.1f", value.x, value.y); + + // Draw a line between the button and the mouse cursor + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + draw_list->PushClipRectFullScreen(); + draw_list->AddLine(ImGui::CalcItemRectClosestPoint(ImGui::GetIO().MousePos, true, -2.0f), ImGui::GetIO().MousePos, ImColor(ImGui::GetStyle().Colors[ImGuiCol_Button]), 4.0f); + draw_list->PopClipRect(); + } + ImGui::SameLine(); ImGui::Text("Raw (%.1f, %.1f), WithLockThresold (%.1f, %.1f)", value_raw.x, value_raw.y, value_with_lock_threshold.x, value_with_lock_threshold.y); + ImGui::TreePop(); + } } if (ImGui::CollapsingHeader("Graphs widgets")) @@ -6820,7 +10351,7 @@ void ImGui::ShowTestWindow(bool* open) ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr)); static bool pause; - static ImVector values; if (values.empty()) { values.resize(100); memset(&values.front(), 0, values.size()*sizeof(float)); } + static ImVector values; if (values.empty()) { values.resize(90); memset(&values.front(), 0, values.size()*sizeof(float)); } static size_t values_offset = 0; if (!pause) { @@ -6835,69 +10366,183 @@ void ImGui::ShowTestWindow(bool* open) phase += 0.10f*values_offset; } } - ImGui::PlotLines("Frame Times", &values.front(), (int)values.size(), (int)values_offset, "avg 0.0", -1.0f, 1.0f, ImVec2(0,70)); - - ImGui::SameLine(); ImGui::Checkbox("pause", &pause); - ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0,70)); + ImGui::PlotLines("##Graph", &values.front(), (int)values.size(), (int)values_offset, "avg 0.0", -1.0f, 1.0f, ImVec2(0,80)); + ImGui::SameLine(0, (int)ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::BeginGroup(); + ImGui::Text("Graph"); + ImGui::Checkbox("pause", &pause); + ImGui::EndGroup(); + ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0,80)); } - if (ImGui::CollapsingHeader("Widgets on same line")) + if (ImGui::CollapsingHeader("Layout")) { - // Text - ImGui::Text("Hello"); - ImGui::SameLine(); - ImGui::Text("World"); + if (ImGui::TreeNode("Widgets Alignment")) + { + static float f = 0.0f; + ImGui::Text("Fixed: 100 pixels"); + ImGui::PushItemWidth(100); + ImGui::InputFloat("float##1", &f); + ImGui::PopItemWidth(); - // Button - if (ImGui::Button("Banana")) printf("Pressed!\n"); - ImGui::SameLine(); - ImGui::Button("Apple"); - ImGui::SameLine(); - ImGui::Button("Corniflower"); + ImGui::Text("Proportional: 50%% of window width"); + ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.5f); + ImGui::InputFloat("float##2", &f); + ImGui::PopItemWidth(); - // Button - ImGui::SmallButton("Banana"); - ImGui::SameLine(); - ImGui::SmallButton("Apple"); - ImGui::SameLine(); - ImGui::SmallButton("Corniflower"); - ImGui::SameLine(); - ImGui::Text("Small buttons fit in a text block"); + ImGui::Text("Right-aligned: Leave 100 pixels for label"); + ImGui::PushItemWidth(-100); + ImGui::InputFloat("float##3", &f); + ImGui::PopItemWidth(); - // Checkbox - static bool c1=false,c2=false,c3=false,c4=false; - ImGui::Checkbox("My", &c1); - ImGui::SameLine(); - ImGui::Checkbox("Tailor", &c2); - ImGui::SameLine(); - ImGui::Checkbox("Is", &c3); - ImGui::SameLine(); - ImGui::Checkbox("Rich", &c4); + ImGui::TreePop(); + } - // SliderFloat - static float f0=1.0f, f1=2.0f, f2=3.0f; - ImGui::PushItemWidth(80); - ImGui::SliderFloat("f0", &f0, 0.0f,5.0f); - ImGui::SameLine(); - ImGui::SliderFloat("f1", &f1, 0.0f,5.0f); - ImGui::SameLine(); - ImGui::SliderFloat("f2", &f2, 0.0f,5.0f); + if (ImGui::TreeNode("Basic Horizontal Layout")) + { + ImGui::TextWrapped("(Use ImGui::SameLine() to keep adding items to the right of the preceeding item)"); - // InputText - static char s0[128] = "one", s1[128] = "two", s2[128] = "three"; - ImGui::InputText("s0", s0, 128); - ImGui::SameLine(); - ImGui::InputText("s1", s1, 128); - ImGui::SameLine(); - ImGui::InputText("s2", s2, 128); + // Text + ImGui::Text("Two items: Hello"); + ImGui::SameLine(); + ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); - // LabelText - ImGui::LabelText("l0", "one"); - ImGui::SameLine(); - ImGui::LabelText("l0", "two"); - ImGui::SameLine(); - ImGui::LabelText("l0", "three"); - ImGui::PopItemWidth(); + // Adjust spacing + ImGui::Text("More spacing: Hello"); + ImGui::SameLine(0, 20); + ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); + + // Button + ImGui::AlignFirstTextHeightToWidgets(); + ImGui::Text("Normal buttons"); ImGui::SameLine(); + ImGui::Button("Banana"); ImGui::SameLine(); + ImGui::Button("Apple"); ImGui::SameLine(); + ImGui::Button("Corniflower"); + + // Button + ImGui::Text("Small buttons"); ImGui::SameLine(); + ImGui::SmallButton("Like this one"); ImGui::SameLine(); + ImGui::Text("can fit within a text block."); + + // Aligned to arbitrary position. Easy/cheap column. + ImGui::Text("Aligned"); + ImGui::SameLine(150); ImGui::Text("x=150"); + ImGui::SameLine(300); ImGui::Text("x=300"); + ImGui::Text("Aligned"); + ImGui::SameLine(150); ImGui::SmallButton("x=150"); + ImGui::SameLine(300); ImGui::SmallButton("x=300"); + + // Checkbox + static bool c1=false,c2=false,c3=false,c4=false; + ImGui::Checkbox("My", &c1); ImGui::SameLine(); + ImGui::Checkbox("Tailor", &c2); ImGui::SameLine(); + ImGui::Checkbox("Is", &c3); ImGui::SameLine(); + ImGui::Checkbox("Rich", &c4); + + // Various + static float f0=1.0f, f1=2.0f, f2=3.0f; + ImGui::PushItemWidth(80); + const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD" }; + static int item = -1; + ImGui::Combo("Combo", &item, items, IM_ARRAYSIZE(items)); ImGui::SameLine(); + ImGui::SliderFloat("X", &f0, 0.0f,5.0f); ImGui::SameLine(); + ImGui::SliderFloat("Y", &f1, 0.0f,5.0f); ImGui::SameLine(); + ImGui::SliderFloat("Z", &f2, 0.0f,5.0f); + ImGui::PopItemWidth(); + + ImGui::PushItemWidth(80); + ImGui::Text("Lists:"); + static int selection[4] = { 0, 1, 2, 3 }; + for (int i = 0; i < 4; i++) + { + if (i > 0) ImGui::SameLine(); + ImGui::PushID(i); + ImGui::ListBox("", &selection[i], items, IM_ARRAYSIZE(items)); + ImGui::PopID(); + //if (ImGui::IsItemHovered()) ImGui::SetTooltip("ListBox %d hovered", i); + } + ImGui::PopItemWidth(); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Groups")) + { + ImGui::TextWrapped("(Using ImGui::BeginGroup()/EndGroup() to layout items)"); + + ImVec2 size; + ImGui::BeginGroup(); + { + ImGui::BeginGroup(); + ImGui::Button("AAA"); + ImGui::SameLine(); + ImGui::Button("BBB"); + ImGui::SameLine(); + ImGui::BeginGroup(); + ImGui::Button("CCC"); + ImGui::Button("DDD"); + ImGui::EndGroup(); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Group hovered"); + ImGui::SameLine(); + ImGui::Button("EEE"); + ImGui::EndGroup(); + + // Capture the group size and create widgets using the same size + size = ImGui::GetItemRectSize(); + const float values[5] = { 0.5f, 0.20f, 0.80f, 0.60f, 0.25f }; + ImGui::PlotHistogram("##values", values, IM_ARRAYSIZE(values), 0, NULL, 0.0f, 1.0f, size); + } + ImGui::Button("ACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x)*0.5f,size.y)); + ImGui::SameLine(); + ImGui::Button("REACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x)*0.5f,size.y)); + ImGui::EndGroup(); + ImGui::SameLine(); + + ImGui::Button("LEVERAGE\nBUZZWORD", size); + ImGui::SameLine(); + + ImGui::ListBoxHeader("List", size); + ImGui::Selectable("Selected", true); + ImGui::Selectable("Not Selected", false); + ImGui::ListBoxFooter(); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Text Baseline Alignment")) + { + ImGui::TextWrapped("(This is testing the vertical alignment that occurs on text to keep it at the same baseline as widgets. Lines only composed of text or \"small\" widgets fit in less vertical spaces than lines with normal widgets)"); + + ImGui::Text("One\nTwo\nThree"); ImGui::SameLine(); + ImGui::Text("Hello\nWorld"); ImGui::SameLine(); + ImGui::Text("Banana"); + + ImGui::Text("Banana"); ImGui::SameLine(); + ImGui::Text("Hello\nWorld"); ImGui::SameLine(); + ImGui::Text("One\nTwo\nThree"); + + ImGui::Button("HOP"); ImGui::SameLine(); + ImGui::Text("Banana"); ImGui::SameLine(); + ImGui::Text("Hello\nWorld"); ImGui::SameLine(); + ImGui::Text("Banana"); + + ImGui::Button("HOP"); ImGui::SameLine(); + ImGui::Text("Hello\nWorld"); ImGui::SameLine(); + ImGui::Text("Banana"); + + ImGui::Button("TEST"); ImGui::SameLine(); + ImGui::Text("TEST"); ImGui::SameLine(); + ImGui::SmallButton("TEST"); + + ImGui::AlignFirstTextHeightToWidgets(); // If your line starts with text, call this to align it to upcoming widgets. + ImGui::Text("Text aligned to Widget"); ImGui::SameLine(); + ImGui::Button("Widget"); ImGui::SameLine(); + ImGui::Text("Widget"); ImGui::SameLine(); + ImGui::SmallButton("Widget"); + + ImGui::TreePop(); + } } if (ImGui::CollapsingHeader("Child regions")) @@ -6909,7 +10554,7 @@ void ImGui::ShowTestWindow(bool* open) ImGui::PushItemWidth(100); goto_line |= ImGui::InputInt("##Line", &line, 0, 0, ImGuiInputTextFlags_EnterReturnsTrue); ImGui::PopItemWidth(); - ImGui::BeginChild("Sub1", ImVec2(ImGui::GetWindowWidth()*0.5f,300)); + ImGui::BeginChild("Sub1", ImVec2(ImGui::GetWindowWidth() * 0.5f,300)); for (int i = 0; i < 100; i++) { ImGui::Text("%04d: scrollable region", i); @@ -6922,49 +10567,78 @@ void ImGui::ShowTestWindow(bool* open) ImGui::SameLine(); + ImGui::PushStyleVar(ImGuiStyleVar_ChildWindowRounding, 5.0f); ImGui::BeginChild("Sub2", ImVec2(0,300), true); ImGui::Text("With border"); ImGui::Columns(2); for (int i = 0; i < 100; i++) { + if (i == 50) + ImGui::NextColumn(); char buf[32]; ImFormatString(buf, IM_ARRAYSIZE(buf), "%08x", i*5731); ImGui::Button(buf); - ImGui::NextColumn(); } ImGui::EndChild(); + ImGui::PopStyleVar(); } if (ImGui::CollapsingHeader("Columns")) { - ImGui::Columns(4, "data", true); + // Basic columns + ImGui::Text("Basic:"); + ImGui::Columns(4, "mycolumns"); + ImGui::Separator(); ImGui::Text("ID"); ImGui::NextColumn(); ImGui::Text("Name"); ImGui::NextColumn(); ImGui::Text("Path"); ImGui::NextColumn(); ImGui::Text("Flags"); ImGui::NextColumn(); ImGui::Separator(); - - ImGui::Text("0000"); ImGui::NextColumn(); - ImGui::Text("Robert"); ImGui::NextColumn(); - ImGui::Text("/path/robert"); ImGui::NextColumn(); - ImGui::Text("...."); ImGui::NextColumn(); - - ImGui::Text("0001"); ImGui::NextColumn(); - ImGui::Text("Stephanie"); ImGui::NextColumn(); - ImGui::Text("/path/stephanie"); ImGui::NextColumn(); - ImGui::Text("line 1"); ImGui::Text("line 2"); ImGui::NextColumn(); // two lines, two items - - ImGui::Text("0002"); ImGui::NextColumn(); - ImGui::Text("C64"); ImGui::NextColumn(); - ImGui::Text("/path/computer"); ImGui::NextColumn(); - ImGui::Text("...."); ImGui::NextColumn(); + const char* names[3] = { "Robert", "Stephanie", "C64" }; + const char* paths[3] = { "/path/robert", "/path/stephanie", "/path/computer" }; + for (int i = 0; i < 3; i++) + { + ImGui::Text("%04d", i); ImGui::NextColumn(); + ImGui::Text(names[i]); ImGui::NextColumn(); + ImGui::Text(paths[i]); ImGui::NextColumn(); + ImGui::Text("...."); ImGui::NextColumn(); + } ImGui::Columns(1); ImGui::Separator(); + ImGui::Spacing(); + // Scrolling columns + /* + ImGui::Text("Scrolling:"); + ImGui::BeginChild("##header", ImVec2(0, ImGui::GetTextLineHeightWithSpacing()+ImGui::GetStyle().ItemSpacing.y)); + ImGui::Columns(3); + ImGui::Text("ID"); ImGui::NextColumn(); + ImGui::Text("Name"); ImGui::NextColumn(); + ImGui::Text("Path"); ImGui::NextColumn(); + ImGui::Columns(1); + ImGui::Separator(); + ImGui::EndChild(); + ImGui::BeginChild("##scrollingregion", ImVec2(0, 60)); + ImGui::Columns(3); + for (int i = 0; i < 10; i++) + { + ImGui::Text("%04d", i); ImGui::NextColumn(); + ImGui::Text("Foobar"); ImGui::NextColumn(); + ImGui::Text("/path/foobar/%04d/", i); ImGui::NextColumn(); + } + ImGui::Columns(1); + ImGui::EndChild(); + + ImGui::Separator(); + ImGui::Spacing(); + */ + + // Create multiple items in a same cell before switching to next column + ImGui::Text("Mixed items:"); ImGui::Columns(3, "mixed"); + ImGui::Separator(); - // Create multiple items in a same cell because switching to next column static int e = 0; ImGui::Text("Hello"); ImGui::Button("Banana"); @@ -6974,32 +10648,41 @@ void ImGui::ShowTestWindow(bool* open) ImGui::Text("ImGui"); ImGui::Button("Apple"); ImGui::RadioButton("radio b", &e, 1); + static float foo = 1.0f; + ImGui::InputFloat("red", &foo, 0.05f, 0, 3); ImGui::Text("An extra line here."); ImGui::NextColumn(); - ImGui::Text("World!"); + ImGui::Text("Sailor"); ImGui::Button("Corniflower"); ImGui::RadioButton("radio c", &e, 2); + static float bar = 1.0f; + ImGui::InputFloat("blue", &bar, 0.05f, 0, 3); ImGui::NextColumn(); if (ImGui::CollapsingHeader("Category A")) ImGui::Text("Blah blah blah"); ImGui::NextColumn(); if (ImGui::CollapsingHeader("Category B")) ImGui::Text("Blah blah blah"); ImGui::NextColumn(); if (ImGui::CollapsingHeader("Category C")) ImGui::Text("Blah blah blah"); ImGui::NextColumn(); - ImGui::Columns(1); ImGui::Separator(); + ImGui::Spacing(); - ImGui::Columns(2, "multiple components"); - static float foo = 1.0f; - ImGui::InputFloat("red", &foo, 0.05f, 0, 3); ImGui::NextColumn(); - static float bar = 1.0f; - ImGui::InputFloat("blue", &bar, 0.05f, 0, 3); ImGui::NextColumn(); + // Tree items + ImGui::Text("Tree items:"); + ImGui::Columns(2, "tree items"); + ImGui::Separator(); + if (ImGui::TreeNode("Hello")) { ImGui::BulletText("Sailor"); ImGui::TreePop(); } ImGui::NextColumn(); + if (ImGui::TreeNode("Bonjour")) { ImGui::BulletText("Marin"); ImGui::TreePop(); } ImGui::NextColumn(); ImGui::Columns(1); ImGui::Separator(); - - ImGui::Columns(2, "word wrapping"); + ImGui::Spacing(); + + // Word-wrapping + ImGui::Text("Word-wrapping:"); + ImGui::Columns(2, "word-wrapping"); + ImGui::Separator(); ImGui::TextWrapped("The quick brown fox jumps over the lazy dog."); ImGui::Text("Hello Left"); ImGui::NextColumn(); @@ -7008,34 +10691,29 @@ void ImGui::ShowTestWindow(bool* open) ImGui::Columns(1); ImGui::Separator(); + ImGui::Spacing(); if (ImGui::TreeNode("Inside a tree..")) { if (ImGui::TreeNode("node 1 (with borders)")) { ImGui::Columns(4); - ImGui::Text("aaa"); ImGui::NextColumn(); - ImGui::Text("bbb"); ImGui::NextColumn(); - ImGui::Text("ccc"); ImGui::NextColumn(); - ImGui::Text("ddd"); ImGui::NextColumn(); - ImGui::Text("eee"); ImGui::NextColumn(); - ImGui::Text("fff"); ImGui::NextColumn(); - ImGui::Text("ggg"); ImGui::NextColumn(); - ImGui::Text("hhh"); ImGui::NextColumn(); + for (int i = 0; i < 8; i++) + { + ImGui::Text("%c%c%c", 'a'+i, 'a'+i, 'a'+i); + ImGui::NextColumn(); + } ImGui::Columns(1); ImGui::TreePop(); } if (ImGui::TreeNode("node 2 (without borders)")) { ImGui::Columns(4, NULL, false); - ImGui::Text("aaa"); ImGui::NextColumn(); - ImGui::Text("bbb"); ImGui::NextColumn(); - ImGui::Text("ccc"); ImGui::NextColumn(); - ImGui::Text("ddd"); ImGui::NextColumn(); - ImGui::Text("eee"); ImGui::NextColumn(); - ImGui::Text("fff"); ImGui::NextColumn(); - ImGui::Text("ggg"); ImGui::NextColumn(); - ImGui::Text("hhh"); ImGui::NextColumn(); + for (int i = 0; i < 8; i++) + { + ImGui::Text("%c%c%c", 'a'+i, 'a'+i, 'a'+i); + ImGui::NextColumn(); + } ImGui::Columns(1); ImGui::TreePop(); } @@ -7058,7 +10736,7 @@ void ImGui::ShowTestWindow(bool* open) ImGui::BulletText("%s", lines[i]); } - if (ImGui::CollapsingHeader("Keyboard & Focus")) + if (ImGui::CollapsingHeader("Keyboard, Mouse & Focus")) { if (ImGui::TreeNode("Tabbing")) { @@ -7085,54 +10763,129 @@ void ImGui::ShowTestWindow(bool* open) if (focus_1) ImGui::SetKeyboardFocusHere(); ImGui::InputText("1", buf, IM_ARRAYSIZE(buf)); - if (ImGui::IsItemFocused()) has_focus = 1; + if (ImGui::IsItemActive()) has_focus = 1; if (focus_2) ImGui::SetKeyboardFocusHere(); ImGui::InputText("2", buf, IM_ARRAYSIZE(buf)); - if (ImGui::IsItemFocused()) has_focus = 2; + if (ImGui::IsItemActive()) has_focus = 2; ImGui::PushAllowKeyboardFocus(false); if (focus_3) ImGui::SetKeyboardFocusHere(); ImGui::InputText("3 (tab skip)", buf, IM_ARRAYSIZE(buf)); - if (ImGui::IsItemFocused()) has_focus = 3; + if (ImGui::IsItemActive()) has_focus = 3; ImGui::PopAllowKeyboardFocus(); if (has_focus) ImGui::Text("Item with focus: %d", has_focus); else ImGui::Text("Item with focus: "); + ImGui::TextWrapped("Cursor & selection are preserved when refocusing last used item in code."); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Mouse cursors")) + { + ImGui::TextWrapped("(Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. You can also set io.MouseDrawCursor to ask ImGui to render the cursor for you in software)"); + ImGui::Checkbox("io.MouseDrawCursor", &ImGui::GetIO().MouseDrawCursor); + ImGui::Text("Hover to see mouse cursors:"); + for (int i = 0; i < ImGuiMouseCursor_Count_; i++) + { + char label[32]; + sprintf(label, "Mouse cursor %d", i); + ImGui::Bullet(); ImGui::Selectable(label, false); + if (ImGui::IsItemHovered()) + ImGui::SetMouseCursor(i); + } ImGui::TreePop(); } } - static bool show_app_console = false; - static bool show_app_long_text = false; - static bool show_app_auto_resize = false; if (ImGui::CollapsingHeader("App Examples")) { + ImGui::Checkbox("Metrics", &show_app_metrics); ImGui::Checkbox("Console", &show_app_console); ImGui::Checkbox("Long text display", &show_app_long_text); ImGui::Checkbox("Auto-resizing window", &show_app_auto_resize); + ImGui::Checkbox("Simple overlay", &show_app_fixed_overlay); + ImGui::Checkbox("Manipulating window title", &show_app_manipulating_window_title); + ImGui::Checkbox("Custom rendering", &show_app_custom_rendering); } - if (show_app_console) - ShowExampleAppConsole(&show_app_console); - if (show_app_long_text) - ShowExampleAppLongText(&show_app_long_text); - if (show_app_auto_resize) - ShowExampleAppAutoResize(&show_app_auto_resize); ImGui::End(); } -static void ShowExampleAppAutoResize(bool* open) +void ImGui::ShowMetricsWindow(bool* opened) { - if (!ImGui::Begin("Example: Auto-Resizing Window", open, ImVec2(0,0), -1.0f, ImGuiWindowFlags_AlwaysAutoResize)) + if (ImGui::Begin("ImGui Metrics", opened)) + { + ImGui::Text("ImGui %s", ImGui::GetVersion()); + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); + ImGui::Text("%d vertices", ImGui::GetIO().MetricsRenderVertices); + ImGui::Separator(); + + struct Funcs + { + static void NodeDrawList(ImDrawList* draw_list, const char* label) + { + bool node_opened = ImGui::TreeNode(draw_list, "%s: %d vtx, %d cmds", label, draw_list->vtx_buffer.size(), draw_list->commands.size()); + if (draw_list == ImGui::GetWindowDrawList()) + { + ImGui::SameLine(); + ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered) + } + if (!node_opened) + return; + for (const ImDrawCmd* pcmd = draw_list->commands.begin(); pcmd < draw_list->commands.end(); pcmd++) + if (pcmd->user_callback) + ImGui::BulletText("Callback %p, user_data %p", pcmd->user_callback, pcmd->user_callback_data); + else + ImGui::BulletText("Draw %d vtx, tex = %p", pcmd->vtx_count, pcmd->texture_id); + ImGui::TreePop(); + } + + static void NodeWindows(ImVector& windows, const char* label) + { + if (!ImGui::TreeNode(label, "%s (%d)", label, (int)windows.size())) + return; + for (int i = 0; i < (int)windows.size(); i++) + Funcs::NodeWindow(windows[i], "Window"); + ImGui::TreePop(); + } + + static void NodeWindow(ImGuiWindow* window, const char* label) + { + if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window)) + return; + NodeDrawList(window->DrawList, "DrawList"); + if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); + if (window->DC.ChildWindows.size() > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); + ImGui::TreePop(); + } + }; + + ImGuiState& g = *GImGui; // Access private state + g.DisableHideTextAfterDoubleHash++; // Not exposed (yet). Disable processing that hides text after '##' markers. + Funcs::NodeWindows(g.Windows, "Windows"); + if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", (int)g.RenderDrawLists[0].size())) + { + for (int i = 0; i < (int)g.RenderDrawLists[0].size(); i++) + Funcs::NodeDrawList(g.RenderDrawLists[0][i], "DrawList"); + ImGui::TreePop(); + } + g.DisableHideTextAfterDoubleHash--; + } + ImGui::End(); +} + +static void ShowExampleAppAutoResize(bool* opened) +{ + if (!ImGui::Begin("Example: Auto-Resizing Window", opened, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::End(); return; } static int lines = 10; - ImGui::TextWrapped("Window will resize every-frame to the size of its content. Note that you don't want to query the window size to output your content because that would create a feedback loop."); + ImGui::Text("Window will resize every-frame to the size of its content.\nNote that you probably don't want to query the window size to\noutput your content because that would create a feedback loop."); ImGui::SliderInt("Number of lines", &lines, 1, 20); for (int i = 0; i < lines; i++) ImGui::Text("%*sThis is line %d", i*4, "", i); // Pad with space to extend size horizontally @@ -7140,36 +10893,274 @@ static void ShowExampleAppAutoResize(bool* open) ImGui::End(); } +static void ShowExampleAppFixedOverlay(bool* opened) +{ + ImGui::SetNextWindowPos(ImVec2(10,10)); + if (!ImGui::Begin("Example: Fixed Overlay", opened, ImVec2(0,0), 0.3f, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoSavedSettings)) + { + ImGui::End(); + return; + } + + ImGui::Text("Simple overlay\non the top-left side of the screen."); + ImGui::Separator(); + ImGui::Text("Mouse Position: (%.1f,%.1f)", ImGui::GetIO().MousePos.x, ImGui::GetIO().MousePos.y); + + ImGui::End(); +} + +static void ShowExampleAppManipulatingWindowTitle(bool* opened) +{ + (void)opened; + + // By default, Windows are uniquely identified by their title. + // You can use the "##" and "###" markers to manipulate the display/ID. Read FAQ at the top of this file! + + // Using "##" to display same title but have unique identifier. + ImGui::SetNextWindowPos(ImVec2(100,100), ImGuiSetCond_FirstUseEver); + ImGui::Begin("Same title as another window##1"); + ImGui::Text("This is window 1.\nMy title is the same as window 2, but my identifier is unique."); + ImGui::End(); + + ImGui::SetNextWindowPos(ImVec2(100,200), ImGuiSetCond_FirstUseEver); + ImGui::Begin("Same title as another window##2"); + ImGui::Text("This is window 2.\nMy title is the same as window 1, but my identifier is unique."); + ImGui::End(); + + // Using "###" to display a changing title but keep a static identifier "AnimatedTitle" + char buf[128]; + ImFormatString(buf, IM_ARRAYSIZE(buf), "Animated title %c %d###AnimatedTitle", "|/-\\"[(int)(ImGui::GetTime()/0.25f)&3], rand()); + ImGui::SetNextWindowPos(ImVec2(100,300), ImGuiSetCond_FirstUseEver); + ImGui::Begin(buf); + ImGui::Text("This window has a changing title."); + ImGui::End(); +} + +static void ShowExampleAppCustomRendering(bool* opened) +{ + ImGui::SetNextWindowSize(ImVec2(300,350), ImGuiSetCond_FirstUseEver); + if (!ImGui::Begin("Example: Custom Rendering", opened)) + { + ImGui::End(); + return; + } + + // Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of overloaded operators, etc. + // Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your types and ImVec2/ImVec4. + // ImGui defines overloaded operators but they are internal to imgui.cpp and not exposed outside (to avoid messing with your types) + // In this example we aren't using the operators. + + static ImVector points; + static bool adding_line = false; + if (ImGui::Button("Clear")) points.clear(); + if (points.size() >= 2) { ImGui::SameLine(); if (ImGui::Button("Undo")) { points.pop_back(); points.pop_back(); } } + ImGui::Text("Left-click and drag to add lines"); + ImGui::Text("Right-click to undo"); + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + + // Here we are using InvisibleButton() as a convenience to 1) advance the cursor and 2) allows us to use IsItemHovered() + // However you can draw directly and poll mouse/keyboard by yourself. You can manipulate the cursor using GetCursorPos() and SetCursorPos(). + // If you only use the ImDrawList API, you can notify the owner window of its extends by using SetCursorPos(max). + ImVec2 canvas_pos = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates! + ImVec2 canvas_size = ImVec2(ImMax(50.0f,ImGui::GetWindowContentRegionMax().x-ImGui::GetCursorPos().x), ImMax(50.0f,ImGui::GetWindowContentRegionMax().y-ImGui::GetCursorPos().y)); // Resize canvas what's available + draw_list->AddRect(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), 0xFFFFFFFF); + bool adding_preview = false; + ImGui::InvisibleButton("canvas", canvas_size); + if (ImGui::IsItemHovered()) + { + ImVec2 mouse_pos_in_canvas = ImVec2(ImGui::GetIO().MousePos.x - canvas_pos.x, ImGui::GetIO().MousePos.y - canvas_pos.y); + if (!adding_line && ImGui::GetIO().MouseClicked[0]) + { + points.push_back(mouse_pos_in_canvas); + adding_line = true; + } + if (adding_line) + { + adding_preview = true; + points.push_back(mouse_pos_in_canvas); + if (!ImGui::GetIO().MouseDown[0]) + adding_line = adding_preview = false; + } + if (ImGui::GetIO().MouseClicked[1] && !points.empty()) + { + adding_line = false; + points.pop_back(); + points.pop_back(); + } + } + draw_list->PushClipRect(ImVec4(canvas_pos.x, canvas_pos.y, canvas_pos.x+canvas_size.x, canvas_pos.y+canvas_size.y)); // clip lines within the canvas (if we resize it, etc.) + for (int i = 0; i < (int)points.size() - 1; i += 2) + draw_list->AddLine(ImVec2(canvas_pos.x + points[i].x, canvas_pos.y + points[i].y), ImVec2(canvas_pos.x + points[i+1].x, canvas_pos.y + points[i+1].y), 0xFF00FFFF); + draw_list->PopClipRect(); + if (adding_preview) + points.pop_back(); + ImGui::End(); +} + struct ExampleAppConsole { - ImVector Items; - bool NewItems; + char InputBuf[256]; + ImVector Items; + bool ScrollToBottom; + ImVector History; + int HistoryPos; // -1: new line, 0..History.size()-1 browsing history. + ImVector Commands; - void Clear() + ExampleAppConsole() + { + ClearLog(); + HistoryPos = -1; + Commands.push_back("HELP"); + Commands.push_back("HISTORY"); + Commands.push_back("CLEAR"); + Commands.push_back("CLASSIFY"); // "classify" is here to provide an example of "C"+[tab] completing to "CL" and displaying matches. + } + ~ExampleAppConsole() + { + ClearLog(); + for (size_t i = 0; i < Items.size(); i++) + ImGui::MemFree(History[i]); + } + + void ClearLog() { for (size_t i = 0; i < Items.size(); i++) ImGui::MemFree(Items[i]); Items.clear(); - NewItems = true; + ScrollToBottom = true; } void AddLog(const char* fmt, ...) { - char buf[512]; + char buf[1024]; va_list args; va_start(args, fmt); ImFormatStringV(buf, IM_ARRAYSIZE(buf), fmt, args); va_end(args); Items.push_back(ImStrdup(buf)); - NewItems = true; + ScrollToBottom = true; } - void TextEditCallback(ImGuiTextEditCallbackData* data) + void Run(const char* title, bool* opened) + { + ImGui::SetNextWindowSize(ImVec2(520,600), ImGuiSetCond_FirstUseEver); + if (!ImGui::Begin(title, opened)) + { + ImGui::End(); + return; + } + + ImGui::TextWrapped("This example implements a console with basic coloring, completion and history. A more elaborate implementation may want to store entries along with extra data such as timestamp, emitter, etc."); + ImGui::TextWrapped("Enter 'HELP' for help, press TAB to use text completion."); + + // TODO: display from bottom + // TODO: clip manually + + if (ImGui::SmallButton("Add Dummy Text")) { AddLog("%d some text", Items.size()); AddLog("some more text"); AddLog("display very important message here!"); } ImGui::SameLine(); + if (ImGui::SmallButton("Add Dummy Error")) AddLog("[error] something went wrong"); ImGui::SameLine(); + if (ImGui::SmallButton("Clear")) ClearLog(); + //static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog("Spam %f", t); } + + ImGui::Separator(); + + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0)); + static ImGuiTextFilter filter; + filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180); + //if (ImGui::IsItemHovered()) ImGui::SetKeyboardFocusHere(-1); // Auto focus on hover + ImGui::PopStyleVar(); + ImGui::Separator(); + + // Display every line as a separate entry so we can change their color or add custom widgets. If you only want raw text you can use ImGui::TextUnformatted(log.begin(), log.end()); + // NB- if you have thousands of entries this approach may be too inefficient. You can seek and display only the lines that are visible - CalcListClipping() is a helper to compute this information. + // If your items are of variable size you may want to implement code similar to what CalcListClipping() does. Or split your data into fixed height items to allow random-seeking into your list. + ImGui::BeginChild("ScrollingRegion", ImVec2(0,-ImGui::GetTextLineHeightWithSpacing()*2)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4,1)); // Tighten spacing + for (size_t i = 0; i < Items.size(); i++) + { + const char* item = Items[i]; + if (!filter.PassFilter(item)) + continue; + ImVec4 col(1,1,1,1); // A better implement may store a type per-item. For the sample let's just parse the text. + if (strstr(item, "[error]")) col = ImVec4(1.0f,0.4f,0.4f,1.0f); + else if (strncmp(item, "# ", 2) == 0) col = ImVec4(1.0f,0.8f,0.6f,1.0f); + ImGui::PushStyleColor(ImGuiCol_Text, col); + ImGui::TextUnformatted(item); + ImGui::PopStyleColor(); + } + if (ScrollToBottom) + ImGui::SetScrollPosHere(); + ScrollToBottom = false; + ImGui::PopStyleVar(); + ImGui::EndChild(); + ImGui::Separator(); + + // Command-line + if (ImGui::InputText("Input", InputBuf, IM_ARRAYSIZE(InputBuf), ImGuiInputTextFlags_EnterReturnsTrue|ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_CallbackHistory, &TextEditCallbackStub, (void*)this)) + { + char* input_end = InputBuf+strlen(InputBuf); + while (input_end > InputBuf && input_end[-1] == ' ') input_end--; *input_end = 0; + if (InputBuf[0]) + ExecCommand(InputBuf); + strcpy(InputBuf, ""); + } + + // Demonstrate keeping auto focus on the input box + if (ImGui::IsItemHovered() || (ImGui::IsRootWindowOrAnyChildFocused() && !ImGui::IsAnyItemActive() && !ImGui::IsMouseClicked(0))) + ImGui::SetKeyboardFocusHere(-1); // Auto focus + + ImGui::End(); + } + + void ExecCommand(const char* command_line) + { + AddLog("# %s\n", command_line); + + // Insert into history. First find match and delete it so it can be pushed to the back. This isn't trying to be smart or optimal. + HistoryPos = -1; + for (int i = (int)History.size()-1; i >= 0; i--) + if (ImStricmp(History[i], command_line) == 0) + { + ImGui::MemFree(History[i]); + History.erase(History.begin() + i); + break; + } + History.push_back(ImStrdup(command_line)); + + // Process command + if (ImStricmp(command_line, "CLEAR") == 0) + { + ClearLog(); + } + else if (ImStricmp(command_line, "HELP") == 0) + { + AddLog("Commands:"); + for (size_t i = 0; i < Commands.size(); i++) + AddLog("- %s", Commands[i]); + } + else if (ImStricmp(command_line, "HISTORY") == 0) + { + for (size_t i = History.size() >= 10 ? History.size() - 10 : 0; i < History.size(); i++) + AddLog("%3d: %s\n", i, History[i]); + } + else + { + AddLog("Unknown command: '%s'\n", command_line); + } + } + + static int TextEditCallbackStub(ImGuiTextEditCallbackData* data) + { + ExampleAppConsole* console = (ExampleAppConsole*)data->UserData; + return console->TextEditCallback(data); + } + + int TextEditCallback(ImGuiTextEditCallbackData* data) { //AddLog("cursor: %d, selection: %d-%d", data->CursorPos, data->SelectionStart, data->SelectionEnd); - switch (data->EventKey) + switch (data->EventFlag) { - case ImGuiKey_Tab: + case ImGuiInputTextFlags_CallbackCompletion: { // Example of TEXT COMPLETION @@ -7179,17 +11170,16 @@ struct ExampleAppConsole while (word_start > data->Buf) { const char c = word_start[-1]; - if (c == ' ' || c == '\t' || c == ',' || c == ';') + if (ImCharIsSpace(c) || c == ',' || c == ';') break; word_start--; } // Build a list of candidates - const char* commands[] = { "HELP", "CLEAR", "CLASSIFY" }; ImVector candidates; - for (size_t i = 0; i < IM_ARRAYSIZE(commands); i++) - if (ImStrnicmp(commands[i], word_start, word_end-word_start) == 0) - candidates.push_back(commands[i]); + for (size_t i = 0; i < Commands.size(); i++) + if (ImStrnicmp(Commands[i], word_start, (int)(word_end-word_start)) == 0) + candidates.push_back(Commands[i]); if (candidates.size() == 0) { @@ -7199,25 +11189,23 @@ struct ExampleAppConsole else if (candidates.size() == 1) { // Single match. Delete the beginning of the word and replace it entirely so we've got nice casing - data->DeleteChars(word_start-data->Buf, word_end-word_start); + data->DeleteChars((int)(word_start-data->Buf), (int)(word_end-word_start)); data->InsertChars(data->CursorPos, candidates[0]); data->InsertChars(data->CursorPos, " "); } else { // Multiple matches. Complete as much as we can, so inputing "C" will complete to "CL" and display "CLEAR" and "CLASSIFY" - int match_len = word_end - word_start; - while (true) + int match_len = (int)(word_end - word_start); + for (;;) { int c = 0; bool all_candidates_matches = true; for (size_t i = 0; i < candidates.size() && all_candidates_matches; i++) - { if (i == 0) c = toupper(candidates[i][match_len]); else if (c != toupper(candidates[i][match_len])) all_candidates_matches = false; - } if (!all_candidates_matches) break; match_len++; @@ -7225,7 +11213,7 @@ struct ExampleAppConsole if (match_len > 0) { - data->DeleteChars(word_start - data->Buf, word_end-word_start); + data->DeleteChars((int)(word_start - data->Buf), (int)(word_end-word_start)); data->InsertChars(data->CursorPos, candidates[0], candidates[0] + match_len); } @@ -7237,104 +11225,57 @@ struct ExampleAppConsole break; } + case ImGuiInputTextFlags_CallbackHistory: + { + // Example of HISTORY + const int prev_history_pos = HistoryPos; + if (data->EventKey == ImGuiKey_UpArrow) + { + if (HistoryPos == -1) + HistoryPos = (int)(History.size() - 1); + else if (HistoryPos > 0) + HistoryPos--; + } + else if (data->EventKey == ImGuiKey_DownArrow) + { + if (HistoryPos != -1) + if (++HistoryPos >= (int)History.size()) + HistoryPos = -1; + } + + // A better implementation would preserve the data on the current input line along with cursor position. + if (prev_history_pos != HistoryPos) + { + ImFormatString(data->Buf, data->BufSize, "%s", (HistoryPos >= 0) ? History[HistoryPos] : ""); + data->BufDirty = true; + data->CursorPos = data->SelectionStart = data->SelectionEnd = (int)strlen(data->Buf); + } + } } + return 0; } }; -static void ShowExampleAppConsole_TextEditCallback(ImGuiTextEditCallbackData* data) +static void ShowExampleAppConsole(bool* opened) { - ExampleAppConsole* console = (ExampleAppConsole*)data->UserData; - console->TextEditCallback(data); -} - -static void ShowExampleAppConsole(bool* open) -{ - if (!ImGui::Begin("Example: Console", open, ImVec2(520,600))) - { - ImGui::End(); - return; - } - - ImGui::TextWrapped("This example implement a simple console. A more elaborate implementation may want to store individual entries along with extra data such as timestamp, emitter, etc."); - ImGui::TextWrapped("Press TAB to use text completion."); - - // TODO: display from bottom - // TODO: clip manually - // TODO: history - static ExampleAppConsole console; - static char input[256] = ""; - - if (ImGui::SmallButton("Add Dummy Text")) console.AddLog("some text\nsome more text"); - ImGui::SameLine(); - if (ImGui::SmallButton("Add Dummy Error")) console.AddLog("[error] something went wrong"); - ImGui::SameLine(); - if (ImGui::SmallButton("Clear all")) console.Clear(); - ImGui::Separator(); - - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0)); - static ImGuiTextFilter filter; - filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180); - if (ImGui::IsItemHovered()) ImGui::SetKeyboardFocusHere(-1); // Auto focus on hover - ImGui::PopStyleVar(); - ImGui::Separator(); - - ImGui::BeginChild("ScrollingRegion", ImVec2(0,-ImGui::GetTextLineSpacing()*2)); - - // Display every line as a separate entry so we can change their color or add custom widgets. If you only want raw text you can use ImGui::TextUnformatted(log.begin(), log.end()); - // NB- if you have lots of text this approach may be too inefficient. You can seek and display only the lines that are on display using a technique similar to what TextUnformatted() does, - // or faster if your entries are already stored into a table. - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4,1)); // tighten spacing - ImGui::GetStyle().ItemSpacing.y = 1; // tighten spacing - for (size_t i = 0; i < console.Items.size(); i++) - { - const char* item = console.Items[i]; - if (!filter.PassFilter(item)) - continue; - ImVec4 col(1,1,1,1); - if (strstr(item, "[error]")) col = ImVec4(1.0f,0.4f,0.4f,1.0f); - else if (strncmp(item, "# ", 2) == 0) col = ImVec4(1.0f,0.8f,0.6f,1.0f); - ImGui::PushStyleColor(ImGuiCol_Text, col); - ImGui::TextUnformatted(item); - ImGui::PopStyleColor(); - } - ImGui::PopStyleVar(); - if (console.NewItems) - { - ImGui::SetScrollPosHere(); - console.NewItems = false; - } - ImGui::EndChild(); - - ImGui::Separator(); - if (ImGui::InputText("Input", input, IM_ARRAYSIZE(input), ImGuiInputTextFlags_EnterReturnsTrue|ImGuiInputTextFlags_CallbackCompletion, &ShowExampleAppConsole_TextEditCallback, (void*)&console)) - { - const char* input_trimmed_end = input+strlen(input); - while (input_trimmed_end > input && input_trimmed_end[-1] == ' ') - input_trimmed_end--; - if (input_trimmed_end > input) - { - console.AddLog("# %s\n", input); - console.AddLog("Unknown command: '%.*s'\n", input_trimmed_end-input, input); // NB: we don't actually handle any command in this sample code - } - strcpy(input, ""); - } - if (ImGui::IsItemHovered()) ImGui::SetKeyboardFocusHere(-1); // Auto focus on hover - - ImGui::End(); + console.Run("Example: Console", opened); } -static void ShowExampleAppLongText(bool* open) +static void ShowExampleAppLongText(bool* opened) { - if (!ImGui::Begin("Example: Long text display", open, ImVec2(520,600))) + ImGui::SetNextWindowSize(ImVec2(520,600), ImGuiSetCond_FirstUseEver); + if (!ImGui::Begin("Example: Long text display", opened)) { ImGui::End(); return; } + static int test_type = 0; static ImGuiTextBuffer log; static int lines = 0; ImGui::Text("Printing unusually long amount of text."); + ImGui::Combo("Test type", &test_type, "Single call to TextUnformatted()\0Multiple calls to Text(), clipped manually\0Multiple calls to Text(), not clipped"); ImGui::Text("Buffer contents: %d lines, %d bytes", lines, log.size()); if (ImGui::Button("Clear")) { log.clear(); lines = 0; } ImGui::SameLine(); @@ -7345,216 +11286,372 @@ static void ShowExampleAppLongText(bool* open) lines += 1000; } ImGui::BeginChild("Log"); - ImGui::TextUnformatted(log.begin(), log.end()); + switch (test_type) + { + case 0: + // Single call to TextUnformatted() with a big buffer + ImGui::TextUnformatted(log.begin(), log.end()); + break; + case 1: + // Multiple calls to Text(), manually coarsely clipped - demonstrate how to use the CalcListClipping() helper. + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0)); + int display_start, display_end; + ImGui::CalcListClipping(lines, ImGui::GetTextLineHeight(), &display_start, &display_end); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + (display_start) * ImGui::GetTextLineHeight()); + for (int i = display_start; i < display_end; i++) + ImGui::Text("%i The quick brown fox jumps over the lazy dog\n", i); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + (lines - display_end) * ImGui::GetTextLineHeight()); + ImGui::PopStyleVar(); + break; + case 2: + // Multiple calls to Text(), not clipped + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0)); + for (int i = 0; i < lines; i++) + ImGui::Text("%i The quick brown fox jumps over the lazy dog\n", i); + ImGui::PopStyleVar(); + break; + } ImGui::EndChild(); - ImGui::End(); } // End of Sample code +#endif //----------------------------------------------------------------------------- -// Font data -// Bitmap exported from proggy_clean.fon (c) by Tristan Grimmer http://upperbounds.net/ -// Also available on unofficial ProggyFonts mirror http://www.proggyfonts.net +// FONT DATA //----------------------------------------------------------------------------- -/* +// Compressed with stb_compress() then converted to a C array. +// Use the program in extra_fonts/binary_to_compressed_c.cpp to create the array from a TTF file. +// Decompressor from stb.h (public domain) by Sean Barrett https://github.com/nothings/stb/blob/master/stb.h +//----------------------------------------------------------------------------- + +static unsigned int stb_decompress_length(unsigned char *input) +{ + return (input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11]; +} + +static unsigned char *stb__barrier, *stb__barrier2, *stb__barrier3, *stb__barrier4; +static unsigned char *stb__dout; +static void stb__match(unsigned char *data, unsigned int length) +{ + // INVERSE of memmove... write each byte before copying the next... + IM_ASSERT (stb__dout + length <= stb__barrier); + if (stb__dout + length > stb__barrier) { stb__dout += length; return; } + if (data < stb__barrier4) { stb__dout = stb__barrier+1; return; } + while (length--) *stb__dout++ = *data++; +} + +static void stb__lit(unsigned char *data, unsigned int length) +{ + IM_ASSERT (stb__dout + length <= stb__barrier); + if (stb__dout + length > stb__barrier) { stb__dout += length; return; } + if (data < stb__barrier2) { stb__dout = stb__barrier+1; return; } + memcpy(stb__dout, data, length); + stb__dout += length; +} + +#define stb__in2(x) ((i[x] << 8) + i[(x)+1]) +#define stb__in3(x) ((i[x] << 16) + stb__in2((x)+1)) +#define stb__in4(x) ((i[x] << 24) + stb__in3((x)+1)) + +static unsigned char *stb_decompress_token(unsigned char *i) +{ + if (*i >= 0x20) { // use fewer if's for cases that expand small + if (*i >= 0x80) stb__match(stb__dout-i[1]-1, i[0] - 0x80 + 1), i += 2; + else if (*i >= 0x40) stb__match(stb__dout-(stb__in2(0) - 0x4000 + 1), i[2]+1), i += 3; + else /* *i >= 0x20 */ stb__lit(i+1, i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1); + } else { // more ifs for cases that expand large, since overhead is amortized + if (*i >= 0x18) stb__match(stb__dout-(stb__in3(0) - 0x180000 + 1), i[3]+1), i += 4; + else if (*i >= 0x10) stb__match(stb__dout-(stb__in3(0) - 0x100000 + 1), stb__in2(3)+1), i += 5; + else if (*i >= 0x08) stb__lit(i+2, stb__in2(0) - 0x0800 + 1), i += 2 + (stb__in2(0) - 0x0800 + 1); + else if (*i == 0x07) stb__lit(i+3, stb__in2(1) + 1), i += 3 + (stb__in2(1) + 1); + else if (*i == 0x06) stb__match(stb__dout-(stb__in3(1)+1), i[4]+1), i += 5; + else if (*i == 0x04) stb__match(stb__dout-(stb__in3(1)+1), stb__in2(4)+1), i += 6; + } + return i; +} + +static unsigned int stb_adler32(unsigned int adler32, unsigned char *buffer, unsigned int buflen) +{ + const unsigned long ADLER_MOD = 65521; + unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16; + unsigned long blocklen, i; + + blocklen = buflen % 5552; + while (buflen) { + for (i=0; i + 7 < blocklen; i += 8) { + s1 += buffer[0], s2 += s1; + s1 += buffer[1], s2 += s1; + s1 += buffer[2], s2 += s1; + s1 += buffer[3], s2 += s1; + s1 += buffer[4], s2 += s1; + s1 += buffer[5], s2 += s1; + s1 += buffer[6], s2 += s1; + s1 += buffer[7], s2 += s1; + + buffer += 8; + } + + for (; i < blocklen; ++i) + s1 += *buffer++, s2 += s1; + + s1 %= ADLER_MOD, s2 %= ADLER_MOD; + buflen -= blocklen; + blocklen = 5552; + } + return (unsigned int)(s2 << 16) + (unsigned int)s1; +} + +static unsigned int stb_decompress(unsigned char *output, unsigned char *i, unsigned int length) +{ + unsigned int olen; + if (stb__in4(0) != 0x57bC0000) return 0; + if (stb__in4(4) != 0) return 0; // error! stream is > 4GB + olen = stb_decompress_length(i); + stb__barrier2 = i; + stb__barrier3 = i+length; + stb__barrier = output + olen; + stb__barrier4 = output; + i += 16; + + stb__dout = output; + for (;;) { + unsigned char *old_i = i; + i = stb_decompress_token(i); + if (i == old_i) { + if (*i == 0x05 && i[1] == 0xfa) { + IM_ASSERT(stb__dout == output + olen); + if (stb__dout != output + olen) return 0; + if (stb_adler32(1, output, olen) != (unsigned int) stb__in4(2)) + return 0; + return olen; + } else { + IM_ASSERT(0); /* NOTREACHED */ + return 0; + } + } + IM_ASSERT(stb__dout <= output + olen); + if (stb__dout > output + olen) + return 0; + } +} + +//----------------------------------------------------------------------------- +// ProggyClean.ttf // Copyright (c) 2004, 2005 Tristan Grimmer - -// 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. -*/ +// MIT license (see License.txt in http://www.upperbounds.net/download/ProggyClean.ttf.zip) +// Download and more information at http://upperbounds.net //----------------------------------------------------------------------------- -// Fonts exported with BMFont http://www.angelcode.com/products/bmfont -// We are using bmfont format and you can load your own font from a file by setting up ImGui::GetIO().Font -// PNG further compressed with pngout.exe http://advsys.net/ken/utils.htm -// Manually converted to C++ array using the following program: -/* -static void binary_to_c(const char* name_in, const char* symbol) +static const unsigned int proggy_clean_ttf_compressed_size = 9583; +static const unsigned int proggy_clean_ttf_compressed_data[9584/4] = { - FILE* fi = fopen(name_in, "rb"); fseek(fi, 0, SEEK_END); long sz = ftell(fi); fseek(fi, 0, SEEK_SET); - fprintf(stdout, "static const unsigned int %s_size = %d;\n", symbol, sz); - fprintf(stdout, "static const unsigned int %s_data[%d/4] =\n{", symbol, ((sz+3)/4)*4); - int column = 0; - for (unsigned int data = 0; fread(&data, 1, 4, fi); data = 0) - if ((column++ % 12) == 0) - fprintf(stdout, "\n 0x%08x, ", data); - else - fprintf(stdout, "0x%08x, ", data); - fprintf(stdout, "\n};\n\n"); - fclose(fi); -} - -int main(int argc, char** argv) -{ - binary_to_c("proggy_clean_13.fnt", "proggy_clean_13_fnt"); - binary_to_c("proggy_clean_13.png", "proggy_clean_13_png"); - return 1; -} -*/ -//----------------------------------------------------------------------------- - -static const unsigned int proggy_clean_13_png_size = 1557; -static const unsigned int proggy_clean_13_png_data[1560/4] = -{ - 0x474e5089, 0x0a1a0a0d, 0x0d000000, 0x52444849, 0x00010000, 0x80000000, 0x00000308, 0x476bd300, 0x00000038, 0x544c5006, 0x00000045, 0xa5ffffff, - 0x00dd9fd9, 0x74010000, 0x00534e52, 0x66d8e640, 0xbd050000, 0x54414449, 0x9bed5e78, 0x30e36e51, 0xeef5440c, 0x31fde97f, 0x584ec0f0, 0x681ace39, - 0xca120e6b, 0x1c5a28a6, 0xc5d98a89, 0x1a3d602e, 0x323c0043, 0xf6bc9e68, 0xbe3ad62c, 0x3d60260f, 0x82d60096, 0xe0bfc707, 0xfb9bf1d1, 0xbf0267ac, - 0x1600260f, 0x061229c0, 0x0000c183, 0x37162c58, 0xdfa088fc, 0xde7d5704, 0x77fcbb80, 0x48e5c3f1, 0x73d8b8f8, 0xc4af7802, 0x1ca111ad, 0x0001ed7a, - 0x76eda3ef, 0xb78d3e00, 0x801c7203, 0x0215c0b1, 0x0410b044, 0xa85100d4, 0x07627ec7, 0x0cf83fa8, 0x94001a22, 0xf87347f1, 0xdcb5cfc1, 0x1c3880cc, - 0xd4e034ca, 0xfa928d9d, 0xb0167e31, 0x325cc570, 0x4bbd584b, 0xbd4e6574, 0x70bae084, 0xf0c0008a, 0x3f601ddb, 0x0bba506a, 0xa58a0082, 0x5b46946e, - 0x720a4ccd, 0xdfaaed39, 0x25dc8042, 0x7ee403f4, 0x2ad69cc9, 0x6c4b3009, 0x429037ed, 0x0293f875, 0x1a69dced, 0xab120198, 0x61c01d88, 0xcf2e43dc, - 0xfc3c00ef, 0xc049a270, 0xdbbea582, 0x0d592601, 0xc3c9a8dd, 0x5013d143, 0x19a47bbb, 0xf89253dd, 0x0a9901dc, 0x38900ecd, 0xb2dec9d7, 0xc2b91230, - 0xb8e0106f, 0x976404cb, 0x5d83c3f3, 0x6e8086fd, 0x5c9ab007, 0xf50354f6, 0xe7e72002, 0x4bc870ca, 0xab49736f, 0xc137c6e0, 0xa9aa6ff3, 0xbff84f2f, - 0x673e6e20, 0xf6e3c7e0, 0x618fe05a, 0x39ca2a00, 0x93ca03b4, 0x3a9d2728, 0xbbebba41, 0xce0e3681, 0x6e29ec05, 0x111eca83, 0xfdfe7ec1, 0xa7c8a75b, - 0xac6bc3ab, 0x72a5bc25, 0x9f612c1c, 0x378ec05e, 0x7202b157, 0x789e5a82, 0x5256bc0e, 0xcb900996, 0x10721105, 0x00823ce0, 0x69ab59fb, 0x39c72084, - 0xf5e37b25, 0xd1794700, 0x538d0637, 0x9a2bff4f, 0xce0d43a4, 0xa6da7ed2, 0xd7095132, 0xf5ad6232, 0x9aaa8e9c, 0xd8d1d3ed, 0x058940a1, 0x21f00d64, - 0x89a5c9de, 0x021b3f24, 0x77a97aac, 0x714be65a, 0x5e2d57ae, 0x27e3610f, 0x28809288, 0x36b9559f, 0xd00e347a, 0x0094e385, 0x565d034d, 0x7f52d5f2, - 0x9aea81de, 0x5e804909, 0x010d7f0a, 0x8f0d3fb1, 0xbbce23bc, 0x375e85ac, 0x01fa03b9, 0xc0526c3a, 0xf7866870, 0x9d46d804, 0x158ebf64, 0x7bd534c5, - 0xd80cf202, 0x410ee80f, 0x79419915, 0x74a844ae, 0x94119881, 0xcbbcc0fc, 0xa263d471, 0x013d0269, 0x67f6a0f8, 0x3e4474d0, 0xd1e50cb5, 0x56fd0e60, - 0xc4c0fd4c, 0x940629ff, 0xe18a7a16, 0xcca0330f, 0xb8ed50b7, 0x6935778b, 0x3735c791, 0x3909eb94, 0x0be36620, 0x0ac0d7aa, 0xefe942c9, 0xf0092727, - 0x5c020ee2, 0x0246da53, 0xa24be8bc, 0xa891ab94, 0xd012c7e2, 0x9c115954, 0xde0dac8e, 0x555dc022, 0x59e84f77, 0xbed2cf80, 0xe9af2cda, 0x4b600716, - 0x8955bd80, 0x7098c3f3, 0x25a8466a, 0x4ddbf26a, 0x5f554753, 0xf4890f28, 0x886a27ab, 0x54a00413, 0x0a157ca9, 0x52909a80, 0x7122a312, 0x0024a75c, - 0xe6d72935, 0xecde29cf, 0x025de009, 0x7995a6aa, 0x4a180491, 0x013df0d8, 0xe009edba, 0xd40019dc, 0x45b36b2a, 0x0122eb0d, 0x6e80a79f, 0x746590f5, - 0xd1a6dd49, 0xc05954b6, 0x83d4b957, 0xa00fe5b1, 0x59695ad7, 0xcff8433d, 0x44a0f340, 0xdd226c73, 0x5537f08c, 0xe1e89c32, 0x431056af, 0x233eb000, - 0x60773f40, 0xed7e490a, 0xc160091f, 0x12829db5, 0x43fbe6cf, 0x0a6b26c2, 0xd5f0f35a, 0xfc09fda8, 0x73525f8c, 0x2ea38cf9, 0x32bc410b, 0x94a60a22, - 0x1f62a42b, 0x5f290034, 0x07beaa91, 0x1e8ccb40, 0x17d6b0f9, 0xa2a017c9, 0x4c79a610, 0xa1de6525, 0xe975029f, 0xe063585f, 0x6246cfbb, 0x04acad44, - 0xe6a05138, 0xd03d8434, 0xc9950013, 0x5d4c809e, 0xfd26932d, 0x739213ac, 0xe260d8ef, 0xe4164617, 0x16fc60aa, 0x1d0b21e7, 0x445004b4, 0x13fd1b59, - 0x56b0f804, 0xaa936a3a, 0x335459c1, 0xb37f8caa, 0x06b68e03, 0x14d5eb01, 0x8300c78c, 0x9674792a, 0x20ba791b, 0x4d88024d, 0xef747354, 0x451e673e, - 0xc4dafc9a, 0xe53b9cd1, 0x32b4011a, 0x3d702c0f, 0x09bc0b40, 0x220d277d, 0x47eb7809, 0x8a946500, 0x7a28c4bd, 0x96e00f99, 0xc04365da, 0x05edcf46, - 0x7dee2c27, 0xe6020b7f, 0x159ecedf, 0xcbdb00ff, 0x516bb9e3, 0xd0716161, 0xeba75956, 0xf17fc22b, 0x5c578beb, 0xfe474a09, 0xc1750a87, 0xe384c189, - 0x5df54e26, 0xa6f76b79, 0xd4b172be, 0x3e8d5ceb, 0x832d90ec, 0x180368e7, 0x354c724d, 0x1a8b1412, 0x8de07be9, 0xaf009efe, 0x4616c621, 0x2860eb01, - 0x244f1404, 0xc3de724b, 0x6497a802, 0xab2f4419, 0x4e02910d, 0xe3ecf410, 0x7a6404a8, 0x8c72b112, 0xde5bc706, 0xd4f8ffe9, 0x50176344, 0x7b49fe7d, - 0x02c1d88c, 0x25634a40, 0x194804f7, 0x03b76d84, 0x392bde58, 0xdeebad27, 0xc160c021, 0xa97a72db, 0xa8040b83, 0x78804f3e, 0x046b9433, 0x178cc824, - 0x62800897, 0x7010370b, 0x21cfe7e4, 0x8053ec40, 0xf9d60526, 0xae9d353f, 0x069b40c7, 0x80496f14, 0x57e682b3, 0x6e0273e0, 0x974e2e28, 0x60ab7c3d, - 0x2025ba33, 0x507b3a8c, 0x12b70173, 0xd095c400, 0xee012d96, 0x6e194c9a, 0xe5933f89, 0x43b70102, 0xf30306aa, 0xc5802189, 0x53c077c3, 0x86029009, - 0xa0c1e780, 0xa4c04c1f, 0x93dbd580, 0xf8149809, 0x06021893, 0x3060c183, 0x83060c18, 0x183060c1, 0xc183060c, 0x0c183060, 0x60c18306, 0xfe0c1830, - 0x0cb69501, 0x7a40d9df, 0x000000dd, 0x4e454900, 0x6042ae44, 0x00000082, + 0x0000bc57, 0x00000000, 0xf8a00000, 0x00000400, 0x00010037, 0x000c0000, 0x00030080, 0x2f534f40, 0x74eb8832, 0x01000090, 0x2c158248, 0x616d634e, + 0x23120270, 0x03000075, 0x241382a0, 0x74766352, 0x82178220, 0xfc042102, 0x02380482, 0x66796c67, 0x5689af12, 0x04070000, 0x80920000, 0x64616568, + 0xd36691d7, 0xcc201b82, 0x36210382, 0x27108268, 0xc3014208, 0x04010000, 0x243b0f82, 0x78746d68, 0x807e008a, 0x98010000, 0x06020000, 0x61636f6c, + 0xd8b0738c, 0x82050000, 0x0402291e, 0x7078616d, 0xda00ae01, 0x28201f82, 0x202c1082, 0x656d616e, 0x96bb5925, 0x84990000, 0x9e2c1382, 0x74736f70, + 0xef83aca6, 0x249b0000, 0xd22c3382, 0x70657270, 0x12010269, 0xf4040000, 0x08202f82, 0x012ecb84, 0x553c0000, 0x0f5fd5e9, 0x0300f53c, 0x00830008, + 0x7767b722, 0x002b3f82, 0xa692bd00, 0xfe0000d7, 0x83800380, 0x21f1826f, 0x00850002, 0x41820120, 0x40fec026, 0x80030000, 0x05821083, 0x07830120, + 0x0221038a, 0x24118200, 0x90000101, 0x82798200, 0x00022617, 0x00400008, 0x2009820a, 0x82098276, 0x82002006, 0x9001213b, 0x0223c883, 0x828a02bc, + 0x858f2010, 0xc5012507, 0x00023200, 0x04210083, 0x91058309, 0x6c412b03, 0x40007374, 0xac200000, 0x00830008, 0x01000523, 0x834d8380, 0x80032103, + 0x012101bf, 0x23b88280, 0x00800000, 0x0b830382, 0x07820120, 0x83800021, 0x88012001, 0x84002009, 0x2005870f, 0x870d8301, 0x2023901b, 0x83199501, + 0x82002015, 0x84802000, 0x84238267, 0x88002027, 0x8561882d, 0x21058211, 0x13880000, 0x01800022, 0x05850d85, 0x0f828020, 0x03208384, 0x03200582, + 0x47901b84, 0x1b850020, 0x1f821d82, 0x3f831d88, 0x3f410383, 0x84058405, 0x210982cd, 0x09830000, 0x03207789, 0xf38a1384, 0x01203782, 0x13872384, + 0x0b88c983, 0x0d898f84, 0x00202982, 0x23900383, 0x87008021, 0x83df8301, 0x86118d03, 0x863f880d, 0x8f35880f, 0x2160820f, 0x04830300, 0x1c220382, + 0x05820100, 0x4c000022, 0x09831182, 0x04001c24, 0x11823000, 0x0800082e, 0x00000200, 0xff007f00, 0xffffac20, 0x00220982, 0x09848100, 0xdf216682, + 0x843586d5, 0x06012116, 0x04400684, 0xa58120d7, 0x00b127d8, 0x01b88d01, 0x2d8685ff, 0xc100c621, 0xf4be0801, 0x9e011c01, 0x88021402, 0x1403fc02, + 0x9c035803, 0x1404de03, 0x50043204, 0xa2046204, 0x66051605, 0x1206bc05, 0xd6067406, 0x7e073807, 0x4e08ec07, 0x96086c08, 0x1009d008, 0x88094a09, + 0x800a160a, 0x560b040b, 0x2e0cc80b, 0xea0c820c, 0xa40d5e0d, 0x500eea0d, 0x280f960e, 0x1210b00f, 0xe0107410, 0xb6115211, 0x6e120412, 0x4c13c412, + 0xf613ac13, 0xae145814, 0x4015ea14, 0xa6158015, 0x1216b815, 0xc6167e16, 0x8e173417, 0x5618e017, 0xee18ba18, 0x96193619, 0x481ad419, 0xf01a9c1a, + 0xc81b5c1b, 0x4c1c041c, 0xea1c961c, 0x921d2a1d, 0x401ed21d, 0xe01e8e1e, 0x761f241f, 0xa61fa61f, 0x01821020, 0x8a202e34, 0xc820b220, 0x74211421, + 0xee219821, 0x86226222, 0x01820c23, 0x83238021, 0x23983c01, 0x24d823b0, 0x244a2400, 0x24902468, 0x250625ae, 0x25822560, 0x26f825f8, 0x82aa2658, + 0xd8be0801, 0x9a274027, 0x68280a28, 0x0e29a828, 0xb8292029, 0x362af829, 0x602a602a, 0x2a2b022b, 0xac2b5e2b, 0x202ce62b, 0x9a2c342c, 0x5c2d282d, + 0xaa2d782d, 0x262ee82d, 0x262fa62e, 0xf42fb62f, 0xc8305e30, 0xb4313e31, 0x9e321e32, 0x82331e33, 0x5c34ee33, 0x3a35ce34, 0xd4358635, 0x72362636, + 0x7637e636, 0x3a38d837, 0x1239a638, 0xae397439, 0x9a3a2e3a, 0x7c3b063b, 0x3a3ce83b, 0x223d963c, 0xec3d863d, 0xc63e563e, 0x9a3f2a3f, 0x6a401240, + 0x3641d040, 0x0842a241, 0x7a424042, 0xf042b842, 0xcc436243, 0x8a442a44, 0x5845ee44, 0xe245b645, 0xb4465446, 0x7a471447, 0x5448da47, 0x4049c648, + 0x15462400, 0x034d0808, 0x0b000700, 0x13000f00, 0x1b001700, 0x23001f00, 0x2b002700, 0x33002f00, 0x3b003700, 0x43003f00, 0x4b004700, 0x53004f00, + 0x5b005700, 0x63005f00, 0x6b006700, 0x73006f00, 0x7b007700, 0x83007f00, 0x8b008700, 0x00008f00, 0x15333511, 0x20039631, 0x20178205, 0xd3038221, + 0x20739707, 0x25008580, 0x028080fc, 0x05be8080, 0x04204a85, 0x05ce0685, 0x0107002a, 0x02000080, 0x00000400, 0x250d8b41, 0x33350100, 0x03920715, + 0x13820320, 0x858d0120, 0x0e8d0320, 0xff260d83, 0x00808000, 0x54820106, 0x04800223, 0x845b8c80, 0x41332059, 0x078b068f, 0x82000121, 0x82fe2039, + 0x84802003, 0x83042004, 0x23598a0e, 0x00180000, 0x03210082, 0x42ab9080, 0x73942137, 0x2013bb41, 0x8f978205, 0x2027a39b, 0x20b68801, 0x84b286fd, + 0x91c88407, 0x41032011, 0x11a51130, 0x15000027, 0x80ff8000, 0x11af4103, 0x841b0341, 0x8bd983fd, 0x9be99bc9, 0x8343831b, 0x21f1821f, 0xb58300ff, + 0x0f84e889, 0xf78a0484, 0x8000ff22, 0x0020eeb3, 0x14200082, 0x2130ef41, 0xeb431300, 0x4133200a, 0xd7410ecb, 0x9a07200b, 0x2027871b, 0x21238221, + 0xe7828080, 0xe784fd20, 0xe8848020, 0xfe808022, 0x08880d85, 0xba41fd20, 0x82248205, 0x85eab02a, 0x008022e7, 0x2cd74200, 0x44010021, 0xd34406eb, + 0x44312013, 0xcf8b0eef, 0x0d422f8b, 0x82332007, 0x0001212f, 0x8023cf82, 0x83000180, 0x820583de, 0x830682d4, 0x820020d4, 0x82dc850a, 0x20e282e9, + 0xb2ff85fe, 0x010327e9, 0x02000380, 0x0f440400, 0x0c634407, 0x68825982, 0x85048021, 0x260a825d, 0x010b0000, 0x4400ff00, 0x2746103f, 0x08d74209, + 0x4d440720, 0x0eaf4406, 0xc3441d20, 0x23078406, 0xff800002, 0x04845b83, 0x8d05b241, 0x1781436f, 0x6b8c87a5, 0x1521878e, 0x06474505, 0x01210783, + 0x84688c00, 0x8904828e, 0x441e8cf7, 0x0b270cff, 0x80008000, 0x45030003, 0xfb430fab, 0x080f4107, 0x410bf942, 0xd34307e5, 0x070d4207, 0x80800123, + 0x205d85fe, 0x849183fe, 0x20128404, 0x82809702, 0x00002217, 0x41839a09, 0x6b4408cf, 0x0733440f, 0x3b460720, 0x82798707, 0x97802052, 0x0000296f, + 0xff800004, 0x01800100, 0x0021ef89, 0x0a914625, 0x410a4d41, 0x00250ed4, 0x00050000, 0x056d4280, 0x210a7b46, 0x21481300, 0x46ed8512, 0x00210bd1, + 0x89718202, 0x21738877, 0x2b850001, 0x00220582, 0x87450a00, 0x0ddb4606, 0x41079b42, 0x9d420c09, 0x0b09420b, 0x8d820720, 0x9742fc84, 0x42098909, + 0x00241e0f, 0x00800014, 0x0b47da82, 0x0833442a, 0x49078d41, 0x2f450f13, 0x42278f17, 0x01200751, 0x22063742, 0x44808001, 0x20450519, 0x88068906, + 0x83fe2019, 0x4203202a, 0x1a941a58, 0x00820020, 0xe7a40e20, 0x420ce146, 0x854307e9, 0x0fcb4713, 0xff20a182, 0xfe209b82, 0x0c867f8b, 0x0021aea4, + 0x219fa40f, 0x7d41003b, 0x07194214, 0xbf440520, 0x071d4206, 0x6941a590, 0x80802309, 0x028900ff, 0xa9a4b685, 0xc5808021, 0x449b82ab, 0x152007eb, + 0x42134d46, 0x61440a15, 0x051e4208, 0x222b0442, 0x47001100, 0xfd412913, 0x17194714, 0x410f5b41, 0x02220773, 0x09428080, 0x21a98208, 0xd4420001, + 0x481c840d, 0x00232bc9, 0x42120000, 0xe74c261b, 0x149d4405, 0x07209d87, 0x410db944, 0x14421c81, 0x42fd2005, 0x80410bd2, 0x203d8531, 0x06874100, + 0x48256f4a, 0xcb420c95, 0x13934113, 0x44075d44, 0x044c0855, 0x00ff2105, 0xfe228185, 0x45448000, 0x22c5b508, 0x410c0000, 0x7b412087, 0x1bb74514, + 0x32429c85, 0x0a574805, 0x21208943, 0x8ba01300, 0x440dfb4e, 0x77431437, 0x245b4113, 0x200fb145, 0x41108ffe, 0x80203562, 0x00200082, 0x46362b42, + 0x1742178d, 0x4527830f, 0x0f830b2f, 0x4a138146, 0x802409a1, 0xfe8000ff, 0x94419982, 0x09294320, 0x04000022, 0x49050f4f, 0xcb470a63, 0x48032008, + 0x2b48067b, 0x85022008, 0x82638338, 0x00002209, 0x05af4806, 0x900e9f49, 0x84c5873f, 0x214285bd, 0x064900ff, 0x0c894607, 0x00000023, 0x4903820a, + 0x714319f3, 0x0749410c, 0x8a07a145, 0x02152507, 0xfe808000, 0x74490386, 0x8080211b, 0x0c276f82, 0x00018000, 0x48028003, 0x2b2315db, 0x43002f00, + 0x6f82142f, 0x44011521, 0x93510da7, 0x20e68508, 0x06494d80, 0x8e838020, 0x06821286, 0x124bff20, 0x25f3830c, 0x03800080, 0xe74a0380, 0x207b8715, + 0x876b861d, 0x4a152007, 0x07870775, 0xf6876086, 0x8417674a, 0x0a0021f2, 0x431c9743, 0x8d421485, 0x200b830b, 0x06474d03, 0x71828020, 0x04510120, + 0x42da8606, 0x1f831882, 0x001a0022, 0xff4d0082, 0x0b0f532c, 0x0d449b94, 0x4e312007, 0x074f12e7, 0x0bf3490b, 0xbb412120, 0x413f820a, 0xef490857, + 0x80002313, 0xe2830001, 0x6441fc20, 0x8b802006, 0x00012108, 0xfd201582, 0x492c9b48, 0x802014ff, 0x51084347, 0x0f4327f3, 0x17bf4a14, 0x201b7944, + 0x06964201, 0x134ffe20, 0x20d6830b, 0x25d78280, 0xfd800002, 0x05888000, 0x9318dc41, 0x21d282d4, 0xdb481800, 0x0dff542a, 0x45107743, 0xe14813f5, + 0x0f034113, 0x83135d45, 0x47b28437, 0xe4510e73, 0x21f58e06, 0x2b8400fd, 0x1041fcac, 0x08db4b0b, 0x421fdb41, 0xdf4b18df, 0x011d210a, 0x420af350, + 0x6e8308af, 0xac85cb86, 0x1e461082, 0x82b7a407, 0x411420a3, 0xa34130ab, 0x178f4124, 0x41139741, 0x86410d93, 0x82118511, 0x057243d8, 0x8941d9a4, + 0x3093480c, 0x4a13474f, 0xfb5016a9, 0x07ad4108, 0x4a0f9d42, 0xfe200fad, 0x4708aa41, 0x83482dba, 0x288f4d06, 0xb398c3bb, 0x44267b41, 0xb34439d7, + 0x0755410f, 0x200ebb45, 0x0f5f4215, 0x20191343, 0x06df5301, 0xf04c0220, 0x2ba64d07, 0x82050841, 0x430020ce, 0xa78f3627, 0x5213ff42, 0x2f970bc1, + 0x4305ab55, 0xa084111b, 0x450bac45, 0x5f4238b8, 0x010c2106, 0x0220ed82, 0x441bb344, 0x875010af, 0x0737480f, 0x490c5747, 0x0c840c03, 0x4c204b42, + 0x8ba905d7, 0x8b948793, 0x510c0c51, 0xfb4b24b9, 0x1b174107, 0x5709d74c, 0xd1410ca5, 0x079d480f, 0x201ff541, 0x06804780, 0x7d520120, 0x80002205, + 0x20a983fe, 0x47bb83fe, 0x1b8409b4, 0x81580220, 0x4e00202c, 0x4f41282f, 0x0eab4f17, 0x57471520, 0x0e0f4808, 0x8221e041, 0x3e1b4a8b, 0x4407175d, + 0x1b4b071f, 0x4a0f8b07, 0x174a0703, 0x0ba5411b, 0x430fb141, 0x0120057b, 0xfc20dd82, 0x4a056047, 0xf4850c0c, 0x01221982, 0x02828000, 0x1a5d088b, + 0x20094108, 0x8c0e3941, 0x4900200e, 0x7744434f, 0x200b870b, 0x0e4b5a33, 0x2b41f78b, 0x8b138307, 0x0b9f450b, 0x2406f741, 0xfd808001, 0x09475a00, + 0x84000121, 0x5980200e, 0x85450e5d, 0x832c8206, 0x4106831e, 0x00213814, 0x28b34810, 0x410c2f4b, 0x5f4a13d7, 0x0b2b4113, 0x6e43a883, 0x11174b05, + 0x4b066a45, 0xcc470541, 0x5000202b, 0xcb472f4b, 0x44b59f0f, 0xc5430b5b, 0x0d654907, 0x21065544, 0xd6828080, 0xfe201982, 0x8230ec4a, 0x120025c2, + 0x80ff8000, 0x4128d74d, 0x3320408b, 0x410a9f50, 0xdb822793, 0x822bd454, 0x61134b2e, 0x410b214a, 0xad4117c9, 0x0001211f, 0x4206854f, 0x4b430596, + 0x06bb5530, 0x2025cf46, 0x0ddd5747, 0x500ea349, 0x0f840fa7, 0x5213c153, 0x634e08d1, 0x0bbe4809, 0x59316e4d, 0x5b50053f, 0x203f6323, 0x5117eb46, + 0x94450a63, 0x246e410a, 0x63410020, 0x0bdb5f2f, 0x4233ab44, 0x39480757, 0x112d4a07, 0x7241118f, 0x000e2132, 0x9f286f41, 0x0f8762c3, 0x33350723, + 0x094e6415, 0x2010925f, 0x067252fe, 0xd0438020, 0x63a68225, 0x11203a4f, 0x480e6360, 0x5748131f, 0x079b521f, 0x200e2f43, 0x864b8315, 0x113348e7, + 0x85084e48, 0x06855008, 0x5880fd21, 0x7c420925, 0x0c414824, 0x37470c86, 0x1b8b422b, 0x5b0a8755, 0x23410c21, 0x0b83420b, 0x5a082047, 0xf482067f, + 0xa80b4c47, 0x0c0021cf, 0x20207b42, 0x0fb74100, 0x420b8744, 0xeb43076f, 0x0f6f420b, 0x4261fe20, 0x439aa00c, 0x215034e3, 0x0ff9570f, 0x4b1f2d5d, + 0x2d5d0c6f, 0x09634d0b, 0x1f51b8a0, 0x620f200c, 0xaf681e87, 0x24f94d07, 0x4e0f4945, 0xfe200c05, 0x22139742, 0x57048080, 0x23950c20, 0x97601585, + 0x4813201f, 0xad620523, 0x200f8f0f, 0x9e638f15, 0x00002181, 0x41342341, 0x0f930f0b, 0x210b4b62, 0x978f0001, 0xfe200f84, 0x8425c863, 0x2704822b, + 0x80000a00, 0x00038001, 0x610e9768, 0x834514bb, 0x0bc3430f, 0x2107e357, 0x80848080, 0x4400fe21, 0x2e410983, 0x00002a1a, 0x00000700, 0x800380ff, + 0x0fdf5800, 0x59150021, 0xd142163d, 0x0c02410c, 0x01020025, 0x65800300, 0x00240853, 0x1d333501, 0x15220382, 0x35420001, 0x44002008, 0x376406d7, + 0x096f6b19, 0x480bc142, 0x8f4908a7, 0x211f8b1f, 0x9e830001, 0x0584fe20, 0x4180fd21, 0x11850910, 0x8d198259, 0x000021d4, 0x5a08275d, 0x275d1983, + 0x06d9420e, 0x9f08b36a, 0x0f7d47b5, 0x8d8a2f8b, 0x4c0e0b57, 0xe7410e17, 0x42d18c1a, 0xb351087a, 0x1ac36505, 0x4b4a2f20, 0x0b9f450d, 0x430beb53, + 0xa7881015, 0xa5826a83, 0x80200f82, 0x86185a65, 0x4100208e, 0x176c3367, 0x0fe7650b, 0x4a17ad4b, 0x0f4217ed, 0x112e4206, 0x41113a42, 0xf7423169, + 0x0cb34737, 0x560f8b46, 0xa75407e5, 0x5f01200f, 0x31590c48, 0x80802106, 0x42268841, 0x0020091e, 0x4207ef64, 0x69461df7, 0x138d4114, 0x820f5145, + 0x53802090, 0xff200529, 0xb944b183, 0x417e8505, 0x00202561, 0x15210082, 0x42378200, 0x9b431cc3, 0x004f220d, 0x0dd54253, 0x4213f149, 0x7d41133b, + 0x42c9870b, 0x802010f9, 0x420b2c42, 0x8f441138, 0x267c4408, 0x600cb743, 0x8f4109d3, 0x05ab701d, 0x83440020, 0x3521223f, 0x0b794733, 0xfb62fe20, + 0x4afd2010, 0xaf410ae7, 0x25ce8525, 0x01080000, 0x7b6b0000, 0x0973710b, 0x82010021, 0x49038375, 0x33420767, 0x052c4212, 0x58464b85, 0x41fe2005, + 0x50440c27, 0x000c2209, 0x1cb36b80, 0x9b06df44, 0x0f93566f, 0x52830220, 0xfe216e8d, 0x200f8200, 0x0fb86704, 0xb057238d, 0x050b5305, 0x7217eb47, + 0xbd410b6b, 0x0f214610, 0x871f9956, 0x1e91567e, 0x2029b741, 0x20008200, 0x18b7410a, 0x27002322, 0x41095543, 0x0f8f0fb3, 0x41000121, 0x889d111c, + 0x14207b82, 0x00200382, 0x73188761, 0x475013a7, 0x6e33200c, 0x234e0ea3, 0x9b138313, 0x08e54d17, 0x9711094e, 0x2ee74311, 0x4908875e, 0xd75d1f1f, + 0x19ab5238, 0xa2084d48, 0x63a7a9b3, 0x55450b83, 0x0fd74213, 0x440d814c, 0x4f481673, 0x05714323, 0x13000022, 0x412e1f46, 0xdf493459, 0x21c7550f, + 0x8408215f, 0x201d49cb, 0xb1103043, 0x0f0d65d7, 0x452b8d41, 0x594b0f8d, 0x0b004605, 0xb215eb46, 0x000a24d7, 0x47000080, 0x002118cf, 0x06436413, + 0x420bd750, 0x2b500743, 0x076a470c, 0x4105c050, 0xd942053f, 0x0d00211a, 0x5f44779c, 0x0ce94805, 0x51558186, 0x14a54c0b, 0x49082b41, 0x0a4b0888, + 0x8080261f, 0x0d000000, 0x20048201, 0x1deb6a03, 0x420cb372, 0x07201783, 0x4306854d, 0x8b830c59, 0x59093c74, 0x0020250f, 0x67070f4a, 0x2341160b, + 0x00372105, 0x431c515d, 0x554e17ef, 0x0e5d6b05, 0x41115442, 0xb74a1ac1, 0x2243420a, 0x5b4f878f, 0x7507200f, 0x384b086f, 0x09d45409, 0x0020869a, + 0x12200082, 0xab460382, 0x10075329, 0x54138346, 0xaf540fbf, 0x1ea75413, 0x9a0c9e54, 0x0f6b44c1, 0x41000021, 0x47412a4f, 0x07374907, 0x5310bf76, + 0xff2009b4, 0x9a09a64c, 0x8200208d, 0x34c34500, 0x970fe141, 0x1fd74b0f, 0x440a3850, 0x206411f0, 0x27934609, 0x470c5d41, 0x555c2947, 0x1787540f, + 0x6e0f234e, 0x7d540a1b, 0x1d736b08, 0x0026a088, 0x80000e00, 0x9b5200ff, 0x08ef4318, 0x450bff77, 0x1d4d0b83, 0x081f7006, 0xcb691b86, 0x4b022008, + 0xc34b0b33, 0x1d0d4a0c, 0x8025a188, 0x0b000000, 0x52a38201, 0xbf7d0873, 0x0c234511, 0x8f0f894a, 0x4101200f, 0x0c880c9d, 0x2b418ea1, 0x06c74128, + 0x66181341, 0x7b4c0bb9, 0x0c06630b, 0xfe200c87, 0x9ba10882, 0x27091765, 0x01000008, 0x02800380, 0x48113f4e, 0x29430cf5, 0x09a75a0b, 0x31618020, + 0x6d802009, 0x61840e33, 0x8208bf51, 0x0c637d61, 0x7f092379, 0x4f470f4b, 0x1797510c, 0x46076157, 0xf5500fdf, 0x0f616910, 0x1171fe20, 0x82802006, + 0x08696908, 0x41127a4c, 0x3f4a15f3, 0x01042607, 0x0200ff00, 0x1cf77700, 0xff204185, 0x00235b8d, 0x43100000, 0x3b22243f, 0x3b4d3f00, 0x0b937709, + 0xad42f18f, 0x0b1f420f, 0x51084b43, 0x8020104a, 0xb557ff83, 0x052b7f2a, 0x0280ff22, 0x250beb78, 0x00170013, 0xbf6d2500, 0x07db760e, 0x410e2b7f, + 0x00230e4f, 0x49030000, 0x0582055b, 0x07000326, 0x00000b00, 0x580bcd46, 0x00200cdd, 0x57078749, 0x8749160f, 0x0f994f0a, 0x41134761, 0x01200b31, + 0xeb796883, 0x0b41500b, 0x0e90b38e, 0x202e7b51, 0x05d95801, 0x41080570, 0x1d530fc9, 0x0b937a0f, 0xaf8eb387, 0xf743b98f, 0x07c74227, 0x80000523, + 0x0fcb4503, 0x430ca37b, 0x7782077f, 0x8d0a9947, 0x08af4666, 0xeb798020, 0x6459881e, 0xc3740bbf, 0x0feb6f0b, 0x20072748, 0x052b6102, 0x435e0584, + 0x7d088308, 0x03200afd, 0x92109e41, 0x28aa8210, 0x80001500, 0x80030000, 0x0fdb5805, 0x209f4018, 0xa7418d87, 0x0aa3440f, 0x20314961, 0x073a52ff, + 0x6108505d, 0x43181051, 0x00223457, 0xe7820500, 0x50028021, 0x81410d33, 0x063d7108, 0xdb41af84, 0x4d888205, 0x00201198, 0x463d835f, 0x152106d7, + 0x0a355a33, 0x6917614e, 0x75411f4d, 0x184b8b07, 0x1809c344, 0x21091640, 0x0b828000, 0x42808021, 0x26790519, 0x86058605, 0x2428422d, 0x22123b42, + 0x42000080, 0xf587513b, 0x7813677b, 0xaf4d139f, 0x00ff210c, 0x5e0a1d57, 0x3b421546, 0x01032736, 0x02000380, 0x41180480, 0x2f420f07, 0x0c624807, + 0x00000025, 0x18000103, 0x83153741, 0x430120c3, 0x042106b2, 0x088d4d00, 0x2f830620, 0x1810434a, 0x18140345, 0x8507fb41, 0x5ee582ea, 0x0023116c, + 0x8d000600, 0x053b56af, 0xa6554fa2, 0x0d704608, 0x40180d20, 0x47181a43, 0xd37b07ff, 0x0b79500c, 0x420fd745, 0x47450bd9, 0x8471830a, 0x095a777e, + 0x84137542, 0x82002013, 0x2f401800, 0x0007213b, 0x4405e349, 0x0d550ff3, 0x16254c0c, 0x820ffe4a, 0x0400218a, 0x89066f41, 0x106b414f, 0xc84d0120, + 0x80802206, 0x0c9a4b03, 0x00100025, 0x68000200, 0x9d8c2473, 0x44134344, 0xf36a0f33, 0x4678860f, 0x1b440a25, 0x41988c0a, 0x80201879, 0x43079b5e, + 0x4a18080b, 0x0341190b, 0x1259530c, 0x43251552, 0x908205c8, 0x0cac4018, 0x86000421, 0x0e504aa2, 0x0020b891, 0xfb450082, 0x51132014, 0x8f5205f3, + 0x35052108, 0x8505cb59, 0x0f6d4f70, 0x82150021, 0x29af5047, 0x4f004b24, 0x75795300, 0x1b595709, 0x460b6742, 0xbf4b0f0d, 0x5743870b, 0xcb6d1461, + 0x08f64505, 0x4e05ab6c, 0x334126c3, 0x0bcb6b0d, 0x1811034d, 0x4111ef4b, 0x814f1ce5, 0x20af8227, 0x07fd7b80, 0x41188e84, 0xef410f33, 0x80802429, + 0x410d0000, 0xa34205ab, 0x76b7881c, 0xff500b89, 0x0741430f, 0x20086f4a, 0x209d8200, 0x234c18fd, 0x05d4670a, 0x4509af51, 0x9642078d, 0x189e831d, + 0x7c1cc74b, 0xcd4c07b9, 0x0e7c440f, 0x8b7b0320, 0x21108210, 0xc76c8080, 0x03002106, 0x6b23bf41, 0xc549060b, 0x7946180b, 0x0ff7530f, 0x17ad4618, + 0x200ecd45, 0x208c83fd, 0x5e0488fe, 0x032009c6, 0x420d044e, 0x0d8f0d7f, 0x00820020, 0x18001021, 0x6d273b45, 0xfd4c0c93, 0xcf451813, 0x0fe5450f, + 0x5a47c382, 0x820a8b0a, 0x282b4998, 0x410a8b5b, 0x4b232583, 0x54004f00, 0x978f0ce3, 0x500f1944, 0xa95f1709, 0x0280220b, 0x05ba7080, 0xa1530682, + 0x06324c13, 0x91412582, 0x05536e2c, 0x63431020, 0x0f434706, 0x8c11374c, 0x176143d7, 0x4d0f454c, 0xd3680bed, 0x0bee4d17, 0x212b9a41, 0x0f530a00, + 0x140d531c, 0x43139143, 0x95610e8d, 0x0f094415, 0x4205fb56, 0x1b4205cf, 0x17015225, 0x5e0c477f, 0xaf6e0aeb, 0x0ff36218, 0x04849a84, 0x0a454218, + 0x9c430420, 0x23c6822b, 0x04000102, 0x45091b4b, 0xf05f0955, 0x82802007, 0x421c2023, 0x5218282b, 0x7b53173f, 0x0fe7480c, 0x74173b7f, 0x47751317, + 0x634d1807, 0x0f6f430f, 0x24086547, 0xfc808002, 0x0b3c7f80, 0x10840120, 0x188d1282, 0x20096b43, 0x0fc24403, 0x00260faf, 0x0180000b, 0x3f500280, + 0x18002019, 0x450b4941, 0xf3530fb9, 0x18002010, 0x8208a551, 0x06234d56, 0xcb58a39b, 0xc3421805, 0x1313461e, 0x0f855018, 0xd34b0120, 0x6cfe2008, + 0x574f0885, 0x09204114, 0x07000029, 0x00008000, 0x44028002, 0x01420f57, 0x10c95c10, 0x11184c18, 0x80221185, 0x7f421e00, 0x00732240, 0x09cd4977, + 0x6d0b2b42, 0x4f180f8f, 0x8f5a0bcb, 0x9b0f830f, 0x0fb9411f, 0x230b5756, 0x00fd8080, 0x82060745, 0x000121d5, 0x8e0fb277, 0x4a8d4211, 0x24061c53, + 0x04000007, 0x12275280, 0x430c954c, 0x80201545, 0x200f764f, 0x20008200, 0x20ce8308, 0x09534f02, 0x660edf64, 0x73731771, 0xe7411807, 0x20a2820c, + 0x13b64404, 0x8f5d6682, 0x1d6b4508, 0x0cff4d18, 0x3348c58f, 0x0fc34c07, 0x31558b84, 0x8398820f, 0x17514712, 0x240b0e46, 0x80000a00, 0x093b4502, + 0x420f9759, 0xa54c0bf1, 0x0f2b470c, 0x410d314b, 0x2584170c, 0x73b30020, 0xb55fe782, 0x204d8410, 0x08e043fe, 0x4f147e41, 0x022008ab, 0x4b055159, + 0x2950068f, 0x00022208, 0x48511880, 0x82002009, 0x00112300, 0x634dff00, 0x24415f27, 0x180f6d43, 0x4d0b5d45, 0x4d5f05ef, 0x01802317, 0x56188000, + 0xa7840807, 0xc6450220, 0x21ca8229, 0x4b781a00, 0x3359182c, 0x0cf3470f, 0x180bef46, 0x420b0354, 0xff470b07, 0x4515200a, 0x9758239b, 0x4a80200c, + 0xd2410a26, 0x05fb4a08, 0x4b05e241, 0x03200dc9, 0x92290941, 0x00002829, 0x00010900, 0x5b020001, 0x23201363, 0x460d776a, 0xef530fdb, 0x209a890c, + 0x13fc4302, 0x00008024, 0xc4820104, 0x08820220, 0x20086b5b, 0x18518700, 0x8408d349, 0x0da449a1, 0x00080024, 0x7b690280, 0x4c438b1a, 0x01220f63, + 0x4c878000, 0x5c149c53, 0xfb430868, 0x2f56181e, 0x0ccf7b1b, 0x0f075618, 0x2008e347, 0x14144104, 0x00207f83, 0x00207b82, 0x201adf47, 0x16c35a13, + 0x540fdf47, 0x802006c8, 0x5418f185, 0x29430995, 0x00002419, 0x58001600, 0x5720316f, 0x4d051542, 0x4b7b1b03, 0x138f4707, 0xb747b787, 0x4aab8213, + 0x058305fc, 0x20115759, 0x82128401, 0x0a0b44e8, 0x46800121, 0xe64210d0, 0x82129312, 0x4bffdffe, 0x3b41171b, 0x9b27870f, 0x808022ff, 0x085c68fe, + 0x41800021, 0x01410b20, 0x001a213a, 0x47480082, 0x11374e12, 0x56130b4c, 0xdf4b0c65, 0x0b0f590b, 0x0f574c18, 0x830feb4b, 0x075f480f, 0x480b4755, + 0x40490b73, 0x80012206, 0x09d74280, 0x80fe8022, 0x80210e86, 0x056643ff, 0x10820020, 0x420b2646, 0x0b58391a, 0xd74c1808, 0x078b4e22, 0x2007f55f, + 0x4b491807, 0x83802017, 0x65aa82a7, 0x3152099e, 0x068b7616, 0x9b431220, 0x09bb742c, 0x500e376c, 0x8342179b, 0x0a4d5d0f, 0x8020a883, 0x180cd349, + 0x2016bb4b, 0x14476004, 0x84136c43, 0x08cf7813, 0x4f4c0520, 0x156f420f, 0x20085f42, 0x6fd3be03, 0xd4d30803, 0xa7411420, 0x004b222c, 0x0d3b614f, + 0x3f702120, 0x1393410a, 0x8f132745, 0x47421827, 0x41e08209, 0xb05e2bb9, 0x18b7410c, 0x18082647, 0x4107a748, 0xeb8826bf, 0x0ca76018, 0x733ecb41, + 0xd0410d83, 0x43ebaf2a, 0x0420067f, 0x721dab4c, 0x472005bb, 0x4105d341, 0x334844cb, 0x20dba408, 0x47d6ac00, 0x034e3aef, 0x0f8f421b, 0x930f134d, + 0x3521231f, 0xb7421533, 0x42f5ad0a, 0x1e961eaa, 0x17000022, 0x4c367b50, 0x7d491001, 0x0bf5520f, 0x4c18fda7, 0xb8460c55, 0x83fe2005, 0x00fe25b9, + 0x80000180, 0x9e751085, 0x261b5c12, 0x82110341, 0x001123fb, 0x4518fe80, 0xf38c2753, 0x6d134979, 0x295107a7, 0xaf5f180f, 0x0fe3660c, 0x180b6079, + 0x2007bd5f, 0x9aab9103, 0x2f4d1811, 0x05002109, 0x44254746, 0x1d200787, 0x450bab75, 0x4f180f57, 0x4f181361, 0x3b831795, 0xeb4b0120, 0x0b734805, + 0x84078f48, 0x2e1b47bc, 0x00203383, 0xaf065f45, 0x831520d7, 0x130f51a7, 0x1797bf97, 0x2b47d783, 0x18fe2005, 0x4a18a44f, 0xa64d086d, 0x1ab0410d, + 0x6205a258, 0xdbab069f, 0x4f06f778, 0xa963081d, 0x133b670a, 0x8323d141, 0x13195b23, 0x530f5e70, 0xe5ad0824, 0x58001421, 0x1f472b4b, 0x47bf410c, + 0x82000121, 0x83fe20cb, 0x07424404, 0x68068243, 0xd7ad0d3d, 0x00010d26, 0x80020000, 0x4a1c6f43, 0x23681081, 0x10a14f13, 0x8a070e57, 0x430a848f, + 0x7372243e, 0x4397a205, 0xb56c1021, 0x43978f0f, 0x64180505, 0x99aa0ff2, 0x0e000022, 0x20223341, 0x094b4f37, 0x074a3320, 0x2639410a, 0xfe208e84, + 0x8b0e0048, 0x508020a3, 0x9e4308fe, 0x073f4115, 0xe3480420, 0x0c9b5f1b, 0x7c137743, 0x9a95185b, 0x6122b148, 0x979b08df, 0x0fe36c18, 0x48109358, + 0x23441375, 0x0ffd5c0b, 0x180fc746, 0x2011d157, 0x07e95702, 0x58180120, 0x18770ac3, 0x51032008, 0x7d4118e3, 0x80802315, 0x3b4c1900, 0xbb5a1830, + 0x0ceb6109, 0x5b0b3d42, 0x4f181369, 0x4f180b8d, 0x4f180f75, 0x355a1b81, 0x200d820d, 0x18e483fd, 0x4528854f, 0x89420846, 0x1321411f, 0x44086b60, + 0x07421d77, 0x107d4405, 0x4113fd41, 0x5a181bf1, 0x4f180db3, 0x8021128f, 0x20f68280, 0x44a882fe, 0x334d249a, 0x052f6109, 0x1520c3a7, 0xef4eb783, + 0x4ec39b1b, 0xc4c90ee7, 0x20060b4d, 0x256f4905, 0x4d0cf761, 0xcf9b1f13, 0xa213d74e, 0x0e1145d4, 0x50135b42, 0xcb4e398f, 0x20d79f27, 0x08865d80, + 0x186d5018, 0xa90f7142, 0x067342d7, 0x3f450420, 0x65002021, 0xe3560771, 0x24d38f23, 0x15333531, 0x0eb94d01, 0x451c9f41, 0x384322fb, 0x00092108, + 0x19af6b18, 0x6e0c6f5a, 0xbd770bfb, 0x22bb7718, 0x20090f57, 0x25e74204, 0x4207275a, 0xdb5408ef, 0x1769450f, 0x1b1b5518, 0x210b1f57, 0x5e4c8001, + 0x55012006, 0x802107f1, 0x0a306a80, 0x45808021, 0x0d850b88, 0x31744f18, 0x1808ec54, 0x2009575b, 0x45ffa505, 0x1b420c73, 0x180f9f0f, 0x4a0cf748, + 0x501805b2, 0x00210f40, 0x4d118f80, 0xd6823359, 0x072b5118, 0x314ad7aa, 0x8fc79f08, 0x45d78b1f, 0xfe20058f, 0x23325118, 0x7b54d9b5, 0x9fc38f46, + 0x10bb410f, 0x41077b42, 0xc1410faf, 0x27cf441d, 0x46051b4f, 0x04200683, 0x2121d344, 0x8f530043, 0x8fcf9f0e, 0x21df8c1f, 0x50188000, 0x5d180e52, + 0xfd201710, 0x4405c341, 0xd68528e3, 0x20071f6b, 0x1b734305, 0x6b080957, 0x7d422b1f, 0x67002006, 0x7f8317b1, 0x2024cb48, 0x08676e00, 0x8749a39b, + 0x18132006, 0x410a6370, 0x8f490b47, 0x7e1f8f13, 0x551805c3, 0x4c180915, 0xfe200e2f, 0x244d5d18, 0x270bcf44, 0xff000019, 0x04800380, 0x5f253342, + 0xff520df7, 0x13274c18, 0x5542dd93, 0x0776181b, 0xf94a1808, 0x084a4c0c, 0x4308ea5b, 0xde831150, 0x7900fd21, 0x00492c1e, 0x060f4510, 0x17410020, + 0x0ce74526, 0x6206b341, 0x1f561083, 0x9d6c181b, 0x08a0500e, 0x112e4118, 0x60000421, 0xbf901202, 0x4408e241, 0xc7ab0513, 0xb40f0950, 0x055943c7, + 0x4f18ff20, 0xc9ae1cad, 0x32b34f18, 0x7a180120, 0x3d520a05, 0x53d1b40a, 0x80200813, 0x1b815018, 0x832bf86f, 0x67731847, 0x297f4308, 0x6418d54e, + 0x734213f7, 0x056b4b27, 0xdba5fe20, 0x1828aa4e, 0x2031a370, 0x06cb6101, 0x2040ad41, 0x07365300, 0x2558d985, 0x83fe200c, 0x0380211c, 0x542c4743, + 0x052006b7, 0x6021df45, 0x897b0707, 0x18d3c010, 0x20090e70, 0x1d5843ff, 0x540a0e44, 0x002126c5, 0x322f7416, 0x636a5720, 0x0f317409, 0x610fe159, + 0x294617e7, 0x08555213, 0x2006a75d, 0x6cec84fd, 0xfb5907be, 0x3a317405, 0x83808021, 0x180f20ea, 0x4626434a, 0x531818e3, 0xdb59172d, 0x0cbb460c, + 0x2013d859, 0x18b94502, 0x8f46188d, 0x77521842, 0x0a184e38, 0x9585fd20, 0x6a180684, 0xc64507e9, 0x51cbb230, 0xd3440cf3, 0x17ff6a0f, 0x450f5b42, + 0x276407c1, 0x4853180a, 0x21ccb010, 0xcf580013, 0x0c15442d, 0x410a1144, 0x1144359d, 0x5cfe2006, 0xa1410a43, 0x2bb64519, 0x2f5b7618, 0xb512b745, + 0x0cfd6fd1, 0x42089f59, 0xb8450c70, 0x0000232d, 0x50180900, 0xb9491ae3, 0x0fc37610, 0x01210f83, 0x0f3b4100, 0xa01b2742, 0x0ccd426f, 0x6e8f6f94, + 0x9c808021, 0xc7511870, 0x17c74b08, 0x9b147542, 0x44fe2079, 0xd5480c7e, 0x95ef861d, 0x101b597b, 0xf5417594, 0x9f471808, 0x86868d0e, 0x3733491c, + 0x690f4d6d, 0x43440b83, 0x1ba94c0b, 0x660cd16b, 0x802008ae, 0x74126448, 0xcb4f38a3, 0x2cb74b0b, 0x47137755, 0xe3971777, 0x1b5d0120, 0x057a4108, + 0x6e08664d, 0x17421478, 0x11af4208, 0x850c3f42, 0x08234f0c, 0x4321eb4a, 0xf3451095, 0x0f394e0f, 0x4310eb45, 0xc09707b1, 0x54431782, 0xaec08d1d, + 0x0f434dbb, 0x9f0c0b45, 0x0a3b4dbb, 0x4618bdc7, 0x536032eb, 0x17354213, 0x4d134169, 0xc7a30c2f, 0x4e254342, 0x174332cf, 0x43cdae17, 0x6b4706e4, + 0x0e16430d, 0x530b5542, 0x2f7c26bb, 0x13075f31, 0x43175342, 0x60181317, 0x6550114e, 0x28624710, 0x58070021, 0x59181683, 0x2d540cf5, 0x05d5660c, + 0x20090c7b, 0x0e157e02, 0x8000ff2b, 0x14000080, 0x80ff8000, 0x27137e03, 0x336a4b20, 0x0f817107, 0x13876e18, 0x730f2f7e, 0x2f450b75, 0x6d02200b, + 0x6d66094c, 0x4b802009, 0x15820a02, 0x2f45fe20, 0x5e032006, 0x00202fd9, 0x450af741, 0xeb412e0f, 0x0ff3411f, 0x420a8b65, 0xf7410eae, 0x1c664810, + 0x540e1145, 0xbfa509f3, 0x42302f58, 0x80200c35, 0xcb066c47, 0x4b1120c1, 0x41492abb, 0x34854110, 0xa7097b72, 0x251545c7, 0x4b2c7f56, 0xc5b40bab, + 0x940cd54e, 0x2e6151c8, 0x09f35f18, 0x4b420420, 0x09677121, 0x8f24f357, 0x1b5418e1, 0x08915a1f, 0x3143d894, 0x22541805, 0x1b9b4b0e, 0x8c0d3443, + 0x1400240d, 0x18ff8000, 0x582e6387, 0xf99b2b3b, 0x8807a550, 0x17a14790, 0x2184fd20, 0x5758fe20, 0x2354882c, 0x15000080, 0x5e056751, 0x334c2c2f, + 0x97c58f0c, 0x1fd7410f, 0x0d4d4018, 0x4114dc41, 0x04470ed6, 0x0dd54128, 0x00820020, 0x02011523, 0x22008700, 0x86480024, 0x0001240a, 0x8682001a, + 0x0002240b, 0x866c000e, 0x8a03200b, 0x8a042017, 0x0005220b, 0x22218614, 0x84060000, 0x86012017, 0x8212200f, 0x250b8519, 0x000d0001, 0x0b850031, + 0x07000224, 0x0b862600, 0x11000324, 0x0b862d00, 0x238a0420, 0x0a000524, 0x17863e00, 0x17840620, 0x01000324, 0x57820904, 0x0b85a783, 0x0b85a785, + 0x0b85a785, 0x22000325, 0x85007a00, 0x85a7850b, 0x85a7850b, 0x22a7850b, 0x82300032, 0x00342201, 0x0805862f, 0x35003131, 0x54207962, 0x74736972, + 0x47206e61, 0x6d6d6972, 0x65527265, 0x616c7567, 0x58545472, 0x6f725020, 0x43796767, 0x6e61656c, 0x30325454, 0x822f3430, 0x35313502, 0x79006200, + 0x54002000, 0x69007200, 0x74007300, 0x6e006100, 0x47200f82, 0x6d240f84, 0x65006d00, 0x52200982, 0x67240582, 0x6c007500, 0x72201d82, 0x54222b82, + 0x23825800, 0x19825020, 0x67006f22, 0x79220182, 0x1b824300, 0x3b846520, 0x1f825420, 0x41000021, 0x1422099b, 0x0b410000, 0x87088206, 0x01012102, + 0x78080982, 0x01020101, 0x01040103, 0x01060105, 0x01080107, 0x010a0109, 0x010c010b, 0x010e010d, 0x0110010f, 0x01120111, 0x01140113, 0x01160115, + 0x01180117, 0x011a0119, 0x011c011b, 0x011e011d, 0x0020011f, 0x00040003, 0x00060005, 0x00080007, 0x000a0009, 0x000c000b, 0x000e000d, 0x0010000f, + 0x00120011, 0x00140013, 0x00160015, 0x00180017, 0x001a0019, 0x001c001b, 0x001e001d, 0x08bb821f, 0x22002142, 0x24002300, 0x26002500, 0x28002700, + 0x2a002900, 0x2c002b00, 0x2e002d00, 0x30002f00, 0x32003100, 0x34003300, 0x36003500, 0x38003700, 0x3a003900, 0x3c003b00, 0x3e003d00, 0x40003f00, + 0x42004100, 0x4b09f382, 0x00450044, 0x00470046, 0x00490048, 0x004b004a, 0x004d004c, 0x004f004e, 0x00510050, 0x00530052, 0x00550054, 0x00570056, + 0x00590058, 0x005b005a, 0x005d005c, 0x005f005e, 0x01610060, 0x01220121, 0x01240123, 0x01260125, 0x01280127, 0x012a0129, 0x012c012b, 0x012e012d, + 0x0130012f, 0x01320131, 0x01340133, 0x01360135, 0x01380137, 0x013a0139, 0x013c013b, 0x013e013d, 0x0140013f, 0x00ac0041, 0x008400a3, 0x00bd0085, + 0x00e80096, 0x008e0086, 0x009d008b, 0x00a400a9, 0x008a00ef, 0x008300da, 0x00f20093, 0x008d00f3, 0x00880097, 0x00de00c3, 0x009e00f1, 0x00f500aa, + 0x00f600f4, 0x00ad00a2, 0x00c700c9, 0x006200ae, 0x00900063, 0x00cb0064, 0x00c80065, 0x00cf00ca, 0x00cd00cc, 0x00e900ce, 0x00d30066, 0x00d100d0, + 0x006700af, 0x009100f0, 0x00d400d6, 0x006800d5, 0x00ed00eb, 0x006a0089, 0x006b0069, 0x006c006d, 0x00a0006e, 0x0071006f, 0x00720070, 0x00750073, + 0x00760074, 0x00ea0077, 0x007a0078, 0x007b0079, 0x007c007d, 0x00a100b8, 0x007e007f, 0x00810080, 0x00ee00ec, 0x6e750eba, 0x646f6369, 0x78302365, + 0x31303030, 0x32200e8d, 0x33200e8d, 0x34200e8d, 0x35200e8d, 0x36200e8d, 0x37200e8d, 0x38200e8d, 0x39200e8d, 0x61200e8d, 0x62200e8d, 0x63200e8d, + 0x64200e8d, 0x65200e8d, 0x66200e8d, 0x31210e8c, 0x8d0e8d30, 0x8d3120ef, 0x8d3120ef, 0x8d3120ef, 0x8d3120ef, 0x8d3120ef, 0x8d3120ef, 0x8d3120ef, + 0x8d3120ef, 0x8d3120ef, 0x8d3120ef, 0x8d3120ef, 0x8d3120ef, 0x8d3120ef, 0x66312def, 0x6c656406, 0x04657465, 0x6f727545, 0x3820ec8c, 0x3820ec8d, + 0x3820ec8d, 0x3820ec8d, 0x3820ec8d, 0x3820ec8d, 0x3820ec8d, 0x3820ec8d, 0x3820ec8d, 0x3820ec8d, 0x3820ec8d, 0x3820ec8d, 0x3820ec8d, 0x3820ec8d, + 0x3820ec8d, 0x200ddc41, 0x0ddc4139, 0xef8d3920, 0xef8d3920, 0xef8d3920, 0xef8d3920, 0xef8d3920, 0xef8d3920, 0xef8d3920, 0xef8d3920, 0xef8d3920, + 0xef8d3920, 0xef8d3920, 0xef8d3920, 0xef8d3920, 0xef8d3920, 0x00663923, 0x48fa0500, 0x00f762f9, }; -static const unsigned int proggy_clean_13_fnt_size = 4647; -static const unsigned int proggy_clean_13_fnt_data[4648/4] = +static void GetDefaultCompressedFontDataTTF(const void** ttf_compressed_data, unsigned int* ttf_compressed_size) { - 0x03464d42, 0x00001a01, 0x40000d00, 0x01006400, 0x00000000, 0x50000101, 0x67676f72, 0x656c4379, 0x02006e61, 0x0000000f, 0x000a000d, 0x00800100, - 0x01000001, 0x03000000, 0x00000016, 0x676f7270, 0x635f7967, 0x6e61656c, 0x5f33315f, 0x6e702e30, 0xd0040067, 0x00000011, 0x2e000000, 0x07000e00, - 0x00000d00, 0x07000000, 0x010f0000, 0x36000000, 0x05003800, 0x01000d00, 0x07000000, 0x020f0000, 0x86000000, 0x07000e00, 0x00000d00, 0x07000000, - 0x030f0000, 0x07000000, 0x06001c00, 0x01000d00, 0x07000000, 0x040f0000, 0x15000000, 0x06001c00, 0x01000d00, 0x07000000, 0x050f0000, 0x23000000, - 0x06001c00, 0x01000d00, 0x07000000, 0x060f0000, 0x31000000, 0x06001c00, 0x01000d00, 0x07000000, 0x070f0000, 0xfc000000, 0x03003800, 0x02000d00, - 0x07000000, 0x080f0000, 0x54000000, 0x05003800, 0x01000d00, 0x07000000, 0x090f0000, 0x4d000000, 0x06001c00, 0x01000d00, 0x07000000, 0x0a0f0000, - 0xa8000000, 0x06001c00, 0x01000d00, 0x07000000, 0x0b0f0000, 0x6a000000, 0x04004600, 0x00000d00, 0x07000000, 0x0c0f0000, 0x74000000, 0x04004600, - 0x00000d00, 0x07000000, 0x0d0f0000, 0x88000000, 0x04004600, 0x03000d00, 0x07000000, 0x0e0f0000, 0x65000000, 0x04004600, 0x03000d00, 0x07000000, - 0x0f0f0000, 0x36000000, 0x07000e00, 0x00000d00, 0x07000000, 0x100f0000, 0x5a000000, 0x05003800, 0x00000d00, 0x07000000, 0x110f0000, 0x60000000, - 0x05003800, 0x00000d00, 0x07000000, 0x120f0000, 0xe4000000, 0x03004600, 0x01000d00, 0x07000000, 0x130f0000, 0xe0000000, 0x03004600, 0x01000d00, - 0x07000000, 0x140f0000, 0x66000000, 0x05003800, 0x00000d00, 0x07000000, 0x150f0000, 0x6c000000, 0x05003800, 0x00000d00, 0x07000000, 0x160f0000, - 0x72000000, 0x05003800, 0x00000d00, 0x07000000, 0x170f0000, 0xd8000000, 0x03004600, 0x00000d00, 0x07000000, 0x180f0000, 0xcc000000, 0x03004600, - 0x01000d00, 0x07000000, 0x190f0000, 0xc8000000, 0x03004600, 0x02000d00, 0x07000000, 0x1a0f0000, 0x78000000, 0x05003800, 0x00000d00, 0x07000000, - 0x1b0f0000, 0x84000000, 0x05003800, 0x00000d00, 0x07000000, 0x1c0f0000, 0x00000000, 0x15000000, 0xf9000d00, 0x070000ff, 0x1d0f0000, 0xb0000000, - 0x15000000, 0xf9000d00, 0x070000ff, 0x1e0f0000, 0x2c000000, 0x15000000, 0xf9000d00, 0x070000ff, 0x200f0000, 0x9a000000, 0x15000000, 0xf9000d00, - 0x070000ff, 0x210f0000, 0x0c000000, 0x01005400, 0x03000d00, 0x07000000, 0x220f0000, 0xbc000000, 0x03004600, 0x02000d00, 0x07000000, 0x230f0000, - 0x4e000000, 0x07000e00, 0x00000d00, 0x07000000, 0x240f0000, 0x8a000000, 0x05003800, 0x01000d00, 0x07000000, 0x250f0000, 0xa6000000, 0x07000e00, - 0x00000d00, 0x07000000, 0x260f0000, 0xf4000000, 0x06000e00, 0x01000d00, 0x07000000, 0x270f0000, 0x06000000, 0x01005400, 0x03000d00, 0x07000000, - 0x280f0000, 0xb8000000, 0x03004600, 0x02000d00, 0x07000000, 0x290f0000, 0xb4000000, 0x03004600, 0x02000d00, 0x07000000, 0x2a0f0000, 0x90000000, - 0x05003800, 0x01000d00, 0x07000000, 0x2b0f0000, 0x96000000, 0x05003800, 0x01000d00, 0x07000000, 0x2c0f0000, 0xe8000000, 0x02004600, 0x01000d00, - 0x07000000, 0x2d0f0000, 0x9c000000, 0x05003800, 0x01000d00, 0x07000000, 0x2e0f0000, 0x04000000, 0x01005400, 0x02000d00, 0x07000000, 0x2f0f0000, - 0xa2000000, 0x05003800, 0x01000d00, 0x07000000, 0x300f0000, 0xae000000, 0x05003800, 0x01000d00, 0x07000000, 0x310f0000, 0xd8000000, 0x05003800, - 0x01000d00, 0x07000000, 0x320f0000, 0xfa000000, 0x05000000, 0x01000d00, 0x07000000, 0x330f0000, 0x31000000, 0x05002a00, 0x01000d00, 0x07000000, - 0x340f0000, 0x3f000000, 0x06001c00, 0x01000d00, 0x07000000, 0x350f0000, 0x37000000, 0x05002a00, 0x01000d00, 0x07000000, 0x360f0000, 0x3d000000, - 0x05002a00, 0x01000d00, 0x07000000, 0x370f0000, 0x43000000, 0x05002a00, 0x01000d00, 0x07000000, 0x380f0000, 0x49000000, 0x05002a00, 0x01000d00, - 0x07000000, 0x390f0000, 0x4f000000, 0x05002a00, 0x01000d00, 0x07000000, 0x3a0f0000, 0x02000000, 0x01005400, 0x03000d00, 0x07000000, 0x3b0f0000, - 0xfa000000, 0x02004600, 0x01000d00, 0x07000000, 0x3c0f0000, 0x77000000, 0x06001c00, 0x00000d00, 0x07000000, 0x3d0f0000, 0x7e000000, 0x06001c00, - 0x01000d00, 0x07000000, 0x3e0f0000, 0x85000000, 0x06001c00, 0x01000d00, 0x07000000, 0x3f0f0000, 0x55000000, 0x05002a00, 0x01000d00, 0x07000000, - 0x400f0000, 0xae000000, 0x07000e00, 0x00000d00, 0x07000000, 0x410f0000, 0xe0000000, 0x06001c00, 0x01000d00, 0x07000000, 0x420f0000, 0xa1000000, - 0x06001c00, 0x01000d00, 0x07000000, 0x430f0000, 0x5b000000, 0x05002a00, 0x01000d00, 0x07000000, 0x440f0000, 0xaf000000, 0x06001c00, 0x01000d00, - 0x07000000, 0x450f0000, 0x61000000, 0x05002a00, 0x01000d00, 0x07000000, 0x460f0000, 0x67000000, 0x05002a00, 0x01000d00, 0x07000000, 0x470f0000, - 0x38000000, 0x06001c00, 0x01000d00, 0x07000000, 0x480f0000, 0x8c000000, 0x06001c00, 0x01000d00, 0x07000000, 0x490f0000, 0xa0000000, 0x03004600, - 0x02000d00, 0x07000000, 0x4a0f0000, 0x97000000, 0x04004600, 0x01000d00, 0x07000000, 0x4b0f0000, 0xb6000000, 0x06001c00, 0x01000d00, 0x07000000, - 0x4c0f0000, 0x6d000000, 0x05002a00, 0x01000d00, 0x07000000, 0x4d0f0000, 0x1e000000, 0x07000e00, 0x00000d00, 0x07000000, 0x4e0f0000, 0x23000000, - 0x06002a00, 0x01000d00, 0x07000000, 0x4f0f0000, 0xed000000, 0x06000e00, 0x01000d00, 0x07000000, 0x500f0000, 0x73000000, 0x05002a00, 0x01000d00, - 0x07000000, 0x510f0000, 0x00000000, 0x06001c00, 0x01000d00, 0x07000000, 0x520f0000, 0x0e000000, 0x06001c00, 0x01000d00, 0x07000000, 0x530f0000, - 0x1c000000, 0x06001c00, 0x01000d00, 0x07000000, 0x540f0000, 0x66000000, 0x07000e00, 0x00000d00, 0x07000000, 0x550f0000, 0x2a000000, 0x06001c00, - 0x01000d00, 0x07000000, 0x560f0000, 0x6e000000, 0x07000e00, 0x00000d00, 0x07000000, 0x570f0000, 0x76000000, 0x07000e00, 0x00000d00, 0x07000000, - 0x580f0000, 0x46000000, 0x06001c00, 0x01000d00, 0x07000000, 0x590f0000, 0x7e000000, 0x07000e00, 0x00000d00, 0x07000000, 0x5a0f0000, 0x54000000, - 0x06001c00, 0x01000d00, 0x07000000, 0x5b0f0000, 0x9c000000, 0x03004600, 0x02000d00, 0x07000000, 0x5c0f0000, 0x79000000, 0x05002a00, 0x01000d00, - 0x07000000, 0x5d0f0000, 0xdc000000, 0x03004600, 0x02000d00, 0x07000000, 0x5e0f0000, 0x7f000000, 0x05002a00, 0x01000d00, 0x07000000, 0x5f0f0000, - 0xc6000000, 0x07000e00, 0x00000d00, 0x07000000, 0x600f0000, 0xfd000000, 0x02004600, 0x02000d00, 0x07000000, 0x610f0000, 0x85000000, 0x05002a00, - 0x01000d00, 0x07000000, 0x620f0000, 0x8b000000, 0x05002a00, 0x01000d00, 0x07000000, 0x630f0000, 0x91000000, 0x05002a00, 0x01000d00, 0x07000000, - 0x640f0000, 0x97000000, 0x05002a00, 0x01000d00, 0x07000000, 0x650f0000, 0x9d000000, 0x05002a00, 0x01000d00, 0x07000000, 0x660f0000, 0xa3000000, - 0x05002a00, 0x01000d00, 0x07000000, 0x670f0000, 0xa9000000, 0x05002a00, 0x01000d00, 0x07000000, 0x680f0000, 0xaf000000, 0x05002a00, 0x01000d00, - 0x07000000, 0x690f0000, 0xee000000, 0x02004600, 0x02000d00, 0x07000000, 0x6a0f0000, 0x92000000, 0x04004600, 0x01000d00, 0x07000000, 0x6b0f0000, - 0xb5000000, 0x05002a00, 0x01000d00, 0x07000000, 0x6c0f0000, 0xfd000000, 0x02002a00, 0x02000d00, 0x07000000, 0x6d0f0000, 0x8e000000, 0x07000e00, - 0x00000d00, 0x07000000, 0x6e0f0000, 0xbb000000, 0x05002a00, 0x01000d00, 0x07000000, 0x6f0f0000, 0xc1000000, 0x05002a00, 0x01000d00, 0x07000000, - 0x700f0000, 0xc7000000, 0x05002a00, 0x01000d00, 0x07000000, 0x710f0000, 0xcd000000, 0x05002a00, 0x01000d00, 0x07000000, 0x720f0000, 0xd3000000, - 0x05002a00, 0x01000d00, 0x07000000, 0x730f0000, 0xd9000000, 0x05002a00, 0x01000d00, 0x07000000, 0x740f0000, 0x7e000000, 0x04004600, 0x02000d00, - 0x07000000, 0x750f0000, 0xdf000000, 0x05002a00, 0x01000d00, 0x07000000, 0x760f0000, 0xe5000000, 0x05002a00, 0x01000d00, 0x07000000, 0x770f0000, - 0xbe000000, 0x07000e00, 0x00000d00, 0x07000000, 0x780f0000, 0xeb000000, 0x05002a00, 0x01000d00, 0x07000000, 0x790f0000, 0xf1000000, 0x05002a00, - 0x01000d00, 0x07000000, 0x7a0f0000, 0xf7000000, 0x05002a00, 0x01000d00, 0x07000000, 0x7b0f0000, 0x00000000, 0x05003800, 0x01000d00, 0x07000000, - 0x7c0f0000, 0x00000000, 0x01005400, 0x03000d00, 0x07000000, 0x7d0f0000, 0x06000000, 0x05003800, 0x01000d00, 0x07000000, 0x7e0f0000, 0x16000000, - 0x07000e00, 0x00000d00, 0x07000000, 0x7f0f0000, 0x58000000, 0x15000000, 0xf9000d00, 0x070000ff, 0x810f0000, 0x16000000, 0x15000000, 0xf9000d00, - 0x070000ff, 0x8d0f0000, 0x00000000, 0x15000e00, 0xf9000d00, 0x070000ff, 0x8f0f0000, 0xc6000000, 0x15000000, 0xf9000d00, 0x070000ff, 0x900f0000, - 0x6e000000, 0x15000000, 0xf9000d00, 0x070000ff, 0x9d0f0000, 0x84000000, 0x15000000, 0xf9000d00, 0x070000ff, 0xa00f0000, 0xdc000000, 0x15000000, - 0xf9000d00, 0x070000ff, 0xa10f0000, 0x0a000000, 0x01005400, 0x03000d00, 0x07000000, 0xa20f0000, 0x0c000000, 0x05003800, 0x01000d00, 0x07000000, - 0xa30f0000, 0x12000000, 0x05003800, 0x01000d00, 0x07000000, 0xa40f0000, 0x96000000, 0x07000e00, 0x00000d00, 0x07000000, 0xa50f0000, 0x5e000000, - 0x07000e00, 0x00000d00, 0x07000000, 0xa60f0000, 0x08000000, 0x01005400, 0x03000d00, 0x07000000, 0xa70f0000, 0x18000000, 0x05003800, 0x01000d00, - 0x07000000, 0xa80f0000, 0xac000000, 0x03004600, 0x02000d00, 0x07000000, 0xa90f0000, 0x56000000, 0x07000e00, 0x00000d00, 0x07000000, 0xaa0f0000, - 0x8d000000, 0x04004600, 0x01000d00, 0x07000000, 0xab0f0000, 0x1e000000, 0x05003800, 0x01000d00, 0x07000000, 0xac0f0000, 0xfb000000, 0x04000e00, - 0x01000d00, 0x07000000, 0xad0f0000, 0x42000000, 0x15000000, 0xf9000d00, 0x070000ff, 0xae0f0000, 0x3e000000, 0x07000e00, 0x00000d00, 0x07000000, - 0xaf0f0000, 0x26000000, 0x07000e00, 0x00000d00, 0x07000000, 0xb00f0000, 0x6f000000, 0x04004600, 0x01000d00, 0x07000000, 0xb10f0000, 0x24000000, - 0x05003800, 0x01000d00, 0x07000000, 0xb20f0000, 0x79000000, 0x04004600, 0x01000d00, 0x07000000, 0xb30f0000, 0x83000000, 0x04004600, 0x01000d00, - 0x07000000, 0xb40f0000, 0xeb000000, 0x02004600, 0x03000d00, 0x07000000, 0xb50f0000, 0x46000000, 0x07000e00, 0x00000d00, 0x07000000, 0xb60f0000, - 0xe6000000, 0x06000e00, 0x01000d00, 0x07000000, 0xb70f0000, 0xc0000000, 0x03004600, 0x02000d00, 0x07000000, 0xb80f0000, 0xf7000000, 0x02004600, - 0x03000d00, 0x07000000, 0xb90f0000, 0xc4000000, 0x03004600, 0x01000d00, 0x07000000, 0xba0f0000, 0x60000000, 0x04004600, 0x01000d00, 0x07000000, - 0xbb0f0000, 0x2a000000, 0x05003800, 0x01000d00, 0x07000000, 0xbc0f0000, 0x1c000000, 0x06002a00, 0x01000d00, 0x07000000, 0xbd0f0000, 0xc4000000, - 0x06001c00, 0x01000d00, 0x07000000, 0xbe0f0000, 0x9e000000, 0x07000e00, 0x00000d00, 0x07000000, 0xbf0f0000, 0x30000000, 0x05003800, 0x01000d00, - 0x07000000, 0xc00f0000, 0x9a000000, 0x06001c00, 0x01000d00, 0x07000000, 0xc10f0000, 0x93000000, 0x06001c00, 0x01000d00, 0x07000000, 0xc20f0000, - 0x70000000, 0x06001c00, 0x01000d00, 0x07000000, 0xc30f0000, 0x69000000, 0x06001c00, 0x01000d00, 0x07000000, 0xc40f0000, 0x62000000, 0x06001c00, - 0x01000d00, 0x07000000, 0xc50f0000, 0x5b000000, 0x06001c00, 0x01000d00, 0x07000000, 0xc60f0000, 0xf2000000, 0x07000000, 0x00000d00, 0x07000000, - 0xc70f0000, 0xbd000000, 0x06001c00, 0x01000d00, 0x07000000, 0xc80f0000, 0x3c000000, 0x05003800, 0x01000d00, 0x07000000, 0xc90f0000, 0x42000000, - 0x05003800, 0x01000d00, 0x07000000, 0xca0f0000, 0x48000000, 0x05003800, 0x01000d00, 0x07000000, 0xcb0f0000, 0x4e000000, 0x05003800, 0x01000d00, - 0x07000000, 0xcc0f0000, 0xa4000000, 0x03004600, 0x02000d00, 0x07000000, 0xcd0f0000, 0xb0000000, 0x03004600, 0x02000d00, 0x07000000, 0xce0f0000, - 0xa8000000, 0x03004600, 0x02000d00, 0x07000000, 0xcf0f0000, 0xfc000000, 0x03001c00, 0x02000d00, 0x07000000, 0xd00f0000, 0xce000000, 0x07000e00, - 0x00000d00, 0x07000000, 0xd10f0000, 0xcb000000, 0x06001c00, 0x01000d00, 0x07000000, 0xd20f0000, 0xd2000000, 0x06001c00, 0x01000d00, 0x07000000, - 0xd30f0000, 0xd9000000, 0x06001c00, 0x01000d00, 0x07000000, 0xd40f0000, 0x2a000000, 0x06002a00, 0x01000d00, 0x07000000, 0xd50f0000, 0xe7000000, - 0x06001c00, 0x01000d00, 0x07000000, 0xd60f0000, 0xee000000, 0x06001c00, 0x01000d00, 0x07000000, 0xd70f0000, 0x7e000000, 0x05003800, 0x01000d00, - 0x07000000, 0xd80f0000, 0xf5000000, 0x06001c00, 0x01000d00, 0x07000000, 0xd90f0000, 0x00000000, 0x06002a00, 0x01000d00, 0x07000000, 0xda0f0000, - 0x07000000, 0x06002a00, 0x01000d00, 0x07000000, 0xdb0f0000, 0x0e000000, 0x06002a00, 0x01000d00, 0x07000000, 0xdc0f0000, 0x15000000, 0x06002a00, - 0x01000d00, 0x07000000, 0xdd0f0000, 0xd6000000, 0x07000e00, 0x00000d00, 0x07000000, 0xde0f0000, 0xa8000000, 0x05003800, 0x01000d00, 0x07000000, - 0xdf0f0000, 0xde000000, 0x07000e00, 0x00000d00, 0x07000000, 0xe00f0000, 0xb4000000, 0x05003800, 0x01000d00, 0x07000000, 0xe10f0000, 0xba000000, - 0x05003800, 0x01000d00, 0x07000000, 0xe20f0000, 0xc0000000, 0x05003800, 0x01000d00, 0x07000000, 0xe30f0000, 0xc6000000, 0x05003800, 0x01000d00, - 0x07000000, 0xe40f0000, 0xcc000000, 0x05003800, 0x01000d00, 0x07000000, 0xe50f0000, 0xd2000000, 0x05003800, 0x01000d00, 0x07000000, 0xe60f0000, - 0xb6000000, 0x07000e00, 0x00000d00, 0x07000000, 0xe70f0000, 0xde000000, 0x05003800, 0x01000d00, 0x07000000, 0xe80f0000, 0xe4000000, 0x05003800, - 0x01000d00, 0x07000000, 0xe90f0000, 0xea000000, 0x05003800, 0x01000d00, 0x07000000, 0xea0f0000, 0xf0000000, 0x05003800, 0x01000d00, 0x07000000, - 0xeb0f0000, 0xf6000000, 0x05003800, 0x01000d00, 0x07000000, 0xec0f0000, 0xf1000000, 0x02004600, 0x02000d00, 0x07000000, 0xed0f0000, 0xf4000000, - 0x02004600, 0x02000d00, 0x07000000, 0xee0f0000, 0xd0000000, 0x03004600, 0x02000d00, 0x07000000, 0xef0f0000, 0xd4000000, 0x03004600, 0x02000d00, - 0x07000000, 0xf00f0000, 0x00000000, 0x05004600, 0x01000d00, 0x07000000, 0xf10f0000, 0x06000000, 0x05004600, 0x01000d00, 0x07000000, 0xf20f0000, - 0x0c000000, 0x05004600, 0x01000d00, 0x07000000, 0xf30f0000, 0x12000000, 0x05004600, 0x01000d00, 0x07000000, 0xf40f0000, 0x18000000, 0x05004600, - 0x01000d00, 0x07000000, 0xf50f0000, 0x1e000000, 0x05004600, 0x01000d00, 0x07000000, 0xf60f0000, 0x24000000, 0x05004600, 0x01000d00, 0x07000000, - 0xf70f0000, 0x2a000000, 0x05004600, 0x01000d00, 0x07000000, 0xf80f0000, 0x30000000, 0x05004600, 0x01000d00, 0x07000000, 0xf90f0000, 0x36000000, - 0x05004600, 0x01000d00, 0x07000000, 0xfa0f0000, 0x3c000000, 0x05004600, 0x01000d00, 0x07000000, 0xfb0f0000, 0x42000000, 0x05004600, 0x01000d00, - 0x07000000, 0xfc0f0000, 0x48000000, 0x05004600, 0x01000d00, 0x07000000, 0xfd0f0000, 0x4e000000, 0x05004600, 0x01000d00, 0x07000000, 0xfe0f0000, - 0x54000000, 0x05004600, 0x01000d00, 0x07000000, 0xff0f0000, 0x5a000000, 0x05004600, 0x01000d00, 0x07000000, 0x000f0000, -}; - -void ImGui::GetDefaultFontData(const void** fnt_data, unsigned int* fnt_size, const void** png_data, unsigned int* png_size) -{ - if (fnt_data) *fnt_data = (const void*)proggy_clean_13_fnt_data; - if (fnt_size) *fnt_size = proggy_clean_13_fnt_size; - if (png_data) *png_data = (const void*)proggy_clean_13_png_data; - if (png_size) *png_size = proggy_clean_13_png_size; + *ttf_compressed_data = proggy_clean_ttf_compressed_data; + *ttf_compressed_size = proggy_clean_ttf_compressed_size; } //----------------------------------------------------------------------------- diff --git a/ext/imgui/imgui.h b/ext/imgui/imgui.h index 6d050a3ccc..8331d747c2 100644 --- a/ext/imgui/imgui.h +++ b/ext/imgui/imgui.h @@ -1,52 +1,63 @@ -// ImGui library v1.18 wip -// See .cpp file for commentary. +// ImGui library v1.39 WIP +// See .cpp file for documentation. // See ImGui::ShowTestWindow() for sample code. // Read 'Programmer guide' in .cpp for notes on how to setup ImGui in your codebase. // Get latest version at https://github.com/ocornut/imgui #pragma once -struct ImDrawList; -struct ImFont; -struct ImGuiAabb; -struct ImGuiIO; -struct ImGuiStorage; -struct ImGuiStyle; -struct ImGuiWindow; - -#include "imconfig.h" +#include "imconfig.h" // User-editable configuration file #include // FLT_MAX #include // va_list -#include // ptrdiff_t -#include // NULL, malloc +#include // ptrdiff_t, NULL +#include // NULL, malloc, free, qsort, atoi +#include // memset, memmove, memcpy, strlen, strchr, strcpy, strcmp +#define IMGUI_VERSION "1.39 WIP" + +// Define assertion handler. #ifndef IM_ASSERT #include #define IM_ASSERT(_EXPR) assert(_EXPR) #endif +// Define attributes of all API symbols declarations, e.g. for DLL under Windows. #ifndef IMGUI_API #define IMGUI_API #endif +// Forward declarations +struct ImDrawCmd; +struct ImDrawList; +struct ImFont; +struct ImFontAtlas; +struct ImGuiIO; +struct ImGuiStorage; +struct ImGuiStyle; + typedef unsigned int ImU32; -typedef unsigned short ImWchar; // hold a character for display -typedef ImU32 ImGuiID; // hold widget unique ID +typedef unsigned short ImWchar; // character for keyboard input/display +typedef void* ImTextureID; // user data to refer to a texture (e.g. store your texture handle/id) +typedef ImU32 ImGuiID; // unique ID used by widgets (typically hashed from a stack of string) typedef int ImGuiCol; // enum ImGuiCol_ typedef int ImGuiStyleVar; // enum ImGuiStyleVar_ typedef int ImGuiKey; // enum ImGuiKey_ +typedef int ImGuiAlign; // enum ImGuiAlign_ typedef int ImGuiColorEditMode; // enum ImGuiColorEditMode_ +typedef int ImGuiMouseCursor; // enum ImGuiMouseCursor_ typedef int ImGuiWindowFlags; // enum ImGuiWindowFlags_ +typedef int ImGuiSetCond; // enum ImGuiSetCond_ typedef int ImGuiInputTextFlags; // enum ImGuiInputTextFlags_ -struct ImGuiTextEditCallbackData; +struct ImGuiTextEditCallbackData; // for advanced uses of InputText() +typedef int (*ImGuiTextEditCallback)(ImGuiTextEditCallbackData *data); struct ImVec2 { float x, y; - ImVec2() {} + ImVec2() { x = y = 0.0f; } ImVec2(float _x, float _y) { x = _x; y = _y; } -#ifdef IM_VEC2_CLASS_EXTRA +#ifdef IM_VEC2_CLASS_EXTRA // Define constructor and implicit cast operators to convert back<>forth from your math types and ImVec2. IM_VEC2_CLASS_EXTRA #endif }; @@ -54,25 +65,610 @@ struct ImVec2 struct ImVec4 { float x, y, z, w; - ImVec4() {} + ImVec4() { x = y = z = w = 0.0f; } ImVec4(float _x, float _y, float _z, float _w) { x = _x; y = _y; z = _z; w = _w; } -#ifdef IM_VEC4_CLASS_EXTRA +#ifdef IM_VEC4_CLASS_EXTRA // Define constructor and implicit cast operators to convert back<>forth from your math types and ImVec4. IM_VEC4_CLASS_EXTRA #endif }; +// Helpers at bottom of the file: +// - class ImVector<> // Lightweight std::vector like class. Use '#define ImVector std::vector' if you want to use the STL type or your own type. +// - IMGUI_ONCE_UPON_A_FRAME // Execute a block of code once per frame only (convenient for creating UI within deep-nested code that runs multiple times) +// - struct ImGuiTextFilter // Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" +// - struct ImGuiTextBuffer // Text buffer for logging/accumulating text +// - struct ImGuiStorage // Custom key value storage (if you need to alter open/close states manually) +// - struct ImGuiTextEditCallbackData // Shared state of ImGui::InputText() when using custom callbacks +// - struct ImColor // Helper functions to created packed 32-bit RGBA color values +// - struct ImDrawList // Draw command list +// - struct ImFontAtlas // Bake multiple fonts into a single texture, TTF font loader, bake glyphs into bitmap +// - struct ImFont // Single font + +// ImGui end-user API +// In a namespace so that user can add extra functions in a separate file (e.g. Value() helpers for your vector or common types) namespace ImGui { - // Proxy functions to access the MemAllocFn/MemFreeFn/MemReallocFn pointers in ImGui::GetIO(). The only reason they exist here is to allow ImVector<> to compile inline. - IMGUI_API void* MemAlloc(size_t sz); - IMGUI_API void MemFree(void* ptr); - IMGUI_API void* MemRealloc(void* ptr, size_t sz); -} + // Main + IMGUI_API ImGuiIO& GetIO(); + IMGUI_API ImGuiStyle& GetStyle(); + IMGUI_API void NewFrame(); + IMGUI_API void Render(); + IMGUI_API void Shutdown(); + IMGUI_API void ShowUserGuide(); // help block + IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); // style editor block + IMGUI_API void ShowTestWindow(bool* opened = NULL); // test window, demonstrate ImGui features + IMGUI_API void ShowMetricsWindow(bool* opened = NULL); // metrics window for debugging imgui -// std::vector<> like class to avoid dragging dependencies (also: windows implementation of STL with debug enabled is absurdly slow, so let's bypass it so our code runs fast in debug). + // Window + // See implementation in .cpp for details + IMGUI_API bool Begin(const char* name = "Debug", bool* p_opened = NULL, ImGuiWindowFlags flags = 0); // return false when window is collapsed, so you can early out in your code. 'bool* p_opened' creates a widget on the upper-right to close the window (which sets your bool to false). + IMGUI_API bool Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_use, float bg_alpha = -1.0f, ImGuiWindowFlags flags = 0); // this is the older/longer API. call SetNextWindowSize() instead if you want to set a window size. For regular windows, 'size_on_first_use' only applies to the first time EVER the window is created and probably not what you want! maybe obsolete this API eventually. + IMGUI_API void End(); + IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags extra_flags = 0); // size==0.0f: use remaining window size, size<0.0f: use remaining window size minus abs(size). size>0.0f: fixed size. each axis can use a different mode, e.g. ImVec2(0,400). + IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags extra_flags = 0); // " + IMGUI_API void EndChild(); + IMGUI_API ImVec2 GetContentRegionMax(); // window or current column boundaries, in windows coordinates + IMGUI_API ImVec2 GetWindowContentRegionMin(); // window boundaries, in windows coordinates + IMGUI_API ImVec2 GetWindowContentRegionMax(); + IMGUI_API ImDrawList* GetWindowDrawList(); // get rendering command-list if you want to append your own draw primitives + IMGUI_API ImFont* GetWindowFont(); + IMGUI_API float GetWindowFontSize(); // size (also height in pixels) of current font with current scale applied + IMGUI_API void SetWindowFontScale(float scale); // per-window font scale. Adjust IO.FontGlobalScale if you want to scale all windows + IMGUI_API ImVec2 GetWindowPos(); // you should rarely need/care about the window position, but it can be useful if you want to do your own drawing + IMGUI_API ImVec2 GetWindowSize(); // get current window position + IMGUI_API float GetWindowWidth(); + IMGUI_API bool GetWindowCollapsed(); + + IMGUI_API void SetNextWindowPos(const ImVec2& pos, ImGuiSetCond cond = 0); // set next window position - call before Begin() + IMGUI_API void SetNextWindowSize(const ImVec2& size, ImGuiSetCond cond = 0); // set next window size. set to ImVec2(0,0) to force an auto-fit + IMGUI_API void SetNextWindowCollapsed(bool collapsed, ImGuiSetCond cond = 0); // set next window collapsed state + IMGUI_API void SetNextWindowFocus(); // set next window to be focused / front-most + IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiSetCond cond = 0); // set current window position - call within Begin()/End(). may incur tearing + IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiSetCond cond = 0); // set current window size. set to ImVec2(0,0) to force an auto-fit. may incur tearing + IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiSetCond cond = 0); // set current window collapsed state + IMGUI_API void SetWindowFocus(); // set current window to be focused / front-most + IMGUI_API void SetWindowPos(const char* name, const ImVec2& pos, ImGuiSetCond cond = 0); // set named window position - call within Begin()/End(). may incur tearing + IMGUI_API void SetWindowSize(const char* name, const ImVec2& size, ImGuiSetCond cond = 0); // set named window size. set to ImVec2(0,0) to force an auto-fit. may incur tearing + IMGUI_API void SetWindowCollapsed(const char* name, bool collapsed, ImGuiSetCond cond = 0); // set named window collapsed state + IMGUI_API void SetWindowFocus(const char* name); // set named window to be focused / front-most. use NULL to remove focus. + + IMGUI_API float GetScrollPosY(); // get scrolling position [0..GetScrollMaxY()] + IMGUI_API float GetScrollMaxY(); // get maximum scrolling position == ContentSize.Y - WindowSize.Y + IMGUI_API void SetScrollPosHere(); // adjust scrolling position to center into the current cursor position + IMGUI_API void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget + IMGUI_API void SetStateStorage(ImGuiStorage* tree); // replace tree state storage with our own (if you want to manipulate it yourself, typically clear subsection of it) + IMGUI_API ImGuiStorage* GetStateStorage(); + + // Parameters stacks (shared) + IMGUI_API void PushFont(ImFont* font); // use NULL as a shortcut to push default font + IMGUI_API void PopFont(); + IMGUI_API void PushStyleColor(ImGuiCol idx, const ImVec4& col); + IMGUI_API void PopStyleColor(int count = 1); + IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); + IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); + IMGUI_API void PopStyleVar(int count = 1); + + // Parameters stacks (current window) + IMGUI_API void PushItemWidth(float item_width); // width of items for the common item+label case, pixels. 0.0f = default to ~2/3 of windows width, >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -1.0f always align width to the right side) + IMGUI_API void PopItemWidth(); + IMGUI_API float CalcItemWidth(); // width of item given pushed settings and current cursor position + IMGUI_API void PushAllowKeyboardFocus(bool v); // allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets + IMGUI_API void PopAllowKeyboardFocus(); + IMGUI_API void PushTextWrapPos(float wrap_pos_x = 0.0f); // word-wrapping for Text*() commands. < 0.0f: no wrapping; 0.0f: wrap to end of window (or column); > 0.0f: wrap at 'wrap_pos_x' position in window local space + IMGUI_API void PopTextWrapPos(); + + // Tooltip + IMGUI_API void SetTooltip(const char* fmt, ...); // set tooltip under mouse-cursor, typically use with ImGui::IsHovered(). last call wins + IMGUI_API void SetTooltipV(const char* fmt, va_list args); + IMGUI_API void BeginTooltip(); // use to create full-featured tooltip windows that aren't just text + IMGUI_API void EndTooltip(); + + // Popup + IMGUI_API void BeginPopup(bool* p_opened); + IMGUI_API void EndPopup(); + + // Layout + IMGUI_API void BeginGroup(); + IMGUI_API void EndGroup(); + IMGUI_API void Separator(); // horizontal line + IMGUI_API void SameLine(int column_x = 0, int spacing_w = -1); // call between widgets or groups to layout them horizontally + IMGUI_API void Spacing(); // add vertical spacing + IMGUI_API void Indent(); // move content position toward the right by style.IndentSpacing pixels + IMGUI_API void Unindent(); // move content position back to the left (cancel Indent) + IMGUI_API void Columns(int count = 1, const char* id = NULL, bool border=true); // setup number of columns. use an identifier to distinguish multiple column sets. close with Columns(1). + IMGUI_API void NextColumn(); // next column + IMGUI_API int GetColumnIndex(); // get current column index + IMGUI_API float GetColumnOffset(int column_index = -1); // get position of column line (in pixels, from the left side of the contents region). pass -1 to use current column, otherwise 0..GetcolumnsCount() inclusive. column 0 is usually 0.0f and not resizable unless you call this + IMGUI_API void SetColumnOffset(int column_index, float offset_x); // set position of column line (in pixels, from the left side of the contents region). pass -1 to use current column + IMGUI_API float GetColumnWidth(int column_index = -1); // column width (== GetColumnOffset(GetColumnIndex()+1) - GetColumnOffset(GetColumnOffset()) + IMGUI_API int GetColumnsCount(); // number of columns (what was passed to Columns()) + IMGUI_API ImVec2 GetCursorPos(); // cursor position is relative to window position + IMGUI_API float GetCursorPosX(); // " + IMGUI_API float GetCursorPosY(); // " + IMGUI_API void SetCursorPos(const ImVec2& pos); // " + IMGUI_API void SetCursorPosX(float x); // " + IMGUI_API void SetCursorPosY(float y); // " + IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute screen coordinates [0..io.DisplaySize] + IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in absolute screen coordinates [0..io.DisplaySize] + IMGUI_API void AlignFirstTextHeightToWidgets(); // call once if the first item on the line is a Text() item and you want to vertically lower it to match subsequent (bigger) widgets + IMGUI_API float GetTextLineHeight(); // height of font == GetWindowFontSize() + IMGUI_API float GetTextLineHeightWithSpacing(); // spacing (in pixels) between 2 consecutive lines of text == GetWindowFontSize() + GetStyle().ItemSpacing.y + + // ID scopes + // If you are creating widgets in a loop you most likely want to push a unique identifier so ImGui can differentiate them + // You can also use "##extra" within your widget name to distinguish them from each others (see 'Programmer Guide') + IMGUI_API void PushID(const char* str_id); // push identifier into the ID stack. IDs are hash of the *entire* stack! + IMGUI_API void PushID(const char* str_id_begin, const char* str_id_end); + IMGUI_API void PushID(const void* ptr_id); + IMGUI_API void PushID(const int int_id); + IMGUI_API void PopID(); + IMGUI_API ImGuiID GetID(const char* str_id); // calculate unique ID (hash of whole ID stack + given parameter). useful if you want to query into ImGuiStorage yourself. otherwise rarely needed + IMGUI_API ImGuiID GetID(const char* str_id_begin, const char* str_id_end); + IMGUI_API ImGuiID GetID(const void* ptr_id); + + // Widgets + IMGUI_API void Text(const char* fmt, ...); + IMGUI_API void TextV(const char* fmt, va_list args); + IMGUI_API void TextColored(const ImVec4& col, const char* fmt, ...); // shortcut for PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor(); + IMGUI_API void TextColoredV(const ImVec4& col, const char* fmt, va_list args); + IMGUI_API void TextWrapped(const char* fmt, ...); // shortcut for PushTextWrapPos(0.0f); Text(fmt, ...); PopTextWrapPos(); + IMGUI_API void TextWrappedV(const char* fmt, va_list args); + IMGUI_API void TextUnformatted(const char* text, const char* text_end = NULL); // doesn't require null terminated string if 'text_end' is specified. no copy done to any bounded stack buffer, recommended for long chunks of text + IMGUI_API void LabelText(const char* label, const char* fmt, ...); // display text+label aligned the same way as value+label widgets + IMGUI_API void LabelTextV(const char* label, const char* fmt, va_list args); + IMGUI_API void Bullet(); + IMGUI_API void BulletText(const char* fmt, ...); + IMGUI_API void BulletTextV(const char* fmt, va_list args); + IMGUI_API bool Button(const char* label, const ImVec2& size = ImVec2(0,0), bool repeat_when_held = false); + IMGUI_API bool SmallButton(const char* label); + IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size); + IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0)); + IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0,0,0,1), const ImVec4& tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no padding + IMGUI_API bool CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false); + IMGUI_API bool Checkbox(const char* label, bool* v); + IMGUI_API bool CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value); + IMGUI_API bool RadioButton(const char* label, bool active); + IMGUI_API bool RadioButton(const char* label, int* v, int v_button); + IMGUI_API bool Combo(const char* label, int* current_item, const char** items, int items_count, int height_in_items = -1); + IMGUI_API bool Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items = -1); // separate items with \0, end item-list with \0\0 + IMGUI_API bool Combo(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1); + IMGUI_API bool ColorButton(const ImVec4& col, bool small_height = false, bool outline_border = true); + IMGUI_API bool ColorEdit3(const char* label, float col[3]); + IMGUI_API bool ColorEdit4(const char* label, float col[4], bool show_alpha = true); + IMGUI_API void ColorEditMode(ImGuiColorEditMode mode); + IMGUI_API void PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0), size_t stride = sizeof(float)); + IMGUI_API void PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0)); + IMGUI_API void PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0), size_t stride = sizeof(float)); + IMGUI_API void PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0)); + + // Widgets: Sliders (tip: ctrl+click on a slider to input text) + IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); // adjust display_format to decorate the value with a prefix or a suffix. Use power!=1.0 for logarithmic sliders + IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); + IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); + IMGUI_API bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); + IMGUI_API bool SliderAngle(const char* label, float* v_rad, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f); + IMGUI_API bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format = "%.0f"); + IMGUI_API bool SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format = "%.0f"); + IMGUI_API bool SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format = "%.0f"); + IMGUI_API bool SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format = "%.0f"); + IMGUI_API bool VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); + IMGUI_API bool VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format = "%.0f"); + + // Widgets: Drags (tip: ctrl+click on a drag box to input text) + IMGUI_API bool DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); // If v_min >= v_max we have no bound + IMGUI_API bool DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); + IMGUI_API bool DragFloat3(const char* label, float v[3], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); + IMGUI_API bool DragFloat4(const char* label, float v[4], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); + IMGUI_API bool DragInt(const char* label, int* v, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); // If v_min >= v_max we have no bound + IMGUI_API bool DragInt2(const char* label, int v[2], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); + IMGUI_API bool DragInt3(const char* label, int v[3], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); + IMGUI_API bool DragInt4(const char* label, int v[4], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); + + // Widgets: Input + IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputFloat2(const char* label, float v[2], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputFloat3(const char* label, float v[3], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputFloat4(const char* label, float v[4], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputInt(const char* label, int* v, int step = 1, int step_fast = 100, ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags = 0); + + // Widgets: Trees + IMGUI_API bool TreeNode(const char* str_label_id); // if returning 'true' the node is open and the user is responsible for calling TreePop + IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...); // " + IMGUI_API bool TreeNode(const void* ptr_id, const char* fmt, ...); // " + IMGUI_API bool TreeNodeV(const char* str_id, const char* fmt, va_list args); // " + IMGUI_API bool TreeNodeV(const void* ptr_id, const char* fmt, va_list args); // " + IMGUI_API void TreePush(const char* str_id = NULL); // already called by TreeNode(), but you can call Push/Pop yourself for layouting purpose + IMGUI_API void TreePush(const void* ptr_id = NULL); // " + IMGUI_API void TreePop(); + IMGUI_API void SetNextTreeNodeOpened(bool opened, ImGuiSetCond cond = 0); // set next tree node to be opened. + + // Widgets: Selectable / Lists + IMGUI_API bool Selectable(const char* label, bool selected = false, const ImVec2& size = ImVec2(0,0)); + IMGUI_API bool Selectable(const char* label, bool* p_selected, const ImVec2& size = ImVec2(0,0)); + IMGUI_API bool ListBox(const char* label, int* current_item, const char** items, int items_count, int height_in_items = -1); + IMGUI_API bool ListBox(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1); + IMGUI_API bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0,0)); // use if you want to reimplement ListBox() will custom data or interactions. make sure to call ListBoxFooter() afterwards. + IMGUI_API bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1); // " + IMGUI_API void ListBoxFooter(); // terminate the scrolling region + + // Widgets: Menus + // FIXME-WIP: v1.39 in development + IMGUI_API bool MenuItem(const char* label, const char* shortcut = NULL, bool selected = false); // bool enabled = true + IMGUI_API bool MenuItem(const char* label, const char* shortcut, bool* p_selected); // bool enabled = true + + // Widgets: Value() Helpers. Output single value in "name: value" format (tip: freely declare your own within the ImGui namespace!) + IMGUI_API void Value(const char* prefix, bool b); + IMGUI_API void Value(const char* prefix, int v); + IMGUI_API void Value(const char* prefix, unsigned int v); + IMGUI_API void Value(const char* prefix, float v, const char* float_format = NULL); + IMGUI_API void Color(const char* prefix, const ImVec4& v); + IMGUI_API void Color(const char* prefix, unsigned int v); + + // Logging: all text output from interface is redirected to tty/file/clipboard. Tree nodes are automatically opened. + IMGUI_API void LogToTTY(int max_depth = -1); // start logging to tty + IMGUI_API void LogToFile(int max_depth = -1, const char* filename = NULL); // start logging to file + IMGUI_API void LogToClipboard(int max_depth = -1); // start logging to OS clipboard + IMGUI_API void LogFinish(); // stop logging (close file, etc.) + IMGUI_API void LogButtons(); // helper to display buttons for logging to tty/file/clipboard + IMGUI_API void LogText(const char* fmt, ...); // pass text data straight to log (without being displayed) + + // Utilities + IMGUI_API bool IsItemHovered(); // was the last item hovered by mouse? + IMGUI_API bool IsItemHoveredRect(); // was the last item hovered by mouse? even if another item is active while we are hovering this + IMGUI_API bool IsItemActive(); // was the last item active? (e.g. button being held, text field being edited- items that don't interact will always return false) + IMGUI_API bool IsAnyItemActive(); // + IMGUI_API bool IsItemVisible(); + IMGUI_API ImVec2 GetItemRectMin(); // get bounding rect of last item in screen space + IMGUI_API ImVec2 GetItemRectMax(); // " + IMGUI_API ImVec2 GetItemRectSize(); // " + IMGUI_API bool IsWindowFocused(); // is current window focused (differentiate child windows from each others) + IMGUI_API bool IsRootWindowFocused(); // is current root window focused + IMGUI_API bool IsRootWindowOrAnyChildFocused(); // is current root window or any of its child (including current window) focused + IMGUI_API bool IsRectClipped(const ImVec2& size); // test if rectangle of given size starting from cursor pos is out of clipping region. to perform coarse clipping on user's side (as an optimization) + IMGUI_API bool IsKeyDown(int key_index); // key_index into the keys_down[512] array, imgui doesn't know the semantic of each entry + IMGUI_API bool IsKeyPressed(int key_index, bool repeat = true); // " + IMGUI_API bool IsMouseDown(int button); + IMGUI_API bool IsMouseClicked(int button, bool repeat = false); + IMGUI_API bool IsMouseDoubleClicked(int button); + IMGUI_API bool IsMouseHoveringWindow(); // is mouse hovering current window ("window" in API names always refer to current window) + IMGUI_API bool IsMouseHoveringAnyWindow(); // is mouse hovering any active imgui window + IMGUI_API bool IsMouseHoveringRect(const ImVec2& rect_min, const ImVec2& rect_max);// is mouse hovering given bounding rect + IMGUI_API bool IsMouseDragging(int button = 0, float lock_threshold = -1.0f); // is mouse dragging. if lock_threshold < -1.0f uses io.MouseDraggingThreshold + IMGUI_API bool IsPosHoveringAnyWindow(const ImVec2& pos); // is given position hovering any active imgui window + IMGUI_API ImVec2 GetMousePos(); // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls + IMGUI_API ImVec2 GetMouseDragDelta(int button = 0, float lock_threshold = -1.0f); // dragging amount since clicking, also see: GetItemActiveDragDelta(). if lock_threshold < -1.0f uses io.MouseDraggingThreshold + IMGUI_API void ResetMouseDragDelta(int button = 0); + IMGUI_API ImGuiMouseCursor GetMouseCursor(); // get desired cursor type, reset in ImGui::NewFrame(), this updated during the frame. valid before Render(). If you use software rendering by setting io.MouseDrawCursor ImGui will render those for you + IMGUI_API void SetMouseCursor(ImGuiMouseCursor type); // set desired cursor type + IMGUI_API float GetTime(); + IMGUI_API int GetFrameCount(); + IMGUI_API const char* GetStyleColName(ImGuiCol idx); + IMGUI_API ImVec2 CalcItemRectClosestPoint(const ImVec2& pos, bool on_edge = false, float outward = +0.0f); // utility to find the closest point the last item bounding rectangle edge. useful to visually link items + IMGUI_API ImVec2 CalcTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_double_hash = false, float wrap_width = -1.0f); + IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // helper to manually clip large list of items. see comments in implementation + + IMGUI_API void BeginChildFrame(ImGuiID id, const ImVec2& size); // helper to create a child window / scrolling region that looks like a normal widget frame + IMGUI_API void EndChildFrame(); + + IMGUI_API ImU32 ColorConvertFloat4ToU32(const ImVec4& in); + IMGUI_API void ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v); + IMGUI_API void ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b); + + // Proxy functions to access the MemAllocFn/MemFreeFn pointers in ImGui::GetIO() + IMGUI_API void* MemAlloc(size_t sz); + IMGUI_API void MemFree(void* ptr); + + // Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself + IMGUI_API const char* GetVersion(); + IMGUI_API void* GetInternalState(); + IMGUI_API size_t GetInternalStateSize(); + IMGUI_API void SetInternalState(void* state, bool construct = false); + + // Obsolete (will be removed) + IMGUI_API void GetDefaultFontData(const void** fnt_data, unsigned int* fnt_size, const void** png_data, unsigned int* png_size); // OBSOLETE + static inline void OpenNextNode(bool open) { ImGui::SetNextTreeNodeOpened(open, 0); } // OBSOLETE + static inline bool GetWindowIsFocused() { return ImGui::IsWindowFocused(); } // OBSOLETE + static inline ImVec2 GetItemBoxMin() { return GetItemRectMin(); } // OBSOLETE + static inline ImVec2 GetItemBoxMax() { return GetItemRectMax(); } // OBSOLETE + static inline bool IsClipped(const ImVec2& size) { return IsRectClipped(size); } // OBSOLETE + static inline bool IsMouseHoveringBox(const ImVec2& rect_min, const ImVec2& rect_max) { return IsMouseHoveringRect(rect_min, rect_max); } // OBSOLETE + +} // namespace ImGui + +// Flags for ImGui::Begin() +enum ImGuiWindowFlags_ +{ + // Default: 0 + ImGuiWindowFlags_NoTitleBar = 1 << 0, // Disable title-bar + ImGuiWindowFlags_NoResize = 1 << 1, // Disable user resizing with the lower-right grip + ImGuiWindowFlags_NoMove = 1 << 2, // Disable user moving the window + ImGuiWindowFlags_NoScrollbar = 1 << 3, // Disable scrollbar (window can still scroll with mouse or programatically) + ImGuiWindowFlags_NoScrollWithMouse = 1 << 4, // Disable user scrolling with mouse wheel + ImGuiWindowFlags_NoCollapse = 1 << 5, // Disable user collapsing window by double-clicking on it + ImGuiWindowFlags_AlwaysAutoResize = 1 << 6, // Resize every window to its content every frame + ImGuiWindowFlags_ShowBorders = 1 << 7, // Show borders around windows and items + ImGuiWindowFlags_NoSavedSettings = 1 << 8, // Never load/save settings in .ini file + // [Internal] + ImGuiWindowFlags_ChildWindow = 1 << 9, // For internal use by BeginChild() + ImGuiWindowFlags_ChildWindowAutoFitX = 1 << 10, // For internal use by BeginChild() + ImGuiWindowFlags_ChildWindowAutoFitY = 1 << 11, // For internal use by BeginChild() + ImGuiWindowFlags_ComboBox = 1 << 12, // For internal use by ComboBox() + ImGuiWindowFlags_Tooltip = 1 << 13, // For internal use by BeginTooltip() + ImGuiWindowFlags_Popup = 1 << 14 // For internal use by BeginPopup() +}; + +// Flags for ImGui::InputText() +enum ImGuiInputTextFlags_ +{ + // Default: 0 + ImGuiInputTextFlags_CharsDecimal = 1 << 0, // Allow 0123456789.+-*/ + ImGuiInputTextFlags_CharsHexadecimal = 1 << 1, // Allow 0123456789ABCDEFabcdef + ImGuiInputTextFlags_CharsUppercase = 1 << 2, // Turn a..z into A..Z + ImGuiInputTextFlags_CharsNoBlank = 1 << 3, // Filter out spaces, tabs + ImGuiInputTextFlags_AutoSelectAll = 1 << 4, // Select entire text when first taking mouse focus + ImGuiInputTextFlags_EnterReturnsTrue = 1 << 5, // Return 'true' when Enter is pressed (as opposed to when the value was modified) + ImGuiInputTextFlags_CallbackCompletion = 1 << 6, // Call user function on pressing TAB (for completion handling) + ImGuiInputTextFlags_CallbackHistory = 1 << 7, // Call user function on pressing Up/Down arrows (for history handling) + ImGuiInputTextFlags_CallbackAlways = 1 << 8, // Call user function every time + ImGuiInputTextFlags_CallbackCharFilter = 1 << 9 // Call user function to filter character. Modify data->EventChar to replace/filter input, or return 1 to discard character. +}; + +// User fill ImGuiIO.KeyMap[] array with indices into the ImGuiIO.KeysDown[512] array +enum ImGuiKey_ +{ + ImGuiKey_Tab, + ImGuiKey_LeftArrow, + ImGuiKey_RightArrow, + ImGuiKey_UpArrow, + ImGuiKey_DownArrow, + ImGuiKey_Home, + ImGuiKey_End, + ImGuiKey_Delete, + ImGuiKey_Backspace, + ImGuiKey_Enter, + ImGuiKey_Escape, + ImGuiKey_A, // for CTRL+A: select all + ImGuiKey_C, // for CTRL+C: copy + ImGuiKey_V, // for CTRL+V: paste + ImGuiKey_X, // for CTRL+X: cut + ImGuiKey_Y, // for CTRL+Y: redo + ImGuiKey_Z, // for CTRL+Z: undo + ImGuiKey_COUNT +}; + +// Enumeration for PushStyleColor() / PopStyleColor() +enum ImGuiCol_ +{ + ImGuiCol_Text, + ImGuiCol_WindowBg, + ImGuiCol_ChildWindowBg, + ImGuiCol_Border, + ImGuiCol_BorderShadow, + ImGuiCol_FrameBg, // Background of checkbox, radio button, plot, slider, text input + ImGuiCol_FrameBgHovered, + ImGuiCol_FrameBgActive, + ImGuiCol_TitleBg, + ImGuiCol_TitleBgCollapsed, + ImGuiCol_ScrollbarBg, + ImGuiCol_ScrollbarGrab, + ImGuiCol_ScrollbarGrabHovered, + ImGuiCol_ScrollbarGrabActive, + ImGuiCol_ComboBg, + ImGuiCol_CheckMark, + ImGuiCol_SliderGrab, + ImGuiCol_SliderGrabActive, + ImGuiCol_Button, + ImGuiCol_ButtonHovered, + ImGuiCol_ButtonActive, + ImGuiCol_Header, + ImGuiCol_HeaderHovered, + ImGuiCol_HeaderActive, + ImGuiCol_Column, + ImGuiCol_ColumnHovered, + ImGuiCol_ColumnActive, + ImGuiCol_ResizeGrip, + ImGuiCol_ResizeGripHovered, + ImGuiCol_ResizeGripActive, + ImGuiCol_CloseButton, + ImGuiCol_CloseButtonHovered, + ImGuiCol_CloseButtonActive, + ImGuiCol_PlotLines, + ImGuiCol_PlotLinesHovered, + ImGuiCol_PlotHistogram, + ImGuiCol_PlotHistogramHovered, + ImGuiCol_TextSelectedBg, + ImGuiCol_TooltipBg, + ImGuiCol_COUNT +}; + +// Enumeration for PushStyleVar() / PopStyleVar() +// NB: the enum only refers to fields of ImGuiStyle() which makes sense to be pushed/poped in UI code. Feel free to add others. +enum ImGuiStyleVar_ +{ + ImGuiStyleVar_Alpha, // float + ImGuiStyleVar_WindowPadding, // ImVec2 + ImGuiStyleVar_WindowRounding, // float + ImGuiStyleVar_ChildWindowRounding, // float + ImGuiStyleVar_FramePadding, // ImVec2 + ImGuiStyleVar_FrameRounding, // float + ImGuiStyleVar_ItemSpacing, // ImVec2 + ImGuiStyleVar_ItemInnerSpacing, // ImVec2 + ImGuiStyleVar_IndentSpacing, // float + ImGuiStyleVar_GrabMinSize // float +}; + +enum ImGuiAlign_ +{ + ImGuiAlign_Left = 1 << 0, + ImGuiAlign_Center = 1 << 1, + ImGuiAlign_Right = 1 << 2, + ImGuiAlign_Default = ImGuiAlign_Left, +}; + +// Enumeration for ColorEditMode() +enum ImGuiColorEditMode_ +{ + ImGuiColorEditMode_UserSelect = -2, + ImGuiColorEditMode_UserSelectShowButton = -1, + ImGuiColorEditMode_RGB = 0, + ImGuiColorEditMode_HSV = 1, + ImGuiColorEditMode_HEX = 2 +}; + +// Enumeration for GetMouseCursor() +enum ImGuiMouseCursor_ +{ + ImGuiMouseCursor_Arrow = 0, + ImGuiMouseCursor_TextInput, // When hovering over InputText, etc. + ImGuiMouseCursor_Move, // Unused + ImGuiMouseCursor_ResizeNS, // Unused + ImGuiMouseCursor_ResizeEW, // When hovering over a column + ImGuiMouseCursor_ResizeNESW, // Unused + ImGuiMouseCursor_ResizeNWSE, // When hovering over the bottom-right corner of a window + ImGuiMouseCursor_Count_ +}; + +// Condition flags for ImGui::SetWindow***(), SetNextWindow***(), SetNextTreeNode***() functions +// All those functions treat 0 as a shortcut to ImGuiSetCond_Always +enum ImGuiSetCond_ +{ + ImGuiSetCond_Always = 1 << 0, // Set the variable + ImGuiSetCond_Once = 1 << 1, // Only set the variable on the first call per runtime session + ImGuiSetCond_FirstUseEver = 1 << 2, // Only set the variable if the window doesn't exist in the .ini file + ImGuiSetCond_Appearing = 1 << 3 // Only set the variable if the window is appearing after being inactive (or the first time) +}; + +struct ImGuiStyle +{ + float Alpha; // Global alpha applies to everything in ImGui + ImVec2 WindowPadding; // Padding within a window + ImVec2 WindowMinSize; // Minimum window size + float WindowRounding; // Radius of window corners rounding. Set to 0.0f to have rectangular windows + ImGuiAlign WindowTitleAlign; // Alignment for title bar text + float ChildWindowRounding; // Radius of child window corners rounding. Set to 0.0f to have rectangular windows + ImVec2 FramePadding; // Padding within a framed rectangle (used by most widgets) + float FrameRounding; // Radius of frame corners rounding. Set to 0.0f to have rectangular frame (used by most widgets). + ImVec2 ItemSpacing; // Horizontal and vertical spacing between widgets/lines + ImVec2 ItemInnerSpacing; // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label) + ImVec2 TouchExtraPadding; // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! + float WindowFillAlphaDefault; // Default alpha of window background, if not specified in ImGui::Begin() + float IndentSpacing; // Horizontal indentation when e.g. entering a tree node + float ColumnsMinSpacing; // Minimum horizontal spacing between two columns + float ScrollbarWidth; // Width of the vertical scrollbar + float ScrollbarRounding; // Radius of grab corners for scrollbar + float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar + ImVec2 DisplayWindowPadding; // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows. + ImVec2 DisplaySafeAreaPadding; // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. + ImVec4 Colors[ImGuiCol_COUNT]; + + IMGUI_API ImGuiStyle(); +}; + +// This is where your app communicate with ImGui. Call ImGui::GetIO() to access. +// Read 'Programmer guide' section in .cpp file for general usage. +struct ImGuiIO +{ + //------------------------------------------------------------------ + // Settings (fill once) // Default value: + //------------------------------------------------------------------ + + ImVec2 DisplaySize; // // Display size, in pixels. For clamping windows positions. + float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. + float IniSavingRate; // = 5.0f // Maximum time between saving positions/sizes to .ini file, in seconds. + const char* IniFilename; // = "imgui.ini" // Path to .ini file. NULL to disable .ini saving. + const char* LogFilename; // = "imgui_log.txt" // Path to .log file (default parameter to ImGui::LogToFile when no file is specified). + float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. + float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. + float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging + int KeyMap[ImGuiKey_COUNT]; // // Map of indices into the KeysDown[512] entries array + void* UserData; // = NULL // Store your own data for retrieval by callbacks. + + ImFontAtlas* Fonts; // // Load and assemble one or more fonts into a single tightly packed texture. Output to Fonts array. + float FontGlobalScale; // = 1.0f // Global scale all fonts + bool FontAllowUserScaling; // = false // Allow user scaling text of individual window with CTRL+Wheel. + ImVec2 DisplayVisibleMin; // (0.0f,0.0f) // If you use DisplaySize as a virtual space larger than your screen, set DisplayVisibleMin/Max to the visible area. + ImVec2 DisplayVisibleMax; // (0.0f,0.0f) // If the values are the same, we defaults to Min=(0.0f) and Max=DisplaySize + + //------------------------------------------------------------------ + // User Functions + //------------------------------------------------------------------ + + // REQUIRED: rendering function. + // See example code if you are unsure of how to implement this. + void (*RenderDrawListsFn)(ImDrawList** const draw_lists, int count); + + // Optional: access OS clipboard + // (default to use native Win32 clipboard on Windows, otherwise uses a private clipboard. Override to access OS clipboard on other architectures) + const char* (*GetClipboardTextFn)(); + void (*SetClipboardTextFn)(const char* text); + + // Optional: override memory allocations. MemFreeFn() may be called with a NULL pointer. + // (default to posix malloc/free) + void* (*MemAllocFn)(size_t sz); + void (*MemFreeFn)(void* ptr); + + // Optional: notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME in Windows) + // (default to use native imm32 api on Windows) + void (*ImeSetInputScreenPosFn)(int x, int y); + void* ImeWindowHandle; // (Windows) Set this to your HWND to get automatic IME cursor positioning. + + //------------------------------------------------------------------ + // Input - Fill before calling NewFrame() + //------------------------------------------------------------------ + + ImVec2 MousePos; // Mouse position, in pixels (set to -1,-1 if no mouse / on another screen, etc.) + bool MouseDown[5]; // Mouse buttons. ImGui itself only uses button 0 (left button). Others buttons allows to track if mouse is being used by your application + available to user as a convenience via IsMouse** API. + float MouseWheel; // Mouse wheel: 1 unit scrolls about 5 lines text. + bool MouseDrawCursor; // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). + bool KeyCtrl; // Keyboard modifier pressed: Control + bool KeyShift; // Keyboard modifier pressed: Shift + bool KeyAlt; // Keyboard modifier pressed: Alt + bool KeysDown[512]; // Keyboard keys that are pressed (in whatever storage order you naturally have access to keyboard data) + ImWchar InputCharacters[16+1]; // List of characters input (translated by user from keypress+keyboard state). Fill using AddInputCharacter() helper. + + // Function + IMGUI_API void AddInputCharacter(ImWchar c); // Helper to add a new character into InputCharacters[] + + //------------------------------------------------------------------ + // Output - Retrieve after calling NewFrame(), you can use them to discard inputs or hide them from the rest of your application + //------------------------------------------------------------------ + + bool WantCaptureMouse; // Mouse is hovering a window or widget is active (= ImGui will use your mouse input) + bool WantCaptureKeyboard; // Widget is active (= ImGui will use your keyboard input) + float Framerate; // Framerate estimation, in frame per second. Rolling average estimation based on IO.DeltaTime over 120 frames + int MetricsRenderVertices; // Vertices processed during last call to Render() + int MetricsActiveWindows; // Number of visible windows (exclude child windows) + + //------------------------------------------------------------------ + // [Internal] ImGui will maintain those fields for you + //------------------------------------------------------------------ + + ImVec2 MousePosPrev; // Previous mouse position + ImVec2 MouseDelta; // Mouse delta. Note that this is zero if either current or previous position are negative to allow mouse enabling/disabling. + bool MouseClicked[5]; // Mouse button went from !Down to Down + ImVec2 MouseClickedPos[5]; // Position at time of clicking + float MouseClickedTime[5]; // Time of last click (used to figure out double-click) + bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? + bool MouseDownOwned[5]; // Track if button was clicked inside a window. We don't request mouse capture from the application if click started outside ImGui bounds. + float MouseDownTime[5]; // Time the mouse button has been down + float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the click point + float KeysDownTime[512]; // Time the keyboard key has been down + + IMGUI_API ImGuiIO(); +}; + +//----------------------------------------------------------------------------- +// Helpers +//----------------------------------------------------------------------------- + +// Lightweight std::vector<> like class to avoid dragging dependencies (also: windows implementation of STL with debug enabled is absurdly slow, so let's bypass it so our code runs fast in debug). // Use '#define ImVector std::vector' if you want to use the STL type or your own type. -// Our implementation does NOT call c++ constructors! because the data types we use don't need them (but that could be added as well). Only provide the minimum functionalities we need. +// Our implementation does NOT call c++ constructors because we don't use them in ImGui. Don't use this class as a straight std::vector replacement in your code! #ifndef ImVector template class ImVector @@ -104,426 +700,31 @@ public: inline const_iterator begin() const { return Data; } inline iterator end() { return Data + Size; } inline const_iterator end() const { return Data + Size; } - inline value_type& front() { return at(0); } - inline const value_type& front() const { return at(0); } - inline value_type& back() { IM_ASSERT(Size > 0); return at(Size-1); } - inline const value_type& back() const { IM_ASSERT(Size > 0); return at(Size-1); } + inline value_type& front() { IM_ASSERT(Size > 0); return Data[0]; } + inline const value_type& front() const { IM_ASSERT(Size > 0); return Data[0]; } + inline value_type& back() { IM_ASSERT(Size > 0); return Data[Size-1]; } + inline const value_type& back() const { IM_ASSERT(Size > 0); return Data[Size-1]; } inline void swap(ImVector& rhs) { const size_t rhs_size = rhs.Size; rhs.Size = Size; Size = rhs_size; const size_t rhs_cap = rhs.Capacity; rhs.Capacity = Capacity; Capacity = rhs_cap; value_type* rhs_data = rhs.Data; rhs.Data = Data; Data = rhs_data; } - inline void reserve(size_t new_capacity) { Data = (value_type*)ImGui::MemRealloc(Data, new_capacity * sizeof(value_type)); Capacity = new_capacity; } inline void resize(size_t new_size) { if (new_size > Capacity) reserve(new_size); Size = new_size; } + inline void reserve(size_t new_capacity) + { + if (new_capacity <= Capacity) return; + T* new_data = (value_type*)ImGui::MemAlloc(new_capacity * sizeof(value_type)); + memcpy(new_data, Data, Size * sizeof(value_type)); + ImGui::MemFree(Data); + Data = new_data; + Capacity = new_capacity; + } inline void push_back(const value_type& v) { if (Size == Capacity) reserve(Capacity ? Capacity * 2 : 4); Data[Size++] = v; } inline void pop_back() { IM_ASSERT(Size > 0); Size--; } inline iterator erase(const_iterator it) { IM_ASSERT(it >= begin() && it < end()); const ptrdiff_t off = it - begin(); memmove(Data + off, Data + off + 1, (Size - (size_t)off - 1) * sizeof(value_type)); Size--; return Data + off; } - inline void insert(const_iterator it, const value_type& v) { IM_ASSERT(it >= begin() && it <= end()); const ptrdiff_t off = it - begin(); if (Size == Capacity) reserve(Capacity ? Capacity * 2 : 4); if (off < (int)Size) memmove(Data + off + 1, Data + off, (Size - (size_t)off) * sizeof(value_type)); Data[off] = v; Size++; } + inline iterator insert(const_iterator it, const value_type& v) { IM_ASSERT(it >= begin() && it <= end()); const ptrdiff_t off = it - begin(); if (Size == Capacity) reserve(Capacity ? Capacity * 2 : 4); if (off < (int)Size) memmove(Data + off + 1, Data + off, (Size - (size_t)off) * sizeof(value_type)); Data[off] = v; Size++; return Data + off; } }; #endif // #ifndef ImVector -// Helpers at bottom of the file: -// - IMGUI_ONCE_UPON_A_FRAME // Execute a block of code once per frame only (convenient for creating UI within deep-nested code that runs multiple times) -// - struct ImGuiTextFilter // Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" -// - struct ImGuiTextBuffer // Text buffer for logging/accumulating text -// - struct ImGuiStorage // Custom key value storage (if you need to alter open/close states manually) -// - struct ImDrawList // Draw command list -// - struct ImBitmapFont // Bitmap font loader - -// ImGui End-user API -// In a namespace so that user can add extra functions (e.g. Value() helpers for your vector or common types) -namespace ImGui -{ - // Main - IMGUI_API ImGuiIO& GetIO(); - IMGUI_API ImGuiStyle& GetStyle(); - IMGUI_API void NewFrame(); - IMGUI_API void Render(); - IMGUI_API void Shutdown(); - IMGUI_API void ShowUserGuide(); - IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); - IMGUI_API void ShowTestWindow(bool* open = NULL); - - // Window - IMGUI_API bool Begin(const char* name = "Debug", bool* open = NULL, ImVec2 size = ImVec2(0,0), float fill_alpha = -1.0f, ImGuiWindowFlags flags = 0); // return false when window is collapsed, so you can early out in your code. - IMGUI_API void End(); - IMGUI_API void BeginChild(const char* str_id, ImVec2 size = ImVec2(0,0), bool border = false, ImGuiWindowFlags extra_flags = 0); // size==0.0f: use remaining window size, size<0.0f: use remaining window size minus abs(size). on each axis. - IMGUI_API void EndChild(); - IMGUI_API bool GetWindowIsFocused(); - IMGUI_API ImVec2 GetWindowSize(); - IMGUI_API float GetWindowWidth(); - IMGUI_API void SetWindowSize(const ImVec2& size); // set to ImVec2(0,0) to force an auto-fit - IMGUI_API ImVec2 GetWindowPos(); // you should rarely need/care about the window position, but it can be useful if you want to use your own drawing. - IMGUI_API void SetWindowPos(const ImVec2& pos); // set current window pos. - IMGUI_API ImVec2 GetContentRegionMax(); // window or current column boundaries - IMGUI_API ImVec2 GetWindowContentRegionMin(); // window boundaries - IMGUI_API ImVec2 GetWindowContentRegionMax(); - IMGUI_API ImDrawList* GetWindowDrawList(); // get rendering command-list if you want to append your own draw primitives. - IMGUI_API ImFont* GetWindowFont(); - IMGUI_API float GetWindowFontSize(); - IMGUI_API void SetWindowFontScale(float scale); // per-window font scale. Adjust IO.FontGlobalScale if you want to scale all windows. - IMGUI_API void SetScrollPosHere(); // adjust scrolling position to center into the current cursor position. - IMGUI_API void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use 'offset' to access sub components of a multiple component widget. - IMGUI_API void SetTreeStateStorage(ImGuiStorage* tree); // replace tree state storage with our own (if you want to manipulate it yourself, typically clear subsection of it). - IMGUI_API ImGuiStorage* GetTreeStateStorage(); - - IMGUI_API void PushItemWidth(float item_width); // width of items for the common item+label case. default to ~2/3 of windows width. - IMGUI_API void PopItemWidth(); - IMGUI_API float GetItemWidth(); - IMGUI_API void PushAllowKeyboardFocus(bool v); // allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets. - IMGUI_API void PopAllowKeyboardFocus(); - IMGUI_API void PushStyleColor(ImGuiCol idx, const ImVec4& col); - IMGUI_API void PopStyleColor(int count = 1); - IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); - IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); - IMGUI_API void PopStyleVar(int count = 1); - IMGUI_API void PushTextWrapPos(float wrap_pos_x = 0.0f); // word-wrapping for Text*() commands. < 0.0f: no wrapping; 0.0f: wrap to end of window (or column); > 0.0f: wrap at 'wrap_pos_x' position in window local space. - IMGUI_API void PopTextWrapPos(); - - // Tooltip - IMGUI_API void SetTooltip(const char* fmt, ...); // set tooltip under mouse-cursor, typically use with ImGui::IsHovered(). last call wins. - IMGUI_API void SetTooltipV(const char* fmt, va_list args); - IMGUI_API void BeginTooltip(); // use to create full-featured tooltip windows that aren't just text. - IMGUI_API void EndTooltip(); - - // Layout - IMGUI_API void Separator(); // horizontal line - IMGUI_API void SameLine(int column_x = 0, int spacing_w = -1); // call between widgets to layout them horizontally - IMGUI_API void Spacing(); - IMGUI_API void Columns(int count = 1, const char* id = NULL, bool border=true); // setup number of columns - IMGUI_API void NextColumn(); // next column - IMGUI_API float GetColumnOffset(int column_index = -1); - IMGUI_API void SetColumnOffset(int column_index, float offset); - IMGUI_API float GetColumnWidth(int column_index = -1); - IMGUI_API ImVec2 GetCursorPos(); // cursor position is relative to window position - IMGUI_API void SetCursorPos(const ImVec2& pos); // " - IMGUI_API void SetCursorPosX(float x); // " - IMGUI_API void SetCursorPosY(float y); // " - IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in screen space - IMGUI_API void AlignFirstTextHeightToWidgets(); // call once if the first item on the line is a Text() item and you want to vertically lower it to match subsequent (bigger) widgets. - IMGUI_API float GetTextLineSpacing(); - IMGUI_API float GetTextLineHeight(); - - // ID scopes - IMGUI_API void PushID(const char* str_id); - IMGUI_API void PushID(const void* ptr_id); - IMGUI_API void PushID(const int int_id); - IMGUI_API void PopID(); - - // Widgets - IMGUI_API void Text(const char* fmt, ...); - IMGUI_API void TextV(const char* fmt, va_list args); - IMGUI_API void TextColored(const ImVec4& col, const char* fmt, ...); // shortcut for PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor(); - IMGUI_API void TextColoredV(const ImVec4& col, const char* fmt, va_list args); - IMGUI_API void TextWrapped(const char* fmt, ...); // shortcut for PushTextWrapPos(0.0f); Text(fmt, ...); PopTextWrapPos(); - IMGUI_API void TextWrappedV(const char* fmt, va_list args); - IMGUI_API void TextUnformatted(const char* text, const char* text_end = NULL); // doesn't require null terminated string if 'text_end' is specified. no copy done to any bounded stack buffer, recommended for long chunks of text. - IMGUI_API void LabelText(const char* label, const char* fmt, ...); // display text+label aligned the same way as value+label widgets - IMGUI_API void LabelTextV(const char* label, const char* fmt, va_list args); - IMGUI_API void BulletText(const char* fmt, ...); - IMGUI_API void BulletTextV(const char* fmt, va_list args); - IMGUI_API bool Button(const char* label, ImVec2 size = ImVec2(0,0), bool repeat_when_held = false); - IMGUI_API bool SmallButton(const char* label); - IMGUI_API bool CollapsingHeader(const char* label, const char* str_id = NULL, const bool display_frame = true, const bool default_open = false); - IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); // adjust display_format to decorate the value with a prefix or a suffix. Use power!=1.0 for logarithmic sliders. - IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); - IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); - IMGUI_API bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); - IMGUI_API bool SliderAngle(const char* label, float* v, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f); // *v in radians - IMGUI_API bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format = "%.0f"); - IMGUI_API void PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0), size_t stride = sizeof(float)); - IMGUI_API void PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0)); - IMGUI_API void PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0), size_t stride = sizeof(float)); - IMGUI_API void PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0)); - IMGUI_API bool Checkbox(const char* label, bool* v); - IMGUI_API bool CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value); - IMGUI_API bool RadioButton(const char* label, bool active); - IMGUI_API bool RadioButton(const char* label, int* v, int v_button); - IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, void (*callback)(ImGuiTextEditCallbackData*) = NULL, void* user_data = NULL); - IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputFloat2(const char* label, float v[2], int decimal_precision = -1); - IMGUI_API bool InputFloat3(const char* label, float v[3], int decimal_precision = -1); - IMGUI_API bool InputFloat4(const char* label, float v[4], int decimal_precision = -1); - IMGUI_API bool InputInt(const char* label, int* v, int step = 1, int step_fast = 100, ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool Combo(const char* label, int* current_item, const char** items, int items_count, int popup_height_items = 7); - IMGUI_API bool Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int popup_height_items = 7); // separate items with \0, end item-list with \0\0 - IMGUI_API bool Combo(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int popup_height_items = 7); - IMGUI_API bool ColorButton(const ImVec4& col, bool small_height = false, bool outline_border = true); - IMGUI_API bool ColorEdit3(const char* label, float col[3]); - IMGUI_API bool ColorEdit4(const char* label, float col[4], bool show_alpha = true); - IMGUI_API void ColorEditMode(ImGuiColorEditMode mode); - IMGUI_API bool TreeNode(const char* str_label_id); // if returning 'true' the node is open and the user is responsible for calling TreePop - IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...); // " - IMGUI_API bool TreeNode(const void* ptr_id, const char* fmt, ...); // " - IMGUI_API bool TreeNodeV(const char* str_id, const char* fmt, va_list args); // " - IMGUI_API bool TreeNodeV(const void* ptr_id, const char* fmt, va_list args); // " - IMGUI_API void TreePush(const char* str_id = NULL); // already called by TreeNode(), but you can call Push/Pop yourself for layouting purpose - IMGUI_API void TreePush(const void* ptr_id = NULL); // " - IMGUI_API void TreePop(); - IMGUI_API void OpenNextNode(bool open); // force open/close the next TreeNode or CollapsingHeader - - // Value helper output "name: value". tip: freely declare your own within the ImGui namespace! - IMGUI_API void Value(const char* prefix, bool b); - IMGUI_API void Value(const char* prefix, int v); - IMGUI_API void Value(const char* prefix, unsigned int v); - IMGUI_API void Value(const char* prefix, float v, const char* float_format = NULL); - IMGUI_API void Color(const char* prefix, const ImVec4& v); - IMGUI_API void Color(const char* prefix, unsigned int v); - - // Logging - IMGUI_API void LogButtons(); - IMGUI_API void LogToTTY(int max_depth = -1); - IMGUI_API void LogToFile(int max_depth = -1, const char* filename = NULL); - IMGUI_API void LogToClipboard(int max_depth = -1); - - // Utilities - IMGUI_API void SetNewWindowDefaultPos(const ImVec2& pos); // set position of window that do - IMGUI_API bool IsItemHovered(); // was the last item active area hovered by mouse? - IMGUI_API bool IsItemFocused(); // was the last item focused for keyboard input? - IMGUI_API ImVec2 GetItemBoxMin(); // get bounding box of last item - IMGUI_API ImVec2 GetItemBoxMax(); // get bounding box of last item - IMGUI_API bool IsClipped(const ImVec2& item_size); // to perform coarse clipping on user's side (as an optimization) - IMGUI_API bool IsKeyPressed(int key_index, bool repeat = true); // key_index into the keys_down[512] array, imgui doesn't know the semantic of each entry - IMGUI_API bool IsMouseClicked(int button, bool repeat = false); - IMGUI_API bool IsMouseDoubleClicked(int button); - IMGUI_API bool IsMouseHoveringWindow(); // is mouse hovering current window ("window" in API names always refer to current window) - IMGUI_API bool IsMouseHoveringAnyWindow(); // is mouse hovering any active imgui window - IMGUI_API bool IsMouseHoveringBox(const ImVec2& box_min, const ImVec2& box_max); // is mouse hovering given bounding box - IMGUI_API bool IsPosHoveringAnyWindow(const ImVec2& pos); // is given position hovering any active imgui window - IMGUI_API ImVec2 GetMousePos(); // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls - IMGUI_API float GetTime(); - IMGUI_API int GetFrameCount(); - IMGUI_API const char* GetStyleColorName(ImGuiCol idx); - IMGUI_API void GetDefaultFontData(const void** fnt_data, unsigned int* fnt_size, const void** png_data, unsigned int* png_size); - IMGUI_API ImVec2 CalcTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_double_hash = true, float wrap_width = -1.0f); - -} // namespace ImGui - -// Flags for ImGui::Begin() -enum ImGuiWindowFlags_ -{ - // Default: 0 - ImGuiWindowFlags_ShowBorders = 1 << 0, - ImGuiWindowFlags_NoTitleBar = 1 << 1, - ImGuiWindowFlags_NoResize = 1 << 2, - ImGuiWindowFlags_NoMove = 1 << 3, - ImGuiWindowFlags_NoScrollbar = 1 << 4, - ImGuiWindowFlags_NoScrollWithMouse = 1 << 5, - ImGuiWindowFlags_AlwaysAutoResize = 1 << 6, - ImGuiWindowFlags_ChildWindow = 1 << 7, // For internal use by BeginChild() - ImGuiWindowFlags_ChildWindowAutoFitX = 1 << 8, // For internal use by BeginChild() - ImGuiWindowFlags_ChildWindowAutoFitY = 1 << 9, // For internal use by BeginChild() - ImGuiWindowFlags_ComboBox = 1 << 10, // For internal use by ComboBox() - ImGuiWindowFlags_Tooltip = 1 << 11 // For internal use by Render() when using Tooltip -}; - -// Flags for ImGui::InputText() -enum ImGuiInputTextFlags_ -{ - // Default: 0 - ImGuiInputTextFlags_CharsDecimal = 1 << 0, // Allow 0123456789.+-*/ - ImGuiInputTextFlags_CharsHexadecimal = 1 << 1, // Allow 0123456789ABCDEFabcdef - ImGuiInputTextFlags_AutoSelectAll = 1 << 2, // Select entire text when first taking focus - ImGuiInputTextFlags_EnterReturnsTrue = 1 << 3, // Return 'true' when Enter is pressed (as opposed to when the value was modified) - ImGuiInputTextFlags_CallbackCompletion = 1 << 4, // Call user function on pressing TAB (for completion handling) - ImGuiInputTextFlags_CallbackHistory = 1 << 5, // Call user function on pressing Up/Down arrows (for history handling) - ImGuiInputTextFlags_CallbackAlways = 1 << 6 // Call user function every time - //ImGuiInputTextFlags_AlignCenter = 1 << 6, -}; - -// User fill ImGuiIO.KeyMap[] array with indices into the ImGuiIO.KeysDown[512] array -enum ImGuiKey_ -{ - ImGuiKey_Tab, - ImGuiKey_LeftArrow, - ImGuiKey_RightArrow, - ImGuiKey_UpArrow, - ImGuiKey_DownArrow, - ImGuiKey_Home, - ImGuiKey_End, - ImGuiKey_Delete, - ImGuiKey_Backspace, - ImGuiKey_Enter, - ImGuiKey_Escape, - ImGuiKey_A, // for CTRL+A: select all - ImGuiKey_C, // for CTRL+C: copy - ImGuiKey_V, // for CTRL+V: paste - ImGuiKey_X, // for CTRL+X: cut - ImGuiKey_Y, // for CTRL+Y: redo - ImGuiKey_Z, // for CTRL+Z: undo - ImGuiKey_COUNT -}; - -// Enumeration for PushStyleColor() / PopStyleColor() -enum ImGuiCol_ -{ - ImGuiCol_Text, - ImGuiCol_WindowBg, - ImGuiCol_Border, - ImGuiCol_BorderShadow, - ImGuiCol_FrameBg, // Background of checkbox, radio button, plot, slider, text input - ImGuiCol_TitleBg, - ImGuiCol_TitleBgCollapsed, - ImGuiCol_ScrollbarBg, - ImGuiCol_ScrollbarGrab, - ImGuiCol_ScrollbarGrabHovered, - ImGuiCol_ScrollbarGrabActive, - ImGuiCol_ComboBg, - ImGuiCol_CheckHovered, - ImGuiCol_CheckActive, - ImGuiCol_SliderGrab, - ImGuiCol_SliderGrabActive, - ImGuiCol_Button, - ImGuiCol_ButtonHovered, - ImGuiCol_ButtonActive, - ImGuiCol_Header, - ImGuiCol_HeaderHovered, - ImGuiCol_HeaderActive, - ImGuiCol_Column, - ImGuiCol_ColumnHovered, - ImGuiCol_ColumnActive, - ImGuiCol_ResizeGrip, - ImGuiCol_ResizeGripHovered, - ImGuiCol_ResizeGripActive, - ImGuiCol_CloseButton, - ImGuiCol_CloseButtonHovered, - ImGuiCol_CloseButtonActive, - ImGuiCol_PlotLines, - ImGuiCol_PlotLinesHovered, - ImGuiCol_PlotHistogram, - ImGuiCol_PlotHistogramHovered, - ImGuiCol_TextSelectedBg, - ImGuiCol_TooltipBg, - ImGuiCol_COUNT -}; - -// Enumeration for PushStyleVar() / PopStyleVar() -// NB: the enum only refers to fields of ImGuiStyle() which makes sense to be pushed/poped in UI code. Feel free to add others. -enum ImGuiStyleVar_ -{ - ImGuiStyleVar_Alpha, // float - ImGuiStyleVar_WindowPadding, // ImVec2 - ImGuiStyleVar_FramePadding, // ImVec2 - ImGuiStyleVar_ItemSpacing, // ImVec2 - ImGuiStyleVar_ItemInnerSpacing, // ImVec2 - ImGuiStyleVar_TreeNodeSpacing, // float - ImGuiStyleVar_ColumnsMinSpacing // float -}; - -// Enumeration for ColorEditMode() -enum ImGuiColorEditMode_ -{ - ImGuiColorEditMode_UserSelect = -1, - ImGuiColorEditMode_RGB = 0, - ImGuiColorEditMode_HSV = 1, - ImGuiColorEditMode_HEX = 2 -}; - -struct ImGuiStyle -{ - float Alpha; // Global alpha applies to everything in ImGui - ImVec2 WindowPadding; // Padding within a window - ImVec2 WindowMinSize; // Minimum window size - ImVec2 FramePadding; // Padding within a framed rectangle (used by most widgets) - ImVec2 ItemSpacing; // Horizontal and vertical spacing between widgets/lines - ImVec2 ItemInnerSpacing; // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label) - ImVec2 TouchExtraPadding; // Expand bounding box for touch-based system where touch position is not accurate enough (unnecessary for mouse inputs). Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget running. So dont grow this too much! - ImVec2 AutoFitPadding; // Extra space after auto-fit (double-clicking on resize grip) - float WindowFillAlphaDefault; // Default alpha of window background, if not specified in ImGui::Begin() - float WindowRounding; // Radius of window corners rounding. Set to 0.0f to have rectangular windows - float TreeNodeSpacing; // Horizontal spacing when entering a tree node - float ColumnsMinSpacing; // Minimum horizontal spacing between two columns - float ScrollBarWidth; // Width of the vertical scroll bar - ImVec4 Colors[ImGuiCol_COUNT]; - - IMGUI_API ImGuiStyle(); -}; - -// This is where your app communicate with ImGui. Call ImGui::GetIO() to access. -// Read 'Programmer guide' section in .cpp file for general usage. -struct ImGuiIO -{ - //------------------------------------------------------------------ - // Settings (fill once) // Default value: - //------------------------------------------------------------------ - - ImVec2 DisplaySize; // // Display size, in pixels. For clamping windows positions. - float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. - float IniSavingRate; // = 5.0f // Maximum time between saving .ini file, in seconds. Set to a negative value to disable .ini saving. - const char* IniFilename; // = "imgui.ini" // Path to .ini file. - const char* LogFilename; // = "imgui_log.txt" // Path to .log file. - float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. - float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. - int KeyMap[ImGuiKey_COUNT]; // // Map of indices into the KeysDown[512] entries array - ImFont* Font; // // Font (also see 'Settings' fields inside ImFont structure for details) - float FontGlobalScale; // = 1.0f // Global scale all fonts - bool FontAllowUserScaling; // = false // Allow user scaling text of individual window with CTRL+Wheel. - float PixelCenterOffset; // = 0.0f // Try to set to 0.5f or 0.375f if rendering is blurry - - void* UserData; // = NULL // Store your own data for retrieval by callbacks. - - //------------------------------------------------------------------ - // User Functions - //------------------------------------------------------------------ - - // REQUIRED: rendering function. - // See example code if you are unsure of how to implement this. - void (*RenderDrawListsFn)(ImDrawList** const draw_lists, int count); - - // Optional: access OS clipboard (default to use native Win32 clipboard on Windows, otherwise use a ImGui private clipboard) - // Override to access OS clipboard on other architectures. - const char* (*GetClipboardTextFn)(); - void (*SetClipboardTextFn)(const char* text); - - // Optional: override memory allocations (default to posix malloc/realloc/free) - void* (*MemAllocFn)(size_t sz); - void* (*MemReallocFn)(void* ptr, size_t sz); - void (*MemFreeFn)(void* ptr); - - // Optional: notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese inputs in Windows) - void (*ImeSetInputScreenPosFn)(int x, int y); - - //------------------------------------------------------------------ - // Input - Fill before calling NewFrame() - //------------------------------------------------------------------ - - ImVec2 MousePos; // Mouse position, in pixels (set to -1,-1 if no mouse / on another screen, etc.) - bool MouseDown[5]; // Mouse buttons. ImGui itself only uses button 0 (left button) but you can use others as storage for convenience. - float MouseWheel; // Mouse wheel: 1 unit scrolls about 5 lines text. - bool KeyCtrl; // Keyboard modifier pressed: Control - bool KeyShift; // Keyboard modifier pressed: Shift - bool KeysDown[512]; // Keyboard keys that are pressed (in whatever order user naturally has access to keyboard data) - ImWchar InputCharacters[16+1]; // List of characters input (translated by user from keypress+keyboard state). Fill using AddInputCharacter() helper. - - // Function - IMGUI_API void AddInputCharacter(ImWchar c); // Helper to add a new character into InputCharacters[] - - //------------------------------------------------------------------ - // Output - Retrieve after calling NewFrame(), you can use them to discard inputs or hide them from the rest of your application - //------------------------------------------------------------------ - - bool WantCaptureMouse; // Mouse is hovering a window or widget is active (= ImGui will use your mouse input) - bool WantCaptureKeyboard; // Widget is active (= ImGui will use your keyboard input) - - //------------------------------------------------------------------ - // [Internal] ImGui will maintain those fields for you - //------------------------------------------------------------------ - - ImVec2 MousePosPrev; - ImVec2 MouseDelta; - bool MouseClicked[5]; - ImVec2 MouseClickedPos[5]; - float MouseClickedTime[5]; - bool MouseDoubleClicked[5]; - float MouseDownTime[5]; - float KeysDownTime[512]; - - IMGUI_API ImGuiIO(); -}; - -//----------------------------------------------------------------------------- -// Helpers -//----------------------------------------------------------------------------- - // Helper: execute a block of code once a frame only // Convenient if you want to quickly create an UI within deep-nested code that runs multiple times every frame. // Usage: @@ -563,7 +764,7 @@ struct ImGuiTextFilter ImVector Filters; int CountGrep; - ImGuiTextFilter(); + ImGuiTextFilter(const char* default_filter = ""); void Clear() { InputBuf[0] = 0; Build(); } void Draw(const char* label = "Filter (inc,-exc)", float width = -1.0f); // Helper calling InputText+Build bool PassFilter(const char* val) const; @@ -577,65 +778,126 @@ struct ImGuiTextBuffer ImVector Buf; ImGuiTextBuffer() { Buf.push_back(0); } - ~ImGuiTextBuffer() { clear(); } const char* begin() const { return &Buf.front(); } const char* end() const { return &Buf.back(); } // Buf is zero-terminated, so end() will point on the zero-terminator size_t size() const { return Buf.size()-1; } - bool empty() { return Buf.empty(); } + bool empty() { return size() >= 1; } void clear() { Buf.clear(); Buf.push_back(0); } IMGUI_API void append(const char* fmt, ...); + IMGUI_API void appendv(const char* fmt, va_list args); }; // Helper: Key->value storage -// - Store collapse state for a tree -// - Store color edit options, etc. +// - Store collapse state for a tree (Int 0/1) +// - Store color edit options (Int using values in ImGuiColorEditMode enum). +// - Custom user storage for temporary values. // Typically you don't have to worry about this since a storage is held within each Window. -// Declare your own storage if you want to manipulate the open/close state of a particular sub-tree in your interface. +// Declare your own storage if: +// - You want to manipulate the open/close state of a particular sub-tree in your interface (tree node uses Int 0/1 to store their state). +// - You want to store custom debug data easily without adding or editing structures in your code. struct ImGuiStorage { - struct Pair { ImU32 key; int val; }; + struct Pair + { + ImGuiID key; + union { int val_i; float val_f; void* val_p; }; + Pair(ImGuiID _key, int _val_i) { key = _key; val_i = _val_i; } + Pair(ImGuiID _key, float _val_f) { key = _key; val_f = _val_f; } + Pair(ImGuiID _key, void* _val_p) { key = _key; val_p = _val_p; } + }; ImVector Data; + // - Get***() functions find pair, never add/allocate. Pairs are sorted so a query is O(log N) + // - Set***() functions find pair, insertion on demand if missing. + // - Sorted insertion is costly but should amortize. A typical frame shouldn't need to insert any new pair. IMGUI_API void Clear(); - IMGUI_API int GetInt(ImU32 key, int default_val = 0); - IMGUI_API void SetInt(ImU32 key, int val); - IMGUI_API void SetAllInt(int val); + IMGUI_API int GetInt(ImGuiID key, int default_val = 0) const; + IMGUI_API void SetInt(ImGuiID key, int val); + IMGUI_API float GetFloat(ImGuiID key, float default_val = 0.0f) const; + IMGUI_API void SetFloat(ImGuiID key, float val); + IMGUI_API void* GetVoidPtr(ImGuiID key) const; // default_val is NULL + IMGUI_API void SetVoidPtr(ImGuiID key, void* val); - IMGUI_API int* Find(ImU32 key); - IMGUI_API void Insert(ImU32 key, int val); + // - Get***Ref() functions finds pair, insert on demand if missing, return pointer. Useful if you intend to do Get+Set. + // - References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer. + // - A typical use case where this is convenient: + // float* pvar = ImGui::GetFloatRef(key); ImGui::SliderFloat("var", pvar, 0, 100.0f); some_var += *pvar; + // - You can also use this to quickly create temporary editable values during a session of using Edit&Continue, without restarting your application. + IMGUI_API int* GetIntRef(ImGuiID key, int default_val = 0); + IMGUI_API float* GetFloatRef(ImGuiID key, float default_val = 0); + IMGUI_API void** GetVoidPtrRef(ImGuiID key, void* default_val = NULL); + + // Use on your own storage if you know only integer are being stored (open/close all tree nodes) + IMGUI_API void SetAllInt(int val); }; // Shared state of InputText(), passed to callback when a ImGuiInputTextFlags_Callback* flag is used. struct ImGuiTextEditCallbackData { - ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only - char* Buf; // Current text // Read-write (pointed data only) - size_t BufSize; // // Read-only - bool BufDirty; // Set if you modify Buf directly // Write - ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only - int CursorPos; // // Read-write - int SelectionStart; // // Read-write (== to SelectionEnd when no selection) - int SelectionEnd; // // Read-write - void* UserData; // What user passed to InputText() + ImGuiInputTextFlags EventFlag; // One of ImGuiInputTextFlags_Callback* // Read-only + ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only + void* UserData; // What user passed to InputText() // Read-only + + // CharFilter event: + ImWchar EventChar; // Character input // Read-write (replace character or set to zero) + + // Completion,History,Always events: + ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only + char* Buf; // Current text // Read-write (pointed data only) + size_t BufSize; // // Read-only + bool BufDirty; // Set if you modify Buf directly // Write + int CursorPos; // // Read-write + int SelectionStart; // // Read-write (== to SelectionEnd when no selection) + int SelectionEnd; // // Read-write // NB: calling those function loses selection. void DeleteChars(int pos, int bytes_count); void InsertChars(int pos, const char* text, const char* text_end = NULL); }; -//----------------------------------------------------------------------------- -// Draw List -// Hold a series of drawing commands. The user provide a renderer for ImDrawList -//----------------------------------------------------------------------------- - -struct ImDrawCmd +// ImColor() is just a helper that implicity converts to either ImU32 (packed 4x1 byte) or ImVec4 (4x1 float) +// None of the ImGui API are using ImColor directly but you can use it as a convenience to pass colors in either formats. +struct ImColor { - unsigned int vtx_count; - ImVec4 clip_rect; + ImVec4 Value; + + ImColor(int r, int g, int b, int a = 255) { Value.x = (float)r / 255.0f; Value.y = (float)g / 255.0f; Value.z = (float)b / 255.0f; Value.w = (float)a / 255.0f; } + ImColor(float r, float g, float b, float a = 1.0f) { Value.x = r; Value.y = g; Value.z = b; Value.w = a; } + ImColor(const ImVec4& col) { Value = col; } + operator ImU32() const { return ImGui::ColorConvertFloat4ToU32(Value); } + operator ImVec4() const { return Value; } + + static ImColor HSV(float h, float s, float v, float a = 1.0f) { float r,g,b; ImGui::ColorConvertHSVtoRGB(h, s, v, r, g, b); return ImColor(r,g,b,a); } }; +//----------------------------------------------------------------------------- +// Draw List +// Hold a series of drawing commands. The user provides a renderer for ImDrawList. +//----------------------------------------------------------------------------- + +// Draw callbacks for advanced uses. +// NB- You most likely DO NOT need to care about draw callbacks just to create your own widget or customized UI rendering (you can poke into the draw list for that) +// Draw callback are useful for example if you want to render a complex 3D scene inside a UI element. +// The expected behavior from your rendering loop is: +// if (cmd.user_callback != NULL) +// cmd.user_callback(parent_list, cmd); +// else +// RenderTriangles() +// It is up to you to decide if your rendering loop or the callback should be responsible for backup/restoring rendering state. +typedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* cmd); + +// Typically, 1 command = 1 gpu draw call (unless command is a callback) +struct ImDrawCmd +{ + unsigned int vtx_count; // Number of vertices (multiple of 3) to be drawn as triangles. The vertices are stored in the callee ImDrawList's vtx_buffer[] array. + ImVec4 clip_rect; // Clipping rectangle (x1, y1, x2, y2) + ImTextureID texture_id; // User-provided texture ID. Set by user in ImfontAtlas::SetTexID() for fonts or passed to Image*() functions. Ignore if never using images or multiple fonts atlas. + ImDrawCallback user_callback; // If != NULL, call the function instead of rendering the vertices. vtx_count will be 0. clip_rect and texture_id will be set normally. + void* user_callback_data; // The draw callback code can access this. +}; + +// Vertex layout #ifndef IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT -// Default vertex layout struct ImDrawVert { ImVec2 pos; @@ -643,139 +905,162 @@ struct ImDrawVert ImU32 col; }; #else -// You can change the vertex format layout by defining IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT. +// You can change the vertex format layout by defining IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT in imconfig.h // The code expect ImVec2 pos (8 bytes), ImVec2 uv (8 bytes), ImU32 col (4 bytes), but you can re-order them or add other fields as needed to simplify integration in your engine. // The type has to be described by the #define (you can either declare the struct or use a typedef) IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT; #endif // Draw command list -// This is the low-level list of polygon that ImGui:: functions are creating. At the end of the frame, all command lists are passed to your ImGuiIO::RenderDrawListFn function for rendering. -// At the moment, each ImGui window contains its own ImDrawList but they could potentially be merged. +// This is the low-level list of polygons that ImGui functions are filling. At the end of the frame, all command lists are passed to your ImGuiIO::RenderDrawListFn function for rendering. +// At the moment, each ImGui window contains its own ImDrawList but they could potentially be merged in the future. // If you want to add custom rendering within a window, you can use ImGui::GetWindowDrawList() to access the current draw list and add your own primitives. // You can interleave normal ImGui:: calls and adding primitives to the current draw list. +// All positions are in screen coordinates (0,0=top-left, 1 pixel per unit). Primitives are always added to the list and not culled (culling is done at render time and at a higher-level by ImGui:: functions). // Note that this only gives you access to rendering polygons. If your intent is to create custom widgets and the publicly exposed functions/data aren't sufficient, you can add code in imgui_user.inl struct ImDrawList { // This is what you have to render - ImVector commands; // commands - ImVector vtx_buffer; // each command consume ImDrawCmd::vtx_count of those + ImVector commands; // Commands. Typically 1 command = 1 gpu draw call. + ImVector vtx_buffer; // Vertex buffer. Each command consume ImDrawCmd::vtx_count of those // [Internal to ImGui] - ImVector clip_rect_stack; // [internal] clip rect stack while building the command-list (so text command can perform clipping early on) - ImDrawVert* vtx_write; // [internal] point within vtx_buffer after each add command (to avoid using the ImVector<> operators too much) + ImVector clip_rect_stack; // [Internal] + ImVector texture_id_stack; // [Internal] + ImDrawVert* vtx_write; // [Internal] point within vtx_buffer after each add command (to avoid using the ImVector<> operators too much) ImDrawList() { Clear(); } - IMGUI_API void Clear(); - IMGUI_API void PushClipRect(const ImVec4& clip_rect); + IMGUI_API void ClearFreeMemory(); + IMGUI_API void PushClipRect(const ImVec4& clip_rect); // Scissoring. The values are x1, y1, x2, y2. + IMGUI_API void PushClipRectFullScreen(); IMGUI_API void PopClipRect(); - IMGUI_API void ReserveVertices(unsigned int vtx_count); - IMGUI_API void AddVtx(const ImVec2& pos, ImU32 col); - IMGUI_API void AddVtxLine(const ImVec2& a, const ImVec2& b, ImU32 col); + IMGUI_API void PushTextureID(const ImTextureID& texture_id); + IMGUI_API void PopTextureID(); // Primitives - IMGUI_API void AddLine(const ImVec2& a, const ImVec2& b, ImU32 col); - IMGUI_API void AddRect(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding = 0.0f, int rounding_corners=0x0F); - IMGUI_API void AddRectFilled(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding = 0.0f, int rounding_corners=0x0F); + IMGUI_API void AddLine(const ImVec2& a, const ImVec2& b, ImU32 col, float thickness = 1.0f); + IMGUI_API void AddRect(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding = 0.0f, int rounding_corners = 0x0F); + IMGUI_API void AddRectFilled(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding = 0.0f, int rounding_corners = 0x0F); IMGUI_API void AddTriangleFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col); IMGUI_API void AddCircle(const ImVec2& centre, float radius, ImU32 col, int num_segments = 12); IMGUI_API void AddCircleFilled(const ImVec2& centre, float radius, ImU32 col, int num_segments = 12); - IMGUI_API void AddArc(const ImVec2& center, float rad, ImU32 col, int a_min, int a_max, bool tris = false, const ImVec2& third_point_offset = ImVec2(0,0)); - IMGUI_API void AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f); + IMGUI_API void AddArcFast(const ImVec2& center, float radius, ImU32 col, int a_min_12, int a_max_12, bool filled = false, const ImVec2& third_point_offset = ImVec2(0,0)); // Angles in 0..12 range + IMGUI_API void AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec2* cpu_clip_max = NULL); + IMGUI_API void AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv0, const ImVec2& uv1, ImU32 col = 0xFFFFFFFF); + + // Advanced + IMGUI_API void AddCallback(ImDrawCallback callback, void* callback_data); // Your rendering function must check for 'user_callback' in ImDrawCmd and call the function instead of rendering triangles. + IMGUI_API void AddDrawCmd(); // This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible + + // Internal helpers + IMGUI_API void PrimReserve(unsigned int vtx_count); + IMGUI_API void PrimTriangle(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col); + IMGUI_API void PrimRect(const ImVec2& a, const ImVec2& b, ImU32 col); + IMGUI_API void PrimRectUV(const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, ImU32 col); + IMGUI_API void PrimQuad(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col); + IMGUI_API void PrimLine(const ImVec2& a, const ImVec2& b, ImU32 col, float thickness = 1.0f); + IMGUI_API void UpdateClipRect(); + IMGUI_API void UpdateTextureID(); + IMGUI_API void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { vtx_write->pos = pos; vtx_write->uv = uv; vtx_write->col = col; vtx_write++; } }; -// Bitmap font data loader & renderer into vertices -// Using the .fnt format exported by BMFont -// - tool: http://www.angelcode.com/products/bmfont -// - file-format: http://www.angelcode.com/products/bmfont/doc/file_format.html -// Assume valid file data (won't handle invalid/malicious data) -// Handle a subset of the options, namely: -// - kerning pair are not supported (because some ImGui code does per-character CalcTextSize calls, need to turn it into something more state-ful to allow for kerning) +// Load and rasterize multiple TTF fonts into a same texture. +// Sharing a texture for multiple fonts allows us to reduce the number of draw calls during rendering. +// We also add custom graphic data into the texture that serves for ImGui. +// 1. (Optional) Call AddFont*** functions. If you don't call any, the default font will be loaded for you. +// 2. Call GetTexDataAsAlpha8() or GetTexDataAsRGBA32() to build and retrieve pixels data. +// 3. Upload the pixels data into a texture within your graphics system. +// 4. Call SetTexID(my_tex_id); and pass the pointer/identifier to your texture. This value will be passed back to you during rendering to identify the texture. +// 5. Call ClearTexData() to free textures memory on the heap. +struct ImFontAtlas +{ + IMGUI_API ImFontAtlas(); + IMGUI_API ~ImFontAtlas(); + IMGUI_API ImFont* AddFontDefault(); + IMGUI_API ImFont* AddFontFromFileTTF(const char* filename, float size_pixels, const ImWchar* glyph_ranges = NULL, int font_no = 0); + IMGUI_API ImFont* AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImWchar* glyph_ranges = NULL, int font_no = 0); // Transfer ownership of 'ttf_data' to ImFontAtlas, will be deleted after Build() + IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImWchar* glyph_ranges = NULL, int font_no = 0); // 'compressed_ttf_data' untouched and still owned by caller. Compress with binary_to_compressed_c.cpp + IMGUI_API void ClearTexData(); // Clear the CPU-side texture data. Saves RAM once the texture has been copied to graphics memory. + IMGUI_API void ClearInputData(); // Clear the input TTF data (inc sizes, glyph ranges) + IMGUI_API void ClearFonts(); // Clear the ImGui-side font data (glyphs storage, UV coordinates) + IMGUI_API void Clear(); // Clear all + + // Retrieve texture data + // User is in charge of copying the pixels into graphics memory, then call SetTextureUserID() + // After loading the texture into your graphic system, store your texture handle in 'TexID' (ignore if you aren't using multiple fonts nor images) + // RGBA32 format is provided for convenience and high compatibility, but note that all RGB pixels are white, so 75% of the memory is wasted. + // Pitch = Width * BytesPerPixels + IMGUI_API void GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 1 byte per-pixel + IMGUI_API void GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 4 bytes-per-pixel + IMGUI_API void SetTexID(void* id) { TexID = id; } + + // Helpers to retrieve list of common Unicode ranges (2 value per range, values are inclusive, zero-terminated list) + // (Those functions could be static but aren't so most users don't have to refer to the ImFontAtlas:: name ever if in their code; just using io.Fonts->) + IMGUI_API const ImWchar* GetGlyphRangesDefault(); // Basic Latin, Extended Latin + IMGUI_API const ImWchar* GetGlyphRangesJapanese(); // Default + Hiragana, Katakana, Half-Width, Selection of 1946 Ideographs + IMGUI_API const ImWchar* GetGlyphRangesChinese(); // Japanese + full set of about 21000 CJK Unified Ideographs + + // Members + // (Access texture data via GetTexData*() calls which will setup a default font for you.) + void* TexID; // User data to refer to the texture once it has been uploaded to user's graphic systems. It ia passed back to you during rendering. + unsigned char* TexPixelsAlpha8; // 1 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight + unsigned int* TexPixelsRGBA32; // 4 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight * 4 + int TexWidth; + int TexHeight; + ImVec2 TexUvWhitePixel; // Texture coordinates to a white pixel (part of the TexExtraData block) + ImVector Fonts; + + // Private + struct ImFontAtlasData; + ImVector InputData; // Internal data + IMGUI_API bool Build(); // Build pixels data. This is automatically for you by the GetTexData*** functions. + IMGUI_API void RenderCustomTexData(int pass, void* rects); +}; + +// TTF font loading and rendering +// ImFontAtlas automatically loads a default embedded font for you when you call GetTexDataAsAlpha8() or GetTexDataAsRGBA32(). +// Kerning isn't supported. At the moment some ImGui code does per-character CalcTextSize calls, need something more state-ful. struct ImFont { - struct FntInfo; - struct FntCommon; - struct FntGlyph; - struct FntKerning; + // Members: Settings + float FontSize; // // Height of characters, set during loading (don't change after loading) + float Scale; // = 1.0f // Base font scale, multiplied by the per-window font scale which you can adjust with SetFontScale() + ImVec2 DisplayOffset; // = (0.0f,0.0f) // Offset font rendering by xx pixels + ImWchar FallbackChar; // = '?' // Replacement glyph if one isn't found. Only set via SetFallbackChar() - // Settings - float Scale; // = 1.0f // Base font scale, multiplied by the per-window font scale which you can adjust with SetFontScale() - ImVec2 DisplayOffset; // = (0.0f,0.0f // Offset font rendering by xx pixels - ImVec2 TexUvForWhite; // = (0.0f,0.0f) // Font texture must have a white pixel at this UV coordinate. Adjust if you are using custom texture. - ImWchar FallbackChar; // = '?' // Replacement glyph is one isn't found. - - // Data - unsigned char* Data; // Raw data, content of .fnt file - size_t DataSize; // - bool DataOwned; // - const FntInfo* Info; // (point into raw data) - const FntCommon* Common; // (point into raw data) - const FntGlyph* Glyphs; // (point into raw data) - size_t GlyphsCount; // - const FntKerning* Kerning; // (point into raw data) - NB: kerning is unsupported - size_t KerningCount; // - ImVector Filenames; // (point into raw data) - ImVector IndexLookup; // (built) - const FntGlyph* FallbackGlyph; // == FindGlyph(FontFallbackChar) + // Members: Runtime data + struct Glyph + { + ImWchar Codepoint; + signed short XAdvance; + signed short Width, Height; + signed short XOffset, YOffset; + float U0, V0, U1, V1; // Texture coordinates + }; + float BaseLine; // Distance from top to bottom of e.g. 'A' [0..FontSize] + ImFontAtlas* ContainerAtlas; // What we has been loaded into + ImVector Glyphs; + const Glyph* FallbackGlyph; // == FindGlyph(FontFallbackChar) + float FallbackXAdvance; // + ImVector IndexXAdvance; // Glyphs->XAdvance directly indexable (for CalcTextSize functions which are often bottleneck in large UI) + ImVector IndexLookup; // Index glyphs by Unicode code-point + // Methods IMGUI_API ImFont(); - IMGUI_API ~ImFont() { Clear(); } - - IMGUI_API bool LoadFromMemory(const void* data, size_t data_size); - IMGUI_API bool LoadFromFile(const char* filename); + IMGUI_API ~ImFont(); IMGUI_API void Clear(); IMGUI_API void BuildLookupTable(); - IMGUI_API const FntGlyph* FindGlyph(unsigned short c) const; - IMGUI_API bool IsLoaded() const { return Info != NULL && Common != NULL && Glyphs != NULL; } + IMGUI_API const Glyph* FindGlyph(unsigned short c) const; + IMGUI_API void SetFallbackChar(ImWchar c); + IMGUI_API bool IsLoaded() const { return ContainerAtlas != NULL; } // 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable. // 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable. IMGUI_API ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** remaining = NULL) const; // utf8 IMGUI_API ImVec2 CalcTextSizeW(float size, float max_width, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL) const; // wchar - IMGUI_API void RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, ImDrawVert*& out_vertices, float wrap_width = 0.0f) const; - + IMGUI_API void RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, ImDrawList* draw_list, float wrap_width = 0.0f, const ImVec2* cpu_clip_max = NULL) const; IMGUI_API const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const; - -#pragma pack(push, 1) - struct FntInfo - { - signed short FontSize; - unsigned char BitField; // bit 0: smooth, bit 1: unicode, bit 2: italic, bit 3: bold, bit 4: fixedHeight, bits 5-7: reserved - unsigned char CharSet; - unsigned short StretchH; - unsigned char AA; - unsigned char PaddingUp, PaddingRight, PaddingDown, PaddingLeft; - unsigned char SpacingHoriz, SpacingVert, Outline; - //char FontName[]; - }; - - struct FntCommon - { - unsigned short LineHeight, Base; - unsigned short ScaleW, ScaleH; - unsigned short Pages; - unsigned char BitField; - unsigned char Channels[4]; - }; - - struct FntGlyph - { - unsigned int Id; - unsigned short X, Y, Width, Height; - signed short XOffset, YOffset; - signed short XAdvance; - unsigned char Page; - unsigned char Channel; - }; - - struct FntKerning - { - unsigned int IdFirst; - unsigned int IdSecond; - signed short Amount; - }; -#pragma pack(pop) }; //---- Include imgui_user.h at the end of imgui.h diff --git a/ext/imgui/stb_image.h b/ext/imgui/stb_image.h deleted file mode 100644 index 5ab9c58ec1..0000000000 --- a/ext/imgui/stb_image.h +++ /dev/null @@ -1,4744 +0,0 @@ -/* stb_image - v1.46 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c - when you control the images you're loading - no warranty implied; use at your own risk - - Do this: - #define STB_IMAGE_IMPLEMENTATION - before you include this file in *one* C or C++ file to create the implementation. - - #define STBI_ASSERT(x) to avoid using assert.h. - - QUICK NOTES: - Primarily of interest to game developers and other people who can - avoid problematic images and only need the trivial interface - - JPEG baseline (no JPEG progressive) - PNG 1/2/4/8-bit-per-channel (16 bpc not supported) - - TGA (not sure what subset, if a subset) - BMP non-1bpp, non-RLE - PSD (composited view only, no extra channels) - - GIF (*comp always reports as 4-channel) - HDR (radiance rgbE format) - PIC (Softimage PIC) - - - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - - decode from arbitrary I/O callbacks - - overridable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD) - - Latest revisions: - 1.xx (2014-09-26) 1/2/4-bit PNG support (both grayscale and paletted) - 1.46 (2014-08-26) fix broken tRNS chunk in non-paletted PNG - 1.45 (2014-08-16) workaround MSVC-ARM internal compiler error by wrapping malloc - 1.44 (2014-08-07) warnings - 1.43 (2014-07-15) fix MSVC-only bug in 1.42 - 1.42 (2014-07-09) no _CRT_SECURE_NO_WARNINGS; error-path fixes; STBI_ASSERT - 1.41 (2014-06-25) fix search&replace that messed up comments/error messages - 1.40 (2014-06-22) gcc warning - 1.39 (2014-06-15) TGA optimization bugfix, multiple BMP fixes - 1.38 (2014-06-06) suppress MSVC run-time warnings, fix accidental rename of 'skip' - 1.37 (2014-06-04) remove duplicate typedef - 1.36 (2014-06-03) converted to header file, allow reading incorrect iphoned-images without iphone flag - 1.35 (2014-05-27) warnings, bugfixes, TGA optimization, etc - - See end of file for full revision history. - - TODO: - stbi_info support for BMP,PSD,HDR,PIC - - - ============================ Contributors ========================= - - Image formats Bug fixes & warning fixes - Sean Barrett (jpeg, png, bmp) Marc LeBlanc - Nicolas Schulz (hdr, psd) Christpher Lloyd - Jonathan Dummer (tga) Dave Moore - Jean-Marc Lienher (gif) Won Chun - Tom Seddon (pic) the Horde3D community - Thatcher Ulrich (psd) Janez Zemva - Jonathan Blow - Laurent Gomila - Extensions, features Aruelien Pocheville - Jetro Lauha (stbi_info) Ryamond Barbiero - James "moose2000" Brown (iPhone PNG) David Woo - Ben "Disch" Wenger (io callbacks) Roy Eltham - Martin "SpartanJ" Golini Luke Graham - Omar Cornut (1/2/4-bit png) Thomas Ruf - John Bartholomew - Optimizations & bugfixes Ken Hamada - Fabian "ryg" Giesen Cort Stratton - Arseny Kapoulkine Blazej Dariusz Roszkowski - Thibault Reuille - Paul Du Bois - Guillaume George - Jerry Jansson - If your name should be here but Hayaki Saito - isn't, let Sean know. Johan Duparc - Ronny Chevalier - Michal Cichon -*/ - -#ifndef STBI_INCLUDE_STB_IMAGE_H -#define STBI_INCLUDE_STB_IMAGE_H - -// Limitations: -// - no jpeg progressive support -// - non-HDR formats support 8-bit samples only (jpeg, png) -// - no delayed line count (jpeg) -- IJG doesn't support either -// - no 1-bit BMP -// - GIF always returns *comp=4 -// -// Basic usage (see HDR discussion below): -// int x,y,n; -// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); -// // ... process data if not NULL ... -// // ... x = width, y = height, n = # 8-bit components per pixel ... -// // ... replace '0' with '1'..'4' to force that many components per pixel -// // ... but 'n' will always be the number that it would have been if you said 0 -// stbi_image_free(data) -// -// Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *comp -- outputs # of image components in image file -// int req_comp -- if non-zero, # of image components requested in result -// -// The return value from an image loader is an 'unsigned char *' which points -// to the pixel data. The pixel data consists of *y scanlines of *x pixels, -// with each pixel consisting of N interleaved 8-bit components; the first -// pixel pointed to is top-left-most in the image. There is no padding between -// image scanlines or between pixels, regardless of format. The number of -// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. -// If req_comp is non-zero, *comp has the number of components that _would_ -// have been output otherwise. E.g. if you set req_comp to 4, you will always -// get RGBA output, but you can check *comp to easily see if it's opaque. -// -// An output image with N components has the following components interleaved -// in this order in each pixel: -// -// N=#comp components -// 1 grey -// 2 grey, alpha -// 3 red, green, blue -// 4 red, green, blue, alpha -// -// If image loading fails for any reason, the return value will be NULL, -// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() -// can be queried for an extremely brief, end-user unfriendly explanation -// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid -// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly -// more user-friendly ones. -// -// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. -// -// =========================================================================== -// -// iPhone PNG support: -// -// By default we convert iphone-formatted PNGs back to RGB; nominally they -// would silently load as BGR, except the existing code should have just -// failed on such iPhone PNGs. But you can disable this conversion by -// by calling stbi_convert_iphone_png_to_rgb(0), in which case -// you will always just get the native iphone "format" through. -// -// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per -// pixel to remove any premultiplied alpha *only* if the image file explicitly -// says there's premultiplied data (currently only happens in iPhone images, -// and only if iPhone convert-to-rgb processing is on). -// -// =========================================================================== -// -// HDR image support (disable by defining STBI_NO_HDR) -// -// stb_image now supports loading HDR images in general, and currently -// the Radiance .HDR file format, although the support is provided -// generically. You can still load any file through the existing interface; -// if you attempt to load an HDR file, it will be automatically remapped to -// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; -// both of these constants can be reconfigured through this interface: -// -// stbi_hdr_to_ldr_gamma(2.2f); -// stbi_hdr_to_ldr_scale(1.0f); -// -// (note, do not use _inverse_ constants; stbi_image will invert them -// appropriately). -// -// Additionally, there is a new, parallel interface for loading files as -// (linear) floats to preserve the full dynamic range: -// -// float *data = stbi_loadf(filename, &x, &y, &n, 0); -// -// If you load LDR images through this interface, those images will -// be promoted to floating point values, run through the inverse of -// constants corresponding to the above: -// -// stbi_ldr_to_hdr_scale(1.0f); -// stbi_ldr_to_hdr_gamma(2.2f); -// -// Finally, given a filename (or an open file or memory block--see header -// file for details) containing image data, you can query for the "most -// appropriate" interface to use (that is, whether the image is HDR or -// not), using: -// -// stbi_is_hdr(char *filename); -// -// =========================================================================== -// -// I/O callbacks -// -// I/O callbacks allow you to read from arbitrary sources, like packaged -// files or some other source. Data read from callbacks are processed -// through a small internal buffer (currently 128 bytes) to try to reduce -// overhead. -// -// The three functions you must define are "read" (reads some bytes of data), -// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). - - -#ifndef STBI_NO_STDIO -#include -#endif // STBI_NO_STDIO - -#define STBI_VERSION 1 - -enum -{ - STBI_default = 0, // only used for req_comp - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -typedef unsigned char stbi_uc; - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef STB_IMAGE_STATIC -#define STBIDEF static -#else -#define STBIDEF extern -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// PRIMARY API - works on images of any type -// - -// -// load image by filename, open file, or memory buffer -// - -STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); -// for stbi_load_from_file, file pointer is left pointing immediately after image -#endif - -typedef struct -{ - int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read - void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative - int (*eof) (void *user); // returns nonzero if we are at end of file/data -} stbi_io_callbacks; - -STBIDEF stbi_uc *stbi_load_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); - -#ifndef STBI_NO_HDR - STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); - - #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); - #endif - - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); - - STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); - STBIDEF void stbi_hdr_to_ldr_scale(float scale); - - STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); - STBIDEF void stbi_ldr_to_hdr_scale(float scale); -#endif // STBI_NO_HDR - -// stbi_is_hdr is always defined -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename); -STBIDEF int stbi_is_hdr_from_file(FILE *f); -#endif // STBI_NO_STDIO - - -// get a VERY brief reason for failure -// NOT THREADSAFE -STBIDEF const char *stbi_failure_reason (void); - -// free the loaded image -- this is just free() -STBIDEF void stbi_image_free (void *retval_from_stbi_load); - -// get image dimensions & components without fully decoding -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); - -#endif - - - -// for image formats that explicitly notate that they have premultiplied alpha, -// we just return the colors as stored in the file. set this flag to force -// unpremultiplication. results are undefined if the unpremultiply overflow. -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); - -// indicate whether we should process iphone images back to canonical format, -// or just pass them through "as-is" -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); - - -// ZLIB client - used by PNG, available for other purposes - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); -STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - -STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - - -// define faster low-level operations (typically SIMD support) -#ifdef STBI_SIMD -typedef void (*stbi_idct_8x8)(stbi_uc *out, int out_stride, short data[64], unsigned short *dequantize); -// compute an integer IDCT on "input" -// input[x] = data[x] * dequantize[x] -// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride' -// CLAMP results to 0..255 -typedef void (*stbi_YCbCr_to_RGB_run)(stbi_uc *output, stbi_uc const *y, stbi_uc const *cb, stbi_uc const *cr, int count, int step); -// compute a conversion from YCbCr to RGB -// 'count' pixels -// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B -// y: Y input channel -// cb: Cb input channel; scale/biased to be 0..255 -// cr: Cr input channel; scale/biased to be 0..255 - -STBIDEF void stbi_install_idct(stbi_idct_8x8 func); -STBIDEF void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func); -#endif // STBI_SIMD - - -#ifdef __cplusplus -} -#endif - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // STBI_INCLUDE_STB_IMAGE_H - -#ifdef STB_IMAGE_IMPLEMENTATION - -#ifndef STBI_NO_HDR -#include // ldexp -#include // strcmp, strtok -#endif - -#ifndef STBI_NO_STDIO -#include -#endif -#include -#include -#ifndef STBI_ASSERT -#include -#define STBI_ASSERT(x) assert(x) -#endif -#include -#include // ptrdiff_t on osx - -#ifndef _MSC_VER - #ifdef __cplusplus - #define stbi_inline inline - #else - #define stbi_inline - #endif -#else - #define stbi_inline __forceinline -#endif - - -#ifdef _MSC_VER -typedef unsigned short stbi__uint16; -typedef signed short stbi__int16; -typedef unsigned int stbi__uint32; -typedef signed int stbi__int32; -#else -#include -typedef uint16_t stbi__uint16; -typedef int16_t stbi__int16; -typedef uint32_t stbi__uint32; -typedef int32_t stbi__int32; -#endif - -// should produce compiler error if size is wrong -typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) -#else -#define STBI_NOTUSED(v) (void)sizeof(v) -#endif - -#ifdef _MSC_VER -#define STBI_HAS_LROTL -#endif - -#ifdef STBI_HAS_LROTL - #define stbi_lrot(x,y) _lrotl(x,y) -#else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) -#endif - -/////////////////////////////////////////////// -// -// stbi__context struct and start_xxx functions - -// stbi__context structure is our basic context used by all images, so it -// contains all the IO context, plus some basic image information -typedef struct -{ - stbi__uint32 img_x, img_y; - int img_n, img_out_n; - - stbi_io_callbacks io; - void *io_user_data; - - int read_from_callbacks; - int buflen; - stbi_uc buffer_start[128]; - - stbi_uc *img_buffer, *img_buffer_end; - stbi_uc *img_buffer_original; -} stbi__context; - - -static void stbi__refill_buffer(stbi__context *s); - -// initialize a memory-decode context -static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) -{ - s->io.read = NULL; - s->read_from_callbacks = 0; - s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; - s->img_buffer_end = (stbi_uc *) buffer+len; -} - -// initialize a callback-based context -static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) -{ - s->io = *c; - s->io_user_data = user; - s->buflen = sizeof(s->buffer_start); - s->read_from_callbacks = 1; - s->img_buffer_original = s->buffer_start; - stbi__refill_buffer(s); -} - -#ifndef STBI_NO_STDIO - -static int stbi__stdio_read(void *user, char *data, int size) -{ - return (int) fread(data,1,size,(FILE*) user); -} - -static void stbi__stdio_skip(void *user, int n) -{ - fseek((FILE*) user, n, SEEK_CUR); -} - -static int stbi__stdio_eof(void *user) -{ - return feof((FILE*) user); -} - -static stbi_io_callbacks stbi__stdio_callbacks = -{ - stbi__stdio_read, - stbi__stdio_skip, - stbi__stdio_eof, -}; - -static void stbi__start_file(stbi__context *s, FILE *f) -{ - stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); -} - -//static void stop_file(stbi__context *s) { } - -#endif // !STBI_NO_STDIO - -static void stbi__rewind(stbi__context *s) -{ - // conceptually rewind SHOULD rewind to the beginning of the stream, - // but we just rewind to the beginning of the initial buffer, because - // we only use it after doing 'test', which only ever looks at at most 92 bytes - s->img_buffer = s->img_buffer_original; -} - -static int stbi__jpeg_test(stbi__context *s); -static stbi_uc *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__png_test(stbi__context *s); -static stbi_uc *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__bmp_test(stbi__context *s); -static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__tga_test(stbi__context *s); -static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__psd_test(stbi__context *s); -static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -#ifndef STBI_NO_HDR -static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -#endif -static int stbi__pic_test(stbi__context *s); -static stbi_uc *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__gif_test(stbi__context *s); -static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); - - -// this is not threadsafe -static const char *stbi__g_failure_reason; - -STBIDEF const char *stbi_failure_reason(void) -{ - return stbi__g_failure_reason; -} - -static int stbi__err(const char *str) -{ - stbi__g_failure_reason = str; - return 0; -} - -static void *stbi__malloc(size_t size) -{ - return malloc(size); -} - -// stbi__err - error -// stbi__errpf - error returning pointer to float -// stbi__errpuc - error returning pointer to unsigned char - -#ifdef STBI_NO_FAILURE_STRINGS - #define stbi__err(x,y) 0 -#elif defined(STBI_FAILURE_USERMSG) - #define stbi__err(x,y) stbi__err(y) -#else - #define stbi__err(x,y) stbi__err(x) -#endif - -#define stbi__errpf(x,y) ((float *) (stbi__err(x,y)?NULL:NULL)) -#define stbi__errpuc(x,y) ((unsigned char *) (stbi__err(x,y)?NULL:NULL)) - -STBIDEF void stbi_image_free(void *retval_from_stbi_load) -{ - free(retval_from_stbi_load); -} - -#ifndef STBI_NO_HDR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); -#endif - -static unsigned char *stbi_load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp); - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp); - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp); - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp); - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp); - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp); - - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp); - return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } - #endif - - // test tga last because it's a crappy test! - if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp); - return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); -} - -#ifndef STBI_NO_STDIO - -FILE *stbi__fopen(char const *filename, char const *mode) -{ - FILE *f; -#if defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != fopen_s(&f, filename, mode)) - f=0; -#else - f = fopen(filename, mode); -#endif - return f; -} - - -STBIDEF unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - unsigned char *result; - if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi_load_main(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} -#endif //!STBI_NO_STDIO - -STBIDEF unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi_load_main(&s,x,y,comp,req_comp); -} - -unsigned char *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi_load_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_HDR - -float *stbi_loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) - return stbi__hdr_load(s,x,y,comp,req_comp); - #endif - data = stbi_load_main(s, x, y, comp, req_comp); - if (data) - return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); -} - -float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi_loadf_main(&s,x,y,comp,req_comp); -} - -float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi_loadf_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_STDIO -float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - float *result; - FILE *f = stbi__fopen(filename, "rb"); - if (!f) return stbi__errpf("can't fopen", "Unable to open file"); - result = stbi_loadf_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_file(&s,f); - return stbi_loadf_main(&s,x,y,comp,req_comp); -} -#endif // !STBI_NO_STDIO - -#endif // !STBI_NO_HDR - -// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is -// defined, for API simplicity; if STBI_NO_HDR is defined, it always -// reports false! - -int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(buffer); - STBI_NOTUSED(len); - return 0; - #endif -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result=0; - if (f) { - result = stbi_is_hdr_from_file(f); - fclose(f); - } - return result; -} - -STBIDEF int stbi_is_hdr_from_file(FILE *f) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_file(&s,f); - return stbi__hdr_test(&s); - #else - return 0; - #endif -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__hdr_test(&s); - #else - return 0; - #endif -} - -#ifndef STBI_NO_HDR -static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; -static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; - -void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } -void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } - -void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } -void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } -#endif - - -////////////////////////////////////////////////////////////////////////////// -// -// Common code used by all image loaders -// - -enum -{ - SCAN_load=0, - SCAN_type, - SCAN_header -}; - -static void stbi__refill_buffer(stbi__context *s) -{ - int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); - if (n == 0) { - // at end of file, treat same as if from memory, but need to handle case - // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file - s->read_from_callbacks = 0; - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start+1; - *s->img_buffer = 0; - } else { - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start + n; - } -} - -stbi_inline static stbi_uc stbi__get8(stbi__context *s) -{ - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - if (s->read_from_callbacks) { - stbi__refill_buffer(s); - return *s->img_buffer++; - } - return 0; -} - -stbi_inline static int stbi__at_eof(stbi__context *s) -{ - if (s->io.read) { - if (!(s->io.eof)(s->io_user_data)) return 0; - // if feof() is true, check if buffer = end - // special case: we've only got the special 0 character at the end - if (s->read_from_callbacks == 0) return 1; - } - - return s->img_buffer >= s->img_buffer_end; -} - -static void stbi__skip(stbi__context *s, int n) -{ - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - s->img_buffer = s->img_buffer_end; - (s->io.skip)(s->io_user_data, n - blen); - return; - } - } - s->img_buffer += n; -} - -static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) -{ - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - int res, count; - - memcpy(buffer, s->img_buffer, blen); - - count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); - res = (count == (n-blen)); - s->img_buffer = s->img_buffer_end; - return res; - } - } - - if (s->img_buffer+n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} - -static int stbi__get16be(stbi__context *s) -{ - int z = stbi__get8(s); - return (z << 8) + stbi__get8(s); -} - -static stbi__uint32 stbi__get32be(stbi__context *s) -{ - stbi__uint32 z = stbi__get16be(s); - return (z << 16) + stbi__get16be(s); -} - -static int stbi__get16le(stbi__context *s) -{ - int z = stbi__get8(s); - return z + (stbi__get8(s) << 8); -} - -static stbi__uint32 stbi__get32le(stbi__context *s) -{ - stbi__uint32 z = stbi__get16le(s); - return z + (stbi__get16le(s) << 16); -} - -////////////////////////////////////////////////////////////////////////////// -// -// generic converter from built-in img_n to req_comp -// individual types do this automatically as much as possible (e.g. jpeg -// does all cases internally since it needs to colorspace convert anyway, -// and it never has alpha, so very few cases ). png can automatically -// interleave an alpha=255 channel, but falls back to this for other cases -// -// assume data buffer is malloced, so malloc a new one and free that one -// only failure mode is malloc failing - -static stbi_uc stbi__compute_y(int r, int g, int b) -{ - return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); -} - -static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - unsigned char *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *) stbi__malloc(req_comp * x * y); - if (good == NULL) { - free(data); - return stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - unsigned char *src = data + j * x * img_n ; - unsigned char *dest = good + j * x * req_comp; - - #define COMBO(a,b) ((a)*8+(b)) - #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (COMBO(img_n, req_comp)) { - CASE(1,2) dest[0]=src[0], dest[1]=255; break; - CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; - CASE(2,1) dest[0]=src[0]; break; - CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; - CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; - CASE(3,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; - CASE(3,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; break; - CASE(4,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; - CASE(4,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; - CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; - default: STBI_ASSERT(0); - } - #undef CASE - } - - free(data); - return good; -} - -#ifndef STBI_NO_HDR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) -{ - int i,k,n; - float *output = (float *) stbi__malloc(x * y * comp * sizeof(float)); - if (output == NULL) { free(data); return stbi__errpf("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); - } - if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; - } - free(data); - return output; -} - -#define stbi__float2int(x) ((int) (x)) -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) -{ - int i,k,n; - stbi_uc *output = (stbi_uc *) stbi__malloc(x * y * comp); - if (output == NULL) { free(data); return stbi__errpuc("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - if (k < comp) { - float z = data[i*comp+k] * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - } - free(data); - return output; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// "baseline" JPEG/JFIF decoder (not actually fully baseline implementation) -// -// simple implementation -// - channel subsampling of at most 2 in each dimension -// - doesn't support delayed output of y-dimension -// - simple interface (only one output format: 8-bit interleaved RGB) -// - doesn't try to recover corrupt jpegs -// - doesn't allow partial loading, loading multiple at once -// - still fast on x86 (copying globals into locals doesn't help x86) -// - allocates lots of intermediate memory (full size of all components) -// - non-interleaved case requires this anyway -// - allows good upsampling (see next) -// high-quality -// - upsampled channels are bilinearly interpolated, even across blocks -// - quality integer IDCT derived from IJG's 'slow' -// performance -// - fast huffman; reasonable integer IDCT -// - uses a lot of intermediate memory, could cache poorly -// - load http://nothings.org/remote/anemones.jpg 3 times on 2.8Ghz P4 -// stb_jpeg: 1.34 seconds (MSVC6, default release build) -// stb_jpeg: 1.06 seconds (MSVC6, processor = Pentium Pro) -// IJL11.dll: 1.08 seconds (compiled by intel) -// IJG 1998: 0.98 seconds (MSVC6, makefile provided by IJG) -// IJG 1998: 0.95 seconds (MSVC6, makefile + proc=PPro) - -// huffman decoding acceleration -#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache - -typedef struct -{ - stbi_uc fast[1 << FAST_BITS]; - // weirdly, repacking this into AoS is a 10% speed loss, instead of a win - stbi__uint16 code[256]; - stbi_uc values[256]; - stbi_uc size[257]; - unsigned int maxcode[18]; - int delta[17]; // old 'firstsymbol' - old 'firstcode' -} stbi__huffman; - -typedef struct -{ - #ifdef STBI_SIMD - unsigned short dequant2[4][64]; - #endif - stbi__context *s; - stbi__huffman huff_dc[4]; - stbi__huffman huff_ac[4]; - stbi_uc dequant[4][64]; - -// sizes for components, interleaved MCUs - int img_h_max, img_v_max; - int img_mcu_x, img_mcu_y; - int img_mcu_w, img_mcu_h; - -// definition of jpeg image component - struct - { - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; - - int x,y,w2,h2; - stbi_uc *data; - void *raw_data; - stbi_uc *linebuf; - } img_comp[4]; - - stbi__uint32 code_buffer; // jpeg entropy-coded buffer - int code_bits; // number of valid bits - unsigned char marker; // marker seen while filling entropy buffer - int nomore; // flag if we saw a marker so must stop - - int scan_n, order[4]; - int restart_interval, todo; -} stbi__jpeg; - -static int stbi__build_huffman(stbi__huffman *h, int *count) -{ - int i,j,k=0,code; - // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) - h->size[k++] = (stbi_uc) (i+1); - h->size[k] = 0; - - // compute actual symbols (from jpeg spec) - code = 0; - k = 0; - for(j=1; j <= 16; ++j) { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) { - while (h->size[k] == j) - h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16-j); - code <<= 1; - } - h->maxcode[j] = 0xffffffff; - - // build non-spec acceleration table; 255 is flag for not-accelerated - memset(h->fast, 255, 1 << FAST_BITS); - for (i=0; i < k; ++i) { - int s = h->size[i]; - if (s <= FAST_BITS) { - int c = h->code[i] << (FAST_BITS-s); - int m = 1 << (FAST_BITS-s); - for (j=0; j < m; ++j) { - h->fast[c+j] = (stbi_uc) i; - } - } - } - return 1; -} - -static void stbi__grow_buffer_unsafe(stbi__jpeg *j) -{ - do { - int b = j->nomore ? 0 : stbi__get8(j->s); - if (b == 0xff) { - int c = stbi__get8(j->s); - if (c != 0) { - j->marker = (unsigned char) c; - j->nomore = 1; - return; - } - } - j->code_buffer |= b << (24 - j->code_bits); - j->code_bits += 8; - } while (j->code_bits <= 24); -} - -// (1 << n) - 1 -static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; - -// decode a jpeg huffman value from the bitstream -stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) -{ - unsigned int temp; - int c,k; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - // look at the top FAST_BITS and determine what symbol ID it is, - // if the code is <= FAST_BITS - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - k = h->fast[c]; - if (k < 255) { - int s = h->size[k]; - if (s > j->code_bits) - return -1; - j->code_buffer <<= s; - j->code_bits -= s; - return h->values[k]; - } - - // naive test is to shift the code_buffer down so k bits are - // valid, then test against maxcode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - temp = j->code_buffer >> 16; - for (k=FAST_BITS+1 ; ; ++k) - if (temp < h->maxcode[k]) - break; - if (k == 17) { - // error! code not found - j->code_bits -= 16; - return -1; - } - - if (k > j->code_bits) - return -1; - - // convert the huffman code to the symbol id - c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; - STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); - - // convert the id to a symbol - j->code_bits -= k; - j->code_buffer <<= k; - return h->values[c]; -} - -// combined JPEG 'receive' and JPEG 'extend', since baseline -// always extends everything it receives. -stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) -{ - unsigned int m = 1 << (n-1); - unsigned int k; - if (j->code_bits < n) stbi__grow_buffer_unsafe(j); - - #if 1 - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - #else - k = (j->code_buffer >> (32 - n)) & stbi__bmask[n]; - j->code_bits -= n; - j->code_buffer <<= n; - #endif - // the following test is probably a random branch that won't - // predict well. I tried to table accelerate it but failed. - // maybe it's compiling as a conditional move? - if (k < m) - return (-1 << n) + k + 1; - else - return k; -} - -// given a value that's at position X in the zigzag stream, -// where does it appear in the 8x8 matrix coded as row-major? -static stbi_uc stbi__jpeg_dezigzag[64+15] = -{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - // let corrupt input sample past end - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63 -}; - -// decode one 64-entry block-- -static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, int b) -{ - int diff,dc,k; - int t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - - // 0 all the ac values now so we can do it 32-bits at a time - memset(data,0,64*sizeof(data[0])); - - diff = t ? stbi__extend_receive(j, t) : 0; - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) dc; - - // decode AC components, see JPEG spec - k = 1; - do { - int r,s; - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (rs != 0xf0) break; // end block - k += 16; - } else { - k += r; - // decode into unzigzag'd location - data[stbi__jpeg_dezigzag[k++]] = (short) stbi__extend_receive(j,s); - } - } while (k < 64); - return 1; -} - -// take a -128..127 value and stbi__clamp it and convert to 0..255 -stbi_inline static stbi_uc stbi__clamp(int x) -{ - // trick to use a single test to catch both cases - if ((unsigned int) x > 255) { - if (x < 0) return 0; - if (x > 255) return 255; - } - return (stbi_uc) x; -} - -#define stbi__f2f(x) (int) (((x) * 4096 + 0.5)) -#define stbi__fsh(x) ((x) << 12) - -// derived from jidctint -- DCT_ISLOW -#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ - int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ - p2 = s2; \ - p3 = s6; \ - p1 = (p2+p3) * stbi__f2f(0.5411961f); \ - t2 = p1 + p3*stbi__f2f(-1.847759065f); \ - t3 = p1 + p2*stbi__f2f( 0.765366865f); \ - p2 = s0; \ - p3 = s4; \ - t0 = stbi__fsh(p2+p3); \ - t1 = stbi__fsh(p2-p3); \ - x0 = t0+t3; \ - x3 = t0-t3; \ - x1 = t1+t2; \ - x2 = t1-t2; \ - t0 = s7; \ - t1 = s5; \ - t2 = s3; \ - t3 = s1; \ - p3 = t0+t2; \ - p4 = t1+t3; \ - p1 = t0+t3; \ - p2 = t1+t2; \ - p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ - t0 = t0*stbi__f2f( 0.298631336f); \ - t1 = t1*stbi__f2f( 2.053119869f); \ - t2 = t2*stbi__f2f( 3.072711026f); \ - t3 = t3*stbi__f2f( 1.501321110f); \ - p1 = p5 + p1*stbi__f2f(-0.899976223f); \ - p2 = p5 + p2*stbi__f2f(-2.562915447f); \ - p3 = p3*stbi__f2f(-1.961570560f); \ - p4 = p4*stbi__f2f(-0.390180644f); \ - t3 += p1+p4; \ - t2 += p2+p3; \ - t1 += p2+p4; \ - t0 += p1+p3; - -#ifdef STBI_SIMD -typedef unsigned short stbi_dequantize_t; -#else -typedef stbi_uc stbi_dequantize_t; -#endif - -// .344 seconds on 3*anemones.jpg -static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64], stbi_dequantize_t *dequantize) -{ - int i,val[64],*v=val; - stbi_dequantize_t *dq = dequantize; - stbi_uc *o; - short *d = data; - - // columns - for (i=0; i < 8; ++i,++d,++dq, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0] * dq[0] << 2; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - STBI__IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24], - d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } - } - - for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - // so we want to round that, which means adding 0.5 * 1<<17, - // aka 65536. Also, we'll end up with -128 to 127 that we want - // to encode as 0..255 by adding 128, so we'll add that before the shift - x0 += 65536 + (128<<17); - x1 += 65536 + (128<<17); - x2 += 65536 + (128<<17); - x3 += 65536 + (128<<17); - // tried computing the shifts into temps, or'ing the temps to see - // if any were out of range, but that was slower - o[0] = stbi__clamp((x0+t3) >> 17); - o[7] = stbi__clamp((x0-t3) >> 17); - o[1] = stbi__clamp((x1+t2) >> 17); - o[6] = stbi__clamp((x1-t2) >> 17); - o[2] = stbi__clamp((x2+t1) >> 17); - o[5] = stbi__clamp((x2-t1) >> 17); - o[3] = stbi__clamp((x3+t0) >> 17); - o[4] = stbi__clamp((x3-t0) >> 17); - } -} - -#ifdef STBI_SIMD -static stbi_idct_8x8 stbi__idct_installed = stbi__idct_block; - -STBIDEF void stbi_install_idct(stbi_idct_8x8 func) -{ - stbi__idct_installed = func; -} -#endif - -#define STBI__MARKER_none 0xff -// if there's a pending marker from the entropy stream, return that -// otherwise, fetch from the stream and get a marker. if there's no -// marker, return 0xff, which is never a valid marker value -static stbi_uc stbi__get_marker(stbi__jpeg *j) -{ - stbi_uc x; - if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } - x = stbi__get8(j->s); - if (x != 0xff) return STBI__MARKER_none; - while (x == 0xff) - x = stbi__get8(j->s); - return x; -} - -// in each scan, we'll have scan_n components, and the order -// of the components is specified by order[] -#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) - -// after a restart interval, stbi__jpeg_reset the entropy decoder and -// the dc prediction -static void stbi__jpeg_reset(stbi__jpeg *j) -{ - j->code_bits = 0; - j->code_buffer = 0; - j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; - j->marker = STBI__MARKER_none; - j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; - // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, - // since we don't even allow 1<<30 pixels -} - -static int stbi__parse_entropy_coded_data(stbi__jpeg *z) -{ - stbi__jpeg_reset(z); - if (z->scan_n == 1) { - int i,j; - #ifdef STBI_SIMD - __declspec(align(16)) - #endif - short data[64]; - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; - #ifdef STBI_SIMD - stbi__idct_installed(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); - #else - stbi__idct_block(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); - #endif - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - } else { // interleaved! - int i,j,k,x,y; - short data[64]; - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x)*8; - int y2 = (j*z->img_comp[n].v + y)*8; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; - #ifdef STBI_SIMD - stbi__idct_installed(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); - #else - stbi__idct_block(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); - #endif - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - } - return 1; -} - -static int stbi__process_marker(stbi__jpeg *z, int m) -{ - int L; - switch (m) { - case STBI__MARKER_none: // no marker found - return stbi__err("expected marker","Corrupt JPEG"); - - case 0xC2: // stbi__SOF - progressive - return stbi__err("progressive jpeg","JPEG format not supported (progressive)"); - - case 0xDD: // DRI - specify restart interval - if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); - z->restart_interval = stbi__get16be(z->s); - return 1; - - case 0xDB: // DQT - define quantization table - L = stbi__get16be(z->s)-2; - while (L > 0) { - int q = stbi__get8(z->s); - int p = q >> 4; - int t = q & 15,i; - if (p != 0) return stbi__err("bad DQT type","Corrupt JPEG"); - if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); - for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = stbi__get8(z->s); - #ifdef STBI_SIMD - for (i=0; i < 64; ++i) - z->dequant2[t][i] = z->dequant[t][i]; - #endif - L -= 65; - } - return L==0; - - case 0xC4: // DHT - define huffman table - L = stbi__get16be(z->s)-2; - while (L > 0) { - stbi_uc *v; - int sizes[16],i,n=0; - int q = stbi__get8(z->s); - int tc = q >> 4; - int th = q & 15; - if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); - for (i=0; i < 16; ++i) { - sizes[i] = stbi__get8(z->s); - n += sizes[i]; - } - L -= 17; - if (tc == 0) { - if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; - v = z->huff_dc[th].values; - } else { - if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; - v = z->huff_ac[th].values; - } - for (i=0; i < n; ++i) - v[i] = stbi__get8(z->s); - L -= n; - } - return L==0; - } - // check for comment block or APP blocks - if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - stbi__skip(z->s, stbi__get16be(z->s)-2); - return 1; - } - return 0; -} - -// after we see stbi__SOS -static int stbi__process_scan_header(stbi__jpeg *z) -{ - int i; - int Ls = stbi__get16be(z->s); - z->scan_n = stbi__get8(z->s); - if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad stbi__SOS component count","Corrupt JPEG"); - if (Ls != 6+2*z->scan_n) return stbi__err("bad stbi__SOS len","Corrupt JPEG"); - for (i=0; i < z->scan_n; ++i) { - int id = stbi__get8(z->s), which; - int q = stbi__get8(z->s); - for (which = 0; which < z->s->img_n; ++which) - if (z->img_comp[which].id == id) - break; - if (which == z->s->img_n) return 0; - z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); - z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); - z->order[i] = which; - } - if (stbi__get8(z->s) != 0) return stbi__err("bad stbi__SOS","Corrupt JPEG"); - stbi__get8(z->s); // should be 63, but might be 0 - if (stbi__get8(z->s) != 0) return stbi__err("bad stbi__SOS","Corrupt JPEG"); - - return 1; -} - -static int stbi__process_frame_header(stbi__jpeg *z, int scan) -{ - stbi__context *s = z->s; - int Lf,p,i,q, h_max=1,v_max=1,c; - Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad stbi__SOF len","Corrupt JPEG"); // JPEG - p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline - s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires - c = stbi__get8(s); - if (c != 3 && c != 1) return stbi__err("bad component count","Corrupt JPEG"); // JFIF requires - s->img_n = c; - for (i=0; i < c; ++i) { - z->img_comp[i].data = NULL; - z->img_comp[i].linebuf = NULL; - } - - if (Lf != 8+3*s->img_n) return stbi__err("bad stbi__SOF len","Corrupt JPEG"); - - for (i=0; i < s->img_n; ++i) { - z->img_comp[i].id = stbi__get8(s); - if (z->img_comp[i].id != i+1) // JFIF requires - if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! - return stbi__err("bad component ID","Corrupt JPEG"); - q = stbi__get8(s); - z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); - z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); - z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); - } - - if (scan != SCAN_load) return 1; - - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - - for (i=0; i < s->img_n; ++i) { - if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; - if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; - } - - // compute interleaved mcu info - z->img_h_max = h_max; - z->img_v_max = v_max; - z->img_mcu_w = h_max * 8; - z->img_mcu_h = v_max * 8; - z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; - z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; - - for (i=0; i < s->img_n; ++i) { - // number of effective pixels (e.g. for non-interleaved MCU) - z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; - z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; - z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); - if (z->img_comp[i].raw_data == NULL) { - for(--i; i >= 0; --i) { - free(z->img_comp[i].raw_data); - z->img_comp[i].data = NULL; - } - return stbi__err("outofmem", "Out of memory"); - } - // align blocks for installable-idct using mmx/sse - z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - z->img_comp[i].linebuf = NULL; - } - - return 1; -} - -// use comparisons since in some cases we handle more than one case (e.g. stbi__SOF) -#define stbi__DNL(x) ((x) == 0xdc) -#define stbi__SOI(x) ((x) == 0xd8) -#define stbi__EOI(x) ((x) == 0xd9) -#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1) -#define stbi__SOS(x) ((x) == 0xda) - -static int decode_jpeg_header(stbi__jpeg *z, int scan) -{ - int m; - z->marker = STBI__MARKER_none; // initialize cached marker to empty - m = stbi__get_marker(z); - if (!stbi__SOI(m)) return stbi__err("no stbi__SOI","Corrupt JPEG"); - if (scan == SCAN_type) return 1; - m = stbi__get_marker(z); - while (!stbi__SOF(m)) { - if (!stbi__process_marker(z,m)) return 0; - m = stbi__get_marker(z); - while (m == STBI__MARKER_none) { - // some files have extra padding after their blocks, so ok, we'll scan - if (stbi__at_eof(z->s)) return stbi__err("no stbi__SOF", "Corrupt JPEG"); - m = stbi__get_marker(z); - } - } - if (!stbi__process_frame_header(z, scan)) return 0; - return 1; -} - -static int decode_jpeg_image(stbi__jpeg *j) -{ - int m; - j->restart_interval = 0; - if (!decode_jpeg_header(j, SCAN_load)) return 0; - m = stbi__get_marker(j); - while (!stbi__EOI(m)) { - if (stbi__SOS(m)) { - if (!stbi__process_scan_header(j)) return 0; - if (!stbi__parse_entropy_coded_data(j)) return 0; - if (j->marker == STBI__MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } else if (x != 0) { - return 0; - } - } - // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 - } - } else { - if (!stbi__process_marker(j, m)) return 0; - } - m = stbi__get_marker(j); - } - return 1; -} - -// static jfif-centered resampling (across block boundaries) - -typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, - int w, int hs); - -#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) - -static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - STBI_NOTUSED(out); - STBI_NOTUSED(in_far); - STBI_NOTUSED(w); - STBI_NOTUSED(hs); - return in_near; -} - -static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples vertically for every one in input - int i; - STBI_NOTUSED(hs); - for (i=0; i < w; ++i) - out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); - return out; -} - -static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples horizontally for every one in input - int i; - stbi_uc *input = in_near; - - if (w == 1) { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; - } - - out[0] = input[0]; - out[1] = stbi__div4(input[0]*3 + input[1] + 2); - for (i=1; i < w-1; ++i) { - int n = 3*input[i]+2; - out[i*2+0] = stbi__div4(n+input[i-1]); - out[i*2+1] = stbi__div4(n+input[i+1]); - } - out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); - out[i*2+1] = input[w-1]; - - STBI_NOTUSED(in_far); - STBI_NOTUSED(hs); - - return out; -} - -#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) - -static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i,t0,t1; - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - out[0] = stbi__div4(t1+2); - for (i=1; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} - -static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // resample with nearest-neighbor - int i,j; - STBI_NOTUSED(in_far); - for (i=0; i < w; ++i) - for (j=0; j < hs; ++j) - out[i*hs+j] = in_near[i]; - return out; -} - -#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) - -// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro) -// VC6 without processor=Pro is generating multiple LEAs per multiply! -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 16) + 32768; // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr*float2fixed(1.40200f); - g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); - b = y_fixed + cb*float2fixed(1.77200f); - r >>= 16; - g >>= 16; - b >>= 16; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} - -#ifdef STBI_SIMD -static stbi_YCbCr_to_RGB_run stbi__YCbCr_installed = stbi__YCbCr_to_RGB_row; - -STBIDEF void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func) -{ - stbi__YCbCr_installed = func; -} -#endif - - -// clean up the temporary component buffers -static void stbi__cleanup_jpeg(stbi__jpeg *j) -{ - int i; - for (i=0; i < j->s->img_n; ++i) { - if (j->img_comp[i].raw_data) { - free(j->img_comp[i].raw_data); - j->img_comp[i].raw_data = NULL; - j->img_comp[i].data = NULL; - } - if (j->img_comp[i].linebuf) { - free(j->img_comp[i].linebuf); - j->img_comp[i].linebuf = NULL; - } - } -} - -typedef struct -{ - resample_row_func resample; - stbi_uc *line0,*line1; - int hs,vs; // expansion factor in each axis - int w_lores; // horizontal pixels pre-expansion - int ystep; // how far through vertical expansion we are - int ypos; // which pre-expansion row we're on -} stbi__resample; - -static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n; - z->s->img_n = 0; // make stbi__cleanup_jpeg safe - - // validate req_comp - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - - // load a jpeg image from whichever source - if (!decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } - - // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n; - - if (z->s->img_n == 3 && n < 3) - decode_n = 1; - else - decode_n = z->s->img_n; - - // resample and color-convert - { - int k; - unsigned int i,j; - stbi_uc *output; - stbi_uc *coutput[4]; - - stbi__resample res_comp[4]; - - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); - if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - r->hs = z->img_h_max / z->img_comp[k].h; - r->vs = z->img_v_max / z->img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (z->s->img_x + r->hs-1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = z->img_comp[k].data; - - if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) r->resample = stbi__resample_row_hv_2; - else r->resample = stbi__resample_row_generic; - } - - // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc(n * z->s->img_x * z->s->img_y + 1); - if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - // now go ahead and resample - for (j=0; j < z->s->img_y; ++j) { - stbi_uc *out = output + n * z->s->img_x * j; - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(z->img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < z->img_comp[k].y) - r->line1 += z->img_comp[k].w2; - } - } - if (n >= 3) { - stbi_uc *y = coutput[0]; - if (z->s->img_n == 3) { - #ifdef STBI_SIMD - stbi__YCbCr_installed(out, y, coutput[1], coutput[2], z->s->img_x, n); - #else - stbi__YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], z->s->img_x, n); - #endif - } else - for (i=0; i < z->s->img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; - } - } - stbi__cleanup_jpeg(z); - *out_x = z->s->img_x; - *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n; // report original components, not output - return output; - } -} - -static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__jpeg j; - j.s = s; - return load_jpeg_image(&j, x,y,comp,req_comp); -} - -static int stbi__jpeg_test(stbi__context *s) -{ - int r; - stbi__jpeg j; - j.s = s; - r = decode_jpeg_header(&j, SCAN_type); - stbi__rewind(s); - return r; -} - -static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) -{ - if (!decode_jpeg_header(j, SCAN_header)) { - stbi__rewind( j->s ); - return 0; - } - if (x) *x = j->s->img_x; - if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n; - return 1; -} - -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__jpeg j; - j.s = s; - return stbi__jpeg_info_raw(&j, x, y, comp); -} - -// public domain zlib decode v0.2 Sean Barrett 2006-11-18 -// simple implementation -// - all input must be provided in an upfront buffer -// - all output is written to a single output buffer (can malloc/realloc) -// performance -// - fast huffman - -// fast-way is faster to check than jpeg huffman, but slow way is slower -#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables -#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) - -// zlib-style huffman encoding -// (jpegs packs from left, zlib from right, so can't share code) -typedef struct -{ - stbi__uint16 fast[1 << STBI__ZFAST_BITS]; - stbi__uint16 firstcode[16]; - int maxcode[17]; - stbi__uint16 firstsymbol[16]; - stbi_uc size[288]; - stbi__uint16 value[288]; -} stbi__zhuffman; - -stbi_inline static int stbi__bitreverse16(int n) -{ - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -stbi_inline static int stbi__bit_reverse(int v, int bits) -{ - STBI_ASSERT(bits <= 16); - // to bit reverse n bits, reverse 16 and shift - // e.g. 11 bits, bit reverse and shift away 5 - return stbi__bitreverse16(v) >> (16-bits); -} - -static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) -{ - int i,k=0; - int code, next_code[16], sizes[17]; - - // DEFLATE spec for generating codes - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 255, sizeof(z->fast)); - for (i=0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i=1; i < 16; ++i) - STBI_ASSERT(sizes[i] <= (1 << i)); - code = 0; - for (i=1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (stbi__uint16) code; - z->firstsymbol[i] = (stbi__uint16) k; - code = (code + sizes[i]); - if (sizes[i]) - if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt JPEG"); - z->maxcode[i] = code << (16-i); // preshift for inner loop - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; // sentinel - for (i=0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - z->size [c] = (stbi_uc ) s; - z->value[c] = (stbi__uint16) i; - if (s <= STBI__ZFAST_BITS) { - int k = stbi__bit_reverse(next_code[s],s); - while (k < (1 << STBI__ZFAST_BITS)) { - z->fast[k] = (stbi__uint16) c; - k += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -// zlib-from-memory implementation for PNG reading -// because PNG allows splitting the zlib stream arbitrarily, -// and it's annoying structurally to have PNG call ZLIB call PNG, -// we require PNG read all the IDATs and combine them into a single -// memory buffer - -typedef struct -{ - stbi_uc *zbuffer, *zbuffer_end; - int num_bits; - stbi__uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - stbi__zhuffman z_length, z_distance; -} stbi__zbuf; - -stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) -{ - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; -} - -static void stbi__fill_bits(stbi__zbuf *z) -{ - do { - STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); - z->code_buffer |= stbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) -{ - unsigned int k; - if (z->num_bits < n) stbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s,k; - if (a->num_bits < 16) stbi__fill_bits(a); - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b < 0xffff) { - s = z->size[b]; - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; - } - - // not resolved by fast table, so compute it the slow way - // use jpeg approach, which requires MSbits at top - k = stbi__bit_reverse(a->code_buffer, 16); - for (s=STBI__ZFAST_BITS+1; ; ++s) - if (k < z->maxcode[s]) - break; - if (s == 16) return -1; // invalid code! - // code size is s, so: - b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - STBI_ASSERT(z->size[b] == s); - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -static int stbi__zexpand(stbi__zbuf *z, int n) // need to make room for n bytes -{ - char *q; - int cur, limit; - if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (int) (z->zout - z->zout_start); - limit = (int) (z->zout_end - z->zout_start); - while (cur + n > limit) - limit *= 2; - q = (char *) realloc(z->zout_start, limit); - if (q == NULL) return stbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static int stbi__zlength_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 int stbi__zlength_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 int stbi__zdist_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 int stbi__zdist_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 int stbi__parse_huffman_block(stbi__zbuf *a) -{ - for(;;) { - int z = stbi__zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes - if (a->zout >= a->zout_end) if (!stbi__zexpand(a, 1)) return 0; - *a->zout++ = (char) z; - } else { - stbi_uc *p; - int len,dist; - if (z == 256) return 1; - z -= 257; - len = stbi__zlength_base[z]; - if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); - z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); - dist = stbi__zdist_base[z]; - if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); - if (a->zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); - if (a->zout + len > a->zout_end) if (!stbi__zexpand(a, len)) return 0; - p = (stbi_uc *) (a->zout - dist); - while (len--) - *a->zout++ = *p++; - } - } -} - -static int stbi__compute_huffman_codes(stbi__zbuf *a) -{ - static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - stbi__zhuffman z_codelength; - stbi_uc lencodes[286+32+137];//padding for maximum single op - stbi_uc codelength_sizes[19]; - int i,n; - - int hlit = stbi__zreceive(a,5) + 257; - int hdist = stbi__zreceive(a,5) + 1; - int hclen = stbi__zreceive(a,4) + 4; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i=0; i < hclen; ++i) { - int s = stbi__zreceive(a,3); - codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; - } - if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < hlit + hdist) { - int c = stbi__zhuffman_decode(a, &z_codelength); - STBI_ASSERT(c >= 0 && c < 19); - if (c < 16) - lencodes[n++] = (stbi_uc) c; - else if (c == 16) { - c = stbi__zreceive(a,2)+3; - memset(lencodes+n, lencodes[n-1], c); - n += c; - } else if (c == 17) { - c = stbi__zreceive(a,3)+3; - memset(lencodes+n, 0, c); - n += c; - } else { - STBI_ASSERT(c == 18); - c = stbi__zreceive(a,7)+11; - memset(lencodes+n, 0, c); - n += c; - } - } - if (n != hlit+hdist) return stbi__err("bad codelengths","Corrupt PNG"); - if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; - return 1; -} - -static int stbi__parse_uncomperssed_block(stbi__zbuf *a) -{ - stbi_uc header[4]; - int len,nlen,k; - if (a->num_bits & 7) - stbi__zreceive(a, a->num_bits & 7); // discard - // drain the bit-packed data into header - k = 0; - while (a->num_bits > 0) { - header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check - a->code_buffer >>= 8; - a->num_bits -= 8; - } - STBI_ASSERT(a->num_bits == 0); - // now fill header the normal way - while (k < 4) - header[k++] = stbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!stbi__zexpand(a, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int stbi__parse_zlib_header(stbi__zbuf *a) -{ - int cmf = stbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = stbi__zget8(a); - if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png - if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; -} - -// @TODO: should statically initialize these for optimal thread safety -static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32]; -static void stbi__init_zdefaults(void) -{ - int i; // use <= to match clearly with spec - for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; - for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; - for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; - for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; - - for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; -} - -static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) -{ - int final, type; - if (parse_header) - if (!stbi__parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - do { - final = stbi__zreceive(a,1); - type = stbi__zreceive(a,2); - if (type == 0) { - if (!stbi__parse_uncomperssed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - // use fixed code lengths - if (!stbi__zdefault_distance[31]) stbi__init_zdefaults(); - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; - } else { - if (!stbi__compute_huffman_codes(a)) return 0; - } - if (!stbi__parse_huffman_block(a)) return 0; - } - } while (!final); - return 1; -} - -static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) -{ - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - - return stbi__parse_zlib(a, parse_header); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - free(a.zout_start); - return NULL; - } -} - -STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) -{ - return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - free(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) - return (int) (a.zout - a.zout_start); - else - return -1; -} - -STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(16384); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer+len; - if (stbi__do_zlib(&a, p, 16384, 1, 0)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - free(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) - return (int) (a.zout - a.zout_start); - else - return -1; -} - -// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 -// simple implementation -// - only 8-bit samples -// - no CRC checking -// - allocates lots of intermediate memory -// - avoids problem of streaming data between subsystems -// - avoids explicit window management -// performance -// - uses stb_zlib, a PD zlib implementation with fast huffman decoding - - -typedef struct -{ - stbi__uint32 length; - stbi__uint32 type; -} stbi__pngchunk; - -#define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) - -static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) -{ - stbi__pngchunk c; - c.length = stbi__get32be(s); - c.type = stbi__get32be(s); - return c; -} - -static int stbi__check_png_header(stbi__context *s) -{ - static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; - int i; - for (i=0; i < 8; ++i) - if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); - return 1; -} - -typedef struct -{ - stbi__context *s; - stbi_uc *idata, *expanded, *out; -} stbi__png; - - -enum { - STBI__F_none=0, STBI__F_sub=1, STBI__F_up=2, STBI__F_avg=3, STBI__F_paeth=4, - STBI__F_avg_first, STBI__F_paeth_first -}; - -static stbi_uc first_row_filter[5] = -{ - STBI__F_none, STBI__F_sub, STBI__F_none, STBI__F_avg_first, STBI__F_paeth_first -}; - -static int stbi__paeth(int a, int b, int c) -{ - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; -} - -#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - -// create the png data from post-deflated data -static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) -{ - stbi__context *s = a->s; - stbi__uint32 i,j,stride = x*out_n; - stbi__uint32 img_len; - int k; - int img_n = s->img_n; // copy it into a local for later - stbi_uc* line8 = NULL; // point into raw when depth==8 else temporary local buffer - - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc(x * y * out_n); - if (!a->out) return stbi__err("outofmem", "Out of memory"); - - img_len = ((((img_n * x * depth) + 7) >> 3) + 1) * y; - if (s->img_x == x && s->img_y == y) { - if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG"); - } else { // interlaced: - if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); - } - - if (depth != 8) { - line8 = (stbi_uc *) stbi__malloc((x+7) * out_n); // allocate buffer for one scanline - if (!line8) return stbi__err("outofmem", "Out of memory"); - } - - for (j=0; j < y; ++j) { - stbi_uc *in; - stbi_uc *cur = a->out + stride*j; - stbi_uc *prior = cur - stride; - int filter = *raw++; - if (filter > 4) { - if (depth != 8) free(line8); - return stbi__err("invalid filter","Corrupt PNG"); - } - - if (depth == 8) { - in = raw; - raw += x*img_n; - } - else { - // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit - // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop - in = line8; - stbi_uc* decode_out = line8; - stbi_uc scale = (color == 0) ? 0xFF/((1<= 1; k-=2, raw++) { - *decode_out++ = scale * ((*raw >> 4) ); - *decode_out++ = scale * ((*raw ) & 0x0f); - } - } else if (depth == 2) { - for (k=x*img_n; k >= 1; k-=4, raw++) { - *decode_out++ = scale * ((*raw >> 6) ); - *decode_out++ = scale * ((*raw >> 4) & 0x03); - *decode_out++ = scale * ((*raw >> 2) & 0x03); - *decode_out++ = scale * ((*raw ) & 0x03); - } - } else if (depth == 1) { - for (k=x*img_n; k >= 1; k-=8, raw++) { - *decode_out++ = scale * ((*raw >> 7) ); - *decode_out++ = scale * ((*raw >> 6) & 0x01); - *decode_out++ = scale * ((*raw >> 5) & 0x01); - *decode_out++ = scale * ((*raw >> 4) & 0x01); - *decode_out++ = scale * ((*raw >> 3) & 0x01); - *decode_out++ = scale * ((*raw >> 2) & 0x01); - *decode_out++ = scale * ((*raw >> 1) & 0x01); - *decode_out++ = scale * ((*raw ) & 0x01); - } - } - } - - // if first row, use special filter that doesn't sample previous row - if (j == 0) filter = first_row_filter[filter]; - - // handle first pixel explicitly - for (k=0; k < img_n; ++k) { - switch (filter) { - case STBI__F_none : cur[k] = in[k]; break; - case STBI__F_sub : cur[k] = in[k]; break; - case STBI__F_up : cur[k] = STBI__BYTECAST(in[k] + prior[k]); break; - case STBI__F_avg : cur[k] = STBI__BYTECAST(in[k] + (prior[k]>>1)); break; - case STBI__F_paeth : cur[k] = STBI__BYTECAST(in[k] + stbi__paeth(0,prior[k],0)); break; - case STBI__F_avg_first : cur[k] = in[k]; break; - case STBI__F_paeth_first: cur[k] = in[k]; break; - } - } - if (img_n != out_n) cur[img_n] = 255; - in += img_n; - cur += out_n; - prior += out_n; - // this is a little gross, so that we don't switch per-pixel or per-component - if (img_n == out_n) { - #define CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, in+=img_n,cur+=img_n,prior+=img_n) \ - for (k=0; k < img_n; ++k) - switch (filter) { - CASE(STBI__F_none) cur[k] = in[k]; break; - CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(in[k] + cur[k-img_n]); break; - CASE(STBI__F_up) cur[k] = STBI__BYTECAST(in[k] + prior[k]); break; - CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(in[k] + ((prior[k] + cur[k-img_n])>>1)); break; - CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(in[k] + stbi__paeth(cur[k-img_n],prior[k],prior[k-img_n])); break; - CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(in[k] + (cur[k-img_n] >> 1)); break; - CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(in[k] + stbi__paeth(cur[k-img_n],0,0)); break; - } - #undef CASE - } else { - STBI_ASSERT(img_n+1 == out_n); - #define CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[img_n]=255,in+=img_n,cur+=out_n,prior+=out_n) \ - for (k=0; k < img_n; ++k) - switch (filter) { - CASE(STBI__F_none) cur[k] = in[k]; break; - CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(in[k] + cur[k-out_n]); break; - CASE(STBI__F_up) cur[k] = STBI__BYTECAST(in[k] + prior[k]); break; - CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(in[k] + ((prior[k] + cur[k-out_n])>>1)); break; - CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(in[k] + stbi__paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; - CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(in[k] + (cur[k-out_n] >> 1)); break; - CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(in[k] + stbi__paeth(cur[k-out_n],0,0)); break; - } - #undef CASE - } - } - - if (depth != 8) free(line8); - return 1; -} - -static int stbi__create_png_image(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, int depth, int color, int interlaced) -{ - stbi_uc *final; - int p; - if (!interlaced) - return stbi__create_png_image_raw(a, raw, raw_len, out_n, a->s->img_x, a->s->img_y, depth, color); - - // de-interlacing - final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n); - for (p=0; p < 7; ++p) { - int xorig[] = { 0,4,0,2,0,1,0 }; - int yorig[] = { 0,0,4,0,2,0,1 }; - int xspc[] = { 8,8,4,4,2,2,1 }; - int yspc[] = { 8,8,8,4,4,2,2 }; - int i,j,x,y; - // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 - x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; - if (x && y) { - stbi__uint32 img_len = ((((out_n * x * depth) + 7) >> 3) + 1) * y; - if (!stbi__create_png_image_raw(a, raw, raw_len, out_n, x, y, depth, color)) { - free(final); - return 0; - } - for (j=0; j < y; ++j) - for (i=0; i < x; ++i) - memcpy(final + (j*yspc[p]+yorig[p])*a->s->img_x*out_n + (i*xspc[p]+xorig[p])*out_n, - a->out + (j*x+i)*out_n, out_n); - free(a->out); - raw += img_len; - raw_len -= img_len; - } - } - a->out = final; - - return 1; -} - -static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - // compute color-based transparency, assuming we've - // already got 255 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i=0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i=0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) -{ - stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - stbi_uc *p, *temp_out, *orig = a->out; - - p = (stbi_uc *) stbi__malloc(pixel_count * pal_img_n); - if (p == NULL) return stbi__err("outofmem", "Out of memory"); - - // between here and free(out) below, exitting would leak - temp_out = p; - - if (pal_img_n == 3) { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p += 3; - } - } else { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p[3] = palette[n+3]; - p += 4; - } - } - free(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -static int stbi__unpremultiply_on_load = 0; -static int stbi__de_iphone_flag = 0; - -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; -} - -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag = flag_true_if_should_convert; -} - -static void stbi__de_iphone(stbi__png *z) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - if (s->img_out_n == 3) { // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 3; - } - } else { - STBI_ASSERT(s->img_out_n == 4); - if (stbi__unpremultiply_on_load) { - // convert bgr to rgb and unpremultiply - for (i=0; i < pixel_count; ++i) { - stbi_uc a = p[3]; - stbi_uc t = p[0]; - if (a) { - p[0] = p[2] * 255 / a; - p[1] = p[1] * 255 / a; - p[2] = t * 255 / a; - } else { - p[0] = p[2]; - p[2] = t; - } - p += 4; - } - } else { - // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 4; - } - } - } -} - -static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) -{ - stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]; - stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, color=0, depth=0, is_iphone=0; - stbi__context *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!stbi__check_png_header(s)) return 0; - - if (scan == SCAN_type) return 1; - - for (;;) { - stbi__pngchunk c = stbi__get_chunk_header(s); - switch (c.type) { - case PNG_TYPE('C','g','B','I'): - is_iphone = 1; - stbi__skip(s, c.length); - break; - case PNG_TYPE('I','H','D','R'): { - int comp,filter; - if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); - first = 0; - if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - depth = stbi__get8(s); if (depth != 1 && depth != 2 && depth != 4 && depth != 8) return stbi__err("1/2/4/8-bit only","PNG not supported: 1/2/4/8-bit only"); - color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); - comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); - filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); - interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); - if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == SCAN_header) return 1; - } else { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS - } - break; - } - - case PNG_TYPE('P','L','T','E'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); - for (i=0; i < pal_len; ++i) { - palette[i*4+0] = stbi__get8(s); - palette[i*4+1] = stbi__get8(s); - palette[i*4+2] = stbi__get8(s); - palette[i*4+3] = 255; - } - break; - } - - case PNG_TYPE('t','R','N','S'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); - if (pal_img_n) { - if (scan == SCAN_header) { s->img_n = 4; return 1; } - if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); - if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); - pal_img_n = 4; - for (i=0; i < c.length; ++i) - palette[i*4+3] = stbi__get8(s); - } else { - if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); - if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); - has_trans = 1; - for (k=0; k < s->img_n; ++k) - tc[k] = (stbi_uc) (stbi__get16be(s) & 255); // non 8-bit images will be larger - } - break; - } - - case PNG_TYPE('I','D','A','T'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == SCAN_header) { s->img_n = pal_img_n; return 1; } - if (ioff + c.length > idata_limit) { - stbi_uc *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - p = (stbi_uc *) realloc(z->idata, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); - ioff += c.length; - break; - } - - case PNG_TYPE('I','E','N','D'): { - stbi__uint32 raw_len; - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (scan != SCAN_load) return 1; - if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); - z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, 16384, (int *) &raw_len, !is_iphone); - if (z->expanded == NULL) return 0; // zlib should set error - free(z->idata); z->idata = NULL; - if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n+1; - else - s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, depth, color, interlace)) return 0; - if (has_trans) - if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; - if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) - stbi__de_iphone(z); - if (pal_img_n) { - // pal_img_n == 3 or 4 - s->img_n = pal_img_n; // record the actual colors we had - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } - free(z->expanded); z->expanded = NULL; - return 1; - } - - default: - // if critical, fail - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - #ifndef STBI_NO_FAILURE_STRINGS - // not threadsafe - static char invalid_chunk[] = "XXXX PNG chunk not known"; - invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); - invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); - invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); - invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); - #endif - return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); - } - stbi__skip(s, c.length); - break; - } - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - } -} - -static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp) -{ - unsigned char *result=NULL; - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - if (stbi__parse_png_file(p, SCAN_load, req_comp)) { - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - result = stbi__convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_out_n; - } - free(p->out); p->out = NULL; - free(p->expanded); p->expanded = NULL; - free(p->idata); p->idata = NULL; - - return result; -} - -static unsigned char *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__png p; - p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp); -} - -static int stbi__png_test(stbi__context *s) -{ - int r; - r = stbi__check_png_header(s); - stbi__rewind(s); - return r; -} - -static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) -{ - if (!stbi__parse_png_file(p, SCAN_header, 0)) { - stbi__rewind( p->s ); - return 0; - } - if (x) *x = p->s->img_x; - if (y) *y = p->s->img_y; - if (comp) *comp = p->s->img_n; - return 1; -} - -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__png p; - p.s = s; - return stbi__png_info_raw(&p, x, y, comp); -} - -// Microsoft/Windows BMP image -static int stbi__bmp_test_raw(stbi__context *s) -{ - int r; - int sz; - if (stbi__get8(s) != 'B') return 0; - if (stbi__get8(s) != 'M') return 0; - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - stbi__get32le(s); // discard data offset - sz = stbi__get32le(s); - r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); - return r; -} - -static int stbi__bmp_test(stbi__context *s) -{ - int r = stbi__bmp_test_raw(s); - stbi__rewind(s); - return r; -} - - -// returns 0..31 for the highest set bit -static int stbi__high_bit(unsigned int z) -{ - int n=0; - if (z == 0) return -1; - if (z >= 0x10000) n += 16, z >>= 16; - if (z >= 0x00100) n += 8, z >>= 8; - if (z >= 0x00010) n += 4, z >>= 4; - if (z >= 0x00004) n += 2, z >>= 2; - if (z >= 0x00002) n += 1, z >>= 1; - return n; -} - -static int stbi__bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; -} - -static int stbi__shiftsigned(int v, int shift, int bits) -{ - int result; - int z=0; - - if (shift < 0) v <<= -shift; - else v >>= shift; - result = v; - - z = bits; - while (z < 8) { - result += v >> z; - z += bits; - } - return result; -} - -static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi_uc *out; - unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0; - stbi_uc pal[256][4]; - int psize=0,i,j,compress=0,width; - int bpp, flip_vertically, pad, target, offset, hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - offset = stbi__get32le(s); - hsz = stbi__get32le(s); - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); - if (hsz == 12) { - s->img_x = stbi__get16le(s); - s->img_y = stbi__get16le(s); - } else { - s->img_x = stbi__get32le(s); - s->img_y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); - bpp = stbi__get16le(s); - if (bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); - flip_vertically = ((int) s->img_y) > 0; - s->img_y = abs((int) s->img_y); - if (hsz == 12) { - if (bpp < 24) - psize = (offset - 14 - 24) / 3; - } else { - compress = stbi__get32le(s); - if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); - stbi__get32le(s); // discard sizeof - stbi__get32le(s); // discard hres - stbi__get32le(s); // discard vres - stbi__get32le(s); // discard colorsused - stbi__get32le(s); // discard max important - if (hsz == 40 || hsz == 56) { - if (hsz == 56) { - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - } - if (bpp == 16 || bpp == 32) { - mr = mg = mb = 0; - if (compress == 0) { - if (bpp == 32) { - mr = 0xffu << 16; - mg = 0xffu << 8; - mb = 0xffu << 0; - ma = 0xffu << 24; - fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255 - STBI_NOTUSED(fake_a); - } else { - mr = 31u << 10; - mg = 31u << 5; - mb = 31u << 0; - } - } else if (compress == 3) { - mr = stbi__get32le(s); - mg = stbi__get32le(s); - mb = stbi__get32le(s); - // not documented, but generated by photoshop and handled by mspaint - if (mr == mg && mg == mb) { - // ?!?!? - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else { - STBI_ASSERT(hsz == 108 || hsz == 124); - mr = stbi__get32le(s); - mg = stbi__get32le(s); - mb = stbi__get32le(s); - ma = stbi__get32le(s); - stbi__get32le(s); // discard color space - for (i=0; i < 12; ++i) - stbi__get32le(s); // discard color space parameters - if (hsz == 124) { - stbi__get32le(s); // discard rendering intent - stbi__get32le(s); // discard offset of profile data - stbi__get32le(s); // discard size of profile data - stbi__get32le(s); // discard reserved - } - } - if (bpp < 16) - psize = (offset - 14 - hsz) >> 2; - } - s->img_n = ma ? 4 : 3; - if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; - else - target = s->img_n; // if they want monochrome, we'll post-convert - out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - if (bpp < 16) { - int z=0; - if (psize == 0 || psize > 256) { free(out); return stbi__errpuc("invalid", "Corrupt BMP"); } - for (i=0; i < psize; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - if (hsz != 12) stbi__get8(s); - pal[i][3] = 255; - } - stbi__skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); - if (bpp == 4) width = (s->img_x + 1) >> 1; - else if (bpp == 8) width = s->img_x; - else { free(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } - pad = (-width)&3; - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - } - stbi__skip(s, pad); - } - } else { - int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; - int z = 0; - int easy=0; - stbi__skip(s, offset - 14 - hsz); - if (bpp == 24) width = 3 * s->img_x; - else if (bpp == 16) width = 2*s->img_x; - else /* bpp = 32 and pad = 0 */ width=0; - pad = (-width) & 3; - if (bpp == 24) { - easy = 1; - } else if (bpp == 32) { - if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) - easy = 2; - } - if (!easy) { - if (!mr || !mg || !mb) { free(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - // right shift amt to put high bit in position #7 - rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); - gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); - bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); - ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); - } - for (j=0; j < (int) s->img_y; ++j) { - if (easy) { - for (i=0; i < (int) s->img_x; ++i) { - unsigned char a; - out[z+2] = stbi__get8(s); - out[z+1] = stbi__get8(s); - out[z+0] = stbi__get8(s); - z += 3; - a = (easy == 2 ? stbi__get8(s) : 255); - if (target == 4) out[z++] = a; - } - } else { - for (i=0; i < (int) s->img_x; ++i) { - stbi__uint32 v = (stbi__uint32) (bpp == 16 ? stbi__get16le(s) : stbi__get32le(s)); - int a; - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); - a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); - if (target == 4) out[z++] = STBI__BYTECAST(a); - } - } - stbi__skip(s, pad); - } - } - if (flip_vertically) { - stbi_uc t; - for (j=0; j < (int) s->img_y>>1; ++j) { - stbi_uc *p1 = out + j *s->img_x*target; - stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; - for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i], p1[i] = p2[i], p2[i] = t; - } - } - } - - if (req_comp && req_comp != target) { - out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - return out; -} - -// Targa Truevision - TGA -// by Jonathan Dummer - -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) -{ - int tga_w, tga_h, tga_comp; - int sz; - stbi__get8(s); // discard Offset - sz = stbi__get8(s); // color type - if( sz > 1 ) { - stbi__rewind(s); - return 0; // only RGB or indexed allowed - } - sz = stbi__get8(s); // image type - // only RGB or grey allowed, +/- RLE - if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0; - stbi__skip(s,9); - tga_w = stbi__get16le(s); - if( tga_w < 1 ) { - stbi__rewind(s); - return 0; // test width - } - tga_h = stbi__get16le(s); - if( tga_h < 1 ) { - stbi__rewind(s); - return 0; // test height - } - sz = stbi__get8(s); // bits per pixel - // only RGB or RGBA or grey allowed - if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) { - stbi__rewind(s); - return 0; - } - tga_comp = sz; - if (x) *x = tga_w; - if (y) *y = tga_h; - if (comp) *comp = tga_comp / 8; - return 1; // seems to have passed everything -} - -static int stbi__tga_test(stbi__context *s) -{ - int res; - int sz; - stbi__get8(s); // discard Offset - sz = stbi__get8(s); // color type - if ( sz > 1 ) return 0; // only RGB or indexed allowed - sz = stbi__get8(s); // image type - if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE - stbi__get16be(s); // discard palette start - stbi__get16be(s); // discard palette length - stbi__get8(s); // discard bits per palette color entry - stbi__get16be(s); // discard x origin - stbi__get16be(s); // discard y origin - if ( stbi__get16be(s) < 1 ) return 0; // test width - if ( stbi__get16be(s) < 1 ) return 0; // test height - sz = stbi__get8(s); // bits per pixel - if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) - res = 0; - else - res = 1; - stbi__rewind(s); - return res; -} - -static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - // read in the TGA header stuff - int tga_offset = stbi__get8(s); - int tga_indexed = stbi__get8(s); - int tga_image_type = stbi__get8(s); - int tga_is_RLE = 0; - int tga_palette_start = stbi__get16le(s); - int tga_palette_len = stbi__get16le(s); - int tga_palette_bits = stbi__get8(s); - int tga_x_origin = stbi__get16le(s); - int tga_y_origin = stbi__get16le(s); - int tga_width = stbi__get16le(s); - int tga_height = stbi__get16le(s); - int tga_bits_per_pixel = stbi__get8(s); - int tga_comp = tga_bits_per_pixel / 8; - int tga_inverted = stbi__get8(s); - // image data - unsigned char *tga_data; - unsigned char *tga_palette = NULL; - int i, j; - unsigned char raw_data[4]; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - - // do a tiny bit of precessing - if ( tga_image_type >= 8 ) - { - tga_image_type -= 8; - tga_is_RLE = 1; - } - /* int tga_alpha_bits = tga_inverted & 15; */ - tga_inverted = 1 - ((tga_inverted >> 5) & 1); - - // error check - if ( //(tga_indexed) || - (tga_width < 1) || (tga_height < 1) || - (tga_image_type < 1) || (tga_image_type > 3) || - ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && - (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) - ) - { - return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA - } - - // If I'm paletted, then I'll use the number of bits from the palette - if ( tga_indexed ) - { - tga_comp = tga_palette_bits / 8; - } - - // tga info - *x = tga_width; - *y = tga_height; - if (comp) *comp = tga_comp; - - tga_data = (unsigned char*)stbi__malloc( tga_width * tga_height * tga_comp ); - if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); - - // skip to the data's starting position (offset usually = 0) - stbi__skip(s, tga_offset ); - - if ( !tga_indexed && !tga_is_RLE) { - for (i=0; i < tga_height; ++i) { - int y = tga_inverted ? tga_height -i - 1 : i; - stbi_uc *tga_row = tga_data + y*tga_width*tga_comp; - stbi__getn(s, tga_row, tga_width * tga_comp); - } - } else { - // do I need to load a palette? - if ( tga_indexed) - { - // any data to skip? (offset usually = 0) - stbi__skip(s, tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_palette_bits / 8 ); - if (!tga_palette) { - free(tga_data); - return stbi__errpuc("outofmem", "Out of memory"); - } - if (!stbi__getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) { - free(tga_data); - free(tga_palette); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - } - // load the data - for (i=0; i < tga_width * tga_height; ++i) - { - // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? - if ( tga_is_RLE ) - { - if ( RLE_count == 0 ) - { - // yep, get the next byte as a RLE command - int RLE_cmd = stbi__get8(s); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } else if ( !RLE_repeating ) - { - read_next_pixel = 1; - } - } else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if ( read_next_pixel ) - { - // load however much data we did have - if ( tga_indexed ) - { - // read in 1 byte, then perform the lookup - int pal_idx = stbi__get8(s); - if ( pal_idx >= tga_palette_len ) - { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_bits_per_pixel / 8; - for (j = 0; j*8 < tga_bits_per_pixel; ++j) - { - raw_data[j] = tga_palette[pal_idx+j]; - } - } else - { - // read in the data raw - for (j = 0; j*8 < tga_bits_per_pixel; ++j) - { - raw_data[j] = stbi__get8(s); - } - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel - - // copy data - for (j = 0; j < tga_comp; ++j) - tga_data[i*tga_comp+j] = raw_data[j]; - - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if ( tga_inverted ) - { - for (j = 0; j*2 < tga_height; ++j) - { - int index1 = j * tga_width * tga_comp; - int index2 = (tga_height - 1 - j) * tga_width * tga_comp; - for (i = tga_width * tga_comp; i > 0; --i) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if ( tga_palette != NULL ) - { - free( tga_palette ); - } - } - - // swap RGB - if (tga_comp >= 3) - { - unsigned char* tga_pixel = tga_data; - for (i=0; i < tga_width * tga_height; ++i) - { - unsigned char temp = tga_pixel[0]; - tga_pixel[0] = tga_pixel[2]; - tga_pixel[2] = temp; - tga_pixel += tga_comp; - } - } - - // convert to target component count - if (req_comp && req_comp != tga_comp) - tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); - - // the things I do to get rid of an error message, and yet keep - // Microsoft's C compilers happy... [8^( - tga_palette_start = tga_palette_len = tga_palette_bits = - tga_x_origin = tga_y_origin = 0; - // OK, done - return tga_data; -} - -// ************************************************************************************************* -// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB - -static int stbi__psd_test(stbi__context *s) -{ - int r = (stbi__get32be(s) == 0x38425053); - stbi__rewind(s); - return r; -} - -static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - int pixelCount; - int channelCount, compression; - int channel, i, count, len; - int w,h; - stbi_uc *out; - - // Check identifier - if (stbi__get32be(s) != 0x38425053) // "8BPS" - return stbi__errpuc("not PSD", "Corrupt PSD image"); - - // Check file type version. - if (stbi__get16be(s) != 1) - return stbi__errpuc("wrong version", "Unsupported version of PSD image"); - - // Skip 6 reserved bytes. - stbi__skip(s, 6 ); - - // Read the number of channels (R, G, B, A, etc). - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) - return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); - - // Read the rows and columns of the image. - h = stbi__get32be(s); - w = stbi__get32be(s); - - // Make sure the depth is 8 bits. - if (stbi__get16be(s) != 8) - return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 bit"); - - // Make sure the color mode is RGB. - // Valid options are: - // 0: Bitmap - // 1: Grayscale - // 2: Indexed color - // 3: RGB color - // 4: CMYK color - // 7: Multichannel - // 8: Duotone - // 9: Lab color - if (stbi__get16be(s) != 3) - return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); - - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) - stbi__skip(s,stbi__get32be(s) ); - - // Skip the image resources. (resolution, pen tool paths, etc) - stbi__skip(s, stbi__get32be(s) ); - - // Skip the reserved data. - stbi__skip(s, stbi__get32be(s) ); - - // Find out if the data is compressed. - // Known values: - // 0: no compression - // 1: RLE compressed - compression = stbi__get16be(s); - if (compression > 1) - return stbi__errpuc("bad compression", "PSD has an unknown compression format"); - - // Create the destination image. - out = (stbi_uc *) stbi__malloc(4 * w*h); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - pixelCount = w*h; - - // Initialize the data to zero. - //memset( out, 0, pixelCount * 4 ); - - // Finally, the image data. - if (compression) { - // RLE as used by .PSD and .TIFF - // Loop until you get the number of unpacked bytes you are expecting: - // Read the next source byte into n. - // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. - // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. - // Else if n is 128, noop. - // Endloop - - // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, - // which we're going to just skip. - stbi__skip(s, h * channelCount * 2 ); - - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out+channel; - if (channel >= channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; - } else { - // Read the RLE data. - count = 0; - while (count < pixelCount) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len ^= 0x0FF; - len += 2; - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } - } - } - - } else { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit value for each pixel in the image. - - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out + channel; - if (channel > channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; - } else { - // Read the data. - for (i = 0; i < pixelCount; i++) - *p = stbi__get8(s), p += 4; - } - } - } - - if (req_comp && req_comp != 4) { - out = stbi__convert_format(out, 4, req_comp, w, h); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - if (comp) *comp = channelCount; - *y = h; - *x = w; - - return out; -} - -// ************************************************************************************************* -// Softimage PIC loader -// by Tom Seddon -// -// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format -// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ - -static int stbi__pic_is4(stbi__context *s,const char *str) -{ - int i; - for (i=0; i<4; ++i) - if (stbi__get8(s) != (stbi_uc)str[i]) - return 0; - - return 1; -} - -static int stbi__pic_test_core(stbi__context *s) -{ - int i; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) - return 0; - - for(i=0;i<84;++i) - stbi__get8(s); - - if (!stbi__pic_is4(s,"PICT")) - return 0; - - return 1; -} - -typedef struct -{ - stbi_uc size,type,channel; -} stbi__pic_packet; - -static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) -{ - int mask=0x80, i; - - for (i=0; i<4; ++i, mask>>=1) { - if (channel & mask) { - if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); - dest[i]=stbi__get8(s); - } - } - - return dest; -} - -static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) -{ - int mask=0x80,i; - - for (i=0;i<4; ++i, mask>>=1) - if (channel&mask) - dest[i]=src[i]; -} - -static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) -{ - int act_comp=0,num_packets=0,y,chained; - stbi__pic_packet packets[10]; - - // this will (should...) cater for even some bizarre stuff like having data - // for the same channel in multiple packets. - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return stbi__errpuc("bad format","too many packets"); - - packet = &packets[num_packets++]; - - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - - act_comp |= packet->channel; - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); - if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? - - for(y=0; ytype) { - default: - return stbi__errpuc("bad format","packet has bad compression type"); - - case 0: {//uncompressed - int x; - - for(x=0;xchannel,dest)) - return 0; - break; - } - - case 1://Pure RLE - { - int left=width, i; - - while (left>0) { - stbi_uc count,value[4]; - - count=stbi__get8(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); - - if (count > left) - count = (stbi_uc) left; - - if (!stbi__readval(s,packet->channel,value)) return 0; - - for(i=0; ichannel,dest,value); - left -= count; - } - } - break; - - case 2: {//Mixed RLE - int left=width; - while (left>0) { - int count = stbi__get8(s), i; - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); - - if (count >= 128) { // Repeated - stbi_uc value[4]; - int i; - - if (count==128) - count = stbi__get16be(s); - else - count -= 127; - if (count > left) - return stbi__errpuc("bad file","scanline overrun"); - - if (!stbi__readval(s,packet->channel,value)) - return 0; - - for(i=0;ichannel,dest,value); - } else { // Raw - ++count; - if (count>left) return stbi__errpuc("bad file","scanline overrun"); - - for(i=0;ichannel,dest)) - return 0; - } - left-=count; - } - break; - } - } - } - } - - return result; -} - -static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp) -{ - stbi_uc *result; - int i, x,y; - - for (i=0; i<92; ++i) - stbi__get8(s); - - x = stbi__get16be(s); - y = stbi__get16be(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if ((1 << 28) / x < y) return stbi__errpuc("too large", "Image too large to decode"); - - stbi__get32be(s); //skip `ratio' - stbi__get16be(s); //skip `fields' - stbi__get16be(s); //skip `pad' - - // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc(x*y*4); - memset(result, 0xff, x*y*4); - - if (!stbi__pic_load_core(s,x,y,comp, result)) { - free(result); - result=0; - } - *px = x; - *py = y; - if (req_comp == 0) req_comp = *comp; - result=stbi__convert_format(result,4,req_comp,x,y); - - return result; -} - -static int stbi__pic_test(stbi__context *s) -{ - int r = stbi__pic_test_core(s); - stbi__rewind(s); - return r; -} - -// ************************************************************************************************* -// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb -typedef struct -{ - stbi__int16 prefix; - stbi_uc first; - stbi_uc suffix; -} stbi__gif_lzw; - -typedef struct -{ - int w,h; - stbi_uc *out; // output buffer (always 4 components) - int flags, bgindex, ratio, transparent, eflags; - stbi_uc pal[256][4]; - stbi_uc lpal[256][4]; - stbi__gif_lzw codes[4096]; - stbi_uc *color_table; - int parse, step; - int lflags; - int start_x, start_y; - int max_x, max_y; - int cur_x, cur_y; - int line_size; -} stbi__gif; - -static int stbi__gif_test_raw(stbi__context *s) -{ - int sz; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; - sz = stbi__get8(s); - if (sz != '9' && sz != '7') return 0; - if (stbi__get8(s) != 'a') return 0; - return 1; -} - -static int stbi__gif_test(stbi__context *s) -{ - int r = stbi__gif_test_raw(s); - stbi__rewind(s); - return r; -} - -static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) -{ - int i; - for (i=0; i < num_entries; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - pal[i][3] = transp ? 0 : 255; - } -} - -static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) -{ - stbi_uc version; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') - return stbi__err("not GIF", "Corrupt GIF"); - - version = stbi__get8(s); - if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); - if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); - - stbi__g_failure_reason = ""; - g->w = stbi__get16le(s); - g->h = stbi__get16le(s); - g->flags = stbi__get8(s); - g->bgindex = stbi__get8(s); - g->ratio = stbi__get8(s); - g->transparent = -1; - - if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments - - if (is_info) return 1; - - if (g->flags & 0x80) - stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); - - return 1; -} - -static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__gif g; - if (!stbi__gif_header(s, &g, comp, 1)) { - stbi__rewind( s ); - return 0; - } - if (x) *x = g.w; - if (y) *y = g.h; - return 1; -} - -static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) -{ - stbi_uc *p, *c; - - // recurse to decode the prefixes, since the linked-list is backwards, - // and working backwards through an interleaved image would be nasty - if (g->codes[code].prefix >= 0) - stbi__out_gif_code(g, g->codes[code].prefix); - - if (g->cur_y >= g->max_y) return; - - p = &g->out[g->cur_x + g->cur_y]; - c = &g->color_table[g->codes[code].suffix * 4]; - - if (c[3] >= 128) { - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } - g->cur_x += 4; - - if (g->cur_x >= g->max_x) { - g->cur_x = g->start_x; - g->cur_y += g->step; - - while (g->cur_y >= g->max_y && g->parse > 0) { - g->step = (1 << g->parse) * g->line_size; - g->cur_y = g->start_y + (g->step >> 1); - --g->parse; - } - } -} - -static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) -{ - stbi_uc lzw_cs; - stbi__int32 len, code; - stbi__uint32 first; - stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; - stbi__gif_lzw *p; - - lzw_cs = stbi__get8(s); - clear = 1 << lzw_cs; - first = 1; - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - bits = 0; - valid_bits = 0; - for (code = 0; code < clear; code++) { - g->codes[code].prefix = -1; - g->codes[code].first = (stbi_uc) code; - g->codes[code].suffix = (stbi_uc) code; - } - - // support no starting clear code - avail = clear+2; - oldcode = -1; - - len = 0; - for(;;) { - if (valid_bits < codesize) { - if (len == 0) { - len = stbi__get8(s); // start new block - if (len == 0) - return g->out; - } - --len; - bits |= (stbi__int32) stbi__get8(s) << valid_bits; - valid_bits += 8; - } else { - stbi__int32 code = bits & codemask; - bits >>= codesize; - valid_bits -= codesize; - // @OPTIMIZE: is there some way we can accelerate the non-clear path? - if (code == clear) { // clear code - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - avail = clear + 2; - oldcode = -1; - first = 0; - } else if (code == clear + 1) { // end of stream code - stbi__skip(s, len); - while ((len = stbi__get8(s)) > 0) - stbi__skip(s,len); - return g->out; - } else if (code <= avail) { - if (first) return stbi__errpuc("no clear code", "Corrupt GIF"); - - if (oldcode >= 0) { - p = &g->codes[avail++]; - if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF"); - p->prefix = (stbi__int16) oldcode; - p->first = g->codes[oldcode].first; - p->suffix = (code == avail) ? p->first : g->codes[code].first; - } else if (code == avail) - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - - stbi__out_gif_code(g, (stbi__uint16) code); - - if ((avail & codemask) == 0 && avail <= 0x0FFF) { - codesize++; - codemask = (1 << codesize) - 1; - } - - oldcode = code; - } else { - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - } - } - } -} - -static void stbi__fill_gif_background(stbi__gif *g) -{ - int i; - stbi_uc *c = g->pal[g->bgindex]; - // @OPTIMIZE: write a dword at a time - for (i = 0; i < g->w * g->h * 4; i += 4) { - stbi_uc *p = &g->out[i]; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } -} - -// this function is designed to support animated gifs, although stb_image doesn't support it -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) -{ - int i; - stbi_uc *old_out = 0; - - if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); - stbi__fill_gif_background(g); - } else { - // animated-gif-only path - if (((g->eflags & 0x1C) >> 2) == 3) { - old_out = g->out; - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); - memcpy(g->out, old_out, g->w*g->h*4); - } - } - - for (;;) { - switch (stbi__get8(s)) { - case 0x2C: /* Image Descriptor */ - { - stbi__int32 x, y, w, h; - stbi_uc *o; - - x = stbi__get16le(s); - y = stbi__get16le(s); - w = stbi__get16le(s); - h = stbi__get16le(s); - if (((x + w) > (g->w)) || ((y + h) > (g->h))) - return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); - - g->line_size = g->w * 4; - g->start_x = x * 4; - g->start_y = y * g->line_size; - g->max_x = g->start_x + w * 4; - g->max_y = g->start_y + h * g->line_size; - g->cur_x = g->start_x; - g->cur_y = g->start_y; - - g->lflags = stbi__get8(s); - - if (g->lflags & 0x40) { - g->step = 8 * g->line_size; // first interlaced spacing - g->parse = 3; - } else { - g->step = g->line_size; - g->parse = 0; - } - - if (g->lflags & 0x80) { - stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); - g->color_table = (stbi_uc *) g->lpal; - } else if (g->flags & 0x80) { - for (i=0; i < 256; ++i) // @OPTIMIZE: stbi__jpeg_reset only the previous transparent - g->pal[i][3] = 255; - if (g->transparent >= 0 && (g->eflags & 0x01)) - g->pal[g->transparent][3] = 0; - g->color_table = (stbi_uc *) g->pal; - } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - - o = stbi__process_gif_raster(s, g); - if (o == NULL) return NULL; - - if (req_comp && req_comp != 4) - o = stbi__convert_format(o, 4, req_comp, g->w, g->h); - return o; - } - - case 0x21: // Comment Extension. - { - int len; - if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. - len = stbi__get8(s); - if (len == 4) { - g->eflags = stbi__get8(s); - stbi__get16le(s); // delay - g->transparent = stbi__get8(s); - } else { - stbi__skip(s, len); - break; - } - } - while ((len = stbi__get8(s)) != 0) - stbi__skip(s, len); - break; - } - - case 0x3B: // gif stream termination code - return (stbi_uc *) s; // using '1' causes warning on some compilers - - default: - return stbi__errpuc("unknown code", "Corrupt GIF"); - } - } -} - -static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi_uc *u = 0; - stbi__gif g; - memset(&g, 0, sizeof(g)); - - u = stbi__gif_load_next(s, &g, comp, req_comp); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - if (u) { - *x = g.w; - *y = g.h; - } - - return u; -} - -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) -{ - return stbi__gif_info_raw(s,x,y,comp); -} - - -// ************************************************************************************************* -// Radiance RGBE HDR loader -// originally by Nicolas Schulz -#ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s) -{ - const char *signature = "#?RADIANCE\n"; - int i; - for (i=0; signature[i]; ++i) - if (stbi__get8(s) != signature[i]) - return 0; - return 1; -} - -static int stbi__hdr_test(stbi__context* s) -{ - int r = stbi__hdr_test_core(s); - stbi__rewind(s); - return r; -} - -#define STBI__HDR_BUFLEN 1024 -static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) -{ - int len=0; - char c = '\0'; - - c = (char) stbi__get8(z); - - while (!stbi__at_eof(z) && c != '\n') { - buffer[len++] = c; - if (len == STBI__HDR_BUFLEN-1) { - // flush to end of line - while (!stbi__at_eof(z) && stbi__get8(z) != '\n') - ; - break; - } - c = (char) stbi__get8(z); - } - - buffer[len] = 0; - return buffer; -} - -static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) -{ - if ( input[3] != 0 ) { - float f1; - // Exponent - f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); - if (req_comp <= 2) - output[0] = (input[0] + input[1] + input[2]) * f1 / 3; - else { - output[0] = input[0] * f1; - output[1] = input[1] * f1; - output[2] = input[2] * f1; - } - if (req_comp == 2) output[1] = 1; - if (req_comp == 4) output[3] = 1; - } else { - switch (req_comp) { - case 4: output[3] = 1; /* fallthrough */ - case 3: output[0] = output[1] = output[2] = 0; - break; - case 2: output[1] = 1; /* fallthrough */ - case 1: output[0] = 0; - break; - } - } -} - -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int width, height; - stbi_uc *scanline; - float *hdr_data; - int len; - unsigned char count, value; - int i, j, k, c1,c2, z; - - - // Check identifier - if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) - return stbi__errpf("not HDR", "Corrupt HDR image"); - - // Parse header - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); - - // Parse width and height - // can't use sscanf() if we're not using stdio! - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - height = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - width = (int) strtol(token, NULL, 10); - - *x = width; - *y = height; - - if (comp) *comp = 3; - if (req_comp == 0) req_comp = 3; - - // Read data - hdr_data = (float *) stbi__malloc(height * width * req_comp * sizeof(float)); - - // Load image data - // image data is stored as some number of sca - if ( width < 8 || width >= 32768) { - // Read flat data - for (j=0; j < height; ++j) { - for (i=0; i < width; ++i) { - stbi_uc rgbe[4]; - main_decode_loop: - stbi__getn(s, rgbe, 4); - stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } - } else { - // Read RLE-encoded data - scanline = NULL; - - for (j = 0; j < height; ++j) { - c1 = stbi__get8(s); - c2 = stbi__get8(s); - len = stbi__get8(s); - if (c1 != 2 || c2 != 2 || (len & 0x80)) { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - stbi_uc rgbe[4]; - rgbe[0] = (stbi_uc) c1; - rgbe[1] = (stbi_uc) c2; - rgbe[2] = (stbi_uc) len; - rgbe[3] = (stbi_uc) stbi__get8(s); - stbi__hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - free(scanline); - goto main_decode_loop; // yes, this makes no sense - } - len <<= 8; - len |= stbi__get8(s); - if (len != width) { free(hdr_data); free(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) scanline = (stbi_uc *) stbi__malloc(width * 4); - - for (k = 0; k < 4; ++k) { - i = 0; - while (i < width) { - count = stbi__get8(s); - if (count > 128) { - // Run - value = stbi__get8(s); - count -= 128; - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = stbi__get8(s); - } - } - } - for (i=0; i < width; ++i) - stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); - } - free(scanline); - } - - return hdr_data; -} - -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - - if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) { - stbi__rewind( s ); - return 0; - } - - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) { - stbi__rewind( s ); - return 0; - } - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *y = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *x = (int) strtol(token, NULL, 10); - *comp = 3; - return 1; -} -#endif // STBI_NO_HDR - -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) -{ - int hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') { - stbi__rewind( s ); - return 0; - } - stbi__skip(s,12); - hsz = stbi__get32le(s); - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) { - stbi__rewind( s ); - return 0; - } - if (hsz == 12) { - *x = stbi__get16le(s); - *y = stbi__get16le(s); - } else { - *x = stbi__get32le(s); - *y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) { - stbi__rewind( s ); - return 0; - } - *comp = stbi__get16le(s) / 8; - return 1; -} - -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) -{ - int channelCount; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - *y = stbi__get32be(s); - *x = stbi__get32be(s); - if (stbi__get16be(s) != 8) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 3) { - stbi__rewind( s ); - return 0; - } - *comp = 4; - return 1; -} - -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) -{ - int act_comp=0,num_packets=0,chained; - stbi__pic_packet packets[10]; - - stbi__skip(s, 92); - - *x = stbi__get16be(s); - *y = stbi__get16be(s); - if (stbi__at_eof(s)) return 0; - if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi__rewind( s ); - return 0; - } - - stbi__skip(s, 8); - - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return 0; - - packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - act_comp |= packet->channel; - - if (stbi__at_eof(s)) { - stbi__rewind( s ); - return 0; - } - if (packet->size != 8) { - stbi__rewind( s ); - return 0; - } - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); - - return 1; -} - -static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) -{ - if (stbi__jpeg_info(s, x, y, comp)) - return 1; - if (stbi__png_info(s, x, y, comp)) - return 1; - if (stbi__gif_info(s, x, y, comp)) - return 1; - if (stbi__bmp_info(s, x, y, comp)) - return 1; - if (stbi__psd_info(s, x, y, comp)) - return 1; - if (stbi__pic_info(s, x, y, comp)) - return 1; - #ifndef STBI_NO_HDR - if (stbi__hdr_info(s, x, y, comp)) - return 1; - #endif - // test tga last because it's a crappy test! - if (stbi__tga_info(s, x, y, comp)) - return 1; - return stbi__err("unknown image type", "Image not of any known type, or corrupt"); -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_info_from_file(f, x, y, comp); - fclose(f); - return result; -} - -STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__info_main(&s,x,y,comp); - fseek(f,pos,SEEK_SET); - return r; -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__info_main(&s,x,y,comp); -} - -#endif // STB_IMAGE_IMPLEMENTATION - -/* - revision history: - 1.46 (2014-08-26) - fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG - 1.45 (2014-08-16) - fix MSVC-ARM internal compiler error by wrapping malloc - 1.44 (2014-08-07) - various warning fixes from Ronny Chevalier - 1.43 (2014-07-15) - fix MSVC-only compiler problem in code changed in 1.42 - 1.42 (2014-07-09) - don't define _CRT_SECURE_NO_WARNINGS (affects user code) - fixes to stbi__cleanup_jpeg path - added STBI_ASSERT to avoid requiring assert.h - 1.41 (2014-06-25) - fix search&replace from 1.36 that messed up comments/error messages - 1.40 (2014-06-22) - fix gcc struct-initialization warning - 1.39 (2014-06-15) - fix to TGA optimization when req_comp != number of components in TGA; - fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) - add support for BMP version 5 (more ignored fields) - 1.38 (2014-06-06) - suppress MSVC warnings on integer casts truncating values - fix accidental rename of 'skip' field of I/O - 1.37 (2014-06-04) - remove duplicate typedef - 1.36 (2014-06-03) - convert to header file single-file library - if de-iphone isn't set, load iphone images color-swapped instead of returning NULL - 1.35 (2014-05-27) - various warnings - fix broken STBI_SIMD path - fix bug where stbi_load_from_file no longer left file pointer in correct place - fix broken non-easy path for 32-bit BMP (possibly never used) - TGA optimization by Arseny Kapoulkine - 1.34 (unknown) - use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case - 1.33 (2011-07-14) - make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements - 1.32 (2011-07-13) - support for "info" function for all supported filetypes (SpartanJ) - 1.31 (2011-06-20) - a few more leak fixes, bug in PNG handling (SpartanJ) - 1.30 (2011-06-11) - added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) - removed deprecated format-specific test/load functions - removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway - error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) - fix inefficiency in decoding 32-bit BMP (David Woo) - 1.29 (2010-08-16) - various warning fixes from Aurelien Pocheville - 1.28 (2010-08-01) - fix bug in GIF palette transparency (SpartanJ) - 1.27 (2010-08-01) - cast-to-stbi_uc to fix warnings - 1.26 (2010-07-24) - fix bug in file buffering for PNG reported by SpartanJ - 1.25 (2010-07-17) - refix trans_data warning (Won Chun) - 1.24 (2010-07-12) - perf improvements reading from files on platforms with lock-heavy fgetc() - minor perf improvements for jpeg - deprecated type-specific functions so we'll get feedback if they're needed - attempt to fix trans_data warning (Won Chun) - 1.23 fixed bug in iPhone support - 1.22 (2010-07-10) - removed image *writing* support - stbi_info support from Jetro Lauha - GIF support from Jean-Marc Lienher - iPhone PNG-extensions from James Brown - warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) - 1.21 fix use of 'stbi_uc' in header (reported by jon blow) - 1.20 added support for Softimage PIC, by Tom Seddon - 1.19 bug in interlaced PNG corruption check (found by ryg) - 1.18 2008-08-02 - fix a threading bug (local mutable static) - 1.17 support interlaced PNG - 1.16 major bugfix - stbi__convert_format converted one too many pixels - 1.15 initialize some fields for thread safety - 1.14 fix threadsafe conversion bug - header-file-only version (#define STBI_HEADER_FILE_ONLY before including) - 1.13 threadsafe - 1.12 const qualifiers in the API - 1.11 Support installable IDCT, colorspace conversion routines - 1.10 Fixes for 64-bit (don't use "unsigned long") - optimized upsampling by Fabian "ryg" Giesen - 1.09 Fix format-conversion for PSD code (bad global variables!) - 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz - 1.07 attempt to fix C++ warning/errors again - 1.06 attempt to fix C++ warning/errors again - 1.05 fix TGA loading to return correct *comp and use good luminance calc - 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free - 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR - 1.02 support for (subset of) HDR files, float interface for preferred access to them - 1.01 fix bug: possible bug in handling right-side up bmps... not sure - fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all - 1.00 interface to zlib that skips zlib header - 0.99 correct handling of alpha in palette - 0.98 TGA loader by lonesock; dynamically add loaders (untested) - 0.97 jpeg errors on too large a file; also catch another malloc failure - 0.96 fix detection of invalid v value - particleman@mollyrocket forum - 0.95 during header scan, seek to markers in case of padding - 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same - 0.93 handle jpegtran output; verbose errors - 0.92 read 4,8,16,24,32-bit BMP files of several formats - 0.91 output 24-bit Windows 3.0 BMP files - 0.90 fix a few more warnings; bump version number to approach 1.0 - 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd - 0.60 fix compiling as c++ - 0.59 fix warnings: merge Dave Moore's -Wall fixes - 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian - 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available - 0.56 fix bug: zlib uncompressed mode len vs. nlen - 0.55 fix bug: restart_interval not initialized to 0 - 0.54 allow NULL for 'int *comp' - 0.53 fix bug in png 3->4; speedup png decoding - 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments - 0.51 obey req_comp requests, 1-component jpegs return as 1-component, - on 'test' only check type, not whether we support this variant - 0.50 first released version -*/ diff --git a/ext/imgui/stb_rect_pack.h b/ext/imgui/stb_rect_pack.h new file mode 100644 index 0000000000..eb0ef2f268 --- /dev/null +++ b/ext/imgui/stb_rect_pack.h @@ -0,0 +1,547 @@ +// stb_rect_pack.h - v0.05 - public domain - rectangle packing +// Sean Barrett 2014 +// +// Useful for e.g. packing rectangular textures into an atlas. +// Does not do rotation. +// +// Not necessarily the awesomest packing method, but better than +// the totally naive one in stb_truetype (which is primarily what +// this is meant to replace). +// +// Has only had a few tests run, may have issues. +// +// More docs to come. +// +// No memory allocations; uses qsort() and assert() from stdlib. +// +// This library currently uses the Skyline Bottom-Left algorithm. +// +// Please note: better rectangle packers are welcome! Please +// implement them to the same API, but with a different init +// function. +// +// Version history: +// +// 0.05: added STBRP_ASSERT to allow replacing assert +// 0.04: fixed minor bug in STBRP_LARGE_RECTS support +// 0.01: initial release + +////////////////////////////////////////////////////////////////////////////// +// +// INCLUDE SECTION +// + +#ifndef STB_INCLUDE_STB_RECT_PACK_H +#define STB_INCLUDE_STB_RECT_PACK_H + +#define STB_RECT_PACK_VERSION 1 + +#ifdef STBRP_STATIC +#define STBRP_DEF static +#else +#define STBRP_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct stbrp_context stbrp_context; +typedef struct stbrp_node stbrp_node; +typedef struct stbrp_rect stbrp_rect; + +#ifdef STBRP_LARGE_RECTS +typedef int stbrp_coord; +#else +typedef unsigned short stbrp_coord; +#endif + +STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); +// Assign packed locations to rectangles. The rectangles are of type +// 'stbrp_rect' defined below, stored in the array 'rects', and there +// are 'num_rects' many of them. +// +// Rectangles which are successfully packed have the 'was_packed' flag +// set to a non-zero value and 'x' and 'y' store the minimum location +// on each axis (i.e. bottom-left in cartesian coordinates, top-left +// if you imagine y increasing downwards). Rectangles which do not fit +// have the 'was_packed' flag set to 0. +// +// You should not try to access the 'rects' array from another thread +// while this function is running, as the function temporarily reorders +// the array while it executes. +// +// To pack into another rectangle, you need to call stbrp_init_target +// again. To continue packing into the same rectangle, you can call +// this function again. Calling this multiple times with multiple rect +// arrays will probably produce worse packing results than calling it +// a single time with the full rectangle array, but the option is +// available. + +struct stbrp_rect +{ + // reserved for your use: + int id; + + // input: + stbrp_coord w, h; + + // output: + stbrp_coord x, y; + int was_packed; // non-zero if valid packing + +}; // 16 bytes, nominally + + +STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); +// Initialize a rectangle packer to: +// pack a rectangle that is 'width' by 'height' in dimensions +// using temporary storage provided by the array 'nodes', which is 'num_nodes' long +// +// You must call this function every time you start packing into a new target. +// +// There is no "shutdown" function. The 'nodes' memory must stay valid for +// the following stbrp_pack_rects() call (or calls), but can be freed after +// the call (or calls) finish. +// +// Note: to guarantee best results, either: +// 1. make sure 'num_nodes' >= 'width' +// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' +// +// If you don't do either of the above things, widths will be quantized to multiples +// of small integers to guarantee the algorithm doesn't run out of temporary storage. +// +// If you do #2, then the non-quantized algorithm will be used, but the algorithm +// may run out of temporary storage and be unable to pack some rectangles. + +STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); +// Optionally call this function after init but before doing any packing to +// change the handling of the out-of-temp-memory scenario, described above. +// If you call init again, this will be reset to the default (false). + + +STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); +// Optionally select which packing heuristic the library should use. Different +// heuristics will produce better/worse results for different data sets. +// If you call init again, this will be reset to the default. + +enum +{ + STBRP_HEURISTIC_Skyline_default=0, + STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, + STBRP_HEURISTIC_Skyline_BF_sortHeight +}; + + +////////////////////////////////////////////////////////////////////////////// +// +// the details of the following structures don't matter to you, but they must +// be visible so you can handle the memory allocations for them + +struct stbrp_node +{ + stbrp_coord x,y; + stbrp_node *next; +}; + +struct stbrp_context +{ + int width; + int height; + int align; + int init_mode; + int heuristic; + int num_nodes; + stbrp_node *active_head; + stbrp_node *free_head; + stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' +}; + +#ifdef __cplusplus +} +#endif + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION SECTION +// + +#ifdef STB_RECT_PACK_IMPLEMENTATION +#include + +#ifndef STBRP_ASSERT +#include +#define STBRP_ASSERT assert +#endif + +enum +{ + STBRP__INIT_skyline = 1 +}; + +STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) +{ + switch (context->init_mode) { + case STBRP__INIT_skyline: + STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); + context->heuristic = heuristic; + break; + default: + STBRP_ASSERT(0); + } +} + +STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) +{ + if (allow_out_of_mem) + // if it's ok to run out of memory, then don't bother aligning them; + // this gives better packing, but may fail due to OOM (even though + // the rectangles easily fit). @TODO a smarter approach would be to only + // quantize once we've hit OOM, then we could get rid of this parameter. + context->align = 1; + else { + // if it's not ok to run out of memory, then quantize the widths + // so that num_nodes is always enough nodes. + // + // I.e. num_nodes * align >= width + // align >= width / num_nodes + // align = ceil(width/num_nodes) + + context->align = (context->width + context->num_nodes-1) / context->num_nodes; + } +} + +STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) +{ + int i; +#ifndef STBRP_LARGE_RECTS + STBRP_ASSERT(width <= 0xffff && height <= 0xffff); +#endif + + for (i=0; i < num_nodes-1; ++i) + nodes[i].next = &nodes[i+1]; + nodes[i].next = NULL; + context->init_mode = STBRP__INIT_skyline; + context->heuristic = STBRP_HEURISTIC_Skyline_default; + context->free_head = &nodes[0]; + context->active_head = &context->extra[0]; + context->width = width; + context->height = height; + context->num_nodes = num_nodes; + stbrp_setup_allow_out_of_mem(context, 0); + + // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) + context->extra[0].x = 0; + context->extra[0].y = 0; + context->extra[0].next = &context->extra[1]; + context->extra[1].x = (stbrp_coord) width; +#ifdef STBRP_LARGE_RECTS + context->extra[1].y = (1<<30); +#else + context->extra[1].y = 65535; +#endif + context->extra[1].next = NULL; +} + +// find minimum y position if it starts at x1 +static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) +{ + (void)c; + stbrp_node *node = first; + int x1 = x0 + width; + int min_y, visited_width, waste_area; + STBRP_ASSERT(first->x <= x0); + + #if 0 + // skip in case we're past the node + while (node->next->x <= x0) + ++node; + #else + STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency + #endif + + STBRP_ASSERT(node->x <= x0); + + min_y = 0; + waste_area = 0; + visited_width = 0; + while (node->x < x1) { + if (node->y > min_y) { + // raise min_y higher. + // we've accounted for all waste up to min_y, + // but we'll now add more waste for everything we've visted + waste_area += visited_width * (node->y - min_y); + min_y = node->y; + // the first time through, visited_width might be reduced + if (node->x < x0) + visited_width += node->next->x - x0; + else + visited_width += node->next->x - node->x; + } else { + // add waste area + int under_width = node->next->x - node->x; + if (under_width + visited_width > width) + under_width = width - visited_width; + waste_area += under_width * (min_y - node->y); + visited_width += under_width; + } + node = node->next; + } + + *pwaste = waste_area; + return min_y; +} + +typedef struct +{ + int x,y; + stbrp_node **prev_link; +} stbrp__findresult; + +static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) +{ + int best_waste = (1<<30), best_x, best_y = (1 << 30); + stbrp__findresult fr; + stbrp_node **prev, *node, *tail, **best = NULL; + + // align to multiple of c->align + width = (width + c->align - 1); + width -= width % c->align; + STBRP_ASSERT(width % c->align == 0); + + node = c->active_head; + prev = &c->active_head; + while (node->x + width <= c->width) { + int y,waste; + y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); + if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL + // bottom left + if (y < best_y) { + best_y = y; + best = prev; + } + } else { + // best-fit + if (y + height <= c->height) { + // can only use it if it first vertically + if (y < best_y || (y == best_y && waste < best_waste)) { + best_y = y; + best_waste = waste; + best = prev; + } + } + } + prev = &node->next; + node = node->next; + } + + best_x = (best == NULL) ? 0 : (*best)->x; + + // if doing best-fit (BF), we also have to try aligning right edge to each node position + // + // e.g, if fitting + // + // ____________________ + // |____________________| + // + // into + // + // | | + // | ____________| + // |____________| + // + // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned + // + // This makes BF take about 2x the time + + if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { + tail = c->active_head; + node = c->active_head; + prev = &c->active_head; + // find first node that's admissible + while (tail->x < width) + tail = tail->next; + while (tail) { + int xpos = tail->x - width; + int y,waste; + STBRP_ASSERT(xpos >= 0); + // find the left position that matches this + while (node->next->x <= xpos) { + prev = &node->next; + node = node->next; + } + STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); + y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); + if (y + height < c->height) { + if (y <= best_y) { + if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { + best_x = xpos; + STBRP_ASSERT(y <= best_y); + best_y = y; + best_waste = waste; + best = prev; + } + } + } + tail = tail->next; + } + } + + fr.prev_link = best; + fr.x = best_x; + fr.y = best_y; + return fr; +} + +static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) +{ + // find best position according to heuristic + stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); + stbrp_node *node, *cur; + + // bail if: + // 1. it failed + // 2. the best node doesn't fit (we don't always check this) + // 3. we're out of memory + if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { + res.prev_link = NULL; + return res; + } + + // on success, create new node + node = context->free_head; + node->x = (stbrp_coord) res.x; + node->y = (stbrp_coord) (res.y + height); + + context->free_head = node->next; + + // insert the new node into the right starting point, and + // let 'cur' point to the remaining nodes needing to be + // stiched back in + + cur = *res.prev_link; + if (cur->x < res.x) { + // preserve the existing one, so start testing with the next one + stbrp_node *next = cur->next; + cur->next = node; + cur = next; + } else { + *res.prev_link = node; + } + + // from here, traverse cur and free the nodes, until we get to one + // that shouldn't be freed + while (cur->next && cur->next->x <= res.x + width) { + stbrp_node *next = cur->next; + // move the current node to the free list + cur->next = context->free_head; + context->free_head = cur; + cur = next; + } + + // stitch the list back in + node->next = cur; + + if (cur->x < res.x + width) + cur->x = (stbrp_coord) (res.x + width); + +#ifdef _DEBUG + cur = context->active_head; + while (cur->x < context->width) { + STBRP_ASSERT(cur->x < cur->next->x); + cur = cur->next; + } + STBRP_ASSERT(cur->next == NULL); + + { + stbrp_node *L1 = NULL, *L2 = NULL; + int count=0; + cur = context->active_head; + while (cur) { + L1 = cur; + cur = cur->next; + ++count; + } + cur = context->free_head; + while (cur) { + L2 = cur; + cur = cur->next; + ++count; + } + STBRP_ASSERT(count == context->num_nodes+2); + } +#endif + + return res; +} + +static int rect_height_compare(const void *a, const void *b) +{ + stbrp_rect *p = (stbrp_rect *) a; + stbrp_rect *q = (stbrp_rect *) b; + if (p->h > q->h) + return -1; + if (p->h < q->h) + return 1; + return (p->w > q->w) ? -1 : (p->w < q->w); +} + +static int rect_width_compare(const void *a, const void *b) +{ + stbrp_rect *p = (stbrp_rect *) a; + stbrp_rect *q = (stbrp_rect *) b; + if (p->w > q->w) + return -1; + if (p->w < q->w) + return 1; + return (p->h > q->h) ? -1 : (p->h < q->h); +} + +static int rect_original_order(const void *a, const void *b) +{ + stbrp_rect *p = (stbrp_rect *) a; + stbrp_rect *q = (stbrp_rect *) b; + return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); +} + +#ifdef STBRP_LARGE_RECTS +#define STBRP__MAXVAL 0xffffffff +#else +#define STBRP__MAXVAL 0xffff +#endif + +STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) +{ + int i; + + // we use the 'was_packed' field internally to allow sorting/unsorting + for (i=0; i < num_rects; ++i) { + rects[i].was_packed = i; + #ifndef STBRP_LARGE_RECTS + STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff); + #endif + } + + // sort according to heuristic + qsort(rects, num_rects, sizeof(rects[0]), rect_height_compare); + + for (i=0; i < num_rects; ++i) { + stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); + if (fr.prev_link) { + rects[i].x = (stbrp_coord) fr.x; + rects[i].y = (stbrp_coord) fr.y; + } else { + rects[i].x = rects[i].y = STBRP__MAXVAL; + } + } + + // unsort + qsort(rects, num_rects, sizeof(rects[0]), rect_original_order); + + // set was_packed flags + for (i=0; i < num_rects; ++i) + rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); +} +#endif diff --git a/ext/imgui/stb_truetype.h b/ext/imgui/stb_truetype.h new file mode 100644 index 0000000000..3c5d6f304e --- /dev/null +++ b/ext/imgui/stb_truetype.h @@ -0,0 +1,2658 @@ +// [ImGui] this is a slightly modified version of stb_truetype.h 1.02 +// [ImGui] we added stbtt_PackFontRangesGatherRects() and stbtt_PackFontRangesRenderIntoRects() and modified stbtt_PackBegin() + +// stb_truetype.h - v1.02 - public domain +// authored from 2009-2014 by Sean Barrett / RAD Game Tools +// +// This library processes TrueType files: +// parse files +// extract glyph metrics +// extract glyph shapes +// render glyphs to one-channel bitmaps with antialiasing (box filter) +// +// Todo: +// non-MS cmaps +// crashproof on bad data +// hinting? (no longer patented) +// cleartype-style AA? +// optimize: use simple memory allocator for intermediates +// optimize: build edge-list directly from curves +// optimize: rasterize directly from curves? +// +// ADDITIONAL CONTRIBUTORS +// +// Mikko Mononen: compound shape support, more cmap formats +// Tor Andersson: kerning, subpixel rendering +// +// Bug/warning reports/fixes: +// "Zer" on mollyrocket (with fix) +// Cass Everitt +// stoiko (Haemimont Games) +// Brian Hook +// Walter van Niftrik +// David Gow +// David Given +// Ivan-Assen Ivanov +// Anthony Pesch +// Johan Duparc +// Hou Qiming +// Fabian "ryg" Giesen +// +// Misc other: +// Ryan Gordon +// +// VERSION HISTORY +// +// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ +// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match +// non-oversampled; STBTT_POINT_SIZE for packed case only +// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling +// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) +// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID +// 0.8b (2014-07-07) fix a warning +// 0.8 (2014-05-25) fix a few more warnings +// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back +// 0.6c (2012-07-24) improve documentation +// 0.6b (2012-07-20) fix a few more warnings +// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, +// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty +// 0.5 (2011-12-09) bugfixes: +// subpixel glyph renderer computed wrong bounding box +// first vertex of shape can be off-curve (FreeSans) +// 0.4b (2011-12-03) fixed an error in the font baking example +// 0.4 (2011-12-01) kerning, subpixel rendering (tor) +// bugfixes for: +// codepoint-to-glyph conversion using table fmt=12 +// codepoint-to-glyph conversion using table fmt=4 +// stbtt_GetBakedQuad with non-square texture (Zer) +// updated Hello World! sample to use kerning and subpixel +// fixed some warnings +// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) +// userdata, malloc-from-userdata, non-zero fill (stb) +// 0.2 (2009-03-11) Fix unsigned/signed char warnings +// 0.1 (2009-03-09) First public release +// +// LICENSE +// +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevokable license to copy +// and modify this file as you see fit. +// +// USAGE +// +// Include this file in whatever places neeed to refer to it. In ONE C/C++ +// file, write: +// #define STB_TRUETYPE_IMPLEMENTATION +// before the #include of this file. This expands out the actual +// implementation into that C/C++ file. +// +// Simple 3D API (don't ship this, but it's fine for tools and quick start) +// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture +// stbtt_GetBakedQuad() -- compute quad to draw for a given char +// +// Improved 3D API (more shippable): +// #include "stb_rect_pack.h" -- optional, but you really want it +// stbtt_PackBegin() +// stbtt_PackSetOversample() -- for improved quality on small fonts +// stbtt_PackFontRanges() -- pack and renders +// stbtt_PackEnd() +// stbtt_GetPackedQuad() +// +// "Load" a font file from a memory buffer (you have to keep the buffer loaded) +// stbtt_InitFont() +// stbtt_GetFontOffsetForIndex() -- use for TTC font collections +// +// Render a unicode codepoint to a bitmap +// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap +// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide +// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be +// +// Character advance/positioning +// stbtt_GetCodepointHMetrics() +// stbtt_GetFontVMetrics() +// stbtt_GetCodepointKernAdvance() +// +// ADDITIONAL DOCUMENTATION +// +// Immediately after this block comment are a series of sample programs. +// +// After the sample programs is the "header file" section. This section +// includes documentation for each API function. +// +// Some important concepts to understand to use this library: +// +// Codepoint +// Characters are defined by unicode codepoints, e.g. 65 is +// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is +// the hiragana for "ma". +// +// Glyph +// A visual character shape (every codepoint is rendered as +// some glyph) +// +// Glyph index +// A font-specific integer ID representing a glyph +// +// Baseline +// Glyph shapes are defined relative to a baseline, which is the +// bottom of uppercase characters. Characters extend both above +// and below the baseline. +// +// Current Point +// As you draw text to the screen, you keep track of a "current point" +// which is the origin of each character. The current point's vertical +// position is the baseline. Even "baked fonts" use this model. +// +// Vertical Font Metrics +// The vertical qualities of the font, used to vertically position +// and space the characters. See docs for stbtt_GetFontVMetrics. +// +// Font Size in Pixels or Points +// The preferred interface for specifying font sizes in stb_truetype +// is to specify how tall the font's vertical extent should be in pixels. +// If that sounds good enough, skip the next paragraph. +// +// Most font APIs instead use "points", which are a common typographic +// measurement for describing font size, defined as 72 points per inch. +// stb_truetype provides a point API for compatibility. However, true +// "per inch" conventions don't make much sense on computer displays +// since they different monitors have different number of pixels per +// inch. For example, Windows traditionally uses a convention that +// there are 96 pixels per inch, thus making 'inch' measurements have +// nothing to do with inches, and thus effectively defining a point to +// be 1.333 pixels. Additionally, the TrueType font data provides +// an explicit scale factor to scale a given font's glyphs to points, +// but the author has observed that this scale factor is often wrong +// for non-commercial fonts, thus making fonts scaled in points +// according to the TrueType spec incoherently sized in practice. +// +// ADVANCED USAGE +// +// Quality: +// +// - Use the functions with Subpixel at the end to allow your characters +// to have subpixel positioning. Since the font is anti-aliased, not +// hinted, this is very import for quality. (This is not possible with +// baked fonts.) +// +// - Kerning is now supported, and if you're supporting subpixel rendering +// then kerning is worth using to give your text a polished look. +// +// Performance: +// +// - Convert Unicode codepoints to glyph indexes and operate on the glyphs; +// if you don't do this, stb_truetype is forced to do the conversion on +// every call. +// +// - There are a lot of memory allocations. We should modify it to take +// a temp buffer and allocate from the temp buffer (without freeing), +// should help performance a lot. +// +// NOTES +// +// The system uses the raw data found in the .ttf file without changing it +// and without building auxiliary data structures. This is a bit inefficient +// on little-endian systems (the data is big-endian), but assuming you're +// caching the bitmaps or glyph shapes this shouldn't be a big deal. +// +// It appears to be very hard to programmatically determine what font a +// given file is in a general way. I provide an API for this, but I don't +// recommend it. +// +// +// SOURCE STATISTICS (based on v0.6c, 2050 LOC) +// +// Documentation & header file 520 LOC \___ 660 LOC documentation +// Sample code 140 LOC / +// Truetype parsing 620 LOC ---- 620 LOC TrueType +// Software rasterization 240 LOC \ . +// Curve tesselation 120 LOC \__ 550 LOC Bitmap creation +// Bitmap management 100 LOC / +// Baked bitmap interface 70 LOC / +// Font name matching & access 150 LOC ---- 150 +// C runtime library abstraction 60 LOC ---- 60 + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// SAMPLE PROGRAMS +//// +// +// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless +// +#if 0 +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<20]; +unsigned char temp_bitmap[512*512]; + +stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs +GLstbtt_uint ftex; + +void my_stbtt_initfont(void) +{ + fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); + stbtt_BakeFontBitmap(data,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! + // can free ttf_buffer at this point + glGenTextures(1, &ftex); + glBindTexture(GL_TEXTURE_2D, ftex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); + // can free temp_bitmap at this point + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +} + +void my_stbtt_print(float x, float y, char *text) +{ + // assume orthographic projection with units = screen pixels, origin at top left + glBindTexture(GL_TEXTURE_2D, ftex); + glBegin(GL_QUADS); + while (*text) { + if (*text >= 32 && *text < 128) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); + } + ++text; + } + glEnd(); +} +#endif +// +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program (this compiles): get a single bitmap, print as ASCII art +// +#if 0 +#include +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<25]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); + + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif +// +// Output: +// +// .ii. +// @@@@@@. +// V@Mio@@o +// :i. V@V +// :oM@@M +// :@@@MM@M +// @@o o@M +// :@@. M@M +// @@@o@@@@ +// :M@@V:@@. +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program: print "Hello World!" banner, with bugs +// +#if 0 +char buffer[24<<20]; +unsigned char screen[20][79]; + +int main(int arg, char **argv) +{ + stbtt_fontinfo font; + int i,j,ascent,baseline,ch=0; + float scale, xpos=2; // leave a little padding in case the character extends left + char *text = "Heljo World!"; + + fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); + stbtt_InitFont(&font, buffer, 0); + + scale = stbtt_ScaleForPixelHeight(&font, 15); + stbtt_GetFontVMetrics(&font, &ascent,0,0); + baseline = (int) (ascent*scale); + + while (text[ch]) { + int advance,lsb,x0,y0,x1,y1; + float x_shift = xpos - (float) floor(xpos); + stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); + stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); + stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); + // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong + // because this API is really for baking character bitmaps into textures. if you want to render + // a sequence of characters, you really need to render each bitmap to a temp buffer, then + // "alpha blend" that into the working buffer + xpos += (advance * scale); + if (text[ch+1]) + xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); + ++ch; + } + + for (j=0; j < 20; ++j) { + for (i=0; i < 78; ++i) + putchar(" .:ioVM@"[screen[j][i]>>5]); + putchar('\n'); + } + + return 0; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// INTEGRATION WITH YOUR CODEBASE +//// +//// The following sections allow you to supply alternate definitions +//// of C library functions used by stb_truetype. + +#ifdef STB_TRUETYPE_IMPLEMENTATION + // #define your own (u)stbtt_int8/16/32 before including to override this + #ifndef stbtt_uint8 + typedef unsigned char stbtt_uint8; + typedef signed char stbtt_int8; + typedef unsigned short stbtt_uint16; + typedef signed short stbtt_int16; + typedef unsigned int stbtt_uint32; + typedef signed int stbtt_int32; + #endif + + typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; + typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; + + #ifdef STBTT_STATIC + #define STBTT_DEF static + #else + #define STBTT_DEF extern + #endif + + // #define your own STBTT_sort() to override this to avoid qsort + #ifndef STBTT_sort + #include + #define STBTT_sort(data,num_items,item_size,compare_func) qsort(data,num_items,item_size,compare_func) + #endif + + // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h + #ifndef STBTT_ifloor + #include + #define STBTT_ifloor(x) ((int) floor(x)) + #define STBTT_iceil(x) ((int) ceil(x)) + #endif + + #ifndef STBTT_sqrt + #include + #define STBTT_sqrt(x) sqrt(x) + #endif + + // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h + #ifndef STBTT_malloc + #include + #define STBTT_malloc(x,u) ((void)(u),malloc(x)) + #define STBTT_free(x,u) ((void)(u),free(x)) + #endif + + #ifndef STBTT_assert + #include + #define STBTT_assert(x) assert(x) + #endif + + #ifndef STBTT_strlen + #include + #define STBTT_strlen(x) strlen(x) + #endif + + #ifndef STBTT_memcpy + #include + #define STBTT_memcpy memcpy + #define STBTT_memset memset + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +#define __STB_INCLUDE_STB_TRUETYPE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// TEXTURE BAKING API +// +// If you use this API, you only have to call two functions ever. +// + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; +} stbtt_bakedchar; + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata); // you allocate this, it's num_chars long +// if return is positive, the first unused row of the bitmap +// if return is negative, returns the negative of the number of characters that fit +// if return is 0, no characters fit and no rows were used +// This uses a very crappy packing. + +typedef struct +{ + float x0,y0,s0,t0; // top-left + float x1,y1,s1,t1; // bottom-right +} stbtt_aligned_quad; + +STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier +// Call GetBakedQuad with char_index = 'character - first_char', and it +// creates the quad you need to draw and advances the current position. +// +// The coordinate system used assumes y increases downwards. +// +// Characters will extend both above and below the current position; +// see discussion of "BASELINE" above. +// +// It's inefficient; you might want to c&p it and optimize it. + + + +////////////////////////////////////////////////////////////////////////////// +// +// NEW TEXTURE BAKING API +// +// This provides options for packing multiple fonts into one atlas, not +// perfectly but better than nothing. + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; + float xoff2,yoff2; +} stbtt_packedchar; + +typedef struct stbtt_pack_context stbtt_pack_context; +typedef struct stbtt_fontinfo stbtt_fontinfo; +#ifndef STB_RECT_PACK_VERSION +typedef struct stbrp_rect stbrp_rect; +#endif + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); +// Initializes a packing context stored in the passed-in stbtt_pack_context. +// Future calls using this context will pack characters into the bitmap passed +// in here: a 1-channel bitmap that is weight x height. stride_in_bytes is +// the distance from one row to the next (or 0 to mean they are packed tightly +// together). "padding" is // the amount of padding to leave between each +// character (normally you want '1' for bitmaps you'll use as textures with +// bilinear filtering). +// +// Returns 0 on failure, 1 on success. + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); +// Cleans up the packing context and frees all memory. + +#define STBTT_POINT_SIZE(x) (-(x)) + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); +// Creates character bitmaps from the font_index'th font found in fontdata (use +// font_index=0 if you don't know what that is). It creates num_chars_in_range +// bitmaps for characters with unicode values starting at first_unicode_char_in_range +// and increasing. Data for how to render them is stored in chardata_for_range; +// pass these to stbtt_GetPackedQuad to get back renderable quads. +// +// font_size is the full height of the character from ascender to descender, +// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed +// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() +// and pass that result as 'font_size': +// ..., 20 , ... // font max minus min y is 20 pixels tall +// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall + +typedef struct +{ + float font_size; + int first_unicode_char_in_range; + int num_chars_in_range; + stbtt_packedchar *chardata_for_range; // output +} stbtt_pack_range; + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); +// Creates character bitmaps from multiple ranges of characters stored in +// ranges. This will usually create a better-packed bitmap than multiple +// calls to stbtt_PackFontRange. + +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +// Those functions are called by stbtt_PackFontRanges(). If you want to +// pack multiple fonts or custom data into a same texture, you may copy +// the contents of stbtt_PackFontRanges() and create a custom version +// using those functions. + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); +// Oversampling a font increases the quality by allowing higher-quality subpixel +// positioning, and is especially valuable at smaller text sizes. +// +// This function sets the amount of oversampling for all following calls to +// stbtt_PackFontRange(s). The default (no oversampling) is achieved by +// h_oversample=1, v_oversample=1. The total number of pixels required is +// h_oversample*v_oversample larger than the default; for example, 2x2 +// oversampling requires 4x the storage of 1x1. For best results, render +// oversampled textures with bilinear filtering. Look at the readme in +// stb/tests/oversample for information about oversampled fonts + +STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int align_to_integer); + +// this is an opaque structure that you shouldn't mess with which holds +// all the context needed from PackBegin to PackEnd. +struct stbtt_pack_context { + void *user_allocator_context; + void *pack_info; + int width; + int height; + int stride_in_bytes; + int padding; + unsigned int h_oversample, v_oversample; + unsigned char *pixels; + void *nodes; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// FONT LOADING +// +// + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +// Each .ttf/.ttc file may have more than one font. Each font has a sequential +// index number starting from 0. Call this function to get the font offset for +// a given index; it returns -1 if the index is out of range. A regular .ttf +// file will only define one font and it always be at offset 0, so it will +// return '0' for index 0, and -1 for all other indices. You can just skip +// this step if you know it's that kind of font. + + +// The following structure is defined publically so you can declare one on +// the stack or as a global or etc, but you should treat it as opaque. +typedef struct stbtt_fontinfo +{ + void * userdata; + unsigned char * data; // pointer to .ttf file + int fontstart; // offset of start of font + + int numGlyphs; // number of glyphs, needed for range checking + + int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf + int index_map; // a cmap mapping for our chosen character encoding + int indexToLocFormat; // format needed to map from glyph index to glyph +} stbtt_fontinfo; + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +// Given an offset into the file that defines a font, this function builds +// the necessary cached info for the rest of the system. You must allocate +// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't +// need to do anything special to free it, because the contents are pure +// value data with no additional data structures. Returns 0 on failure. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER TO GLYPH-INDEX CONVERSIOn + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +// If you're going to perform multiple operations on the same character +// and you want a speed-up, call this function with the character you're +// going to process, then use glyph-based functions instead of the +// codepoint-based functions. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER PROPERTIES +// + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose "height" is 'pixels' tall. +// Height is measured as the distance from the highest ascender to the lowest +// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics +// and computing: +// scale = pixels / (ascent - descent) +// so if you prefer to measure height by the ascent only, use a similar calculation. + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose EM size is mapped to +// 'pixels' tall. This is probably what traditional APIs compute, but +// I'm not positive. + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +// ascent is the coordinate above the baseline the font extends; descent +// is the coordinate below the baseline the font extends (i.e. it is typically negative) +// lineGap is the spacing between one row's descent and the next row's ascent... +// so you should advance the vertical position by "*ascent - *descent + *lineGap" +// these are expressed in unscaled coordinates, so you must multiply by +// the scale factor for a given size + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); +// the bounding box around all possible characters + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +// leftSideBearing is the offset from the current horizontal position to the left edge of the character +// advanceWidth is the offset from the current horizontal position to the next horizontal position +// these are expressed in unscaled coordinates + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +// an additional amount to add to the 'advance' value between ch1 and ch2 + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +// Gets the bounding box of the visible part of the glyph, in unscaled coordinates + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +// as above, but takes one or more glyph indices for greater efficiency + + +////////////////////////////////////////////////////////////////////////////// +// +// GLYPH SHAPES (you probably don't need these, but they have to go before +// the bitmaps for C declaration-order reasons) +// + +#ifndef STBTT_vmove // you can predefine these to use different values (but why?) + enum { + STBTT_vmove=1, + STBTT_vline, + STBTT_vcurve + }; +#endif + +#ifndef stbtt_vertex // you can predefine this to use different values + // (we share this with other code at RAD) + #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file + typedef struct + { + stbtt_vertex_type x,y,cx,cy; + unsigned char type,padding; + } stbtt_vertex; +#endif + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); +// returns non-zero if nothing is drawn for this glyph + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +// returns # of vertices and fills *vertices with the pointer to them +// these are expressed in "unscaled" coordinates +// +// The shape is a series of countours. Each one starts with +// a STBTT_moveto, then consists of a series of mixed +// STBTT_lineto and STBTT_curveto segments. A lineto +// draws a line from previous endpoint to its x,y; a curveto +// draws a quadratic bezier from previous endpoint to +// its x,y, using cx,cy as the bezier control point. + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +// frees the data allocated above + +////////////////////////////////////////////////////////////////////////////// +// +// BITMAP RENDERING +// + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +// frees the bitmap allocated below + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// allocates a large-enough single-channel 8bpp bitmap and renders the +// specified character/glyph at the specified scale into it, with +// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). +// *width & *height are filled out with the width & height of the bitmap, +// which is stored left-to-right, top-to-bottom. +// +// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap +// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap +// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the +// width and height and positioning info for it first. + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); +// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +// get the bbox of the bitmap centered around the glyph origin; so the +// bitmap width is ix1-ix0, height is iy1-iy0, and location to place +// the bitmap top left is (leftSideBearing*scale,iy0). +// (Note that the bitmap uses y-increases-down, but the shape uses +// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); +// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel +// shift for the character + +// the following functions are equivalent to the above functions, but operate +// on glyph indices instead of Unicode codepoints (for efficiency) +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); + + +// @TODO: don't expose this structure +typedef struct +{ + int w,h,stride; + unsigned char *pixels; +} stbtt__bitmap; + +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata); + +////////////////////////////////////////////////////////////////////////////// +// +// Finding the right font... +// +// You should really just solve this offline, keep your own tables +// of what font is what, and don't try to get it out of the .ttf file. +// That's because getting it out of the .ttf file is really hard, because +// the names in the file can appear in many possible encodings, in many +// possible languages, and e.g. if you need a case-insensitive comparison, +// the details of that depend on the encoding & language in a complex way +// (actually underspecified in truetype, but also gigantic). +// +// But you can use the provided functions in two possible ways: +// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on +// unicode-encoded names to try to find the font you want; +// you can run this before calling stbtt_InitFont() +// +// stbtt_GetFontNameString() lets you get any of the various strings +// from the file yourself and do your own comparisons on them. +// You have to have called stbtt_InitFont() first. + + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +// returns the offset (not index) of the font that matches, or -1 if none +// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". +// if you use any other flag, use a font name like "Arial"; this checks +// the 'macStyle' header field; i don't know if fonts set this consistently +#define STBTT_MACSTYLE_DONTCARE 0 +#define STBTT_MACSTYLE_BOLD 1 +#define STBTT_MACSTYLE_ITALIC 2 +#define STBTT_MACSTYLE_UNDERSCORE 4 +#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +// returns 1/0 whether the first string interpreted as utf8 is identical to +// the second string interpreted as big-endian utf16... useful for strings from next func + +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +// returns the string (which may be big-endian double byte, e.g. for unicode) +// and puts the length in bytes in *length. +// +// some of the values for the IDs are below; for more see the truetype spec: +// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html +// http://www.microsoft.com/typography/otspec/name.htm + +enum { // platformID + STBTT_PLATFORM_ID_UNICODE =0, + STBTT_PLATFORM_ID_MAC =1, + STBTT_PLATFORM_ID_ISO =2, + STBTT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_UNICODE + STBTT_UNICODE_EID_UNICODE_1_0 =0, + STBTT_UNICODE_EID_UNICODE_1_1 =1, + STBTT_UNICODE_EID_ISO_10646 =2, + STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, + STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT + STBTT_MS_EID_SYMBOL =0, + STBTT_MS_EID_UNICODE_BMP =1, + STBTT_MS_EID_SHIFTJIS =2, + STBTT_MS_EID_UNICODE_FULL =10 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes + STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, + STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, + STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, + STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 +}; + +enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... + // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs + STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, + STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, + STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, + STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, + STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, + STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D +}; + +enum { // languageID for STBTT_PLATFORM_ID_MAC + STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, + STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, + STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, + STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , + STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , + STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, + STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 +}; + +#ifdef __cplusplus +} +#endif + +#endif // __STB_INCLUDE_STB_TRUETYPE_H__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// IMPLEMENTATION +//// +//// + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +#ifndef STBTT_MAX_OVERSAMPLE +#define STBTT_MAX_OVERSAMPLE 8 +#endif + +typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; + +////////////////////////////////////////////////////////////////////////// +// +// accessors to parse data from file +// + +// on platforms that don't allow misaligned reads, if we want to allow +// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE + +#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +#define ttCHAR(p) (* (stbtt_int8 *) (p)) +#define ttFixed(p) ttLONG(p) + +#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE) + + #define ttUSHORT(p) (* (stbtt_uint16 *) (p)) + #define ttSHORT(p) (* (stbtt_int16 *) (p)) + #define ttULONG(p) (* (stbtt_uint32 *) (p)) + #define ttLONG(p) (* (stbtt_int32 *) (p)) + +#else + + static stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + static stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + static stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + static stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + +#endif + +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) + +static int stbtt__isfont(const stbtt_uint8 *font) +{ + // check the version number + if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 + if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! + if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF + if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + return 0; +} + +// @OPTIMIZE: binary search +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) +{ + stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); + stbtt_uint32 tabledir = fontstart + 12; + stbtt_int32 i; + for (i=0; i < num_tables; ++i) { + stbtt_uint32 loc = tabledir + 16*i; + if (stbtt_tag(data+loc+0, tag)) + return ttULONG(data+loc+8); + } + return 0; +} + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) +{ + // if it's just a font, there's only one valid index + if (stbtt__isfont(font_collection)) + return index == 0 ? 0 : -1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + stbtt_int32 n = ttLONG(font_collection+8); + if (index >= n) + return -1; + return ttULONG(font_collection+12+index*14); + } + } + return -1; +} + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) +{ + stbtt_uint8 *data = (stbtt_uint8 *) data2; + stbtt_uint32 cmap, t; + stbtt_int32 i,numTables; + + info->data = data; + info->fontstart = fontstart; + + cmap = stbtt__find_table(data, fontstart, "cmap"); // required + info->loca = stbtt__find_table(data, fontstart, "loca"); // required + info->head = stbtt__find_table(data, fontstart, "head"); // required + info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required + info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required + info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required + info->kern = stbtt__find_table(data, fontstart, "kern"); // not required + if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) + return 0; + + t = stbtt__find_table(data, fontstart, "maxp"); + if (t) + info->numGlyphs = ttUSHORT(data+t+4); + else + info->numGlyphs = 0xffff; + + // find a cmap encoding table we understand *now* to avoid searching + // later. (todo: could make this installable) + // the same regardless of glyph. + numTables = ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + stbtt_uint32 encoding_record = cmap + 4 + 8 * i; + // find an encoding we understand: + switch(ttUSHORT(data+encoding_record)) { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data+encoding_record+2)) { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + // MS/Unicode + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + break; + case STBTT_PLATFORM_ID_UNICODE: + // Mac/iOS has these + // all the encodingIDs are unicode, so we don't bother to check it + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = ttUSHORT(data+info->head + 50); + return 1; +} + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +{ + stbtt_uint8 *data = info->data; + stbtt_uint32 index_map = info->index_map; + + stbtt_uint16 format = ttUSHORT(data + index_map + 0); + if (format == 0) { // apple byte encoding + stbtt_int32 bytes = ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + stbtt_uint32 first = ttUSHORT(data + index_map + 6); + stbtt_uint32 count = ttUSHORT(data + index_map + 8); + if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) + return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); + return 0; + } else if (format == 2) { + STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean + return 0; + } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges + stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; + stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; + stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); + stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; + stbtt_uint16 item, offset, start, end; + + // do a binary search of the segments + stbtt_uint32 endCount = index_map + 14; + stbtt_uint32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + // they lie from endCount .. endCount + segCount + // but searchRange is the nearest power of two, so... + if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) + search += rangeShift*2; + + // now decrement to bias correctly to find smallest + search -= 2; + while (entrySelector) { + searchRange >>= 1; + start = ttUSHORT(data + search + searchRange*2 + segcount*2 + 2); + end = ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + item = (stbtt_uint16) ((search - endCount) >> 1); + + STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + end = ttUSHORT(data + index_map + 14 + 2 + 2*item); + if (unicode_codepoint < start) + return 0; + + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } else if (format == 12 || format == 13) { + stbtt_uint32 ngroups = ttULONG(data+index_map+12); + stbtt_int32 low,high; + low = 0; high = (stbtt_int32)ngroups; + // Binary search the right group. + while (low < high) { + stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high + stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); + stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); + if ((stbtt_uint32) unicode_codepoint < start_char) + high = mid; + else if ((stbtt_uint32) unicode_codepoint > end_char) + low = mid+1; + else { + stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return start_glyph + unicode_codepoint-start_char; + else // format == 13 + return start_glyph; + } + } + return 0; // not found + } + // @TODO + STBTT_assert(0); + return 0; +} + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +{ + return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +} + +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) +{ + v->type = type; + v->x = (stbtt_int16) x; + v->y = (stbtt_int16) y; + v->cx = (stbtt_int16) cx; + v->cy = (stbtt_int16) cy; +} + +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range + if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; // if length is 0, return -1 +} + +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + return 1; +} + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +{ + return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +} + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt_int16 numberOfContours; + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 1; + numberOfContours = ttSHORT(info->data + g); + return numberOfContours == 0; +} + +static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, + stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) +{ + if (start_off) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + return num_vertices; +} + +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + stbtt_int16 numberOfContours; + stbtt_uint8 *endPtsOfContours; + stbtt_uint8 *data = info->data; + stbtt_vertex *vertices=0; + int num_vertices=0; + int g = stbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = ttSHORT(data + g); + + if (numberOfContours > 0) { + stbtt_uint8 flags=0,flagcount; + stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; + stbtt_uint8 *points; + endPtsOfContours = (data + g + 10); + ins = ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); + + m = n + 2*numberOfContours; // a loose bound on how many vertices we might need + vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + // in first pass, we load uninterpreted data into the allocated array + // above, shifted to the end of the array so we won't overwrite it when + // we create our final data starting from the front + + off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated + + // first load flags + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + // now load x coordinates + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + stbtt_int16 dx = *points++; + x += (flags & 16) ? dx : -dx; // ??? + } else { + if (!(flags & 16)) { + x = x + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (stbtt_int16) x; + } + + // now load y coordinates + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + stbtt_int16 dy = *points++; + y += (flags & 32) ? dy : -dy; // ??? + } else { + if (!(flags & 32)) { + y = y + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (stbtt_int16) y; + } + + // now convert them to our format + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (stbtt_int16) vertices[off+i].x; + y = (stbtt_int16) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + // now start the new one + start_off = !(flags & 1); + if (start_off) { + // if we start off with an off-curve point, then when we need to find a point on the curve + // where we can start, and we need to save some state for when we wraparound. + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + // next point is also a curve point, so interpolate an on-point curve + sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; + sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; + } else { + // otherwise just use the next point as our start point + sx = (stbtt_int32) vertices[off+i+1].x; + sy = (stbtt_int32) vertices[off+i+1].y; + ++i; // we're using point i+1 as the starting point, so skip it + } + } else { + sx = x; + sy = y; + } + stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) { // if it's a curve + if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours == -1) { + // Compound shapes. + int more = 1; + stbtt_uint8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + stbtt_uint16 flags, gidx; + int comp_num_verts = 0, i; + stbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = ttSHORT(comp); comp+=2; + gidx = ttSHORT(comp); comp+=2; + + if (flags & 2) { // XY values + if (flags & 1) { // shorts + mtx[4] = ttSHORT(comp); comp+=2; + mtx[5] = ttSHORT(comp); comp+=2; + } else { + mtx[4] = ttCHAR(comp); comp+=1; + mtx[5] = ttCHAR(comp); comp+=1; + } + } + else { + // @TODO handle matching point + STBTT_assert(0); + } + if (flags & (1<<3)) { // WE_HAVE_A_SCALE + mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } + + // Find transformation scales. + m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + // Get indexed glyph. + comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + // Transform vertices. + for (i = 0; i < comp_num_verts; ++i) { + stbtt_vertex* v = &comp_verts[i]; + stbtt_vertex_type x,y; + x=v->x; y=v->y; + v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + // Append vertices. + tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); + if (!tmp) { + if (vertices) STBTT_free(vertices, info->userdata); + if (comp_verts) STBTT_free(comp_verts, info->userdata); + return 0; + } + if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); + if (vertices) STBTT_free(vertices, info->userdata); + vertices = tmp; + STBTT_free(comp_verts, info->userdata); + num_vertices += comp_num_verts; + } + // More components ? + more = flags & (1<<5); + } + } else if (numberOfContours < 0) { + // @TODO other compound variations? + STBTT_assert(0); + } else { + // numberOfCounters == 0, do nothing + } + + *pvertices = vertices; + return num_vertices; +} + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +{ + stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint8 *data = info->data + info->kern; + stbtt_uint32 needle, straw; + int l, r, m; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + l = 0; + r = ttUSHORT(data+10) - 1; + needle = glyph1 << 16 | glyph2; + while (l <= r) { + m = (l + r) >> 1; + straw = ttULONG(data+18+(m*6)); // note: unaligned read + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else + return ttSHORT(data+22+(m*6)); + } + return 0; +} + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +{ + if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs + return 0; + return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); +} + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +{ + stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +} + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +} + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) +{ + *x0 = ttSHORT(info->data + info->head + 36); + *y0 = ttSHORT(info->data + info->head + 38); + *x1 = ttSHORT(info->data + info->head + 40); + *y1 = ttSHORT(info->data + info->head + 42); +} + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +{ + int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); + return (float) height / fheight; +} + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) +{ + int unitsPerEm = ttUSHORT(info->data + info->head + 18); + return pixels / unitsPerEm; +} + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +{ + STBTT_free(v, info->userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// antialiasing software rasterizer +// + +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0,y0,x1,y1; + if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + // e.g. space character + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + // move to integral bboxes (treating pixels as little squares, what pixels get touched)? + if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); + if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); + if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); + if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); + } +} + +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); +} + +typedef struct stbtt__edge { + float x0,y0, x1,y1; + int invert; +} stbtt__edge; + +typedef struct stbtt__active_edge +{ + int x,dx; + float ey; + struct stbtt__active_edge *next; + int valid; +} stbtt__active_edge; + +#define FIXSHIFT 10 +#define FIX (1 << FIXSHIFT) +#define FIXMASK (FIX-1) + +static stbtt__active_edge *new_active(stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) STBTT_malloc(sizeof(*z), userdata); // @TODO: make a pool of these!!! + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(e->y0 <= start_point); + if (!z) return z; + // round dx down to avoid going too far + if (dxdy < 0) + z->dx = -STBTT_ifloor(FIX * -dxdy); + else + z->dx = STBTT_ifloor(FIX * dxdy); + z->x = STBTT_ifloor(FIX * (e->x0 + dxdy * (start_point - e->y0))); + z->x -= off_x * FIX; + z->ey = e->y1; + z->next = 0; + z->valid = e->invert ? 1 : -1; + return z; +} + +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +{ + // non-zero winding fill + int x0=0, w=0; + + while (e) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->valid; + } else { + int x1 = e->x; w += e->valid; + // if we went to zero, we need to draw + if (w == 0) { + int i = x0 >> FIXSHIFT; + int j = x1 >> FIXSHIFT; + + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> FIXSHIFT); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = scanline[i] + (stbtt_uint8) (((FIX - (x0 & FIXMASK)) * max_weight) >> FIXSHIFT); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & FIXMASK) * max_weight) >> FIXSHIFT); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = scanline[i] + (stbtt_uint8) max_weight; + } + } + } + } + + e = e->next; + } +} + +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__active_edge *active = NULL; + int y,j=0; + int max_weight = (255 / vsubsample); // weight per vertical scanline + int s; // vertical subsample index + unsigned char scanline_data[512], *scanline; + + if (result->w > 512) + scanline = (unsigned char *) STBTT_malloc(result->w, userdata); + else + scanline = scanline_data; + + y = off_y * vsubsample; + e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; + + while (j < result->h) { + STBTT_memset(scanline, 0, result->w); + for (s=0; s < vsubsample; ++s) { + // find center of pixel for this scanline + float scan_y = y + 0.5f; + stbtt__active_edge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y) { + *step = z->next; // delete from list + STBTT_assert(z->valid); + z->valid = 0; + STBTT_free(z, userdata); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for(;;) { + int changed=0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + stbtt__active_edge *t = *step; + stbtt__active_edge *q = t->next; + + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e->y0 <= scan_y) { + if (e->y1 > scan_y) { + stbtt__active_edge *z = new_active(e, off_x, scan_y, userdata); + // find insertion point + if (active == NULL) + active = z; + else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + ++e; + } + + // now process all active edges in XOR fashion + if (active) + stbtt__fill_active_edges(scanline, result->w, active, max_weight); + + ++y; + } + STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); + ++j; + } + + while (active) { + stbtt__active_edge *z = active; + active = active->next; + STBTT_free(z, userdata); + } + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} + +static int stbtt__edge_compare(const void *p, const void *q) +{ + stbtt__edge *a = (stbtt__edge *) p; + stbtt__edge *b = (stbtt__edge *) q; + + if (a->y0 < b->y0) return -1; + if (a->y0 > b->y0) return 1; + return 0; +} + +typedef struct +{ + float x,y; +} stbtt__point; + +static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + stbtt__edge *e; + int n,i,j,k,m; + int vsubsample = result->h < 8 ? 15 : 5; + // vsubsample should divide 255 evenly; otherwise we won't reach full opacity + + // now we have to blow out the windings into explicit edge lists + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + stbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + // skip the edge if horizontal + if (p[j].y == p[k].y) + continue; + // add edge from j to k to the list + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; + ++n; + } + } + + // now sort the edges by their highest point (should snap to integer, and then by x) + STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + + // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule + stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); + + STBTT_free(e, userdata); +} + +static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +{ + if (!points) return; // during first pass, it's unallocated + points[n].x = x; + points[n].y = y; +} + +// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching +static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +{ + // midpoint + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + // versus directly drawn line + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) // 65536 segments on one curve better be enough! + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA + stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +// returns number of contours +static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +{ + stbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + // count how many "moves" there are to get the contour count + for (i=0; i < num_verts; ++i) + if (vertices[i].type == STBTT_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + // make two passes through the points so we don't need to realloc + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case STBTT_vmove: + // start the next contour + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x,y); + break; + case STBTT_vline: + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x, y); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; +error: + STBTT_free(points, userdata); + STBTT_free(*contour_lengths, userdata); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count, *winding_lengths; + stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); + if (windings) { + stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); + STBTT_free(winding_lengths, userdata); + STBTT_free(windings, userdata); + } +} + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + int ix0,iy0,ix1,iy1; + stbtt__bitmap gbm; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) return NULL; + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); + + // now we get the size + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; // in case we error + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); + if (gbm.pixels) { + gbm.stride = gbm.w; + + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); + } + } + STBTT_free(vertices, info->userdata); + return gbm.pixels; +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) +{ + int ix0,iy0; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + stbtt__bitmap gbm; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); + + STBTT_free(vertices, info->userdata); +} + +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +{ + stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); +} + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-CRAPPY packing to keep source code small + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata) +{ + float scale; + int x,y,bottom_y, i; + stbtt_fontinfo f; + if (!stbtt_InitFont(&f, data, offset)) + return -1; + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + x=y=1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&f, pixel_height); + + for (i=0; i < num_chars; ++i) { + int advance, lsb, x0,y0,x1,y1,gw,gh; + int g = stbtt_FindGlyphIndex(&f, first_char + i); + stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); + gw = x1-x0; + gh = y1-y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; // advance to next row + if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row + return -i; + STBTT_assert(x+gw < pw); + STBTT_assert(y+gh < ph); + stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16) x; + chardata[i].y0 = (stbtt_int16) y; + chardata[i].x1 = (stbtt_int16) (x + gw); + chardata[i].y1 = (stbtt_int16) (y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float) x0; + chardata[i].yoff = (float) y0; + x = x + gw + 1; + if (y+gh+1 > bottom_y) + bottom_y = y+gh+1; + } + return bottom_y; +} + +STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5); + + q->x0 = round_x + d3d_bias; + q->y0 = round_y + d3d_bias; + q->x1 = round_x + b->x1 - b->x0 + d3d_bias; + q->y1 = round_y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// rectangle packing replacement routines if you don't have stb_rect_pack.h +// + +#ifndef STB_RECT_PACK_VERSION +#ifdef _MSC_VER +#define STBTT__NOTUSED(v) (void)(v) +#else +#define STBTT__NOTUSED(v) (void)sizeof(v) +#endif + +typedef int stbrp_coord; + +//////////////////////////////////////////////////////////////////////////////////// +// // +// // +// COMPILER WARNING ?!?!? // +// // +// // +// if you get a compile warning due to these symbols being defined more than // +// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // +// // +//////////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + int width,height; + int x,y,bottom_y; +} stbrp_context; + +typedef struct +{ + unsigned char x; +} stbrp_node; + +struct stbrp_rect +{ + stbrp_coord x,y; + int id,w,h,was_packed; +}; + +static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) +{ + con->width = pw; + con->height = ph; + con->x = 0; + con->y = 0; + con->bottom_y = 0; + STBTT__NOTUSED(nodes); + STBTT__NOTUSED(num_nodes); +} + +static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) +{ + int i; + for (i=0; i < num_rects; ++i) { + if (con->x + rects[i].w > con->width) { + con->x = 0; + con->y = con->bottom_y; + } + if (con->y + rects[i].h > con->height) + break; + rects[i].x = con->x; + rects[i].y = con->y; + rects[i].was_packed = 1; + con->x += rects[i].w; + if (con->y + rects[i].h > con->bottom_y) + con->bottom_y = con->y + rects[i].h; + } + for ( ; i < num_rects; ++i) + rects[i].was_packed = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If +// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) +{ + stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); + int num_nodes = pw - padding; + stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); + + if (context == NULL || nodes == NULL) { + if (context != NULL) STBTT_free(context, alloc_context); + if (nodes != NULL) STBTT_free(nodes , alloc_context); + return 0; + } + + spc->user_allocator_context = alloc_context; + spc->width = pw; + spc->height = ph; + spc->pixels = pixels; + spc->pack_info = context; + spc->nodes = nodes; + spc->padding = padding; + spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; + spc->h_oversample = 1; + spc->v_oversample = 1; + + stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); + + if (pixels) + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + + return 1; +} + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) +{ + STBTT_free(spc->nodes , spc->user_allocator_context); + STBTT_free(spc->pack_info, spc->user_allocator_context); +} + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) +{ + STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); + STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); + if (h_oversample <= STBTT_MAX_OVERSAMPLE) + spc->h_oversample = h_oversample; + if (v_oversample <= STBTT_MAX_OVERSAMPLE) + spc->v_oversample = v_oversample; +} + +#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) + +static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_w = w - kernel_width; + int j; + for (j=0; j < h; ++j) { + int i; + unsigned int total; + memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 4); + } + break; + default: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < w; ++i) { + STBTT_assert(pixels[i] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i] = (unsigned char) (total / kernel_width); + } + + pixels += stride_in_bytes; + } +} + +static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_h = h - kernel_width; + int j; + for (j=0; j < w; ++j) { + int i; + unsigned int total; + memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 4); + } + break; + default: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < h; ++i) { + STBTT_assert(pixels[i*stride_in_bytes] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + + pixels += 1; + } +} + +static float stbtt__oversample_shift(int oversample) +{ + if (!oversample) + return 0.0f; + + // The prefilter is a box filter of width "oversample", + // which shifts phase by (oversample - 1)/2 pixels in + // oversampled space. We want to shift in the opposite + // direction to counter this. + return (float)-(oversample - 1) / (2.0f * (float)oversample); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k; + + k=0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + for (j=0; j < ranges[i].num_chars_in_range; ++j) { + int x0,y0,x1,y1; + int glyph = stbtt_FindGlyphIndex(info,ranges[i].first_unicode_char_in_range + j); + if (glyph) { + stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + &x0,&y0,&x1,&y1); + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + } else { + rects[k].w = rects[k].h = 1; + } + ++k; + } + } + + return k; +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + float recip_h = 1.0f / spc->h_oversample; + float recip_v = 1.0f / spc->v_oversample; + float sub_x = stbtt__oversample_shift(spc->h_oversample); + float sub_y = stbtt__oversample_shift(spc->v_oversample); + int i,j,k, return_value = 1; + + k = 0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + for (j=0; j < ranges[i].num_chars_in_range; ++j) { + stbrp_rect *r = &rects[k]; + if (r->was_packed) { + stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; + int advance, lsb, x0,y0,x1,y1; + int glyph = stbtt_FindGlyphIndex(info, ranges[i].first_unicode_char_in_range + j); + stbrp_coord pad = (stbrp_coord) spc->padding; + + // pad on left and top + r->x += pad; + r->y += pad; + r->w -= pad; + r->h -= pad; + stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); + stbtt_GetGlyphBitmapBox(info, glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + &x0,&y0,&x1,&y1); + stbtt_MakeGlyphBitmapSubpixel(info, + spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w - spc->h_oversample+1, + r->h - spc->v_oversample+1, + spc->stride_in_bytes, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + glyph); + + if (spc->h_oversample > 1) + stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->h_oversample); + + if (spc->v_oversample > 1) + stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->v_oversample); + + bc->x0 = (stbtt_int16) r->x; + bc->y0 = (stbtt_int16) r->y; + bc->x1 = (stbtt_int16) (r->x + r->w); + bc->y1 = (stbtt_int16) (r->y + r->h); + bc->xadvance = scale * advance; + bc->xoff = (float) x0 * recip_h + sub_x; + bc->yoff = (float) y0 * recip_v + sub_y; + bc->xoff2 = (x0 + r->w) * recip_h + sub_x; + bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + } else { + return_value = 0; // if any fail, report failure + } + + ++k; + } + } + + return return_value; +} + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) +{ + stbtt_fontinfo info; + int i,j,n, return_value = 1; + stbrp_context *context = (stbrp_context *) spc->pack_info; + stbrp_rect *rects; + + // flag all characters as NOT packed + for (i=0; i < num_ranges; ++i) + for (j=0; j < ranges[i].num_chars_in_range; ++j) + ranges[i].chardata_for_range[j].x0 = + ranges[i].chardata_for_range[j].y0 = + ranges[i].chardata_for_range[j].x1 = + ranges[i].chardata_for_range[j].y1 = 0; + + n = 0; + for (i=0; i < num_ranges; ++i) + n += ranges[i].num_chars_in_range; + + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); + if (rects == NULL) + return 0; + + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); + + n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); + + stbrp_pack_rects(context, rects, n); + + return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); + + return return_value; +} + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) +{ + stbtt_pack_range range; + range.first_unicode_char_in_range = first_unicode_char_in_range; + range.num_chars_in_range = num_chars_in_range; + range.chardata_for_range = chardata_for_range; + range.font_size = font_size; + return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); +} + +STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) +{ + float ipw = 1.0f / pw, iph = 1.0f / ph; + stbtt_packedchar *b = chardata + char_index; + + if (align_to_integer) { + float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5); + float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5); + q->x0 = x; + q->y0 = y; + q->x1 = x + b->xoff2 - b->xoff; + q->y1 = y + b->yoff2 - b->yoff; + } else { + q->x0 = *xpos + b->xoff; + q->y0 = *ypos + b->yoff; + q->x1 = *xpos + b->xoff2; + q->y1 = *ypos + b->yoff2; + } + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + + +////////////////////////////////////////////////////////////////////////////// +// +// font name matching -- recommended not to use this +// + +// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 *s1, stbtt_int32 len1, const stbtt_uint8 *s2, stbtt_int32 len2) +{ + stbtt_int32 i=0; + + // convert utf16 to utf8 and compare the results while converting + while (len2) { + stbtt_uint16 ch = s2[0]*256 + s2[1]; + if (ch < 0x80) { + if (i >= len1) return -1; + if (s1[i++] != ch) return -1; + } else if (ch < 0x800) { + if (i+1 >= len1) return -1; + if (s1[i++] != 0xc0 + (ch >> 6)) return -1; + if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; + } else if (ch >= 0xd800 && ch < 0xdc00) { + stbtt_uint32 c; + stbtt_uint16 ch2 = s2[2]*256 + s2[3]; + if (i+3 >= len1) return -1; + c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; + if (s1[i++] != 0xf0 + (c >> 18)) return -1; + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; + s2 += 2; // plus another 2 below + len2 -= 2; + } else if (ch >= 0xdc00 && ch < 0xe000) { + return -1; + } else { + if (i+2 >= len1) return -1; + if (s1[i++] != 0xe0 + (ch >> 12)) return -1; + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; + } + s2 += 2; + len2 -= 2; + } + return i; +} + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, len2); +} + +// returns results in whatever encoding you request... but note that 2-byte encodings +// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +{ + stbtt_int32 i,count,stringOffset; + stbtt_uint8 *fc = font->data; + stbtt_uint32 offset = font->fontstart; + stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return NULL; + + count = ttUSHORT(fc+nm+2); + stringOffset = nm + ttUSHORT(fc+nm+4); + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) + && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { + *length = ttUSHORT(fc+loc+8); + return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); + } + } + return NULL; +} + +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +{ + stbtt_int32 i; + stbtt_int32 count = ttUSHORT(fc+nm+2); + stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); + + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + stbtt_int32 id = ttUSHORT(fc+loc+6); + if (id == target_id) { + // find the encoding + stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); + + // is this a Unicode encoding? + if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { + stbtt_int32 slen = ttUSHORT(fc+loc+8); + stbtt_int32 off = ttUSHORT(fc+loc+10); + + // check if there's a prefix match + stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); + if (matchlen >= 0) { + // check for target_id+1 immediately following, with same encoding & language + if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { + slen = ttUSHORT(fc+loc+12+8); + off = ttUSHORT(fc+loc+12+10); + if (slen == 0) { + if (matchlen == nlen) + return 1; + } else if (matchlen < nlen && name[matchlen] == ' ') { + ++matchlen; + if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + return 1; + } + } else { + // if nothing immediately following + if (matchlen == nlen) + return 1; + } + } + } + + // @TODO handle other encodings + } + } + return 0; +} + +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +{ + stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); + stbtt_uint32 nm,hd; + if (!stbtt__isfont(fc+offset)) return 0; + + // check italics/bold/underline flags in macStyle... + if (flags) { + hd = stbtt__find_table(fc, offset, "head"); + if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; + } + + nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return 0; + + if (flags) { + // if we checked the macStyle flags, then just check the family and ignore the subfamily + if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } else { + if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } + + return 0; +} + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) +{ + stbtt_int32 i; + for (i=0;;++i) { + stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); + if (off < 0) return off; + if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) + return off; + } +} + +#endif // STB_TRUETYPE_IMPLEMENTATION diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 110b80052c..d4538f2fdc 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -42,8 +42,6 @@ #include #include -#define STB_IMAGE_IMPLEMENTATION -#include namespace { const std::string _loggerCat = "GUI"; @@ -169,7 +167,7 @@ void GUI::initialize() { io.IniFilename = buffer; //io.IniSavingRate = 5.f; io.DeltaTime = 1.f / 60.f; - io.PixelCenterOffset = 0.5f; + //io.PixelCenterOffset = 0.5f; io.KeyMap[ImGuiKey_Tab] = SGCT_KEY_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. io.KeyMap[ImGuiKey_LeftArrow] = SGCT_KEY_LEFT; io.KeyMap[ImGuiKey_RightArrow] = SGCT_KEY_RIGHT; @@ -211,13 +209,11 @@ void GUI::initializeGL() { glBindTexture(GL_TEXTURE_2D, fontTex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - const void* png_data; - unsigned int png_size; - ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); - int tex_x, tex_y, tex_comp; - void* tex_data = stbi_load_from_memory((const unsigned char*)png_data, (int)png_size, &tex_x, &tex_y, &tex_comp, 0); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_x, tex_y, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex_data); - stbi_image_free(tex_data); + unsigned char* png_data; + int tex_x, tex_y; + ImGui::GetIO().Fonts->GetTexDataAsRGBA32(&png_data, &tex_x, &tex_y); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_x, tex_y, 0, GL_RGBA, GL_UNSIGNED_BYTE, png_data); + //stbi_image_free(tex_data); glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); From 98024ca29daafaa7fd23e33cc55e5eee05d7712e Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 24 May 2015 00:09:52 +0200 Subject: [PATCH 051/329] Remove warnings --- ext/ghoul | 2 +- include/openspace/util/progressbar.h | 12 ++++--- .../externalcontrol/randomexternalcontrol.cpp | 7 ++-- src/network/networkengine.cpp | 2 +- src/rendering/renderengine.cpp | 35 ++++++++++--------- src/util/powerscaledcoordinate.cpp | 5 +++ src/util/powerscaledscalar.cpp | 6 +++- src/util/progressbar.cpp | 3 -- 8 files changed, 42 insertions(+), 30 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index 12aba7d7dc..ef3241fe4b 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 12aba7d7dc13e2cb33d6bf70466ea9d7b24f5704 +Subproject commit ef3241fe4bb4da19c6fe167cad257f5230987234 diff --git a/include/openspace/util/progressbar.h b/include/openspace/util/progressbar.h index f26df46bef..789bb03766 100644 --- a/include/openspace/util/progressbar.h +++ b/include/openspace/util/progressbar.h @@ -31,19 +31,21 @@ namespace openspace { class ProgressBar { public: - ProgressBar(int end, int width = 70); - ProgressBar(int end, int width, std::ostream& stream); + ProgressBar(int end, int width = 70, std::ostream& stream = std::cout); ~ProgressBar(); + ProgressBar& operator=(const ProgressBar& rhs) = delete; + void print(int current); + private: int _width; int _previous; int _end; std::ostream& _stream; -}; // class ProgressBar +}; -} // namespace opensapce +} // namespace openspace -#endif \ No newline at end of file +#endif // __PROGRESSBAR_H__ diff --git a/src/interaction/externalcontrol/randomexternalcontrol.cpp b/src/interaction/externalcontrol/randomexternalcontrol.cpp index b15446fb24..2433f9d6a6 100644 --- a/src/interaction/externalcontrol/randomexternalcontrol.cpp +++ b/src/interaction/externalcontrol/randomexternalcontrol.cpp @@ -21,11 +21,12 @@ void *updatedx(void * arg) { //printf("Hello world!\n"); *dx = *dx + 0.5; - // random sleep time - int diff = rand() % 200; #ifndef __WIN32__ - usleep(10000*diff); + // random sleep time + int diff = rand() % 200; + + usleep(10000*diff); #endif } delete p; diff --git a/src/network/networkengine.cpp b/src/network/networkengine.cpp index 2d802e69c9..cc32a43418 100644 --- a/src/network/networkengine.cpp +++ b/src/network/networkengine.cpp @@ -48,7 +48,7 @@ namespace { namespace openspace { NetworkEngine::NetworkEngine() - : _lastAssignedIdentifier(-1) // -1 is okay as we assign one identifier in this ctor + : _lastAssignedIdentifier(MessageIdentifier(-1)) // -1 is okay as we assign one identifier in this ctor , _shouldPublishStatusMessage(true) { static_assert( diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 650cc92d4b..764dee30f4 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -386,16 +386,15 @@ void RenderEngine::render(const glm::mat4 &projectionMatrix, const glm::mat4 &vi #define PrintColorText(__i__, __format__, __size__, __color__) Freetype::print(font, __size__, static_cast(startY - font_size_mono * __i__ * 2), __color__, __format__); if (_onScreenInformation._node != -1) { - int thisId = sgct_core::ClusterManager::instance()->getThisNodeId(); + //int thisId = sgct_core::ClusterManager::instance()->getThisNodeId(); - if (thisId == _onScreenInformation._node) { - const unsigned int font_size_mono = _onScreenInformation._size; - int x1, xSize, y1, ySize; - const sgct_text::Font* font = sgct_text::FontManager::instance()->getFont(constants::fonts::keyMono, font_size_mono); - sgct::Engine::instance()->getActiveWindowPtr()->getCurrentViewportPixelCoords(x1, y1, xSize, ySize); - int startY = ySize - 2 * font_size_mono; - - } + //if (thisId == _onScreenInformation._node) { + //const unsigned int font_size_mono = _onScreenInformation._size; + //int x1, xSize, y1, ySize; + //const sgct_text::Font* font = sgct_text::FontManager::instance()->getFont(constants::fonts::keyMono, font_size_mono); + //sgct::Engine::instance()->getActiveWindowPtr()->getCurrentViewportPixelCoords(x1, y1, xSize, ySize); + //int startY = ySize - 2 * font_size_mono; + //} } // Print some useful information on the master viewport @@ -413,14 +412,14 @@ void RenderEngine::render(const glm::mat4 &projectionMatrix, const glm::mat4 &vi int x1, xSize, y1, ySize; sgct::Engine::instance()->getActiveWindowPtr()->getCurrentViewportPixelCoords(x1, y1, xSize, ySize); int startY = ySize - 2 * font_size_mono; - const glm::vec2& scaling = _mainCamera->scaling(); - const glm::vec3& viewdirection = _mainCamera->viewDirection(); - const psc& position = _mainCamera->position(); - const psc& origin = OsEng.interactionHandler()->focusNode()->worldPosition(); - const PowerScaledScalar& pssl = (position - origin).length(); + //const glm::vec2& scaling = _mainCamera->scaling(); + //const glm::vec3& viewdirection = _mainCamera->viewDirection(); + //const psc& position = _mainCamera->position(); + //const psc& origin = OsEng.interactionHandler()->focusNode()->worldPosition(); + //const PowerScaledScalar& pssl = (position - origin).length(); // Next 2 lines neccesary for instrument switching to work. - double currentTime = Time::ref().currentTime(); + //double currentTime = Time::ref().currentTime(); // GUI PRINT // Using a macro to shorten line length and increase readability @@ -837,7 +836,11 @@ void RenderEngine::storePerformanceMeasurements() { SceneGraphNode* node = scene()->allSceneGraphNodes()[i]; memset(layout->entries[i].name, 0, lengthName); - strcpy(layout->entries[i].name, node->name().c_str()); +#ifdef _MSC_VER + strcpy_s(layout->entries[i].name, node->name().length() + 1, node->name().c_str()); +#else + strcpy(layout->entries[i].name, node->name().c_str()); +#endif layout->entries[i].currentRenderTime = 0; layout->entries[i].currentUpdateRenderable = 0; diff --git a/src/util/powerscaledcoordinate.cpp b/src/util/powerscaledcoordinate.cpp index 7bb80e95b7..b55c361bb0 100644 --- a/src/util/powerscaledcoordinate.cpp +++ b/src/util/powerscaledcoordinate.cpp @@ -81,7 +81,12 @@ PowerScaledCoordinate // find out how many digits // TODO: profile the next two lines and replace with something more efficient (ab) +#ifdef _MSC_VER + sprintf_s(buff, 600, "%.0f", max); + //sprintf(buff, "%.0f", max); +#else sprintf(buff, "%.0f", max); +#endif size_t digits = strlen(buff); //digits += 3; diff --git a/src/util/powerscaledscalar.cpp b/src/util/powerscaledscalar.cpp index ce16ba45ca..1caf2d9e64 100644 --- a/src/util/powerscaledscalar.cpp +++ b/src/util/powerscaledscalar.cpp @@ -56,7 +56,11 @@ PowerScaledScalar PowerScaledScalar::CreatePSS(double d1) { double ad1 = std::abs(d1); // find out how many digits - sprintf ( buff, "%.0f", ad1); +#ifdef _MSC_VER + sprintf_s(buff, 30, "%.0f", ad1); +#else + sprintf(buff, "%.0f", ad1); +#endif size_t digits = strlen(buff)-1; // rescale and return diff --git a/src/util/progressbar.cpp b/src/util/progressbar.cpp index 6f71a15549..79604d4b7b 100644 --- a/src/util/progressbar.cpp +++ b/src/util/progressbar.cpp @@ -27,9 +27,6 @@ #include namespace openspace { -ProgressBar::ProgressBar(int end, int width) - : ProgressBar(end, width, std::cout) -{} ProgressBar::ProgressBar(int end, int width, std::ostream& stream) : _width(width) From cedf3796230e52de575aab2a0e3ad22ec97da282 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 24 May 2015 00:34:03 +0200 Subject: [PATCH 052/329] Add flags for compiler warnings/errors for Mac and Linux --- support/cmake/module_definition.cmake | 6 ++++++ support/cmake/support_macros.cmake | 15 ++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/support/cmake/module_definition.cmake b/support/cmake/module_definition.cmake index a75445cc17..45554fc3e0 100644 --- a/support/cmake/module_definition.cmake +++ b/support/cmake/module_definition.cmake @@ -103,6 +103,9 @@ function (set_common_compile_settings target_name) endif () target_compile_options(${library_name} PUBLIC "-stdlib=libc++") + if (OPENSPACE_WARNINGS_AS_ERRORS) + target_compile_options(${library_name} PUBLIC "-Werror") + endif () elseif (UNIX) include (CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) @@ -117,6 +120,9 @@ function (set_common_compile_settings target_name) endif () target_compile_definitions(${library_name} PUBLIC "-ggdb") + if (OPENSPACE_WARNINGS_AS_ERRORS) + target_compile_options(${library_name} PUBLIC "-Werror") + endif () endif () endfunction () diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 990b560b1e..4165d5bf2c 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -81,8 +81,13 @@ function (set_compile_settings) if (MSVC) target_compile_options(libOpenSpace PUBLIC "/MP" "/wd4201" "/wd4127") if (OPENSPACE_WARNINGS_AS_ERRORS) - target_compile_options(libOpenSpace PUBLIC "/WX") - target_compile_options(OpenSpace PUBLIC "/WX") + if (MSVC) + target_compile_options(libOpenSpace PUBLIC "/WX") + target_compile_options(OpenSpace PUBLIC "/WX") + else () + target_compile_options(libOpenSpace PUBLIC "-Werror") + target_compile_options(OpenSpace PUBLIC "-Werror") + endif () endif () set_target_properties(OpenSpace PROPERTIES LINK_FLAGS @@ -181,7 +186,7 @@ function (add_external_dependencies) if (MSVC) target_compile_options(ccmc PUBLIC "/W0" "/MP") else () - message("Fill me will values ---abock") + target_compile_options(ccmc PUBLIC "-w") endif () target_compile_definitions(ccmc PUBLIC "_SCL_SECURE_NO_WARNINGS") endif () @@ -191,7 +196,7 @@ function (add_external_dependencies) if (MSVC) target_compile_options(cdf PUBLIC "/W0" "/MP") else () - message("Fill me will values ---abock") + target_compile_options(ccmc PUBLIC "-w") endif () endif () set_property(TARGET cdf PROPERTY FOLDER "External") @@ -207,7 +212,7 @@ function (add_external_dependencies) if (MSVC) target_compile_options(Imgui PUBLIC "/W0" "/MP") else () - message("Fill me will values ---abock") + target_compile_options(ccmc PUBLIC "-w") endif () endif () endfunction () From 458d37d0e2bbf71e4a4542e110a4fbe2df578295 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 24 May 2015 00:44:42 +0200 Subject: [PATCH 053/329] Always use lower case module name in generated files --- support/cmake/module_definition.cmake | 2 +- support/cmake/support_macros.cmake | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/support/cmake/module_definition.cmake b/support/cmake/module_definition.cmake index 45554fc3e0..f69997c8b8 100644 --- a/support/cmake/module_definition.cmake +++ b/support/cmake/module_definition.cmake @@ -188,7 +188,7 @@ endfunction () function (write_module_name module_name) string(TOLOWER ${module_name} module_name_lower) - file(WRITE ${CMAKE_BINARY_DIR}/modules/${module_name}/modulename.cmake + file(WRITE ${CMAKE_BINARY_DIR}/modules/${module_name_lower}/modulename.cmake "set(MODULE_NAME ${module_name}Module)\n" "set(MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/${module_name_lower}module.h)" ) diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 4165d5bf2c..947b7bb833 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -342,9 +342,10 @@ function (handle_internal_modules) # Create registration file string(TOUPPER ${module} module_upper) + string(TOLOWER ${module{ module_lower) unset(MODULE_NAME) unset(MODULE_PATH) - include(${CMAKE_BINARY_DIR}/modules/${module}/modulename.cmake) + include(${CMAKE_BINARY_DIR}/modules/${module_lower}/modulename.cmake) list(APPEND MODULE_HEADERS #"#ifdef REGISTRATION_OPENSPACE${module_upper}MODULE\n" From d045da3d2ee73c9d88deee83dc50e088fff35e4c Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 24 May 2015 00:45:27 +0200 Subject: [PATCH 054/329] Fixing spelling error --- support/cmake/support_macros.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 947b7bb833..2a8a08ab91 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -342,7 +342,7 @@ function (handle_internal_modules) # Create registration file string(TOUPPER ${module} module_upper) - string(TOLOWER ${module{ module_lower) + string(TOLOWER ${module} module_lower) unset(MODULE_NAME) unset(MODULE_PATH) include(${CMAKE_BINARY_DIR}/modules/${module_lower}/modulename.cmake) From e65183eb493dd062a85c5c267c57c73945312e6a Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 24 May 2015 00:47:51 +0200 Subject: [PATCH 055/329] Fix -ggdb flags on Unix --- support/cmake/module_definition.cmake | 2 +- support/cmake/support_macros.cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/support/cmake/module_definition.cmake b/support/cmake/module_definition.cmake index f69997c8b8..1cfb40a8f1 100644 --- a/support/cmake/module_definition.cmake +++ b/support/cmake/module_definition.cmake @@ -119,7 +119,7 @@ function (set_common_compile_settings target_name) message(FATAL_ERROR "Compiler does not have C++11 support") endif () - target_compile_definitions(${library_name} PUBLIC "-ggdb") + target_compile_options(${library_name} PUBLIC "-ggdb") if (OPENSPACE_WARNINGS_AS_ERRORS) target_compile_options(${library_name} PUBLIC "-Werror") endif () diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 2a8a08ab91..a0acfa8590 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -135,7 +135,7 @@ function (set_compile_settings) message(FATAL_ERROR "Compiler does not have C++11 support") endif () - target_compile_definitions(libOpenSpace PUBLIC "-ggdb") + target_compile_options(libOpenSpace PUBLIC "-ggdb") endif () endfunction () From 83220d753d3a51e0fd9e0b46d2f7c633d57f5f25 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 24 May 2015 00:49:21 +0200 Subject: [PATCH 056/329] Use new Ghoul version --- ext/ghoul | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/ghoul b/ext/ghoul index ef3241fe4b..3bb3ff1476 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit ef3241fe4bb4da19c6fe167cad257f5230987234 +Subproject commit 3bb3ff1476faefcc537aedf32be573b21bc69f7d From fd0501d61cd955318c34d4981fe037e88c3ca98d Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 24 May 2015 14:30:18 +0200 Subject: [PATCH 057/329] Defined default constructor for RenderData --- include/openspace/util/updatestructures.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/openspace/util/updatestructures.h b/include/openspace/util/updatestructures.h index 92286f02a7..74e6792fe6 100644 --- a/include/openspace/util/updatestructures.h +++ b/include/openspace/util/updatestructures.h @@ -42,7 +42,13 @@ struct UpdateData { }; struct RenderData { - RenderData() = delete; + RenderData(const Camera& cam, psc pos, bool performance) + : camera(cam) + , position(std::move(pos)) + , doPerformanceMeasurement(std::move(performance)) + {} + + //RenderData() = default; RenderData& operator=(const RenderData& rhs) = delete; const Camera& camera; From 8304c007752ef345281bb5fed78f987d9466d7e2 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 24 May 2015 14:39:33 +0200 Subject: [PATCH 058/329] Don't set NODEFAULTLIB on non MSVC machines --- support/cmake/support_macros.cmake | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index a0acfa8590..8541a80564 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -252,9 +252,11 @@ function (handle_option_tests) ) target_link_libraries(OpenSpaceTest gtest libOpenSpace) - set_target_properties(OpenSpaceTest PROPERTIES LINK_FLAGS - "/NODEFAULTLIB:LIBCMTD.lib /NODEFAULTLIB:LIBCMT.lib" - ) + if (MSVC) + set_target_properties(OpenSpaceTest PROPERTIES LINK_FLAGS + "/NODEFAULTLIB:LIBCMTD.lib /NODEFAULTLIB:LIBCMT.lib" + ) + endif () set_property(TARGET OpenSpaceTest PROPERTY FOLDER "Unit Tests") endif (OPENSPACE_HAVE_TESTS) if (TARGET GhoulTest) From 98ffe369f4a91cd1c67eb7cc45889c660a258d82 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 24 May 2015 14:57:09 +0200 Subject: [PATCH 059/329] Add X11 linking --- support/cmake/support_macros.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 8541a80564..192f524180 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -158,7 +158,7 @@ function (add_external_dependencies) target_include_directories(libOpenSpace SYSTEM PUBLIC ${SGCT_INCLUDE_DIRECTORIES}) target_link_libraries(libOpenSpace ${SGCT_LIBRARIES}) if (UNIX AND (NOT APPLE)) - target_link_libraries(libOpenSpace Xcursor Xinerama) + target_link_libraries(libOpenSpace Xcursor Xinerama X11) endif () # Spice From 435b4034a76a9683f57b925393cf8b3cf9e5a122 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 24 May 2015 15:47:58 +0200 Subject: [PATCH 060/329] Update to new Ghoul version --- ext/ghoul | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/ghoul b/ext/ghoul index 3bb3ff1476..5a0eeedb39 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 3bb3ff1476faefcc537aedf32be573b21bc69f7d +Subproject commit 5a0eeedb390ffbe8faa50a77304a232c974feb05 From cbc49afbb42d37dde461aa4c1e122f0e50705bd3 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 24 May 2015 16:00:06 +0200 Subject: [PATCH 061/329] Remove warnings for cdf on linux --- support/cmake/support_macros.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 192f524180..0631a0a6db 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -196,7 +196,7 @@ function (add_external_dependencies) if (MSVC) target_compile_options(cdf PUBLIC "/W0" "/MP") else () - target_compile_options(ccmc PUBLIC "-w") + target_compile_options(cdf PUBLIC "-w") endif () endif () set_property(TARGET cdf PROPERTY FOLDER "External") From c7c55f917a3f80cf625f5646203953a34752c29e Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 25 May 2015 11:13:22 +0200 Subject: [PATCH 062/329] Place gtest project into it's own folder --- ext/ghoul | 2 +- support/cmake/support_macros.cmake | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ext/ghoul b/ext/ghoul index 5a0eeedb39..a865235d5e 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 5a0eeedb390ffbe8faa50a77304a232c974feb05 +Subproject commit a865235d5e932ec0d1b635f8de0a9cdc3f2e39d2 diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 0631a0a6db..7bb368e2e3 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -240,6 +240,7 @@ function (handle_option_tests) if (OPENSPACE_HAVE_TESTS) if (NOT TARGET gtest) add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul/ext/gtest) + set_property(TARGET gtest PROPERTY FOLDER "External") endif () file(GLOB_RECURSE OPENSPACE_TEST_FILES ${OPENSPACE_BASE_DIR}/tests/*.inl) @@ -260,6 +261,11 @@ function (handle_option_tests) set_property(TARGET OpenSpaceTest PROPERTY FOLDER "Unit Tests") endif (OPENSPACE_HAVE_TESTS) if (TARGET GhoulTest) + if (NOT TARGET gtest) + add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul/ext/gtest) + endif () + + set_property(TARGET gtest PROPERTY FOLDER "External") set_property(TARGET GhoulTest PROPERTY FOLDER "Unit Tests") endif () endfunction () From f128cfd745b8e585e6438e7457d26a99220bda3c Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 25 May 2015 12:21:25 +0200 Subject: [PATCH 063/329] Cmake cleanup Apply new Ghoul version --- ext/ghoul | 2 +- support/cmake/support_macros.cmake | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index a865235d5e..0c76e6c559 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit a865235d5e932ec0d1b635f8de0a9cdc3f2e39d2 +Subproject commit 0c76e6c55961d6c1bde2c5ee67ad2f9993d1123a diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 7bb368e2e3..417dd73d50 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -81,13 +81,8 @@ function (set_compile_settings) if (MSVC) target_compile_options(libOpenSpace PUBLIC "/MP" "/wd4201" "/wd4127") if (OPENSPACE_WARNINGS_AS_ERRORS) - if (MSVC) - target_compile_options(libOpenSpace PUBLIC "/WX") - target_compile_options(OpenSpace PUBLIC "/WX") - else () - target_compile_options(libOpenSpace PUBLIC "-Werror") - target_compile_options(OpenSpace PUBLIC "-Werror") - endif () + target_compile_options(libOpenSpace PUBLIC "/WX") + target_compile_options(OpenSpace PUBLIC "/WX") endif () set_target_properties(OpenSpace PROPERTIES LINK_FLAGS @@ -108,6 +103,11 @@ function (set_compile_settings) message(FATAL_ERROR "Compiler does not have C++11 support") endif () + if (OPENSPACE_WARNINGS_AS_ERRORS) + target_compile_options(libOpenSpace PUBLIC "-Werror") + target_compile_options(OpenSpace PUBLIC "-Werror") + endif () + target_compile_options(libOpenSpace PUBLIC "-stdlib=libc++") target_include_directories(libOpenSpace PUBLIC "/Developer/Headers/FlatCarbon") @@ -135,6 +135,11 @@ function (set_compile_settings) message(FATAL_ERROR "Compiler does not have C++11 support") endif () + if (OPENSPACE_WARNINGS_AS_ERRORS) + target_compile_options(libOpenSpace PUBLIC "-Werror") + target_compile_options(OpenSpace PUBLIC "-Werror") + endif () + target_compile_options(libOpenSpace PUBLIC "-ggdb") endif () endfunction () From a5e95ad45e9f89ec95a43ac082066cba5297d843 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 25 May 2015 16:46:36 +0200 Subject: [PATCH 064/329] Make Visual Leak Detector not crash --- CMakeLists.txt | 9 ++-- ext/ghoul | 2 +- ext/vld/lib/vld.lib | Bin 6348 -> 5620 bytes ext/vld/vld.h | 72 ++++++++--------------------- ext/vld/vld_def.h | 5 +- src/main.cpp | 6 +++ support/cmake/support_macros.cmake | 10 +++- 7 files changed, 40 insertions(+), 64 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e2d2bd6dec..8144a87665 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,10 +42,6 @@ set_build_output_directories() configure_openspace_version(0 1 0 "prerelease-5") option(OPENSPACE_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF) -if (MSVC) - option(OPENSPACE_ENABLE_VLD "Enable the Visual Leak Detector" OFF) - handle_option_vld() -endif () option(OPENSPACE_DISABLE_EXTERNAL_WARNINGS "Disable warnings in external libraries" ON) option(OPENSPACE_BUILD_GUI_APPLICATIONS "Build GUI Applications" OFF) @@ -56,6 +52,11 @@ create_openspace_targets() set_compile_settings() add_external_dependencies() +if (MSVC) + option(OPENSPACE_ENABLE_VLD "Enable the Visual Leak Detector" OFF) + handle_option_vld() +endif () + option(OPENSPACE_HAVE_TESTS "Activate the OpenSpace unit tests" ON) handle_option_tests() diff --git a/ext/ghoul b/ext/ghoul index 0c76e6c559..707a23bd17 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 0c76e6c55961d6c1bde2c5ee67ad2f9993d1123a +Subproject commit 707a23bd1775622c81546f263e47cf6f76569994 diff --git a/ext/vld/lib/vld.lib b/ext/vld/lib/vld.lib index 6325a37c8258c8c56191b60cf7bd547fc3dcc210..3675e9fc6be5135b418da76a1dbc6dc41ae88669 100644 GIT binary patch delta 1107 zcmZ9K&1(~35XPT0+a#Op_hxrPt7)ZBq*a>q%Qm&O1o0$=A_%>d2*raS2%beP6|^D> zg9k+gp@*t1269mp)LRcy@KUfB5&Q#k5Gi~Q>(Fv+){>^e}Y_h-gsbv8S0htXTy9;Ovpf3aNN{F`x2+U?kehv_Len@_ce>2uj z1lL-OQ4KLUPvERa50Z{LhGV?$!YqN z9Hu4Jqsz*>&iTft+VcfG;6?`6+77e&tAMd1kmBbfQ>XX_I|G zHEpn4gx}K_y^mgNzv5Cr+qxN6eb$f1s+z`7D@;Z6oGfVC0sZjZPq&6h!{@c(iG^yL85-Ln#R8YP-w3&S|Nzw<>mV zl;4+1*WEEGiB`FxmVAWeEd6xLQWl=&yqgo%gDmHu(CuU|UC9^YNuhm_6EvFjI_w{M zp?xpQI=%5a?73x4*dJrr;5$OM_*%d8`=o!g{#K9TN}Kf8KPhG2ktGbo-&<^O@%-5f q^wB;cGk9OE;vUYh>02-&rTA(bnkmG37qdSoguVB3rAx001OEWayZEvI literal 6348 zcmcIo%}*Og6o26(B<9PQU}&I~qedx_DvH5&K|<}i5E>K%ks+L-m<4ZQi-kpAgL;V6 zOO#WUQ;yBC*Bo+dPN>I7m3nDzJ@g-la?T}vvoo{%cD%c>vAfdq?7n$3zj?F2_x6pi zE$OxW%6GAEQvNGFznEJ{FQn5MzkXIcrsw8F&u$C=W&vVT00XN4gO34*-U1|-xJ=v! zU@}tSGM)r*{RU&-7FaT!HQ8g_Ul$n)AF!WSScE| zWu6+#x@p>VegD7~Lj$`|xn4l26z?+c#mo9RHU<4*!KZ-Z^i%~pgbQ1 zxPkH#=bA=+5y#9T)>RmU&tM2fp$`V29}+MQNf?1R48s`a#a@_zOAv$0a1ky*7xc6= zqMPL=xh>wU2rO@R-8-)b8R@4{W3oYw3}l=Dp8{i@5RWsJ**f$g3safH!YnvaA_K57 zr1_Fu-i8ofP2RXfLcze~NjwkAnH)$Oo$IUA+{dSZ{i;IEqQL>BTKQkUJEuYds`q?l z-^@TlQ1b>_c%43SC~9@}Aw+RUh110~QAk7d>;oN`2g%~Sv=&_h_!FE$DHjv<4g2V) z7d7>hEVPOd)y+)VG@aD=shTjeI4`Qh-B|f)H~evy-|D{rn1}c^Q#STa9$nuOkC4Zk zU*v;mHHiN(Q!VRt{ra5jVyW>@lz)Yy8(R%#&xBo0s4))$d_<9UOs_%etP9%&WiRYGxu`)&QL4R;0#pj^hZM470&O;M!z84b!LCkGR5CtOeym&_m?nbUMd+=ev=QP zWlZ^B7|_gn{Tvq&dVFYk*A_&%k@Di}9|YhpIe6D1;wvjRip3|3FM4U9$@t1hJ+u2; zK8Tjtb$oZ5-Pw+1uAFYlvE*K5VSWJKpn)oDB7;@j--Yo>9Od(1S zMG0`JWvqz(>*ZMf;)ei0%Wum~kP0ilK+i6^XxuG+x~FjDzPWvriA6zUrb9F}O_>4Hpu4$mSYF;20_uF`PtEaHenRFBkc`55(WO}L285vwhnsBAsT wag>uAg(LUPu_Pi}Uvbeok*%Zh*qDpNv$gZbFLC7@{0ONTQpo + #pragma comment(lib, "vld.lib") // Force a symbolic reference to the global VisualLeakDetector class object from @@ -126,17 +123,7 @@ __declspec(dllimport) void VLDGlobalEnable (); // // None. // -__declspec(dllimport) VLD_UINT VLDReportLeaks (); - -// VLDReportThreadLeaks - Report thread leaks up to the execution point. -// -// threadId: thread Id. -// -// Return Value: -// -// None. -// -__declspec(dllimport) VLD_UINT VLDReportThreadLeaks (VLD_UINT threadId); +__declspec(dllimport) UINT VLDReportLeaks (); // VLDGetLeaksCount - Return memory leaks count to the execution point. // @@ -144,17 +131,7 @@ __declspec(dllimport) VLD_UINT VLDReportThreadLeaks (VLD_UINT threadId); // // None. // -__declspec(dllimport) VLD_UINT VLDGetLeaksCount (); - -// VLDGetThreadLeaksCount - Return thread memory leaks count to the execution point. -// -// threadId: thread Id. -// -// Return Value: -// -// None. -// -__declspec(dllimport) VLD_UINT VLDGetThreadLeaksCount (VLD_UINT threadId); +__declspec(dllimport) UINT VLDGetLeaksCount (); // VLDMarkAllLeaksAsReported - Mark all leaks as reported. // @@ -164,16 +141,6 @@ __declspec(dllimport) VLD_UINT VLDGetThreadLeaksCount (VLD_UINT threadId); // __declspec(dllimport) void VLDMarkAllLeaksAsReported (); -// VLDMarkThreadLeaksAsReported - Mark thread leaks as reported. -// -// threadId: thread Id. -// -// Return Value: -// -// None. -// -__declspec(dllimport) void VLDMarkThreadLeaksAsReported (VLD_UINT threadId); - // VLDRefreshModules - Look for recently loaded DLLs and patch them if necessary. // @@ -193,7 +160,7 @@ __declspec(dllimport) void VLDRefreshModules(); // None. // -__declspec(dllimport) void VLDEnableModule(VLD_HMODULE module); +__declspec(dllimport) void VLDEnableModule(HMODULE module); // VLDDisableModule - Disable Memory leak checking on the specified module. @@ -204,7 +171,7 @@ __declspec(dllimport) void VLDEnableModule(VLD_HMODULE module); // // None. // -__declspec(dllimport) void VLDDisableModule(VLD_HMODULE module); +__declspec(dllimport) void VLDDisableModule(HMODULE module); // VLDGetOptions - Return all current options. // @@ -212,7 +179,7 @@ __declspec(dllimport) void VLDDisableModule(VLD_HMODULE module); // // Mask of current options. // -__declspec(dllimport) VLD_UINT VLDGetOptions(); +__declspec(dllimport) UINT VLDGetOptions(); // VLDGetReportFilename - Return current report filename. // @@ -222,7 +189,7 @@ __declspec(dllimport) VLD_UINT VLDGetOptions(); // // None. // -__declspec(dllimport) void VLDGetReportFilename(wchar_t *filename); +__declspec(dllimport) void VLDGetReportFilename(WCHAR *filename); // VLDSetOptions - Update the report options via function call rather than INI file. // @@ -244,7 +211,7 @@ __declspec(dllimport) void VLDGetReportFilename(wchar_t *filename); // // None. // -__declspec(dllimport) void VLDSetOptions(VLD_UINT option_mask, VLD_SIZET maxDataDump, VLD_UINT maxTraceFrames); +__declspec(dllimport) void VLDSetOptions(UINT option_mask, SIZE_T maxDataDump, UINT maxTraceFrames); // VLDSetModulesList - Set list of modules included/excluded in leak detection // depending on parameter "includeModules". @@ -257,7 +224,7 @@ __declspec(dllimport) void VLDSetOptions(VLD_UINT option_mask, VLD_SIZET maxData // // None. // -__declspec(dllimport) void VLDSetModulesList(const wchar_t *modules, VLD_BOOL includeModules); +__declspec(dllimport) void VLDSetModulesList(CONST WCHAR *modules, BOOL includeModules); // VLDGetModulesList - Return current list of included/excluded modules // depending on flag VLD_OPT_TRACE_INTERNAL_FRAMES. @@ -268,9 +235,9 @@ __declspec(dllimport) void VLDSetModulesList(const wchar_t *modules, VLD_BOOL in // // Return Value: // -// VLD_BOOL: TRUE if include modules, otherwise FALSE. +// BOOL: TRUE if include modules, otherwise FALSE. // -__declspec(dllimport) VLD_BOOL VLDGetModulesList(wchar_t *modules, VLD_UINT size); +__declspec(dllimport) BOOL VLDGetModulesList(WCHAR *modules, UINT size); // VLDSetReportOptions - Update the report options via function call rather than INI file. // @@ -286,7 +253,7 @@ __declspec(dllimport) VLD_BOOL VLDGetModulesList(wchar_t *modules, VLD_UINT size // // None. // -__declspec(dllimport) void VLDSetReportOptions(VLD_UINT option_mask, const wchar_t *filename); +__declspec(dllimport) void VLDSetReportOptions(UINT option_mask, CONST WCHAR *filename); // VLDSetReportHook - Installs or uninstalls a client-defined reporting function by hooking it // into the C run-time debug reporting process (debug version only). @@ -323,21 +290,18 @@ __declspec(dllexport) void VLDResolveCallstacks(); #define VLDEnable() #define VLDDisable() #define VLDRestore() -#define VLDReportLeaks() (0) -#define VLDReportThreadLeaks() (0) -#define VLDGetLeaksCount() (0) -#define VLDGetThreadLeaksCount() (0) +#define VLDReportLeaks() 0 +#define VLDGetLeaksCount() 0 #define VLDMarkAllLeaksAsReported() -#define VLDMarkThreadLeaksAsReported(a) #define VLDRefreshModules() #define VLDEnableModule(a) #define VLDDisableModule(b) -#define VLDGetOptions() (0) +#define VLDGetOptions() 0 #define VLDGetReportFilename(a) #define VLDSetOptions(a, b, c) #define VLDSetReportHook(a, b) #define VLDSetModulesList(a) -#define VLDGetModulesList(a, b) (FALSE) +#define VLDGetModulesList(a, b) FALSE #define VLDSetReportOptions(a, b) #endif // _DEBUG diff --git a/ext/vld/vld_def.h b/ext/vld/vld_def.h index 4b6d92ed89..93bbfaf131 100644 --- a/ext/vld/vld_def.h +++ b/ext/vld/vld_def.h @@ -1,7 +1,7 @@ //////////////////////////////////////////////////////////////////////////////// // // Visual Leak Detector - Import Library Header -// Copyright (c) 2005-2014 VLD Team +// Copyright (c) 2005-2013 VLD Team // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -23,8 +23,6 @@ #pragma once -#include - #define VLD_OPT_AGGREGATE_DUPLICATES 0x0001 // If set, aggregate duplicate leaks in the leak report. #define VLD_OPT_MODULE_LIST_INCLUDE 0x0002 // If set, modules in the module list are included, all others are excluded. #define VLD_OPT_REPORT_TO_DEBUGGER 0x0004 // If set, the memory leak report is sent to the debugger. @@ -39,6 +37,7 @@ #define VLD_OPT_REPORT_TO_STDOUT 0x0800 // If set, the memory leak report is sent to stdout. #define VLD_OPT_SKIP_HEAPFREE_LEAKS 0x1000 // If set, VLD skip HeapFree memory leaks. #define VLD_OPT_VALIDATE_HEAPFREE 0x2000 // If set, VLD verifies and reports heap consistency for HeapFree calls. +#define VLD_OPT_RELEASE_CRT_RUNTIME 0x4000 // If set, VLD treat CRT runtime as release version (use only with define VLD_FORCE_ENABLE). #define VLD_RPTHOOK_INSTALL 0 #define VLD_RPTHOOK_REMOVE 1 diff --git a/src/main.cpp b/src/main.cpp index a7c161752e..40d1c7549f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,6 +29,12 @@ #include #include +#ifdef _MSC_VER +#ifdef OPENSPACE_ENABLE_VLD +#include +#endif +#endif + sgct::Engine* _sgctEngine; // function pointer declarations diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 417dd73d50..25ec7b0d5d 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -226,8 +226,14 @@ endfunction () function (handle_option_vld) if (OPENSPACE_ENABLE_VLD) - target_link_libraries(libOpenSpace ${OPENSPACE_EXT_DIR}/vld/lib/vld.lib) - target_include_directories(libOpenSpace PUBLIC ${OPENSPACE_EXT_DIR}/vld) + target_compile_definitions(OpenSpace PUBLIC "OPENSPACE_ENABLE_VLD") + target_link_libraries(OpenSpace ${OPENSPACE_EXT_DIR}/vld/lib/vld.lib) + target_include_directories(OpenSpace PUBLIC ${OPENSPACE_EXT_DIR}/vld) + + copy_files(OpenSpace + "${OPENSPACE_EXT_DIR}/vld/bin/vld_x64.dll" + "${OPENSPACE_EXT_DIR/vld/bin/Microsoft.DTfW.DHL.manifest" + ) endif () endfunction () From 018ba9865507cf6c2132dcdf7f2da5ecad6e71e5 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 25 May 2015 16:47:57 +0200 Subject: [PATCH 065/329] Fix typo --- support/cmake/support_macros.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 25ec7b0d5d..c92a109715 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -232,7 +232,7 @@ function (handle_option_vld) copy_files(OpenSpace "${OPENSPACE_EXT_DIR}/vld/bin/vld_x64.dll" - "${OPENSPACE_EXT_DIR/vld/bin/Microsoft.DTfW.DHL.manifest" + "${OPENSPACE_EXT_DIR}/vld/bin/Microsoft.DTfW.DHL.manifest" ) endif () endfunction () From b84063181355558ec1c00f9f698a9f8194a8f44e Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 25 May 2015 17:28:11 +0200 Subject: [PATCH 066/329] Making Visual Leak Detector work --- ext/vld/lib/vld.lib | Bin 5620 -> 6348 bytes ext/vld/vld.h | 72 +++++++++++++++++++++-------- ext/vld/vld_def.h | 5 +- src/engine/openspaceengine.cpp | 7 +++ src/main.cpp | 6 --- support/cmake/support_macros.cmake | 11 ++--- 6 files changed, 68 insertions(+), 33 deletions(-) diff --git a/ext/vld/lib/vld.lib b/ext/vld/lib/vld.lib index 3675e9fc6be5135b418da76a1dbc6dc41ae88669..6325a37c8258c8c56191b60cf7bd547fc3dcc210 100644 GIT binary patch literal 6348 zcmcIo%}*Og6o26(B<9PQU}&I~qedx_DvH5&K|<}i5E>K%ks+L-m<4ZQi-kpAgL;V6 zOO#WUQ;yBC*Bo+dPN>I7m3nDzJ@g-la?T}vvoo{%cD%c>vAfdq?7n$3zj?F2_x6pi zE$OxW%6GAEQvNGFznEJ{FQn5MzkXIcrsw8F&u$C=W&vVT00XN4gO34*-U1|-xJ=v! zU@}tSGM)r*{RU&-7FaT!HQ8g_Ul$n)AF!WSScE| zWu6+#x@p>VegD7~Lj$`|xn4l26z?+c#mo9RHU<4*!KZ-Z^i%~pgbQ1 zxPkH#=bA=+5y#9T)>RmU&tM2fp$`V29}+MQNf?1R48s`a#a@_zOAv$0a1ky*7xc6= zqMPL=xh>wU2rO@R-8-)b8R@4{W3oYw3}l=Dp8{i@5RWsJ**f$g3safH!YnvaA_K57 zr1_Fu-i8ofP2RXfLcze~NjwkAnH)$Oo$IUA+{dSZ{i;IEqQL>BTKQkUJEuYds`q?l z-^@TlQ1b>_c%43SC~9@}Aw+RUh110~QAk7d>;oN`2g%~Sv=&_h_!FE$DHjv<4g2V) z7d7>hEVPOd)y+)VG@aD=shTjeI4`Qh-B|f)H~evy-|D{rn1}c^Q#STa9$nuOkC4Zk zU*v;mHHiN(Q!VRt{ra5jVyW>@lz)Yy8(R%#&xBo0s4))$d_<9UOs_%etP9%&WiRYGxu`)&QL4R;0#pj^hZM470&O;M!z84b!LCkGR5CtOeym&_m?nbUMd+=ev=QP zWlZ^B7|_gn{Tvq&dVFYk*A_&%k@Di}9|YhpIe6D1;wvjRip3|3FM4U9$@t1hJ+u2; zK8Tjtb$oZ5-Pw+1uAFYlvE*K5VSWJKpn)oDB7;@j--Yo>9Od(1S zMG0`JWvqz(>*ZMf;)ei0%Wum~kP0ilK+i6^XxuG+x~FjDzPWvriA6zUrb9F}O_>4Hpu4$mSYF;20_uF`PtEaHenRFBkc`55(WO}L285vwhnsBAsT wag>uAg(LUPu_Pi}Uvbeok*%Zh*qDpNv$gZbFLC7@{0ONTQpoq%Qm&O1o0$=A_%>d2*raS2%beP6|^D> zg9k+gp@*t1269mp)LRcy@KUfB5&Q#k5Gi~Q>(Fv+){>^e}Y_h-gsbv8S0htXTy9;Ovpf3aNN{F`x2+U?kehv_Len@_ce>2uj z1lL-OQ4KLUPvERa50Z{LhGV?$!YqN z9Hu4Jqsz*>&iTft+VcfG;6?`6+77e&tAMd1kmBbfQ>XX_I|G zHEpn4gx}K_y^mgNzv5Cr+qxN6eb$f1s+z`7D@;Z6oGfVC0sZjZPq&6h!{@c(iG^yL85-Ln#R8YP-w3&S|Nzw<>mV zl;4+1*WEEGiB`FxmVAWeEd6xLQWl=&yqgo%gDmHu(CuU|UC9^YNuhm_6EvFjI_w{M zp?xpQI=%5a?73x4*dJrr;5$OM_*%d8`=o!g{#K9TN}Kf8KPhG2ktGbo-&<^O@%-5f q^wB;cGk9OE;vUYh>02-&rTA(bnkmG37qdSoguVB3rAx001OEWayZEvI diff --git a/ext/vld/vld.h b/ext/vld/vld.h index a24a09f91f..52686dcb8c 100644 --- a/ext/vld/vld.h +++ b/ext/vld/vld.h @@ -1,7 +1,7 @@ //////////////////////////////////////////////////////////////////////////////// // // Visual Leak Detector - Import Library Header -// Copyright (c) 2005-2013 VLD Team +// Copyright (c) 2005-2014 VLD Team // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -25,9 +25,12 @@ #include "vld_def.h" -#if defined _DEBUG || defined VLD_FORCE_ENABLE +typedef int VLD_BOOL; +typedef unsigned int VLD_UINT; +typedef size_t VLD_SIZET; +typedef void* VLD_HMODULE; -#include +#if defined _DEBUG || defined VLD_FORCE_ENABLE #pragma comment(lib, "vld.lib") @@ -123,7 +126,17 @@ __declspec(dllimport) void VLDGlobalEnable (); // // None. // -__declspec(dllimport) UINT VLDReportLeaks (); +__declspec(dllimport) VLD_UINT VLDReportLeaks (); + +// VLDReportThreadLeaks - Report thread leaks up to the execution point. +// +// threadId: thread Id. +// +// Return Value: +// +// None. +// +__declspec(dllimport) VLD_UINT VLDReportThreadLeaks (VLD_UINT threadId); // VLDGetLeaksCount - Return memory leaks count to the execution point. // @@ -131,7 +144,17 @@ __declspec(dllimport) UINT VLDReportLeaks (); // // None. // -__declspec(dllimport) UINT VLDGetLeaksCount (); +__declspec(dllimport) VLD_UINT VLDGetLeaksCount (); + +// VLDGetThreadLeaksCount - Return thread memory leaks count to the execution point. +// +// threadId: thread Id. +// +// Return Value: +// +// None. +// +__declspec(dllimport) VLD_UINT VLDGetThreadLeaksCount (VLD_UINT threadId); // VLDMarkAllLeaksAsReported - Mark all leaks as reported. // @@ -141,6 +164,16 @@ __declspec(dllimport) UINT VLDGetLeaksCount (); // __declspec(dllimport) void VLDMarkAllLeaksAsReported (); +// VLDMarkThreadLeaksAsReported - Mark thread leaks as reported. +// +// threadId: thread Id. +// +// Return Value: +// +// None. +// +__declspec(dllimport) void VLDMarkThreadLeaksAsReported (VLD_UINT threadId); + // VLDRefreshModules - Look for recently loaded DLLs and patch them if necessary. // @@ -160,7 +193,7 @@ __declspec(dllimport) void VLDRefreshModules(); // None. // -__declspec(dllimport) void VLDEnableModule(HMODULE module); +__declspec(dllimport) void VLDEnableModule(VLD_HMODULE module); // VLDDisableModule - Disable Memory leak checking on the specified module. @@ -171,7 +204,7 @@ __declspec(dllimport) void VLDEnableModule(HMODULE module); // // None. // -__declspec(dllimport) void VLDDisableModule(HMODULE module); +__declspec(dllimport) void VLDDisableModule(VLD_HMODULE module); // VLDGetOptions - Return all current options. // @@ -179,7 +212,7 @@ __declspec(dllimport) void VLDDisableModule(HMODULE module); // // Mask of current options. // -__declspec(dllimport) UINT VLDGetOptions(); +__declspec(dllimport) VLD_UINT VLDGetOptions(); // VLDGetReportFilename - Return current report filename. // @@ -189,7 +222,7 @@ __declspec(dllimport) UINT VLDGetOptions(); // // None. // -__declspec(dllimport) void VLDGetReportFilename(WCHAR *filename); +__declspec(dllimport) void VLDGetReportFilename(wchar_t *filename); // VLDSetOptions - Update the report options via function call rather than INI file. // @@ -211,7 +244,7 @@ __declspec(dllimport) void VLDGetReportFilename(WCHAR *filename); // // None. // -__declspec(dllimport) void VLDSetOptions(UINT option_mask, SIZE_T maxDataDump, UINT maxTraceFrames); +__declspec(dllimport) void VLDSetOptions(VLD_UINT option_mask, VLD_SIZET maxDataDump, VLD_UINT maxTraceFrames); // VLDSetModulesList - Set list of modules included/excluded in leak detection // depending on parameter "includeModules". @@ -224,7 +257,7 @@ __declspec(dllimport) void VLDSetOptions(UINT option_mask, SIZE_T maxDataDump, U // // None. // -__declspec(dllimport) void VLDSetModulesList(CONST WCHAR *modules, BOOL includeModules); +__declspec(dllimport) void VLDSetModulesList(const wchar_t *modules, VLD_BOOL includeModules); // VLDGetModulesList - Return current list of included/excluded modules // depending on flag VLD_OPT_TRACE_INTERNAL_FRAMES. @@ -235,9 +268,9 @@ __declspec(dllimport) void VLDSetModulesList(CONST WCHAR *modules, BOOL includeM // // Return Value: // -// BOOL: TRUE if include modules, otherwise FALSE. +// VLD_BOOL: TRUE if include modules, otherwise FALSE. // -__declspec(dllimport) BOOL VLDGetModulesList(WCHAR *modules, UINT size); +__declspec(dllimport) VLD_BOOL VLDGetModulesList(wchar_t *modules, VLD_UINT size); // VLDSetReportOptions - Update the report options via function call rather than INI file. // @@ -253,7 +286,7 @@ __declspec(dllimport) BOOL VLDGetModulesList(WCHAR *modules, UINT size); // // None. // -__declspec(dllimport) void VLDSetReportOptions(UINT option_mask, CONST WCHAR *filename); +__declspec(dllimport) void VLDSetReportOptions(VLD_UINT option_mask, const wchar_t *filename); // VLDSetReportHook - Installs or uninstalls a client-defined reporting function by hooking it // into the C run-time debug reporting process (debug version only). @@ -290,18 +323,21 @@ __declspec(dllexport) void VLDResolveCallstacks(); #define VLDEnable() #define VLDDisable() #define VLDRestore() -#define VLDReportLeaks() 0 -#define VLDGetLeaksCount() 0 +#define VLDReportLeaks() (0) +#define VLDReportThreadLeaks() (0) +#define VLDGetLeaksCount() (0) +#define VLDGetThreadLeaksCount() (0) #define VLDMarkAllLeaksAsReported() +#define VLDMarkThreadLeaksAsReported(a) #define VLDRefreshModules() #define VLDEnableModule(a) #define VLDDisableModule(b) -#define VLDGetOptions() 0 +#define VLDGetOptions() (0) #define VLDGetReportFilename(a) #define VLDSetOptions(a, b, c) #define VLDSetReportHook(a, b) #define VLDSetModulesList(a) -#define VLDGetModulesList(a, b) FALSE +#define VLDGetModulesList(a, b) (FALSE) #define VLDSetReportOptions(a, b) #endif // _DEBUG diff --git a/ext/vld/vld_def.h b/ext/vld/vld_def.h index 93bbfaf131..4b6d92ed89 100644 --- a/ext/vld/vld_def.h +++ b/ext/vld/vld_def.h @@ -1,7 +1,7 @@ //////////////////////////////////////////////////////////////////////////////// // // Visual Leak Detector - Import Library Header -// Copyright (c) 2005-2013 VLD Team +// Copyright (c) 2005-2014 VLD Team // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -23,6 +23,8 @@ #pragma once +#include + #define VLD_OPT_AGGREGATE_DUPLICATES 0x0001 // If set, aggregate duplicate leaks in the leak report. #define VLD_OPT_MODULE_LIST_INCLUDE 0x0002 // If set, modules in the module list are included, all others are excluded. #define VLD_OPT_REPORT_TO_DEBUGGER 0x0004 // If set, the memory leak report is sent to the debugger. @@ -37,7 +39,6 @@ #define VLD_OPT_REPORT_TO_STDOUT 0x0800 // If set, the memory leak report is sent to stdout. #define VLD_OPT_SKIP_HEAPFREE_LEAKS 0x1000 // If set, VLD skip HeapFree memory leaks. #define VLD_OPT_VALIDATE_HEAPFREE 0x2000 // If set, VLD verifies and reports heap consistency for HeapFree calls. -#define VLD_OPT_RELEASE_CRT_RUNTIME 0x4000 // If set, VLD treat CRT runtime as release version (use only with define VLD_FORCE_ENABLE). #define VLD_RPTHOOK_INSTALL 0 #define VLD_RPTHOOK_REMOVE 1 diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 6a689f4400..1fe1bcc9d7 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -63,6 +63,13 @@ #include #include +#ifdef _MSC_VER +#ifdef OPENSPACE_ENABLE_VLD +#include +#endif +#endif + + using namespace openspace::scripting; using namespace ghoul::filesystem; using namespace ghoul::logging; diff --git a/src/main.cpp b/src/main.cpp index 40d1c7549f..a7c161752e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,12 +29,6 @@ #include #include -#ifdef _MSC_VER -#ifdef OPENSPACE_ENABLE_VLD -#include -#endif -#endif - sgct::Engine* _sgctEngine; // function pointer declarations diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index c92a109715..714902e4c4 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -226,14 +226,11 @@ endfunction () function (handle_option_vld) if (OPENSPACE_ENABLE_VLD) - target_compile_definitions(OpenSpace PUBLIC "OPENSPACE_ENABLE_VLD") - target_link_libraries(OpenSpace ${OPENSPACE_EXT_DIR}/vld/lib/vld.lib) - target_include_directories(OpenSpace PUBLIC ${OPENSPACE_EXT_DIR}/vld) + target_compile_definitions(libOpenSpace PUBLIC "OPENSPACE_ENABLE_VLD") + target_link_libraries(libOpenSpace ${OPENSPACE_EXT_DIR}/vld/lib/vld.lib) + target_include_directories(libOpenSpace PUBLIC ${OPENSPACE_EXT_DIR}/vld) - copy_files(OpenSpace - "${OPENSPACE_EXT_DIR}/vld/bin/vld_x64.dll" - "${OPENSPACE_EXT_DIR}/vld/bin/Microsoft.DTfW.DHL.manifest" - ) + copy_files(OpenSpace "${OPENSPACE_EXT_DIR}/vld/bin/vld_x64.dll") endif () endfunction () From 659326b9a981547977216614fff92f0efcbbb460 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 25 May 2015 19:37:56 +0200 Subject: [PATCH 067/329] Remove 1500+ memory leaks in OpenSpace and Ghoul --- ext/ghoul | 2 +- modules/base/rendering/renderablemodel.cpp | 9 +- modules/base/rendering/renderablepath.cpp | 7 ++ modules/base/rendering/renderableplane.cpp | 11 ++ modules/base/rendering/renderableplane.h | 2 +- modules/base/rendering/renderablesphere.cpp | 10 +- modules/base/rendering/renderablesphere.h | 1 - modules/base/rendering/renderablestars.cpp | 1 + modules/base/rendering/renderabletrail.cpp | 4 + modules/base/rendering/renderabletrail.h | 1 + openspace.cfg | 4 +- src/engine/moduleengine.cpp | 1 + src/engine/openspaceengine.cpp | 8 +- src/main.cpp | 124 ++++++++++---------- src/properties/propertyowner.cpp | 33 ++---- src/scene/scenegraph.cpp | 13 ++ src/scene/scenegraphnode.cpp | 1 + 17 files changed, 136 insertions(+), 96 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index 707a23bd17..d2c5eb2aa0 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 707a23bd1775622c81546f263e47cf6f76569994 +Subproject commit d2c5eb2aa00d81776e952e583cd8e667f0704c47 diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index ff22486f0d..8151fb54af 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -148,12 +148,13 @@ bool RenderableModel::deinitialize() { if (_geometry) { _geometry->deinitialize(); delete _geometry; + _geometry = nullptr; } - if (_texture) - delete _texture; + delete _texture; + _texture = nullptr; - _geometry = nullptr; - _texture = nullptr; + delete _programObject; + _programObject = nullptr; return true; } diff --git a/modules/base/rendering/renderablepath.cpp b/modules/base/rendering/renderablepath.cpp index 0654ecc36c..4321bcccf5 100644 --- a/modules/base/rendering/renderablepath.cpp +++ b/modules/base/rendering/renderablepath.cpp @@ -119,7 +119,14 @@ bool RenderablePath::initialize() { bool RenderablePath::deinitialize() { glDeleteVertexArrays(1, &_vaoID); + _vaoID = 0; + glDeleteBuffers(1, &_vBufferID); + _vBufferID = 0; + + delete _programObject; + _programObject = nullptr; + return true; } diff --git a/modules/base/rendering/renderableplane.cpp b/modules/base/rendering/renderableplane.cpp index aac8d1fb84..4bb27bf458 100644 --- a/modules/base/rendering/renderableplane.cpp +++ b/modules/base/rendering/renderableplane.cpp @@ -109,6 +109,7 @@ RenderablePlane::RenderablePlane(const ghoul::Dictionary& dictionary) RenderablePlane::~RenderablePlane() { delete _textureFile; + _textureFile = nullptr; } bool RenderablePlane::isReady() const { @@ -142,9 +143,19 @@ bool RenderablePlane::initialize() { bool RenderablePlane::deinitialize() { glDeleteVertexArrays(1, &_quad); _quad = 0; + glDeleteBuffers(1, &_vertexPositionBuffer); _vertexPositionBuffer = 0; + delete _texture; + _texture = nullptr; + + delete _textureFile; + _textureFile = nullptr; + + delete _shader; + _shader = nullptr; + return true; } diff --git a/modules/base/rendering/renderableplane.h b/modules/base/rendering/renderableplane.h index 105ca7c7f9..60effc4c5e 100644 --- a/modules/base/rendering/renderableplane.h +++ b/modules/base/rendering/renderableplane.h @@ -52,7 +52,7 @@ class RenderablePlane : public Renderable { public: RenderablePlane(const ghoul::Dictionary& dictionary); - ~RenderablePlane(); + ~RenderablePlane(); bool initialize() override; bool deinitialize() override; diff --git a/modules/base/rendering/renderablesphere.cpp b/modules/base/rendering/renderablesphere.cpp index 70c6dee647..ae65124ca5 100644 --- a/modules/base/rendering/renderablesphere.cpp +++ b/modules/base/rendering/renderablesphere.cpp @@ -106,9 +106,6 @@ RenderableSphere::RenderableSphere(const ghoul::Dictionary& dictionary) _texturePath.onChange(std::bind(&RenderableSphere::loadTexture, this)); } -RenderableSphere::~RenderableSphere() { -} - bool RenderableSphere::isReady() const { return (_sphere != nullptr) && (_shader != nullptr) && (_texture != nullptr); } @@ -132,7 +129,14 @@ bool RenderableSphere::initialize() { bool RenderableSphere::deinitialize() { delete _sphere; + _sphere = nullptr; + delete _texture; + _texture = nullptr; + + delete _shader; + _shader = nullptr; + return true; } diff --git a/modules/base/rendering/renderablesphere.h b/modules/base/rendering/renderablesphere.h index e8b7defddc..06f15ae1ab 100644 --- a/modules/base/rendering/renderablesphere.h +++ b/modules/base/rendering/renderablesphere.h @@ -41,7 +41,6 @@ class PowerScaledSphere; class RenderableSphere : public Renderable { public: RenderableSphere(const ghoul::Dictionary& dictionary); - ~RenderableSphere(); bool initialize() override; bool deinitialize() override; diff --git a/modules/base/rendering/renderablestars.cpp b/modules/base/rendering/renderablestars.cpp index 1bc08cff79..a9a4cc8832 100644 --- a/modules/base/rendering/renderablestars.cpp +++ b/modules/base/rendering/renderablestars.cpp @@ -137,6 +137,7 @@ RenderableStars::RenderableStars(const ghoul::Dictionary& dictionary) RenderableStars::~RenderableStars() { delete _psfTextureFile; delete _colorTextureFile; + delete _colorTexture; } bool RenderableStars::isReady() const { diff --git a/modules/base/rendering/renderabletrail.cpp b/modules/base/rendering/renderabletrail.cpp index a6dbd75e5f..7fdb52c9e2 100644 --- a/modules/base/rendering/renderabletrail.cpp +++ b/modules/base/rendering/renderabletrail.cpp @@ -100,6 +100,10 @@ RenderableTrail::RenderableTrail(const ghoul::Dictionary& dictionary) _distanceFade = 1.0; } +RenderableTrail::~RenderableTrail() { + delete _programObject; +} + bool RenderableTrail::initialize() { if (!_successfullDictionaryFetch) { LERROR("The following keys need to be set in the Dictionary. Cannot initialize!"); diff --git a/modules/base/rendering/renderabletrail.h b/modules/base/rendering/renderabletrail.h index cd04a00c22..c42adf3dc6 100644 --- a/modules/base/rendering/renderabletrail.h +++ b/modules/base/rendering/renderabletrail.h @@ -43,6 +43,7 @@ namespace openspace { class RenderableTrail : public Renderable { public: RenderableTrail(const ghoul::Dictionary& dictionary); + ~RenderableTrail(); bool initialize() override; bool deinitialize() override; diff --git a/openspace.cfg b/openspace.cfg index 1fb1965c3e..cbfe4c5435 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -53,6 +53,6 @@ return { Type = "text", File = "${BASE_PATH}/Properties.txt" }, - RenderingMethod = "ABufferSingleLinked" -- On Windows and Unix - -- RenderingMethod = "ABufferFrameBuffer" -- On Mac due to OpenGL 4.1 restrictions + -- RenderingMethod = "ABufferSingleLinked" -- On Windows and Unix + RenderingMethod = "ABufferFrameBuffer" -- On Mac due to OpenGL 4.1 restrictions } \ No newline at end of file diff --git a/src/engine/moduleengine.cpp b/src/engine/moduleengine.cpp index b6b541156b..86f5163810 100644 --- a/src/engine/moduleengine.cpp +++ b/src/engine/moduleengine.cpp @@ -60,6 +60,7 @@ bool ModuleEngine::deinitialize() { LERROR("Could not deinitialize module '" << m->name() << "'"); return false; } + delete m; } LDEBUG("Finished Deinitializing modules"); return true; diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 1fe1bcc9d7..835e34c7bb 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -49,7 +49,7 @@ #include #include - +#include #include #include #include @@ -148,6 +148,8 @@ bool OpenSpaceEngine::create( std::vector& sgctArguments, std::string& openGlVersion) { + ghoul::initialize(); + ghoul_assert(!_engine, "OpenSpaceEngine was already created"); // Initialize the LogManager and add the console log as this will be used every time @@ -271,6 +273,8 @@ bool OpenSpaceEngine::create( void OpenSpaceEngine::destroy() { _engine->_moduleEngine->deinitialize(); _engine->_console->deinitialize(); + + _engine->_scriptEngine->deinitialize(); delete _engine; ghoul::systemcapabilities::SystemCapabilities::deinitialize(); FactoryManager::deinitialize(); @@ -279,6 +283,8 @@ void OpenSpaceEngine::destroy() { FileSystem::deinitialize(); LogManager::deinitialize(); + + ghoul::deinitialize(); } bool OpenSpaceEngine::initialize() { diff --git a/src/main.cpp b/src/main.cpp index a7c161752e..e2340f90e9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -53,8 +53,10 @@ void setupPostFX(); GLint _postFXTexLoc; GLint _postFXOpacityLoc; +#include + namespace { - const std::string _loggerCat = "main"; + const std::string _loggerCat = "main"; } int main(int argc, char** argv) { @@ -76,16 +78,16 @@ int main(int argc, char** argv) { newArgv[i] = const_cast(sgctArguments.at(i).c_str()); // Need to set this before the creation of the sgct::Engine - sgct::MessageHandler::instance()->setLogToConsole(false); - sgct::MessageHandler::instance()->setShowTime(false); - sgct::MessageHandler::instance()->setLogToCallback(true); - sgct::MessageHandler::instance()->setLogCallback(mainLogCallback); + sgct::MessageHandler::instance()->setLogToConsole(false); + sgct::MessageHandler::instance()->setShowTime(false); + sgct::MessageHandler::instance()->setLogToCallback(true); + sgct::MessageHandler::instance()->setLogCallback(mainLogCallback); #ifdef __APPLE__ glfwWindowHint(GLFW_STENCIL_BITS, 8); #endif - LDEBUG("Creating SGCT Engine"); + LDEBUG("Creating SGCT Engine"); _sgctEngine = new sgct::Engine(newArgc, newArgv); // deallocate sgct c arguments @@ -100,9 +102,9 @@ int main(int argc, char** argv) { _sgctEngine->setKeyboardCallbackFunction(mainKeyboardCallback); _sgctEngine->setMouseButtonCallbackFunction(mainMouseButtonCallback); _sgctEngine->setMousePosCallbackFunction(mainMousePosCallback); - _sgctEngine->setMouseScrollCallbackFunction(mainMouseScrollCallback); - _sgctEngine->setExternalControlCallback(mainExternalControlCallback); - _sgctEngine->setCharCallbackFunction(mainCharCallback); + _sgctEngine->setMouseScrollCallbackFunction(mainMouseScrollCallback); + _sgctEngine->setExternalControlCallback(mainExternalControlCallback); + _sgctEngine->setCharCallbackFunction(mainCharCallback); // set encode and decode functions // NOTE: starts synchronizing before init functions @@ -110,9 +112,9 @@ int main(int argc, char** argv) { sgct::SharedData::instance()->setDecodeFunction(mainDecodeFun); // try to open a window - LDEBUG("Initialize SGCT Engine"); + LDEBUG("Initialize SGCT Engine"); #ifdef __APPLE__ - sgct::Engine::RunMode rm = sgct::Engine::RunMode::OpenGL_4_1_Core_Profile; + sgct::Engine::RunMode rm = sgct::Engine::RunMode::OpenGL_4_1_Core_Profile; #else std::map versionMapping = { { "4.2", sgct::Engine::RunMode::OpenGL_4_2_Core_Profile }, @@ -126,27 +128,27 @@ int main(int argc, char** argv) { } sgct::Engine::RunMode rm = versionMapping[openGlVersion]; #endif - const bool initSuccess = _sgctEngine->init(rm); + const bool initSuccess = _sgctEngine->init(rm); if (!initSuccess) { - LFATAL("Initializing failed"); + LFATAL("Initializing failed"); // could not open a window, deallocates and exits delete _sgctEngine; openspace::OpenSpaceEngine::destroy(); return EXIT_FAILURE; } - //is this node the master? (must be set after call to _sgctEngine->init()) - OsEng.setMaster(_sgctEngine->isMaster()); + //is this node the master? (must be set after call to _sgctEngine->init()) + OsEng.setMaster(_sgctEngine->isMaster()); // Main loop - LDEBUG("Starting rendering loop"); + LDEBUG("Starting rendering loop"); _sgctEngine->render(); - LDEBUG("Destroying OpenSpaceEngine"); - openspace::OpenSpaceEngine::destroy(); + LDEBUG("Destroying OpenSpaceEngine"); + openspace::OpenSpaceEngine::destroy(); // Clean up (de-allocate) - LDEBUG("Destroying SGCT Engine"); + LDEBUG("Destroying SGCT Engine"); delete _sgctEngine; // Exit program @@ -154,44 +156,44 @@ int main(int argc, char** argv) { } void mainInitFunc() { - bool success = OsEng.initialize(); - if (success) - success = OsEng.initializeGL(); + bool success = OsEng.initialize(); + if (success) + success = OsEng.initializeGL(); - if (!success) { - LFATAL("Initializing OpenSpaceEngine failed"); - std::cout << "Press any key to continue..."; - std::cin.ignore(100); - exit(EXIT_FAILURE); - } + if (!success) { + LFATAL("Initializing OpenSpaceEngine failed"); + std::cout << "Press any key to continue..."; + std::cin.ignore(100); + exit(EXIT_FAILURE); + } - //temporary post-FX solution, TODO add a more permanent solution @JK - setupPostFX(); + //temporary post-FX solution, TODO add a more permanent solution @JK + setupPostFX(); } void mainPreSyncFunc() { - OsEng.preSynchronization(); + OsEng.preSynchronization(); } void mainPostSyncPreDrawFunc() { - OsEng.postSynchronizationPreDraw(); + OsEng.postSynchronizationPreDraw(); } void mainRenderFunc() { using glm::mat4; using glm::translate; - //not the most efficient, but for clarity @JK - - mat4 userMatrix = translate(mat4(1.f), _sgctEngine->getDefaultUserPtr()->getPos()); - mat4 sceneMatrix = _sgctEngine->getModelMatrix(); - mat4 viewMatrix = _sgctEngine->getActiveViewMatrix() * userMatrix; - - //dont shift nav-direction on master, makes it very tricky to navigate @JK - if (!OsEng.ref().isMaster()) - viewMatrix = viewMatrix * sceneMatrix; + //not the most efficient, but for clarity @JK + + mat4 userMatrix = translate(mat4(1.f), _sgctEngine->getDefaultUserPtr()->getPos()); + mat4 sceneMatrix = _sgctEngine->getModelMatrix(); + mat4 viewMatrix = _sgctEngine->getActiveViewMatrix() * userMatrix; + + //dont shift nav-direction on master, makes it very tricky to navigate @JK + if (!OsEng.ref().isMaster()) + viewMatrix = viewMatrix * sceneMatrix; - mat4 projectionMatrix = _sgctEngine->getActiveProjectionMatrix(); - OsEng.render(projectionMatrix, viewMatrix); + mat4 projectionMatrix = _sgctEngine->getActiveProjectionMatrix(); + OsEng.render(projectionMatrix, viewMatrix); } void mainPostDrawFunc() { @@ -200,7 +202,7 @@ void mainPostDrawFunc() { void mainExternalControlCallback(const char* receivedChars, int size) { if (OsEng.isMaster()) - OsEng.externalControlCallback(receivedChars, size, 0); + OsEng.externalControlCallback(receivedChars, size, 0); } void mainKeyboardCallback(int key, int action) { @@ -224,8 +226,8 @@ void mainMouseScrollCallback(double posX, double posY) { } void mainCharCallback(unsigned int codepoint) { - if (OsEng.isMaster()) - OsEng.charCallback(codepoint); + if (OsEng.isMaster()) + OsEng.charCallback(codepoint); } void mainEncodeFun() { @@ -237,30 +239,30 @@ void mainDecodeFun() { } void mainLogCallback(const char* msg){ - std::string message = msg; - // Remove the trailing \n that is passed along - LINFOC("SGCT", message.substr(0, std::max(message.size() - 1, 0))); + std::string message = msg; + // Remove the trailing \n that is passed along + LINFOC("SGCT", message.substr(0, std::max(message.size() - 1, 0))); } void postFXPass(){ - glUniform1i(_postFXTexLoc, 0); + glUniform1i(_postFXTexLoc, 0); if (OsEng.isMaster()) glUniform1f(_postFXOpacityLoc, 1.f); else - glUniform1f(_postFXOpacityLoc, OsEng.renderEngine()->globalBlackOutFactor()); + glUniform1f(_postFXOpacityLoc, OsEng.renderEngine()->globalBlackOutFactor()); } void setupPostFX(){ #ifndef __APPLE__ - sgct::PostFX fx[1]; - sgct::ShaderProgram *shader; - fx[0].init("OpacityControl", absPath("${SHADERS}/postFX_vs.glsl"), absPath("${SHADERS}/postFX_fs.glsl")); - fx[0].setUpdateUniformsFunction(postFXPass); - shader = fx[0].getShaderProgram(); - shader->bind(); - _postFXTexLoc = shader->getUniformLocation("Tex"); - _postFXOpacityLoc = shader->getUniformLocation("Opacity"); - shader->unbind(); - _sgctEngine->addPostFX(fx[0]); + sgct::PostFX fx[1]; + sgct::ShaderProgram *shader; + fx[0].init("OpacityControl", absPath("${SHADERS}/postFX_vs.glsl"), absPath("${SHADERS}/postFX_fs.glsl")); + fx[0].setUpdateUniformsFunction(postFXPass); + shader = fx[0].getShaderProgram(); + shader->bind(); + _postFXTexLoc = shader->getUniformLocation("Tex"); + _postFXOpacityLoc = shader->getUniformLocation("Opacity"); + shader->unbind(); + _sgctEngine->addPostFX(fx[0]); #endif } diff --git a/src/properties/propertyowner.cpp b/src/properties/propertyowner.cpp index 0dd4c72422..3872831d17 100644 --- a/src/properties/propertyowner.cpp +++ b/src/properties/propertyowner.cpp @@ -50,19 +50,16 @@ PropertyOwner::PropertyOwner() { } -PropertyOwner::~PropertyOwner() -{ +PropertyOwner::~PropertyOwner() { _properties.clear(); _subOwners.clear(); } -const std::vector& PropertyOwner::properties() const -{ +const std::vector& PropertyOwner::properties() const { return _properties; } -std::vector PropertyOwner::propertiesRecursive() const -{ +std::vector PropertyOwner::propertiesRecursive() const { std::vector props = properties(); for (const PropertyOwner* owner : _subOwners) { @@ -73,8 +70,7 @@ std::vector PropertyOwner::propertiesRecursive() const return std::move(props); } -Property* PropertyOwner::property(const std::string& id) const -{ +Property* PropertyOwner::property(const std::string& id) const { assert(std::is_sorted(_properties.begin(), _properties.end(), propertyLess)); // As the _properties list is sorted, just finding the lower bound is sufficient @@ -138,13 +134,11 @@ bool PropertyOwner::hasPropertySubOwner(const std::string& name) const { return propertySubOwner(name) != nullptr; } -void PropertyOwner::setPropertyGroupName(std::string groupID, std::string name) -{ +void PropertyOwner::setPropertyGroupName(std::string groupID, std::string name) { _groupNames[std::move(groupID)] = std::move(name); } -const std::string& PropertyOwner::propertyGroupName(const std::string& groupID) const -{ +const std::string& PropertyOwner::propertyGroupName(const std::string& groupID) const { auto it = _groupNames.find(groupID); if (it == _groupNames.end()) return groupID; @@ -193,8 +187,7 @@ void PropertyOwner::addProperty(Property* prop) } } -void PropertyOwner::addProperty(Property& prop) -{ +void PropertyOwner::addProperty(Property& prop) { addProperty(&prop); } @@ -241,8 +234,7 @@ void PropertyOwner::addPropertySubOwner(openspace::properties::PropertyOwner& ow addPropertySubOwner(&owner); } -void PropertyOwner::removeProperty(Property* prop) -{ +void PropertyOwner::removeProperty(Property* prop) { assert(prop != nullptr); // See if we can find the identifier of the property to add in the properties list @@ -261,8 +253,7 @@ void PropertyOwner::removeProperty(Property* prop) << "' not found for removal."); } -void PropertyOwner::removeProperty(Property& prop) -{ +void PropertyOwner::removeProperty(Property& prop) { removeProperty(&prop); } @@ -288,13 +279,11 @@ void PropertyOwner::removePropertySubOwner(openspace::properties::PropertyOwner& removePropertySubOwner(&owner); } -void PropertyOwner::setName(std::string name) -{ +void PropertyOwner::setName(std::string name) { _name = std::move(name); } -const std::string& PropertyOwner::name() const -{ +const std::string& PropertyOwner::name() const { return _name; } diff --git a/src/scene/scenegraph.cpp b/src/scene/scenegraph.cpp index 09f932aa37..1df72077c6 100644 --- a/src/scene/scenegraph.cpp +++ b/src/scene/scenegraph.cpp @@ -33,6 +33,12 @@ #include +#ifdef _MSC_VER +#ifdef OPENSPACE_ENABLE_VLD +#include +#endif +#endif + namespace { const std::string _loggerCat = "SceneGraph"; const std::string _moduleExtension = ".mod"; @@ -169,7 +175,13 @@ bool SceneGraph::loadFromFile(const std::string& sceneDescription) { } ghoul::Dictionary moduleDictionary; +//#ifdef OPENSPACE_ENABLE_VLD +// VLDDisable(); +//#endif bool s = ghoul::lua::loadDictionaryFromFile(moduleFile, moduleDictionary, state); +//#ifdef OPENSPACE_ENABLE_VLD +// VLDEnable(); +//#endif if (!s) continue; @@ -226,6 +238,7 @@ bool SceneGraph::loadFromFile(const std::string& sceneDescription) { _nodes.push_back(internalNode); } } + ghoul::lua::destroyLuaState(state); FileSys.setCurrentDirectory(oldDirectory); for (SceneGraphNodeInternal* node : _nodes) { diff --git a/src/scene/scenegraphnode.cpp b/src/scene/scenegraphnode.cpp index 4a125b93ff..e4c9fd37b4 100644 --- a/src/scene/scenegraphnode.cpp +++ b/src/scene/scenegraphnode.cpp @@ -63,6 +63,7 @@ SceneGraphNode* SceneGraphNode::createFromDictionary(const ghoul::Dictionary& di if (!dictionary.hasValue(KeyName)) { LERROR("SceneGraphNode did not contain a '" << KeyName << "' key"); + delete result; return nullptr; } std::string name; From 3a8a178c6af29db8277dbb9d6d58d5be18496714 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 25 May 2015 19:38:21 +0200 Subject: [PATCH 068/329] Undo change in openspace.cfg --- openspace.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openspace.cfg b/openspace.cfg index cbfe4c5435..1fb1965c3e 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -53,6 +53,6 @@ return { Type = "text", File = "${BASE_PATH}/Properties.txt" }, - -- RenderingMethod = "ABufferSingleLinked" -- On Windows and Unix - RenderingMethod = "ABufferFrameBuffer" -- On Mac due to OpenGL 4.1 restrictions + RenderingMethod = "ABufferSingleLinked" -- On Windows and Unix + -- RenderingMethod = "ABufferFrameBuffer" -- On Mac due to OpenGL 4.1 restrictions } \ No newline at end of file From 4dddafcec959ebe8aa8f04fa260851671f586bb2 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 26 May 2015 21:44:49 +0200 Subject: [PATCH 069/329] Fixing more memory leaks --- ext/ghoul | 2 +- include/openspace/scene/scenegraph.h | 5 ++- src/scene/scene.cpp | 51 ---------------------------- src/scene/scenegraph.cpp | 18 +++++----- 4 files changed, 14 insertions(+), 62 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index d2c5eb2aa0..544fadd8d3 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit d2c5eb2aa00d81776e952e583cd8e667f0704c47 +Subproject commit 544fadd8d3f04e8120f810be81afbfefb08d55ba diff --git a/include/openspace/scene/scenegraph.h b/include/openspace/scene/scenegraph.h index 87c1f4da8a..7a8111224e 100644 --- a/include/openspace/scene/scenegraph.h +++ b/include/openspace/scene/scenegraph.h @@ -37,6 +37,7 @@ class SceneGraphNode; class SceneGraph { public: SceneGraph(); + ~SceneGraph(); void clear(); bool loadFromFile(const std::string& sceneDescription); @@ -52,7 +53,9 @@ public: private: struct SceneGraphNodeInternal { - SceneGraphNode* node; + ~SceneGraphNodeInternal(); + + SceneGraphNode* node = nullptr; // From nodes that are dependent on this one std::vector incomingEdges; // To nodes that this node depends on diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 9ac182c09c..7d42e35a3c 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -204,60 +204,9 @@ void Scene::clearSceneGraph() { } bool Scene::loadSceneInternal(const std::string& sceneDescriptionFilePath) { - // using ghoul::Dictionary; - // using ghoul::lua::loadDictionaryFromFile; - - //if (!FileSys.fileExists(sceneDescriptionFilePath)) { - // LFATAL("Scene description file '" << sceneDescriptionFilePath << "' not found"); - // return false; - //} - - // LDEBUG("Loading scenegraph nodes"); - // if (_root != nullptr) { - // LFATAL("Scenegraph already loaded"); - // return false; - // } - - // OsEng.disableBarrier(); - - // _root = new SceneGraphNode(); - // _root->setName(SceneGraphNode::RootNodeName); - // _nodes.push_back(_root); - // _allNodes.emplace(SceneGraphNode::RootNodeName, _root); - // _focus = SceneGraphNode::RootNodeName; - - // bool success = SceneGraphLoader::load(sceneDescriptionFilePath, _nodes); - - ghoul::Dictionary dictionary; - ////load default.scene ghoul::lua::loadDictionaryFromFile(sceneDescriptionFilePath, dictionary); - //std::string&& sceneDescriptionDirectory = - // ghoul::filesystem::File(sceneDescriptionFilePath).directoryName(); - //std::string moduleDirectory("."); - //dictionary.getValue(constants::scenegraph::keyPathScene, moduleDirectory); - - //// The scene path could either be an absolute or relative path to the description - //// paths directory - //std::string&& relativeCandidate = sceneDescriptionDirectory + - // ghoul::filesystem::FileSystem::PathSeparator + moduleDirectory; - //std::string&& absoluteCandidate = absPath(moduleDirectory); - - //if (FileSys.directoryExists(relativeCandidate)) - // moduleDirectory = relativeCandidate; - //else if (FileSys.directoryExists(absoluteCandidate)) - // moduleDirectory = absoluteCandidate; - //else { - // LFATAL("The '" << constants::scenegraph::keyPathScene << "' pointed to a " - // "path '" << moduleDirectory << "' that did not exist"); - // OsEng.enableBarrier(); - // return false; - //} - - //// Load the modules/scenegraph nodes - //loadModules(moduleDirectory, dictionary); - _graph.loadFromFile(sceneDescriptionFilePath); // TODO: Make it less hard-coded and more flexible when nodes are not found diff --git a/src/scene/scenegraph.cpp b/src/scene/scenegraph.cpp index 1df72077c6..a26ec95e0c 100644 --- a/src/scene/scenegraph.cpp +++ b/src/scene/scenegraph.cpp @@ -53,16 +53,22 @@ namespace { namespace openspace { +SceneGraph::SceneGraphNodeInternal::~SceneGraphNodeInternal() { + delete node; +} + SceneGraph::SceneGraph() : _rootNode(nullptr) {} +SceneGraph::~SceneGraph() { + clear(); +} + void SceneGraph::clear() { // Untested ---abock - for (SceneGraphNodeInternal* n : _nodes) { - delete n->node; + for (SceneGraphNodeInternal* n : _nodes) delete n; - } _nodes.clear(); _rootNode = nullptr; @@ -175,13 +181,7 @@ bool SceneGraph::loadFromFile(const std::string& sceneDescription) { } ghoul::Dictionary moduleDictionary; -//#ifdef OPENSPACE_ENABLE_VLD -// VLDDisable(); -//#endif bool s = ghoul::lua::loadDictionaryFromFile(moduleFile, moduleDictionary, state); -//#ifdef OPENSPACE_ENABLE_VLD -// VLDEnable(); -//#endif if (!s) continue; From 77cc5dba4972318478351ba655dbbfb29dab5500 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 26 May 2015 22:52:12 +0200 Subject: [PATCH 070/329] Moving onscreen gui into its own module --- include/openspace/engine/openspaceengine.h | 6 +- modules/onscreengui/CMakeLists.txt | 61 +++++++++++++++++++ .../onscreengui/ext}/imgui/.gitignore | 0 .../onscreengui/ext}/imgui/CMakeLists.txt | 0 .../onscreengui/ext}/imgui/LICENSE | 0 .../onscreengui/ext}/imgui/README.md | 0 .../onscreengui/ext}/imgui/imconfig.h | 0 .../onscreengui/ext}/imgui/imgui.cpp | 0 .../onscreengui/ext}/imgui/imgui.h | 0 .../onscreengui/ext}/imgui/stb_rect_pack.h | 0 .../onscreengui/ext}/imgui/stb_textedit.h | 0 .../onscreengui/ext}/imgui/stb_truetype.h | 0 modules/onscreengui/include.cmake | 1 + .../gui => modules/onscreengui/include}/gui.h | 10 +-- .../onscreengui/include}/guicomponent.h | 0 .../onscreengui/include}/guihelpcomponent.h | 2 +- .../onscreengui/include}/guiorigincomponent.h | 2 +- .../include}/guiperformancecomponent.h | 2 +- .../include}/guipropertycomponent.h | 2 +- .../onscreengui/include}/guitimecomponent.h | 2 +- modules/onscreengui/onscreenguimodule.cpp | 40 ++++++++++++ modules/onscreengui/onscreenguimodule.h | 40 ++++++++++++ .../onscreengui/shaders}/gui_fs.glsl | 0 .../onscreengui/shaders}/gui_vs.glsl | 0 {src/gui => modules/onscreengui/src}/gui.cpp | 9 ++- .../onscreengui/src}/guicomponent.cpp | 2 +- .../onscreengui/src}/guihelpcomponent.cpp | 2 +- .../onscreengui/src}/guiorigincomponent.cpp | 2 +- .../src}/guiperformancecomponent.cpp | 2 +- .../onscreengui/src}/guipropertycomponent.cpp | 2 +- .../onscreengui/src}/guitimecomponent.cpp | 2 +- src/CMakeLists.txt | 14 ----- src/engine/openspaceengine.cpp | 5 +- src/scene/scene.cpp | 5 +- support/cmake/handle_external_library.cmake | 45 ++++++++++++++ support/cmake/module_definition.cmake | 3 +- support/cmake/support_macros.cmake | 14 ----- 37 files changed, 224 insertions(+), 51 deletions(-) create mode 100644 modules/onscreengui/CMakeLists.txt rename {ext => modules/onscreengui/ext}/imgui/.gitignore (100%) rename {ext => modules/onscreengui/ext}/imgui/CMakeLists.txt (100%) rename {ext => modules/onscreengui/ext}/imgui/LICENSE (100%) rename {ext => modules/onscreengui/ext}/imgui/README.md (100%) rename {ext => modules/onscreengui/ext}/imgui/imconfig.h (100%) rename {ext => modules/onscreengui/ext}/imgui/imgui.cpp (100%) rename {ext => modules/onscreengui/ext}/imgui/imgui.h (100%) rename {ext => modules/onscreengui/ext}/imgui/stb_rect_pack.h (100%) rename {ext => modules/onscreengui/ext}/imgui/stb_textedit.h (100%) rename {ext => modules/onscreengui/ext}/imgui/stb_truetype.h (100%) create mode 100644 modules/onscreengui/include.cmake rename {include/openspace/gui => modules/onscreengui/include}/gui.h (91%) rename {include/openspace/gui => modules/onscreengui/include}/guicomponent.h (100%) rename {include/openspace/gui => modules/onscreengui/include}/guihelpcomponent.h (97%) rename {include/openspace/gui => modules/onscreengui/include}/guiorigincomponent.h (98%) rename {include/openspace/gui => modules/onscreengui/include}/guiperformancecomponent.h (97%) rename {include/openspace/gui => modules/onscreengui/include}/guipropertycomponent.h (98%) rename {include/openspace/gui => modules/onscreengui/include}/guitimecomponent.h (98%) create mode 100644 modules/onscreengui/onscreenguimodule.cpp create mode 100644 modules/onscreengui/onscreenguimodule.h rename {shaders => modules/onscreengui/shaders}/gui_fs.glsl (100%) rename {shaders => modules/onscreengui/shaders}/gui_vs.glsl (100%) rename {src/gui => modules/onscreengui/src}/gui.cpp (99%) rename {src/gui => modules/onscreengui/src}/guicomponent.cpp (97%) rename {src/gui => modules/onscreengui/src}/guihelpcomponent.cpp (97%) rename {src/gui => modules/onscreengui/src}/guiorigincomponent.cpp (99%) rename {src/gui => modules/onscreengui/src}/guiperformancecomponent.cpp (98%) rename {src/gui => modules/onscreengui/src}/guipropertycomponent.cpp (99%) rename {src/gui => modules/onscreengui/src}/guitimecomponent.cpp (98%) create mode 100644 support/cmake/handle_external_library.cmake diff --git a/include/openspace/engine/openspaceengine.h b/include/openspace/engine/openspaceengine.h index 78956f5a6a..11f1eee190 100644 --- a/include/openspace/engine/openspaceengine.h +++ b/include/openspace/engine/openspaceengine.h @@ -25,7 +25,8 @@ #ifndef __OPENSPACEENGINE_H__ #define __OPENSPACEENGINE_H__ -#include +#include +#include #include #include @@ -49,6 +50,9 @@ class ModuleEngine; namespace interaction { class InteractionHandler; } +namespace gui { + class GUI; +} namespace scripting { class ScriptEngine; } diff --git a/modules/onscreengui/CMakeLists.txt b/modules/onscreengui/CMakeLists.txt new file mode 100644 index 0000000000..21375eea6c --- /dev/null +++ b/modules/onscreengui/CMakeLists.txt @@ -0,0 +1,61 @@ +######################################################################################### +# # +# OpenSpace # +# # +# Copyright (c) 2014-2015 # +# # +# Permission is hereby granted, free of charge, to any person obtaining a copy of this # +# software and associated documentation files (the "Software"), to deal in the Software # +# without restriction, including without limitation the rights to use, copy, modify, # +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # +# permit persons to whom the Software is furnished to do so, subject to the following # +# conditions: # +# # +# The above copyright notice and this permission notice shall be included in all copies # +# or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF # +# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # +# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # +######################################################################################### + +include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) + +set(HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/include/gui.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/guicomponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/guihelpcomponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/guiorigincomponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/guiperformancecomponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/guipropertycomponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/guitimecomponent.h +) +source_group("Header Files" FILES ${HEADER_FILES}) + +set(SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/src/gui.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/guicomponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/guihelpcomponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/guiorigincomponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/guiperformancecomponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/guipropertycomponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/guitimecomponent.cpp +) +source_group("Source Files" FILES ${SOURCE_FILES}) + +set(SHADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/gui_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/gui_vs.glsl +) +source_group("Shader Files" FILES ${SHADER_FILES}) + +create_new_module( + "OnScreenGUI" + onscreengui_module + ${HEADER_FILES} ${SOURCE_FILES} ${SHADER_FILES} +) + +include_external_library(${onscreengui_module} Imgui ${CMAKE_CURRENT_SOURCE_DIR}/ext/imgui) diff --git a/ext/imgui/.gitignore b/modules/onscreengui/ext/imgui/.gitignore similarity index 100% rename from ext/imgui/.gitignore rename to modules/onscreengui/ext/imgui/.gitignore diff --git a/ext/imgui/CMakeLists.txt b/modules/onscreengui/ext/imgui/CMakeLists.txt similarity index 100% rename from ext/imgui/CMakeLists.txt rename to modules/onscreengui/ext/imgui/CMakeLists.txt diff --git a/ext/imgui/LICENSE b/modules/onscreengui/ext/imgui/LICENSE similarity index 100% rename from ext/imgui/LICENSE rename to modules/onscreengui/ext/imgui/LICENSE diff --git a/ext/imgui/README.md b/modules/onscreengui/ext/imgui/README.md similarity index 100% rename from ext/imgui/README.md rename to modules/onscreengui/ext/imgui/README.md diff --git a/ext/imgui/imconfig.h b/modules/onscreengui/ext/imgui/imconfig.h similarity index 100% rename from ext/imgui/imconfig.h rename to modules/onscreengui/ext/imgui/imconfig.h diff --git a/ext/imgui/imgui.cpp b/modules/onscreengui/ext/imgui/imgui.cpp similarity index 100% rename from ext/imgui/imgui.cpp rename to modules/onscreengui/ext/imgui/imgui.cpp diff --git a/ext/imgui/imgui.h b/modules/onscreengui/ext/imgui/imgui.h similarity index 100% rename from ext/imgui/imgui.h rename to modules/onscreengui/ext/imgui/imgui.h diff --git a/ext/imgui/stb_rect_pack.h b/modules/onscreengui/ext/imgui/stb_rect_pack.h similarity index 100% rename from ext/imgui/stb_rect_pack.h rename to modules/onscreengui/ext/imgui/stb_rect_pack.h diff --git a/ext/imgui/stb_textedit.h b/modules/onscreengui/ext/imgui/stb_textedit.h similarity index 100% rename from ext/imgui/stb_textedit.h rename to modules/onscreengui/ext/imgui/stb_textedit.h diff --git a/ext/imgui/stb_truetype.h b/modules/onscreengui/ext/imgui/stb_truetype.h similarity index 100% rename from ext/imgui/stb_truetype.h rename to modules/onscreengui/ext/imgui/stb_truetype.h diff --git a/modules/onscreengui/include.cmake b/modules/onscreengui/include.cmake new file mode 100644 index 0000000000..ffea0ac430 --- /dev/null +++ b/modules/onscreengui/include.cmake @@ -0,0 +1 @@ +set(DEFAULT_MODULE ON) diff --git a/include/openspace/gui/gui.h b/modules/onscreengui/include/gui.h similarity index 91% rename from include/openspace/gui/gui.h rename to modules/onscreengui/include/gui.h index a874b35147..00e2c623cb 100644 --- a/include/openspace/gui/gui.h +++ b/modules/onscreengui/include/gui.h @@ -25,11 +25,11 @@ #ifndef __GUI_H__ #define __GUI_H__ -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include namespace openspace { diff --git a/include/openspace/gui/guicomponent.h b/modules/onscreengui/include/guicomponent.h similarity index 100% rename from include/openspace/gui/guicomponent.h rename to modules/onscreengui/include/guicomponent.h diff --git a/include/openspace/gui/guihelpcomponent.h b/modules/onscreengui/include/guihelpcomponent.h similarity index 97% rename from include/openspace/gui/guihelpcomponent.h rename to modules/onscreengui/include/guihelpcomponent.h index bad3e6faf7..cfa228b466 100644 --- a/include/openspace/gui/guihelpcomponent.h +++ b/modules/onscreengui/include/guihelpcomponent.h @@ -25,7 +25,7 @@ #ifndef __GUIHELPCOMPONENT_H__ #define __GUIHELPCOMPONENT_H__ -#include +#include namespace openspace { namespace gui { diff --git a/include/openspace/gui/guiorigincomponent.h b/modules/onscreengui/include/guiorigincomponent.h similarity index 98% rename from include/openspace/gui/guiorigincomponent.h rename to modules/onscreengui/include/guiorigincomponent.h index 06657ac365..914a875ee3 100644 --- a/include/openspace/gui/guiorigincomponent.h +++ b/modules/onscreengui/include/guiorigincomponent.h @@ -25,7 +25,7 @@ #ifndef __GUIORIGINCOMPONENT_H__ #define __GUIORIGINCOMPONENT_H__ -#include +#include namespace openspace { diff --git a/include/openspace/gui/guiperformancecomponent.h b/modules/onscreengui/include/guiperformancecomponent.h similarity index 97% rename from include/openspace/gui/guiperformancecomponent.h rename to modules/onscreengui/include/guiperformancecomponent.h index 913720cc5a..00c87997f9 100644 --- a/include/openspace/gui/guiperformancecomponent.h +++ b/modules/onscreengui/include/guiperformancecomponent.h @@ -25,7 +25,7 @@ #ifndef __GUIPERFORMANCECOMPONENT_H__ #define __GUIPERFORMANCECOMPONENT_H__ -#include +#include namespace ghoul { class SharedMemory; diff --git a/include/openspace/gui/guipropertycomponent.h b/modules/onscreengui/include/guipropertycomponent.h similarity index 98% rename from include/openspace/gui/guipropertycomponent.h rename to modules/onscreengui/include/guipropertycomponent.h index c6f356299c..acc282fa84 100644 --- a/include/openspace/gui/guipropertycomponent.h +++ b/modules/onscreengui/include/guipropertycomponent.h @@ -25,7 +25,7 @@ #ifndef __GUIPROPERTYCOMPONENT_H__ #define __GUIPROPERTYCOMPONENT_H__ -#include +#include #include #include diff --git a/include/openspace/gui/guitimecomponent.h b/modules/onscreengui/include/guitimecomponent.h similarity index 98% rename from include/openspace/gui/guitimecomponent.h rename to modules/onscreengui/include/guitimecomponent.h index d8a8b286e8..1694517363 100644 --- a/include/openspace/gui/guitimecomponent.h +++ b/modules/onscreengui/include/guitimecomponent.h @@ -25,7 +25,7 @@ #ifndef __GUITIMECOMPONENT_H__ #define __GUITIMECOMPONENT_H__ -#include +#include namespace openspace { diff --git a/modules/onscreengui/onscreenguimodule.cpp b/modules/onscreengui/onscreenguimodule.cpp new file mode 100644 index 0000000000..ac024bc300 --- /dev/null +++ b/modules/onscreengui/onscreenguimodule.cpp @@ -0,0 +1,40 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +namespace openspace { + +OnScreenGUIModule::OnScreenGUIModule() { + setName("OnScreenGUI"); +} + +bool OnScreenGUIModule::initialize() { + bool success = OpenSpaceModule::initialize(); + if (!success) + return false; + return true; +} + +} // namespace openspace diff --git a/modules/onscreengui/onscreenguimodule.h b/modules/onscreengui/onscreenguimodule.h new file mode 100644 index 0000000000..0479d78b99 --- /dev/null +++ b/modules/onscreengui/onscreenguimodule.h @@ -0,0 +1,40 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __ONSCREENGUIMODULE_H__ +#define __ONSCREENGUIMODULE_H__ + +#include + +namespace openspace { + +class OnScreenGUIModule : public OpenSpaceModule { +public: + OnScreenGUIModule(); + bool initialize() override; +}; + +} // namespace openspace + +#endif // __ONSCREENGUIMODULE_H__ diff --git a/shaders/gui_fs.glsl b/modules/onscreengui/shaders/gui_fs.glsl similarity index 100% rename from shaders/gui_fs.glsl rename to modules/onscreengui/shaders/gui_fs.glsl diff --git a/shaders/gui_vs.glsl b/modules/onscreengui/shaders/gui_vs.glsl similarity index 100% rename from shaders/gui_vs.glsl rename to modules/onscreengui/shaders/gui_vs.glsl diff --git a/src/gui/gui.cpp b/modules/onscreengui/src/gui.cpp similarity index 99% rename from src/gui/gui.cpp rename to modules/onscreengui/src/gui.cpp index d4538f2fdc..b5ba647efb 100644 --- a/src/gui/gui.cpp +++ b/modules/onscreengui/src/gui.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include @@ -196,8 +196,11 @@ void GUI::initialize() { } void GUI::initializeGL() { - _program = ghoul::opengl::ProgramObject::Build("GUI", - "${SHADERS}/gui_vs.glsl", "${SHADERS}/gui_fs.glsl"); + _program = ghoul::opengl::ProgramObject::Build( + "GUI", + "${MODULE_ONSCREENGUI}/shaders/gui_vs.glsl", + "${MODULE_ONSCREENGUI}/shaders/gui_fs.glsl" + ); if (!_program) return; diff --git a/src/gui/guicomponent.cpp b/modules/onscreengui/src/guicomponent.cpp similarity index 97% rename from src/gui/guicomponent.cpp rename to modules/onscreengui/src/guicomponent.cpp index 28764ebe82..f88561908a 100644 --- a/src/gui/guicomponent.cpp +++ b/modules/onscreengui/src/guicomponent.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include namespace openspace { namespace gui { diff --git a/src/gui/guihelpcomponent.cpp b/modules/onscreengui/src/guihelpcomponent.cpp similarity index 97% rename from src/gui/guihelpcomponent.cpp rename to modules/onscreengui/src/guihelpcomponent.cpp index 1f0b3fbad6..c266364cd9 100644 --- a/src/gui/guihelpcomponent.cpp +++ b/modules/onscreengui/src/guihelpcomponent.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include "imgui.h" diff --git a/src/gui/guiorigincomponent.cpp b/modules/onscreengui/src/guiorigincomponent.cpp similarity index 99% rename from src/gui/guiorigincomponent.cpp rename to modules/onscreengui/src/guiorigincomponent.cpp index c7839f416d..7322367890 100644 --- a/src/gui/guiorigincomponent.cpp +++ b/modules/onscreengui/src/guiorigincomponent.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include diff --git a/src/gui/guiperformancecomponent.cpp b/modules/onscreengui/src/guiperformancecomponent.cpp similarity index 98% rename from src/gui/guiperformancecomponent.cpp rename to modules/onscreengui/src/guiperformancecomponent.cpp index eca3734915..094349193e 100644 --- a/src/gui/guiperformancecomponent.cpp +++ b/modules/onscreengui/src/guiperformancecomponent.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include diff --git a/src/gui/guipropertycomponent.cpp b/modules/onscreengui/src/guipropertycomponent.cpp similarity index 99% rename from src/gui/guipropertycomponent.cpp rename to modules/onscreengui/src/guipropertycomponent.cpp index e8b34a2cff..15b3977e2b 100644 --- a/src/gui/guipropertycomponent.cpp +++ b/modules/onscreengui/src/guipropertycomponent.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include diff --git a/src/gui/guitimecomponent.cpp b/modules/onscreengui/src/guitimecomponent.cpp similarity index 98% rename from src/gui/guitimecomponent.cpp rename to modules/onscreengui/src/guitimecomponent.cpp index 709713b865..85c0d90d1c 100644 --- a/src/gui/guitimecomponent.cpp +++ b/modules/onscreengui/src/guitimecomponent.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 96a6c83e8b..bddf3e7753 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,13 +33,6 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/engine/logfactory.cpp ${OPENSPACE_BASE_DIR}/src/engine/moduleengine.cpp ${OPENSPACE_BASE_DIR}/src/engine/openspaceengine.cpp - ${OPENSPACE_BASE_DIR}/src/gui/gui.cpp - ${OPENSPACE_BASE_DIR}/src/gui/guicomponent.cpp - ${OPENSPACE_BASE_DIR}/src/gui/guihelpcomponent.cpp - ${OPENSPACE_BASE_DIR}/src/gui/guiorigincomponent.cpp - ${OPENSPACE_BASE_DIR}/src/gui/guiperformancecomponent.cpp - ${OPENSPACE_BASE_DIR}/src/gui/guipropertycomponent.cpp - ${OPENSPACE_BASE_DIR}/src/gui/guitimecomponent.cpp ${OPENSPACE_BASE_DIR}/src/interaction/controller.cpp ${OPENSPACE_BASE_DIR}/src/interaction/deviceidentifier.cpp ${OPENSPACE_BASE_DIR}/src/interaction/interactionhandler.cpp @@ -106,13 +99,6 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/engine/logfactory.h ${OPENSPACE_BASE_DIR}/include/openspace/engine/moduleengine.h ${OPENSPACE_BASE_DIR}/include/openspace/engine/openspaceengine.h - ${OPENSPACE_BASE_DIR}/include/openspace/gui/gui.h - ${OPENSPACE_BASE_DIR}/include/openspace/gui/guicomponent.h - ${OPENSPACE_BASE_DIR}/include/openspace/gui/guihelpcomponent.h - ${OPENSPACE_BASE_DIR}/include/openspace/gui/guiorigincomponent.h - ${OPENSPACE_BASE_DIR}/include/openspace/gui/guiperformancecomponent.h - ${OPENSPACE_BASE_DIR}/include/openspace/gui/guipropertycomponent.h - ${OPENSPACE_BASE_DIR}/include/openspace/gui/guitimecomponent.h ${OPENSPACE_BASE_DIR}/include/openspace/interaction/controller.h ${OPENSPACE_BASE_DIR}/include/openspace/interaction/deviceidentifier.h ${OPENSPACE_BASE_DIR}/include/openspace/interaction/interactionhandler.h diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 835e34c7bb..7197d9249d 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -32,7 +32,6 @@ #include #include -#include #include #include #include @@ -63,6 +62,10 @@ #include #include +#ifdef OPENSPACE_MODULE_ONSCREENGUI_ENABLED +#include +#endif + #ifdef _MSC_VER #ifdef OPENSPACE_ENABLE_VLD #include diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 7d42e35a3c..8e6b0c58bc 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -53,6 +52,10 @@ #include #include +#ifdef OPENSPACE_MODULE_ONSCREENGUI_ENABLED +#include +#endif + #include "scene_lua.inl" namespace { diff --git a/support/cmake/handle_external_library.cmake b/support/cmake/handle_external_library.cmake new file mode 100644 index 0000000000..bd75cce586 --- /dev/null +++ b/support/cmake/handle_external_library.cmake @@ -0,0 +1,45 @@ +######################################################################################### +# # +# OpenSpace # +# # +# Copyright (c) 2014-2015 # +# # +# 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. # +######################################################################################### + +# Includes an external library by adding its subdirectory using 'add_subdirectory' +# target_name: Target to which the library is added +# library_name: The library that is added by including 'path' +# path: The path that will be included +function (include_external_library target_name library_name path) + if (NOT TARGET ${library_name}) + add_subdirectory(${path}) + get_property(INCLUDE_DIR TARGET ${target_name} PROPERTY INTERFACE_INCLUDE_DIRECTORIES) + target_link_libraries(${target_name} ${library_name}) + target_include_directories(${target_name} PUBLIC ${INCLUDE_DIR}) + set_property(TARGET ${library_name} PROPERTY FOLDER "External") + if (OPENSPACE_DISABLE_EXTERNAL_WARNINGS) + if (MSVC) + target_compile_options(${library_name} PUBLIC "/W0" "/MP") + else () + target_compile_options(${library_name} PUBLIC "-w") + endif () + endif () + endif () + +endfunction () \ No newline at end of file diff --git a/support/cmake/module_definition.cmake b/support/cmake/module_definition.cmake index 1cfb40a8f1..c1c6be037e 100644 --- a/support/cmake/module_definition.cmake +++ b/support/cmake/module_definition.cmake @@ -23,6 +23,7 @@ ######################################################################################### include (${OPENSPACE_CMAKE_EXT_DIR}/module_common.cmake) +include (${OPENSPACE_CMAKE_EXT_DIR}/handle_external_library.cmake) # Creates a new project and a library for the module with name . The name of # the library is returned in for outside configuration @@ -53,7 +54,7 @@ function (create_new_module module_name output_library_name) write_module_name(${module_name}) - set(${output_library_name} ${library_name}) + set(${output_library_name} ${library_name} PARENT_SCOPE) endfunction () diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 714902e4c4..6a26c38801 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -206,20 +206,6 @@ function (add_external_dependencies) endif () set_property(TARGET cdf PROPERTY FOLDER "External") endif () - - # Imgui - add_subdirectory(${OPENSPACE_EXT_DIR}/imgui) - get_property(IMGUI_INCLUDE_DIR TARGET Imgui PROPERTY INTERFACE_INCLUDE_DIRECTORIES) - target_link_libraries(libOpenSpace Imgui) - target_include_directories(libOpenSpace PUBLIC ${IMGUI_INCLUDE_DIR}) - set_property(TARGET Imgui PROPERTY FOLDER "External") - if (OPENSPACE_DISABLE_EXTERNAL_WARNINGS) - if (MSVC) - target_compile_options(Imgui PUBLIC "/W0" "/MP") - else () - target_compile_options(ccmc PUBLIC "-w") - endif () - endif () endfunction () From 9d733eb613fbeec51c758d53edc6d3f274d9ddc1 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 26 May 2015 23:51:15 +0200 Subject: [PATCH 071/329] Moving Kameleon into its own module Moving fieldline rendering into its own module --- .gitmodules | 4 +- ext/ghoul | 2 +- modules/base/CMakeLists.txt | 105 +++++++++--------- modules/base/basemodule.cpp | 2 - modules/fieldlines/CMakeLists.txt | 48 ++++++++ modules/fieldlines/fieldlinesmodule.cpp | 53 +++++++++ modules/fieldlines/fieldlinesmodule.h | 40 +++++++ modules/fieldlines/include.cmake | 3 + .../rendering/renderablefieldlines.cpp | 4 +- .../rendering/renderablefieldlines.h | 0 .../shaders/fieldline_fs.glsl | 0 .../shaders/fieldline_gs.glsl | 0 .../shaders/fieldline_vs.glsl | 0 modules/kameleon/CMakeLists.txt | 77 +++++++++++++ {ext => modules/kameleon/ext}/kameleon | 0 .../kameleon/include}/kameleonwrapper.h | 0 modules/kameleon/kameleonmodule.cpp | 41 +++++++ modules/kameleon/kameleonmodule.h | 40 +++++++ .../kameleon/src}/kameleonwrapper.cpp | 2 +- modules/volume/include.cmake | 4 +- modules/volume/rendering/renderablevolume.cpp | 2 +- .../volume/rendering/renderablevolumegl.cpp | 2 +- src/CMakeLists.txt | 2 - src/rendering/renderengine.cpp | 2 +- support/cmake/support_macros.cmake | 36 +----- 25 files changed, 366 insertions(+), 103 deletions(-) create mode 100644 modules/fieldlines/CMakeLists.txt create mode 100644 modules/fieldlines/fieldlinesmodule.cpp create mode 100644 modules/fieldlines/fieldlinesmodule.h create mode 100644 modules/fieldlines/include.cmake rename modules/{base => fieldlines}/rendering/renderablefieldlines.cpp (99%) rename modules/{base => fieldlines}/rendering/renderablefieldlines.h (100%) rename modules/{base => fieldlines}/shaders/fieldline_fs.glsl (100%) rename modules/{base => fieldlines}/shaders/fieldline_gs.glsl (100%) rename modules/{base => fieldlines}/shaders/fieldline_vs.glsl (100%) create mode 100644 modules/kameleon/CMakeLists.txt rename {ext => modules/kameleon/ext}/kameleon (100%) rename {include/openspace/util => modules/kameleon/include}/kameleonwrapper.h (100%) create mode 100644 modules/kameleon/kameleonmodule.cpp create mode 100644 modules/kameleon/kameleonmodule.h rename {src/util => modules/kameleon/src}/kameleonwrapper.cpp (99%) diff --git a/.gitmodules b/.gitmodules index b4d9520ac1..e78869f917 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,6 +4,6 @@ [submodule "openspace-data"] path = openspace-data url = git@openspace.itn.liu.se:/openspace-data -[submodule "ext/kameleon"] - path = ext/kameleon +[submodule "modules/kameleon/ext/kameleon"] + path = modules/kameleon/ext/kameleon url = git@openspace.itn.liu.se:/kameleon diff --git a/ext/ghoul b/ext/ghoul index 544fadd8d3..c660e8dd75 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 544fadd8d3f04e8120f810be81afbfefb08d55ba +Subproject commit c660e8dd75beae2a827cbc04d19987e45b23f4fb diff --git a/modules/base/CMakeLists.txt b/modules/base/CMakeLists.txt index cd2491b6af..8cd92a6bc8 100644 --- a/modules/base/CMakeLists.txt +++ b/modules/base/CMakeLists.txt @@ -25,69 +25,64 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) set(HEADER_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/modelgeometry.h - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometry.h - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableconstellationbounds.h - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefieldlines.h - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablemodel.h - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablepath.h - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplane.h - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplanet.h - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablesphere.h - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablesphericalgrid.h - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablestars.h - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderabletrail.h - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometry.h - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/wavefrontgeometry.h - ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/dynamicephemeris.h - ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/spiceephemeris.h - ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/staticephemeris.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/modelgeometry.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometry.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableconstellationbounds.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablemodel.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablepath.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplane.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplanet.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablesphere.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablesphericalgrid.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablestars.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderabletrail.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometry.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/wavefrontgeometry.h + ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/dynamicephemeris.h + ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/spiceephemeris.h + ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/staticephemeris.h ) source_group("Header Files" FILES ${HEADER_FILES}) set(SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/modelgeometry.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometry.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableconstellationbounds.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefieldlines.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablemodel.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablepath.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplane.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplanet.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablesphere.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablesphericalgrid.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablestars.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderabletrail.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometry.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/wavefrontgeometry.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/dynamicephemeris.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/spiceephemeris.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/staticephemeris.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/modelgeometry.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometry.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableconstellationbounds.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablemodel.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablepath.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplane.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplanet.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablesphere.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablesphericalgrid.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablestars.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderabletrail.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometry.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/wavefrontgeometry.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/dynamicephemeris.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/spiceephemeris.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/staticephemeris.cpp ) source_group("Source Files" FILES ${SOURCE_FILES}) set(SHADER_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/constellationbounds_fs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/constellationbounds_vs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/ephemeris_fs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/ephemeris_vs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/fieldline_fs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/fieldline_gs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/fieldline_vs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/imageplane_fs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/imageplane_vs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/model_fs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/model_vs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/path_fs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/path_gs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/path_vs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/plane_fs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/plane_vs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/sphere_fs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/sphere_vs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/star_fs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/star_ge.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/star_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/constellationbounds_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/constellationbounds_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/ephemeris_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/ephemeris_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/imageplane_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/imageplane_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/model_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/model_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/path_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/path_gs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/path_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/plane_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/plane_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/sphere_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/sphere_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/star_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/star_ge.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/star_vs.glsl ) source_group("Shader Files" FILES ${SHADER_FILES}) diff --git a/modules/base/basemodule.cpp b/modules/base/basemodule.cpp index f2edeb85a2..e03cc14326 100644 --- a/modules/base/basemodule.cpp +++ b/modules/base/basemodule.cpp @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include @@ -76,7 +75,6 @@ bool BaseModule::initialize() { fRenderable->registerClass("RenderableSphericalGrid"); fRenderable->registerClass("RenderableModel"); fRenderable->registerClass("RenderablePlane"); - fRenderable->registerClass("RenderableFieldlines"); auto fEphemeris = FactoryManager::ref().factory(); ghoul_assert(fEphemeris, "Ephemeris factory was not created"); diff --git a/modules/fieldlines/CMakeLists.txt b/modules/fieldlines/CMakeLists.txt new file mode 100644 index 0000000000..e8c847c274 --- /dev/null +++ b/modules/fieldlines/CMakeLists.txt @@ -0,0 +1,48 @@ +######################################################################################### +# # +# OpenSpace # +# # +# Copyright (c) 2014-2015 # +# # +# Permission is hereby granted, free of charge, to any person obtaining a copy of this # +# software and associated documentation files (the "Software"), to deal in the Software # +# without restriction, including without limitation the rights to use, copy, modify, # +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # +# permit persons to whom the Software is furnished to do so, subject to the following # +# conditions: # +# # +# The above copyright notice and this permission notice shall be included in all copies # +# or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF # +# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # +# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # +######################################################################################### + +include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) + +set(HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefieldlines.h +) +source_group("Header Files" FILES ${HEADER_FILES}) + +set(SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefieldlines.cpp +) +source_group("Source Files" FILES ${SOURCE_FILES}) + +set(SHADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/fieldline_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/fieldline_gs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/fieldline_vs.glsl +) +source_group("Shader Files" FILES ${SHADER_FILES}) + +create_new_module( + "Fieldlines" + fieldlines_module + ${HEADER_FILES} ${SOURCE_FILES} ${SHADER_FILES} +) diff --git a/modules/fieldlines/fieldlinesmodule.cpp b/modules/fieldlines/fieldlinesmodule.cpp new file mode 100644 index 0000000000..1c72f6d264 --- /dev/null +++ b/modules/fieldlines/fieldlinesmodule.cpp @@ -0,0 +1,53 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include + +#include + +#include + +namespace openspace { + +FieldlinesModule::FieldlinesModule() { + setName("Fieldlines"); +} + +bool FieldlinesModule::initialize() { + bool success = OpenSpaceModule::initialize(); + if (!success) + return false; + + auto fRenderable = FactoryManager::ref().factory(); + ghoul_assert(fRenderable, "No renderable factory existed"); + + fRenderable->registerClass("RenderableFieldlines"); + + return true; +} + +} // namespace openspace diff --git a/modules/fieldlines/fieldlinesmodule.h b/modules/fieldlines/fieldlinesmodule.h new file mode 100644 index 0000000000..2acd5d2164 --- /dev/null +++ b/modules/fieldlines/fieldlinesmodule.h @@ -0,0 +1,40 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __FIELDLINESMODULE_H__ +#define __FIELDLINESMODULE_H__ + +#include + +namespace openspace { + +class FieldlinesModule : public OpenSpaceModule { +public: + FieldlinesModule(); + bool initialize() override; +}; + +} // namespace openspace + +#endif // __FIELDLINESMODULE_H__ diff --git a/modules/fieldlines/include.cmake b/modules/fieldlines/include.cmake new file mode 100644 index 0000000000..3751826997 --- /dev/null +++ b/modules/fieldlines/include.cmake @@ -0,0 +1,3 @@ +set (OPENSPACE_DEPENDENCIES + kameleon +) \ No newline at end of file diff --git a/modules/base/rendering/renderablefieldlines.cpp b/modules/fieldlines/rendering/renderablefieldlines.cpp similarity index 99% rename from modules/base/rendering/renderablefieldlines.cpp rename to modules/fieldlines/rendering/renderablefieldlines.cpp index 62a6503c67..69ca17ca64 100644 --- a/modules/base/rendering/renderablefieldlines.cpp +++ b/modules/fieldlines/rendering/renderablefieldlines.cpp @@ -22,10 +22,10 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include -#include +#include #include #include diff --git a/modules/base/rendering/renderablefieldlines.h b/modules/fieldlines/rendering/renderablefieldlines.h similarity index 100% rename from modules/base/rendering/renderablefieldlines.h rename to modules/fieldlines/rendering/renderablefieldlines.h diff --git a/modules/base/shaders/fieldline_fs.glsl b/modules/fieldlines/shaders/fieldline_fs.glsl similarity index 100% rename from modules/base/shaders/fieldline_fs.glsl rename to modules/fieldlines/shaders/fieldline_fs.glsl diff --git a/modules/base/shaders/fieldline_gs.glsl b/modules/fieldlines/shaders/fieldline_gs.glsl similarity index 100% rename from modules/base/shaders/fieldline_gs.glsl rename to modules/fieldlines/shaders/fieldline_gs.glsl diff --git a/modules/base/shaders/fieldline_vs.glsl b/modules/fieldlines/shaders/fieldline_vs.glsl similarity index 100% rename from modules/base/shaders/fieldline_vs.glsl rename to modules/fieldlines/shaders/fieldline_vs.glsl diff --git a/modules/kameleon/CMakeLists.txt b/modules/kameleon/CMakeLists.txt new file mode 100644 index 0000000000..724e7a40a9 --- /dev/null +++ b/modules/kameleon/CMakeLists.txt @@ -0,0 +1,77 @@ +######################################################################################### +# # +# OpenSpace # +# # +# Copyright (c) 2014-2015 # +# # +# Permission is hereby granted, free of charge, to any person obtaining a copy of this # +# software and associated documentation files (the "Software"), to deal in the Software # +# without restriction, including without limitation the rights to use, copy, modify, # +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # +# permit persons to whom the Software is furnished to do so, subject to the following # +# conditions: # +# # +# The above copyright notice and this permission notice shall be included in all copies # +# or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF # +# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # +# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # +######################################################################################### + +include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) + +set(HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/include/kameleonwrapper.h +) +source_group("Header Files" FILES ${HEADER_FILES}) + +set(SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/src/kameleonwrapper.cpp +) +source_group("Source Files" FILES ${SOURCE_FILES}) + +create_new_module( + "Kameleon" + kameleon_module + ${HEADER_FILES} ${SOURCE_FILES} +) + + option(KAMELEON_LIBRARY_ONLY "Build with Kameleon as library only" ON) + if (WIN32) + option(BUILD_SHARED_LIBS "Build Shared Libraries" OFF) + else () + option(BUILD_SHARED_LIBS "Build Shared Libraries" ON) + endif () + mark_as_advanced(BUILD_SHARED_LIBS) # Change to set instead of option + option(KAMELEON_USE_HDF5 "Kameleon use HDF5" OFF) + set(KAMELEON_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/kameleon) + set(KAMELEON_INCLUDES ${KAMELEON_ROOT_DIR}/src) + set(BOOST_ROOT "${OPENSPACE_EXT_DIR}/ghoul/ext/boost") + add_subdirectory(${KAMELEON_ROOT_DIR}) + target_include_directories(${kameleon_module} SYSTEM PUBLIC ${KAMELEON_INCLUDES}) + target_link_libraries(${kameleon_module} ccmc) + if (OPENSPACE_DISABLE_EXTERNAL_WARNINGS) + if (MSVC) + target_compile_options(ccmc PUBLIC "/W0" "/MP") + else () + target_compile_options(ccmc PUBLIC "-w") + endif () + target_compile_definitions(ccmc PUBLIC "_SCL_SECURE_NO_WARNINGS") + endif () + set_property(TARGET ccmc PROPERTY FOLDER "External") + if (TARGET cdf) + if (OPENSPACE_DISABLE_EXTERNAL_WARNINGS) + if (MSVC) + target_compile_options(cdf PUBLIC "/W0" "/MP") + else () + target_compile_options(cdf PUBLIC "-w") + endif () + endif () + set_property(TARGET cdf PROPERTY FOLDER "External") + endif () + +#include_external_library(${onscreengui_module} Imgui ${CMAKE_CURRENT_SOURCE_DIR}/ext/kameleon) diff --git a/ext/kameleon b/modules/kameleon/ext/kameleon similarity index 100% rename from ext/kameleon rename to modules/kameleon/ext/kameleon diff --git a/include/openspace/util/kameleonwrapper.h b/modules/kameleon/include/kameleonwrapper.h similarity index 100% rename from include/openspace/util/kameleonwrapper.h rename to modules/kameleon/include/kameleonwrapper.h diff --git a/modules/kameleon/kameleonmodule.cpp b/modules/kameleon/kameleonmodule.cpp new file mode 100644 index 0000000000..bde173db2b --- /dev/null +++ b/modules/kameleon/kameleonmodule.cpp @@ -0,0 +1,41 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +namespace openspace { + +KameleonModule::KameleonModule() { + setName("Kameleon"); +} + +bool KameleonModule::initialize() { + bool success = OpenSpaceModule::initialize(); + if (!success) + return false; + + return true; +} + +} // namespace openspace diff --git a/modules/kameleon/kameleonmodule.h b/modules/kameleon/kameleonmodule.h new file mode 100644 index 0000000000..19e2b51712 --- /dev/null +++ b/modules/kameleon/kameleonmodule.h @@ -0,0 +1,40 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __KAMELEONMODULE_H__ +#define __KAMELEONMODULE_H__ + +#include + +namespace openspace { + +class KameleonModule : public OpenSpaceModule { +public: + KameleonModule(); + bool initialize() override; +}; + +} // namespace openspace + +#endif // __KAMELEONMODULE_H__ diff --git a/src/util/kameleonwrapper.cpp b/modules/kameleon/src/kameleonwrapper.cpp similarity index 99% rename from src/util/kameleonwrapper.cpp rename to modules/kameleon/src/kameleonwrapper.cpp index 3c2c5e3a8f..1ebdf0c3a2 100644 --- a/src/util/kameleonwrapper.cpp +++ b/modules/kameleon/src/kameleonwrapper.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include diff --git a/modules/volume/include.cmake b/modules/volume/include.cmake index ffea0ac430..3751826997 100644 --- a/modules/volume/include.cmake +++ b/modules/volume/include.cmake @@ -1 +1,3 @@ -set(DEFAULT_MODULE ON) +set (OPENSPACE_DEPENDENCIES + kameleon +) \ No newline at end of file diff --git a/modules/volume/rendering/renderablevolume.cpp b/modules/volume/rendering/renderablevolume.cpp index 4afdd65974..e9f1d2e60a 100644 --- a/modules/volume/rendering/renderablevolume.cpp +++ b/modules/volume/rendering/renderablevolume.cpp @@ -25,7 +25,7 @@ // open space includes #include #include -#include +#include #include #include diff --git a/modules/volume/rendering/renderablevolumegl.cpp b/modules/volume/rendering/renderablevolumegl.cpp index a3fddd75fa..7c45905eb5 100644 --- a/modules/volume/rendering/renderablevolumegl.cpp +++ b/modules/volume/rendering/renderablevolumegl.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bddf3e7753..2c1f8518cb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -71,7 +71,6 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/scripting/scriptengine_lua.inl ${OPENSPACE_BASE_DIR}/src/util/camera.cpp ${OPENSPACE_BASE_DIR}/src/util/factorymanager.cpp - ${OPENSPACE_BASE_DIR}/src/util/kameleonwrapper.cpp ${OPENSPACE_BASE_DIR}/src/util/openspacemodule.cpp ${OPENSPACE_BASE_DIR}/src/util/powerscaledcoordinate.cpp ${OPENSPACE_BASE_DIR}/src/util/powerscaledscalar.cpp @@ -142,7 +141,6 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/util/constants.h ${OPENSPACE_BASE_DIR}/include/openspace/util/factorymanager.h ${OPENSPACE_BASE_DIR}/include/openspace/util/factorymanager.inl - ${OPENSPACE_BASE_DIR}/include/openspace/util/kameleonwrapper.h ${OPENSPACE_BASE_DIR}/include/openspace/util/keys.h ${OPENSPACE_BASE_DIR}/include/openspace/util/openspacemodule.h ${OPENSPACE_BASE_DIR}/include/openspace/util/powerscaledcoordinate.h diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 764dee30f4..60bcc157c4 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -419,7 +419,6 @@ void RenderEngine::render(const glm::mat4 &projectionMatrix, const glm::mat4 &vi //const PowerScaledScalar& pssl = (position - origin).length(); // Next 2 lines neccesary for instrument switching to work. - //double currentTime = Time::ref().currentTime(); // GUI PRINT // Using a macro to shorten line length and increase readability @@ -441,6 +440,7 @@ void RenderEngine::render(const glm::mat4 &projectionMatrix, const glm::mat4 &vi //PrintText(i++, "Scaling: (% .5f, % .5f)", scaling[0], scaling[1]); #ifdef OPENSPACE_MODULE_NEWHORIZONS_ENABLED + double currentTime = Time::ref().currentTime(); if (openspace::ImageSequencer2::ref().isReady()) { double remaining = openspace::ImageSequencer2::ref().getNextCaptureTime() - currentTime; double t = 1.0 - remaining / openspace::ImageSequencer2::ref().getIntervalLength(); diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 6a26c38801..73219f13da 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -172,40 +172,6 @@ function (add_external_dependencies) target_include_directories(libOpenSpace SYSTEM PUBLIC ${SPICE_INCLUDE_DIRS}) target_link_libraries(libOpenSpace ${SPICE_LIBRARIES}) - # # Kameleon - option(KAMELEON_LIBRARY_ONLY "Build with Kameleon as library only" ON) - if (WIN32) - option(BUILD_SHARED_LIBS "Build Shared Libraries" OFF) - else () - option(BUILD_SHARED_LIBS "Build Shared Libraries" ON) - endif () - mark_as_advanced(BUILD_SHARED_LIBS) # Change to set instead of option - option(KAMELEON_USE_HDF5 "Kameleon use HDF5" OFF) - set(KAMELEON_ROOT_DIR ${OPENSPACE_EXT_DIR}/kameleon) - set(KAMELEON_INCLUDES ${KAMELEON_ROOT_DIR}/src) - set(BOOST_ROOT "${OPENSPACE_EXT_DIR}/ghoul/ext/boost") - add_subdirectory(${KAMELEON_ROOT_DIR}) - target_include_directories(libOpenSpace SYSTEM PUBLIC ${KAMELEON_INCLUDES}) - target_link_libraries(libOpenSpace ccmc) - if (OPENSPACE_DISABLE_EXTERNAL_WARNINGS) - if (MSVC) - target_compile_options(ccmc PUBLIC "/W0" "/MP") - else () - target_compile_options(ccmc PUBLIC "-w") - endif () - target_compile_definitions(ccmc PUBLIC "_SCL_SECURE_NO_WARNINGS") - endif () - set_property(TARGET ccmc PROPERTY FOLDER "External") - if (TARGET cdf) - if (OPENSPACE_DISABLE_EXTERNAL_WARNINGS) - if (MSVC) - target_compile_options(cdf PUBLIC "/W0" "/MP") - else () - target_compile_options(cdf PUBLIC "-w") - endif () - endif () - set_property(TARGET cdf PROPERTY FOLDER "External") - endif () endfunction () @@ -331,6 +297,8 @@ function (handle_internal_modules) set(MODULE_HEADERS "") set(MODULE_CLASSES "") + message(STATUS ${sortedModules}) + # Add subdirectories in the correct order foreach (module ${sortedModules}) create_option_name(${module} optionName) From 630ea6ba267673819d5a3f4b980276039f4773b4 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 27 May 2015 00:57:40 +0200 Subject: [PATCH 072/329] Add python script to make jenkins build all modules --- support/jenkins/buildAllModules.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 support/jenkins/buildAllModules.py diff --git a/support/jenkins/buildAllModules.py b/support/jenkins/buildAllModules.py new file mode 100644 index 0000000000..ab5fe0b5ca --- /dev/null +++ b/support/jenkins/buildAllModules.py @@ -0,0 +1,12 @@ +import os +from subprocess import call + +modules = os.listdir("../../modules") + +cmd = ["cmake"] +cmd.append("-DGHOUL_USE_DEVIL=OFF") +for m in modules: + cmd.append("-DOPENSPACE_MODULE_" + m.upper() + "=ON") + +cmd.append(".") +call(cmd) \ No newline at end of file From 21cd57381abc44d65c0265308b16b21016f5e97f Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 27 May 2015 00:59:09 +0200 Subject: [PATCH 073/329] Updated python script --- support/jenkins/buildAllModules.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/support/jenkins/buildAllModules.py b/support/jenkins/buildAllModules.py index ab5fe0b5ca..9fad2bad71 100644 --- a/support/jenkins/buildAllModules.py +++ b/support/jenkins/buildAllModules.py @@ -1,7 +1,8 @@ import os from subprocess import call -modules = os.listdir("../../modules") +# To be called from the OpenSpace main folder +modules = os.listdir("modules") cmd = ["cmake"] cmd.append("-DGHOUL_USE_DEVIL=OFF") From a14ce43c2d318f864f70392eab16145ec6bc4733 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 27 May 2015 01:39:39 +0200 Subject: [PATCH 074/329] Add Python script to build all combinations of modules --- support/jenkins/buildAllModuleCombination.py | 28 ++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 support/jenkins/buildAllModuleCombination.py diff --git a/support/jenkins/buildAllModuleCombination.py b/support/jenkins/buildAllModuleCombination.py new file mode 100644 index 0000000000..958092c7eb --- /dev/null +++ b/support/jenkins/buildAllModuleCombination.py @@ -0,0 +1,28 @@ +import os +from subprocess import call +from itertools import product, repeat + +# To be called from the OpenSpace main folder +modules = os.listdir("modules") +modules.remove("base") + +# Get 2**len(modules) combinatorical combinations of ON/OFF +settings = [] +for args in product(*repeat(("ON", "OFF"), len(modules))): + settings.append(args) + +# Create all commands +cmds = [] +for s in settings: + cmd = ["cmake", "-DGHOUL_USE_DEVIL=NO", "-DOPENSPACE_MODULE_BASE=ON"] + + for m,s in zip(modules, s): + cmd.append("-DOPENSPACE_MODULE_" + m.upper() + "=" + s) + cmds.append(cmd) + +# Build cmake and compile +for c in cmds: + call(cmd) + call(["make", "clean"]) + call(["make", "-j4"]) + From eafd43442cc697bf2278f6df67c1577cc8f5bf56 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 27 May 2015 01:44:05 +0200 Subject: [PATCH 075/329] Print progress of combinatorical build --- support/jenkins/buildAllModuleCombination.py | 1 + 1 file changed, 1 insertion(+) diff --git a/support/jenkins/buildAllModuleCombination.py b/support/jenkins/buildAllModuleCombination.py index 958092c7eb..fbd3ec86c5 100644 --- a/support/jenkins/buildAllModuleCombination.py +++ b/support/jenkins/buildAllModuleCombination.py @@ -22,6 +22,7 @@ for s in settings: # Build cmake and compile for c in cmds: + print "CMake:" , cmd call(cmd) call(["make", "clean"]) call(["make", "-j4"]) From 5ab855b5635e09c69ecf67ea1020c9856677ab8d Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 27 May 2015 11:15:53 +0200 Subject: [PATCH 076/329] Update jenkins python scripts --- support/jenkins/buildAllModuleCombination.py | 6 +++--- support/jenkins/buildAllModules.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/support/jenkins/buildAllModuleCombination.py b/support/jenkins/buildAllModuleCombination.py index fbd3ec86c5..61acb95938 100644 --- a/support/jenkins/buildAllModuleCombination.py +++ b/support/jenkins/buildAllModuleCombination.py @@ -2,8 +2,8 @@ import os from subprocess import call from itertools import product, repeat -# To be called from the OpenSpace main folder -modules = os.listdir("modules") +# To be called from the build folder in the OpenSpace +modules = os.listdir("../modules") modules.remove("base") # Get 2**len(modules) combinatorical combinations of ON/OFF @@ -18,12 +18,12 @@ for s in settings: for m,s in zip(modules, s): cmd.append("-DOPENSPACE_MODULE_" + m.upper() + "=" + s) + cmd.append("..") cmds.append(cmd) # Build cmake and compile for c in cmds: print "CMake:" , cmd call(cmd) - call(["make", "clean"]) call(["make", "-j4"]) diff --git a/support/jenkins/buildAllModules.py b/support/jenkins/buildAllModules.py index 9fad2bad71..5d7250aabd 100644 --- a/support/jenkins/buildAllModules.py +++ b/support/jenkins/buildAllModules.py @@ -1,13 +1,13 @@ import os from subprocess import call -# To be called from the OpenSpace main folder -modules = os.listdir("modules") +# To be called from the build folder in the OpenSpace +modules = os.listdir("../modules") cmd = ["cmake"] cmd.append("-DGHOUL_USE_DEVIL=OFF") for m in modules: cmd.append("-DOPENSPACE_MODULE_" + m.upper() + "=ON") -cmd.append(".") +cmd.append("..") call(cmd) \ No newline at end of file From 6107d0c6d097026a9f44508d60668be4fb829048 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 27 May 2015 11:20:37 +0200 Subject: [PATCH 077/329] Updates module building script to delete the old results first --- support/jenkins/buildAllModuleCombination.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/support/jenkins/buildAllModuleCombination.py b/support/jenkins/buildAllModuleCombination.py index 61acb95938..da527934a6 100644 --- a/support/jenkins/buildAllModuleCombination.py +++ b/support/jenkins/buildAllModuleCombination.py @@ -24,6 +24,9 @@ for s in settings: # Build cmake and compile for c in cmds: print "CMake:" , cmd + call["rm", "-rf", "build", "bin"] + call["mkdir", "build"] + call["cd", "build"] call(cmd) call(["make", "-j4"]) - + call["cd", ".."] From c5c710fbc58da1c9c8e1fcab3901e6020db70bfa Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 27 May 2015 11:21:18 +0200 Subject: [PATCH 078/329] Fixing spelling error --- support/jenkins/buildAllModuleCombination.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/support/jenkins/buildAllModuleCombination.py b/support/jenkins/buildAllModuleCombination.py index da527934a6..1453b138b8 100644 --- a/support/jenkins/buildAllModuleCombination.py +++ b/support/jenkins/buildAllModuleCombination.py @@ -2,8 +2,8 @@ import os from subprocess import call from itertools import product, repeat -# To be called from the build folder in the OpenSpace -modules = os.listdir("../modules") +# To be called from the main OpenSpace +modules = os.listdir("modules") modules.remove("base") # Get 2**len(modules) combinatorical combinations of ON/OFF From 5014370797b3eab38c8a0f19543a0e9202db9ee3 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 27 May 2015 11:22:25 +0200 Subject: [PATCH 079/329] Fixing Python script parse error --- support/jenkins/buildAllModuleCombination.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/support/jenkins/buildAllModuleCombination.py b/support/jenkins/buildAllModuleCombination.py index 1453b138b8..03ef472c4f 100644 --- a/support/jenkins/buildAllModuleCombination.py +++ b/support/jenkins/buildAllModuleCombination.py @@ -24,9 +24,9 @@ for s in settings: # Build cmake and compile for c in cmds: print "CMake:" , cmd - call["rm", "-rf", "build", "bin"] - call["mkdir", "build"] - call["cd", "build"] + call(["rm", "-rf", "build", "bin"]) + call(["mkdir", "build"]) + call(["cd", "build"]) call(cmd) call(["make", "-j4"]) - call["cd", ".."] + call(["cd", ".."]) From a2ffa3d02ef4b7cd345602b475f86ac172bd7c6c Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 27 May 2015 11:27:55 +0200 Subject: [PATCH 080/329] Use python internal functions for generating, removing and changing directories --- support/jenkins/buildAllModuleCombination.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/support/jenkins/buildAllModuleCombination.py b/support/jenkins/buildAllModuleCombination.py index 03ef472c4f..6f36f43ac0 100644 --- a/support/jenkins/buildAllModuleCombination.py +++ b/support/jenkins/buildAllModuleCombination.py @@ -1,6 +1,7 @@ import os from subprocess import call from itertools import product, repeat +import shutil # To be called from the main OpenSpace modules = os.listdir("modules") @@ -24,9 +25,10 @@ for s in settings: # Build cmake and compile for c in cmds: print "CMake:" , cmd - call(["rm", "-rf", "build", "bin"]) - call(["mkdir", "build"]) - call(["cd", "build"]) + shutil.rmtree("build") + shutil.rmtree("bin") + os.makedirs("build") + os.chdir("build") call(cmd) call(["make", "-j4"]) - call(["cd", ".."]) + os.chdir("..") \ No newline at end of file From 227b1dafa775bb8dcca70a937798f0d372a80a30 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 27 May 2015 11:29:35 +0200 Subject: [PATCH 081/329] Ignore errors on rmtree call --- support/jenkins/buildAllModuleCombination.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/support/jenkins/buildAllModuleCombination.py b/support/jenkins/buildAllModuleCombination.py index 6f36f43ac0..94c4440a87 100644 --- a/support/jenkins/buildAllModuleCombination.py +++ b/support/jenkins/buildAllModuleCombination.py @@ -25,8 +25,8 @@ for s in settings: # Build cmake and compile for c in cmds: print "CMake:" , cmd - shutil.rmtree("build") - shutil.rmtree("bin") + shutil.rmtree("build", ignore_errors=True) + shutil.rmtree("bin", ignore_errors=True) os.makedirs("build") os.chdir("build") call(cmd) From fb222d4e7a3ff33ac3f3e84e655f303b4d961246 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 27 May 2015 11:34:17 +0200 Subject: [PATCH 082/329] Add python script to build all modules with warnings as errors --- support/jenkins/buildAllModulesWithErrors.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 support/jenkins/buildAllModulesWithErrors.py diff --git a/support/jenkins/buildAllModulesWithErrors.py b/support/jenkins/buildAllModulesWithErrors.py new file mode 100644 index 0000000000..d113e7d5dc --- /dev/null +++ b/support/jenkins/buildAllModulesWithErrors.py @@ -0,0 +1,14 @@ +import os +from subprocess import call + +# To be called from the build folder in the OpenSpace +modules = os.listdir("../modules") + +cmd = ["cmake"] +cmd.append("-DGHOUL_USE_DEVIL=OFF") +cmd.append("-OPENSPACE_WARNINGS_AS_ERRORS=ON") +for m in modules: + cmd.append("-DOPENSPACE_MODULE_" + m.upper() + "=ON") + +cmd.append("..") +call(cmd) \ No newline at end of file From 630fc3fc969af76005f7ca55d1ba431934d288a7 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 27 May 2015 13:15:52 +0200 Subject: [PATCH 083/329] Increase warning level for modules and main library in Unix --- support/cmake/module_definition.cmake | 2 +- support/cmake/support_macros.cmake | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/support/cmake/module_definition.cmake b/support/cmake/module_definition.cmake index c1c6be037e..59b381864f 100644 --- a/support/cmake/module_definition.cmake +++ b/support/cmake/module_definition.cmake @@ -120,7 +120,7 @@ function (set_common_compile_settings target_name) message(FATAL_ERROR "Compiler does not have C++11 support") endif () - target_compile_options(${library_name} PUBLIC "-ggdb") + target_compile_options(${library_name} PUBLIC "-ggdb" "-Wall" "-Wno-long-long" "-pedantic" "-Wextra") if (OPENSPACE_WARNINGS_AS_ERRORS) target_compile_options(${library_name} PUBLIC "-Werror") endif () diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 73219f13da..048f9249f3 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -140,7 +140,8 @@ function (set_compile_settings) target_compile_options(OpenSpace PUBLIC "-Werror") endif () - target_compile_options(libOpenSpace PUBLIC "-ggdb") + target_compile_options(libOpenSpace PUBLIC "-ggdb" "-Wall" "-Wno-long-long" "-pedantic" "-Wextra") + target_compile_options(OpenSpace PUBLIC "-ggdb" "-Wall" "-Wno-long-long" "-pedantic" "-Wextra") endif () endfunction () From b18ccfad23fd81499f533367c259502ba12b1e84 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 27 May 2015 16:47:45 +0200 Subject: [PATCH 084/329] Add DownloadEngine Add curl library --- ext/curl/include/curl/Makefile.am | 53 + ext/curl/include/curl/Makefile.in | 692 ++++++ ext/curl/include/curl/curl.h | 2362 +++++++++++++++++++++ ext/curl/include/curl/curlbuild.h | 585 +++++ ext/curl/include/curl/curlbuild.h.cmake | 197 ++ ext/curl/include/curl/curlbuild.h.in | 197 ++ ext/curl/include/curl/curlrules.h | 262 +++ ext/curl/include/curl/curlver.h | 69 + ext/curl/include/curl/easy.h | 102 + ext/curl/include/curl/mprintf.h | 74 + ext/curl/include/curl/multi.h | 399 ++++ ext/curl/include/curl/stdcheaders.h | 33 + ext/curl/include/curl/typecheck-gcc.h | 610 ++++++ ext/curl/lib/libcurl.dll | Bin 0 -> 278016 bytes ext/curl/lib/libcurl_imp.lib | Bin 0 -> 13608 bytes include/openspace/engine/downloadengine.h | 47 + src/CMakeLists.txt | 2 + src/engine/downloadengine.cpp | 73 + src/engine/openspaceengine.cpp | 5 +- support/cmake/support_macros.cmake | 19 + 20 files changed, 5780 insertions(+), 1 deletion(-) create mode 100644 ext/curl/include/curl/Makefile.am create mode 100644 ext/curl/include/curl/Makefile.in create mode 100644 ext/curl/include/curl/curl.h create mode 100644 ext/curl/include/curl/curlbuild.h create mode 100644 ext/curl/include/curl/curlbuild.h.cmake create mode 100644 ext/curl/include/curl/curlbuild.h.in create mode 100644 ext/curl/include/curl/curlrules.h create mode 100644 ext/curl/include/curl/curlver.h create mode 100644 ext/curl/include/curl/easy.h create mode 100644 ext/curl/include/curl/mprintf.h create mode 100644 ext/curl/include/curl/multi.h create mode 100644 ext/curl/include/curl/stdcheaders.h create mode 100644 ext/curl/include/curl/typecheck-gcc.h create mode 100644 ext/curl/lib/libcurl.dll create mode 100644 ext/curl/lib/libcurl_imp.lib create mode 100644 include/openspace/engine/downloadengine.h create mode 100644 src/engine/downloadengine.cpp diff --git a/ext/curl/include/curl/Makefile.am b/ext/curl/include/curl/Makefile.am new file mode 100644 index 0000000000..86e8b78344 --- /dev/null +++ b/ext/curl/include/curl/Makefile.am @@ -0,0 +1,53 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +########################################################################### +pkginclude_HEADERS = \ + curl.h curlver.h easy.h mprintf.h stdcheaders.h multi.h \ + typecheck-gcc.h curlbuild.h curlrules.h + +pkgincludedir= $(includedir)/curl + +# curlbuild.h does not exist in the git tree. When the original libcurl +# source code distribution archive file is created, curlbuild.h.dist is +# renamed to curlbuild.h and included in the tarball so that it can be +# used directly on non-configure systems. +# +# The distributed curlbuild.h will be overwritten on configure systems +# when the configure script runs, with one that is suitable and specific +# to the library being configured and built. +# +# curlbuild.h.in is the distributed template file from which the configure +# script creates curlbuild.h at library configuration time, overwiting the +# one included in the distribution archive. +# +# curlbuild.h.dist is not included in the source code distribution archive. + +EXTRA_DIST = curlbuild.h.in + +DISTCLEANFILES = curlbuild.h + +checksrc: + @@PERL@ $(top_srcdir)/lib/checksrc.pl -Wcurlbuild.h -D$(top_srcdir)/include/curl $(pkginclude_HEADERS) $(EXTRA_DIST) + +if CURLDEBUG +# for debug builds, we scan the sources on all regular make invokes +all-local: checksrc +endif diff --git a/ext/curl/include/curl/Makefile.in b/ext/curl/include/curl/Makefile.in new file mode 100644 index 0000000000..fa07d3258c --- /dev/null +++ b/ext/curl/include/curl/Makefile.in @@ -0,0 +1,692 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = include/curl +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(srcdir)/curlbuild.h.in $(top_srcdir)/mkinstalldirs \ + $(pkginclude_HEADERS) +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/curl-compilers.m4 \ + $(top_srcdir)/m4/curl-confopts.m4 \ + $(top_srcdir)/m4/curl-functions.m4 \ + $(top_srcdir)/m4/curl-openssl.m4 \ + $(top_srcdir)/m4/curl-override.m4 \ + $(top_srcdir)/m4/curl-reentrant.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/xc-am-iface.m4 \ + $(top_srcdir)/m4/xc-cc-check.m4 \ + $(top_srcdir)/m4/xc-lt-iface.m4 \ + $(top_srcdir)/m4/xc-translit.m4 \ + $(top_srcdir)/m4/xc-val-flgs.m4 \ + $(top_srcdir)/m4/zz40-xc-ovr.m4 \ + $(top_srcdir)/m4/zz50-xc-ovr.m4 \ + $(top_srcdir)/m4/zz60-xc-ovr.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/lib/curl_config.h curlbuild.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkgincludedir)" +HEADERS = $(pkginclude_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ + $(LISP)curlbuild.h.in +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +pkgincludedir = $(includedir)/curl +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BLANK_AT_MAKETIME = @BLANK_AT_MAKETIME@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CFLAG_CURL_SYMBOL_HIDING = @CFLAG_CURL_SYMBOL_HIDING@ +CONFIGURE_OPTIONS = @CONFIGURE_OPTIONS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAG_CURL_STATICLIB = @CPPFLAG_CURL_STATICLIB@ +CURLVERSION = @CURLVERSION@ +CURL_CA_BUNDLE = @CURL_CA_BUNDLE@ +CURL_CFLAG_EXTRAS = @CURL_CFLAG_EXTRAS@ +CURL_DISABLE_DICT = @CURL_DISABLE_DICT@ +CURL_DISABLE_FILE = @CURL_DISABLE_FILE@ +CURL_DISABLE_FTP = @CURL_DISABLE_FTP@ +CURL_DISABLE_GOPHER = @CURL_DISABLE_GOPHER@ +CURL_DISABLE_HTTP = @CURL_DISABLE_HTTP@ +CURL_DISABLE_IMAP = @CURL_DISABLE_IMAP@ +CURL_DISABLE_LDAP = @CURL_DISABLE_LDAP@ +CURL_DISABLE_LDAPS = @CURL_DISABLE_LDAPS@ +CURL_DISABLE_POP3 = @CURL_DISABLE_POP3@ +CURL_DISABLE_PROXY = @CURL_DISABLE_PROXY@ +CURL_DISABLE_RTSP = @CURL_DISABLE_RTSP@ +CURL_DISABLE_SMB = @CURL_DISABLE_SMB@ +CURL_DISABLE_SMTP = @CURL_DISABLE_SMTP@ +CURL_DISABLE_TELNET = @CURL_DISABLE_TELNET@ +CURL_DISABLE_TFTP = @CURL_DISABLE_TFTP@ +CURL_LT_SHLIB_VERSIONED_FLAVOUR = @CURL_LT_SHLIB_VERSIONED_FLAVOUR@ +CURL_NETWORK_AND_TIME_LIBS = @CURL_NETWORK_AND_TIME_LIBS@ +CURL_NETWORK_LIBS = @CURL_NETWORK_LIBS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ENABLE_SHARED = @ENABLE_SHARED@ +ENABLE_STATIC = @ENABLE_STATIC@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_GNUTLS_SRP = @HAVE_GNUTLS_SRP@ +HAVE_LDAP_SSL = @HAVE_LDAP_SSL@ +HAVE_LIBZ = @HAVE_LIBZ@ +HAVE_OPENSSL_SRP = @HAVE_OPENSSL_SRP@ +IDN_ENABLED = @IDN_ENABLED@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +IPV6_ENABLED = @IPV6_ENABLED@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBCURL_LIBS = @LIBCURL_LIBS@ +LIBMETALINK_CPPFLAGS = @LIBMETALINK_CPPFLAGS@ +LIBMETALINK_LDFLAGS = @LIBMETALINK_LDFLAGS@ +LIBMETALINK_LIBS = @LIBMETALINK_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MANOPT = @MANOPT@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NROFF = @NROFF@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKGADD_NAME = @PKGADD_NAME@ +PKGADD_PKG = @PKGADD_PKG@ +PKGADD_VENDOR = @PKGADD_VENDOR@ +PKGCONFIG = @PKGCONFIG@ +RANDOM_FILE = @RANDOM_FILE@ +RANLIB = @RANLIB@ +REQUIRE_LIB_DEPS = @REQUIRE_LIB_DEPS@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SSL_ENABLED = @SSL_ENABLED@ +SSL_LIBS = @SSL_LIBS@ +STRIP = @STRIP@ +SUPPORT_FEATURES = @SUPPORT_FEATURES@ +SUPPORT_PROTOCOLS = @SUPPORT_PROTOCOLS@ +USE_ARES = @USE_ARES@ +USE_AXTLS = @USE_AXTLS@ +USE_CYASSL = @USE_CYASSL@ +USE_DARWINSSL = @USE_DARWINSSL@ +USE_GNUTLS = @USE_GNUTLS@ +USE_GNUTLS_NETTLE = @USE_GNUTLS_NETTLE@ +USE_LIBRTMP = @USE_LIBRTMP@ +USE_LIBSSH2 = @USE_LIBSSH2@ +USE_NGHTTP2 = @USE_NGHTTP2@ +USE_NSS = @USE_NSS@ +USE_OPENLDAP = @USE_OPENLDAP@ +USE_POLARSSL = @USE_POLARSSL@ +USE_SCHANNEL = @USE_SCHANNEL@ +USE_UNIX_SOCKETS = @USE_UNIX_SOCKETS@ +USE_WINDOWS_SSPI = @USE_WINDOWS_SSPI@ +VERSION = @VERSION@ +VERSIONNUM = @VERSIONNUM@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +libext = @libext@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +########################################################################### +pkginclude_HEADERS = \ + curl.h curlver.h easy.h mprintf.h stdcheaders.h multi.h \ + typecheck-gcc.h curlbuild.h curlrules.h + + +# curlbuild.h does not exist in the git tree. When the original libcurl +# source code distribution archive file is created, curlbuild.h.dist is +# renamed to curlbuild.h and included in the tarball so that it can be +# used directly on non-configure systems. +# +# The distributed curlbuild.h will be overwritten on configure systems +# when the configure script runs, with one that is suitable and specific +# to the library being configured and built. +# +# curlbuild.h.in is the distributed template file from which the configure +# script creates curlbuild.h at library configuration time, overwiting the +# one included in the distribution archive. +# +# curlbuild.h.dist is not included in the source code distribution archive. +EXTRA_DIST = curlbuild.h.in +DISTCLEANFILES = curlbuild.h +all: curlbuild.h + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign include/curl/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign include/curl/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +curlbuild.h: stamp-h2 + @test -f $@ || rm -f stamp-h2 + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h2 + +stamp-h2: $(srcdir)/curlbuild.h.in $(top_builddir)/config.status + @rm -f stamp-h2 + cd $(top_builddir) && $(SHELL) ./config.status include/curl/curlbuild.h + +distclean-hdr: + -rm -f curlbuild.h stamp-h2 + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkgincludeHEADERS: $(pkginclude_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgincludedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \ + done + +uninstall-pkgincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgincludedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +@CURLDEBUG_FALSE@all-local: +all-am: Makefile $(HEADERS) curlbuild.h all-local +installdirs: + for dir in "$(DESTDIR)$(pkgincludedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-hdr distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pkgincludeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pkgincludeHEADERS + +.MAKE: all install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \ + clean-generic clean-libtool cscopelist-am ctags ctags-am \ + distclean distclean-generic distclean-hdr distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-pkgincludeHEADERS \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-pkgincludeHEADERS + + +checksrc: + @@PERL@ $(top_srcdir)/lib/checksrc.pl -Wcurlbuild.h -D$(top_srcdir)/include/curl $(pkginclude_HEADERS) $(EXTRA_DIST) + +# for debug builds, we scan the sources on all regular make invokes +@CURLDEBUG_TRUE@all-local: checksrc + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/ext/curl/include/curl/curl.h b/ext/curl/include/curl/curl.h new file mode 100644 index 0000000000..ae1b0e4dbc --- /dev/null +++ b/ext/curl/include/curl/curl.h @@ -0,0 +1,2362 @@ +#ifndef __CURL_CURL_H +#define __CURL_CURL_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2015, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* + * If you have libcurl problems, all docs and details are found here: + * http://curl.haxx.se/libcurl/ + * + * curl-library mailing list subscription and unsubscription web interface: + * http://cool.haxx.se/mailman/listinfo/curl-library/ + */ + +#include "curlver.h" /* libcurl version defines */ +#include "curlbuild.h" /* libcurl build definitions */ +#include "curlrules.h" /* libcurl rules enforcement */ + +/* + * Define WIN32 when build target is Win32 API + */ + +#if (defined(_WIN32) || defined(__WIN32__)) && \ + !defined(WIN32) && !defined(__SYMBIAN32__) +#define WIN32 +#endif + +#include +#include + +#if defined(__FreeBSD__) && (__FreeBSD__ >= 2) +/* Needed for __FreeBSD_version symbol definition */ +#include +#endif + +/* The include stuff here below is mainly for time_t! */ +#include +#include + +#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__CYGWIN__) +#if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H) || defined(__LWIP_OPT_H__)) +/* The check above prevents the winsock2 inclusion if winsock.h already was + included, since they can't co-exist without problems */ +#include +#include +#endif +#endif + +/* HP-UX systems version 9, 10 and 11 lack sys/select.h and so does oldish + libc5-based Linux systems. Only include it on systems that are known to + require it! */ +#if defined(_AIX) || defined(__NOVELL_LIBC__) || defined(__NetBSD__) || \ + defined(__minix) || defined(__SYMBIAN32__) || defined(__INTEGRITY) || \ + defined(ANDROID) || defined(__ANDROID__) || defined(__OpenBSD__) || \ + (defined(__FreeBSD_version) && (__FreeBSD_version < 800000)) +#include +#endif + +#if !defined(WIN32) && !defined(_WIN32_WCE) +#include +#endif + +#if !defined(WIN32) && !defined(__WATCOMC__) && !defined(__VXWORKS__) +#include +#endif + +#ifdef __BEOS__ +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void CURL; + +/* + * libcurl external API function linkage decorations. + */ + +#ifdef CURL_STATICLIB +# define CURL_EXTERN +#elif defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__) +# if defined(BUILDING_LIBCURL) +# define CURL_EXTERN __declspec(dllexport) +# else +# define CURL_EXTERN __declspec(dllimport) +# endif +#elif defined(BUILDING_LIBCURL) && defined(CURL_HIDDEN_SYMBOLS) +# define CURL_EXTERN CURL_EXTERN_SYMBOL +#else +# define CURL_EXTERN +#endif + +#ifndef curl_socket_typedef +/* socket typedef */ +#if defined(WIN32) && !defined(__LWIP_OPT_H__) +typedef SOCKET curl_socket_t; +#define CURL_SOCKET_BAD INVALID_SOCKET +#else +typedef int curl_socket_t; +#define CURL_SOCKET_BAD -1 +#endif +#define curl_socket_typedef +#endif /* curl_socket_typedef */ + +struct curl_httppost { + struct curl_httppost *next; /* next entry in the list */ + char *name; /* pointer to allocated name */ + long namelength; /* length of name length */ + char *contents; /* pointer to allocated data contents */ + long contentslength; /* length of contents field */ + char *buffer; /* pointer to allocated buffer contents */ + long bufferlength; /* length of buffer field */ + char *contenttype; /* Content-Type */ + struct curl_slist* contentheader; /* list of extra headers for this form */ + struct curl_httppost *more; /* if one field name has more than one + file, this link should link to following + files */ + long flags; /* as defined below */ +#define HTTPPOST_FILENAME (1<<0) /* specified content is a file name */ +#define HTTPPOST_READFILE (1<<1) /* specified content is a file name */ +#define HTTPPOST_PTRNAME (1<<2) /* name is only stored pointer + do not free in formfree */ +#define HTTPPOST_PTRCONTENTS (1<<3) /* contents is only stored pointer + do not free in formfree */ +#define HTTPPOST_BUFFER (1<<4) /* upload file from buffer */ +#define HTTPPOST_PTRBUFFER (1<<5) /* upload file from pointer contents */ +#define HTTPPOST_CALLBACK (1<<6) /* upload file contents by using the + regular read callback to get the data + and pass the given pointer as custom + pointer */ + + char *showfilename; /* The file name to show. If not set, the + actual file name will be used (if this + is a file part) */ + void *userp; /* custom pointer used for + HTTPPOST_CALLBACK posts */ +}; + +/* This is the CURLOPT_PROGRESSFUNCTION callback proto. It is now considered + deprecated but was the only choice up until 7.31.0 */ +typedef int (*curl_progress_callback)(void *clientp, + double dltotal, + double dlnow, + double ultotal, + double ulnow); + +/* This is the CURLOPT_XFERINFOFUNCTION callback proto. It was introduced in + 7.32.0, it avoids floating point and provides more detailed information. */ +typedef int (*curl_xferinfo_callback)(void *clientp, + curl_off_t dltotal, + curl_off_t dlnow, + curl_off_t ultotal, + curl_off_t ulnow); + +#ifndef CURL_MAX_WRITE_SIZE + /* Tests have proven that 20K is a very bad buffer size for uploads on + Windows, while 16K for some odd reason performed a lot better. + We do the ifndef check to allow this value to easier be changed at build + time for those who feel adventurous. The practical minimum is about + 400 bytes since libcurl uses a buffer of this size as a scratch area + (unrelated to network send operations). */ +#define CURL_MAX_WRITE_SIZE 16384 +#endif + +#ifndef CURL_MAX_HTTP_HEADER +/* The only reason to have a max limit for this is to avoid the risk of a bad + server feeding libcurl with a never-ending header that will cause reallocs + infinitely */ +#define CURL_MAX_HTTP_HEADER (100*1024) +#endif + +/* This is a magic return code for the write callback that, when returned, + will signal libcurl to pause receiving on the current transfer. */ +#define CURL_WRITEFUNC_PAUSE 0x10000001 + +typedef size_t (*curl_write_callback)(char *buffer, + size_t size, + size_t nitems, + void *outstream); + + + +/* enumeration of file types */ +typedef enum { + CURLFILETYPE_FILE = 0, + CURLFILETYPE_DIRECTORY, + CURLFILETYPE_SYMLINK, + CURLFILETYPE_DEVICE_BLOCK, + CURLFILETYPE_DEVICE_CHAR, + CURLFILETYPE_NAMEDPIPE, + CURLFILETYPE_SOCKET, + CURLFILETYPE_DOOR, /* is possible only on Sun Solaris now */ + + CURLFILETYPE_UNKNOWN /* should never occur */ +} curlfiletype; + +#define CURLFINFOFLAG_KNOWN_FILENAME (1<<0) +#define CURLFINFOFLAG_KNOWN_FILETYPE (1<<1) +#define CURLFINFOFLAG_KNOWN_TIME (1<<2) +#define CURLFINFOFLAG_KNOWN_PERM (1<<3) +#define CURLFINFOFLAG_KNOWN_UID (1<<4) +#define CURLFINFOFLAG_KNOWN_GID (1<<5) +#define CURLFINFOFLAG_KNOWN_SIZE (1<<6) +#define CURLFINFOFLAG_KNOWN_HLINKCOUNT (1<<7) + +/* Content of this structure depends on information which is known and is + achievable (e.g. by FTP LIST parsing). Please see the url_easy_setopt(3) man + page for callbacks returning this structure -- some fields are mandatory, + some others are optional. The FLAG field has special meaning. */ +struct curl_fileinfo { + char *filename; + curlfiletype filetype; + time_t time; + unsigned int perm; + int uid; + int gid; + curl_off_t size; + long int hardlinks; + + struct { + /* If some of these fields is not NULL, it is a pointer to b_data. */ + char *time; + char *perm; + char *user; + char *group; + char *target; /* pointer to the target filename of a symlink */ + } strings; + + unsigned int flags; + + /* used internally */ + char * b_data; + size_t b_size; + size_t b_used; +}; + +/* return codes for CURLOPT_CHUNK_BGN_FUNCTION */ +#define CURL_CHUNK_BGN_FUNC_OK 0 +#define CURL_CHUNK_BGN_FUNC_FAIL 1 /* tell the lib to end the task */ +#define CURL_CHUNK_BGN_FUNC_SKIP 2 /* skip this chunk over */ + +/* if splitting of data transfer is enabled, this callback is called before + download of an individual chunk started. Note that parameter "remains" works + only for FTP wildcard downloading (for now), otherwise is not used */ +typedef long (*curl_chunk_bgn_callback)(const void *transfer_info, + void *ptr, + int remains); + +/* return codes for CURLOPT_CHUNK_END_FUNCTION */ +#define CURL_CHUNK_END_FUNC_OK 0 +#define CURL_CHUNK_END_FUNC_FAIL 1 /* tell the lib to end the task */ + +/* If splitting of data transfer is enabled this callback is called after + download of an individual chunk finished. + Note! After this callback was set then it have to be called FOR ALL chunks. + Even if downloading of this chunk was skipped in CHUNK_BGN_FUNC. + This is the reason why we don't need "transfer_info" parameter in this + callback and we are not interested in "remains" parameter too. */ +typedef long (*curl_chunk_end_callback)(void *ptr); + +/* return codes for FNMATCHFUNCTION */ +#define CURL_FNMATCHFUNC_MATCH 0 /* string corresponds to the pattern */ +#define CURL_FNMATCHFUNC_NOMATCH 1 /* pattern doesn't match the string */ +#define CURL_FNMATCHFUNC_FAIL 2 /* an error occurred */ + +/* callback type for wildcard downloading pattern matching. If the + string matches the pattern, return CURL_FNMATCHFUNC_MATCH value, etc. */ +typedef int (*curl_fnmatch_callback)(void *ptr, + const char *pattern, + const char *string); + +/* These are the return codes for the seek callbacks */ +#define CURL_SEEKFUNC_OK 0 +#define CURL_SEEKFUNC_FAIL 1 /* fail the entire transfer */ +#define CURL_SEEKFUNC_CANTSEEK 2 /* tell libcurl seeking can't be done, so + libcurl might try other means instead */ +typedef int (*curl_seek_callback)(void *instream, + curl_off_t offset, + int origin); /* 'whence' */ + +/* This is a return code for the read callback that, when returned, will + signal libcurl to immediately abort the current transfer. */ +#define CURL_READFUNC_ABORT 0x10000000 +/* This is a return code for the read callback that, when returned, will + signal libcurl to pause sending data on the current transfer. */ +#define CURL_READFUNC_PAUSE 0x10000001 + +typedef size_t (*curl_read_callback)(char *buffer, + size_t size, + size_t nitems, + void *instream); + +typedef enum { + CURLSOCKTYPE_IPCXN, /* socket created for a specific IP connection */ + CURLSOCKTYPE_ACCEPT, /* socket created by accept() call */ + CURLSOCKTYPE_LAST /* never use */ +} curlsocktype; + +/* The return code from the sockopt_callback can signal information back + to libcurl: */ +#define CURL_SOCKOPT_OK 0 +#define CURL_SOCKOPT_ERROR 1 /* causes libcurl to abort and return + CURLE_ABORTED_BY_CALLBACK */ +#define CURL_SOCKOPT_ALREADY_CONNECTED 2 + +typedef int (*curl_sockopt_callback)(void *clientp, + curl_socket_t curlfd, + curlsocktype purpose); + +struct curl_sockaddr { + int family; + int socktype; + int protocol; + unsigned int addrlen; /* addrlen was a socklen_t type before 7.18.0 but it + turned really ugly and painful on the systems that + lack this type */ + struct sockaddr addr; +}; + +typedef curl_socket_t +(*curl_opensocket_callback)(void *clientp, + curlsocktype purpose, + struct curl_sockaddr *address); + +typedef int +(*curl_closesocket_callback)(void *clientp, curl_socket_t item); + +typedef enum { + CURLIOE_OK, /* I/O operation successful */ + CURLIOE_UNKNOWNCMD, /* command was unknown to callback */ + CURLIOE_FAILRESTART, /* failed to restart the read */ + CURLIOE_LAST /* never use */ +} curlioerr; + +typedef enum { + CURLIOCMD_NOP, /* no operation */ + CURLIOCMD_RESTARTREAD, /* restart the read stream from start */ + CURLIOCMD_LAST /* never use */ +} curliocmd; + +typedef curlioerr (*curl_ioctl_callback)(CURL *handle, + int cmd, + void *clientp); + +/* + * The following typedef's are signatures of malloc, free, realloc, strdup and + * calloc respectively. Function pointers of these types can be passed to the + * curl_global_init_mem() function to set user defined memory management + * callback routines. + */ +typedef void *(*curl_malloc_callback)(size_t size); +typedef void (*curl_free_callback)(void *ptr); +typedef void *(*curl_realloc_callback)(void *ptr, size_t size); +typedef char *(*curl_strdup_callback)(const char *str); +typedef void *(*curl_calloc_callback)(size_t nmemb, size_t size); + +/* the kind of data that is passed to information_callback*/ +typedef enum { + CURLINFO_TEXT = 0, + CURLINFO_HEADER_IN, /* 1 */ + CURLINFO_HEADER_OUT, /* 2 */ + CURLINFO_DATA_IN, /* 3 */ + CURLINFO_DATA_OUT, /* 4 */ + CURLINFO_SSL_DATA_IN, /* 5 */ + CURLINFO_SSL_DATA_OUT, /* 6 */ + CURLINFO_END +} curl_infotype; + +typedef int (*curl_debug_callback) + (CURL *handle, /* the handle/transfer this concerns */ + curl_infotype type, /* what kind of data */ + char *data, /* points to the data */ + size_t size, /* size of the data pointed to */ + void *userptr); /* whatever the user please */ + +/* All possible error codes from all sorts of curl functions. Future versions + may return other values, stay prepared. + + Always add new return codes last. Never *EVER* remove any. The return + codes must remain the same! + */ + +typedef enum { + CURLE_OK = 0, + CURLE_UNSUPPORTED_PROTOCOL, /* 1 */ + CURLE_FAILED_INIT, /* 2 */ + CURLE_URL_MALFORMAT, /* 3 */ + CURLE_NOT_BUILT_IN, /* 4 - [was obsoleted in August 2007 for + 7.17.0, reused in April 2011 for 7.21.5] */ + CURLE_COULDNT_RESOLVE_PROXY, /* 5 */ + CURLE_COULDNT_RESOLVE_HOST, /* 6 */ + CURLE_COULDNT_CONNECT, /* 7 */ + CURLE_FTP_WEIRD_SERVER_REPLY, /* 8 */ + CURLE_REMOTE_ACCESS_DENIED, /* 9 a service was denied by the server + due to lack of access - when login fails + this is not returned. */ + CURLE_FTP_ACCEPT_FAILED, /* 10 - [was obsoleted in April 2006 for + 7.15.4, reused in Dec 2011 for 7.24.0]*/ + CURLE_FTP_WEIRD_PASS_REPLY, /* 11 */ + CURLE_FTP_ACCEPT_TIMEOUT, /* 12 - timeout occurred accepting server + [was obsoleted in August 2007 for 7.17.0, + reused in Dec 2011 for 7.24.0]*/ + CURLE_FTP_WEIRD_PASV_REPLY, /* 13 */ + CURLE_FTP_WEIRD_227_FORMAT, /* 14 */ + CURLE_FTP_CANT_GET_HOST, /* 15 */ + CURLE_HTTP2, /* 16 - A problem in the http2 framing layer. + [was obsoleted in August 2007 for 7.17.0, + reused in July 2014 for 7.38.0] */ + CURLE_FTP_COULDNT_SET_TYPE, /* 17 */ + CURLE_PARTIAL_FILE, /* 18 */ + CURLE_FTP_COULDNT_RETR_FILE, /* 19 */ + CURLE_OBSOLETE20, /* 20 - NOT USED */ + CURLE_QUOTE_ERROR, /* 21 - quote command failure */ + CURLE_HTTP_RETURNED_ERROR, /* 22 */ + CURLE_WRITE_ERROR, /* 23 */ + CURLE_OBSOLETE24, /* 24 - NOT USED */ + CURLE_UPLOAD_FAILED, /* 25 - failed upload "command" */ + CURLE_READ_ERROR, /* 26 - couldn't open/read from file */ + CURLE_OUT_OF_MEMORY, /* 27 */ + /* Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error + instead of a memory allocation error if CURL_DOES_CONVERSIONS + is defined + */ + CURLE_OPERATION_TIMEDOUT, /* 28 - the timeout time was reached */ + CURLE_OBSOLETE29, /* 29 - NOT USED */ + CURLE_FTP_PORT_FAILED, /* 30 - FTP PORT operation failed */ + CURLE_FTP_COULDNT_USE_REST, /* 31 - the REST command failed */ + CURLE_OBSOLETE32, /* 32 - NOT USED */ + CURLE_RANGE_ERROR, /* 33 - RANGE "command" didn't work */ + CURLE_HTTP_POST_ERROR, /* 34 */ + CURLE_SSL_CONNECT_ERROR, /* 35 - wrong when connecting with SSL */ + CURLE_BAD_DOWNLOAD_RESUME, /* 36 - couldn't resume download */ + CURLE_FILE_COULDNT_READ_FILE, /* 37 */ + CURLE_LDAP_CANNOT_BIND, /* 38 */ + CURLE_LDAP_SEARCH_FAILED, /* 39 */ + CURLE_OBSOLETE40, /* 40 - NOT USED */ + CURLE_FUNCTION_NOT_FOUND, /* 41 */ + CURLE_ABORTED_BY_CALLBACK, /* 42 */ + CURLE_BAD_FUNCTION_ARGUMENT, /* 43 */ + CURLE_OBSOLETE44, /* 44 - NOT USED */ + CURLE_INTERFACE_FAILED, /* 45 - CURLOPT_INTERFACE failed */ + CURLE_OBSOLETE46, /* 46 - NOT USED */ + CURLE_TOO_MANY_REDIRECTS , /* 47 - catch endless re-direct loops */ + CURLE_UNKNOWN_OPTION, /* 48 - User specified an unknown option */ + CURLE_TELNET_OPTION_SYNTAX , /* 49 - Malformed telnet option */ + CURLE_OBSOLETE50, /* 50 - NOT USED */ + CURLE_PEER_FAILED_VERIFICATION, /* 51 - peer's certificate or fingerprint + wasn't verified fine */ + CURLE_GOT_NOTHING, /* 52 - when this is a specific error */ + CURLE_SSL_ENGINE_NOTFOUND, /* 53 - SSL crypto engine not found */ + CURLE_SSL_ENGINE_SETFAILED, /* 54 - can not set SSL crypto engine as + default */ + CURLE_SEND_ERROR, /* 55 - failed sending network data */ + CURLE_RECV_ERROR, /* 56 - failure in receiving network data */ + CURLE_OBSOLETE57, /* 57 - NOT IN USE */ + CURLE_SSL_CERTPROBLEM, /* 58 - problem with the local certificate */ + CURLE_SSL_CIPHER, /* 59 - couldn't use specified cipher */ + CURLE_SSL_CACERT, /* 60 - problem with the CA cert (path?) */ + CURLE_BAD_CONTENT_ENCODING, /* 61 - Unrecognized/bad encoding */ + CURLE_LDAP_INVALID_URL, /* 62 - Invalid LDAP URL */ + CURLE_FILESIZE_EXCEEDED, /* 63 - Maximum file size exceeded */ + CURLE_USE_SSL_FAILED, /* 64 - Requested FTP SSL level failed */ + CURLE_SEND_FAIL_REWIND, /* 65 - Sending the data requires a rewind + that failed */ + CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialise ENGINE */ + CURLE_LOGIN_DENIED, /* 67 - user, password or similar was not + accepted and we failed to login */ + CURLE_TFTP_NOTFOUND, /* 68 - file not found on server */ + CURLE_TFTP_PERM, /* 69 - permission problem on server */ + CURLE_REMOTE_DISK_FULL, /* 70 - out of disk space on server */ + CURLE_TFTP_ILLEGAL, /* 71 - Illegal TFTP operation */ + CURLE_TFTP_UNKNOWNID, /* 72 - Unknown transfer ID */ + CURLE_REMOTE_FILE_EXISTS, /* 73 - File already exists */ + CURLE_TFTP_NOSUCHUSER, /* 74 - No such user */ + CURLE_CONV_FAILED, /* 75 - conversion failed */ + CURLE_CONV_REQD, /* 76 - caller must register conversion + callbacks using curl_easy_setopt options + CURLOPT_CONV_FROM_NETWORK_FUNCTION, + CURLOPT_CONV_TO_NETWORK_FUNCTION, and + CURLOPT_CONV_FROM_UTF8_FUNCTION */ + CURLE_SSL_CACERT_BADFILE, /* 77 - could not load CACERT file, missing + or wrong format */ + CURLE_REMOTE_FILE_NOT_FOUND, /* 78 - remote file not found */ + CURLE_SSH, /* 79 - error from the SSH layer, somewhat + generic so the error message will be of + interest when this has happened */ + + CURLE_SSL_SHUTDOWN_FAILED, /* 80 - Failed to shut down the SSL + connection */ + CURLE_AGAIN, /* 81 - socket is not ready for send/recv, + wait till it's ready and try again (Added + in 7.18.2) */ + CURLE_SSL_CRL_BADFILE, /* 82 - could not load CRL file, missing or + wrong format (Added in 7.19.0) */ + CURLE_SSL_ISSUER_ERROR, /* 83 - Issuer check failed. (Added in + 7.19.0) */ + CURLE_FTP_PRET_FAILED, /* 84 - a PRET command failed */ + CURLE_RTSP_CSEQ_ERROR, /* 85 - mismatch of RTSP CSeq numbers */ + CURLE_RTSP_SESSION_ERROR, /* 86 - mismatch of RTSP Session Ids */ + CURLE_FTP_BAD_FILE_LIST, /* 87 - unable to parse FTP file list */ + CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */ + CURLE_NO_CONNECTION_AVAILABLE, /* 89 - No connection available, the + session will be queued */ + CURLE_SSL_PINNEDPUBKEYNOTMATCH, /* 90 - specified pinned public key did not + match */ + CURLE_SSL_INVALIDCERTSTATUS, /* 91 - invalid certificate status */ + CURL_LAST /* never use! */ +} CURLcode; + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ + +/* Previously obsolete error code re-used in 7.38.0 */ +#define CURLE_OBSOLETE16 CURLE_HTTP2 + +/* Previously obsolete error codes re-used in 7.24.0 */ +#define CURLE_OBSOLETE10 CURLE_FTP_ACCEPT_FAILED +#define CURLE_OBSOLETE12 CURLE_FTP_ACCEPT_TIMEOUT + +/* compatibility with older names */ +#define CURLOPT_ENCODING CURLOPT_ACCEPT_ENCODING + +/* The following were added in 7.21.5, April 2011 */ +#define CURLE_UNKNOWN_TELNET_OPTION CURLE_UNKNOWN_OPTION + +/* The following were added in 7.17.1 */ +/* These are scheduled to disappear by 2009 */ +#define CURLE_SSL_PEER_CERTIFICATE CURLE_PEER_FAILED_VERIFICATION + +/* The following were added in 7.17.0 */ +/* These are scheduled to disappear by 2009 */ +#define CURLE_OBSOLETE CURLE_OBSOLETE50 /* no one should be using this! */ +#define CURLE_BAD_PASSWORD_ENTERED CURLE_OBSOLETE46 +#define CURLE_BAD_CALLING_ORDER CURLE_OBSOLETE44 +#define CURLE_FTP_USER_PASSWORD_INCORRECT CURLE_OBSOLETE10 +#define CURLE_FTP_CANT_RECONNECT CURLE_OBSOLETE16 +#define CURLE_FTP_COULDNT_GET_SIZE CURLE_OBSOLETE32 +#define CURLE_FTP_COULDNT_SET_ASCII CURLE_OBSOLETE29 +#define CURLE_FTP_WEIRD_USER_REPLY CURLE_OBSOLETE12 +#define CURLE_FTP_WRITE_ERROR CURLE_OBSOLETE20 +#define CURLE_LIBRARY_NOT_FOUND CURLE_OBSOLETE40 +#define CURLE_MALFORMAT_USER CURLE_OBSOLETE24 +#define CURLE_SHARE_IN_USE CURLE_OBSOLETE57 +#define CURLE_URL_MALFORMAT_USER CURLE_NOT_BUILT_IN + +#define CURLE_FTP_ACCESS_DENIED CURLE_REMOTE_ACCESS_DENIED +#define CURLE_FTP_COULDNT_SET_BINARY CURLE_FTP_COULDNT_SET_TYPE +#define CURLE_FTP_QUOTE_ERROR CURLE_QUOTE_ERROR +#define CURLE_TFTP_DISKFULL CURLE_REMOTE_DISK_FULL +#define CURLE_TFTP_EXISTS CURLE_REMOTE_FILE_EXISTS +#define CURLE_HTTP_RANGE_ERROR CURLE_RANGE_ERROR +#define CURLE_FTP_SSL_FAILED CURLE_USE_SSL_FAILED + +/* The following were added earlier */ + +#define CURLE_OPERATION_TIMEOUTED CURLE_OPERATION_TIMEDOUT + +#define CURLE_HTTP_NOT_FOUND CURLE_HTTP_RETURNED_ERROR +#define CURLE_HTTP_PORT_FAILED CURLE_INTERFACE_FAILED +#define CURLE_FTP_COULDNT_STOR_FILE CURLE_UPLOAD_FAILED + +#define CURLE_FTP_PARTIAL_FILE CURLE_PARTIAL_FILE +#define CURLE_FTP_BAD_DOWNLOAD_RESUME CURLE_BAD_DOWNLOAD_RESUME + +/* This was the error code 50 in 7.7.3 and a few earlier versions, this + is no longer used by libcurl but is instead #defined here only to not + make programs break */ +#define CURLE_ALREADY_COMPLETE 99999 + +/* Provide defines for really old option names */ +#define CURLOPT_FILE CURLOPT_WRITEDATA /* name changed in 7.9.7 */ +#define CURLOPT_INFILE CURLOPT_READDATA /* name changed in 7.9.7 */ +#define CURLOPT_WRITEHEADER CURLOPT_HEADERDATA + +/* Since long deprecated options with no code in the lib that does anything + with them. */ +#define CURLOPT_WRITEINFO CURLOPT_OBSOLETE40 +#define CURLOPT_CLOSEPOLICY CURLOPT_OBSOLETE72 + +#endif /*!CURL_NO_OLDIES*/ + +/* This prototype applies to all conversion callbacks */ +typedef CURLcode (*curl_conv_callback)(char *buffer, size_t length); + +typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, /* easy handle */ + void *ssl_ctx, /* actually an + OpenSSL SSL_CTX */ + void *userptr); + +typedef enum { + CURLPROXY_HTTP = 0, /* added in 7.10, new in 7.19.4 default is to use + CONNECT HTTP/1.1 */ + CURLPROXY_HTTP_1_0 = 1, /* added in 7.19.4, force to use CONNECT + HTTP/1.0 */ + CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already + in 7.10 */ + CURLPROXY_SOCKS5 = 5, /* added in 7.10 */ + CURLPROXY_SOCKS4A = 6, /* added in 7.18.0 */ + CURLPROXY_SOCKS5_HOSTNAME = 7 /* Use the SOCKS5 protocol but pass along the + host name rather than the IP address. added + in 7.18.0 */ +} curl_proxytype; /* this enum was added in 7.10 */ + +/* + * Bitmasks for CURLOPT_HTTPAUTH and CURLOPT_PROXYAUTH options: + * + * CURLAUTH_NONE - No HTTP authentication + * CURLAUTH_BASIC - HTTP Basic authentication (default) + * CURLAUTH_DIGEST - HTTP Digest authentication + * CURLAUTH_NEGOTIATE - HTTP Negotiate (SPNEGO) authentication + * CURLAUTH_GSSNEGOTIATE - Alias for CURLAUTH_NEGOTIATE (deprecated) + * CURLAUTH_NTLM - HTTP NTLM authentication + * CURLAUTH_DIGEST_IE - HTTP Digest authentication with IE flavour + * CURLAUTH_NTLM_WB - HTTP NTLM authentication delegated to winbind helper + * CURLAUTH_ONLY - Use together with a single other type to force no + * authentication or just that single type + * CURLAUTH_ANY - All fine types set + * CURLAUTH_ANYSAFE - All fine types except Basic + */ + +#define CURLAUTH_NONE ((unsigned long)0) +#define CURLAUTH_BASIC (((unsigned long)1)<<0) +#define CURLAUTH_DIGEST (((unsigned long)1)<<1) +#define CURLAUTH_NEGOTIATE (((unsigned long)1)<<2) +/* Deprecated since the advent of CURLAUTH_NEGOTIATE */ +#define CURLAUTH_GSSNEGOTIATE CURLAUTH_NEGOTIATE +#define CURLAUTH_NTLM (((unsigned long)1)<<3) +#define CURLAUTH_DIGEST_IE (((unsigned long)1)<<4) +#define CURLAUTH_NTLM_WB (((unsigned long)1)<<5) +#define CURLAUTH_ONLY (((unsigned long)1)<<31) +#define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE) +#define CURLAUTH_ANYSAFE (~(CURLAUTH_BASIC|CURLAUTH_DIGEST_IE)) + +#define CURLSSH_AUTH_ANY ~0 /* all types supported by the server */ +#define CURLSSH_AUTH_NONE 0 /* none allowed, silly but complete */ +#define CURLSSH_AUTH_PUBLICKEY (1<<0) /* public/private key files */ +#define CURLSSH_AUTH_PASSWORD (1<<1) /* password */ +#define CURLSSH_AUTH_HOST (1<<2) /* host key files */ +#define CURLSSH_AUTH_KEYBOARD (1<<3) /* keyboard interactive */ +#define CURLSSH_AUTH_AGENT (1<<4) /* agent (ssh-agent, pageant...) */ +#define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY + +#define CURLGSSAPI_DELEGATION_NONE 0 /* no delegation (default) */ +#define CURLGSSAPI_DELEGATION_POLICY_FLAG (1<<0) /* if permitted by policy */ +#define CURLGSSAPI_DELEGATION_FLAG (1<<1) /* delegate always */ + +#define CURL_ERROR_SIZE 256 + +enum curl_khtype { + CURLKHTYPE_UNKNOWN, + CURLKHTYPE_RSA1, + CURLKHTYPE_RSA, + CURLKHTYPE_DSS +}; + +struct curl_khkey { + const char *key; /* points to a zero-terminated string encoded with base64 + if len is zero, otherwise to the "raw" data */ + size_t len; + enum curl_khtype keytype; +}; + +/* this is the set of return values expected from the curl_sshkeycallback + callback */ +enum curl_khstat { + CURLKHSTAT_FINE_ADD_TO_FILE, + CURLKHSTAT_FINE, + CURLKHSTAT_REJECT, /* reject the connection, return an error */ + CURLKHSTAT_DEFER, /* do not accept it, but we can't answer right now so + this causes a CURLE_DEFER error but otherwise the + connection will be left intact etc */ + CURLKHSTAT_LAST /* not for use, only a marker for last-in-list */ +}; + +/* this is the set of status codes pass in to the callback */ +enum curl_khmatch { + CURLKHMATCH_OK, /* match */ + CURLKHMATCH_MISMATCH, /* host found, key mismatch! */ + CURLKHMATCH_MISSING, /* no matching host/key found */ + CURLKHMATCH_LAST /* not for use, only a marker for last-in-list */ +}; + +typedef int + (*curl_sshkeycallback) (CURL *easy, /* easy handle */ + const struct curl_khkey *knownkey, /* known */ + const struct curl_khkey *foundkey, /* found */ + enum curl_khmatch, /* libcurl's view on the keys */ + void *clientp); /* custom pointer passed from app */ + +/* parameter for the CURLOPT_USE_SSL option */ +typedef enum { + CURLUSESSL_NONE, /* do not attempt to use SSL */ + CURLUSESSL_TRY, /* try using SSL, proceed anyway otherwise */ + CURLUSESSL_CONTROL, /* SSL for the control connection or fail */ + CURLUSESSL_ALL, /* SSL for all communication or fail */ + CURLUSESSL_LAST /* not an option, never use */ +} curl_usessl; + +/* Definition of bits for the CURLOPT_SSL_OPTIONS argument: */ + +/* - ALLOW_BEAST tells libcurl to allow the BEAST SSL vulnerability in the + name of improving interoperability with older servers. Some SSL libraries + have introduced work-arounds for this flaw but those work-arounds sometimes + make the SSL communication fail. To regain functionality with those broken + servers, a user can this way allow the vulnerability back. */ +#define CURLSSLOPT_ALLOW_BEAST (1<<0) + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ + +/* Backwards compatibility with older names */ +/* These are scheduled to disappear by 2009 */ + +#define CURLFTPSSL_NONE CURLUSESSL_NONE +#define CURLFTPSSL_TRY CURLUSESSL_TRY +#define CURLFTPSSL_CONTROL CURLUSESSL_CONTROL +#define CURLFTPSSL_ALL CURLUSESSL_ALL +#define CURLFTPSSL_LAST CURLUSESSL_LAST +#define curl_ftpssl curl_usessl +#endif /*!CURL_NO_OLDIES*/ + +/* parameter for the CURLOPT_FTP_SSL_CCC option */ +typedef enum { + CURLFTPSSL_CCC_NONE, /* do not send CCC */ + CURLFTPSSL_CCC_PASSIVE, /* Let the server initiate the shutdown */ + CURLFTPSSL_CCC_ACTIVE, /* Initiate the shutdown */ + CURLFTPSSL_CCC_LAST /* not an option, never use */ +} curl_ftpccc; + +/* parameter for the CURLOPT_FTPSSLAUTH option */ +typedef enum { + CURLFTPAUTH_DEFAULT, /* let libcurl decide */ + CURLFTPAUTH_SSL, /* use "AUTH SSL" */ + CURLFTPAUTH_TLS, /* use "AUTH TLS" */ + CURLFTPAUTH_LAST /* not an option, never use */ +} curl_ftpauth; + +/* parameter for the CURLOPT_FTP_CREATE_MISSING_DIRS option */ +typedef enum { + CURLFTP_CREATE_DIR_NONE, /* do NOT create missing dirs! */ + CURLFTP_CREATE_DIR, /* (FTP/SFTP) if CWD fails, try MKD and then CWD + again if MKD succeeded, for SFTP this does + similar magic */ + CURLFTP_CREATE_DIR_RETRY, /* (FTP only) if CWD fails, try MKD and then CWD + again even if MKD failed! */ + CURLFTP_CREATE_DIR_LAST /* not an option, never use */ +} curl_ftpcreatedir; + +/* parameter for the CURLOPT_FTP_FILEMETHOD option */ +typedef enum { + CURLFTPMETHOD_DEFAULT, /* let libcurl pick */ + CURLFTPMETHOD_MULTICWD, /* single CWD operation for each path part */ + CURLFTPMETHOD_NOCWD, /* no CWD at all */ + CURLFTPMETHOD_SINGLECWD, /* one CWD to full dir, then work on file */ + CURLFTPMETHOD_LAST /* not an option, never use */ +} curl_ftpmethod; + +/* bitmask defines for CURLOPT_HEADEROPT */ +#define CURLHEADER_UNIFIED 0 +#define CURLHEADER_SEPARATE (1<<0) + +/* CURLPROTO_ defines are for the CURLOPT_*PROTOCOLS options */ +#define CURLPROTO_HTTP (1<<0) +#define CURLPROTO_HTTPS (1<<1) +#define CURLPROTO_FTP (1<<2) +#define CURLPROTO_FTPS (1<<3) +#define CURLPROTO_SCP (1<<4) +#define CURLPROTO_SFTP (1<<5) +#define CURLPROTO_TELNET (1<<6) +#define CURLPROTO_LDAP (1<<7) +#define CURLPROTO_LDAPS (1<<8) +#define CURLPROTO_DICT (1<<9) +#define CURLPROTO_FILE (1<<10) +#define CURLPROTO_TFTP (1<<11) +#define CURLPROTO_IMAP (1<<12) +#define CURLPROTO_IMAPS (1<<13) +#define CURLPROTO_POP3 (1<<14) +#define CURLPROTO_POP3S (1<<15) +#define CURLPROTO_SMTP (1<<16) +#define CURLPROTO_SMTPS (1<<17) +#define CURLPROTO_RTSP (1<<18) +#define CURLPROTO_RTMP (1<<19) +#define CURLPROTO_RTMPT (1<<20) +#define CURLPROTO_RTMPE (1<<21) +#define CURLPROTO_RTMPTE (1<<22) +#define CURLPROTO_RTMPS (1<<23) +#define CURLPROTO_RTMPTS (1<<24) +#define CURLPROTO_GOPHER (1<<25) +#define CURLPROTO_SMB (1<<26) +#define CURLPROTO_SMBS (1<<27) +#define CURLPROTO_ALL (~0) /* enable everything */ + +/* long may be 32 or 64 bits, but we should never depend on anything else + but 32 */ +#define CURLOPTTYPE_LONG 0 +#define CURLOPTTYPE_OBJECTPOINT 10000 +#define CURLOPTTYPE_FUNCTIONPOINT 20000 +#define CURLOPTTYPE_OFF_T 30000 + +/* name is uppercase CURLOPT_, + type is one of the defined CURLOPTTYPE_ + number is unique identifier */ +#ifdef CINIT +#undef CINIT +#endif + +#ifdef CURL_ISOCPP +#define CINIT(na,t,nu) CURLOPT_ ## na = CURLOPTTYPE_ ## t + nu +#else +/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ +#define LONG CURLOPTTYPE_LONG +#define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT +#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT +#define OFF_T CURLOPTTYPE_OFF_T +#define CINIT(name,type,number) CURLOPT_/**/name = type + number +#endif + +/* + * This macro-mania below setups the CURLOPT_[what] enum, to be used with + * curl_easy_setopt(). The first argument in the CINIT() macro is the [what] + * word. + */ + +typedef enum { + /* This is the FILE * or void * the regular output should be written to. */ + CINIT(WRITEDATA, OBJECTPOINT, 1), + + /* The full URL to get/put */ + CINIT(URL, OBJECTPOINT, 2), + + /* Port number to connect to, if other than default. */ + CINIT(PORT, LONG, 3), + + /* Name of proxy to use. */ + CINIT(PROXY, OBJECTPOINT, 4), + + /* "user:password;options" to use when fetching. */ + CINIT(USERPWD, OBJECTPOINT, 5), + + /* "user:password" to use with proxy. */ + CINIT(PROXYUSERPWD, OBJECTPOINT, 6), + + /* Range to get, specified as an ASCII string. */ + CINIT(RANGE, OBJECTPOINT, 7), + + /* not used */ + + /* Specified file stream to upload from (use as input): */ + CINIT(READDATA, OBJECTPOINT, 9), + + /* Buffer to receive error messages in, must be at least CURL_ERROR_SIZE + * bytes big. If this is not used, error messages go to stderr instead: */ + CINIT(ERRORBUFFER, OBJECTPOINT, 10), + + /* Function that will be called to store the output (instead of fwrite). The + * parameters will use fwrite() syntax, make sure to follow them. */ + CINIT(WRITEFUNCTION, FUNCTIONPOINT, 11), + + /* Function that will be called to read the input (instead of fread). The + * parameters will use fread() syntax, make sure to follow them. */ + CINIT(READFUNCTION, FUNCTIONPOINT, 12), + + /* Time-out the read operation after this amount of seconds */ + CINIT(TIMEOUT, LONG, 13), + + /* If the CURLOPT_INFILE is used, this can be used to inform libcurl about + * how large the file being sent really is. That allows better error + * checking and better verifies that the upload was successful. -1 means + * unknown size. + * + * For large file support, there is also a _LARGE version of the key + * which takes an off_t type, allowing platforms with larger off_t + * sizes to handle larger files. See below for INFILESIZE_LARGE. + */ + CINIT(INFILESIZE, LONG, 14), + + /* POST static input fields. */ + CINIT(POSTFIELDS, OBJECTPOINT, 15), + + /* Set the referrer page (needed by some CGIs) */ + CINIT(REFERER, OBJECTPOINT, 16), + + /* Set the FTP PORT string (interface name, named or numerical IP address) + Use i.e '-' to use default address. */ + CINIT(FTPPORT, OBJECTPOINT, 17), + + /* Set the User-Agent string (examined by some CGIs) */ + CINIT(USERAGENT, OBJECTPOINT, 18), + + /* If the download receives less than "low speed limit" bytes/second + * during "low speed time" seconds, the operations is aborted. + * You could i.e if you have a pretty high speed connection, abort if + * it is less than 2000 bytes/sec during 20 seconds. + */ + + /* Set the "low speed limit" */ + CINIT(LOW_SPEED_LIMIT, LONG, 19), + + /* Set the "low speed time" */ + CINIT(LOW_SPEED_TIME, LONG, 20), + + /* Set the continuation offset. + * + * Note there is also a _LARGE version of this key which uses + * off_t types, allowing for large file offsets on platforms which + * use larger-than-32-bit off_t's. Look below for RESUME_FROM_LARGE. + */ + CINIT(RESUME_FROM, LONG, 21), + + /* Set cookie in request: */ + CINIT(COOKIE, OBJECTPOINT, 22), + + /* This points to a linked list of headers, struct curl_slist kind. This + list is also used for RTSP (in spite of its name) */ + CINIT(HTTPHEADER, OBJECTPOINT, 23), + + /* This points to a linked list of post entries, struct curl_httppost */ + CINIT(HTTPPOST, OBJECTPOINT, 24), + + /* name of the file keeping your private SSL-certificate */ + CINIT(SSLCERT, OBJECTPOINT, 25), + + /* password for the SSL or SSH private key */ + CINIT(KEYPASSWD, OBJECTPOINT, 26), + + /* send TYPE parameter? */ + CINIT(CRLF, LONG, 27), + + /* send linked-list of QUOTE commands */ + CINIT(QUOTE, OBJECTPOINT, 28), + + /* send FILE * or void * to store headers to, if you use a callback it + is simply passed to the callback unmodified */ + CINIT(HEADERDATA, OBJECTPOINT, 29), + + /* point to a file to read the initial cookies from, also enables + "cookie awareness" */ + CINIT(COOKIEFILE, OBJECTPOINT, 31), + + /* What version to specifically try to use. + See CURL_SSLVERSION defines below. */ + CINIT(SSLVERSION, LONG, 32), + + /* What kind of HTTP time condition to use, see defines */ + CINIT(TIMECONDITION, LONG, 33), + + /* Time to use with the above condition. Specified in number of seconds + since 1 Jan 1970 */ + CINIT(TIMEVALUE, LONG, 34), + + /* 35 = OBSOLETE */ + + /* Custom request, for customizing the get command like + HTTP: DELETE, TRACE and others + FTP: to use a different list command + */ + CINIT(CUSTOMREQUEST, OBJECTPOINT, 36), + + /* HTTP request, for odd commands like DELETE, TRACE and others */ + CINIT(STDERR, OBJECTPOINT, 37), + + /* 38 is not used */ + + /* send linked-list of post-transfer QUOTE commands */ + CINIT(POSTQUOTE, OBJECTPOINT, 39), + + CINIT(OBSOLETE40, OBJECTPOINT, 40), /* OBSOLETE, do not use! */ + + CINIT(VERBOSE, LONG, 41), /* talk a lot */ + CINIT(HEADER, LONG, 42), /* throw the header out too */ + CINIT(NOPROGRESS, LONG, 43), /* shut off the progress meter */ + CINIT(NOBODY, LONG, 44), /* use HEAD to get http document */ + CINIT(FAILONERROR, LONG, 45), /* no output on http error codes >= 400 */ + CINIT(UPLOAD, LONG, 46), /* this is an upload */ + CINIT(POST, LONG, 47), /* HTTP POST method */ + CINIT(DIRLISTONLY, LONG, 48), /* bare names when listing directories */ + + CINIT(APPEND, LONG, 50), /* Append instead of overwrite on upload! */ + + /* Specify whether to read the user+password from the .netrc or the URL. + * This must be one of the CURL_NETRC_* enums below. */ + CINIT(NETRC, LONG, 51), + + CINIT(FOLLOWLOCATION, LONG, 52), /* use Location: Luke! */ + + CINIT(TRANSFERTEXT, LONG, 53), /* transfer data in text/ASCII format */ + CINIT(PUT, LONG, 54), /* HTTP PUT */ + + /* 55 = OBSOLETE */ + + /* DEPRECATED + * Function that will be called instead of the internal progress display + * function. This function should be defined as the curl_progress_callback + * prototype defines. */ + CINIT(PROGRESSFUNCTION, FUNCTIONPOINT, 56), + + /* Data passed to the CURLOPT_PROGRESSFUNCTION and CURLOPT_XFERINFOFUNCTION + callbacks */ + CINIT(PROGRESSDATA, OBJECTPOINT, 57), +#define CURLOPT_XFERINFODATA CURLOPT_PROGRESSDATA + + /* We want the referrer field set automatically when following locations */ + CINIT(AUTOREFERER, LONG, 58), + + /* Port of the proxy, can be set in the proxy string as well with: + "[host]:[port]" */ + CINIT(PROXYPORT, LONG, 59), + + /* size of the POST input data, if strlen() is not good to use */ + CINIT(POSTFIELDSIZE, LONG, 60), + + /* tunnel non-http operations through a HTTP proxy */ + CINIT(HTTPPROXYTUNNEL, LONG, 61), + + /* Set the interface string to use as outgoing network interface */ + CINIT(INTERFACE, OBJECTPOINT, 62), + + /* Set the krb4/5 security level, this also enables krb4/5 awareness. This + * is a string, 'clear', 'safe', 'confidential' or 'private'. If the string + * is set but doesn't match one of these, 'private' will be used. */ + CINIT(KRBLEVEL, OBJECTPOINT, 63), + + /* Set if we should verify the peer in ssl handshake, set 1 to verify. */ + CINIT(SSL_VERIFYPEER, LONG, 64), + + /* The CApath or CAfile used to validate the peer certificate + this option is used only if SSL_VERIFYPEER is true */ + CINIT(CAINFO, OBJECTPOINT, 65), + + /* 66 = OBSOLETE */ + /* 67 = OBSOLETE */ + + /* Maximum number of http redirects to follow */ + CINIT(MAXREDIRS, LONG, 68), + + /* Pass a long set to 1 to get the date of the requested document (if + possible)! Pass a zero to shut it off. */ + CINIT(FILETIME, LONG, 69), + + /* This points to a linked list of telnet options */ + CINIT(TELNETOPTIONS, OBJECTPOINT, 70), + + /* Max amount of cached alive connections */ + CINIT(MAXCONNECTS, LONG, 71), + + CINIT(OBSOLETE72, LONG, 72), /* OBSOLETE, do not use! */ + + /* 73 = OBSOLETE */ + + /* Set to explicitly use a new connection for the upcoming transfer. + Do not use this unless you're absolutely sure of this, as it makes the + operation slower and is less friendly for the network. */ + CINIT(FRESH_CONNECT, LONG, 74), + + /* Set to explicitly forbid the upcoming transfer's connection to be re-used + when done. Do not use this unless you're absolutely sure of this, as it + makes the operation slower and is less friendly for the network. */ + CINIT(FORBID_REUSE, LONG, 75), + + /* Set to a file name that contains random data for libcurl to use to + seed the random engine when doing SSL connects. */ + CINIT(RANDOM_FILE, OBJECTPOINT, 76), + + /* Set to the Entropy Gathering Daemon socket pathname */ + CINIT(EGDSOCKET, OBJECTPOINT, 77), + + /* Time-out connect operations after this amount of seconds, if connects are + OK within this time, then fine... This only aborts the connect phase. */ + CINIT(CONNECTTIMEOUT, LONG, 78), + + /* Function that will be called to store headers (instead of fwrite). The + * parameters will use fwrite() syntax, make sure to follow them. */ + CINIT(HEADERFUNCTION, FUNCTIONPOINT, 79), + + /* Set this to force the HTTP request to get back to GET. Only really usable + if POST, PUT or a custom request have been used first. + */ + CINIT(HTTPGET, LONG, 80), + + /* Set if we should verify the Common name from the peer certificate in ssl + * handshake, set 1 to check existence, 2 to ensure that it matches the + * provided hostname. */ + CINIT(SSL_VERIFYHOST, LONG, 81), + + /* Specify which file name to write all known cookies in after completed + operation. Set file name to "-" (dash) to make it go to stdout. */ + CINIT(COOKIEJAR, OBJECTPOINT, 82), + + /* Specify which SSL ciphers to use */ + CINIT(SSL_CIPHER_LIST, OBJECTPOINT, 83), + + /* Specify which HTTP version to use! This must be set to one of the + CURL_HTTP_VERSION* enums set below. */ + CINIT(HTTP_VERSION, LONG, 84), + + /* Specifically switch on or off the FTP engine's use of the EPSV command. By + default, that one will always be attempted before the more traditional + PASV command. */ + CINIT(FTP_USE_EPSV, LONG, 85), + + /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") */ + CINIT(SSLCERTTYPE, OBJECTPOINT, 86), + + /* name of the file keeping your private SSL-key */ + CINIT(SSLKEY, OBJECTPOINT, 87), + + /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") */ + CINIT(SSLKEYTYPE, OBJECTPOINT, 88), + + /* crypto engine for the SSL-sub system */ + CINIT(SSLENGINE, OBJECTPOINT, 89), + + /* set the crypto engine for the SSL-sub system as default + the param has no meaning... + */ + CINIT(SSLENGINE_DEFAULT, LONG, 90), + + /* Non-zero value means to use the global dns cache */ + CINIT(DNS_USE_GLOBAL_CACHE, LONG, 91), /* DEPRECATED, do not use! */ + + /* DNS cache timeout */ + CINIT(DNS_CACHE_TIMEOUT, LONG, 92), + + /* send linked-list of pre-transfer QUOTE commands */ + CINIT(PREQUOTE, OBJECTPOINT, 93), + + /* set the debug function */ + CINIT(DEBUGFUNCTION, FUNCTIONPOINT, 94), + + /* set the data for the debug function */ + CINIT(DEBUGDATA, OBJECTPOINT, 95), + + /* mark this as start of a cookie session */ + CINIT(COOKIESESSION, LONG, 96), + + /* The CApath directory used to validate the peer certificate + this option is used only if SSL_VERIFYPEER is true */ + CINIT(CAPATH, OBJECTPOINT, 97), + + /* Instruct libcurl to use a smaller receive buffer */ + CINIT(BUFFERSIZE, LONG, 98), + + /* Instruct libcurl to not use any signal/alarm handlers, even when using + timeouts. This option is useful for multi-threaded applications. + See libcurl-the-guide for more background information. */ + CINIT(NOSIGNAL, LONG, 99), + + /* Provide a CURLShare for mutexing non-ts data */ + CINIT(SHARE, OBJECTPOINT, 100), + + /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default), + CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5. */ + CINIT(PROXYTYPE, LONG, 101), + + /* Set the Accept-Encoding string. Use this to tell a server you would like + the response to be compressed. Before 7.21.6, this was known as + CURLOPT_ENCODING */ + CINIT(ACCEPT_ENCODING, OBJECTPOINT, 102), + + /* Set pointer to private data */ + CINIT(PRIVATE, OBJECTPOINT, 103), + + /* Set aliases for HTTP 200 in the HTTP Response header */ + CINIT(HTTP200ALIASES, OBJECTPOINT, 104), + + /* Continue to send authentication (user+password) when following locations, + even when hostname changed. This can potentially send off the name + and password to whatever host the server decides. */ + CINIT(UNRESTRICTED_AUTH, LONG, 105), + + /* Specifically switch on or off the FTP engine's use of the EPRT command ( + it also disables the LPRT attempt). By default, those ones will always be + attempted before the good old traditional PORT command. */ + CINIT(FTP_USE_EPRT, LONG, 106), + + /* Set this to a bitmask value to enable the particular authentications + methods you like. Use this in combination with CURLOPT_USERPWD. + Note that setting multiple bits may cause extra network round-trips. */ + CINIT(HTTPAUTH, LONG, 107), + + /* Set the ssl context callback function, currently only for OpenSSL ssl_ctx + in second argument. The function must be matching the + curl_ssl_ctx_callback proto. */ + CINIT(SSL_CTX_FUNCTION, FUNCTIONPOINT, 108), + + /* Set the userdata for the ssl context callback function's third + argument */ + CINIT(SSL_CTX_DATA, OBJECTPOINT, 109), + + /* FTP Option that causes missing dirs to be created on the remote server. + In 7.19.4 we introduced the convenience enums for this option using the + CURLFTP_CREATE_DIR prefix. + */ + CINIT(FTP_CREATE_MISSING_DIRS, LONG, 110), + + /* Set this to a bitmask value to enable the particular authentications + methods you like. Use this in combination with CURLOPT_PROXYUSERPWD. + Note that setting multiple bits may cause extra network round-trips. */ + CINIT(PROXYAUTH, LONG, 111), + + /* FTP option that changes the timeout, in seconds, associated with + getting a response. This is different from transfer timeout time and + essentially places a demand on the FTP server to acknowledge commands + in a timely manner. */ + CINIT(FTP_RESPONSE_TIMEOUT, LONG, 112), +#define CURLOPT_SERVER_RESPONSE_TIMEOUT CURLOPT_FTP_RESPONSE_TIMEOUT + + /* Set this option to one of the CURL_IPRESOLVE_* defines (see below) to + tell libcurl to resolve names to those IP versions only. This only has + affect on systems with support for more than one, i.e IPv4 _and_ IPv6. */ + CINIT(IPRESOLVE, LONG, 113), + + /* Set this option to limit the size of a file that will be downloaded from + an HTTP or FTP server. + + Note there is also _LARGE version which adds large file support for + platforms which have larger off_t sizes. See MAXFILESIZE_LARGE below. */ + CINIT(MAXFILESIZE, LONG, 114), + + /* See the comment for INFILESIZE above, but in short, specifies + * the size of the file being uploaded. -1 means unknown. + */ + CINIT(INFILESIZE_LARGE, OFF_T, 115), + + /* Sets the continuation offset. There is also a LONG version of this; + * look above for RESUME_FROM. + */ + CINIT(RESUME_FROM_LARGE, OFF_T, 116), + + /* Sets the maximum size of data that will be downloaded from + * an HTTP or FTP server. See MAXFILESIZE above for the LONG version. + */ + CINIT(MAXFILESIZE_LARGE, OFF_T, 117), + + /* Set this option to the file name of your .netrc file you want libcurl + to parse (using the CURLOPT_NETRC option). If not set, libcurl will do + a poor attempt to find the user's home directory and check for a .netrc + file in there. */ + CINIT(NETRC_FILE, OBJECTPOINT, 118), + + /* Enable SSL/TLS for FTP, pick one of: + CURLUSESSL_TRY - try using SSL, proceed anyway otherwise + CURLUSESSL_CONTROL - SSL for the control connection or fail + CURLUSESSL_ALL - SSL for all communication or fail + */ + CINIT(USE_SSL, LONG, 119), + + /* The _LARGE version of the standard POSTFIELDSIZE option */ + CINIT(POSTFIELDSIZE_LARGE, OFF_T, 120), + + /* Enable/disable the TCP Nagle algorithm */ + CINIT(TCP_NODELAY, LONG, 121), + + /* 122 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 123 OBSOLETE. Gone in 7.16.0 */ + /* 124 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 125 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 126 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 127 OBSOLETE. Gone in 7.16.0 */ + /* 128 OBSOLETE. Gone in 7.16.0 */ + + /* When FTP over SSL/TLS is selected (with CURLOPT_USE_SSL), this option + can be used to change libcurl's default action which is to first try + "AUTH SSL" and then "AUTH TLS" in this order, and proceed when a OK + response has been received. + + Available parameters are: + CURLFTPAUTH_DEFAULT - let libcurl decide + CURLFTPAUTH_SSL - try "AUTH SSL" first, then TLS + CURLFTPAUTH_TLS - try "AUTH TLS" first, then SSL + */ + CINIT(FTPSSLAUTH, LONG, 129), + + CINIT(IOCTLFUNCTION, FUNCTIONPOINT, 130), + CINIT(IOCTLDATA, OBJECTPOINT, 131), + + /* 132 OBSOLETE. Gone in 7.16.0 */ + /* 133 OBSOLETE. Gone in 7.16.0 */ + + /* zero terminated string for pass on to the FTP server when asked for + "account" info */ + CINIT(FTP_ACCOUNT, OBJECTPOINT, 134), + + /* feed cookies into cookie engine */ + CINIT(COOKIELIST, OBJECTPOINT, 135), + + /* ignore Content-Length */ + CINIT(IGNORE_CONTENT_LENGTH, LONG, 136), + + /* Set to non-zero to skip the IP address received in a 227 PASV FTP server + response. Typically used for FTP-SSL purposes but is not restricted to + that. libcurl will then instead use the same IP address it used for the + control connection. */ + CINIT(FTP_SKIP_PASV_IP, LONG, 137), + + /* Select "file method" to use when doing FTP, see the curl_ftpmethod + above. */ + CINIT(FTP_FILEMETHOD, LONG, 138), + + /* Local port number to bind the socket to */ + CINIT(LOCALPORT, LONG, 139), + + /* Number of ports to try, including the first one set with LOCALPORT. + Thus, setting it to 1 will make no additional attempts but the first. + */ + CINIT(LOCALPORTRANGE, LONG, 140), + + /* no transfer, set up connection and let application use the socket by + extracting it with CURLINFO_LASTSOCKET */ + CINIT(CONNECT_ONLY, LONG, 141), + + /* Function that will be called to convert from the + network encoding (instead of using the iconv calls in libcurl) */ + CINIT(CONV_FROM_NETWORK_FUNCTION, FUNCTIONPOINT, 142), + + /* Function that will be called to convert to the + network encoding (instead of using the iconv calls in libcurl) */ + CINIT(CONV_TO_NETWORK_FUNCTION, FUNCTIONPOINT, 143), + + /* Function that will be called to convert from UTF8 + (instead of using the iconv calls in libcurl) + Note that this is used only for SSL certificate processing */ + CINIT(CONV_FROM_UTF8_FUNCTION, FUNCTIONPOINT, 144), + + /* if the connection proceeds too quickly then need to slow it down */ + /* limit-rate: maximum number of bytes per second to send or receive */ + CINIT(MAX_SEND_SPEED_LARGE, OFF_T, 145), + CINIT(MAX_RECV_SPEED_LARGE, OFF_T, 146), + + /* Pointer to command string to send if USER/PASS fails. */ + CINIT(FTP_ALTERNATIVE_TO_USER, OBJECTPOINT, 147), + + /* callback function for setting socket options */ + CINIT(SOCKOPTFUNCTION, FUNCTIONPOINT, 148), + CINIT(SOCKOPTDATA, OBJECTPOINT, 149), + + /* set to 0 to disable session ID re-use for this transfer, default is + enabled (== 1) */ + CINIT(SSL_SESSIONID_CACHE, LONG, 150), + + /* allowed SSH authentication methods */ + CINIT(SSH_AUTH_TYPES, LONG, 151), + + /* Used by scp/sftp to do public/private key authentication */ + CINIT(SSH_PUBLIC_KEYFILE, OBJECTPOINT, 152), + CINIT(SSH_PRIVATE_KEYFILE, OBJECTPOINT, 153), + + /* Send CCC (Clear Command Channel) after authentication */ + CINIT(FTP_SSL_CCC, LONG, 154), + + /* Same as TIMEOUT and CONNECTTIMEOUT, but with ms resolution */ + CINIT(TIMEOUT_MS, LONG, 155), + CINIT(CONNECTTIMEOUT_MS, LONG, 156), + + /* set to zero to disable the libcurl's decoding and thus pass the raw body + data to the application even when it is encoded/compressed */ + CINIT(HTTP_TRANSFER_DECODING, LONG, 157), + CINIT(HTTP_CONTENT_DECODING, LONG, 158), + + /* Permission used when creating new files and directories on the remote + server for protocols that support it, SFTP/SCP/FILE */ + CINIT(NEW_FILE_PERMS, LONG, 159), + CINIT(NEW_DIRECTORY_PERMS, LONG, 160), + + /* Set the behaviour of POST when redirecting. Values must be set to one + of CURL_REDIR* defines below. This used to be called CURLOPT_POST301 */ + CINIT(POSTREDIR, LONG, 161), + + /* used by scp/sftp to verify the host's public key */ + CINIT(SSH_HOST_PUBLIC_KEY_MD5, OBJECTPOINT, 162), + + /* Callback function for opening socket (instead of socket(2)). Optionally, + callback is able change the address or refuse to connect returning + CURL_SOCKET_BAD. The callback should have type + curl_opensocket_callback */ + CINIT(OPENSOCKETFUNCTION, FUNCTIONPOINT, 163), + CINIT(OPENSOCKETDATA, OBJECTPOINT, 164), + + /* POST volatile input fields. */ + CINIT(COPYPOSTFIELDS, OBJECTPOINT, 165), + + /* set transfer mode (;type=) when doing FTP via an HTTP proxy */ + CINIT(PROXY_TRANSFER_MODE, LONG, 166), + + /* Callback function for seeking in the input stream */ + CINIT(SEEKFUNCTION, FUNCTIONPOINT, 167), + CINIT(SEEKDATA, OBJECTPOINT, 168), + + /* CRL file */ + CINIT(CRLFILE, OBJECTPOINT, 169), + + /* Issuer certificate */ + CINIT(ISSUERCERT, OBJECTPOINT, 170), + + /* (IPv6) Address scope */ + CINIT(ADDRESS_SCOPE, LONG, 171), + + /* Collect certificate chain info and allow it to get retrievable with + CURLINFO_CERTINFO after the transfer is complete. */ + CINIT(CERTINFO, LONG, 172), + + /* "name" and "pwd" to use when fetching. */ + CINIT(USERNAME, OBJECTPOINT, 173), + CINIT(PASSWORD, OBJECTPOINT, 174), + + /* "name" and "pwd" to use with Proxy when fetching. */ + CINIT(PROXYUSERNAME, OBJECTPOINT, 175), + CINIT(PROXYPASSWORD, OBJECTPOINT, 176), + + /* Comma separated list of hostnames defining no-proxy zones. These should + match both hostnames directly, and hostnames within a domain. For + example, local.com will match local.com and www.local.com, but NOT + notlocal.com or www.notlocal.com. For compatibility with other + implementations of this, .local.com will be considered to be the same as + local.com. A single * is the only valid wildcard, and effectively + disables the use of proxy. */ + CINIT(NOPROXY, OBJECTPOINT, 177), + + /* block size for TFTP transfers */ + CINIT(TFTP_BLKSIZE, LONG, 178), + + /* Socks Service */ + CINIT(SOCKS5_GSSAPI_SERVICE, OBJECTPOINT, 179), + + /* Socks Service */ + CINIT(SOCKS5_GSSAPI_NEC, LONG, 180), + + /* set the bitmask for the protocols that are allowed to be used for the + transfer, which thus helps the app which takes URLs from users or other + external inputs and want to restrict what protocol(s) to deal + with. Defaults to CURLPROTO_ALL. */ + CINIT(PROTOCOLS, LONG, 181), + + /* set the bitmask for the protocols that libcurl is allowed to follow to, + as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs + to be set in both bitmasks to be allowed to get redirected to. Defaults + to all protocols except FILE and SCP. */ + CINIT(REDIR_PROTOCOLS, LONG, 182), + + /* set the SSH knownhost file name to use */ + CINIT(SSH_KNOWNHOSTS, OBJECTPOINT, 183), + + /* set the SSH host key callback, must point to a curl_sshkeycallback + function */ + CINIT(SSH_KEYFUNCTION, FUNCTIONPOINT, 184), + + /* set the SSH host key callback custom pointer */ + CINIT(SSH_KEYDATA, OBJECTPOINT, 185), + + /* set the SMTP mail originator */ + CINIT(MAIL_FROM, OBJECTPOINT, 186), + + /* set the SMTP mail receiver(s) */ + CINIT(MAIL_RCPT, OBJECTPOINT, 187), + + /* FTP: send PRET before PASV */ + CINIT(FTP_USE_PRET, LONG, 188), + + /* RTSP request method (OPTIONS, SETUP, PLAY, etc...) */ + CINIT(RTSP_REQUEST, LONG, 189), + + /* The RTSP session identifier */ + CINIT(RTSP_SESSION_ID, OBJECTPOINT, 190), + + /* The RTSP stream URI */ + CINIT(RTSP_STREAM_URI, OBJECTPOINT, 191), + + /* The Transport: header to use in RTSP requests */ + CINIT(RTSP_TRANSPORT, OBJECTPOINT, 192), + + /* Manually initialize the client RTSP CSeq for this handle */ + CINIT(RTSP_CLIENT_CSEQ, LONG, 193), + + /* Manually initialize the server RTSP CSeq for this handle */ + CINIT(RTSP_SERVER_CSEQ, LONG, 194), + + /* The stream to pass to INTERLEAVEFUNCTION. */ + CINIT(INTERLEAVEDATA, OBJECTPOINT, 195), + + /* Let the application define a custom write method for RTP data */ + CINIT(INTERLEAVEFUNCTION, FUNCTIONPOINT, 196), + + /* Turn on wildcard matching */ + CINIT(WILDCARDMATCH, LONG, 197), + + /* Directory matching callback called before downloading of an + individual file (chunk) started */ + CINIT(CHUNK_BGN_FUNCTION, FUNCTIONPOINT, 198), + + /* Directory matching callback called after the file (chunk) + was downloaded, or skipped */ + CINIT(CHUNK_END_FUNCTION, FUNCTIONPOINT, 199), + + /* Change match (fnmatch-like) callback for wildcard matching */ + CINIT(FNMATCH_FUNCTION, FUNCTIONPOINT, 200), + + /* Let the application define custom chunk data pointer */ + CINIT(CHUNK_DATA, OBJECTPOINT, 201), + + /* FNMATCH_FUNCTION user pointer */ + CINIT(FNMATCH_DATA, OBJECTPOINT, 202), + + /* send linked-list of name:port:address sets */ + CINIT(RESOLVE, OBJECTPOINT, 203), + + /* Set a username for authenticated TLS */ + CINIT(TLSAUTH_USERNAME, OBJECTPOINT, 204), + + /* Set a password for authenticated TLS */ + CINIT(TLSAUTH_PASSWORD, OBJECTPOINT, 205), + + /* Set authentication type for authenticated TLS */ + CINIT(TLSAUTH_TYPE, OBJECTPOINT, 206), + + /* Set to 1 to enable the "TE:" header in HTTP requests to ask for + compressed transfer-encoded responses. Set to 0 to disable the use of TE: + in outgoing requests. The current default is 0, but it might change in a + future libcurl release. + + libcurl will ask for the compressed methods it knows of, and if that + isn't any, it will not ask for transfer-encoding at all even if this + option is set to 1. + + */ + CINIT(TRANSFER_ENCODING, LONG, 207), + + /* Callback function for closing socket (instead of close(2)). The callback + should have type curl_closesocket_callback */ + CINIT(CLOSESOCKETFUNCTION, FUNCTIONPOINT, 208), + CINIT(CLOSESOCKETDATA, OBJECTPOINT, 209), + + /* allow GSSAPI credential delegation */ + CINIT(GSSAPI_DELEGATION, LONG, 210), + + /* Set the name servers to use for DNS resolution */ + CINIT(DNS_SERVERS, OBJECTPOINT, 211), + + /* Time-out accept operations (currently for FTP only) after this amount + of miliseconds. */ + CINIT(ACCEPTTIMEOUT_MS, LONG, 212), + + /* Set TCP keepalive */ + CINIT(TCP_KEEPALIVE, LONG, 213), + + /* non-universal keepalive knobs (Linux, AIX, HP-UX, more) */ + CINIT(TCP_KEEPIDLE, LONG, 214), + CINIT(TCP_KEEPINTVL, LONG, 215), + + /* Enable/disable specific SSL features with a bitmask, see CURLSSLOPT_* */ + CINIT(SSL_OPTIONS, LONG, 216), + + /* Set the SMTP auth originator */ + CINIT(MAIL_AUTH, OBJECTPOINT, 217), + + /* Enable/disable SASL initial response */ + CINIT(SASL_IR, LONG, 218), + + /* Function that will be called instead of the internal progress display + * function. This function should be defined as the curl_xferinfo_callback + * prototype defines. (Deprecates CURLOPT_PROGRESSFUNCTION) */ + CINIT(XFERINFOFUNCTION, FUNCTIONPOINT, 219), + + /* The XOAUTH2 bearer token */ + CINIT(XOAUTH2_BEARER, OBJECTPOINT, 220), + + /* Set the interface string to use as outgoing network + * interface for DNS requests. + * Only supported by the c-ares DNS backend */ + CINIT(DNS_INTERFACE, OBJECTPOINT, 221), + + /* Set the local IPv4 address to use for outgoing DNS requests. + * Only supported by the c-ares DNS backend */ + CINIT(DNS_LOCAL_IP4, OBJECTPOINT, 222), + + /* Set the local IPv4 address to use for outgoing DNS requests. + * Only supported by the c-ares DNS backend */ + CINIT(DNS_LOCAL_IP6, OBJECTPOINT, 223), + + /* Set authentication options directly */ + CINIT(LOGIN_OPTIONS, OBJECTPOINT, 224), + + /* Enable/disable TLS NPN extension (http2 over ssl might fail without) */ + CINIT(SSL_ENABLE_NPN, LONG, 225), + + /* Enable/disable TLS ALPN extension (http2 over ssl might fail without) */ + CINIT(SSL_ENABLE_ALPN, LONG, 226), + + /* Time to wait for a response to a HTTP request containing an + * Expect: 100-continue header before sending the data anyway. */ + CINIT(EXPECT_100_TIMEOUT_MS, LONG, 227), + + /* This points to a linked list of headers used for proxy requests only, + struct curl_slist kind */ + CINIT(PROXYHEADER, OBJECTPOINT, 228), + + /* Pass in a bitmask of "header options" */ + CINIT(HEADEROPT, LONG, 229), + + /* The public key in DER form used to validate the peer public key + this option is used only if SSL_VERIFYPEER is true */ + CINIT(PINNEDPUBLICKEY, OBJECTPOINT, 230), + + /* Path to Unix domain socket */ + CINIT(UNIX_SOCKET_PATH, OBJECTPOINT, 231), + + /* Set if we should verify the certificate status. */ + CINIT(SSL_VERIFYSTATUS, LONG, 232), + + /* Set if we should enable TLS false start. */ + CINIT(SSL_FALSESTART, LONG, 233), + + /* Do not squash dot-dot sequences */ + CINIT(PATH_AS_IS, LONG, 234), + + CURLOPT_LASTENTRY /* the last unused */ +} CURLoption; + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ + +/* Backwards compatibility with older names */ +/* These are scheduled to disappear by 2011 */ + +/* This was added in version 7.19.1 */ +#define CURLOPT_POST301 CURLOPT_POSTREDIR + +/* These are scheduled to disappear by 2009 */ + +/* The following were added in 7.17.0 */ +#define CURLOPT_SSLKEYPASSWD CURLOPT_KEYPASSWD +#define CURLOPT_FTPAPPEND CURLOPT_APPEND +#define CURLOPT_FTPLISTONLY CURLOPT_DIRLISTONLY +#define CURLOPT_FTP_SSL CURLOPT_USE_SSL + +/* The following were added earlier */ + +#define CURLOPT_SSLCERTPASSWD CURLOPT_KEYPASSWD +#define CURLOPT_KRB4LEVEL CURLOPT_KRBLEVEL + +#else +/* This is set if CURL_NO_OLDIES is defined at compile-time */ +#undef CURLOPT_DNS_USE_GLOBAL_CACHE /* soon obsolete */ +#endif + + + /* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host + name resolves addresses using more than one IP protocol version, this + option might be handy to force libcurl to use a specific IP version. */ +#define CURL_IPRESOLVE_WHATEVER 0 /* default, resolves addresses to all IP + versions that your system allows */ +#define CURL_IPRESOLVE_V4 1 /* resolve to IPv4 addresses */ +#define CURL_IPRESOLVE_V6 2 /* resolve to IPv6 addresses */ + + /* three convenient "aliases" that follow the name scheme better */ +#define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER + + /* These enums are for use with the CURLOPT_HTTP_VERSION option. */ +enum { + CURL_HTTP_VERSION_NONE, /* setting this means we don't care, and that we'd + like the library to choose the best possible + for us! */ + CURL_HTTP_VERSION_1_0, /* please use HTTP 1.0 in the request */ + CURL_HTTP_VERSION_1_1, /* please use HTTP 1.1 in the request */ + CURL_HTTP_VERSION_2_0, /* please use HTTP 2.0 in the request */ + + CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */ +}; + +/* + * Public API enums for RTSP requests + */ +enum { + CURL_RTSPREQ_NONE, /* first in list */ + CURL_RTSPREQ_OPTIONS, + CURL_RTSPREQ_DESCRIBE, + CURL_RTSPREQ_ANNOUNCE, + CURL_RTSPREQ_SETUP, + CURL_RTSPREQ_PLAY, + CURL_RTSPREQ_PAUSE, + CURL_RTSPREQ_TEARDOWN, + CURL_RTSPREQ_GET_PARAMETER, + CURL_RTSPREQ_SET_PARAMETER, + CURL_RTSPREQ_RECORD, + CURL_RTSPREQ_RECEIVE, + CURL_RTSPREQ_LAST /* last in list */ +}; + + /* These enums are for use with the CURLOPT_NETRC option. */ +enum CURL_NETRC_OPTION { + CURL_NETRC_IGNORED, /* The .netrc will never be read. + * This is the default. */ + CURL_NETRC_OPTIONAL, /* A user:password in the URL will be preferred + * to one in the .netrc. */ + CURL_NETRC_REQUIRED, /* A user:password in the URL will be ignored. + * Unless one is set programmatically, the .netrc + * will be queried. */ + CURL_NETRC_LAST +}; + +enum { + CURL_SSLVERSION_DEFAULT, + CURL_SSLVERSION_TLSv1, /* TLS 1.x */ + CURL_SSLVERSION_SSLv2, + CURL_SSLVERSION_SSLv3, + CURL_SSLVERSION_TLSv1_0, + CURL_SSLVERSION_TLSv1_1, + CURL_SSLVERSION_TLSv1_2, + + CURL_SSLVERSION_LAST /* never use, keep last */ +}; + +enum CURL_TLSAUTH { + CURL_TLSAUTH_NONE, + CURL_TLSAUTH_SRP, + CURL_TLSAUTH_LAST /* never use, keep last */ +}; + +/* symbols to use with CURLOPT_POSTREDIR. + CURL_REDIR_POST_301, CURL_REDIR_POST_302 and CURL_REDIR_POST_303 + can be bitwise ORed so that CURL_REDIR_POST_301 | CURL_REDIR_POST_302 + | CURL_REDIR_POST_303 == CURL_REDIR_POST_ALL */ + +#define CURL_REDIR_GET_ALL 0 +#define CURL_REDIR_POST_301 1 +#define CURL_REDIR_POST_302 2 +#define CURL_REDIR_POST_303 4 +#define CURL_REDIR_POST_ALL \ + (CURL_REDIR_POST_301|CURL_REDIR_POST_302|CURL_REDIR_POST_303) + +typedef enum { + CURL_TIMECOND_NONE, + + CURL_TIMECOND_IFMODSINCE, + CURL_TIMECOND_IFUNMODSINCE, + CURL_TIMECOND_LASTMOD, + + CURL_TIMECOND_LAST +} curl_TimeCond; + + +/* curl_strequal() and curl_strnequal() are subject for removal in a future + libcurl, see lib/README.curlx for details */ +CURL_EXTERN int (curl_strequal)(const char *s1, const char *s2); +CURL_EXTERN int (curl_strnequal)(const char *s1, const char *s2, size_t n); + +/* name is uppercase CURLFORM_ */ +#ifdef CFINIT +#undef CFINIT +#endif + +#ifdef CURL_ISOCPP +#define CFINIT(name) CURLFORM_ ## name +#else +/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ +#define CFINIT(name) CURLFORM_/**/name +#endif + +typedef enum { + CFINIT(NOTHING), /********* the first one is unused ************/ + + /* */ + CFINIT(COPYNAME), + CFINIT(PTRNAME), + CFINIT(NAMELENGTH), + CFINIT(COPYCONTENTS), + CFINIT(PTRCONTENTS), + CFINIT(CONTENTSLENGTH), + CFINIT(FILECONTENT), + CFINIT(ARRAY), + CFINIT(OBSOLETE), + CFINIT(FILE), + + CFINIT(BUFFER), + CFINIT(BUFFERPTR), + CFINIT(BUFFERLENGTH), + + CFINIT(CONTENTTYPE), + CFINIT(CONTENTHEADER), + CFINIT(FILENAME), + CFINIT(END), + CFINIT(OBSOLETE2), + + CFINIT(STREAM), + + CURLFORM_LASTENTRY /* the last unused */ +} CURLformoption; + +#undef CFINIT /* done */ + +/* structure to be used as parameter for CURLFORM_ARRAY */ +struct curl_forms { + CURLformoption option; + const char *value; +}; + +/* use this for multipart formpost building */ +/* Returns code for curl_formadd() + * + * Returns: + * CURL_FORMADD_OK on success + * CURL_FORMADD_MEMORY if the FormInfo allocation fails + * CURL_FORMADD_OPTION_TWICE if one option is given twice for one Form + * CURL_FORMADD_NULL if a null pointer was given for a char + * CURL_FORMADD_MEMORY if the allocation of a FormInfo struct failed + * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used + * CURL_FORMADD_INCOMPLETE if the some FormInfo is not complete (or error) + * CURL_FORMADD_MEMORY if a curl_httppost struct cannot be allocated + * CURL_FORMADD_MEMORY if some allocation for string copying failed. + * CURL_FORMADD_ILLEGAL_ARRAY if an illegal option is used in an array + * + ***************************************************************************/ +typedef enum { + CURL_FORMADD_OK, /* first, no error */ + + CURL_FORMADD_MEMORY, + CURL_FORMADD_OPTION_TWICE, + CURL_FORMADD_NULL, + CURL_FORMADD_UNKNOWN_OPTION, + CURL_FORMADD_INCOMPLETE, + CURL_FORMADD_ILLEGAL_ARRAY, + CURL_FORMADD_DISABLED, /* libcurl was built with this disabled */ + + CURL_FORMADD_LAST /* last */ +} CURLFORMcode; + +/* + * NAME curl_formadd() + * + * DESCRIPTION + * + * Pretty advanced function for building multi-part formposts. Each invoke + * adds one part that together construct a full post. Then use + * CURLOPT_HTTPPOST to send it off to libcurl. + */ +CURL_EXTERN CURLFORMcode curl_formadd(struct curl_httppost **httppost, + struct curl_httppost **last_post, + ...); + +/* + * callback function for curl_formget() + * The void *arg pointer will be the one passed as second argument to + * curl_formget(). + * The character buffer passed to it must not be freed. + * Should return the buffer length passed to it as the argument "len" on + * success. + */ +typedef size_t (*curl_formget_callback)(void *arg, const char *buf, + size_t len); + +/* + * NAME curl_formget() + * + * DESCRIPTION + * + * Serialize a curl_httppost struct built with curl_formadd(). + * Accepts a void pointer as second argument which will be passed to + * the curl_formget_callback function. + * Returns 0 on success. + */ +CURL_EXTERN int curl_formget(struct curl_httppost *form, void *arg, + curl_formget_callback append); +/* + * NAME curl_formfree() + * + * DESCRIPTION + * + * Free a multipart formpost previously built with curl_formadd(). + */ +CURL_EXTERN void curl_formfree(struct curl_httppost *form); + +/* + * NAME curl_getenv() + * + * DESCRIPTION + * + * Returns a malloc()'ed string that MUST be curl_free()ed after usage is + * complete. DEPRECATED - see lib/README.curlx + */ +CURL_EXTERN char *curl_getenv(const char *variable); + +/* + * NAME curl_version() + * + * DESCRIPTION + * + * Returns a static ascii string of the libcurl version. + */ +CURL_EXTERN char *curl_version(void); + +/* + * NAME curl_easy_escape() + * + * DESCRIPTION + * + * Escapes URL strings (converts all letters consider illegal in URLs to their + * %XX versions). This function returns a new allocated string or NULL if an + * error occurred. + */ +CURL_EXTERN char *curl_easy_escape(CURL *handle, + const char *string, + int length); + +/* the previous version: */ +CURL_EXTERN char *curl_escape(const char *string, + int length); + + +/* + * NAME curl_easy_unescape() + * + * DESCRIPTION + * + * Unescapes URL encoding in strings (converts all %XX codes to their 8bit + * versions). This function returns a new allocated string or NULL if an error + * occurred. + * Conversion Note: On non-ASCII platforms the ASCII %XX codes are + * converted into the host encoding. + */ +CURL_EXTERN char *curl_easy_unescape(CURL *handle, + const char *string, + int length, + int *outlength); + +/* the previous version */ +CURL_EXTERN char *curl_unescape(const char *string, + int length); + +/* + * NAME curl_free() + * + * DESCRIPTION + * + * Provided for de-allocation in the same translation unit that did the + * allocation. Added in libcurl 7.10 + */ +CURL_EXTERN void curl_free(void *p); + +/* + * NAME curl_global_init() + * + * DESCRIPTION + * + * curl_global_init() should be invoked exactly once for each application that + * uses libcurl and before any call of other libcurl functions. + * + * This function is not thread-safe! + */ +CURL_EXTERN CURLcode curl_global_init(long flags); + +/* + * NAME curl_global_init_mem() + * + * DESCRIPTION + * + * curl_global_init() or curl_global_init_mem() should be invoked exactly once + * for each application that uses libcurl. This function can be used to + * initialize libcurl and set user defined memory management callback + * functions. Users can implement memory management routines to check for + * memory leaks, check for mis-use of the curl library etc. User registered + * callback routines with be invoked by this library instead of the system + * memory management routines like malloc, free etc. + */ +CURL_EXTERN CURLcode curl_global_init_mem(long flags, + curl_malloc_callback m, + curl_free_callback f, + curl_realloc_callback r, + curl_strdup_callback s, + curl_calloc_callback c); + +/* + * NAME curl_global_cleanup() + * + * DESCRIPTION + * + * curl_global_cleanup() should be invoked exactly once for each application + * that uses libcurl + */ +CURL_EXTERN void curl_global_cleanup(void); + +/* linked-list structure for the CURLOPT_QUOTE option (and other) */ +struct curl_slist { + char *data; + struct curl_slist *next; +}; + +/* + * NAME curl_slist_append() + * + * DESCRIPTION + * + * Appends a string to a linked list. If no list exists, it will be created + * first. Returns the new list, after appending. + */ +CURL_EXTERN struct curl_slist *curl_slist_append(struct curl_slist *, + const char *); + +/* + * NAME curl_slist_free_all() + * + * DESCRIPTION + * + * free a previously built curl_slist. + */ +CURL_EXTERN void curl_slist_free_all(struct curl_slist *); + +/* + * NAME curl_getdate() + * + * DESCRIPTION + * + * Returns the time, in seconds since 1 Jan 1970 of the time string given in + * the first argument. The time argument in the second parameter is unused + * and should be set to NULL. + */ +CURL_EXTERN time_t curl_getdate(const char *p, const time_t *unused); + +/* info about the certificate chain, only for OpenSSL builds. Asked + for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */ +struct curl_certinfo { + int num_of_certs; /* number of certificates with information */ + struct curl_slist **certinfo; /* for each index in this array, there's a + linked list with textual information in the + format "name: value" */ +}; + +/* enum for the different supported SSL backends */ +typedef enum { + CURLSSLBACKEND_NONE = 0, + CURLSSLBACKEND_OPENSSL = 1, + CURLSSLBACKEND_GNUTLS = 2, + CURLSSLBACKEND_NSS = 3, + CURLSSLBACKEND_OBSOLETE4 = 4, /* Was QSOSSL. */ + CURLSSLBACKEND_GSKIT = 5, + CURLSSLBACKEND_POLARSSL = 6, + CURLSSLBACKEND_CYASSL = 7, + CURLSSLBACKEND_SCHANNEL = 8, + CURLSSLBACKEND_DARWINSSL = 9, + CURLSSLBACKEND_AXTLS = 10 +} curl_sslbackend; + +/* Information about the SSL library used and the respective internal SSL + handle, which can be used to obtain further information regarding the + connection. Asked for with CURLINFO_TLS_SESSION. */ +struct curl_tlssessioninfo { + curl_sslbackend backend; + void *internals; +}; + +#define CURLINFO_STRING 0x100000 +#define CURLINFO_LONG 0x200000 +#define CURLINFO_DOUBLE 0x300000 +#define CURLINFO_SLIST 0x400000 +#define CURLINFO_MASK 0x0fffff +#define CURLINFO_TYPEMASK 0xf00000 + +typedef enum { + CURLINFO_NONE, /* first, never use this */ + CURLINFO_EFFECTIVE_URL = CURLINFO_STRING + 1, + CURLINFO_RESPONSE_CODE = CURLINFO_LONG + 2, + CURLINFO_TOTAL_TIME = CURLINFO_DOUBLE + 3, + CURLINFO_NAMELOOKUP_TIME = CURLINFO_DOUBLE + 4, + CURLINFO_CONNECT_TIME = CURLINFO_DOUBLE + 5, + CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE + 6, + CURLINFO_SIZE_UPLOAD = CURLINFO_DOUBLE + 7, + CURLINFO_SIZE_DOWNLOAD = CURLINFO_DOUBLE + 8, + CURLINFO_SPEED_DOWNLOAD = CURLINFO_DOUBLE + 9, + CURLINFO_SPEED_UPLOAD = CURLINFO_DOUBLE + 10, + CURLINFO_HEADER_SIZE = CURLINFO_LONG + 11, + CURLINFO_REQUEST_SIZE = CURLINFO_LONG + 12, + CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG + 13, + CURLINFO_FILETIME = CURLINFO_LONG + 14, + CURLINFO_CONTENT_LENGTH_DOWNLOAD = CURLINFO_DOUBLE + 15, + CURLINFO_CONTENT_LENGTH_UPLOAD = CURLINFO_DOUBLE + 16, + CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE + 17, + CURLINFO_CONTENT_TYPE = CURLINFO_STRING + 18, + CURLINFO_REDIRECT_TIME = CURLINFO_DOUBLE + 19, + CURLINFO_REDIRECT_COUNT = CURLINFO_LONG + 20, + CURLINFO_PRIVATE = CURLINFO_STRING + 21, + CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG + 22, + CURLINFO_HTTPAUTH_AVAIL = CURLINFO_LONG + 23, + CURLINFO_PROXYAUTH_AVAIL = CURLINFO_LONG + 24, + CURLINFO_OS_ERRNO = CURLINFO_LONG + 25, + CURLINFO_NUM_CONNECTS = CURLINFO_LONG + 26, + CURLINFO_SSL_ENGINES = CURLINFO_SLIST + 27, + CURLINFO_COOKIELIST = CURLINFO_SLIST + 28, + CURLINFO_LASTSOCKET = CURLINFO_LONG + 29, + CURLINFO_FTP_ENTRY_PATH = CURLINFO_STRING + 30, + CURLINFO_REDIRECT_URL = CURLINFO_STRING + 31, + CURLINFO_PRIMARY_IP = CURLINFO_STRING + 32, + CURLINFO_APPCONNECT_TIME = CURLINFO_DOUBLE + 33, + CURLINFO_CERTINFO = CURLINFO_SLIST + 34, + CURLINFO_CONDITION_UNMET = CURLINFO_LONG + 35, + CURLINFO_RTSP_SESSION_ID = CURLINFO_STRING + 36, + CURLINFO_RTSP_CLIENT_CSEQ = CURLINFO_LONG + 37, + CURLINFO_RTSP_SERVER_CSEQ = CURLINFO_LONG + 38, + CURLINFO_RTSP_CSEQ_RECV = CURLINFO_LONG + 39, + CURLINFO_PRIMARY_PORT = CURLINFO_LONG + 40, + CURLINFO_LOCAL_IP = CURLINFO_STRING + 41, + CURLINFO_LOCAL_PORT = CURLINFO_LONG + 42, + CURLINFO_TLS_SESSION = CURLINFO_SLIST + 43, + /* Fill in new entries below here! */ + + CURLINFO_LASTONE = 43 +} CURLINFO; + +/* CURLINFO_RESPONSE_CODE is the new name for the option previously known as + CURLINFO_HTTP_CODE */ +#define CURLINFO_HTTP_CODE CURLINFO_RESPONSE_CODE + +typedef enum { + CURLCLOSEPOLICY_NONE, /* first, never use this */ + + CURLCLOSEPOLICY_OLDEST, + CURLCLOSEPOLICY_LEAST_RECENTLY_USED, + CURLCLOSEPOLICY_LEAST_TRAFFIC, + CURLCLOSEPOLICY_SLOWEST, + CURLCLOSEPOLICY_CALLBACK, + + CURLCLOSEPOLICY_LAST /* last, never use this */ +} curl_closepolicy; + +#define CURL_GLOBAL_SSL (1<<0) +#define CURL_GLOBAL_WIN32 (1<<1) +#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) +#define CURL_GLOBAL_NOTHING 0 +#define CURL_GLOBAL_DEFAULT CURL_GLOBAL_ALL +#define CURL_GLOBAL_ACK_EINTR (1<<2) + + +/***************************************************************************** + * Setup defines, protos etc for the sharing stuff. + */ + +/* Different data locks for a single share */ +typedef enum { + CURL_LOCK_DATA_NONE = 0, + /* CURL_LOCK_DATA_SHARE is used internally to say that + * the locking is just made to change the internal state of the share + * itself. + */ + CURL_LOCK_DATA_SHARE, + CURL_LOCK_DATA_COOKIE, + CURL_LOCK_DATA_DNS, + CURL_LOCK_DATA_SSL_SESSION, + CURL_LOCK_DATA_CONNECT, + CURL_LOCK_DATA_LAST +} curl_lock_data; + +/* Different lock access types */ +typedef enum { + CURL_LOCK_ACCESS_NONE = 0, /* unspecified action */ + CURL_LOCK_ACCESS_SHARED = 1, /* for read perhaps */ + CURL_LOCK_ACCESS_SINGLE = 2, /* for write perhaps */ + CURL_LOCK_ACCESS_LAST /* never use */ +} curl_lock_access; + +typedef void (*curl_lock_function)(CURL *handle, + curl_lock_data data, + curl_lock_access locktype, + void *userptr); +typedef void (*curl_unlock_function)(CURL *handle, + curl_lock_data data, + void *userptr); + +typedef void CURLSH; + +typedef enum { + CURLSHE_OK, /* all is fine */ + CURLSHE_BAD_OPTION, /* 1 */ + CURLSHE_IN_USE, /* 2 */ + CURLSHE_INVALID, /* 3 */ + CURLSHE_NOMEM, /* 4 out of memory */ + CURLSHE_NOT_BUILT_IN, /* 5 feature not present in lib */ + CURLSHE_LAST /* never use */ +} CURLSHcode; + +typedef enum { + CURLSHOPT_NONE, /* don't use */ + CURLSHOPT_SHARE, /* specify a data type to share */ + CURLSHOPT_UNSHARE, /* specify which data type to stop sharing */ + CURLSHOPT_LOCKFUNC, /* pass in a 'curl_lock_function' pointer */ + CURLSHOPT_UNLOCKFUNC, /* pass in a 'curl_unlock_function' pointer */ + CURLSHOPT_USERDATA, /* pass in a user data pointer used in the lock/unlock + callback functions */ + CURLSHOPT_LAST /* never use */ +} CURLSHoption; + +CURL_EXTERN CURLSH *curl_share_init(void); +CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *, CURLSHoption option, ...); +CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *); + +/**************************************************************************** + * Structures for querying information about the curl library at runtime. + */ + +typedef enum { + CURLVERSION_FIRST, + CURLVERSION_SECOND, + CURLVERSION_THIRD, + CURLVERSION_FOURTH, + CURLVERSION_LAST /* never actually use this */ +} CURLversion; + +/* The 'CURLVERSION_NOW' is the symbolic name meant to be used by + basically all programs ever that want to get version information. It is + meant to be a built-in version number for what kind of struct the caller + expects. If the struct ever changes, we redefine the NOW to another enum + from above. */ +#define CURLVERSION_NOW CURLVERSION_FOURTH + +typedef struct { + CURLversion age; /* age of the returned struct */ + const char *version; /* LIBCURL_VERSION */ + unsigned int version_num; /* LIBCURL_VERSION_NUM */ + const char *host; /* OS/host/cpu/machine when configured */ + int features; /* bitmask, see defines below */ + const char *ssl_version; /* human readable string */ + long ssl_version_num; /* not used anymore, always 0 */ + const char *libz_version; /* human readable string */ + /* protocols is terminated by an entry with a NULL protoname */ + const char * const *protocols; + + /* The fields below this were added in CURLVERSION_SECOND */ + const char *ares; + int ares_num; + + /* This field was added in CURLVERSION_THIRD */ + const char *libidn; + + /* These field were added in CURLVERSION_FOURTH */ + + /* Same as '_libiconv_version' if built with HAVE_ICONV */ + int iconv_ver_num; + + const char *libssh_version; /* human readable string */ + +} curl_version_info_data; + +#define CURL_VERSION_IPV6 (1<<0) /* IPv6-enabled */ +#define CURL_VERSION_KERBEROS4 (1<<1) /* Kerberos V4 auth is supported + (deprecated) */ +#define CURL_VERSION_SSL (1<<2) /* SSL options are present */ +#define CURL_VERSION_LIBZ (1<<3) /* libz features are present */ +#define CURL_VERSION_NTLM (1<<4) /* NTLM auth is supported */ +#define CURL_VERSION_GSSNEGOTIATE (1<<5) /* Negotiate auth is supported + (deprecated) */ +#define CURL_VERSION_DEBUG (1<<6) /* Built with debug capabilities */ +#define CURL_VERSION_ASYNCHDNS (1<<7) /* Asynchronous DNS resolves */ +#define CURL_VERSION_SPNEGO (1<<8) /* SPNEGO auth is supported */ +#define CURL_VERSION_LARGEFILE (1<<9) /* Supports files larger than 2GB */ +#define CURL_VERSION_IDN (1<<10) /* Internationized Domain Names are + supported */ +#define CURL_VERSION_SSPI (1<<11) /* Built against Windows SSPI */ +#define CURL_VERSION_CONV (1<<12) /* Character conversions supported */ +#define CURL_VERSION_CURLDEBUG (1<<13) /* Debug memory tracking supported */ +#define CURL_VERSION_TLSAUTH_SRP (1<<14) /* TLS-SRP auth is supported */ +#define CURL_VERSION_NTLM_WB (1<<15) /* NTLM delegation to winbind helper + is suported */ +#define CURL_VERSION_HTTP2 (1<<16) /* HTTP2 support built-in */ +#define CURL_VERSION_GSSAPI (1<<17) /* Built against a GSS-API library */ +#define CURL_VERSION_KERBEROS5 (1<<18) /* Kerberos V5 auth is supported */ +#define CURL_VERSION_UNIX_SOCKETS (1<<19) /* Unix domain sockets support */ + + /* + * NAME curl_version_info() + * + * DESCRIPTION + * + * This function returns a pointer to a static copy of the version info + * struct. See above. + */ +CURL_EXTERN curl_version_info_data *curl_version_info(CURLversion); + +/* + * NAME curl_easy_strerror() + * + * DESCRIPTION + * + * The curl_easy_strerror function may be used to turn a CURLcode value + * into the equivalent human readable error string. This is useful + * for printing meaningful error messages. + */ +CURL_EXTERN const char *curl_easy_strerror(CURLcode); + +/* + * NAME curl_share_strerror() + * + * DESCRIPTION + * + * The curl_share_strerror function may be used to turn a CURLSHcode value + * into the equivalent human readable error string. This is useful + * for printing meaningful error messages. + */ +CURL_EXTERN const char *curl_share_strerror(CURLSHcode); + +/* + * NAME curl_easy_pause() + * + * DESCRIPTION + * + * The curl_easy_pause function pauses or unpauses transfers. Select the new + * state by setting the bitmask, use the convenience defines below. + * + */ +CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); + +#define CURLPAUSE_RECV (1<<0) +#define CURLPAUSE_RECV_CONT (0) + +#define CURLPAUSE_SEND (1<<2) +#define CURLPAUSE_SEND_CONT (0) + +#define CURLPAUSE_ALL (CURLPAUSE_RECV|CURLPAUSE_SEND) +#define CURLPAUSE_CONT (CURLPAUSE_RECV_CONT|CURLPAUSE_SEND_CONT) + +#ifdef __cplusplus +} +#endif + +/* unfortunately, the easy.h and multi.h include files need options and info + stuff before they can be included! */ +#include "easy.h" /* nothing in curl is fun without the easy stuff */ +#include "multi.h" + +/* the typechecker doesn't work in C++ (yet) */ +#if defined(__GNUC__) && defined(__GNUC_MINOR__) && \ + ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) && \ + !defined(__cplusplus) && !defined(CURL_DISABLE_TYPECHECK) +#include "typecheck-gcc.h" +#else +#if defined(__STDC__) && (__STDC__ >= 1) +/* This preprocessor magic that replaces a call with the exact same call is + only done to make sure application authors pass exactly three arguments + to these functions. */ +#define curl_easy_setopt(handle,opt,param) curl_easy_setopt(handle,opt,param) +#define curl_easy_getinfo(handle,info,arg) curl_easy_getinfo(handle,info,arg) +#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param) +#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param) +#endif /* __STDC__ >= 1 */ +#endif /* gcc >= 4.3 && !__cplusplus */ + +#endif /* __CURL_CURL_H */ diff --git a/ext/curl/include/curl/curlbuild.h b/ext/curl/include/curl/curlbuild.h new file mode 100644 index 0000000000..f09419a843 --- /dev/null +++ b/ext/curl/include/curl/curlbuild.h @@ -0,0 +1,585 @@ +#ifndef __CURL_CURLBUILD_H +#define __CURL_CURLBUILD_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2013, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* ================================================================ */ +/* NOTES FOR CONFIGURE CAPABLE SYSTEMS */ +/* ================================================================ */ + +/* + * NOTE 1: + * ------- + * + * See file include/curl/curlbuild.h.in, run configure, and forget + * that this file exists it is only used for non-configure systems. + * But you can keep reading if you want ;-) + * + */ + +/* ================================================================ */ +/* NOTES FOR NON-CONFIGURE SYSTEMS */ +/* ================================================================ */ + +/* + * NOTE 1: + * ------- + * + * Nothing in this file is intended to be modified or adjusted by the + * curl library user nor by the curl library builder. + * + * If you think that something actually needs to be changed, adjusted + * or fixed in this file, then, report it on the libcurl development + * mailing list: http://cool.haxx.se/mailman/listinfo/curl-library/ + * + * Try to keep one section per platform, compiler and architecture, + * otherwise, if an existing section is reused for a different one and + * later on the original is adjusted, probably the piggybacking one can + * be adversely changed. + * + * In order to differentiate between platforms/compilers/architectures + * use only compiler built in predefined preprocessor symbols. + * + * This header file shall only export symbols which are 'curl' or 'CURL' + * prefixed, otherwise public name space would be polluted. + * + * NOTE 2: + * ------- + * + * For any given platform/compiler curl_off_t must be typedef'ed to a + * 64-bit wide signed integral data type. The width of this data type + * must remain constant and independent of any possible large file + * support settings. + * + * As an exception to the above, curl_off_t shall be typedef'ed to a + * 32-bit wide signed integral data type if there is no 64-bit type. + * + * As a general rule, curl_off_t shall not be mapped to off_t. This + * rule shall only be violated if off_t is the only 64-bit data type + * available and the size of off_t is independent of large file support + * settings. Keep your build on the safe side avoiding an off_t gating. + * If you have a 64-bit off_t then take for sure that another 64-bit + * data type exists, dig deeper and you will find it. + * + * NOTE 3: + * ------- + * + * Right now you might be staring at file include/curl/curlbuild.h.dist or + * at file include/curl/curlbuild.h, this is due to the following reason: + * file include/curl/curlbuild.h.dist is renamed to include/curl/curlbuild.h + * when the libcurl source code distribution archive file is created. + * + * File include/curl/curlbuild.h.dist is not included in the distribution + * archive. File include/curl/curlbuild.h is not present in the git tree. + * + * The distributed include/curl/curlbuild.h file is only intended to be used + * on systems which can not run the also distributed configure script. + * + * On systems capable of running the configure script, the configure process + * will overwrite the distributed include/curl/curlbuild.h file with one that + * is suitable and specific to the library being configured and built, which + * is generated from the include/curl/curlbuild.h.in template file. + * + * If you check out from git on a non-configure platform, you must run the + * appropriate buildconf* script to set up curlbuild.h and other local files. + * + */ + +/* ================================================================ */ +/* DEFINITION OF THESE SYMBOLS SHALL NOT TAKE PLACE ANYWHERE ELSE */ +/* ================================================================ */ + +#ifdef CURL_SIZEOF_LONG +# error "CURL_SIZEOF_LONG shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SIZEOF_LONG_already_defined +#endif + +#ifdef CURL_TYPEOF_CURL_SOCKLEN_T +# error "CURL_TYPEOF_CURL_SOCKLEN_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_TYPEOF_CURL_SOCKLEN_T_already_defined +#endif + +#ifdef CURL_SIZEOF_CURL_SOCKLEN_T +# error "CURL_SIZEOF_CURL_SOCKLEN_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SIZEOF_CURL_SOCKLEN_T_already_defined +#endif + +#ifdef CURL_TYPEOF_CURL_OFF_T +# error "CURL_TYPEOF_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_TYPEOF_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_FORMAT_CURL_OFF_T +# error "CURL_FORMAT_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_FORMAT_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_FORMAT_CURL_OFF_TU +# error "CURL_FORMAT_CURL_OFF_TU shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_FORMAT_CURL_OFF_TU_already_defined +#endif + +#ifdef CURL_FORMAT_OFF_T +# error "CURL_FORMAT_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_FORMAT_OFF_T_already_defined +#endif + +#ifdef CURL_SIZEOF_CURL_OFF_T +# error "CURL_SIZEOF_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SIZEOF_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_SUFFIX_CURL_OFF_T +# error "CURL_SUFFIX_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_SUFFIX_CURL_OFF_TU +# error "CURL_SUFFIX_CURL_OFF_TU shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_TU_already_defined +#endif + +/* ================================================================ */ +/* EXTERNAL INTERFACE SETTINGS FOR NON-CONFIGURE SYSTEMS ONLY */ +/* ================================================================ */ + +#if defined(__DJGPP__) || defined(__GO32__) +# if defined(__DJGPP__) && (__DJGPP__ > 1) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# else +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__SALFORDC__) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__BORLANDC__) +# if (__BORLANDC__ < 0x520) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_FORMAT_OFF_T "%I64d" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__TURBOC__) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__WATCOMC__) +# if defined(__386__) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_FORMAT_OFF_T "%I64d" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# else +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__POCC__) +# if (__POCC__ < 280) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# elif defined(_MSC_VER) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_FORMAT_OFF_T "%I64d" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# else +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__LCC__) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__SYMBIAN32__) +# if defined(__EABI__) /* Treat all ARM compilers equally */ +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(__CW32__) +# pragma longlong on +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(__VC32__) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__MWERKS__) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(_WIN32_WCE) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_FORMAT_OFF_T "%I64d" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__MINGW32__) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_FORMAT_OFF_T "%I64d" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__VMS) +# if defined(__VAX) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__OS400__) +# if defined(__ILEC400__) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 +# endif + +#elif defined(__MVS__) +# if defined(__IBMC__) || defined(__IBMCPP__) +# if defined(_ILP32) +# define CURL_SIZEOF_LONG 4 +# elif defined(_LP64) +# define CURL_SIZEOF_LONG 8 +# endif +# if defined(_LONG_LONG) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(_LP64) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 +# endif + +#elif defined(__370__) +# if defined(__IBMC__) || defined(__IBMCPP__) +# if defined(_ILP32) +# define CURL_SIZEOF_LONG 4 +# elif defined(_LP64) +# define CURL_SIZEOF_LONG 8 +# endif +# if defined(_LONG_LONG) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(_LP64) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 +# endif + +#elif defined(TPF) +# define CURL_SIZEOF_LONG 8 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +/* ===================================== */ +/* KEEP MSVC THE PENULTIMATE ENTRY */ +/* ===================================== */ + +#elif defined(_MSC_VER) +# if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_FORMAT_OFF_T "%I64d" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# else +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +/* ===================================== */ +/* KEEP GENERIC GCC THE LAST ENTRY */ +/* ===================================== */ + +#elif defined(__GNUC__) +# if defined(__ILP32__) || \ + defined(__i386__) || defined(__ppc__) || defined(__arm__) || defined(__sparc__) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(__LP64__) || \ + defined(__x86_64__) || defined(__ppc64__) || defined(__sparc64__) +# define CURL_SIZEOF_LONG 8 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 + +#else +# error "Unknown non-configure build target!" + Error Compilation_aborted_Unknown_non_configure_build_target +#endif + +/* CURL_PULL_SYS_TYPES_H is defined above when inclusion of header file */ +/* sys/types.h is required here to properly make type definitions below. */ +#ifdef CURL_PULL_SYS_TYPES_H +# include +#endif + +/* CURL_PULL_SYS_SOCKET_H is defined above when inclusion of header file */ +/* sys/socket.h is required here to properly make type definitions below. */ +#ifdef CURL_PULL_SYS_SOCKET_H +# include +#endif + +/* Data type definition of curl_socklen_t. */ + +#ifdef CURL_TYPEOF_CURL_SOCKLEN_T + typedef CURL_TYPEOF_CURL_SOCKLEN_T curl_socklen_t; +#endif + +/* Data type definition of curl_off_t. */ + +#ifdef CURL_TYPEOF_CURL_OFF_T + typedef CURL_TYPEOF_CURL_OFF_T curl_off_t; +#endif + +#endif /* __CURL_CURLBUILD_H */ diff --git a/ext/curl/include/curl/curlbuild.h.cmake b/ext/curl/include/curl/curlbuild.h.cmake new file mode 100644 index 0000000000..60bc7a70ec --- /dev/null +++ b/ext/curl/include/curl/curlbuild.h.cmake @@ -0,0 +1,197 @@ +#ifndef __CURL_CURLBUILD_H +#define __CURL_CURLBUILD_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* ================================================================ */ +/* NOTES FOR CONFIGURE CAPABLE SYSTEMS */ +/* ================================================================ */ + +/* + * NOTE 1: + * ------- + * + * Nothing in this file is intended to be modified or adjusted by the + * curl library user nor by the curl library builder. + * + * If you think that something actually needs to be changed, adjusted + * or fixed in this file, then, report it on the libcurl development + * mailing list: http://cool.haxx.se/mailman/listinfo/curl-library/ + * + * This header file shall only export symbols which are 'curl' or 'CURL' + * prefixed, otherwise public name space would be polluted. + * + * NOTE 2: + * ------- + * + * Right now you might be staring at file include/curl/curlbuild.h.in or + * at file include/curl/curlbuild.h, this is due to the following reason: + * + * On systems capable of running the configure script, the configure process + * will overwrite the distributed include/curl/curlbuild.h file with one that + * is suitable and specific to the library being configured and built, which + * is generated from the include/curl/curlbuild.h.in template file. + * + */ + +/* ================================================================ */ +/* DEFINITION OF THESE SYMBOLS SHALL NOT TAKE PLACE ANYWHERE ELSE */ +/* ================================================================ */ + +#ifdef CURL_SIZEOF_LONG +#error "CURL_SIZEOF_LONG shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SIZEOF_LONG_already_defined +#endif + +#ifdef CURL_TYPEOF_CURL_SOCKLEN_T +#error "CURL_TYPEOF_CURL_SOCKLEN_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_TYPEOF_CURL_SOCKLEN_T_already_defined +#endif + +#ifdef CURL_SIZEOF_CURL_SOCKLEN_T +#error "CURL_SIZEOF_CURL_SOCKLEN_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SIZEOF_CURL_SOCKLEN_T_already_defined +#endif + +#ifdef CURL_TYPEOF_CURL_OFF_T +#error "CURL_TYPEOF_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_TYPEOF_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_FORMAT_CURL_OFF_T +#error "CURL_FORMAT_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_FORMAT_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_FORMAT_CURL_OFF_TU +#error "CURL_FORMAT_CURL_OFF_TU shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_FORMAT_CURL_OFF_TU_already_defined +#endif + +#ifdef CURL_FORMAT_OFF_T +#error "CURL_FORMAT_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_FORMAT_OFF_T_already_defined +#endif + +#ifdef CURL_SIZEOF_CURL_OFF_T +#error "CURL_SIZEOF_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SIZEOF_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_SUFFIX_CURL_OFF_T +#error "CURL_SUFFIX_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_SUFFIX_CURL_OFF_TU +#error "CURL_SUFFIX_CURL_OFF_TU shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_TU_already_defined +#endif + +/* ================================================================ */ +/* EXTERNAL INTERFACE SETTINGS FOR CONFIGURE CAPABLE SYSTEMS ONLY */ +/* ================================================================ */ + +/* Configure process defines this to 1 when it finds out that system */ +/* header file ws2tcpip.h must be included by the external interface. */ +#cmakedefine CURL_PULL_WS2TCPIP_H +#ifdef CURL_PULL_WS2TCPIP_H +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# include +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file sys/types.h must be included by the external interface. */ +#cmakedefine CURL_PULL_SYS_TYPES_H +#ifdef CURL_PULL_SYS_TYPES_H +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file stdint.h must be included by the external interface. */ +#cmakedefine CURL_PULL_STDINT_H +#ifdef CURL_PULL_STDINT_H +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file inttypes.h must be included by the external interface. */ +#cmakedefine CURL_PULL_INTTYPES_H +#ifdef CURL_PULL_INTTYPES_H +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file sys/socket.h must be included by the external interface. */ +#cmakedefine CURL_PULL_SYS_SOCKET_H +#ifdef CURL_PULL_SYS_SOCKET_H +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file sys/poll.h must be included by the external interface. */ +#cmakedefine CURL_PULL_SYS_POLL_H +#ifdef CURL_PULL_SYS_POLL_H +# include +#endif + +/* The size of `long', as computed by sizeof. */ +#define CURL_SIZEOF_LONG ${CURL_SIZEOF_LONG} + +/* Integral data type used for curl_socklen_t. */ +#define CURL_TYPEOF_CURL_SOCKLEN_T ${CURL_TYPEOF_CURL_SOCKLEN_T} + +/* The size of `curl_socklen_t', as computed by sizeof. */ +#define CURL_SIZEOF_CURL_SOCKLEN_T ${CURL_SIZEOF_CURL_SOCKLEN_T} + +/* Data type definition of curl_socklen_t. */ +typedef CURL_TYPEOF_CURL_SOCKLEN_T curl_socklen_t; + +/* Signed integral data type used for curl_off_t. */ +#define CURL_TYPEOF_CURL_OFF_T ${CURL_TYPEOF_CURL_OFF_T} + +/* Data type definition of curl_off_t. */ +typedef CURL_TYPEOF_CURL_OFF_T curl_off_t; + +/* curl_off_t formatting string directive without "%" conversion specifier. */ +#define CURL_FORMAT_CURL_OFF_T "${CURL_FORMAT_CURL_OFF_T}" + +/* unsigned curl_off_t formatting string without "%" conversion specifier. */ +#define CURL_FORMAT_CURL_OFF_TU "${CURL_FORMAT_CURL_OFF_TU}" + +/* curl_off_t formatting string directive with "%" conversion specifier. */ +#define CURL_FORMAT_OFF_T "${CURL_FORMAT_OFF_T}" + +/* The size of `curl_off_t', as computed by sizeof. */ +#define CURL_SIZEOF_CURL_OFF_T ${CURL_SIZEOF_CURL_OFF_T} + +/* curl_off_t constant suffix. */ +#define CURL_SUFFIX_CURL_OFF_T ${CURL_SUFFIX_CURL_OFF_T} + +/* unsigned curl_off_t constant suffix. */ +#define CURL_SUFFIX_CURL_OFF_TU ${CURL_SUFFIX_CURL_OFF_TU} + +#endif /* __CURL_CURLBUILD_H */ diff --git a/ext/curl/include/curl/curlbuild.h.in b/ext/curl/include/curl/curlbuild.h.in new file mode 100644 index 0000000000..e29f195d25 --- /dev/null +++ b/ext/curl/include/curl/curlbuild.h.in @@ -0,0 +1,197 @@ +#ifndef __CURL_CURLBUILD_H +#define __CURL_CURLBUILD_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* ================================================================ */ +/* NOTES FOR CONFIGURE CAPABLE SYSTEMS */ +/* ================================================================ */ + +/* + * NOTE 1: + * ------- + * + * Nothing in this file is intended to be modified or adjusted by the + * curl library user nor by the curl library builder. + * + * If you think that something actually needs to be changed, adjusted + * or fixed in this file, then, report it on the libcurl development + * mailing list: http://cool.haxx.se/mailman/listinfo/curl-library/ + * + * This header file shall only export symbols which are 'curl' or 'CURL' + * prefixed, otherwise public name space would be polluted. + * + * NOTE 2: + * ------- + * + * Right now you might be staring at file include/curl/curlbuild.h.in or + * at file include/curl/curlbuild.h, this is due to the following reason: + * + * On systems capable of running the configure script, the configure process + * will overwrite the distributed include/curl/curlbuild.h file with one that + * is suitable and specific to the library being configured and built, which + * is generated from the include/curl/curlbuild.h.in template file. + * + */ + +/* ================================================================ */ +/* DEFINITION OF THESE SYMBOLS SHALL NOT TAKE PLACE ANYWHERE ELSE */ +/* ================================================================ */ + +#ifdef CURL_SIZEOF_LONG +#error "CURL_SIZEOF_LONG shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SIZEOF_LONG_already_defined +#endif + +#ifdef CURL_TYPEOF_CURL_SOCKLEN_T +#error "CURL_TYPEOF_CURL_SOCKLEN_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_TYPEOF_CURL_SOCKLEN_T_already_defined +#endif + +#ifdef CURL_SIZEOF_CURL_SOCKLEN_T +#error "CURL_SIZEOF_CURL_SOCKLEN_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SIZEOF_CURL_SOCKLEN_T_already_defined +#endif + +#ifdef CURL_TYPEOF_CURL_OFF_T +#error "CURL_TYPEOF_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_TYPEOF_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_FORMAT_CURL_OFF_T +#error "CURL_FORMAT_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_FORMAT_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_FORMAT_CURL_OFF_TU +#error "CURL_FORMAT_CURL_OFF_TU shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_FORMAT_CURL_OFF_TU_already_defined +#endif + +#ifdef CURL_FORMAT_OFF_T +#error "CURL_FORMAT_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_FORMAT_OFF_T_already_defined +#endif + +#ifdef CURL_SIZEOF_CURL_OFF_T +#error "CURL_SIZEOF_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SIZEOF_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_SUFFIX_CURL_OFF_T +#error "CURL_SUFFIX_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_SUFFIX_CURL_OFF_TU +#error "CURL_SUFFIX_CURL_OFF_TU shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_TU_already_defined +#endif + +/* ================================================================ */ +/* EXTERNAL INTERFACE SETTINGS FOR CONFIGURE CAPABLE SYSTEMS ONLY */ +/* ================================================================ */ + +/* Configure process defines this to 1 when it finds out that system */ +/* header file ws2tcpip.h must be included by the external interface. */ +#undef CURL_PULL_WS2TCPIP_H +#ifdef CURL_PULL_WS2TCPIP_H +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# include +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file sys/types.h must be included by the external interface. */ +#undef CURL_PULL_SYS_TYPES_H +#ifdef CURL_PULL_SYS_TYPES_H +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file stdint.h must be included by the external interface. */ +#undef CURL_PULL_STDINT_H +#ifdef CURL_PULL_STDINT_H +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file inttypes.h must be included by the external interface. */ +#undef CURL_PULL_INTTYPES_H +#ifdef CURL_PULL_INTTYPES_H +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file sys/socket.h must be included by the external interface. */ +#undef CURL_PULL_SYS_SOCKET_H +#ifdef CURL_PULL_SYS_SOCKET_H +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file sys/poll.h must be included by the external interface. */ +#undef CURL_PULL_SYS_POLL_H +#ifdef CURL_PULL_SYS_POLL_H +# include +#endif + +/* The size of `long', as computed by sizeof. */ +#undef CURL_SIZEOF_LONG + +/* Integral data type used for curl_socklen_t. */ +#undef CURL_TYPEOF_CURL_SOCKLEN_T + +/* The size of `curl_socklen_t', as computed by sizeof. */ +#undef CURL_SIZEOF_CURL_SOCKLEN_T + +/* Data type definition of curl_socklen_t. */ +typedef CURL_TYPEOF_CURL_SOCKLEN_T curl_socklen_t; + +/* Signed integral data type used for curl_off_t. */ +#undef CURL_TYPEOF_CURL_OFF_T + +/* Data type definition of curl_off_t. */ +typedef CURL_TYPEOF_CURL_OFF_T curl_off_t; + +/* curl_off_t formatting string directive without "%" conversion specifier. */ +#undef CURL_FORMAT_CURL_OFF_T + +/* unsigned curl_off_t formatting string without "%" conversion specifier. */ +#undef CURL_FORMAT_CURL_OFF_TU + +/* curl_off_t formatting string directive with "%" conversion specifier. */ +#undef CURL_FORMAT_OFF_T + +/* The size of `curl_off_t', as computed by sizeof. */ +#undef CURL_SIZEOF_CURL_OFF_T + +/* curl_off_t constant suffix. */ +#undef CURL_SUFFIX_CURL_OFF_T + +/* unsigned curl_off_t constant suffix. */ +#undef CURL_SUFFIX_CURL_OFF_TU + +#endif /* __CURL_CURLBUILD_H */ diff --git a/ext/curl/include/curl/curlrules.h b/ext/curl/include/curl/curlrules.h new file mode 100644 index 0000000000..7c2ede35b6 --- /dev/null +++ b/ext/curl/include/curl/curlrules.h @@ -0,0 +1,262 @@ +#ifndef __CURL_CURLRULES_H +#define __CURL_CURLRULES_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* ================================================================ */ +/* COMPILE TIME SANITY CHECKS */ +/* ================================================================ */ + +/* + * NOTE 1: + * ------- + * + * All checks done in this file are intentionally placed in a public + * header file which is pulled by curl/curl.h when an application is + * being built using an already built libcurl library. Additionally + * this file is also included and used when building the library. + * + * If compilation fails on this file it is certainly sure that the + * problem is elsewhere. It could be a problem in the curlbuild.h + * header file, or simply that you are using different compilation + * settings than those used to build the library. + * + * Nothing in this file is intended to be modified or adjusted by the + * curl library user nor by the curl library builder. + * + * Do not deactivate any check, these are done to make sure that the + * library is properly built and used. + * + * You can find further help on the libcurl development mailing list: + * http://cool.haxx.se/mailman/listinfo/curl-library/ + * + * NOTE 2 + * ------ + * + * Some of the following compile time checks are based on the fact + * that the dimension of a constant array can not be a negative one. + * In this way if the compile time verification fails, the compilation + * will fail issuing an error. The error description wording is compiler + * dependent but it will be quite similar to one of the following: + * + * "negative subscript or subscript is too large" + * "array must have at least one element" + * "-1 is an illegal array size" + * "size of array is negative" + * + * If you are building an application which tries to use an already + * built libcurl library and you are getting this kind of errors on + * this file, it is a clear indication that there is a mismatch between + * how the library was built and how you are trying to use it for your + * application. Your already compiled or binary library provider is the + * only one who can give you the details you need to properly use it. + */ + +/* + * Verify that some macros are actually defined. + */ + +#ifndef CURL_SIZEOF_LONG +# error "CURL_SIZEOF_LONG definition is missing!" + Error Compilation_aborted_CURL_SIZEOF_LONG_is_missing +#endif + +#ifndef CURL_TYPEOF_CURL_SOCKLEN_T +# error "CURL_TYPEOF_CURL_SOCKLEN_T definition is missing!" + Error Compilation_aborted_CURL_TYPEOF_CURL_SOCKLEN_T_is_missing +#endif + +#ifndef CURL_SIZEOF_CURL_SOCKLEN_T +# error "CURL_SIZEOF_CURL_SOCKLEN_T definition is missing!" + Error Compilation_aborted_CURL_SIZEOF_CURL_SOCKLEN_T_is_missing +#endif + +#ifndef CURL_TYPEOF_CURL_OFF_T +# error "CURL_TYPEOF_CURL_OFF_T definition is missing!" + Error Compilation_aborted_CURL_TYPEOF_CURL_OFF_T_is_missing +#endif + +#ifndef CURL_FORMAT_CURL_OFF_T +# error "CURL_FORMAT_CURL_OFF_T definition is missing!" + Error Compilation_aborted_CURL_FORMAT_CURL_OFF_T_is_missing +#endif + +#ifndef CURL_FORMAT_CURL_OFF_TU +# error "CURL_FORMAT_CURL_OFF_TU definition is missing!" + Error Compilation_aborted_CURL_FORMAT_CURL_OFF_TU_is_missing +#endif + +#ifndef CURL_FORMAT_OFF_T +# error "CURL_FORMAT_OFF_T definition is missing!" + Error Compilation_aborted_CURL_FORMAT_OFF_T_is_missing +#endif + +#ifndef CURL_SIZEOF_CURL_OFF_T +# error "CURL_SIZEOF_CURL_OFF_T definition is missing!" + Error Compilation_aborted_CURL_SIZEOF_CURL_OFF_T_is_missing +#endif + +#ifndef CURL_SUFFIX_CURL_OFF_T +# error "CURL_SUFFIX_CURL_OFF_T definition is missing!" + Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_T_is_missing +#endif + +#ifndef CURL_SUFFIX_CURL_OFF_TU +# error "CURL_SUFFIX_CURL_OFF_TU definition is missing!" + Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_TU_is_missing +#endif + +/* + * Macros private to this header file. + */ + +#define CurlchkszEQ(t, s) sizeof(t) == s ? 1 : -1 + +#define CurlchkszGE(t1, t2) sizeof(t1) >= sizeof(t2) ? 1 : -1 + +/* + * Verify that the size previously defined and expected for long + * is the same as the one reported by sizeof() at compile time. + */ + +typedef char + __curl_rule_01__ + [CurlchkszEQ(long, CURL_SIZEOF_LONG)]; + +/* + * Verify that the size previously defined and expected for + * curl_off_t is actually the the same as the one reported + * by sizeof() at compile time. + */ + +typedef char + __curl_rule_02__ + [CurlchkszEQ(curl_off_t, CURL_SIZEOF_CURL_OFF_T)]; + +/* + * Verify at compile time that the size of curl_off_t as reported + * by sizeof() is greater or equal than the one reported for long + * for the current compilation. + */ + +typedef char + __curl_rule_03__ + [CurlchkszGE(curl_off_t, long)]; + +/* + * Verify that the size previously defined and expected for + * curl_socklen_t is actually the the same as the one reported + * by sizeof() at compile time. + */ + +typedef char + __curl_rule_04__ + [CurlchkszEQ(curl_socklen_t, CURL_SIZEOF_CURL_SOCKLEN_T)]; + +/* + * Verify at compile time that the size of curl_socklen_t as reported + * by sizeof() is greater or equal than the one reported for int for + * the current compilation. + */ + +typedef char + __curl_rule_05__ + [CurlchkszGE(curl_socklen_t, int)]; + +/* ================================================================ */ +/* EXTERNALLY AND INTERNALLY VISIBLE DEFINITIONS */ +/* ================================================================ */ + +/* + * CURL_ISOCPP and CURL_OFF_T_C definitions are done here in order to allow + * these to be visible and exported by the external libcurl interface API, + * while also making them visible to the library internals, simply including + * curl_setup.h, without actually needing to include curl.h internally. + * If some day this section would grow big enough, all this should be moved + * to its own header file. + */ + +/* + * Figure out if we can use the ## preprocessor operator, which is supported + * by ISO/ANSI C and C++. Some compilers support it without setting __STDC__ + * or __cplusplus so we need to carefully check for them too. + */ + +#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) || \ + defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) || \ + defined(__POCC__) || defined(__SALFORDC__) || defined(__HIGHC__) || \ + defined(__ILEC400__) + /* This compiler is believed to have an ISO compatible preprocessor */ +#define CURL_ISOCPP +#else + /* This compiler is believed NOT to have an ISO compatible preprocessor */ +#undef CURL_ISOCPP +#endif + +/* + * Macros for minimum-width signed and unsigned curl_off_t integer constants. + */ + +#if defined(__BORLANDC__) && (__BORLANDC__ == 0x0551) +# define __CURL_OFF_T_C_HLPR2(x) x +# define __CURL_OFF_T_C_HLPR1(x) __CURL_OFF_T_C_HLPR2(x) +# define CURL_OFF_T_C(Val) __CURL_OFF_T_C_HLPR1(Val) ## \ + __CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_T) +# define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val) ## \ + __CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_TU) +#else +# ifdef CURL_ISOCPP +# define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val ## Suffix +# else +# define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val/**/Suffix +# endif +# define __CURL_OFF_T_C_HLPR1(Val,Suffix) __CURL_OFF_T_C_HLPR2(Val,Suffix) +# define CURL_OFF_T_C(Val) __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_T) +# define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_TU) +#endif + +/* + * Get rid of macros private to this header file. + */ + +#undef CurlchkszEQ +#undef CurlchkszGE + +/* + * Get rid of macros not intended to exist beyond this point. + */ + +#undef CURL_PULL_WS2TCPIP_H +#undef CURL_PULL_SYS_TYPES_H +#undef CURL_PULL_SYS_SOCKET_H +#undef CURL_PULL_SYS_POLL_H +#undef CURL_PULL_STDINT_H +#undef CURL_PULL_INTTYPES_H + +#undef CURL_TYPEOF_CURL_SOCKLEN_T +#undef CURL_TYPEOF_CURL_OFF_T + +#ifdef CURL_NO_OLDIES +#undef CURL_FORMAT_OFF_T /* not required since 7.19.0 - obsoleted in 7.20.0 */ +#endif + +#endif /* __CURL_CURLRULES_H */ diff --git a/ext/curl/include/curl/curlver.h b/ext/curl/include/curl/curlver.h new file mode 100644 index 0000000000..7976c1fcf1 --- /dev/null +++ b/ext/curl/include/curl/curlver.h @@ -0,0 +1,69 @@ +#ifndef __CURL_CURLVER_H +#define __CURL_CURLVER_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2015, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* This header file contains nothing but libcurl version info, generated by + a script at release-time. This was made its own header file in 7.11.2 */ + +/* This is the global package copyright */ +#define LIBCURL_COPYRIGHT "1996 - 2015 Daniel Stenberg, ." + +/* This is the version number of the libcurl package from which this header + file origins: */ +#define LIBCURL_VERSION "7.42.1" + +/* The numeric version number is also available "in parts" by using these + defines: */ +#define LIBCURL_VERSION_MAJOR 7 +#define LIBCURL_VERSION_MINOR 42 +#define LIBCURL_VERSION_PATCH 1 + +/* This is the numeric version of the libcurl version number, meant for easier + parsing and comparions by programs. The LIBCURL_VERSION_NUM define will + always follow this syntax: + + 0xXXYYZZ + + Where XX, YY and ZZ are the main version, release and patch numbers in + hexadecimal (using 8 bits each). All three numbers are always represented + using two digits. 1.2 would appear as "0x010200" while version 9.11.7 + appears as "0x090b07". + + This 6-digit (24 bits) hexadecimal number does not show pre-release number, + and it is always a greater number in a more recent release. It makes + comparisons with greater than and less than work. +*/ +#define LIBCURL_VERSION_NUM 0x072a01 + +/* + * This is the date and time when the full source package was created. The + * timestamp is not stored in git, as the timestamp is properly set in the + * tarballs by the maketgz script. + * + * The format of the date should follow this template: + * + * "Mon Feb 12 11:35:33 UTC 2007" + */ +#define LIBCURL_TIMESTAMP "Wed Apr 29 06:07:13 UTC 2015" + +#endif /* __CURL_CURLVER_H */ diff --git a/ext/curl/include/curl/easy.h b/ext/curl/include/curl/easy.h new file mode 100644 index 0000000000..c1e3e76096 --- /dev/null +++ b/ext/curl/include/curl/easy.h @@ -0,0 +1,102 @@ +#ifndef __CURL_EASY_H +#define __CURL_EASY_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +CURL_EXTERN CURL *curl_easy_init(void); +CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...); +CURL_EXTERN CURLcode curl_easy_perform(CURL *curl); +CURL_EXTERN void curl_easy_cleanup(CURL *curl); + +/* + * NAME curl_easy_getinfo() + * + * DESCRIPTION + * + * Request internal information from the curl session with this function. The + * third argument MUST be a pointer to a long, a pointer to a char * or a + * pointer to a double (as the documentation describes elsewhere). The data + * pointed to will be filled in accordingly and can be relied upon only if the + * function returns CURLE_OK. This function is intended to get used *AFTER* a + * performed transfer, all results from this function are undefined until the + * transfer is completed. + */ +CURL_EXTERN CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...); + + +/* + * NAME curl_easy_duphandle() + * + * DESCRIPTION + * + * Creates a new curl session handle with the same options set for the handle + * passed in. Duplicating a handle could only be a matter of cloning data and + * options, internal state info and things like persistent connections cannot + * be transferred. It is useful in multithreaded applications when you can run + * curl_easy_duphandle() for each new thread to avoid a series of identical + * curl_easy_setopt() invokes in every thread. + */ +CURL_EXTERN CURL* curl_easy_duphandle(CURL *curl); + +/* + * NAME curl_easy_reset() + * + * DESCRIPTION + * + * Re-initializes a CURL handle to the default values. This puts back the + * handle to the same state as it was in when it was just created. + * + * It does keep: live connections, the Session ID cache, the DNS cache and the + * cookies. + */ +CURL_EXTERN void curl_easy_reset(CURL *curl); + +/* + * NAME curl_easy_recv() + * + * DESCRIPTION + * + * Receives data from the connected socket. Use after successful + * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. + */ +CURL_EXTERN CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, + size_t *n); + +/* + * NAME curl_easy_send() + * + * DESCRIPTION + * + * Sends data over the connected socket. Use after successful + * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. + */ +CURL_EXTERN CURLcode curl_easy_send(CURL *curl, const void *buffer, + size_t buflen, size_t *n); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ext/curl/include/curl/mprintf.h b/ext/curl/include/curl/mprintf.h new file mode 100644 index 0000000000..c6b0d7679a --- /dev/null +++ b/ext/curl/include/curl/mprintf.h @@ -0,0 +1,74 @@ +#ifndef __CURL_MPRINTF_H +#define __CURL_MPRINTF_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2015, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include +#include /* needed for FILE */ + +#include "curl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +CURL_EXTERN int curl_mprintf(const char *format, ...); +CURL_EXTERN int curl_mfprintf(FILE *fd, const char *format, ...); +CURL_EXTERN int curl_msprintf(char *buffer, const char *format, ...); +CURL_EXTERN int curl_msnprintf(char *buffer, size_t maxlength, + const char *format, ...); +CURL_EXTERN int curl_mvprintf(const char *format, va_list args); +CURL_EXTERN int curl_mvfprintf(FILE *fd, const char *format, va_list args); +CURL_EXTERN int curl_mvsprintf(char *buffer, const char *format, va_list args); +CURL_EXTERN int curl_mvsnprintf(char *buffer, size_t maxlength, + const char *format, va_list args); +CURL_EXTERN char *curl_maprintf(const char *format, ...); +CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args); + +#ifdef _MPRINTF_REPLACE +# undef printf +# undef fprintf +# undef sprintf +# undef vsprintf +# undef snprintf +# undef vprintf +# undef vfprintf +# undef vsnprintf +# undef aprintf +# undef vaprintf +# define printf curl_mprintf +# define fprintf curl_mfprintf +# define sprintf curl_msprintf +# define vsprintf curl_mvsprintf +# define snprintf curl_msnprintf +# define vprintf curl_mvprintf +# define vfprintf curl_mvfprintf +# define vsnprintf curl_mvsnprintf +# define aprintf curl_maprintf +# define vaprintf curl_mvaprintf +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __CURL_MPRINTF_H */ diff --git a/ext/curl/include/curl/multi.h b/ext/curl/include/curl/multi.h new file mode 100644 index 0000000000..3c4acb0f6e --- /dev/null +++ b/ext/curl/include/curl/multi.h @@ -0,0 +1,399 @@ +#ifndef __CURL_MULTI_H +#define __CURL_MULTI_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2013, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +/* + This is an "external" header file. Don't give away any internals here! + + GOALS + + o Enable a "pull" interface. The application that uses libcurl decides where + and when to ask libcurl to get/send data. + + o Enable multiple simultaneous transfers in the same thread without making it + complicated for the application. + + o Enable the application to select() on its own file descriptors and curl's + file descriptors simultaneous easily. + +*/ + +/* + * This header file should not really need to include "curl.h" since curl.h + * itself includes this file and we expect user applications to do #include + * without the need for especially including multi.h. + * + * For some reason we added this include here at one point, and rather than to + * break existing (wrongly written) libcurl applications, we leave it as-is + * but with this warning attached. + */ +#include "curl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void CURLM; + +typedef enum { + CURLM_CALL_MULTI_PERFORM = -1, /* please call curl_multi_perform() or + curl_multi_socket*() soon */ + CURLM_OK, + CURLM_BAD_HANDLE, /* the passed-in handle is not a valid CURLM handle */ + CURLM_BAD_EASY_HANDLE, /* an easy handle was not good/valid */ + CURLM_OUT_OF_MEMORY, /* if you ever get this, you're in deep sh*t */ + CURLM_INTERNAL_ERROR, /* this is a libcurl bug */ + CURLM_BAD_SOCKET, /* the passed in socket argument did not match */ + CURLM_UNKNOWN_OPTION, /* curl_multi_setopt() with unsupported option */ + CURLM_ADDED_ALREADY, /* an easy handle already added to a multi handle was + attempted to get added - again */ + CURLM_LAST +} CURLMcode; + +/* just to make code nicer when using curl_multi_socket() you can now check + for CURLM_CALL_MULTI_SOCKET too in the same style it works for + curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */ +#define CURLM_CALL_MULTI_SOCKET CURLM_CALL_MULTI_PERFORM + +typedef enum { + CURLMSG_NONE, /* first, not used */ + CURLMSG_DONE, /* This easy handle has completed. 'result' contains + the CURLcode of the transfer */ + CURLMSG_LAST /* last, not used */ +} CURLMSG; + +struct CURLMsg { + CURLMSG msg; /* what this message means */ + CURL *easy_handle; /* the handle it concerns */ + union { + void *whatever; /* message-specific data */ + CURLcode result; /* return code for transfer */ + } data; +}; +typedef struct CURLMsg CURLMsg; + +/* Based on poll(2) structure and values. + * We don't use pollfd and POLL* constants explicitly + * to cover platforms without poll(). */ +#define CURL_WAIT_POLLIN 0x0001 +#define CURL_WAIT_POLLPRI 0x0002 +#define CURL_WAIT_POLLOUT 0x0004 + +struct curl_waitfd { + curl_socket_t fd; + short events; + short revents; /* not supported yet */ +}; + +/* + * Name: curl_multi_init() + * + * Desc: inititalize multi-style curl usage + * + * Returns: a new CURLM handle to use in all 'curl_multi' functions. + */ +CURL_EXTERN CURLM *curl_multi_init(void); + +/* + * Name: curl_multi_add_handle() + * + * Desc: add a standard curl handle to the multi stack + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *multi_handle, + CURL *curl_handle); + + /* + * Name: curl_multi_remove_handle() + * + * Desc: removes a curl handle from the multi stack again + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *multi_handle, + CURL *curl_handle); + + /* + * Name: curl_multi_fdset() + * + * Desc: Ask curl for its fd_set sets. The app can use these to select() or + * poll() on. We want curl_multi_perform() called as soon as one of + * them are ready. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *exc_fd_set, + int *max_fd); + +/* + * Name: curl_multi_wait() + * + * Desc: Poll on all fds within a CURLM set as well as any + * additional fds passed to the function. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle, + struct curl_waitfd extra_fds[], + unsigned int extra_nfds, + int timeout_ms, + int *ret); + + /* + * Name: curl_multi_perform() + * + * Desc: When the app thinks there's data available for curl it calls this + * function to read/write whatever there is right now. This returns + * as soon as the reads and writes are done. This function does not + * require that there actually is data available for reading or that + * data can be written, it can be called just in case. It returns + * the number of handles that still transfer data in the second + * argument's integer-pointer. + * + * Returns: CURLMcode type, general multi error code. *NOTE* that this only + * returns errors etc regarding the whole multi stack. There might + * still have occurred problems on invidual transfers even when this + * returns OK. + */ +CURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle, + int *running_handles); + + /* + * Name: curl_multi_cleanup() + * + * Desc: Cleans up and removes a whole multi stack. It does not free or + * touch any individual easy handles in any way. We need to define + * in what state those handles will be if this function is called + * in the middle of a transfer. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle); + +/* + * Name: curl_multi_info_read() + * + * Desc: Ask the multi handle if there's any messages/informationals from + * the individual transfers. Messages include informationals such as + * error code from the transfer or just the fact that a transfer is + * completed. More details on these should be written down as well. + * + * Repeated calls to this function will return a new struct each + * time, until a special "end of msgs" struct is returned as a signal + * that there is no more to get at this point. + * + * The data the returned pointer points to will not survive calling + * curl_multi_cleanup(). + * + * The 'CURLMsg' struct is meant to be very simple and only contain + * very basic informations. If more involved information is wanted, + * we will provide the particular "transfer handle" in that struct + * and that should/could/would be used in subsequent + * curl_easy_getinfo() calls (or similar). The point being that we + * must never expose complex structs to applications, as then we'll + * undoubtably get backwards compatibility problems in the future. + * + * Returns: A pointer to a filled-in struct, or NULL if it failed or ran out + * of structs. It also writes the number of messages left in the + * queue (after this read) in the integer the second argument points + * to. + */ +CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle, + int *msgs_in_queue); + +/* + * Name: curl_multi_strerror() + * + * Desc: The curl_multi_strerror function may be used to turn a CURLMcode + * value into the equivalent human readable error string. This is + * useful for printing meaningful error messages. + * + * Returns: A pointer to a zero-terminated error message. + */ +CURL_EXTERN const char *curl_multi_strerror(CURLMcode); + +/* + * Name: curl_multi_socket() and + * curl_multi_socket_all() + * + * Desc: An alternative version of curl_multi_perform() that allows the + * application to pass in one of the file descriptors that have been + * detected to have "action" on them and let libcurl perform. + * See man page for details. + */ +#define CURL_POLL_NONE 0 +#define CURL_POLL_IN 1 +#define CURL_POLL_OUT 2 +#define CURL_POLL_INOUT 3 +#define CURL_POLL_REMOVE 4 + +#define CURL_SOCKET_TIMEOUT CURL_SOCKET_BAD + +#define CURL_CSELECT_IN 0x01 +#define CURL_CSELECT_OUT 0x02 +#define CURL_CSELECT_ERR 0x04 + +typedef int (*curl_socket_callback)(CURL *easy, /* easy handle */ + curl_socket_t s, /* socket */ + int what, /* see above */ + void *userp, /* private callback + pointer */ + void *socketp); /* private socket + pointer */ +/* + * Name: curl_multi_timer_callback + * + * Desc: Called by libcurl whenever the library detects a change in the + * maximum number of milliseconds the app is allowed to wait before + * curl_multi_socket() or curl_multi_perform() must be called + * (to allow libcurl's timed events to take place). + * + * Returns: The callback should return zero. + */ +typedef int (*curl_multi_timer_callback)(CURLM *multi, /* multi handle */ + long timeout_ms, /* see above */ + void *userp); /* private callback + pointer */ + +CURL_EXTERN CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s, + int *running_handles); + +CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *multi_handle, + curl_socket_t s, + int ev_bitmask, + int *running_handles); + +CURL_EXTERN CURLMcode curl_multi_socket_all(CURLM *multi_handle, + int *running_handles); + +#ifndef CURL_ALLOW_OLD_MULTI_SOCKET +/* This macro below was added in 7.16.3 to push users who recompile to use + the new curl_multi_socket_action() instead of the old curl_multi_socket() +*/ +#define curl_multi_socket(x,y,z) curl_multi_socket_action(x,y,0,z) +#endif + +/* + * Name: curl_multi_timeout() + * + * Desc: Returns the maximum number of milliseconds the app is allowed to + * wait before curl_multi_socket() or curl_multi_perform() must be + * called (to allow libcurl's timed events to take place). + * + * Returns: CURLM error code. + */ +CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *multi_handle, + long *milliseconds); + +#undef CINIT /* re-using the same name as in curl.h */ + +#ifdef CURL_ISOCPP +#define CINIT(name,type,num) CURLMOPT_ ## name = CURLOPTTYPE_ ## type + num +#else +/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ +#define LONG CURLOPTTYPE_LONG +#define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT +#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT +#define OFF_T CURLOPTTYPE_OFF_T +#define CINIT(name,type,number) CURLMOPT_/**/name = type + number +#endif + +typedef enum { + /* This is the socket callback function pointer */ + CINIT(SOCKETFUNCTION, FUNCTIONPOINT, 1), + + /* This is the argument passed to the socket callback */ + CINIT(SOCKETDATA, OBJECTPOINT, 2), + + /* set to 1 to enable pipelining for this multi handle */ + CINIT(PIPELINING, LONG, 3), + + /* This is the timer callback function pointer */ + CINIT(TIMERFUNCTION, FUNCTIONPOINT, 4), + + /* This is the argument passed to the timer callback */ + CINIT(TIMERDATA, OBJECTPOINT, 5), + + /* maximum number of entries in the connection cache */ + CINIT(MAXCONNECTS, LONG, 6), + + /* maximum number of (pipelining) connections to one host */ + CINIT(MAX_HOST_CONNECTIONS, LONG, 7), + + /* maximum number of requests in a pipeline */ + CINIT(MAX_PIPELINE_LENGTH, LONG, 8), + + /* a connection with a content-length longer than this + will not be considered for pipelining */ + CINIT(CONTENT_LENGTH_PENALTY_SIZE, OFF_T, 9), + + /* a connection with a chunk length longer than this + will not be considered for pipelining */ + CINIT(CHUNK_LENGTH_PENALTY_SIZE, OFF_T, 10), + + /* a list of site names(+port) that are blacklisted from + pipelining */ + CINIT(PIPELINING_SITE_BL, OBJECTPOINT, 11), + + /* a list of server types that are blacklisted from + pipelining */ + CINIT(PIPELINING_SERVER_BL, OBJECTPOINT, 12), + + /* maximum number of open connections in total */ + CINIT(MAX_TOTAL_CONNECTIONS, LONG, 13), + + CURLMOPT_LASTENTRY /* the last unused */ +} CURLMoption; + + +/* + * Name: curl_multi_setopt() + * + * Desc: Sets options for the multi handle. + * + * Returns: CURLM error code. + */ +CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle, + CURLMoption option, ...); + + +/* + * Name: curl_multi_assign() + * + * Desc: This function sets an association in the multi handle between the + * given socket and a private pointer of the application. This is + * (only) useful for curl_multi_socket uses. + * + * Returns: CURLM error code. + */ +CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle, + curl_socket_t sockfd, void *sockp); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif diff --git a/ext/curl/include/curl/stdcheaders.h b/ext/curl/include/curl/stdcheaders.h new file mode 100644 index 0000000000..ad82ef6335 --- /dev/null +++ b/ext/curl/include/curl/stdcheaders.h @@ -0,0 +1,33 @@ +#ifndef __STDC_HEADERS_H +#define __STDC_HEADERS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include + +size_t fread (void *, size_t, size_t, FILE *); +size_t fwrite (const void *, size_t, size_t, FILE *); + +int strcasecmp(const char *, const char *); +int strncasecmp(const char *, const char *, size_t); + +#endif /* __STDC_HEADERS_H */ diff --git a/ext/curl/include/curl/typecheck-gcc.h b/ext/curl/include/curl/typecheck-gcc.h new file mode 100644 index 0000000000..69d41a20d1 --- /dev/null +++ b/ext/curl/include/curl/typecheck-gcc.h @@ -0,0 +1,610 @@ +#ifndef __CURL_TYPECHECK_GCC_H +#define __CURL_TYPECHECK_GCC_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2014, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* wraps curl_easy_setopt() with typechecking */ + +/* To add a new kind of warning, add an + * if(_curl_is_sometype_option(_curl_opt)) + * if(!_curl_is_sometype(value)) + * _curl_easy_setopt_err_sometype(); + * block and define _curl_is_sometype_option, _curl_is_sometype and + * _curl_easy_setopt_err_sometype below + * + * NOTE: We use two nested 'if' statements here instead of the && operator, in + * order to work around gcc bug #32061. It affects only gcc 4.3.x/4.4.x + * when compiling with -Wlogical-op. + * + * To add an option that uses the same type as an existing option, you'll just + * need to extend the appropriate _curl_*_option macro + */ +#define curl_easy_setopt(handle, option, value) \ +__extension__ ({ \ + __typeof__ (option) _curl_opt = option; \ + if(__builtin_constant_p(_curl_opt)) { \ + if(_curl_is_long_option(_curl_opt)) \ + if(!_curl_is_long(value)) \ + _curl_easy_setopt_err_long(); \ + if(_curl_is_off_t_option(_curl_opt)) \ + if(!_curl_is_off_t(value)) \ + _curl_easy_setopt_err_curl_off_t(); \ + if(_curl_is_string_option(_curl_opt)) \ + if(!_curl_is_string(value)) \ + _curl_easy_setopt_err_string(); \ + if(_curl_is_write_cb_option(_curl_opt)) \ + if(!_curl_is_write_cb(value)) \ + _curl_easy_setopt_err_write_callback(); \ + if((_curl_opt) == CURLOPT_READFUNCTION) \ + if(!_curl_is_read_cb(value)) \ + _curl_easy_setopt_err_read_cb(); \ + if((_curl_opt) == CURLOPT_IOCTLFUNCTION) \ + if(!_curl_is_ioctl_cb(value)) \ + _curl_easy_setopt_err_ioctl_cb(); \ + if((_curl_opt) == CURLOPT_SOCKOPTFUNCTION) \ + if(!_curl_is_sockopt_cb(value)) \ + _curl_easy_setopt_err_sockopt_cb(); \ + if((_curl_opt) == CURLOPT_OPENSOCKETFUNCTION) \ + if(!_curl_is_opensocket_cb(value)) \ + _curl_easy_setopt_err_opensocket_cb(); \ + if((_curl_opt) == CURLOPT_PROGRESSFUNCTION) \ + if(!_curl_is_progress_cb(value)) \ + _curl_easy_setopt_err_progress_cb(); \ + if((_curl_opt) == CURLOPT_DEBUGFUNCTION) \ + if(!_curl_is_debug_cb(value)) \ + _curl_easy_setopt_err_debug_cb(); \ + if((_curl_opt) == CURLOPT_SSL_CTX_FUNCTION) \ + if(!_curl_is_ssl_ctx_cb(value)) \ + _curl_easy_setopt_err_ssl_ctx_cb(); \ + if(_curl_is_conv_cb_option(_curl_opt)) \ + if(!_curl_is_conv_cb(value)) \ + _curl_easy_setopt_err_conv_cb(); \ + if((_curl_opt) == CURLOPT_SEEKFUNCTION) \ + if(!_curl_is_seek_cb(value)) \ + _curl_easy_setopt_err_seek_cb(); \ + if(_curl_is_cb_data_option(_curl_opt)) \ + if(!_curl_is_cb_data(value)) \ + _curl_easy_setopt_err_cb_data(); \ + if((_curl_opt) == CURLOPT_ERRORBUFFER) \ + if(!_curl_is_error_buffer(value)) \ + _curl_easy_setopt_err_error_buffer(); \ + if((_curl_opt) == CURLOPT_STDERR) \ + if(!_curl_is_FILE(value)) \ + _curl_easy_setopt_err_FILE(); \ + if(_curl_is_postfields_option(_curl_opt)) \ + if(!_curl_is_postfields(value)) \ + _curl_easy_setopt_err_postfields(); \ + if((_curl_opt) == CURLOPT_HTTPPOST) \ + if(!_curl_is_arr((value), struct curl_httppost)) \ + _curl_easy_setopt_err_curl_httpost(); \ + if(_curl_is_slist_option(_curl_opt)) \ + if(!_curl_is_arr((value), struct curl_slist)) \ + _curl_easy_setopt_err_curl_slist(); \ + if((_curl_opt) == CURLOPT_SHARE) \ + if(!_curl_is_ptr((value), CURLSH)) \ + _curl_easy_setopt_err_CURLSH(); \ + } \ + curl_easy_setopt(handle, _curl_opt, value); \ +}) + +/* wraps curl_easy_getinfo() with typechecking */ +/* FIXME: don't allow const pointers */ +#define curl_easy_getinfo(handle, info, arg) \ +__extension__ ({ \ + __typeof__ (info) _curl_info = info; \ + if(__builtin_constant_p(_curl_info)) { \ + if(_curl_is_string_info(_curl_info)) \ + if(!_curl_is_arr((arg), char *)) \ + _curl_easy_getinfo_err_string(); \ + if(_curl_is_long_info(_curl_info)) \ + if(!_curl_is_arr((arg), long)) \ + _curl_easy_getinfo_err_long(); \ + if(_curl_is_double_info(_curl_info)) \ + if(!_curl_is_arr((arg), double)) \ + _curl_easy_getinfo_err_double(); \ + if(_curl_is_slist_info(_curl_info)) \ + if(!_curl_is_arr((arg), struct curl_slist *)) \ + _curl_easy_getinfo_err_curl_slist(); \ + } \ + curl_easy_getinfo(handle, _curl_info, arg); \ +}) + +/* TODO: typechecking for curl_share_setopt() and curl_multi_setopt(), + * for now just make sure that the functions are called with three + * arguments + */ +#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param) +#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param) + + +/* the actual warnings, triggered by calling the _curl_easy_setopt_err* + * functions */ + +/* To define a new warning, use _CURL_WARNING(identifier, "message") */ +#define _CURL_WARNING(id, message) \ + static void __attribute__((__warning__(message))) \ + __attribute__((__unused__)) __attribute__((__noinline__)) \ + id(void) { __asm__(""); } + +_CURL_WARNING(_curl_easy_setopt_err_long, + "curl_easy_setopt expects a long argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_curl_off_t, + "curl_easy_setopt expects a curl_off_t argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_string, + "curl_easy_setopt expects a " + "string (char* or char[]) argument for this option" + ) +_CURL_WARNING(_curl_easy_setopt_err_write_callback, + "curl_easy_setopt expects a curl_write_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_read_cb, + "curl_easy_setopt expects a curl_read_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_ioctl_cb, + "curl_easy_setopt expects a curl_ioctl_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_sockopt_cb, + "curl_easy_setopt expects a curl_sockopt_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_opensocket_cb, + "curl_easy_setopt expects a " + "curl_opensocket_callback argument for this option" + ) +_CURL_WARNING(_curl_easy_setopt_err_progress_cb, + "curl_easy_setopt expects a curl_progress_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_debug_cb, + "curl_easy_setopt expects a curl_debug_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_ssl_ctx_cb, + "curl_easy_setopt expects a curl_ssl_ctx_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_conv_cb, + "curl_easy_setopt expects a curl_conv_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_seek_cb, + "curl_easy_setopt expects a curl_seek_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_cb_data, + "curl_easy_setopt expects a " + "private data pointer as argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_error_buffer, + "curl_easy_setopt expects a " + "char buffer of CURL_ERROR_SIZE as argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_FILE, + "curl_easy_setopt expects a FILE* argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_postfields, + "curl_easy_setopt expects a void* or char* argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_curl_httpost, + "curl_easy_setopt expects a struct curl_httppost* argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_curl_slist, + "curl_easy_setopt expects a struct curl_slist* argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_CURLSH, + "curl_easy_setopt expects a CURLSH* argument for this option") + +_CURL_WARNING(_curl_easy_getinfo_err_string, + "curl_easy_getinfo expects a pointer to char * for this info") +_CURL_WARNING(_curl_easy_getinfo_err_long, + "curl_easy_getinfo expects a pointer to long for this info") +_CURL_WARNING(_curl_easy_getinfo_err_double, + "curl_easy_getinfo expects a pointer to double for this info") +_CURL_WARNING(_curl_easy_getinfo_err_curl_slist, + "curl_easy_getinfo expects a pointer to struct curl_slist * for this info") + +/* groups of curl_easy_setops options that take the same type of argument */ + +/* To add a new option to one of the groups, just add + * (option) == CURLOPT_SOMETHING + * to the or-expression. If the option takes a long or curl_off_t, you don't + * have to do anything + */ + +/* evaluates to true if option takes a long argument */ +#define _curl_is_long_option(option) \ + (0 < (option) && (option) < CURLOPTTYPE_OBJECTPOINT) + +#define _curl_is_off_t_option(option) \ + ((option) > CURLOPTTYPE_OFF_T) + +/* evaluates to true if option takes a char* argument */ +#define _curl_is_string_option(option) \ + ((option) == CURLOPT_URL || \ + (option) == CURLOPT_PROXY || \ + (option) == CURLOPT_INTERFACE || \ + (option) == CURLOPT_NETRC_FILE || \ + (option) == CURLOPT_USERPWD || \ + (option) == CURLOPT_USERNAME || \ + (option) == CURLOPT_PASSWORD || \ + (option) == CURLOPT_PROXYUSERPWD || \ + (option) == CURLOPT_PROXYUSERNAME || \ + (option) == CURLOPT_PROXYPASSWORD || \ + (option) == CURLOPT_NOPROXY || \ + (option) == CURLOPT_ACCEPT_ENCODING || \ + (option) == CURLOPT_REFERER || \ + (option) == CURLOPT_USERAGENT || \ + (option) == CURLOPT_COOKIE || \ + (option) == CURLOPT_COOKIEFILE || \ + (option) == CURLOPT_COOKIEJAR || \ + (option) == CURLOPT_COOKIELIST || \ + (option) == CURLOPT_FTPPORT || \ + (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER || \ + (option) == CURLOPT_FTP_ACCOUNT || \ + (option) == CURLOPT_RANGE || \ + (option) == CURLOPT_CUSTOMREQUEST || \ + (option) == CURLOPT_SSLCERT || \ + (option) == CURLOPT_SSLCERTTYPE || \ + (option) == CURLOPT_SSLKEY || \ + (option) == CURLOPT_SSLKEYTYPE || \ + (option) == CURLOPT_KEYPASSWD || \ + (option) == CURLOPT_SSLENGINE || \ + (option) == CURLOPT_CAINFO || \ + (option) == CURLOPT_CAPATH || \ + (option) == CURLOPT_RANDOM_FILE || \ + (option) == CURLOPT_EGDSOCKET || \ + (option) == CURLOPT_SSL_CIPHER_LIST || \ + (option) == CURLOPT_KRBLEVEL || \ + (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 || \ + (option) == CURLOPT_SSH_PUBLIC_KEYFILE || \ + (option) == CURLOPT_SSH_PRIVATE_KEYFILE || \ + (option) == CURLOPT_CRLFILE || \ + (option) == CURLOPT_ISSUERCERT || \ + (option) == CURLOPT_SOCKS5_GSSAPI_SERVICE || \ + (option) == CURLOPT_SSH_KNOWNHOSTS || \ + (option) == CURLOPT_MAIL_FROM || \ + (option) == CURLOPT_RTSP_SESSION_ID || \ + (option) == CURLOPT_RTSP_STREAM_URI || \ + (option) == CURLOPT_RTSP_TRANSPORT || \ + (option) == CURLOPT_XOAUTH2_BEARER || \ + (option) == CURLOPT_DNS_SERVERS || \ + (option) == CURLOPT_DNS_INTERFACE || \ + (option) == CURLOPT_DNS_LOCAL_IP4 || \ + (option) == CURLOPT_DNS_LOCAL_IP6 || \ + (option) == CURLOPT_LOGIN_OPTIONS || \ + 0) + +/* evaluates to true if option takes a curl_write_callback argument */ +#define _curl_is_write_cb_option(option) \ + ((option) == CURLOPT_HEADERFUNCTION || \ + (option) == CURLOPT_WRITEFUNCTION) + +/* evaluates to true if option takes a curl_conv_callback argument */ +#define _curl_is_conv_cb_option(option) \ + ((option) == CURLOPT_CONV_TO_NETWORK_FUNCTION || \ + (option) == CURLOPT_CONV_FROM_NETWORK_FUNCTION || \ + (option) == CURLOPT_CONV_FROM_UTF8_FUNCTION) + +/* evaluates to true if option takes a data argument to pass to a callback */ +#define _curl_is_cb_data_option(option) \ + ((option) == CURLOPT_WRITEDATA || \ + (option) == CURLOPT_READDATA || \ + (option) == CURLOPT_IOCTLDATA || \ + (option) == CURLOPT_SOCKOPTDATA || \ + (option) == CURLOPT_OPENSOCKETDATA || \ + (option) == CURLOPT_PROGRESSDATA || \ + (option) == CURLOPT_HEADERDATA || \ + (option) == CURLOPT_DEBUGDATA || \ + (option) == CURLOPT_SSL_CTX_DATA || \ + (option) == CURLOPT_SEEKDATA || \ + (option) == CURLOPT_PRIVATE || \ + (option) == CURLOPT_SSH_KEYDATA || \ + (option) == CURLOPT_INTERLEAVEDATA || \ + (option) == CURLOPT_CHUNK_DATA || \ + (option) == CURLOPT_FNMATCH_DATA || \ + 0) + +/* evaluates to true if option takes a POST data argument (void* or char*) */ +#define _curl_is_postfields_option(option) \ + ((option) == CURLOPT_POSTFIELDS || \ + (option) == CURLOPT_COPYPOSTFIELDS || \ + 0) + +/* evaluates to true if option takes a struct curl_slist * argument */ +#define _curl_is_slist_option(option) \ + ((option) == CURLOPT_HTTPHEADER || \ + (option) == CURLOPT_HTTP200ALIASES || \ + (option) == CURLOPT_QUOTE || \ + (option) == CURLOPT_POSTQUOTE || \ + (option) == CURLOPT_PREQUOTE || \ + (option) == CURLOPT_TELNETOPTIONS || \ + (option) == CURLOPT_MAIL_RCPT || \ + 0) + +/* groups of curl_easy_getinfo infos that take the same type of argument */ + +/* evaluates to true if info expects a pointer to char * argument */ +#define _curl_is_string_info(info) \ + (CURLINFO_STRING < (info) && (info) < CURLINFO_LONG) + +/* evaluates to true if info expects a pointer to long argument */ +#define _curl_is_long_info(info) \ + (CURLINFO_LONG < (info) && (info) < CURLINFO_DOUBLE) + +/* evaluates to true if info expects a pointer to double argument */ +#define _curl_is_double_info(info) \ + (CURLINFO_DOUBLE < (info) && (info) < CURLINFO_SLIST) + +/* true if info expects a pointer to struct curl_slist * argument */ +#define _curl_is_slist_info(info) \ + (CURLINFO_SLIST < (info)) + + +/* typecheck helpers -- check whether given expression has requested type*/ + +/* For pointers, you can use the _curl_is_ptr/_curl_is_arr macros, + * otherwise define a new macro. Search for __builtin_types_compatible_p + * in the GCC manual. + * NOTE: these macros MUST NOT EVALUATE their arguments! The argument is + * the actual expression passed to the curl_easy_setopt macro. This + * means that you can only apply the sizeof and __typeof__ operators, no + * == or whatsoever. + */ + +/* XXX: should evaluate to true iff expr is a pointer */ +#define _curl_is_any_ptr(expr) \ + (sizeof(expr) == sizeof(void*)) + +/* evaluates to true if expr is NULL */ +/* XXX: must not evaluate expr, so this check is not accurate */ +#define _curl_is_NULL(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), __typeof__(NULL))) + +/* evaluates to true if expr is type*, const type* or NULL */ +#define _curl_is_ptr(expr, type) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), type *) || \ + __builtin_types_compatible_p(__typeof__(expr), const type *)) + +/* evaluates to true if expr is one of type[], type*, NULL or const type* */ +#define _curl_is_arr(expr, type) \ + (_curl_is_ptr((expr), type) || \ + __builtin_types_compatible_p(__typeof__(expr), type [])) + +/* evaluates to true if expr is a string */ +#define _curl_is_string(expr) \ + (_curl_is_arr((expr), char) || \ + _curl_is_arr((expr), signed char) || \ + _curl_is_arr((expr), unsigned char)) + +/* evaluates to true if expr is a long (no matter the signedness) + * XXX: for now, int is also accepted (and therefore short and char, which + * are promoted to int when passed to a variadic function) */ +#define _curl_is_long(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), long) || \ + __builtin_types_compatible_p(__typeof__(expr), signed long) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned long) || \ + __builtin_types_compatible_p(__typeof__(expr), int) || \ + __builtin_types_compatible_p(__typeof__(expr), signed int) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned int) || \ + __builtin_types_compatible_p(__typeof__(expr), short) || \ + __builtin_types_compatible_p(__typeof__(expr), signed short) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned short) || \ + __builtin_types_compatible_p(__typeof__(expr), char) || \ + __builtin_types_compatible_p(__typeof__(expr), signed char) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned char)) + +/* evaluates to true if expr is of type curl_off_t */ +#define _curl_is_off_t(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), curl_off_t)) + +/* evaluates to true if expr is abuffer suitable for CURLOPT_ERRORBUFFER */ +/* XXX: also check size of an char[] array? */ +#define _curl_is_error_buffer(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), char *) || \ + __builtin_types_compatible_p(__typeof__(expr), char[])) + +/* evaluates to true if expr is of type (const) void* or (const) FILE* */ +#if 0 +#define _curl_is_cb_data(expr) \ + (_curl_is_ptr((expr), void) || \ + _curl_is_ptr((expr), FILE)) +#else /* be less strict */ +#define _curl_is_cb_data(expr) \ + _curl_is_any_ptr(expr) +#endif + +/* evaluates to true if expr is of type FILE* */ +#define _curl_is_FILE(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), FILE *)) + +/* evaluates to true if expr can be passed as POST data (void* or char*) */ +#define _curl_is_postfields(expr) \ + (_curl_is_ptr((expr), void) || \ + _curl_is_arr((expr), char)) + +/* FIXME: the whole callback checking is messy... + * The idea is to tolerate char vs. void and const vs. not const + * pointers in arguments at least + */ +/* helper: __builtin_types_compatible_p distinguishes between functions and + * function pointers, hide it */ +#define _curl_callback_compatible(func, type) \ + (__builtin_types_compatible_p(__typeof__(func), type) || \ + __builtin_types_compatible_p(__typeof__(func), type*)) + +/* evaluates to true if expr is of type curl_read_callback or "similar" */ +#define _curl_is_read_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), __typeof__(fread)) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_read_callback) || \ + _curl_callback_compatible((expr), _curl_read_callback1) || \ + _curl_callback_compatible((expr), _curl_read_callback2) || \ + _curl_callback_compatible((expr), _curl_read_callback3) || \ + _curl_callback_compatible((expr), _curl_read_callback4) || \ + _curl_callback_compatible((expr), _curl_read_callback5) || \ + _curl_callback_compatible((expr), _curl_read_callback6)) +typedef size_t (_curl_read_callback1)(char *, size_t, size_t, void*); +typedef size_t (_curl_read_callback2)(char *, size_t, size_t, const void*); +typedef size_t (_curl_read_callback3)(char *, size_t, size_t, FILE*); +typedef size_t (_curl_read_callback4)(void *, size_t, size_t, void*); +typedef size_t (_curl_read_callback5)(void *, size_t, size_t, const void*); +typedef size_t (_curl_read_callback6)(void *, size_t, size_t, FILE*); + +/* evaluates to true if expr is of type curl_write_callback or "similar" */ +#define _curl_is_write_cb(expr) \ + (_curl_is_read_cb(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), __typeof__(fwrite)) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_write_callback) || \ + _curl_callback_compatible((expr), _curl_write_callback1) || \ + _curl_callback_compatible((expr), _curl_write_callback2) || \ + _curl_callback_compatible((expr), _curl_write_callback3) || \ + _curl_callback_compatible((expr), _curl_write_callback4) || \ + _curl_callback_compatible((expr), _curl_write_callback5) || \ + _curl_callback_compatible((expr), _curl_write_callback6)) +typedef size_t (_curl_write_callback1)(const char *, size_t, size_t, void*); +typedef size_t (_curl_write_callback2)(const char *, size_t, size_t, + const void*); +typedef size_t (_curl_write_callback3)(const char *, size_t, size_t, FILE*); +typedef size_t (_curl_write_callback4)(const void *, size_t, size_t, void*); +typedef size_t (_curl_write_callback5)(const void *, size_t, size_t, + const void*); +typedef size_t (_curl_write_callback6)(const void *, size_t, size_t, FILE*); + +/* evaluates to true if expr is of type curl_ioctl_callback or "similar" */ +#define _curl_is_ioctl_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_ioctl_callback) || \ + _curl_callback_compatible((expr), _curl_ioctl_callback1) || \ + _curl_callback_compatible((expr), _curl_ioctl_callback2) || \ + _curl_callback_compatible((expr), _curl_ioctl_callback3) || \ + _curl_callback_compatible((expr), _curl_ioctl_callback4)) +typedef curlioerr (_curl_ioctl_callback1)(CURL *, int, void*); +typedef curlioerr (_curl_ioctl_callback2)(CURL *, int, const void*); +typedef curlioerr (_curl_ioctl_callback3)(CURL *, curliocmd, void*); +typedef curlioerr (_curl_ioctl_callback4)(CURL *, curliocmd, const void*); + +/* evaluates to true if expr is of type curl_sockopt_callback or "similar" */ +#define _curl_is_sockopt_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_sockopt_callback) || \ + _curl_callback_compatible((expr), _curl_sockopt_callback1) || \ + _curl_callback_compatible((expr), _curl_sockopt_callback2)) +typedef int (_curl_sockopt_callback1)(void *, curl_socket_t, curlsocktype); +typedef int (_curl_sockopt_callback2)(const void *, curl_socket_t, + curlsocktype); + +/* evaluates to true if expr is of type curl_opensocket_callback or + "similar" */ +#define _curl_is_opensocket_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_opensocket_callback) ||\ + _curl_callback_compatible((expr), _curl_opensocket_callback1) || \ + _curl_callback_compatible((expr), _curl_opensocket_callback2) || \ + _curl_callback_compatible((expr), _curl_opensocket_callback3) || \ + _curl_callback_compatible((expr), _curl_opensocket_callback4)) +typedef curl_socket_t (_curl_opensocket_callback1) + (void *, curlsocktype, struct curl_sockaddr *); +typedef curl_socket_t (_curl_opensocket_callback2) + (void *, curlsocktype, const struct curl_sockaddr *); +typedef curl_socket_t (_curl_opensocket_callback3) + (const void *, curlsocktype, struct curl_sockaddr *); +typedef curl_socket_t (_curl_opensocket_callback4) + (const void *, curlsocktype, const struct curl_sockaddr *); + +/* evaluates to true if expr is of type curl_progress_callback or "similar" */ +#define _curl_is_progress_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_progress_callback) || \ + _curl_callback_compatible((expr), _curl_progress_callback1) || \ + _curl_callback_compatible((expr), _curl_progress_callback2)) +typedef int (_curl_progress_callback1)(void *, + double, double, double, double); +typedef int (_curl_progress_callback2)(const void *, + double, double, double, double); + +/* evaluates to true if expr is of type curl_debug_callback or "similar" */ +#define _curl_is_debug_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_debug_callback) || \ + _curl_callback_compatible((expr), _curl_debug_callback1) || \ + _curl_callback_compatible((expr), _curl_debug_callback2) || \ + _curl_callback_compatible((expr), _curl_debug_callback3) || \ + _curl_callback_compatible((expr), _curl_debug_callback4) || \ + _curl_callback_compatible((expr), _curl_debug_callback5) || \ + _curl_callback_compatible((expr), _curl_debug_callback6) || \ + _curl_callback_compatible((expr), _curl_debug_callback7) || \ + _curl_callback_compatible((expr), _curl_debug_callback8)) +typedef int (_curl_debug_callback1) (CURL *, + curl_infotype, char *, size_t, void *); +typedef int (_curl_debug_callback2) (CURL *, + curl_infotype, char *, size_t, const void *); +typedef int (_curl_debug_callback3) (CURL *, + curl_infotype, const char *, size_t, void *); +typedef int (_curl_debug_callback4) (CURL *, + curl_infotype, const char *, size_t, const void *); +typedef int (_curl_debug_callback5) (CURL *, + curl_infotype, unsigned char *, size_t, void *); +typedef int (_curl_debug_callback6) (CURL *, + curl_infotype, unsigned char *, size_t, const void *); +typedef int (_curl_debug_callback7) (CURL *, + curl_infotype, const unsigned char *, size_t, void *); +typedef int (_curl_debug_callback8) (CURL *, + curl_infotype, const unsigned char *, size_t, const void *); + +/* evaluates to true if expr is of type curl_ssl_ctx_callback or "similar" */ +/* this is getting even messier... */ +#define _curl_is_ssl_ctx_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_ssl_ctx_callback) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback1) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback2) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback3) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback4) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback5) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback6) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback7) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback8)) +typedef CURLcode (_curl_ssl_ctx_callback1)(CURL *, void *, void *); +typedef CURLcode (_curl_ssl_ctx_callback2)(CURL *, void *, const void *); +typedef CURLcode (_curl_ssl_ctx_callback3)(CURL *, const void *, void *); +typedef CURLcode (_curl_ssl_ctx_callback4)(CURL *, const void *, const void *); +#ifdef HEADER_SSL_H +/* hack: if we included OpenSSL's ssl.h, we know about SSL_CTX + * this will of course break if we're included before OpenSSL headers... + */ +typedef CURLcode (_curl_ssl_ctx_callback5)(CURL *, SSL_CTX, void *); +typedef CURLcode (_curl_ssl_ctx_callback6)(CURL *, SSL_CTX, const void *); +typedef CURLcode (_curl_ssl_ctx_callback7)(CURL *, const SSL_CTX, void *); +typedef CURLcode (_curl_ssl_ctx_callback8)(CURL *, const SSL_CTX, + const void *); +#else +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback5; +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback6; +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback7; +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback8; +#endif + +/* evaluates to true if expr is of type curl_conv_callback or "similar" */ +#define _curl_is_conv_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_conv_callback) || \ + _curl_callback_compatible((expr), _curl_conv_callback1) || \ + _curl_callback_compatible((expr), _curl_conv_callback2) || \ + _curl_callback_compatible((expr), _curl_conv_callback3) || \ + _curl_callback_compatible((expr), _curl_conv_callback4)) +typedef CURLcode (*_curl_conv_callback1)(char *, size_t length); +typedef CURLcode (*_curl_conv_callback2)(const char *, size_t length); +typedef CURLcode (*_curl_conv_callback3)(void *, size_t length); +typedef CURLcode (*_curl_conv_callback4)(const void *, size_t length); + +/* evaluates to true if expr is of type curl_seek_callback or "similar" */ +#define _curl_is_seek_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_seek_callback) || \ + _curl_callback_compatible((expr), _curl_seek_callback1) || \ + _curl_callback_compatible((expr), _curl_seek_callback2)) +typedef CURLcode (*_curl_seek_callback1)(void *, curl_off_t, int); +typedef CURLcode (*_curl_seek_callback2)(const void *, curl_off_t, int); + + +#endif /* __CURL_TYPECHECK_GCC_H */ diff --git a/ext/curl/lib/libcurl.dll b/ext/curl/lib/libcurl.dll new file mode 100644 index 0000000000000000000000000000000000000000..1c912ca6add9f0dafce018652cee634713d20481 GIT binary patch literal 278016 zcmd?Sd3aPs*1&x`orJK28eQ)Ir>g5NT&j6BP0Quawl%Gpul)0?|Np=LugIfm{fDpWul=L<)}xv|p{++v znR(@n#j|E#|I68zU0Xc;vg@w9K3x2Z%Zq16t}DLsy5gF%&n>?8`Wcs>R9M)j#071n zjWhZ!Ja$I*@7Ja8&&VZy*6ZKS=&!!}-EZ3rO~u<){KYw+&*-b_9XG?|d&3uPGv=!A zH!}+Pdfxb6efuqZMtyJiqEmf;e&zI;($;_LS$(ah&6uCBJ+k`p^Ri`jYeyIN&FQCU zl~AgHPV;Y(pKy^1MpcT0b2KeqC3SzbD3lfA^3SU^Qq-xG{rouYq|_JWYSRJskN&xu zLbfqi+mQ#5@?0%irocJ>o0Y3gq22%O-$T2++N_?1>b+Wni&xL&Z1|+`<#WSChMey9 zQ~Lgc^2M6gc+%_{mxV9Ww7+elFfg^vd`I)`@y`!Co#YhLT6&RD3?RWd|6qWWYdL9_ zQ&9DhzG@>W2Q7O3qn!Vw**DIfPNJd-v{8gV^{2{Re$Dk1R5XHCst==nRBl{P`2T+j z8_A1H2Ay~Q)WDR$d4cl-Q;nw2TIj!F=KZ=TS2HR$7|D2v2nEBes`%VUPAO>!nO~S+ z*PiluD7AJuqgi8qY#rNM3hXk>nvw>?+}@^ntQ(hkNZM}o9n5G|J{y4K;liM~%{l_E zP7i=Xf@Z#vy1Qhd0=|&qni(ughDz#f->`sJi*?%JkQpyoF8R~{QSt!az&}071&fj- zR6SM9CsJ1R)J#ByM;O!Zc1qIZ!asSmcuPcA=+rDMt!PRA5q9%eJ~b9B^!O1pMP#&! z6cVY8f3C-K34~I4(`&q1$lSKd;};CKTYqKF)>dq)Gus2HbG*`ls^4l_qD4>C0h0=6 zQ6W|DrHo`A>1LLbX_$)~oKjOt8m+&L&r#J5BL_OoP1W0pmPp1q%}_}=G<{0R ztYl4zU_Jxbm;T$fiTa7oY-+s*Y7?uRWX5-tKk3z=Re5OoW`nd+N?dccxu~P%3D8gF zZ5{8`R@q}!7sxKQKAQH!Zp^-4FnpCUGIv>D9|jWZ)3-na>BYU!)G(Kn3Z)a9ZZ3yF zk-2)<|9BnZBWCQ9#^Adcw3F+0#(U$KGkjdl0&3K*>1yJ@DLS@M2n%H zy&HhxfktAl9-nr=wjDIASs+nkUQ<%f=n9nZ(dK>ft*vMYm|JaM^h{cx-eFWJ#Pmcv z@K%XD5tLq{vOZihnhr>}Z|h^wjj>jamdR_Dgpk=1G+zUZ^~NVTg2nA;k88`VB(yhN z8!|f>-&G(gy$^Zc17GXs%Vd}%R|Us8V(>RBiS27UIC8`^XoT*mvg0xxy zLkrn~X@N@vmjo`JcIhSSU&#Jb^VdjTQ&N1sn!d5miVgE;A@duD{JXEP0uN_)a~Z67203 z!)~dmZk_kmDlMwU%=Rs2r3fa^3??rv2qY&IMc0U!&?3KN?nizRGUp5WP-+f~tr|~; z#4v05TP8g+5^si2GR#Q@w(p6)xtckt$Y>v5007Nshk-RMw%-ot+rD~QX&+x?^RM|B zk~4+PlAXy9wDs}`K~%3VhVNj$1|zniSV(=TBC`KnLy^?eqs>yM%_^r&03^p37)h_& z+6UELt&s(=i@Hbag%=!ns|L}&VZNR^D{cFF1II8AHIlzloih7Ut*S}FtA0r(DLG0rlIyc2F`Hj&gm)0ERcdGklUhJWe_4 zuh?w+?#t$jK#EqKi0HJL%1(v&&d3`AuM&8+Z=PGU(x@Uke5fH}y3k12a|?~Kc1AeU zm&nb9aD%aLmywJ;L?cFWwIdb4CQB2eU1iCjWRrxag`bOrp9dMqa7l6Zx)l4YkwxkI z-tMBL78(yr(W;B30khemoo@Sfj)JGm(`#9+QcubP6*4<(S)+cXB=X`)KEdQ-QB@3c zafh&xxr>Ab5`t!bBl+irq}3(tNFT*4=>U2Y2<%uz8dpAM9+8wN8 zHD>f0IM?<)U*$!FbtYPFJ~CwXf^}aW!P z1OB#Znu4h(HCUPF)ES-wgdD*7wE}dKtB*7Os^>!Z6xOlj#p&IH((hBVK#G`J7Vj_#_3{>beOjQ@D5I)4H?!4n1Ml@)C?}9YUS3)d;$h<)xB0;O$ zBSy&k3CJTLt&h=ak#g4ahxCw++pKraaodZB8RyKYMrvTGQPvqsJ}F3Mo?0scGc$DU z8S*(y0L%k`xm~4$S?zXnzv_Dm^%IVQhZ_u}7W6->)GH71y51Ka`LG!~d z5!p56LgqTde9>tAv^Z$C1j;&$R;##dNApW{zbM-GskyGTGvD0N`sr}*x+`C_-~jKs zu9}X7t==3)nzkC|x*#>Bc1>AJ+2$((`gJW==+|xfQvX*Af1CE`m*(w4&g{_}TGtz` zX^MVYOga6!Rw*LcE6HAuG$}>DIYZc_MWGSc_g?F#`6yQ9S8f&*YRXyzSGGuqh5J;| z1j?EJr7m9nbI((ALZlKLZoXb;zGoy)2$`Q6vF*0GwZ_~UOqLpUdu6}sNGDSKeOL&%h&3X5v`)7E;$-4h^is} zhssGqJyC#FY+rL;uBMR7ypmlGUw|%^LbFDt6@`#t#dT(f%LS>zF^DfqmI#1V3Q0xY zAiS>;p%X3{)KlL0L+0ZpODP*NA1Ptw0V^Z6)ea_WVfEL8W+&1on9MDyOKgfDR^Qf# z*8taEAE?Y76xnB_`u(0dn$i5%zc~MleeWADerlx7%C&u8=ZehVjc5{5^$w&a4oTl2 zf+6vy9-l}cJAW&7(kkoRKz)U7uVY*nF3J_040}uF5QMK+k6l3Vwzx_q85}#k^Ip(b zEK<1FNZ>9i5Qvxj+6lZE&{woZ-}kmyxq;~WddvH{eL6^L3ADVQ(_-ZZT0YGQMBf`* z)2GAUtjB*vC3|ytf|Qt`ugNW!+T-*!16fX5YhcoHBK^wY;8JqdQG^I|M-9CR`L#-R zts>(tJI|wKE|bdSOd%%=+ZO2jth^P0-uk&7uB@a_u85U&fwzR8`v!id?P8~f@jwpa__&e0Z5$Z5?K zke1`5g;kn|G%0a~#PXF^BdaidDwoMqDN|1(oX9kv7M9G0WlUnIaeM#?APzs)dTvkv`_hlxowSC2&@MOK0rW57aXPg^-6e5n|8^1 z+h3##+^R~h&%AYT_7CNOkcF*p00AZ2Yl$i&%-cl>rSJOD{JbEc%n@vWV=F&{ z_-q?r4_{@(Zl0-yr~g!wnt)^1Q*8S_E=PZeNiB5une-JMo(@BTAu?8?a0vy3+?AEieKb8hg9VlVwaNMChqDD|?wnR%0KN;) z#6+(=oa!}7Tc9_7SS62K>xIb&&D}9lsIu}0)Areu1-?w%n#yWt+Gf|Eno8!cGkrr< zYh=K>IDeZO*WV2j%1l;MQ|9GG5HMRZJF=|zsuc4w_kdSRUn47BDqiAOUtgtkL->z* z4<@W%*$$rWb}(N$csiv`)FdOdNY%88!_u^c6p6S5$(DchPRi#~{vd*Ug;23p*BwNV z5-9{hM(D$%bdLQ{4{`S3Nk5{?|4I*Y=)v?@E&Oh7M;zF`Uf%;R@n+=M8rh7gF;)^| zn-%#+tRNy^!vk!iD3qLAfV9V2@!YJOpCIQI(g>62N(CrON(W%dSAQzaASKn5-h!fC zXWmpG?3TFed)p2r*9a4eaDJbvZgUy0y}&WewSCm4i#O zY)La4x?~ix41CelvnXv%jhpOwrhE#35~M|**0kO%|2_9KQFEJa5_W%^pJLb023 zb5O3s-ij95ILA@eT8!Ab+?AjLtDKzEbNN;C?Z;cgV#6F9+hzyb8BIFm&fhvSU2DFF zEd)g^tf$V;tw~MrihTZLNDgb;1`I;7<#?S+vb;h2!|6buQ@jw2MHG1izuOawD)wd} zhO->K#rhB!qBl06*y;(04BfU_zdtDl(_{E3nZ)(oO+0#H7pL~Eswt@tUmWYOGnaIM z%`PTe)RZz5dM_v>)Os?t6Yg+31*Sc)xJ0m!y{e4Q((5pHy`fe9&zMnHE)dDKy?gm< zP$oU4gZyY^`#-N2j1c9YhIwJ#sjsXkB=XkZp_b$3=#H%ya=LVMj?c%y9)WGki?W0;6n)G2LTS@0ge0 zEHiCAleUYKoIsa-sy#LnDI@=^<;-Mcbot)cLnIYNhE&Fu5r`aWnC3$=`BJ{~SlFx< z1GOeLON0mi99{K?9fvZ;j-pAP4<%lW+Ld}cYHxqJ>n(MaAE?U@GI=jtR% z`B+k5``0^dtTo!3Bvt-(gP`cbpQt03?zNeo)Z7&nehpX%yI_4_MH0a7ClL#5zqI@OM9dJNf&D zJh9Pd*;we!`hq>Z+Z53BnWmPAygR;TZaC`PZiY2vR$*n7{rw36^FbNT5EbXj|7O*i zUkA+Z{il4sFN2lI8t&bZ%Kf>wrEHh=&F8YzD?`*Cy=bV3j-gNeC{VWGE0D)t)M2&~ zk)9FB+Lj9EAu$|7QbWfT`pGZs3FrfsET_<(kUsMLoAUJ17c*YH^d-rwGZ#e3(WkcT z3F;?^?P=W}Lm$}^OfGnu^f{E<5;X4nPirE!#|fplZoXDHfL# zf>8B3WBQkYq)H0vXYU9m7wz-YZT~+#!Q|chiU`)43xp8rREzy>P4&8f*;1QSiF(~S zyF-uP2Po3#4R$EE&+17atZNx>t+_EceOJ)D`(1%|+ghY29z$r?nfHmdREON2NIjuuow;PL`Y8HC z;#tH1CYCj&jRowT<}ReEj>x;JOzhO#t*h=(M{nl?5-^4orNl*UH3mqZ`lgJdYC!@I zN*nsrope{C_sLjGV2QNGY-BO9cc^BT(~Q?`#_KfmAK?4txbq_d@lA8bz${{mt)tCD zY15U*AH!}|`FM|D?JP^@Wr&CNR4qY*wk5coTlA=EW63zJ}a2&S7fyA&o?kOv|3{gFXOBFs>){$0khi= z30|#|W*GMe_jL*>XVH>hwWLPMZONgx(-Ml(|D`3N-Va)mr0$mHhN}0$><3V@Zbv15 zEm-z)hI8OoWE&zhu!09JYmlub7LmVMUrWXNWn&o~; zTOg=QnqnOoNstJoN($<{s#LJ)J4Ljk712Hmnyq!I+<}k-3wBVXNqQ`zAL3;C*Cjr> zyrjrOfv15RtZvG;ER904G_|ljLnAGg{U+sv8Okfv`%I zwyY1GWJNxbpH|78)(i3_zCmTbfC6N#bV@2V!p?&d(;+*SD9JLCjS_iCA}Qa5;}Ni= zHAtG!vkrxac)fv6I!jrJL^k{7T&q$tP~#!sFfsP&pw-Y}Z>KQ!N?-KK}kP3(52a6X$(P@8nk|A2Pe zaQK(>8acPUzv%-u{tREgoM7wzKIu!`a+*K?wnnF1Nw%D=Yle3nG&#nWU>WBO`v=PU z(0oHd^e}C-_K>kJZFo7t_y(Pg!;CG?EZHHx7{yA?TD|DBm}aobV4ZKKk2f&8kzXBI zv^E~=qD31nS_9K_G`x)6xAavHpiR14`_?V+bp5-Z{s}k}$xdugIHBaA5$mDu)5V z)p_&2yy}AsUW5@uE``j^GI-21A-G@>+dut|i?HMhUiY2&GwuAT1~u%~Ov*XWK&^wS&6a$7N=d zGr2bfoA!SKSKTo>c;%)j2Wz0|fR)0jygvaCCSfO-ZQtP#)x^hR+$Jb^SGHj=l4jE# zWM=grL2-~eHo+(8{APcVtiQd1T=pJYsN31C7>)u|Ob+{M!`u@>_RS@xOl_+)sd3m? zi~z;-&Gz+`D$X%Sft+&ONve^2prlf2v=fnvELmbJ7XJ-Y60&}KNQmm*Q)&~XB350gMbMgVvv_ZNYxOAFx{Qsl#A^52nOXsSYg0~#t-yh@@$h2(VI1me& zJ7`XiKO?!x26j$whKI=h9Fu4V-SgAGmMG_d^v~>MuZ||Dq}w9p1{Fx&QWQ*%D*}Xjd){PDPlzd z-Gg4<4(q{Rdo)llwc9!5DT>+ovmY{KM=Xm0HjZ*R4}dfmb11+T=aThUal9=-v!<;e z^M)$em6K)n7mx~(uFb5G9AG372gv|@6c{ACKDGanHHQ?=2c?(Af%AnPLDN%&GZ+6DnI5s&1Kcn&CaCh%>~KtpZ_7DJ9wCIV!oa!0>h&rU#c3;qCm`+=85W&*E^A z;l%WuB58_xhWFfpP+7aR3Cp<{14%90Zg?g!?gv|Dl=lnd3Mh_O{Z;n-RVNGi&HbFS z4n|TrZWfLm^Dz>HwU3wfeMa>zL!Y=_jvjk#0XeDZ8Kq$VACwRq+d;N|85C*1NYNLQ z9#(Q`<_w{FxkInmtp&iXz5_8gsmS*I6Nw06ZQtW6@UG0Yw+v=Fy<+NI3}etWcPy4>hasy!~~NU?Wjv$-D2Ho z=Ky3Iic^#jR;raaJJgu#If%0{fuqk!T|?%ub8dVxwK2o~;wTvu8v!3B6vB5x1af)B zPAOh&jljaJHU&jN@ui#hFAhZ3r@s`v5V^th{YOcQbc@1`MvNJyg=Lj81S)d2QUSBx z-Vc;ca8689`f1+CgYxUlt=6ipBBPxm0}d);{kALrNb-Zz8SfzTwjgC-|9|^q8Bejr z>hUi)IL@vR7|r|33c>j;>%*|F_2CIA@2n4c{8=ifQ<*{)jRk!_e4$wC-;Ia45}@}m^$ zc@G|3CEis_%hBN zl2%;oDi*X_C~Wfsu9+iMT&)5lB!hY_DQo3uBi+O#=w>2QlakX02_uPGp;!pr0(40K zAsbcL>2XwNM;8oEXQ2opfEsOIfi#yzkAq*Jh@m#ge+@tdcf>F+s+vWTK*;oq~&=I8?9}=S^67mhGEO3lQr#{n?t5 zn^tPOv4f3hU8(u`R=0+WjOn#;^MK)j_tUS?#@aIem);_+rRpAHmZ+2KK(hu_@kdf! za#rO(QHGk3vb>vr-F{sDzEIZb$6qKHOilK0RFn!C3U3eb43E%qEWa!iUzGwKAIZP*)^W(X}!Js6=bU zRoiGs++t%`F5twO-VrqlXm~i2_-9n>p86th{_~|X`y;dJ%#xB2sv!t{q$l1>O32 zqf);gR6_n|EBS@ZXUdGJgr^nvpQ zq`~kh-QyKNrWF9}$dWty27!+b@C?<0<_orOIf0P*p~#wD(l!?f?ooNHcZ&#!!r`uR zsQ(=>h|y!F%wWJNEM<-EXk#B;r_XVq4G6f}2X z@D0Gma|7mgq2wba9gsP7R zb~Bb2ud!|Hb>SmqfPDcozs|h9ASl{z=YWWh<}&Y_cpt~s7m$FNmT6~q#5xbr7la8K z%gjZ;G7JLV&G!3Z_e+c=Ib@Anv1Qa!QkVCbdk-6CYVp_iuLC1LM`(S7s;=U7>^w+q^+y7W=)Qn-47Fj)4s%;O1z zR8J<81Xek16N)|Q-vGqzanGtC9R3BsJ$hWHxB}Iw1Y;ebLU>+^+{-0v)m&$!ZC|xC zVs@yU_bD4nHV$g{k5=NqoG^=h1%H!gwFQdNW4}lbmj1{_O85`Iz_)s!_*arZ(W6k@ z&CxxPa;S)7NR+JqGDK6!qL+T@X}XnK)G+h}ejq)^$w)scS>2R0()Tzm_$lAWTJqmp z_*ytLIiXRtFpXM2NxJeZ>0XxN(#7Wq*uF#PVwQB9Na-e>CY^Ul_b54#uKXuSSE)$% z&UcD*;VW(5nl}~M)G{--Z9=6d-Ad+95UA?*nURU$Jv04ScY%Y4ygqU)`hWwlo?#3h z*EXTSlb%KSwM+~xGh(d>(#(--8Fww?Q=;cY%CCiDTsK*Z{(?Mu8-aj5qo#6t0gIN| z7e9Z>gpwm_7EUi9wHP03a}taCo;M_U`XJJaNv~At<+kq)C%v5XO41utIwEbIlU`4H z1FF?55qI~COqZhr&Q2;mGDNC@LfpUIVOiXHqJId3#OY+n&QU_F)5(&L9ywQYQ*hn90AS5LOBUdvq=uY!PCvi2iS{v&L2w`Gv@KUlby>fw$HcDc6DOGMx){V+1rpqtBt2~1~ z^IXSD_qwB@o0Ax$YU=|QmA&KZbH!Iit_D%Dukw0 zrvl2n@RVa)z^ov?3I6i}Q<~=%WAi-ZE0{@HNgJfFV} zIV|xzW`$_M>MQ}f=qy!3RAzL6nG#7pSJKEgM7%+LpDU@CulcR;O#EmjP!Ijcoaow} zZmOv>Co=J-!ogbu_RGPliG%d`T8%a3U>nK}uS&=1UP1(Np zU(OOZe5-6ZF5oy^$*i)G6{~+{TDcP(|Gv!E`Ley=!?U-qmPc{w|Oqc zvdcXacH0CN29y){J8g0z^A1~uXZni-*whn%Xp`g9&e+WbIoM(&$0Jrnp37;0Od_^Ft`~fW+r^3}Y1Q75Ig53wNQ-cP z5MQX2Z;&p${4d$!J3GC|KxLTz5s*n3Se>h)0>}59)Nr=s*m>E+B7ge8?+VbZAc4an z1}FHv3mYXpam!a6A^rKm{cE|Uj&<=6p^76#8cC5Ae24lzUb0I{um>^Z&hhKqg4iu$ zvL17^?fcg2)dH#W8*5KtGtN0e>a0K7zDIXvu|!>EIq}C?_Dcz@TzIGq_!Z>4*LEEK zzJ!GI8#J@38jet`a|?j+hQr}Dj*EA6f3aK(?H%DV!z3;3FXaG~1^o)_dF!~3aTxb8L~dYtbk7Prvc!AW>njRq&DYf$ z_aO{gYmwHXm1F$+-EFMy;iJ3kQ4pi%Fne?{EZIBKH_%3eGlb2NPsre){2iP#56$N4 zE3i?6Id?EbMW_|g4BmzRD8xGP+NzVB0ZX*_^?TYlFZ0kA^PzVrb&MBinLOE1 zpA5d?OX|!G92he;BVY@$WsD-3w$G6QSPv?$73 z_yT=JEn@W&&wt^!?rAkbER4#DT~DlKk5TCjABoLg*rcFG)+1zPyiiSgC_*L>tvXsb zp|xTsPQ!?t4{X*da|$Pi%`A-a?+$ScNlq+x97=D?4-w9sAt%xjK=PekDUgn-*lAtv zrLq)-Q~D}TD^;e?&0N2kSPr#$BY`FNF`;|xj#4c$M7W3>o0we!+tkRQW~0vk$|=F# z;&!oJ-Mnfn2}*$+1%B#GiI_A44X2Tm=^HHD$nAXVZxaRoT|&t5!LqkG|IOk2cdLRe z5x<$eJ!kYfK*jEp%dyv3>Va9mFGj)kx`yr818( zZ2fCM)&X8bs~rALu>{})B`p$f+8{Ex%P_-zdl>f+Tt))T_&iWUeA3T+hFRL2PhOuo%nk`O8-#9GLH!u`!-)+z(p;t0k1a~C6-Pl~Fr;E`w-551wZc?3iaf|ANTIKWT023RIn9f3$9skLa8kAWicT|`g zZQn5n!t~syg$zp^2n947Ia6}S)tJqn-?S(Q+AcMaMl@S+Q&5aaQ z)(%29j@?m?SUs0)!`!-X;sw^d@T*~7qWMqRt8{*`@np}Brz%sIHNHv>UA&AebRe0H z;Sq*8mB3E%2I#PkQY9jT(_)(zyb|JQqo%W-_)78Xc$q$W{B;$XN`%K7t(R7l%oUPUH&5V#bpY=H*){eeTh-J{@go^Rc+N8=M ze?NCZ2>cJ75K{bg722*B_0k7;^`f(j^Z}EK^rDHy`hfAp#LJ176ZaGM6R#&uwLht9 z-@5u~$o$eUalv>q@^$7oI;TWIG1RSMT0zdPd_D0DBPsTiz0uo>wD9DRc{Y?)i&oz0 zsQavND6y4t8lUow;lr-I1sS9^`|SEz&e6BfI9V2*UB7sv$gbZwU#*W3Wy9@{ABtUz zhgW-g9(H^Qa$Q5B*AiX&{<)ggYl@}~=kI?0-sEpLf4$Cg_QeQnJ^nC5;GU7=fD=T& zM7hNN-67;0CVPMzWb0cg{xs}p+5Or+Q)NzKB$}KMBQeJbF%s80A!PD&CsbtnE^tDS z^lT@@=+vqZYtv{IPf@t@_{&fn=E)=bH#SWnO zjuQgKmz@wOZgxVT*y4mhafK5C#mAixDE>)>K=FPRI0!{rmqYpdb(_ZbPs6l7|xHj!K7Nerql13mkj$ zz>IR>OAfbg&nB=7=oAk<`0#nP6$mCzclIXiZv29a<9>lZ3iMh6KGL$|2%)doBikG~ zZA4!-t3`FI7oySswk*XScS2C?PfiGmEptLpY_Su9VsR$~#cpvzP;8b8L9t(|fTGxa z|HXqs?N?;~+QUCyxkSBQu?g9U?}O}LZC`j_4qN+*&y}SYE5%zPbE>NK_)AJYao-d} znze>U)kM11r_Rm^ny*^Nz9*~j5Jz`al1{9Q%2I|n>m97#pf8r=x#+D8TDZ5YeevH3 ztuWV#CUZ*kR=*ZL5&}gyj(FAj534Xzb%;vN6S~b3d!TGT2c9a@EZj&{#M~x1zIeqN z&TyfyOINTC6KBoR_*fPo}J3Jvfhi6$Y5 zjCx$o)S!#-;Q(7!@dSR?cMHC$Q$Obe-prX#H^>-@-s8B&3*QZbpgv95O0%4q6(e7&F zA7KaLK(4nM!R0{Xjj^Wyf%DZW*;1=<>5x{y~5KfJlSbE za+kt?={nJZKXS4C=F1#*=@W|3kWmV|2Ai?RD72xxOY7~nPZWzk7!v3HMh-r3Srn@y z_W-o2TLump=Q5m^ES=QvrfS#qzO8sK8+N2sU+vv5gT6f|X z4_i6-%0d1tA@E$tS}!+fne2J3!du&| z(@<-fGB1iJYF7!FKk^PA)HXuqAM2qulZ1_y9zRt!F5o>==sZj4Ja-jIq_KR+W(1$I zb@Cibxz&eL$8LTLn*WqhgMFSj$}4*QktJw26}DJp@B`i3J^6msB9o8KAcy1W+;|-Yy0t@e8^1T@z4fS?>Ls2#_-_2B$ZvgLYe37G_@+MZL zh45HrsK2sW&B5^kQ+BtvkWa3@g6zP;>j$aB=&xwlorXI(ZF4Pa5G~8}| zwb$nEWl5tJ=Dq_HQEm5z1G3J{Edig$zZLJSt=0=6Yz(qw;IGj=Gf$$R^keo?dbyI# zL3TdAX5Gq#V{({}XJ_Z*_Wxx*RAJpqj~%+#-fo1&6F^L<%Q?OwSaYUYhChxRNAz6#m|gE z7hbEB727ZTH&^&iv7X3qL*9(*a$!*NS^0PJV!~2(xte;mAv~F=z|_Kd)?79o4tJSb zU@NQn9hm{Yk<#4Q6cjDd=%5m88dJ$B-$k;;f&ng?#L-nbdk)N@Nr5PZpIxKh$pC z4^y4QK|=0snERDp)d~36%>&W?<)FgyONUa0zD~jXB`bg(OPF7@LEE?G-#OwIVWb+$ zDf5bA@0Voam9uXlPgZ5Hu8`T>OJ*nQsoD~T!?0fv_7kJV=Y>K#CVs!?g`1^HWGm;= zoV>~$xDG%f0)Q$VVhH7O3)&#!0^bj3PN9FR_LPdCe1u%z+8-2ONpWi)4R*utPerwj z&HSz#J`JiC6um;`;E;I;FV+O*#1xaP3>TMu*SY&AY=AYDxt_>Ax%1RgXP(782jhj`#JiLyIuy0Sljla2=frj zAo(O9;1(k_W-1)#EOhvE%gq#dTxAy?-q1;coza`oV|%W4CsHGV!m~blO9Ll?&aQMR zFj?d7l8?DvZga4BMsM*8>>bftWL3+%STYrG?r}Q{<2XDg;bi5OC4Pvh!q)^jSruh{ zEVh>Q5vCQ*KrLL^uDSE8OSMynth~B_^Sfal=;D|5=xAMx_dH(|8R*wnuq6KAN&$${ z81tytDj5yo>+o61P^i?V0U1TAu(wTSRU0Gh-k6|RUbOmTIXY+VtXfIsRN-25Jl@@t zh=LT!J*6d6YuIhk(G`M5^}ZYP{HNHNV>q@^kB{LDb{s7ri!G!2KqSwP{ih&0dicp4 zCQt&%jR12H7a8*d><^-b>#wfljmQS~Y5S2*Q{MeawmRo^I8r8KX_J+>nEtG-9ny+3Eq%&YW9*<<9RG}A zHZ_wGXdC}Dw*j7=i!v8Vjrko^YRmD&RxSbzmL%t`<{DfD)KV%p4;HBlo^A1xMN~(2 zJm=g3N53*%J+~9%;#7>0#Xc@2`5@>Qv< z4r>5yFI;dJl{5HFWO>OOu7ldbOW_(GW|L=F?Z~K_N=3#9DixPUwp)3ll_jWmo2u{0 z6a&|z;BL28a-`Kf7&Oum9xJU`_>?pOO!=_l_Muo%ufZ8M?%uasjDpY5^p*~%d}k@a z#_LRfkYwvyZXLMifAM_)1(3`mt__;^%G7bElQo@-144|KRN7W+gizKs8~u|seTA61 zIG#taKlnEU`_D&T6e$UEo*uMYCRC2}vKFS!#e~52nj%Mj#A)0m)XU_p8l^yzc;AN* z5MpvxRmwP_bAa~G0S2-Xf$vhjJNM*h=9cXHU>pwhQKvu0+Y#QQrj+x78D_;RPKM z>LbKppCoh$a2xQwmSZi8*3^@mPPgj%l^ZS15eP)=3~LrvbBFt0Zy`~6ym9t z&;xlKwQRlh@Kzb5MQ*A>HfX*jcO1$b6bvjr-+(H6EP!E=+;jgn&{VFD<2I?X2 z#-MkTbsh!b<4Tb}@bMDqF8nW8B%hK9lSVRYaKS?Ck@}dVwk?oEk3&<}Ai$ib#ut@Xs$?k=lBsjW(%Q;va@hpR`%dGc^NQ`0!$mBm zQs`tOwWuWRU{L`U&6g1lth|!{r|KU=99;%BEL9!WOxAd~Lj2RiM?yDQh>71ST*9NP z$O=YY62)?gdn~M$N@$oX5P`Prhc~S1FUr zi&ou&>7si5oS;$tlmMFdWR|HIQ}xC+&xaUBnbXN+Ht7zbZn=w@RRv~9)pBooC3GNU@Mt`1Pp@wKhR5Rk)r{TsEqkormnvF_kP3^}QM#v+L4O0EnP)JGNA6bCN8N z1;`3;IyUf{%EuM!hmQ}LiyIkdUj1+26HL{V$P>pM|tASzq)W6C8hRX7!`C)0<(WC9`c{jA=E z-RVh3A^IFg8`g3MC(v zxVoHRE_lE#e1s~@)^@S_EdIM0(c0C_r)S3UnJ16g7!dR!3e9pX# z-HtOsuokrsrNk|xhU*%^*B4-562}O10wm)9>B_!Emw)pInzW8kmmNf)SRNa-upGrH(994iOooApz2cr*>9uN8S_&aY>9I3cPxl20}eG*WqMsKtsR8G-hRbA_&18lpb=u2+z@ zS^E$uLc<~4SSTR<-8US@I-9`~Vic<0U_(-C(aK|;)O{#j^3>Eapn{^@x0IkYc#Ryj zx2PX18tUSj*M(~GJn?B>yx^gt90IOKexwIG+H8G`=0GX;})S?XG6qIkjRd zquj-dtx2?;<(zbYItR@MPMV%7Vi;vUL`EQYH6nF=@t?+RLhcg;?TzE2qj3=3`{OAS zRp@N93Pk^Nr_5Kox~9x!F6$3>`g{UARGDCJASa~s;dJO;mky`9MLFgc^0X^Kw9R_T zDXNSCt0j$dK$X>D<efVvMJ%$UQR9%i3{i5JP#i4+E0C0h3 z=8)o{Z-l%^RViS^TJz9s1ie4YWLNtNtMspM>b+#wC9j9$)fi`|Z!n2gmbdqK>LypH zS?)~l?Bw|DFKmh(d7Sd~1+NSj-+eg*x@DZ$E~QC%(A%L%cv_{hc7>)fJ!DJr`oLE2^f5H1iR5M052E`3*XZxYp4sOsCT{wKlsTdBiP3fpQJ(pX}kGcyy3 z_=+_ed|eLD!jJr+E_u3iJ#!DwM`h;%%Pm|;oo$HFAuZx5P`+=M-NAIlR^nJMd(J9n z9)FK}M35(tIJF9jFnIpYvS4qs?t#R-;lA<&aDo)Iuh&&?oOc|U*MK>%s7ToXARl{- z^K|X|wgvdKXr7wnW2f>JYkJlOyQoC%$M9hO^=e4gv_W*TE+3AbzZB+Jpp|`GomcR_ zmt!Y42ji5}X@1K2Yo&0#xmwwE#dh2zlACn30ZTTi@it=xQYzV`c7SGVnG=)!pBy5@ zUz8z2uK7devr)pLgy1gRAAk0TU_y2<;0bSN@qX;RyA36K}tZ(+y6QcwBhJ zlQQ-+C9IF16}p{m$mwK>?K}NN`r1CxklLAnvCdriu|Fzx*I>!b<@eWu;{83YrgBXf zEF)Dlm8&mAz`_PuUK`t2|}z;5Qb0jqf_lx1)>&j*X8zs@)=neM1g| z>eXq;H1~lpp=W_hNBRAOwx*f1q*+sv>gb>>wt6NL&l4_&iVu+^s%*HZnOO~8yACeg z-y!RYXHN^V?==H>)k3PQWN^`xU;BgXvHnlL7{;-|aw-|goMw?xyfBbGA9o+I+ReLD zMRG+^yn$Xf_U#B(x7^4vMsMyrsAg~zV|1ihgSfKTxrZF?S@ossLp%m9r&@B93#{=4 z!BoF;150Cd$4xgIUDpxtn4B|A9=F0Q(C_p~DSpsl-;0!)-8-1#V!PMot}wPPfzn?d z+B7*sd+Ya@GJC@fwy%|-ECi6UukBkcF|!-FzY!=ZUCZlo}})d_B^)1nN5E5a4BR_Z{YEI)J@zAdv2Yg zwfQSGC(Qe09@Ux0hRlE0m8E6m)o*0{J6zhV`9<2wIDDwb>SazS?VugWfXPUj7dg6zuiX5Y#Z&3QYJQ~)M*Or+pZGmCz{(~A zh7X^fs?Wi|DaQ~DRIzo>aa3WB97mP2Q@O{{#wyCeCz3lyauq;|^fo*k(Kz7aB~c1_ zl-JKSM)j-VOXbHa%mqqJll(t z{BEZ0J5}11mp4nN@qC9c=Fy~3i1RN3&UA5d6^r#aDxTs#9RhKW=O;n%X5`%1`TZ?? z-T3*eb3Bfgi5X-xFwrg?B)hm3P~laG=qtwyP$}M`nu`w3lB8Z;)lF=4&!i^Iq$n-CD<<^wiJF* z=LOuC-Xc;`Ii*Z0bw8AI-!mu*F>(CMUbwT;oCa{24c%(LU=v#-mJ2P+ zMGW-3!eiM0I2wzU>8z}{5F*l=jpTvaSGJu^vqD;FSnFKu(Sx9GXqZiI`M>MQ8qR5Iyb zVOK`P%HXM>MT(=h+SQRG+Q!?XoL}y1q80HXjo@O1D8o2@M}azee?(lp*xpxvr!Nu{ zy(6ZWO^k`GI*oGJEbd$mS9dvd$)L&oQC_8sf+#1+Z!ClVEh2CT9sNn0pj| zTV-4Mi60AWWu-f?OM1n9s1jD;jp-FY;8C8LC}DzKzGHPKrQYC&Y2_DcX8={+?U{n# zN$Z@hAE#~T{&89!=iZt9aoW1+Sg7GMDqPHu({6Tuoc1AU)iLL)->9WXu{AzT+e^f5 zfPN>IUUvN9!8V)sZE}$7`wf+l11aA+31F`C-LH_}W*x6)V~F0O1ugoU^vFH4Xxbn& z`O)vCB8j^15ATsBM>z?M5|@RQSE<=y4h|m@T`jW;KXb$Uotjp?2&ier3GmTbe-~L~ z@IiRh8iyB{Go;RQXCFH{19P$K3bG-$ft9CT*GjMShyRX`x+~Gu=uq*W&9=8Q|NldK zKXT#;icdDo@{W2cO&lcH6Is`(HuU&!po8Ikm%H8gBo9(NKfPV?JQs4i=5+U^x5;F> zzMDs<>iZ~hII26XzifArkjJ@6Wq4`euXB=e%Q!H*w1>a6yMiNhKgd)4J=(Ch%LVi5 z12=x?xNkt=gB^_UqW&bio-2Jv>1Q}pBjmw<@aR!Jr|K7Y|L`KpQ0X{dA9tMb6Khkw z!)sm=Bbn~5-AInxwNtWPo7SB+%7H!P z&wXDb>(r@D`^8=s9zcTF)WRd0_KzgtHvBHkg#fZ9bF#wyzZOoed$q*`wuiFUO4(5k zMos(g`>!CD35dQ9h>jj0Y6;J%<%;;Urq$9bX;V)<3lGzQyQ!on{-PoZD?R>uBF-Mf z?oiGbdi+jdc++$aE*pQtC?sQ|m55GodRt6@crs`;rXE12Q?c$oX*do^SpJ5P=1kg~IY<|L z(y>Txw~qg(av2|=DFfIP1<(N}kEluhQmlk`xq#EdI69uM zn8y)N`sk58b&W5g)g$GmG>-{*nf@b`_gi`CTa{ZI5E+S|c-oX`n>*VJ_KM;|ciU~pemCqEBxzu04K;nYyLCfqwc0l;nkexB%RIl0xsqoZqYp_Ue@fL9$)tWDwLqN}S#^N$R} z#Kay^v-Tg5@rd+y+1zYPW8Pwd2tJ~|)^n>1G1d?=&xm#AL`FFK3Xyx;jns+Lw{w#C zufL;YfP`~nJQI{h9}=>N;Krpy14He*fRe)5`{gAc8azA;qFzA!{Sc=gk(n+YvRye> zb{7u!|M3NNWO1qh;V_J!y*0O~6B}`11^Ng9vh7IJ!PPQbX$j2Lr1y%-}Al`v*hzS^L@n$xD%$0)JoZ& z1X2-i_x%iv8Z(`1;mNakWw96U>9h!0Uc;g-+I4c`T)L;nXQRLxWz%ND#=+x;yqCKa@7anCxC<_C>20;n1#}!l{*8?_WZow*nZP+bycd5Qt$B2z z9$(Iv*9lSq4~GEk9}*>So7s;#!rv4z#Tt@0yF~WyrkIR6qqG@>}pFKU? zRc!l?Uz@|BB90KKkF!$!qTWrOzOl8-sltP>tNFyLxqtLG0@PkaZ9(%bmV>lB5zA3{ z)XBP^EYt|ww`)y~*6syKVei{ucU6E&bJ#;j-^TN8&+njsQ!LYKvq9jOWk*80(2|AQD)YyFCT@b}t7)ns-ycqw1Ua$wU82h4M{& z2RkxrbF00#7f7naYbu|RWi~w7%pW7I&Z`|G3u>fyt>aA+IcnSnN_!%C zu`@3ZkBoKpop&6qN{Ps@p!tVch-mSIJcP?=Liev86qkUAx6SJ@`!}V<5+2Tr?Vk}D zR#WwaX!YS-q2%>NZ42I^jE9Rb($A+Pq9+!s$P?4(w4P`pk{zT};rJcG znH(f))~;{Wf-%D#V&?%2Oxb8sZ$Bp;N)-V>OojY_b*sMQ28J9QnMZRuZQfyUO`Yrg zmgjEF-0G)VQ@*!%3Xim8X<*$m2sXM}rjN@dslwl~i48QX-R4vWff^S9>pm`~WVgaY zX$oL%&ZjAIDZ3R2}l z=FsE42 zhUIfA7G|4FKd6d2Gd@*#m>Nq~7|k;G3#s+^RZI^&^P<9ZvGs>Iy23DLe?JGK93q1) z{m4j<4bO*0s_-MW{?4F@@^hvFCO@RKjk6^ndaS>mk47F_->atTDKSpy37=q+Dm)i7 zv)6cEvhtjYMKwuPiV6kko8?R{RX9o&gi}E8CF`wD#TkdTFA!+7pn#|AoU(_hva*~@ z*{2UGD=R;6bJIsTW%sBF#UQA%@t-Q2pZ*$C1UPI{WeeQ04Nh4-`K;ieaNX*ZTcOH{ zgq8NoRXIIA0))EC_DTOy%63hw->52hfNGZJmY2gG1erM7MEbkDJ23l1g&H9?odalh z0KDq#Mqg=G>RgY(u|WGj=m6a`{^LGHx_&N=(~A&26naAy%F=h9Q%LWoZ+eDPZj~yR zrR@||4qx+>p98VN{~=SlJjO{LLozCA;k|D1u}<<3lEu+ziJPpeWSrq?!68tZn>d=& zwv1QW;{V;WYTqRNVIkJ6m-nj9u*zo%!u$B^`$LeSO23_<2ofoZR-X{=L6r~H?slR> zt&`PW)p;T1k!R^Za)Gj+RwdLLk`PJJwt#g=^Q5N{4kgv%l0H|$RZobUj2@4Y-HqY3 z>jgvB7|lAx!EicrM@noJx0~x8mE$nwSd0*h@N-w{dVp9ak%zddIneG=9ynq-QWtWA zu~zobTe%%=ZT(x9^UYSP5oqZ)HE**M`x2y>OQFAgttfP^yZ(ri{S!Z9_`(6XoAdGt z88D|k?7nQzTlN5~ zR-+c`@Cn_N$*7Wtl4T8YN?z!eybp+xIu{wWpv$vn);RTCs$*a6fI(QExRk_>X`6N2 z9+^Q~%-#I{XPnp4d3x(i;THssEUKAD2Vn)WMqcB zDqvR6l+*7h2uOkFFOqqyCwNRtK#m3^$C&U{?K?!((Zx_6Vrj%vQK~RBikD>nQEcsC zCiKWOUlNGFBT*pU21IjWqbG4Uqox8a)J^9CeRN@@Z!k5EA5c+1*;7QxidOdFDNugI z#7Q-m$S}ILkW1Sp^5T5*`ubRUMz|6eq{M?{x+;>M_%#uhta_#=d}=kIjkhmYPNH+}cyl za4s1m#g2NH7Z%5ek2{>E-$p^ou6aU@YmsY0jl-mQhrS_qU*HH1H9N&Ms0>HjE!HLI zH&T8q& zXeeAb?+Ua9u>nOc$!d{P2qC;G&k*TXXIoz*(Vu+}gF6RG)=?(5q!_xy)t*zDon7vu-FljCd2tUkp5m*RO zo4GWCIOl)L4wM#sm!0)%eCWd1T)NPU-w!BJM?Q)N_>su=UM$r%2i-;L%Q;2t8=G`^ ztR^WUKq<1Js_Tm)ZsmOGNVu0nqXXF~G@Jb*$B0`Fo<4W=h4-e`II;qCo7is&=-2zX z{m6`FLu!qppC~`&Y6n^B>>!VFnaEZCr57mweO;mm!MMAYy12y8Z;4m(+j@L&G=B2; z^?qcJyq~S8dgqE$O!gNiVepnSxa@rscDNW8=YQvhFyS`VO|5I>&?RKo?s}R)h_woi{dQ+Xi7Hq<&|fi=bpQryPbRPx#ymnJEB(Vop&_n(;jAF z>MbpP`~LA<4v@(!x4ROdje7lm|r_iB<|?&wB%?F$uy@yW~mv$5ow z4QiFFK-1Qv*S^{P2D88HnOf3z!^!m&JM28V2s%}h->I|ul`ma$qs{`b;JsFhhZ_t_ zA*X~zlVC3GHvjbz=bGu^RR0>LEhFf7*$wA^8m@d^JL-|~7{+=n6S$vgfEg*K&8{Dl zkdZer``ILph1M%-O+Ko&1_$=2nFi0C%_&>tTkWoMFLCl@7#n!roceZaH_#L|x9NaB zLGFlbP;^=a{m^L`wQgFk;r@Rp^RwH z`$dmX4c25Yn+>dj4ModW}@XRk-H|=aMya*VZe)sV&JH~ZRScz=Vr~(^pL1Odp;QZ z^4JCbI@I2%*@@7Q+yd+-%^Lw|7NESEj+@0N>rd8}3Q<%^-|5doV`1Hk+#wcUE}@Oj zxmj|0bIq6)-t4eE;Yd$r6JK_f(vR(B7=(54WfF{9ZXr6*zvN~Kv`LR%#D3*}kDoGU zN`E@mL^RnDE^d`1mTehzvD9JjsD1oPUUx5gz_55%8&lp!2)lwjOt4^621{Axe-;v) zgiHmVAd>qQ6hO7HU)PcN8BuL1et(nS>F*oA*xpJS_LGbr7JQK1%f7sQ+E6UYd>TEU zev!{|LOHN^YW7VyPwOO1&u(U7D40h)lD$bU8T?5VT*4<248I@oSGQS#t~-oQ8=p7S zhs?Hh+OV$v?bC))%_2-eiE9zgbC5v_Jrd5BaYo(Adm`_EP~qbK(LFmt{nZW~A?9Tj zdhI%{D-cxm+#6bGpbFI+ia~wCmh1-2Gf*0~ci5v|Ataz+ulRlU+p_Ivb|SH)RHV_3 zhFvOfiWXkZhR{+az&$npV?+&?rs_(7W&-_jThUVV3~z_>9}bc@LC zZTBOCPh;`&#T+;)&GZz6+9^sV=d z==H~*{qtQl+?q#8>}ci+#BK~3U^Mp!wht4(Y{*kX-h2n8=XwR?%4ZfuDm!kv%01vN zmBP?n^dVk}1|hh!!pSq~`X~^I_$G}OIiJwb`O_g3nW+uoj;XV$HDdnvl-@DbH2V*D zv0v{#c_;bhVW0aD>4|0MuCO6gS<~F61*@D!P0kbBphf*|JeXh2&c8CV^R6M$k%?WJ zotN3!xv;s->4XPZXY}-tfb-dR`e!`nx`IqS^=!v+mHZBSm1vdoUc^aPJKyW%Pk*+W zuHMlyiyA{m3T2RJL*?CWi`d1bvpWoiw=x9t4kO^JJ$A# z9?in3ly7ytJt7s)YzD{ZXi*QYZ`!W8>=^}>R(Q|+w89r8M&*}b|FpNU;vXH$qQA{_ zhjm%*G?aNgJXz(9Uckw|7+2Hnh@wNcKL|O&=+~ zne&sdwD7$woi*|Mz-B3;M_+m`ylYqo343P~nt4TL#jslJ%o}85Lw$yz3M*g{=lKu^ zPy5lI(r+3$S73+OA5#2VF6`P|SWn);{_Q{_{15QnT|jLbs4>R(<6wfm{F35-{F9y$)p5ZyD^K8Qxb)F$H8qyku*bF4TtW1PL8pma1UTZF&)eNxH-}G;Jz~eT3 zWI`-#Gj5J={(}$7ks5Xmcr#0m;TguG8FOfQDZcnkQJGQ&r||091NTwZJy9>627_!L zXFw=EFJGjj4G-cx&zIQ^;o>>;Q?mE>vPU$X>aArkkENq5R^urc&0+WX6g!l8h3;~E z7j~q~HS@OaQo}w%jE`I@gM8zUYC-P~dU6h#*%DD3_C4|9d=ky$um!Qn{Tz9e6x&rz zQUz+f4a^&#+lEv3`EtD$4e>+zfA9!fbw_k6GOm8P>7R{j#4Wj}!S1nD`WU#2@hy$7 z=w`I|V%(PIC;(M*7ML#g5x~9qIizI?1z^Kfk_&_*YG_(`O8nU&3h#@3!6nWamV$Ev zq4uUxf(^z$`%Qug!Hirk%(;vb;`m&bj=@kYN5jzC2fZCAhD2(TXL3wxYH@Ayl#(;) zF?IA%dd${qay=#pB)-fbLm8u0l(II3ohM3Kwmc6{_sVSZ**>ZM$ofkC(X`fXm|P@G zsA8q7D&9Y3s#qDD+WlYk4a&3=?hK+xb)s4rV`{L(4vydLM)oddlsnX330FR->P9i4 zJLJD}ZH>LO4zcD%NR7&%#L%&F^LpEuHD}Lv%2q8DOT+nhm*Qb&xtH8m)cAQ1Dn@(D zjdVno>ieg~wjoMVxX?1SbWP>w5&x8pT?pz1T8PuSUB1p5$anRc%I$LpHLvFs_xcdW zI%lf0sq*S-X08o6*c8+ONpzF%>=1FX^!EAzVwSrOMe{t0r-xKqn3eTpA(ed4;GZw9l8qh>&Vwf3uY#qiAa9A(3f&qJhvUR`2mCdMY|l{lBFN|&F`&*X^* zLvX8JU_OL&qmY^$Vpn2b>*U&p>mZ(XBtNGYpM+~;gTO;=r$eR>dE0>#Lf4h+86tzaZhFB0h zgTm8p0hsYt1D~9^bDhN+Vy&xEL47X`V91+ISZVf(mz~4^*Y#Kl1eS#$*FV=dZk0?R55GzkVcN5R z=Ioka6h&pS*FE|c5rQYhdxeuL#FteipA=V@a15uH>ykMh+^fH=8Llik+4Z5vk2Aa8 zu36V$u^o&w{f^F^0bxR6#`RWD)~;9C#p@Eg`dt@{IL;z8Ayv4;n9<*Avh#!Q8e{sRz2uAwl{ujnU}oy(oYK1w$}J$xm$3W z8b80=?_UGc2Wg)yv`DxR()5vO%F0sk=Y=KE1p_LHQT; zJ-ShXzCD(I{Y$n*oR!chV%%@GpJsln!yKu{XM?0}m}+8f2bb?oy>1M{|CN(78LmcY zI<#~d+|;c%m+&&zYRa_ks9@_V5JwM9!sWcI2tMR|$MJT}I+?)WEAoBum8RI*7i#XN z0>mtHGu^5^vkaqi zWXJd`r%d2Kmnngb?sI;6ZDN&(02QCWk0a~n2Fq8q@+s@2D;M$Mn^y(T)v6Rzfvry{ z;f?OjBQ*z+TPY)JKh~D`U1c*BxgLzA*!KCVq+Bi4IKMU|QlcGXKpWkME7_Oqm~V2Y z&Z*67XnIiKRsb*xRoukQrD-4_q?E8{ZXEMU{7tX$MHlclw-C~~DBLkET>mkg)t*1m zF<}I7qGoVA|KQrmp!+QHOI@$t0LsaW!m-js_kJ9t<;kZge~!yYw-Zh|9f* z@BN1hE~OHYUd4|ok+u@!D~)I)dUC``Nvc)@<6*6?*Ep;OeYKSc>uxmx$HF-I{LMG< z8%o^7q|`w22`~9XHuaav@fMX6ztq(7)eOuU^V`q z3F=5j3G*?_%>7xT=ZHY3QU_txL!S4?mvLAS4#BN&E=)FT&wDqooVn25=y!=Bk`gk~ ziGL$H1Q9-O62hjb;ox=qGFhC$)Q>bIRq&;B(pdN*~IV=MW~Gm zx4Ny*eI2Td1br4dmCy{R_rP#P>voy~E0@W~^dVpD=>C+!pPVyGxUCx^sAI{<+wyRn zr9vwCq=mg;V33+81$N&*M57;s4X$R=k>JMAd+|vnETgmHm0-lF&{YIqIA1X*qKB!P z4Yso4sC6FI41g}K27GK*>g>FV+pnZr>7^QAu|jK=b8cf#?~Yz>xUVAurPkQw zetj4eY`gP(h1oQQAqgc1XjA`6vW>6S;6jP-VjQ95;UVt$mLzw720)bO-FT!3HmDrCX%9Qgggd+h+f5` zabiaI$womtCLk1xhbkm8=wE~?t}2f064d*8HaUXTPuil&c?Eq1)TlF2mIhPuYm=cuf{Qt;`<^gugfYJRt!XPdE!qF& zAu_y7x|-2Uz#3u*)VM}{KYjMW{VWag_*svC3|eE^6v&2iFN9(wRvSD;7>0oQDO|{h zV&XuI0XnT3JQe2^M-QrD8%k-}>fY3+x@Dv6KXj}vHWp_yR`S|gc7jBnUGLRt084xy zR7IKbYxu>uIrvCtLpN}1^xXLBqfC{Y3hn#XBaHnDL-cgzQo*@@!JYJoVD_vHCwNa1 zJQXO^CKbcXhhB?n6Gt}Cx@Dzuv!r5iSpQ>DogqqzP#)_(LMt&agwEt5g)>=GLDp(q z;BuCg?JdPnKW?$u`75%cqDDc%)HT82Wc)LMZPp`tG-4$grgK-nl84ABt}2-nijJIA z#dU)%8ax;xZEw*i$y1#PLa)fTa{q$I48{W{w&^PK`xcI$$5yf6ZHml4hl~z z;@+#qwN*A2n7jk8Kv>nmDqBU#j_iw;r9N zx1fSfj#@5=4C?H9s?r`pWKI2N!)-Z7(vYH}C0cit!V~J~9iy_Oh;%c7Bm!xw4NIQA zg7kRbAbB+#9kNle!TryXT9$9b%5;Y7cesm|eG+#xR`*jUjpkqu`lAAD1Ym|0&Cfzh zXt?`qRi$IJNp)&ep2h95AAXzqW^QF8Lv}ijYStC=m0L$b@l`)d4^OJoag((oDJh-NT6LEnbI62JO`dmj*RIuEz zu6J{fTy~vB>w6u+&@U^W?%BDZb^pi6cAcXpwwa?2ETT9¬y&!vgp%0#i=>{l)HazHs^C_%mre0Dxn~??RKso?N2Q0Bx%9nI<(evg1ku-t z(k=g{-`xES)#2#Jkj?Ih`}5FsgEiHpOXAl>oa-Phx0)m2nte-ZoWo_sEN;?&-Th*$ znRG3{)5f=sP;%hpKay*-p2?(I$6ZS_o&({EfiiDki^e&Oy#C|5xaGjQC%$Ri+#~do z`^?WwwND9wslVw6dQAICB|4sk93m^xF#>6t_Di!-hdtMi;oCdr8&V5~t60hUx;kx7 z>)_s|h3WyfTC+u|z*oeoVsUd=nhsH}pnvG)yBc42PkEVCS}p5leocRr>4%^=A!iUG zZAwbgkBjDUX#;j{75h%b79~=5k;#kTMe7nP_Jcr-2|EK~A@A;>uyeZ>1@2dusLvWn zxm$6phUNe}BOq+jyle8%SSCdulS3EsuS5x{Ny=R&ph9VEnOy}lX6?l7F8@k$^@XB= z9f@r97_1D_2VT93XXX!cpKk7)8`H<1Z4X^$$q;*$*v`}-bljk$UaH#MPAk4UU)P!-9j@qG1e5t8xo@`>{fBK|I$sCzO= zilm`G5s{epAMR4bpy|hnNC{O-vNcPuIsIO*2O|ek58iumVN?kX_Ylb=A+FcW9Y^Yg z$>O{yqQt=7UdY!$xo?)$WVv*7bJyw1SbzGy=AhG>T?NkCHSXyA%}WabC3fZePg|pE z?OViD;Bv1VYd<0h#s05A|6Tv|uM%k{Yzo@U>vvoq-8Ou+p1O@)KLeTqKwRi1apnzLA=fTaw|hi7p=+21%uU_=WX zdcVW;9*GP_h!oEm-)Bm2I=ZGNZY=giOSwveHdW_khpUXfPUt?J+jYNSybjeZWvrMi zLzFNKk)^p%tzKIJhAZl+VKaUWe}s$=ZVj$4|Jt+sOW5-N#VsP3(UWu`GxzveR|PH( z5}$2dzxb?@RDNZ1Ukz7&#s&llGb%sH%(CWe-;yM-BnI9}!>nbLI@1 zOC-c7p#GDt=RWuWT|~V}DX{y1UaG+14@DgE+Y=2YQp(X-L5{Os-If0RJD8`q*;#WB zD=|DkQ6@1I;$X-7{<#Ff!kbTXo9OCkO|Pu3Al1ACqVEM8_l8B91t}alO zY!vBmSC&xq(r9UV6awmB)sba%pBim49+rGk;exFz6xr4Wtw9IW21VbhFm)n)=t_}s zg}VtE(ap&&;jzpt-CcTCjk8J|qqk0OPdK2)Uem87Xyde?_F(8Vvl1U;?2yZ|eVG^h zP5D$>>g++O+5uJc`Hrb`L5x2C>N)D)SX9vp?KRTI;N+8HFyVBdG0}{nAqR*@$}oT2 zr{EN#9`UnASvXmU0!8|L5;vFf5)J#`Es8IRwZEwqpU|_UYd7$F#%o8-`1ywI#jx(6 zkn>zs#T5-QN%DMTW;eHOct`xeVmua{Ho8t3YvS=556oAZOThqgSLjZ82%Kl0!YF2h z2%)_?t&?wOQl~LF@)6m4+Ag5iJs+*zg|%{nfA2A>0#xFKn~@QPmr% zDD^jGsJ*J>Oe%Osw7;6ipwo4{H+oFZ*r71xNBEmQHC6q5wtrTYUpdgT*Bzf*o*}@C z3Lt6zx@?r?CdwXQU?l6WyU+X%Jd%kxC8Tr2;BYp$6Sc#I{b81nYLw^R$u@?SfONWN z9IeHZziEz1ZG+p72(~>D(xWu~$Su5Rn1#ITm92=K&A@R`uV9mj%-zY#*=TS1n+i=r zSuBu(Ixk6|Vh#4(3u9lqQxFnWv9)xFnH&-(S?+huJa5b`ek~4SG(o~MNxHIA_NVVq z7-KJZliS4lo5?|Xu zM%I`>9@dN4(Tp#ZCOakCDGOe4k-UyzF~5l31*T=p?n=aoYY(0!?0kupv>2FN>&VK$ zp)o%ytR3?g0am=O0ZH=>-syYQOEE3)G{XA=nZ0$wK@I9iN);_R{6Uy&eg z^2A&2W|(f%BBNN3V|Ic7_`27#Lt`ve?jb813)Fn>^Xy#M=7QC@5>_MI_j+|b!*Bf8 z`dilCl6TrwuJj4)u7Qm9U(fhiSnHfIs zu?Moj{WFt;W?A;cd~W0<3eTc!YOuzM5#o}YlFQ5gxFtYuUJC+91%+EG%#@O9-)^wq&9Jt1i zm6zP#zmduGtiP9O<=j5C#3wrjViwA89T(??mP_OJ3qkxw8v9vO?X}qO(zQyWwyNvq zRtUN2a(x&3HBPNa!p9Gpns%?*%6t(ZP8a7C{)iB;*8+YdIB9u*Dq)Ov54b+s%m3}) zz*ZkAPoatDuT1CQr{}&qOLgMQMBowJ3p+7k$>buw!VZ)3%Za7?(fRtJagn_JA)r;| z&SlWWC`Aj+5>b{#(v9RrEYWHAw!IALJ)vrjoxPBr$1scuqn~MQIyFo-Vdo}XYLIh)*?w+)=&LW3UZCR#O+0|O6do z$$~D7UHiG@m6~q!>g$|uLgT;9Y|7aW_cg_m4s8L z zn}CDhGF-DtE=DQ|rJqz@HEw!LTgnG}$)Bd$?HN;SOH2Pl0*u}*g1tqNQjgS-|D8}YRVMIPK2YB3j~HU8t?<#{ zLPgWKOgeo_+pBs#dJ$i?-3(baS`HT62v*(95Pc*I4z&ep7kib@q4cF`j-MRQXPjdVP{nSS6N^@@n& zv|`Ox$hWDtydF}?sgJ{mz~%JZXGestm-wjnVs^g_z0JVvfXS`&Osl-&?G3OQ)p{|6 zB6Z)mMWoCD+)@tUa(MiG39Ze3REb1YXR~xmT{$i;!=dVDPQmZ0ye?c#GaP9Ywv*)y zpei^_1ejq`qkbMxPs{`+5!h*wkPbuM3Z=i$O3nPLmTtKkGavHuH+S)ys$< z^diLmCfn*&&&o&bi`a&o7ZJt6vE0Fn}qND-~ytMw5ORE(RX=gMuSE~Xv0=QQdhiprp&U&7L zVLa80K*pJhV@5N(1A%%a2cXQ3XP6@#W-Qlx#U2?oV;PkiYnSqIVomXo7%L<=>h4Q3 zgPH8!i?6C@wCQ-C)1o4okuOa!lds0KsKy~App0XpC+4&repvBPZK68Dl{Q6oKd)%16GVE;4J{G}oPqt9QOPpQ4?jjz63U0z#*Z6K<; z2(v7T&fz4wiw6_y7GCb2(}coVu7 zz5H$=zpmTNGEYJTpSu-J9D^v8#}?8)teEIc2PIZ(bogQ;y>Sp@4ZF#GmQ9@W43;3y zy7Za6;;ZaY5w?&{n~pITWn?H}Eylp#sS-N=kpG!>Rnu%r`*(;oM;uHeZGQ;?j@77` z*k+|6WavwOL)}9yO@0yO_XPmrE@YOj)*5f4`_+rt-Y-#QFNVOAZ}jMc9O*1mIhJbk z=dEyv)swkDsOckJ_3O8?#>(!rm~7ka zc8kf9`e1W2x6ID+C)(JMULBn!(ZIIOXlwe*C?Q!EM6|=Q3dID9FEd_7sA)XroiKff7JJ=__8eP_+d(Sve zNtP9fFEg5%UKk_VTcuFx0VdQ)8;ZC~1%1^(W8NC=H)vb0stU6xen_+YJOMJ7(ygd@% z_+yxri%UtftB8+sePAod$PAG(4XR2m&?>WQK&C}+h;wwdbGJT2&idw8=J^HTz)-L- zv!?Qexu45<8d|13^2L@#1SWsdB{4ZDSnGWC;Z5%10x?OQBKh0!v2r+dmNNAoPa7i9 zH7r_{*$_P-v%%lALvL zP04HL5pCH{x5f}D3nW?bjz5qrL@Tan*ojrMQLLByd&PRpYLUC}iwpr{L4sNO6k_{=rKuLJ4%R%>PWm8@TCI{^5Ww1$MN5 zNkJztI(KW;xML@_rRwteIL^JX-H=99?l!o?g$EOwQQ$f29700~&R=IUlb*~FN;Y8u zW2d77YN-WPF8G1D;NDt=pgTF;U}Cbcoja9@cc zjeWaa$F#elO(cB}1*NNnjW;2@U_nZH*6?A52roL*z}qwa$SsL;<6 z1-tg>6=AloGyL_|aOKN4jz%kyWET;J1*U?%Mm;SZr<9F4&iI$Z$x}#iJtGz&b5U2;*DySzg?T zkU5`Z&pBsjfFAm9qno+LbbDut^loFjFHYyMQR-f6^ElOZ_`S))c5oW)u-v*-TK zttEA}jo?w^ULzz+jic-)!RXdRM07;jy% zvLTcBZtvJ>I>;A28oECFDkN3bxJX1sf`TwLPtnk|sreGNrOy6@u_X$2;yN>WB1Sm7 z(QONA0Byt`Cw*#S;=5w*7_>&X9yUH4S=s3B`yHMm6W@)BjnL$|u_pO|I+6P&JvOU# zAC#B^CQ$M_IxVK;HN$tl#>-3dO?p@cg@;eJE7 zL}r9tUw#6S>^dxw*~>bwLQ%16|3oGb8&H)xsk>`XA~P~pq)*eh?yzp{ZJ22?yZdBn zELTIj(L0G#OM3M5p?uis5L4h7QWJ3=FpB7L z4aRfBl^xez7p{CnNzMHWMBLb~L6X}L9&c%4G7Gxtr?k_tDkEh2Gq4fe&1uouf!bU9 z8X{amem?gaj0J@{s*Sv%N6^rvh3BS(P41mGuW((H;ww}=Ob7EVNiFD7m{i~s^RgmI z+5MUOtuZI%2_44}#ZuRL4e43x2nZ86?xeZZ?kM+`?*3PQjxTFq0-*BW?lp9HCJUtgaYU@oa@ISN-{yYsjlpY7@=2{;ENw7~L}pgM zhzZnvoYGokd-HXJImvKd3Gx{4p**-{!7F<5jV_cfI6{^Axq5C2+ zN!7kQ@s-e0#12Nsv7EJDroDQz&pn||`vUj3QN8S4!oOgL5a~*dlM;v(>Gmqb-9bLK zRY@~|y;q~vK{IQ=Ez?$2J}FQ4y(4Cre+7NXJH-b$-+)KX8MtgD*>(-nkw2qAFlh)r znKicO1@1XYhxACgjoqx&N&BYvMW!3CJU)iqjxW0=nEQR`UECW#Q>daWX;p%>=VJJS zSGh7i6Bu&|yF1!rLlWpSk=PT!UKEkC5}q6Jf%#2gJTWx|6}9j2gM+<$C`J5)QP-&O_zj-?hy9dr>ioRc zs!Cjw!V-C0}Ll(;ASQoQo~Manvr+RRJJN7JT@Q^}+D%~@k7+#i$n?w*7N znNwwq>MQ&*ol*MpZAO(8*3)PR))DdOILTxJ8>kI}b2;5szL@AZPyBNT4Sn{Wd4{h_ zKR_xq&O2SD{%0^D5J@!)0Zm3{aqAO)?oF4Vnof@Zc)$FNYy?uotg5NhcZ_BoBqAb# zP1A@GagNf~=AVw01`aKuBN8+i`yDKvL=;T7oA_pr{S_stop)ooZ=?IyAzV#wG#p1{ z0wrot=h{+d3yH;E>nhM|jLrL@>824&#T4M z9CsRv3rA2C^gpxZ(op4d{+7om(4`A6WuvyPUu+NmGxVNoQ!yJs-uqk zSMU)@?nnQ&{V>h~-xoc5a_C@I0;j6kHoAU`)MCaweS>T0I#nB6LG1r%sIeyL7##<# zP!+?kybvt zcfSZ|Xu*0#Hc+Y#CD4NmGi<%0y}@ny${RKQrlYBEDB2%wrM^u7ukG(x)~iyd zF%9(0xhlhAvS+fk!vO(b8wiw!{CBMhr?|*Hn}5Mu*P4t^Al(o$ZO6*L-2puaJc;}c z1jbne4(vf-&)x_;|Gg$bRXKIn7nQCOnM_Fq_N1q&qrb6KwvnJA=S|@LIy+~kG_^^V zde}*-f&0b*TkqwO$mIbm;^jfm-x1Wk@)=mJsXbuM`>9)#+KKUHrUqii686(M zxnxCq2bh#&hvn+YI~t+7%hnjNex@7-_swB9{w8?#xze{ljk?raL->Iwj+3qhGwk$8 z#m}Ub#eDdC42RMdrJ;*L7hEjckV&fwq!R-$EXV zm4wGX@i50-ZeU8Qme}nj_noI{40^gRy@Ehh6@RQFWq;G3fK-=1tSxeX^SwB@hxZ}9 zoaVC#z~XK@J!5dUw5S9ied8qVk;$R(8mT6YmtzV6DtT7MYLOLsZYwq6Y)Rim!iklt zqP%&7R|JK^&47f$4kQAEpws$R;O|RIt;lPH;|+T0a|N;O6Wvd@h^7LYq06*{JN>Cl zW(BfyU-~F7y>s3%=^cR6=Gf1Snb6WYLJ{*IQyH78{8er4$ELTb=5|n0ujU3SwGVK6 zV>{h0paE1CzZZ`$$&{?uNxfpuWAeX|FHHNZnSF; zSAKrYzv3rgM`4rQob@l6mIs{G^|(o6wR_rLJ|H%-HLt5*D<@-&A6ddrzuT2x)jB3p z@h5Ez#(oNSeMIzPs^~pUjH$p$Y;>yg{tQxKG#rPRiKGZjyDryFN7%FEn~*DsVAKs? ziX?BE5$~X5F|)bgvDn` zFS3;h+-y@iQz$cj9wCXYcG~E56=~&vRr4tdZZ&fTn$6#?-^WiViTOQ#Ll+mGP!jD% zm`YVsgMsk)uO^j5_fXAM?LoDw-aez&DD^keFd)y@U$RRJYVJQq=^!xdYFl%Med^U5 zT0p4v{9_z9xEux{$?e5H2LxNis(-JR%s_$k^@Lf}6DFO8J)m&A2=8zHCl2+sgE`8T z*2W(`Mx1tFRmGncsr=qZyc(=D5;s0ZH2Neb!P`T4;?v zA~&d4RVd=++8ShPYm++|9DMSO8RPU<(0gx-$dWH8?;^Y_PimO=bCu=lH?w8w)YR}iKR;NO z=n>9>hlrR8Wh`|>=Tt`}A0}sS?)bx_#DyL5rEek?56dFly~;?8xTPYdjP8kDUdftj zl>IbCv@_LR2C?)}J&=4PZ;;fI9?0h$(FC$b59CqPH0f`lu(WI^nVT7|xChJc{sxw3 z3CpbnQWgA?L-C~2sW>+8t#28~k{-yvIFUk_hk77q|HD9j)dM+`^Q(l319@wQ&-c;S zKHnAH_PLSw<2>7WX72R)?&BHwjnDT1&-c4M)!+Jj!!gaUkmvWiJx}4zKJ9$#*`K+7 z3-6)4%e-d_?<|`m`Aw0LA3KeknLbwNfHS)*%nzq+aj)J-9mrE^=g1OFc$s?{$-#e( zs9blWAGQTzBK0{V#$+$9Q1lmC%=v;*naubtv9~3E>j{$0*n6A&jsBt)b@o!VH;RQQ zh)I8umdFI^&QL#=Tqar(Z@|X=-Ykha5@GzA+I{ZA^>h-+a3oO41lE^`6eOnOtth9r zbA3VA=urFwU$n!dx43VBG7gXWviF;30>8r7GdEX)6jRA$0zU;Pb?+5&T3weKGIYO6 zgJW_4`NY^5^8%Nfz+~lxXT1qT0;PaKvLHi+bE)?%VJK4^U^`pi&VaS)nlM``2`%iLj-Nv`l-^5ptrv1U3`;Urb1nZRO2O(yD; zrS^()Y2Nhzm?lN(3WF`<_BkW}Z7rchHOW(1tLoODf5SwElLjb&)7nNs@Gg=o`Iubb zd%_3qQ+0!OfBu?=&{rjPYksVfgwDdU5ivH|x+;#Hd0^>?!Tmo}o{AI|+BG~30p#aG9ua9?yz zeDxLP<8ld^$|P8YO`z`Y8@G*jjq~6=WbeLzJhj5=qs+t2$UCX88qEC5|3QS+HAxdz zi&#a~Hh*knV)d0QIr>Ellg2PoSAoi~MmltDCiyGSp_^lL=*;z>$YCEqGNtDh@0r1S z0#CwwXB(hry^1BI0(T|rk?cAW10_55%g<3N8^g$()o9+K9BQ`=fwH}Fvt0K-@9#LG z2wDt@J?1i+ril~|Q@&^D`#0v>-+TgoY$SA3E;@<~u{#YK6`T2}=fj8ywKhg*$e)ZSO-g1!cM3VGNGXz zb~tqxV!YgJ#%}U5JT5D;(eTE9kh{-^W_s}V5a#sHOkx&SbMEpS06qRoPZcJQn)rs0 z$to7(c#ymC1A3tAyhN?Pl2|(+d57>$CkVN8IzL_WcAQinjAGsJq*5edGS4C^*~$i; zsATNozHM1Qu%|7hv=p1%g98esM_TsvP$ltGwwDttlv}>zMgxY1EF*be^QTz7wXM&I zUZd^;p`r^SEVa9|OoY8CmZ4X@h>Xr-8|8(XhOqN72SU&^7~q78*KF&@{>{uz=j~Qw zU9bX`#0E{J72(2bC(w8aM!MDx`~FD{v&kJ%rNT;4G&)TBGo9gNxx0~dmU#PMxbiJ; zAM?s_<-Mt1gekn(*wD7O@iLK^%UjS-aJKFUM)NT_$&JUtxIuc6H4X0dd*=CI+>Z-^ z5%a8f2XqWu@2ltH&NEhEAWOj|dBN84?%=~D;au`jQyXT1KkOP%ebfhlYn;1Hao8;( z=`we(gH#-$?{E4gJ+a0)t-<~Ga2Yx_s+g2`6y25mc2W*|Mp3lR0RP$U$cHifh*`V z+WaB~VKfz+*OScBy4?+O{`Se?N|ZbJ#@I-e^Hz_U9%bmPh#LeGypMl*` zZ@u(#$9i6kIK1iD-qONIR=vsgpuhP6nl`?|PS(d|r|-k#E9^YZEd+OLF@4S4facx? zyTAb7!%4V6D~$ec^rH9*qao~X8c9}8LcB?Z;ZXd|{rRSzm0Ed=e*Mj#oAml5dz?wu zvwPnw>2!+?Yg>DPeeEeiTwr3v_VM~6e2YAtV6-_ztKVj$Ej2Hr6z%>!VKBeWNqhpd zqD8;4%X(Esv?QFm!$%@$Ea~M=;Yd7WraG}g(x85^NgCi6(P^XSMJk^fCw^uQ3=_v5 zH@PDZRM^<4Naa^^_7aK3e&}Z!dNjC5Iz{N^IoTG}jW5S=a+rNq4It>faPgk|K% zupJh5)^)viRGW@?$*PCWBVLfTgzxbiM(~Rq{ft6$QoPf>m%WOK>zD6KD)a=;mUwBF zqwLMk_?xy-5{R>y6!#~E8nD=NV0^$Pyy`x_mjI4I2Uvj{G(*~PDJrZypgktO;-1f9 zjrD_o$rZs{d^$a{5Aza-fbHqyOknc}vjh;=kO{1_4)JqD6O5O~?^~}1>!lExz?~M> zM#~dUUYS7L!nRwOys)F*C$79Qf$4(TNFWtvlboAz9Vf^~CQxZTKY>3bH%$J!yd9cH?LVoTUXQ_2JxNjU|s&TO?{qqDQ@?*Bx z7WTMci52QV!om1y&W;|PO_I&`Jok&Elukmy44iFl&zV`z-SW!H4S(Z|8QSFSTPdcoEIzN&O4QZeme#pylWpc>0!xG=(W8C!5RD7UJ#E1kV-D)4Qh$r zcQFOZ@FV5%Wrm(a_eGh1Z|8Q=lf=jUqqUhAxG(C3=n$SgOexFP*|kV{m6OzfW^c!i z`P<{fk#Yb7)ZE*p1rGsy?q9B>${1NDCOZO+ibpX`JbOKWiNIc;yZst3tgTA(YnNu? znv4meofOpH1Vch@7l`e`#x3__Arv9e)9cvymj$v7?$aab933Y~{B2t7wIX#ELx&?y zu%3w^IylH}p>R^6%;qZRB+Y)aAj^up^nL|)EWh%)yK{emPib&xvlh^7JD)^FY|6R8 zMU5vKb!3g@1<-DR#lNViy)lnInCE`Ws5eUqb1~`LAR2bQ1n1c6y8gwB%>7%2kr=P_ zOP}Z~`$T`RPju6+o80B&l_r%z6B*WzX6{+w?#6%i5we)0qF;E`5_XQzo)-Ips+>Ao zF?71_<6;xztF=Gjiyj|N+}PlY)k!Y)F$tS8bwzaVH)BJ~_ZOkt z7Ru)?Ar0sXt?9DPEi4p7}+51zQGu$1=GM>TWaDJ#bx@Ua#$uO;)_>8X*d*+Ki*Pc*PY#JMRfp!jqnZOp< zJ|MG6w0dGDl3Z=c_Zq-B1m?;pGbT6^y~+LE2$Y4BzpP7qf9Q21!pq=8`uoU+ZI93A z} z=*P~lDmUoI&fqRQUEpiV-SvgJLLED24=#jG5k|~VbP$&Fw^7tCtX(^8e0Ut`(TF^R z{J49iSV^%IsVl(PHD)f~Vj~UA3WUt|+9Vxuft}Z$9ct=EH9{l{TMW#rC2e~IWsE{L zF_`_WUabxQnpW-k`N8qi>i|C195GbcxJdb=Zq_TbY!K*Y?d^p8MQ}LrIG|el@(&Nj zfrc0QhldyP5nKdhksyx&c}$QtAZ>zd2C`X@Z9ujOvK`3w8kY4B<09qXG|+^S_tcr@ z-ryhZbH6P`>OS#(7c@a&dkx&+?_+uL!b@X62|0B(XDY>vv<^CO|6U0cyNg}0u zi>uRU%B_r{PS6#57oAaJ4qdR$uDRdHRd^5o8K^KIwF|-qTd;fjWNIp=0eKYE9a_M5oAd41Tq#U?YH z810%K$Av5Qk;LSTB3!XAy7ppra;LE)!5~GIw8cYZflX1`#6KKmgo(04Q4AWplXq_x z%C#r=QAzrkXhX_9wS;?m)|t)sYC&vTR48)!$aHi|h2=F}2Qs=#fsrdct)i zwh&Bx0EtxGR9eA#242ND@Zk3VpV)S3N8&G{nW%m==nb$V5#ei+qIDz~BtAN?5?W+l zx==6CZ6OMdi`iGNB3`YDzYzh+55KL6hzKX5bNiakt2Ghjw_|E?xp`HY{HBKWYERrv zq&yIEaSEa5aI0YLiJ*Ks5-;+42+xj$HeuN00>aDz8x&$VZm5(#`n{T&e0;ei`GGTai7}|nV!@gY{n>Kf#xOoWq63!w<09z{9KwGJ$so2+8dEB)IU+0Gy88xbu(= zC-?6G;cYvlDZfV*w(8WzuGYcj&M&C+=;7s#6f$(BvY80|OV-;3LZYEemqxYvEj80h z5f00Sj-4S?=5J@CvXdH^R&Lhb(c$F@JFO*F%%TAOV$bDf*C1jksp%U5lz14$8`_oM zJ}|F6Aq0@DTg1H~)TaSOg*hNEFe=~*4_{MAw9F$ZsE&!4`04}_#CSf z+@002{U^H~-cyvpzc5@eSRA)UO{gF=pw=0Nx6|E@=wT^MF4~-3ApZb;E|ctWyE(fu z{9tCz1;-Zh+cbvX#M22awZ^ zy#SL0nB5C-8~{WYL%rUfJ`2CZAJp^zoeuX3r(-Ya<9Y!^E7Aw`0_emlDWU4uKtXUY zt2|<&`3SXImC1rzW!#SaoopGU1(tqC0H=`tF`m7Sz1ab1UPADWgp>>DR=)H~LU>6C zmi{a5n+0&=>u`u)|oBmNSrHKBm2@rd&N6J34McIlPns?3-EC-K!pHXdjTp1 z=oCObgCXimFE_42H}Do)QQHP46OuKhAMzqK{nqSDsD4#p*{W~GyJjX9*j9)Sy76F! zvf*wlzt+Gj~I{SBl1Ajpz!HwO5MN2H!iQ$u>0v_yzJCH$Ed{Pvia}J zhiM_QR#)&a@S6NPlsYI17HwmL&A3W`3M843kT<=}cos3I4@o}qfl9Rzh|W3nSH{0a zuCZfnQFK7!y?-ZO=$Cl!Rp%w~h0dDH8tFax6FW!G{bDJ3`qFnR)jLcYx0qDPV}i!b zT)DhnAXWPG62lrK%e!8H z1OVGZx2Z*=+0d?Bmu6j`$TdCd+NKIPS+4B3>Xd(Z*59}e?d>lnAYGjGH?F~1m)L^z zxBe^|K2^u3IJ4!{-9qVq3V`X@bKl70+(4pOaXLNN0l<=p{RDd2fN&M5QUBB{6$$Ip zzqZ~w%&Eh)`^?b%vcY}NK{Wlis%LMqn0BZj$<+?NLnl0CS<0^DNhxJS*I+@PXbFu+mlOWvq?%OlAjDC|CjT}>N7<&Ar=+!P_}-Zcz2 zNM+q0%T2_r`wh8i)vWskx#6#}ZYfQyW@62{|3_}ESH1l1m3wfH`xd!}^tf-p?R*D; z5wcg|FqKvY@0Pw;F0b65$ote@fW6eyCTGJJ%XLK7wVzyLdIc^Opr{w%5CJknd(uBt zfX@Kbur5lP>s`|C;B4GA{#0=gPf!GZQkd-+Q60CrRd`Dr%6qGCo4aMo}KP15C`K`$wdUBDDLZv^Xoo%}X6=so-;}qG4Bg}A&|Bczx&Fig<_nN~M4nbD>@0<5eJJ#% z<iPM74TKABBKn5iiV!QWq(RbF*t-~WN9ZV7^^%9uy0|~gJdJ_1y zcLIJTaGg!y-)CA3x_)Lbuy$SCmB+Jje6|honipcd4KYlU$Yc)OrH_$+#Uny6ws+`p zYrj#x?{HekD>sG91P1QRcI9Zyix9msTO1Yj1Yw#Qwl^Eap{4XD%Kf^#l_If^s^leT zDJ5mSOX&TBBB|4cgxL8*jb@VsANOMnEt{ZQX39;zH+6HT*#gKUBgG6M_P+$03&?h~ zd(@J3m4-lEnuBf^^mq%^ozHfpY!)LB$~hmC^Qc^$HpLl09JZ+R&FC{TtIp!5hvZ^) zD%Q79at6$9+h;F-t)cu#8Rccb*zGe77@eJW|8l%HVC1516)OD{>9Em&(YAU)Q+H^W z+1+@7Sceku+YXFR8icsE%b^Ube5neTIkt1=wpH&VxcD*sKG%4uq@{+mvc{L)n62#gvDs}lVMVqL^q~sVR?BlxMR9Ml z(1&u+xS$OddRq?Ktv33Jg*F(dP+F`exh&^ggp+aWZ$1Z5Ch*9?w2DMoh?f*Fmwg{+VDez5cU4{Qea zBMVY6sV1K^FC8(S4q_p7PoKr)w`P~$Y%BLlWR2NTHc-?GOC+Tk5>B2n3%Mty6PhN$ zidm&twQ(#hAH&T8Sq~>%8crH;7E&>*Oo~{+ayne6#*CMQ6Gq_bs4y0vQ@|(vBmwL| z>Q-ewWSr`KkAc10aIC5E+j7vQg8sxpwK%p>XRG60mUG7CoRM=blvAorwlgeJQ;xPt z9Fv2p^SOg86rE!);VnSZnx$>jRt5Ut7=zO;Qnk>9f^M|X4|33Z1YK&O8x2%F2FBf& z7F%r*m$GE~v7EDzQI`IlmjtuWpo3ru-SmPU$kzuN2#2Ul=7jmsLL}o8IzV2vkn;?L zOdCu15|c0r(e)nVI5zfYSiY=B`bfSl4!Q%e`LuvgKYEqWi#7qDm?dmU~r}lvq+~GmYG%!aYY)D!TEi>*Muj&kG7G zH15qgOXYNO&T^dD(zs(O%_BiUk_maFb-v15#t57~GZ(K=&Z?Yqk(^-bwZvjMscz4y zHc9WDbJojQm~+mOb0;<7wW6FK=bWu_zL|5D$@yZ=xm{gqWzN|x=ihQppDO9Wtg~^O z;x5G5`0+zDNOT?w5LYKY&W|3L_;^%wFD{*FzTi`x4pD1%6S(VA`@LP?Gin?}Mazo# z_NTfDhj6xy{J4KbX&D`MPr4YuD}@jK5;FdWo|61$ex z8W1;$A5)U&o60koXB5xDJh$^K;<@fedA=Duvv|JQH_tbeL+3x@spdI{=Vv@={D$!C z$5YAk2c8<9MxL>w^L&N-=lO>7{)qIR2X;SkULdWHdA9P*1b!J|zUIA<_j1_b)Jj0s0+SILJ6=3?4Fc*dG3(;WPFe zu~)zkY~qh`$5hp&D2oAIL=Gxi@nV@Ao0F=J=UIAENj9C*;d{Fcr*+qC@h08J?T@j_{0tEY|a!&-Y^9T|BFJf;?ByN7nIN zN%+b5=RpaE^BxE6RA9IBbo0H(_&nb!JSm<5Y^XB_Fl{StT_qde4?YV-Q#_pdD& zP|F{EXD~0*acYZ(*X}9ch`nk9diVJoDc9b`wWIXjM?bau?x)`$?O$6vx@62)J`boJ zS9@S>?LoB%mmVT7zU|+k*$=(P*VaxrtQX+$+9P_o_$)iJw)UurN7vRKqb`^AJHE2E zcGBb%YHLq45IUZDPpz#o@9NsxX(xqiPR4OcZG^KR{f=OqQ~ob`KHyQ?Tnns*XFAW% zc$#_ch`2){-;+ZkH2BgyW1Cj`L*74>)x|F8X66#h`jLK9%qawxvf-S^Ay#r568)PV+ZQ8a+s zUv?j|XJ?j^YwZb+$1}|&RtK4FI7JHI*a-x?{AuQyj)WZgtA-UFdz;!gT+R*^h*aC<^qs3eWP5zNp>D={eskt*>@xT8+L2LA7_&n&5D*;Mia}?p3mSP<2$JEQ)X=xRz$*E{8wk))t!Eja&(R5}8u5v{A`y%C!nzwb;S(iZ` zn=C>~c%dfYeP#mBf2dyL2$4)+sd1nn@hBfznqhM2>C%jsQgwk(wIIr!0(?nAvN-QfN2ACrUT3b9(vz&ka`^UxUhlV zW}#YgvMC_XOyC9!n`L40$^@s1U`GuU|mQ*2S0hC|7T&_Y&dywpSy**7A7z3fLIu7Eya}= zdQKMBZDI1t1a7e~t+NOxuT0=t3#->iPp*(xCh!vrTdZD2UEzful!bL#n7lH9<1NgL z*5nF#asJ!F?C2)8Jl*SAruGc-E3C_q63n?MfsGK0wL-W*+9g>xL;OYg zp<8K`lX}cSy=cz+oqeW^7};^`d_qU77d62Bl=A$J-_Xtufad{?zjWb8&No{=C~<`FN-PcK)obxQB4aVol7s$Dkk)T;<=O|lyQ z7R8WpU4~m#`O(E#Tyz!`B8o9K)WVfp!u}~+GJ$*w0(PlW`f*)?ym{H3h*i0|&OyEg8NUu*?EA}-QTXeYwVS6TUtKvJg+pvFPzuZiqfiGhy z;OBfmp%c&5=ZtxskAMo~W-$T#fyqu}%)UeMv;v9DNbkj$Gq{m70Gr^zHCV_NJJ?+&6??920rb(+> zjVIdKN~OI;Az{hpFCuC9&!A+Cdn{+ek|aGd0*yuFLxW)Yri>&XGm{`T=D6pObg3mn zR#dho)N88BmxvR<4vaP;H@R=3;bDt%1-`_$e8>9eSm?X(G94+lm-ehofi}6*G2)jl zH=*wq$0SWORB>-pYq*tkJ`QO#-;YD~8S&WYwi7CQ4tpUU#t6hbilrJ8TOIp_j_O;C z&Ko8skgF;f|8Gl^dWS4*y@aw!VwGWbt}Uu+T_XObeZJVBJX9XDDMj;Rds9N4yyK#( zRKfl50n~u9X`insdD%Dzn4T4|8C=?7Qc@sQ4lkuZ6=&-`FIp5|rTDO7){{mt^|vBd zf8)V0%TuAV>(tW!o_R3xWsDlBt8?G&e>u^3kE&3p|_dPFWAtt za-kVA=h@JLWdax2(A!Pu`v~1W{|2F(SS<}OU-XoSv%nP@warA&-iB>L*Zt>@wNjW@ zj3He+m;(1!3gu1iQJ16fzn8!Dv|M7;o!G>@2qQM)kbrZxtJdAJf3^#i3QZs%m1H3! z1xXdWcBNj!_SY+U+h#LY&EIBTw{5X5SFdb4ve23beoW#UGj*lrn8}aB$rIY=YnemU zFR+J${^5S|FA?BPZq*pYrqeJG=yImT*j2Q-q+CA14R>)+_BI~NNuEo|>Ze5RA zSDAG!wXRa@YQyC|@r6Cz^v*8s^o(&o4eLZ($}=cXCltip^y>GS$u5ECj`6Ah3r)+v z60)zmuTX&?W}+mg?-gGyMM{n-##b+8^TEfNS$?5v*uP`4v7Ps7>@({`RU=;U8>bWh z!ft=FIo@SYi=@nH5w1KY0IOilgG`m06?IG{l3!D7Z_)Bji;O(p_MII+6HbZQ6faUy zU+7P)Ht`K|k%|TY68=I`e&j`X-bC=qi~UkswYK>Kt{S8{4^|S_P~y#M=<`)&8bA^~ zYvaCC2k+0u^vqFja=T>|?Z8|%*Kl5TlPd)yr4z`7KA6x=99#2{DE5#D8YDKlE;U-y zQBC^T<2jF?blv~Q*}1?+SzP^pbAteh8^l$-M?^*O5{=eCP2Z+ z7PRlb|34o#&-2WjIWu$S%*>fHXU>=zmGd(jyD=$uKRQ%o;LOJJFEBu+lV;X41mkd7 zOX>=rTVdwfPcgSq{Y&oXkpj`U#4Wq&>gx%05Ank61x}RBt$5$g8tY7Qdy*|Z70+l+gMaZr7P`me z&x@}_asTTx@1I41els45z7bf<2{BIOUOUwBqA5z6|P&%JpF260J9Cx1$$8Sq5cb^rOC5FD9KB7(C4Q+|K z8;0_1E)Hj9(oz;29>2XI6w&39#t!|XU`)2kvBB=$|6Rgby*GO*!oef7atpqnIN8aq zwmR2WhldE);m~^h7S3;Nlrl#?dBRWOk@gg?2YbdNi))5FkC6Mq3?8EtqC_aFFZ)e3{2z z-J8GTDX-r-w7X3w?tI;Cv-C)q8~f0)`H)Dt3&7emF%a2Ia`^sML+U1Dex~x*k>mQs)>_6%mj5td*EOQ|%!i@XFjT+lw7%?ED`U{lF^4K4G zcw?lRqSk6P>unu8a^}`Vobxb0ij7_nan4TM9`WfWi|-p1AWP9D8}{ZJ*WF&>;2vpL z#JPi$MY;I%7UfinGXA*Tuc8U3Xkk1}Yn7QT$XxP!Uv%p3^U6;tKMtWxsDIo2J6V|= zD33Z)F7^e(S@~iy;1-H?46vcb?%M~jnu$2?L)6NI`Z99v_OzkC|${rKIc`wbyME){r<#tOOEqbcini?!721roT}@o)Cpi* z$p)J1B;itkjqaUD{!2_#L!>$tsX-+Tic)z;SWZ){<1X2p#?~ZOnrSsw*>Trq1^khV zLnNg0&;W`*o5xjy7N-?YC{M}2?k#I2>dQ{xrg;nExxiPZQP zmoU~H9Z9#8j7-EU&1d{y@_zi)(^SIQ_5OQADIg>*}aO2oq;H+|W|D6-lmLpl(CAfgt6YjErh z*}m9WI!|8LlAa92+s}YQv$5t3oKK=YdtE_#d4U&eZ8)E-<$LCMZqG1|Z8o^?y@UU- z^3#C)izyi$o_Wh)I}PU;-0_;etYnh2RvxXpffZaQXqvjqPv!&QZvz}nohJ4p_ncB( zLJC$%o80?N3fnND?(%av77`{dj5w{NT7=dnyH1G z*#id|9>YgHE3fBDsinlMKdNr&>QC?v5?aC_Mpn;8(zD9k_kuaNKXyN$)a4Hn%!fFE2*u{&F86CC#1(&`vgRkvV z5QIJB9O(UJK~n&x;b|q_d0e+S9UFPLsn$$0!f%#C=sI2NzR&_kMnA8s($x~t)WarJ zJ*Ccc7yPs7x~IR`V9lQLa^>$p<4r-#$#DFCY@$?XS)!?5OBW}R^i0@3uV1DAH#dwN zNdG^_NAzsQ@%bYYAYhtiy#n{RPaa7Ps;S_WRZ_CvDTJwcLe|o_HtyLJ-$;fDg3$jF zPiFcMHRjeYh`*sz;OCRq^G+q&} z40j$2x~#I9?PYT~*%)2AQTtWo=(-FrhqJcOEh3$@_9Btr6P}vqQrzr$iE~ejAGAb_ zzaN#no4euAm3u83R5-OxTC)&i@_^JmLSPmew{4)3G9Q}PtDuy3!D#rOA!qG=Sna%- z|Eu|&tI6ATFwudkVyY0;ua!ryS`)%fCzruQM;?>E4F(q@wLZ7uEeXE!M`F%_xM=T* z=_U=I?1?JUG3K7q{cuXfNX+84T;R9V1xV~Rl;M4Iskne}M?2pnw|6$c*B%_DASYxY z+XJLq?>USh%koq@?)f#w3)}q$LqFh80Q7tTS?Y*|txq7jm8l>y_i&>lcz}C7K-;!f zlqksC!AmZav^@*bggEhINE4Q`ctncbo~L-D*n$OWPf}+&<5R3MIGfYR{zK~(@iQYi zt2wcXm2~UW&bw|Thg=}@iKwL22Kt@cnr=Em@El{6R;=v|VZ9N5Gxzm{VEdTAB$l9E+fOf(Bd)CWx+|#Ym>Ns?j0jtBMq@2n5#bxxxl^bIe38L ztr$~Ei&cdumrL1K$`&c*Z-|V>LugdVw9rVm=ETl*?u$lWbUl;TPWsmAl3Q-h_bB=F z2zB3e3yo94^LjnqG3Wri11+KSEVS0TE=yF|+H2~X3v4nkQ59~jd7X;*;Y|VF5(B@a znj6-%NVNGWKrJbRabaSDJT;&H-_-k`JXotXElw2|7tJKuq|}+HR#iJ~ zgJL3}^{qMYyxv_w`I1tA)}x*Kyf{zeD{tnm%r=P{R%Q4tmag91Bpg{ARGBK7LBDVRlLU zD1^&Wgv$jjxvueMfwGG?GsS%ui~#!EWf&o~;rd+Q0K%k?lYsbhf?+`t2OH`l{_y6{ zqPP(7%(sD~^GsrXqexz*@|8J7#iMJ~<^o$j()e2HPlrh`$>}J9hD1l$T0=JjR&tx@ zc8F3#wS0?6iKs@Am`V(+}9`0iN9t zcw0Z<3;@A4n~+GlI>w`;i>gKa;n%Vi(eziif)tAV(p@D!yNQI*LMz6(z1YO( z0`){$eO>H)EyHp>iE-z$UZs6?UfDKonJ>PJ?Y|PkBhN<4SQB2SbOf;kl0xsj*N_6r zPQxxLTP-J{Lzb`$hCZ$=^ms|4@x1u){|mlc;3$xhI(vO$g^n8f;$P9gQ2T<4)@QhG z1); zTkBPin@i#!Mw|}yJNAk2Ta_4B2@zT>J3^O!c^>*SXg!ak!w9B2@q?sQ$#sPiSyNUtwS@$=5Csq_D zJ2MpI0#|@3(qqa88eeyZ0iY|iBlOsdE8K75yCs@hA>1||4IYD4l$j3`EvAANhw*-c zMzmV@t7y2jmifdG!tg?3%;k0)FWq`vE^znRvGF%*I4?qvtkA;DgR`=6G=H; zrY_>ApuJU*>Yz0$k=7{K$blEf$b_QnT3fuQNj&WVh{LscB7x3)a#K+0?F*D#|qXRWiB zfZd3|&V3?SKX#L+&1B6wlS%By;Ep|Pksc1xE)s}8J*4x6lpaFCC9l%jt=NXON4FqN>7nu)(oEf*6+WE^;4?Xr2iQEmnWcrwGeSjx2nUGp`1z$#3ig`sm^)&%j)!E zq}D0)3g&C2aPEc3={i@T0gW^3nhw+&*{pZ4b($Q&Y>!ktEmI_A2!U(O;Ol?*s!94Yy zk(4PF_fps}ZIjT&Qi~Mn{({nxiMCKMZPAxJ~R`ZLLO`BUM zjs&KY6^DPB3lhqWV zHVE-wr*)Q;=%39%_-k1DNb^`%{}PUe%*tmsieX~tdsA7Kq5K%bVR`lInMOiHkXetw z&jTx?E5fhyHT$;~Yy#wQ=4yO!I~nUT~0?c-_pM595!qe2xSPp?CbO_#IKFz<4c z^s{dBexdNRqbWpsl^THAp%UNgpWC&SQ3G#Xyt7zg&^3FZJ?6?Y!9<)^HZP2q)lIoA z)bcD3B>99(wC}b-PH>jhJSCsd80otWS6A~Mf-n7#M`oq&QP{t49@axNOf7qNKJ^jV zLAH(33W%HnoS^DMh>llvwjHW4TKDobiKX&}Y}_HIYK_3PYMF?3pG~$Uh8a%vWy4v+ zzs7PJy{6*`33?)dq0(}C=^;vK&dUiI?H?bM4jpbY=uRp#+1#7_ig;D8hW`8S4;9wc zTp1X3ex{vJJb*?VBmH&bpz9@OfYQS;8cR2^C%=j1cY4KR)WSWIqY)B=en%1_%Yb|R zFjZ%hTk1t=bCW1ty0YGjdXs}CN)@+Jl|-EhcBOjSi)shEhwwNr>VHgB{04%R;y^F> zM>hDf^d;#2kx_pxWt*n!dfKpIN(t^zxjQv6c5J+?yR=qa;js9>qR8>6WexxbSD3Uo zPrJunV0%SoEjb%4LgqP&=cz8idx25nNf?cthm_wRak`1vs)!@dTbYQUOv_mK1#Dqr zWeT8sRRkN(HdoR=eW>cY!TqEercIyoSAC*QA- zLYk`vRCFtlPHz$SZ_-}GEH;qkWmvX$5NfHH)iYuFkIis6tf*G8jAmU^tIPMP)#WC_ z)T&|sTAgR&q;XJ>tm24}sAy`;jm@UUFT&HR&R|>PH}t6*Yqg^qn|Q{e z3NAcSz*&PnRM89xzOOd13jt5LiA!B{UXbE<;3*@S+@&+=AH=5kB-sEhKQeVP$wB;TN!DZcM~YVmHO?N2r45-5$nK{PAaaGb5# z)@o$fP4%n*u3QtD4umz7PFGE$8R*r2SuCm;`d{c@d~np+*uEu(TiD)~Wh*qG<#Gej zkiQyA^o^Y}V3P5Cl^*|#amUa31rX`myu!7zTx5ko7chH-BvFniD+S$8u@26YS z%9ll^CtYG9W0C^CZ2|+C;-$$pAZQJ$lU3_GflPs7{{=eYEuckW^yVnhC$x* z*Hnz>nW`v<9vHiXOyJOpxBo1omx}jzVE1wgp`8_P@)1c_Y~g`_)^x>h{-hW0A9}Bd z>5moT1!i{!>IlgN4n=KlHUq@|=Nc6~N)!Xkp31O)ur5WK_r)-WK~u|qD0m~zd@hWq zMl6SNEWNsw4$=`kt?Sw~|hmCs8zqme_ z*n1qp1lLL5{AK^*iEIP!rLquVB3~@yciOLW%x;l(E<}J?X!K?gQp%hTKEA#Q@os{S zzX6OAw*7WT!&{@i%^Z73iOV*)g6?w)Fg zpJ=MCC3OQ+chsTq>d4dNM0xH$m6Oakei7T!lGU<*g^t?a=s!R5Vl%aL^O*EDI6Z%6xd(RcUe z7Wfwri{v(>$3Gi(-b(geH0PMeLV9w0`Lr8Z!aMWZr|@hkdph^p_|AjQrqbDQEyU{0{Pi>Y5rvb#(q>v%iSx$-(cBny7tzY_Z||M}Uc;)@gP525HH z=FQXJUiDLpP9Nrl`n1Bo=ihJt4`pOOKw~IMSF$in>gYwpm!fy3fAQ#G;?w=ObjlmW zTzY~1DsU;AF=#R1%W3;?>D6%QHE?Me;Be_*oMJ9@$9JB5uC{CqYsOQ$$OGP^6j1zJ zW1@*=#zuDnsY0ppk+RIN%!00;{gk42k1Xa+>}Tqq%uL5xnlSs#3`WpqV^Ti8CNgGx zQ`*TkaA_nr9z&Q_Yv5N#t$ul1=O?(N>P1P5W0%%K9?rcy2H)VUSkfx4fYr1l zD_V=-*tkZZJS%zlvL{Czq%ZkTMcv)F`lcfyWm|HA0m1|SaRU`LEKN0$GKOIu&O~65 zea5lNtYoZlg8s#(&fbn<)OoojHLgHp0D7aNnjbtHE}PI7Go zv!Lq69a_~~^#_V;LTm~qX)_|9O}LSpBmLt&n?W_kKFC-KQMOT0hYh(vJqlYh<-08u z18mk~tg#o-4pLDfJDsMPgmZ!LHj+^y#KXW&-q{6b6K}m+Zjlt-jBXhq;;fCPg2SUJ zoUTBZNGh^5l4|9o1>fnwUYXFR3F--MQ}ehhSTo4(R~gVAM@JeV1-Edhe$MLdP|mBj zXo7%7O+*Hd*Yq$&HB30hw&McKaQL~o;+j0TSUFi z52xb%*FQ5$eRiUb3w}t^K7 zcbvcDaX@6`a~whiQwpJ(hY8AcW3(J{cBkUC&Cb6#w}hIVhHXSXfr_E@WGqxJFA1fq z?hdEVv~4=lfW)_jQ*ox#T`|8Jc5s9ZD3vkG#4xEh?Cer(<44W@3-{LVYQ}!`6s8y* zcz7FPhMj9`qv^ALl-ipswilg*RYTy4>U?YCH~%HWJ{-chqBpsTVQWp9DR~+k$_DT= ziom}PxIp2!n&hTn0sKn9sSWi`x(pxS>idd+o)Nx{h%{jBEk;DTYXQ#|Hb`DeZYs%) zB2{uzX{J`Gmwi;3+*F=9$b?m9s!Z6sRQ)8*%Jj+AVf1XiCm=-9TcB^TDWG&B@vh`|#N*q3g zmO*H(vN@L=Y)K#VbU1Zj=39gjR}|V)3M=D+>vJMl=i!3zb0Q?Cn>Gd&WEr#%Iukj> zrXq6iXCvPsIVsyV`Ive>8@;Izjrq)Hqowh(`40VToOVA~dv^aWL^ZE>LjWMANxp77H~Cgi+KofAhK zX)0++vCQbIjHa6FQ0w?RPBjO^n%O>Oj?fY#C>=z^ZW~EEJZbu-=8l~I?scMGy0vdo zIy|zbx$$qe4GSgbRt{p=n@%BD`Il`QG#eFaf9!?m%J&I4SD>}A0}W_Z5#({o7HBxSlzL?Y2I4)(d{l;d zf;+`;q1VRn?%XD*k@{zrh3#QB(n4KE zhi~w^oqR$(r}5jw`+Yob*?D+-qpHwd$v(O!wBjK-WIv$0#CW(EWygnG~xO7LW@*`MEylk4uc6#?2&yVBg9sB>0L9=H&!HI4EVzcvnsO@!M5arx~7fk=+c9!#8 zI#G48Gxh0d*@5F7LYx7dgVsJKS!cG{_14I60|MbruIKd%0 zTOMK^INphFZ*6uqhE`?DLfo7hhNeN?)I1zjpPp5cMR*Qlyw-Cq{E^&U=I=nz^eqt? zBk2b(^6Qaqr#il3AJ_0mb$p$_z}%=XqizXWeG6{WJ_3(?WLJ9e4qDMW{zjUL1o3J_qc6}IbznNl7&`=HROcLs)bJs(Q^vo@62 z*o?=5>`{suwtv+5io%3-#(U%aHDBC54HS{|_e4?XJBtU!o6+LGeRD4G;1*fID2&eV zRdtL9_2F;7sX{!?omvH+ZYKmxhC$=oGrIEj0(d|)3dU+|>ggPCtIRYKWy)WbVlW45 z=0rlw8^Ja4fvJYfA%x7}>E}7wxfL|jWa#YMEL8HJpNb${aDZ5oPro#M82;` zD9)|V^>n;+$95E9n~DnhlA?MLUj6Nl8YuQS?*2J%`{Vdy5spk7A1cG(*_ELcbVOfv zoIZ!l9?4*FSt$cax_S-|1yqjTPW~N;rswHSw}! zwzRopd%TL4D2)%`Z%M2a49?zA^3C%27*((GrZb@Y$r{!Q@}o5Ebygd+KF0`ZhPiJt zvzaD%O=Y0Mv~}#-rKgB`*uHM3_#ywowM6;b-$8I}`RLWmKY5x4C(_?O1JW3ZM$>_R z`9$Q3136U^^^e~iS}L+xDn}W#8_ci**32WIQ1N2}myT9eLrg7(o6?zb=9z?ZJCqOI zP0$y+RMj^Z;>Ir&Bh(4GH}L)jI5Ci#cJ{$brxZn{h(eK?J$&ihqR`o1XoW$DWD-Bc z5N`JI5U$qP^Lbh1xMk4J31L$h@oR29^NBb>8oGbN=}epX>MHSdl@(HzCC=mOaKmqiWxoJy^C}`TA2D zECbKRGSJL+qhD!u{uxScuZ$g{l8hjIp`Oe2(IaBSM?s7crhfh9Ex<`qH{1<9WVJPt zab_U>4I`jKnV(4^P#im{{9mJy@XGPB*33tORa5)E-6b}`AbYdWQ8;gBOJ*q)J3iMF zM(VAVs)mD+tI&gf8B+{C5D79JALVkmfzFmPOgL%DGujLK-k*_m+SW?$$)8C>L3{=K zQJ$7p8eS_ec~FIH80X8MBzhgBq44}Vawu^8M ztXCPP9v#`mGZ*N3Qo2qZD2&(U0&nrf(%&)j=EktoP@AY8w`@by+Wd^iRKE@ z==0;Ou>WY&C5g?R0D|8d1Z><~qzKB%hDuj_bS{vxfIlK0?3CAi6>s_G4B}BlEuruE zG9@;zUs@z`0kqkawOonqmMwXnYf;^!Je$dLaX!z#_u4$MxnWC!{e27g-vT@$hcrOL zVie<*=%-HuoeuO8>y>CyzC`muWJ~m447P?fLQ9UJDAkZ_mTWvX?DWd_$0p8+kyhI%C(+EgUVr;DfSrT>i?v)CIA$!!c<@^mQo1M4(l>p2A1*yE7r0K)5P9>T2@WF5?YslJQFstzOYiHJg&Og`Z=wA0wT z_81{~wi)OB?MafcEr0?v7+O*iRCXMV&I8|XfhVx2L<;2C#B*@5dnMh;FlxKjX2CPw z&ZsQ^9?N8dC0-5Wa#R4m%mIKSACC-r2&)SSKOkBNWgn!;i3J8dTBzI2z9`MnNfZq@ z)3heumKHq4shBfQfSAUV6wcU);Nfw``^TCB+H8M7%3T&gN$guf9}#(Gg_erWlVpKw zx@fICb)?Kz*g1^NKrd85iLzH{e}Nn2yY4Wblf9yUH(+UDff}?&FeQnr#KUwoqpYwu zx%bfDO?XdG`_}m))CT>F-_}^-Lm>tOaNvX0#*wb?p1pLhA3OjkJm_A1x6ZHMrP$jJ z(CwT@l0`W4BeMk%7(!LmCnt``<(8YBe(2RI{#Iuho(tS$yVCoJ3BYN5*aoO!{;SrB z69n{2-k{q%{XokB`i!p{Dw8+tFwEpOpGM`;SKIEc!^38%;>=AKbES74D~=b{r#-ZQSa zpU1M6ILh+TCwVLnS}b+@Q)nG(NGRlQ|Cv(kPl@auAX!#GGQ}b}Gn#gOOMY^YV@eeG zsR6On;e6y8Ly`D5dy?&5fwEr!(U<%3rSV%tXNwCSGSzs{P_cx|u~UmGNu&oCGvZqU ztH_+c<2FR@>;?AJ82gZzT?MXDrr2ry{HR=RWgS?uhuDZ?yoi({PFKCW)^DJ)UhC_) zJ$Z}R^r20U5|PGv3*R_z8P(!k$03S0ip)W^U(~i^v*4YIo%a@ayvu~VV2o&%YPiV- z4l{#*IV|yq-6a|}wf6qxNGfm=dUz`8e;fuHR~oOHvC6H)#Z5V;3p!h|5OBe62G-2E zDx=APGLEK<^m7$KZV2=CoG&cfjBlW&gp&2yDwsLGg`MSi&(2caIJH1ssprmKVD&GO zzEf6Ak<^__24~;*|#^aKi(%3LA<~}<- z=FDO7(|gK?sZ`lfPL)EB38&5+mOZ?u-N35_II+TXyKE1y?#m;@9qro+!|t zCG>}b==|`QKMnJtP>!D?WW1Pc>QoXwfH*FkvJf^q(idg-sj?eL7;<=WO*t2Q`{&D> zskil5FwHh41QfV z?#*53%-`I4J6DZMix#+>fsB#4A#>>wI_jbzhoENXDHD1gp&%1U;#}2Ko#hx-_FO_f z$%nkAkdp~fJq*m>6ml4H4}p6LWM$k-5T@F_ax5}6l>GRFJ5HfAXPDBoZ=Ti8AwULO zEOV8AJ^6=OHrKkV4u=dBZcV7|V^g13NYy7?lAkQ}hkUvV^MK8EGaS1Cf-RC;^KqKo z_$PiFE`Fb8VlRYE*~X_U=@4`xAyf zfro@e{g;O4nf&yGIR{U2+INUDU?21hb^H@?*}8N`zC8u7_OT8F;L^F>`vf4z1lNEq z?5uO|wAez)y(ipp=%nf0M1AS`65oZS6JqxoB{)5lzN>(es1L*&K%N4m8_c5|2Fhlq z+dWMv(}7ApRo+@Ywe!KE?;Xt8_Lj`9BKBj-7flX7w20hh(6DMAhdVnp z(zs*&?T_nuLM+IU3X0$Oq5rP;d2!YmbyGp#M3KrHK$P$heKMR5xXxJ@Y3#n~jm#=i zB$)8o`4*CN;BE9GBIREJTLH&e#6R*7hoyG|Ux@V!bNNv@9KfxY0e#>|(7O+jC_9|5 zAu2wb-GKoai|($XTvuc#N7Lt*68rK8CRVm_?pKxeEE=u+=F|L(&o3vR3(NR(eo1K6 zYn7o@+bT(*^LybTy@ZyMad=3VdkV?W=NLyD7cpO}?1vI2y-QGI7Rv;=S=LB(700`a z>@{QxFx;H;sFi3r9p6I??F%)uJJCh#L|M0k8dp!Hp{6+V7iqYs{Q`Wp^n@2x0_scI z;B12e_~yJ!Kjau7k5+_8DM$wJu+zOv5K2iw1`}5L`B%OtR3h2xnxWbM{PDyn7mZYL z-_?{Nd`(f(9V8v;h38?h!v&QgkActJM%rR?DfBV>mycYS*Tj!^gs^drovkv?Fd0E@ zn(I(9x~!?k!l}ucQk0*#HTgio_)x8EiW60|JX9V?O-LnGKFuSU*G-}#v}>A@f4NuE zUP=5#L1_xgCswwZ$N2N_$}j5Ps6P`_{9B3&a}tHQu0Y~qqy;Kd>dYk|7d34)i))hs z!Abp521K>Dv{qg6sWJ!fu@AYSH`{(wkceh7OUuep` zoDOjyrM0FzSd$BO&c*Nj!OJD|e~X1=IdAmfik<4C8?@T|<1o!GLkO%QFzjrCP2Ck} z=L(`mQ2W*l=H5}a!hC7hYqI&N?cch@X*DA4k@Gx}7Nrm>ZrrYMy0tld{mAmD^Kr;| z8hved3%dhaDrnI!RV13n6>1+$PwU3z;)8geZU)sB=j8>sYJG&r5Zhx~#{@&^Rvb^G zpqLS6(vG-Qs}YsDl6@sNhw&OBP$>=?BGW(B@(K|jgW-+Q(d(mSYczW|*yZsAhNwsx z28vEElE$z|0M=K`$EYP?4&?+kIf4ol~U8dkKW{Dbk2q9exX^k|l zpFJSlImX}_Cadt;NaM4!2m2RCM}E0;a1r`2{wb8%5G5f#Jo2JWOj(Pv-?wNxZCo@x zZy7~cb}qz=N;}7)@%rd!bXD`Zs8|bQxO*R3yq4*AtA(Tl6RXOSJ7&siNCS~9uTD`I z>%6xMygFd01dSg^k7E5aXqf#bik5vaGN=7OhlKGD00xVz%Hpc-$F-~mTuF0C4;4r7 zl7c>YCYoVb_$%#&Raq+`^$fG!L!#@cfy4WNOtZeW3|&H+of{TT9d=XWYnT z9#-6#0)w4d5yOA~iMe5{k zgpFUBmlOOQ-)GP%9PB}-J}R`*$F!%iv;rd;6rMnDM)o1RaiH*hh+3c>s{NQCDXpjR z?cV>KI{I>HELLiOi1BSiha0RX!P@uvW>8Z zlv&h<;!)&QWti@TWzJrq^ z`XB2-fBOgUsXXdTpC|PLGshKp8`(sC`>xVa{!0J62Y|p%DpAe=+)~z?d#%TSQfFWL zOYzB=DvWT}MW=V8xW~(6*=nQ9k52D(j~`;aTigc%^3GhsGqEQZ$0?!m5-Vm`CH7R~ zL)HXf58`99Ff|%D=fliM<(pV(R^9%^FU9JUdrr7xQj0Txt68#f0O3NWfLe%ot*hCj z`i~r)qToN~OC#BvRVcNerBpowm6iJg#>)PpXVxM5wCxeg#Fw!7R6#0jrwO0lv;X~{ z<()B^R-bG0KHAIsy8ZHgY2UnC_RTv*;(XqE|4;M2!RGzcKTW-V!I$d&E@UsR_p5ZK zqIz#&tShQ_by41W|4;M&m&vsHVw?8~Uf#3!%RBkOK6ziXZ{GKkIA3qQXa8I5fp1H( zhn5|gS`EzUd1fMaDkF`({$w>lorgwpzKr&g5_@*~+tvR`>2F`d3r^!LICs;m(oE0f zijLiURdiy7t`F8=>m$h3-dw6IG&rP6@{JxlFO+W6njMNtvvVXWonCL*zVaW2-hZRc z?wx~K;SGc9o)jI^fj<)G-asIH=YH6iue}%$pogb&l##5#JpkNnYWwHCr7|y62OE{| zVEfdVD&TM==d)CiF{r18{`b|S(#Q~|(w|pH740j9qJfb6(fMODfTq68Zwj9~f=%t23hzE5AXygRI=iWJ~Mc z4xX_Ko5M5sk461n$S7wcqKLfnGm(hmn#Vya-+;z zLj{ja|4{yKkn5vPm)Y#e1^%Ignq1NVNN|y^=1=PEf^&Z|QPj8|DaH5}^tXS}5MvT) zo{Sxm7=sOBi_oU@m^?RKYdSy*;9p#G-_L%NhJGbKG|Ge%w0Z8_9v5U zSLQoBq5lqgyE1+GO*ov5Q`~H2ZzB9=T3+SW|u_t3%D#JXE z?Ff6}{8Kig@VoI_PQwsnqze%r>0;)6)CK8+&%!D!@4*VJ7n`6bhw6^iGw6%|SA@Zw#=eU4UHI2_A7CKS zR!ox9UtUz$ixOY%7ljVnq`H~GX}_ogyeL`m5EaVf)c%LnLqh`GYb@|DBlS5C7(qL9EjbHriG^}*wQ9;xLI^EcSJ ze2IPRFk!Cwdjw2mv;uaQ#yiSr^Z{+FbGdhO$&W|*=jq@Fz4_~xagvEc5u9ZzX{Gf- z&i!pC^K2=5CdbL3wmOwEUH!8~rsqZQDav+ssuF6psW_OPb4H&JY0(NFJ3EU$9_t57 zx2d=uSh`JxE~M$CsqBYo{xu8!@Eh($=Jz*#+xh*A-(G&hDcul$$MN%3?)R^xva&QE z#AU*~4$$X7Q~Rf%Zd?6nZWHxC%0G`yVpIQ1ZT+eC!;CKZYO9S3MN^v=KGd>>54CUM zL#EhbFU)kY&DGPEtFSXQeafyw%;2@P2A73rEGKV zb*IgABY7%H%8iZF)(&qO&qdz1x>oVG=|n~F3-^m}ho6h#S*q^y{m_2!5A65-$-I5x z)m{7X;lrx!d=@WHqT9f7U!5v7BwuvXi;Ie;{#=x_tD#MU1O9dT1L>#x;CI^2-0!<# zpKm+D8qzG7!WMHUzlHoB;WwAx5`HuJ_43=w&*j(0uM&bc@teYLCcnA-j^$U+ualoI ze;2Q=Vm9v;y5V;a7y8=!e9gtZH3ts$`lD@cTDtA6!x_J0-{NlgYm;vch)qs@JSaA# zsF)GwBDQ+k>YxSwQBuAXO4bNt{T3bCyc3lu1{v)D5RBnaIbX6pRBjhI1MrDoo_M>%gTl6DB>K-Vi%2RV)?yfj=1tM zcK*qC`I#$U2S=Thu_tkyPU1PFYb3_p_pJe%dk$%8bS%l7dqs|LH-CzOQqMrbeM?T_ zk2zs>4ry!RI8HEEV3{k6we?lS5eB7sfHX@E6YxO*CLK+(wO)xh8f1&I%fp&6=gSsG z8hdV5zCMe?i*zq4pn4K~iRH}N(A4a7c?kXOzcn!cY5q7$RtHGLZtoUC$L&GMlK2PTv+i@fRWub{cw2a@K~*cDS8sl5&G7 z1Y<11BWY7&+yHsrl$4FW$-uhT0HwXl)==XPZC*`@5A04}4;Iv)o`=)2cSEVuXiy{n zAcnm59U<1)C*#xJHnH(Xa+%plrbR2NXB8T_0b4bW;xr@wnw@0~fH^sG4|NGUZDKcL z5qwm5Oxq4VZVfpSxxghXDzHbhO9Ikjhnow0@>B32c;YYxU$UF9 zu=^_wf(ddhtIXbUwXuu+i?@tBFXRvZ4X-{34?#JP#i(T)pSeH>F4Juev7Z*Oc#t`a0^nI_5%$Mr*~es^HxNSj5F^43%NwYsh~-hF4g?TO zlY24%Bfk^|hA`VUg0# z$F8KvS{}cJJkO)0FcjGO--e(6!GlbGMJD7B^*47FT~4(1R>f>?%o$@* zm1YC`1dp)6C2@7(F&3HoLz4GnTQ=CV!g}|nn;D+y;opv%+lbIvCQJ;DizHMJqszd} zK}pm_eoG7XK^hn_tZAD?UsXf4B$v+QwKT?F!sa_TSr#~kHG+EtnRh2Oos{Gct`mRj z;l!V=_)m6!CO&_#-E<%?zvNQ22DEtrv*2{#6-E}fgrrL}Qsk$#?GV?vm#WXD+B#`k zqz{5pOVxppc-2XG^zle7{e<{b+d}>l^av07ia3RPU@!{w*anx0t>mi-rz%~~2hA&7LKZL7BZx>WdIeFSMkCSk!gQ_e&^99#~W?Bd-_ zIKbyNll&Tz^9}~5&poaWi&`mhNMm-$Ymom^+|;x`0~twCP*_*p+mQ+gg^L ztqO2fjSP&#^(^xvK3;s;avjV|ymnGzcPZvfT!Lu=TL{FjkU17Df0I}PU-BCgyU9<> z4a07DQN-EgPTI9Mmu{}lRFQ0#y>ARZpu5(QLt^D^1o~npYP>}y!I}r>>6@%$wu(wE z)BPRKL7Gs*K!3-2UMvEJUJzhxwr@K3bvBQc1jUb;Kc~2@*Ps{Y5(>DxDHt1Jywk9h z9T;(%YEdBaJzPuM7QRREoed_JKTL^A;%DR4>;c|XuUoF>^00X)(+O^^O&U_kbfntW z4`Wxr^v^w(nxd|8ZAW3SI zN%Dg}@}g{{tam@1wwH+;A30_h2J@60<2Lp^FF;;1{qOhy&P5^WGhPUY+C!B4d?EC1 zqPUP=qc4*Qrs8ES@K+4CJ`-zO8hde0mV9C^#qz}ujgFStQJL3r_X)*;L}VwEaAZT$ z2UP5c6?q zm`IP<7U=g9PlO;VZ!ch_TDWSk%vbL7xBrhKrqAorqio(|Jgl$qhBo>7^|x!TY%tB@ z@95w?`NVd*%~_QlqEom|d(R_6oSLWcA~KyFcrh3w|9l%kOSMK)_h{eO^1;zY>=stt?vJeM8{j#S z7~RV?0HaqEa~lf#Rf>s}^@hIIrGrH{hxlRSquonUuHDhnxVpt3UCk~|#DAeZr@ zethDVlGEbkv!+y^hwyo7d}!j9($nH-6xNjKs*E`<=9xwB6t4Y0ftkP;7n+hocTUjA`SC^yqH$FlS$puzR+p8g!WoE=b zzAxN)b{5ymzw?51tGPMg-Tg<7`>lc7N3f!JGO@chHkl6i>!q z(RvUQP39d|cU*RV8vg9lky3L}#bMXTp@Jbe;{@mKh7y2f{UnwXOV9(9W&do}1%K7- zIQk9@aBx_38gl}Aefe^YRgF6jpZ)m1I}PM5=_id4ZsDTPA(7Ed%!I3={-`m?9+UTh@BF|FXzAviOgfr> zYTZ}gk$=-ZPG9-kPh@mp(!OW186_s|%(ARww4AgvlDwtX7w5jjTSocf$1jumK@;Qq zUqcu%@`dmAD)-M{W~@mit&lNkXDE39Ecj%c8`nz482|7w{6DPipwYg!mWVqB;SST^ zekIclqgNLwyUW!r%q!B(OmBdTI;}G&A$K3gJ=Q_)v8L#MQ{TvKq1cj29%7PdXb$>2 zzRrv4ANvNN*q`JcnlW zOn|@DxRJbNCiCB9UX?#yR7@bKyh@(QcXGM5If;Cbzp8qN_AR_}e8hxi=GcV7k}Hk) z5<52B@M}$b{*E<5m=3JoAPQ~lPW~OR5;wU6iYHf&Vn*Orti*Db8jPL?lN|68%_@SU zWY;MFm0H{RSmY;H)+&YM^!qf6t0~=oStnUZU~);7)U$nvH6?F}GM8aswoK^(jvb5m zHVwg9cjdaTWMG0@q9yUe_5rdN#y8D$R}!CCZl^zt~D9d!x>rIyrS8FIy$f%TjMHk8!`7)pUmSQp;TTvM)*eW2tH&9`hh(0TDg% z@jCEz&yz6D01}^~D$qA9Ik;%hX_^)(L11BrZ@Px9{WjX_ zT~h1*^x6XX{T+`gjaWSH7fIrN(*=!pFHvY*zIydEa7_!8i4BO3nKmKk)+j)ZLr2T2tpPbwyEs zOj-PRP@M4;@qZ`2d!7+rHM)Dn>GqXmwyQa~KTc2|QU4{!5RlodPyp5b#DEUip1Y)- zs-xU)x*uQW=fEcW%8jn*QVZ<~*WjwXH?_yH*L~Al z9xKIkbkIh$Lhgh6%%a6!WWwgZze8O)+^|R*bKJu4L8W%0;PS=mmiiJ>XMN?%h?uFQ z0uh@`YkvgmX&LdvKWNIs0nUg3_|)A(PPa#yM?3jOY+^8TOiKK(4~~yyl0^=;sB){q zo#iEo58wQ1xK(h;Im}uo~NcSaj%9t7S_Atrg2qlESDrkO3W4k zbx_H&t-I!l4{GC=cnpzPVYG6wCewByu7G)V@pcOxj+f;jXm*U2x>wea7x(k?#}^zMgv-eaAEPxsB|X`uLLz3?vxW z=c%NG7&?`|UEyZLTd&Jl2vval4eq26XTEM4d(m7$rxq>X;mmtz0Q%>o{-Tar$fGl< zzY*sL)AT@S9Zg%_a||6DJ~m;_#g&>nzjm)i85^X$+lAe^M~Z4{7aQtEurUI_26V;# zi%4nD6?tebaLuJ=Jg_0??Y(^rfP(!%=U5;c!hN*fR!~ne2yuS@(kopN=_m8rk-WCW zQI_tB8&w|1&?dD=LAk)+zbHc54WiT{MbU0H%;b?;r1Infe>P!s)#6uH-!vFh zs~Bx^s<@wUzK*kBH`=eG?bnia6L+Zny2i#0H001-PE%{;=+7v?YUL{V2iWcj0BpA zVZwMtIy5>PODw&bq1wl3n{?pHZlsvgUu8B@D`ck_kA-g6@svQ4z4WV>1ENuJNCD~$ zLtBjRsgbqCg~F{mr*8MS-QYG&40!oPu~rvJp=&J+rqi_4maa9pPi^+yU%_E~s**{K z^CK4<$J$!=&Qk~v$?5Li32N6(?ibGInZD;CN-xh73#gcT{5L4D4Re2`8(Gr#%!H@& zmpHith)>@05J5GT%y`V8rDUB|MCJc0J@XqCB5Wmu&49yKOxx0R(Dtu?&(jt~`xmif z{OWNkJ|l2r{XWfI4|I9s4G?R81Efh;;5P{(s!D!eSE8itBQN|sZ#wm^Nzx*-pyf8 zNh6&UO`R3KP_w`(&E|3BWb-)dJo7jw!h;*&Ov(2h0F*;SwJEq65PuGr+|7Ldp4Yz* zfDKiupdh*S2Pqekj6Qt+YFP1tmte&_?mUPToDpYfLd25Sx%8KC%UtzU=#Zc&Pm-jXT z#=Cct)|9Q+eY2VMOyA%;PGO%vS9<%2$=u-EhNUmGqv^BTSjJttfJzND9;}RUyHAQ=+<J|fR~Kh%4AW%?c|jU&=8qJQ$sQ;9Rn5^4jHD}w$;FK7cSYwl zBx=!*e%s>pBZM9uo_k4K%x`gMF3MR?IzGdGc;qo0sy@-lc2Z>LeHL8jYQ2@*JJ_Fo z6uvYpYg~HY0(>x$y8?wX;geElj?_|GT@xd`?SIi`M>z+ESB1ugD|2!DI_w@0-x2CI zryEMrp*@+aNX9w_zu;^Dz4~qOT8nFZm}Q;;$05z^4mmrl`<8hbF ztuQjipA6ha;M%#Y@&yCWQg`fJJ23a|gCO%w4Z(Y+vQv^C&z&V)5j|WB<0=mtr>7hw zB;VW3%T!dzIShO9VE>q7k;=g@rJ`P5!9rtS@!xp@xc!T>+T4890LBhrOyKrJdFE&0 z9jBPrZ#Mnai+Rp`#YZS+mWTXcklS=udLiXDWL;=)ceoQ{-Ldb_ISGjF&`GaE(vx$0 z*RX$1al)lL3;u4okG#7CBK5{UAQhF!%t2~R>^UVK=$>$jsK;=UHU1zn-!L(ka}0|Y z4kzXUAKF6t`zfEfzyMA4cU(s^?L(B!2AFL`B8n0mnn(7CMaF!&#*9W}{JuvvUO&z3 zWF&{?@gq6MGf-O-P>kF-bf&(dT@F;XHd#DY54yf4c*0V$YSaOA>pIiA@$u)Z1E7CL~Q-tuJmH z`z~RH+NSUz3wEB2U+saJ25`2&6#F{$BGjuLUdynpQdYG8)*}(;0z_MEEUq?KP|P*W zBqicxs{*`K%%6qZ7Fb%cUC`oS#`ku_`O!>P`0S-KbGG>Z;6|LoxdJCV=DMobfe{oA zXm9eH5m#v0rb=m}GdyNAeN@B4pG{vY2{{{1t{XSV|NT|DRUE-k%#RgwwCX{t_uv$L zTy1pBbv5xO7ZZ7Od+gVVD<)ziu=F8-a)IxKLHAhS=NX)c~~E}Ip}@tq!z_3DJVHyy8-NZV?QI4Z=-?5#oIYMWqi zMN$)HIzu$HVPT~qu44zW&ct$^m>X_`+HZmj1R}RB0!bN=N`V}T1kGTG`#XQfO$2ii znTdUHZ1OVpU45A!C}q=df~w<(a^FxJrgv|Lirm;GZ~o{G%ecG zR4!Fw<^)?Ms(`$CvuYF|1Ul8mk8_%+3U-kx>?F%3s5+Hc{xxX;X03VM@XT8fA;LLM zmI^4`|CS4Udlm&0$u<*X8b(L(k1+IRPg?xUYQJ_rMh|i)K`!tG(kkiqNJ;~W3YnCs z+%%kma7WE-_EV}G3V9u1^NKCZOb04{uPL*m@|mQQ35-l{DmRzgg!F-*zoW35o_}X+ zhEkS2m3z%=%f)1+njc=s&9Kn{Xo8?XZt~nr2{D#;K0+aojxd!^m>Dt_jp5FdSt4DA zRdRLWk8(!x4mvp1!*o06c1+|hWT5FmfAmEY6B|khLtRfU&}0JQ@0!Ka^EP&v?kn@+ z*Ld;ahSU5Vzo%in%E29TGdEBY$`r$8cGK`Y!KM)C0fq2eA-HD2`B(NNyPi$ut3W%eA81G8uHqL$8%<^?+wUv>m9Aj6LxFGk#`;YDne9moqb zQr>y)pe1TeORObvp%Ld`bzMFEfAS2|a-(IK_D#$bnI8d(&Vf@7yr?}n$wRn^9qyco z{)E0tKXQkV6*mVB_uskDL{F?qwoHS+C)T7wruP{ZPc=ov4TjBwAoN;0MKRUdr`TdC zoA|XdQ&z7nnJ4%gOuSo1c3igf8dF+E7jBqJw*P#e$hC@;g^pL?qj^<&A7FN<`J^fijj@@Uy!N63-ds#sUh=Wf62~;0z&q> zN@zDjWZ|fPcObhV?|(Xn>Bcl_P){bPQN;umqh)R+ zPdjit2rfEY`+PhdWWGc&-M-Q|lej=doqy-{JcCu2!!aq%&+?c z{p}LCZTdDZ{SupAEtZP{YyS28(_ALaknt+f13W!#@ zw2GJN8AmH><)$*<@4wHPNus{*_q^Zxe0iRov*(Z8$4C zd4V&_NiZlfB-QQ05E`23%;Z~5OxmZtmq_!&I4ab#OKVA!Wlsb?8rC*`SvwMcISe5% z16$x($~vfl{j~4EUTMmnIN!Z>m-=CF8GF%vWnU`jSH)dXzM%KF^ zT$3#!nTk`>DqNE|?Rs2JO6m;-7FXl;((PXInqI(X0EzePT%@K*FAsju`dZ( zL(ILz)8Nb33erLA&$De^-(j?#SP`c>-%kYzI3vxo=S z#~Ea4*Bo>(1|B?P$fA^`Zc2kr=A)a(IXpBc*{XexyZ`l-Ye!mHMe&@wcgSclScOmeSY~B`@Nj;@2loX>6hP5QQo4IaN#pvzRaB*Gc*nTgcMBurh{<0Y$o#gZb2B z4+>AcKb5kJD5+|*p(KJ6M9t&hVzeEM(M4)=^l<(i6M@gPp`2gM?qq2=^d2x0kB>d< znBSs(#53eRZ*rfl?(-h^`78JN>2FAnobYdyv61i`;qQch68=R1G%?>uLZyRfI*KGqG-RDr^Q`*w`&fBW@u z-_v9BZ=V(SJ{*GSN@ra}wc7%o#%=aP=5kwu&#R z&hTa0F=I5Je32uSu>(-+?3y@|((t?Rt;)pRd>jjVu$ZSY3{L!#s+4k2c+uDa;rTx6 zZneZ$bGJIONOR)Epmpyoe&lxeV&)?T9f8^0AXrt&A^5Rtq7?4yGGCgBro2VGC!?qJ z6v__9?pEgD>>sN@YH~|kmN&qcGL5Y zGyH#Zfo|T8=B)$8AwcmMr8Z3NNZz}+!z)^zmFUka=M<~L5d)T)jd$_{Q|k4UD!H$H zdw<-j#3oi^Hk%d>&XjO)R$48mG%Z`Zdc&L*Py8GH<9+V#B-n)enL9)Gv%d43Nl457 z^wj(=>{&mIA&eyX5V{DfL^b_D3G&p8Jm2%w4it`4-kT&$1RqY2i*q`Kjpv(=)Ur}- za_MmM_$&;aa8(wFEutaL>HWqfO!=c(e9_Y9{9ENF~_T)szME$aGww_IoQuZ7Q1AY}T9!pS-EXMC|j+ghdEqKAvSrTn~d` zk1(HPEsLc~eRU7-fr(uYe7;0q6UW3-F0ppo<);aC;$kbUA3e)|loBwfGomZ*&F*|k zSn>2eoMX1_T&$Z?bL1%dIQ`=;@$Se_>xjR0i_a4}oGh@8NYMaIsvjLP3uw3f)l-kA zDh{=!mdHoJpkD!Y>pACMSN+zo0eP{z2u@)Y@)-#G?1mgdkUVxB^_(lv!w5 z3cnyeNV%dN>8Mp?|Gre9A%zsa!~LzU2P)P-f57h%|rDYI}1!%8`Q7JOagB^0Yom61GaQ?fACCVx8_4EgW%O`Y`y)mRNO zga3fkKb_MhDea(a-O@}i!98T$HNN?%7?Y(GtK4$)65VCr{D2jpmH7`3CZW#9Y;R3A zu4GAqWqj9C6{$bjrfa|h!m%b6=}K&1(X~Yv>%``D%ox?Y4!$HS@}h4I;4r1t1-OJd z?&C_e6el6Q^Ka&G0#1j~lj0wRvEHNJGDFugnX>Z7RX`0LrHy9Zgm7*>dwo^Pw+wSd zG`CPv2sj2`pLpus6Slf!#;s~-41HeL5_X`}|kr=HTfZT97~QtD@p560%Fz6o5iJgp*h0qbYGcqD#f;^@`|XLG92 zYM_K7)KN`Uv{$#y;t*kGVD`p9m-Efgz4%Dr`~y`Jh#pDFdSaXX2P7Q5XruKiFR?b= zXwoGm=n9}O@6V(_j!t&%S>M58<3yNq=Doa68ls|>9e2Tl+`luyCOTyA%4bM zhUtfAb#oKH=EPb`_!+bc`CMu~+hlfrUt)gCp_;SbV1Lx|uG543;?pw3avy!N^2g)w zlm!@(cTRx2UYTo{JTpt`^qTa3F?F0H&Co0% zhe?687XF!d#jF_EmH!;o3-yw-yYf4U(^AwtnR?-AnpxGFS!3Lpl?~2HbAuVm%({cg zM`H#^kGh6Axa6eXv(QaTD+~{aoj1ufs{5!wquM>*jH;lJvAa+6Y;|0B4}?0D6#r*s z83?W9jWvPQ?<=V01>!7;QX}^??85rL@VH*9+i^KoWOU$fLbxFV+Wk2Ys6mO zV(q!J*c(K-9qBQ*Od zvSJrJ1os{LcR2I-gcEKr`VXNegJ1h*Hk=-1|{FS%OYi>}_ zJZ-n|Lwen%@-uJ1X>AqqH1jp(J?2t_uV-;?PYmjA?XY&)A5W&DxcAZrv_@_)cS>xt zvv9DNs8AEYT+}Tc)qr$S_OC%Zw;uIh+A;^6$w1+Gy$ZkIDg1`}dl&x8KT(-c3Tupn!j)qMpohvdiri+x1iP)An@svxL>PVMOMEkY@?jT$ zyo}2-Qle-o!pgt!BzNH3cF;hy4N;-;rR z*M%%Ojo`XMG`=y;Nc==hAVb-d9!kRzQj@)B9G#Rj<*O8+ixANJNc(H(@K=}h!(YQV zSb{81R5bI<+ldify_b+RjOk`GkwSBpROE906|vgwy!vSnnoft z&YH)TWKnAFN522c*7gcU^bv|oH$k)}T2=5t)P#HSo9W+VH@tg3zI&`b{#Y|emu^9xEKb#pm z5FwMxj+<+s>g0FrzE!F$i@?Q__XG^M;N!E)b|#k|usGSd>B#DMA{+ao9KHm)RBZJlf@~i|G2g9Pjsr^o!VS^jE zT1iA=x50-6(~Q18C-ag|-TQ0xHSTSrXztPJ67+nua89agj$CTxLg5 zNY8OKL44WWp#Wl-C zISyRFd}?_{R3Ia$K^a-=U+sX%fK3w$v(2z!-(BIF5#M-{eOb9FyaiwHM#P9E0%? zpV6E7U{!+cAkSI$P-YZn&}r5KH3ZAB9$E(t^Kp!>*)C^hmNDz$xjwo`84elmy{#8A z5>o8x9 zyZC%np>)GwL)~=G*-sdpX`8F$jNowaXC84w<6chf0F05z5Lc)6%gq94-+UdqhXA6K zy112H8<6j?rO_Sc;q%@4H)B%WE{-hvNw(or5+_j;3I6IPcvK0_BtfE(4@TUq_I`lr zj2$|L{yrs%5;DBIwHaSL*!r=v9e>UqTfxIXjj4yj`7d|5nXWxFI{B&)zFPtF4o*pId`0tCv(W~d|Job`Kb7Ma> z^ZKJC_kyce)`sLIReOW6@kFMuLCbZbbv%+a%eYLKG%4>xL5<%w!yN7M0b%^YeJ@QC zRKXj+PFG;xPtlsjJskP@W60g|j3xl{_o`n0YBfK5OgHB-J+3&;ogV(OxiWT@VGp&W}!>;7!jJ@Gm z!P%YiL{=V(k8zSl7mc2U3q4n#wPs#^wjx;H~=Z!MKz!uIwcN0UFgxHebMdMsi z(&l8&6Zoh~g>$5f&(_efN;E>!UJc5ekFF^-H9zZRHW^uO)aWyF5IvLqVt_fvu?7Cd z&Fh{5kp7nRUP28Y_!LD6sf|pzR7uhTWoZAs<|7=dPC2Wk$$T|l%=d=psQK3*8ZQpi zj*3uUfzyuIw+oYJnAhl?CP(Q6nB&r7bNN36qsfs}C zMpEC%>BlF-#M(`zC6hQ+?uabYc@EoDHAED1ufF$eTGKr=ehHG;!QG7c|)Vd0%D>z|dIIg6X&^n>h4f_lHo<{(FgC%3bTBo! zq_JjZNn~U&Mvf~AgBGIknZ?0WT}d$3RA`!n(9Oq36*Qq(arI2Kr1MtF*Qc4<2qRITOV+Kc&YS| zE|x`WNEYIg)>OJAfzE_XrMuMjQyueu-3rMJQ)#yNOuhgcnstVnwbym@QRke~a>Z(& z)>UQ(9$3_O$13mUh=x8!AF+_}= zg{%K1g|)YpE_&6`H<3v0fnwlgNKU!5bq%T*m;oe-ip`&)kD7s(%B;g2xdE}6((Bl# z%L1(r)E1UC78o^Xma~|t_Z-I0SrM@MHCkgh$3Icna}K|ucMF+X&jqZZzSN6A2Xq0M zK$mUyQ>g!SHcQQAOr5*0S-E^yIZakWm=0eY z)HH${2UbFG);@#XXg!VL5+T#RQ)r*iTOKfUeMJv?L(yII&P=RImm-UcVpUDs@u!i0 zo4sEH0P-X5-|m9*S>PES;p!9Zw$aIFh`^0O9>(HxOO?q|=bmLB7EzQACTPgLr6$6e zla7>h2+;%ra#E&W^qs>(A4E3@V*5&B7nf?jb1L~E*b}<};l;p@NyxIje6TP;>z3nD zPr$3R{)uF0afQo&1mc4(2Z`-gqvYrb#PW7axvJ)@UExu($gO>4(LXfT%u-?&C{;yI zbW*xEe1d%QrMBV-%Nn@bUTz9003Vq!dn0e~G36a<8gWkS+ocAw$-kIYjh_a*4zx{q zy`pJoKKm@UPjL*9rqnE0b*xIPOwk3!S&>P>*%ucEplY={?yL=ozuXb9wlv1`KsLaQ zLIKNCXKH;g+EEy+-LfbFDVYtW8@M50WvduW1ATxpT|8V{)`no0k6n$$csbd3J1GS!+_QvyuG+H~@G{Od~&G)jOs@TkJijAI0#yx47uFX2QtCv1U z_KiDF3SdQ8wIdkM15BN_Cav{VJA(%iF|5a0?A>z83*xi+BxLYe)!da3(rxKG?H`;W zj*km4;8%s*U*eso7k>$Kn3XKNGBG<^BvM|4hC-o0EeH-sT~?A-C52V`f+{EqsNKp1 z3WN3;eKguf$v4b*7UTF6?aNdg$BU9}A<_SK&9RaGHJ5Tk$yo++BHySc7EyPb-wa4jz1+%|*KYI%?Ot+Wz(SIm3JMyje2h#i1_dDLvDD+FbN>}U= zdE{a*SBY3+jCR45?JqQ5TYNCtd8D7UzSAC+yThNm-merltk=%1V%H5D^vvy+Ah_jl z-2_k8B1mUPOF~h-G^wK((qN`xq+fJ#URL;$fORaS#U7EX9nf-h3y0SO&6eAK3&-op z=@3EtAH>v8#OCU<>2CKoGu<86)7@X(r-q*xh&{c}z-9(3*Ik{eS*O9yiu8}Jds2}w ze8LaR_>;TR&dHHCqa_0*gxP98Bi$?0ctdM4^GB!$32AAi0C|_XKfN%zDTHsZ4wqy$ zvFs4R+!;Pt~2_6Cqe7IP?T&q=Ftg*KZry zGG29=^i3k~49_K6Wwtuy=yRRT)vcy596s^U{X$m`8!D^iG3w};WI976-Jj+8(>pOcb%>TW$nkNhhk zmdu;$c(B07x&4-eTCT#2m_uhDD<-dgmVV#FTJwi7sJxcwwqwi|^pZo8LHFxruJf{7 zFDKY-e=$ks>K7_7fZL)FYs%a}tYtn@pRW2v`81=eex7#cU+P?KN|XI<1F0!>X!OPw zHrcbBpR@Q;XV(v-Wv0M|v+UE1r~D~M7Wp>U?lOwQQ_4~FebPi(!KV}mGG-ZMM+K%h zcoc9~i!|B@{`o9PVHcUwzZZS(_k8y^1qlPIeKX^M>`0DWV;~bsm&SNnEi33~<^{in z>zUtmhHW&8S9puGhKiM)Y}R>kYt<_P)>i}86~)mdCE1bEu1PxWp&J*KL&NM!)b2CV zzIw4{aF(pwTgC2xvFa?n%q1(e)lwtO_q0_IH#^p$dbu|3&(G7AqYu&V;Hs(ENLPDN7jXKA$Dw<9&iiyuTXCaZ z+dF}~ON>PYzna@h!nv{AO6scr@-^fw;p3tU=VgbFu`9KdFab^h&8ZCa+Ca^1bHn*H zx6O;-D{N97@6C7`2i}vwYc`8fO%EjJ`}o2yIT;Pq+))@FWcKYbXjy$>vdARWz7knS z?CN3{3y@GOoNqtCG)YY=Nlh9F$YgiF^ie9eKV-g(b*t3d%(k=LL8zYJYG|+ThIAT! zSgNJcyEzrE+YrramuNm9nn5=QGS5q#QMz(NyQa6WuSZqDj9P|YX4-gKPu7^Y0|lj_ z!@^l{$W`yfVCjRi?5)Mjb4r&At6xoMZ*=P9o`_PKD-d7Tumy z2X{T!ys$2wYae6Q*{O5wBRFyAZIe9&V<40JkVm9sD%8V{zh~Zwa@=L2jx1@M;m|c7 z7+Ns53fCNX=GMZrj%A8%@Z8vq^8&Hoo573S2)TPjN4-oMvD?J4cIP4+a>A2GM9%|ulFCneWO4l9tnf;>WT z=IZ!E5^e~-Fg>hxwUYKHQ+8?vaRCsvpHqX=# zQX75GvmfXzjDD8yx#t!zz0o>fooU0zDZ}Y>M*GkvU+e;QcKHtUAWZ=Yy3gWt8nm3L zD`$k7Jxi%0QtZ-v0O2-=r#o5t) zUmhT4f8{xIe&4o|x6Y)p?C6xNn%@H89@ zSmPxMxG#-A)(m)ZZFrd*sjsPI{ebn9d{0M>a~1w43_(u5nw}Zy*Rs#iVo;tI?lA?r1u&P! zJW1Rv23XVdSq$#e2Q3Dk)+o{FcS;ypNdqOL0>A+?aeSVKHaUk5a3pl6f1sw=hjzR! zYv^k0z^~Ot9UvB_f+Ouy9A<^h;pXs2ISH@_L=W}5W5}i!9T38W))#XzU?5haz4uo8 zO|y<#W;rIuUyIs*mo~i{B{#!Et?9TLoKlPvWrXgM-eb+u^$HX@6<>hyP$8VeoAFRuC6{0Pa?Z(5F& zU6hrz+qH%`NVYrAM%en+~1_L_YcNbXf?5GVf%H$r~SMnFYy&x za_krTye4zAh<-YM%R4m@syKxz2K&}<0GausW;Sbcl}>72H1~Ru851)iqKn2}v{DN3 zrHC3B525&B`-x+KCFRzb$u5W)Dfs1rf zt(WMH>6N$_AIwJyB1RgOyBQ&A%>P{096Xdhha|DkWmDFNhkrhWo4{}*p6A#1?)2~6 zPB)7+06EIl>5BvLOA8yVXZy6znsCU0MIaaT@ocK~75ZDx4d)ZPrSL#b4zXykFy}x{ zo)gQXfzkDxZEcRMM`m#+oAu$hWHU<7q4Ow2{#~2QMm7p@pR+%$(j?Q=gOXoT)qyNb zO_>~uUo-%Xnici&ylT(t7XrB*o~J__2l!gIY4LjLKxbY!Z%%Yep1k=gf-Y{y9AAuk zBsz;5YM;5a3&by#Y11xhNqIj*Cr5&hN-cvqEMgsruOKTo$euWhSb9eItm3MUnjv_N zbO^SU{ebV_A*8r2{WkO6%vWJft&!twI6<1 zY;A{!K-1LUv7wQJ5ZSABgl}lH9@GdpX;}n(@X^svQeh9OWjUeP@g@@_wb8n(gLFYO zdctFv&=wiI=8c4zt`%Zir!nK&UU(4+&k++gZ8r0J%8Bflrznhh($G(U&%B5pd? zyFu@^5?2Tjzp+3J+K6Qotrhubi=Hj-vVQIr*5xtQv3_As-#%elj4nL zQZPaOA}+vO6nYjqcRToDx04uPh|ed_cd*bm5^|&k=n2`6DdWke-sOD~K9Nai-%T1M z<^309QeR(;5}SMB=2Ih0nNx~=E3>Va?B)ML&FO>wrkdF=(8Ae*^ia>zNo*s*`QmE! ztHODW#L%Tui~-Hto+byS_Y6i_oWXhHXDN+L;vte48xQ4tl(#6+q88P_{h+v^;9c2k zj4Nt3mq$iZRJIvc#sS5OMmACm{l$fL^DE%)fwm6ga=_!?pO{FgYQTkRviw<4&_d)w zPP0~%-(P*_?(i{`$i>1Xr1B%?NqC5;Rn2!|wTd5$RSh!_>>9B-+S$KmN{Od+e<8cS zDJ9XvLvVdf9FWaV1x6-zIq~9XyCyJtcFnYpqVqAo;uDCdRXFj4Nj9aJThVQP2dwWg zZMk0-!)oVRK5fIi@7`!|gU#V%OBlcWr^K|GQ`+b3N0}yUiG5G8m}iBnst%~Jb=8q- zPuoel|&Z!^a75DZKmvG<=o{V(jB%Wpt#Aj67&vE0AN|T%L`r_tx zdEd+mmsA~~oE%T<#|5SzdYMQN*S`EUo=$bBa=xe^3rqTWTDLe!)$D|{vs48Gx`&O= zvQ5GaK))wE8tL1)2Ir{Mvuas{q7UpI(Gl(FSA(a8)>d-ja)IfbSk9^=qInCi)b1PH zLYa8g_OIU&dQfCFSH3MQDe$yT<})DYe^%l`{!`HzpBu`s#~H>5l1(#&1{H^xa<|%l z{wgaF8!rd-fA~6pq1KP+PzGA>n_4M(yK45dzQo7R*YSI|j$ih!;}32rTx>stK}2oM zyl5%mN5ImS%hMXutD56kSuyTwUC6IZElQkC|N0yJ2Tcv0Cce%`u3*>w6WrFt!1xr8tRkuqR8rJX>VJ+w+fo^0a9(FPg z3>UUb1(NYLIpRq3#yK^5NZtV+jhsOF?VC`p!l7*p~ zJNI$1B3%4d``v116ye(-VB6@gcJMPvjnjw=q{Q2}&)SdQ+4os)&4fx6n-%9V07llKr=@sQkpwQ&NZiKThV7vrIJ5& z@$?_|>D)153VXw9{D+vCvndU=-gb?UQ9H#L%#?M6QwF!Jx6FZqLo`=?CQAF9J=*w= zLE|pkzLHj?BBjIxChjyb2yp_#E@U$UsEe9;JIJ6QG|PGWgu!t$n#DvtZI752EpmvF z#{IbB8QfpOuT3pVEZiS#iW{6RPS-iAqxS0u#Se1f!I^wYvXGhe*#6{vjh$}nqM2ec z2XQsKoH%&mr<<&B2|XWHnh%5dpqf0bdBo_FYD<`OmZfq)I^iBH!s78d)YP1+eSUEj zwUEKro>gbEdC1n*!L_{|_I02r_=uA1ci>E2O#6*$wk)Rsn>ifj4oat}C4Y4XZHcD{IQ8gnxB+`M48Krp>zkS->*iBZBuq=4t3i3ABNm(q zn99FSU^-!Fd z!bt70$~ge7Gu0e?N7Y2t7D?7j;SKwo!Ath@J;m@rhs->ND&E2+L}r$a2KV+EtMRS& zv)^$W!l6+wvJvQN*hgkFBb>S{>T=OSisXWxCJ{?PxWt_RnKTPq?`pjy3UdEa@A!C7nad`)t)Tp=Md*4nX!>uH^~iQW|9af zsZL@-`wZABlP>-yAH_O+pC^rQxoNvZZPCy&yDr_-EHtG38tSc^nuVyQH*`YFoBdUK z@HyN#omaZP@^m3$^yo;>57*n#T_kh6ksK`G>8?`rDXN^-gs=KsizgZ^2B*f^FMni6 z0S>9^@d{@Mg3HiSPO;~woosqx<-TuB)!kCJ?-AxP+PFq$})X}yS(pxA9A<*$x2n`Yii^P5u{iCf(*=S+0Hul(8u z40KCi_P&iEQ+-Q?F?)hEXC*gM0EZh-wHp-Ed4@?pXT_?sadkxlM&#EJS>tp0o_P%|v{MN3z)>QZc=}XMh0dWOOg04)?me}~~ z2yXUje~GLDdsc=!Eo8I0oT~#k5SRswq>95aw>f8=<(@NsWe;d14sWaZSl#k;dS@6O zV4#vXK{L=*ZyxJA(3OKv8EctgnjBg(MAIdoGxg*Ne9qSL-fXB1mQVEwtQanhM(G6&C&1kDIu zP*SADu65hz-gB_pl>_4`Qy*kBZXZnhGx|QI(yMnQg6v`_{;-{V*O zGUaXfm(vySfb-`{0j=6(3}tkBbdp|QY+t$4$v*;Emg%E&)b zr*@Jj{geBXkc6b|jgg0BM8YqS%7}y;_BXF}q!PMXGQrBwtzPp)ieR3*+$W=Meq%5K z3t=>ZJZ+BJ92)oH>o9}D$v{R%6^ISdFIPl0TxiDq(Hj`x``NAUAD`K+8%Ckq5fj@B zUV$GqrGxvU$kH14_eB+MO@xTf&~$s z;kM3~dOp+mp&hQL{qvZ-CxUmPw`(o6+u^uf``ALj!%* z2fi2Hwzj)E>9S!G3x8-Q1EU1}R7@nMjYR*`xGLT}}CmQ};vQVfXAZUb%m6{s!e^N=K)d=C5 zvC^-3j&KJVFe+-XI|`jDtQqaW=re_>^|U>Dgu`<@AYl-EATaxgzvi}Co_nvRz)*Z) zwZzpK{ZgF5h)^}IMB=_!=OeYDjopso2KO=rYP%M_r*ZJKqJcqodv~>NRknR6YoTr^ zRD=IbZ#Bk7L;7w59f#Fpz^Y_|k$wAFb>|)k)Rc(7GVC7;Gsl??F3Ke`_OQ@)8IbJ9 z=~p0z9c_lRpYLQpM|*-c75zWvpX=mL&XRA4E7LGUur?tBLs!kO2aG{00^w{}$w+oU zlQV*#&ts>gX@*82C}}78X*IOR z>Su`;5N5&c?blgI^b_JO&ouKYa~1+eFlQX{)v-BQzad=b2EA(|t}vK*pjUA1v7&44 zryvU79Vv9^C3iSL7%S}}j=^Ysf4S0vjB;%sZ`*b*I5WCVi&Iv_Ygv!;Y}>q~2(69H zp8KKT;dLBZ#b#m?x+Rr_{hSi;TTvxP;_a*LSakA|QorSc_?xT^wVjJ3qQ*su$iJr* zo)@g}p;#GxgiHooO?H@RwbL}j_1|U`A!5sTizpgpWy3D&aMNdHdw#j&HV?|*a241y zHL|Frc9`Dv+%W1WQW%{(@UCzEEH^)wyE*ymzL-B_&jwkE^pE>;9U#$qQz?hy!4k7U zsoIPgWOizYrCTGyC+kvp$<7CZ@#zGRo;1G(oPk=YXw@Kl*>;ib`cnD`ET)v(FMHj+ z5X~CYs{nkNYSC$a#Vk5zwA8<~0W!uXuSt0q^F@8hjTUm-H$eK_eUpxI1g;X0mRq|? zI3PaBB;-7uaYv06GgMB$TQv$C2GB65fL!EuDp6scpSNY7{Ss8&pYrhZmXU-r=WReZ_34)WzGC zv~?#!#(29*2*d}yI)c5Pga*L-1^ag)Bb_X6A7@@jqsZ_(o>lc+-msw`Zgfhx0XyYg zG02#3a>rghE*&@EFU2;T=T>uSZ89Z3-POA_*k_2LUxs?L>n8xmC!iFhu14h zm1x~5NTeQVO1!GW+0nrO~9hNru+&;P&NPOcQi9_gi}yfmC>iL6Y7D z;}g~p;1GxV!fHJJK6V@}^fc_Fv46TmKzPx9^-R&ldz=(8Zsqtf|-(WHGT!z`yi4^ky+GEUmmO4B=*Q2wunKWy2n`sdJDouGel7I>- z)9N+P!l^46)*HXzle-@j{sM$OWH#FLSdcas#vN+P((U@w)x5JLFiWFF2Z}B`TZp($LaZ(jhcvd-*i{Tmrcbg zEajcCTg5^%LqyUGUvH3^{jED653eaUEiN&cq(mPcVNET;eqJwP(qHPp_*TVzwyTHl zH+if?VBe zG<$}!0$&d_u+%us5vtaGaWoIIsgk|}MpVsXi*WB_Klf+hDq2t^Hiv*OV3(;>dRMhK zK0Qa{Q^~+;Uqk>W9^g9zSXsJ*Ge#)f%clG-8)mWiTl+WVGRNkhmhzV4g0Qyw&XGF8A4c49 zfFy2$dyc&mK$+y_>31)nC}CWhGlYIKi46q0s&=l)V$^|ad5XbT^>q0}>LcNZP(1&Q z**Y%WzSexTwo1LyzN{R@d&$PBYg1HT!1HMgh)@R2?(RUvR=DDvMY)?ewv=SsAWh{L zQe>MH&_m9QYxjo>Wo`Q)acvxDx~S9ynCdcMiXOhwv-FRKoO-iXv62~&;wwYxty(;P zOOxYKe5d<--F<%OK99fH`F@xCe9(RV*?n$tpL^Wr5%+mA7FqICKG}VK$9;a+eLm>5%+ogWlsJH?(;79`Dgd}y8C9H-Dl3_PC3WB&vV`91ot`3ect6hzwbVO;Xc>8&pqz*9ru}Yg;VZO_j#`Syuy8c zSI-rm$vIiW38M(76V4%AKp0P$M3_prjBqvK4#M?>C4{Af<%IhP4--Ps{%W32 z6S~~r+j+iBc$M(B`|f?72ivpD zcYlAI=N*J4gr$W02oDof#&3E4iLjc`Mc7VwneZwhMOaQKAf1OWoG^-TI^i6`1%&a0 zNrb6{r%8Jm&#MV<6ThD4`#cX4zD-}g&F?!1O9)E|%L(@p9wwwZ(LK52>&SI>K9Qyt zD=zAx7J2UI{SX&1!mg3-@3Yd+NzVH*?(ebgGvq!mai5P1yYLS@jPK}Zrl zB78!~rBv11v)&swTzgs$ALcyJBap#mv2o67>3kP~TJ-QWsI543_WqRya-LPKIgd`u zks`~x7iZe&9ZH>vdMD+HiX%CPzw@4~c!d*J`xCmxi1%kIevA`We-Z^wT=OsS32ZZI z)Sm<=&Wc~F_#00AcEw+I;@?$VLLk!oSaI!rh(DI{_sN5z*q@wLR0i+S0pNQ6kYB3M0oUshx`kyjMCg2=xVLBG@c zsUqx~y~kWhWIT~G6*<2paRj^ypU$TAc)}oY%9))TsjgG1Vj>lau=n%k5HW0moc(U{ z*QWT@o0pukVhK2G5$+@WlJFQ9E+U1;Jx_9K{~$aw)fUKtuv|8ZE-gfv>>|XQGl6~u`x9VU#>2#8mRq7(CWZb z7yM^<=$i4Ti#MH!p^(}Al%p=%344sYF4)TQgm_cauk=6l&+JELu^*ZGEGX%yr8jw2 z`Et2&_u_2+v6ht*skZ=2n*}X_f&y-Qo(OX}d`=jLCz_;v@!W2T1{R~fX&>E>7at|{iQq3!5 zR+5-r{FWg1P+2n{>D(;CGaGwX$DfBbHZ+|3f)To^_K}`qJYna0iUY--AybO2y3$yE zsR0w>x%LXDn9mt7y8BIyt?|nWxaw?CK-5G6o{8F_(h07I)ucG^4;`t*PTl+onylI{L zaw@w070)M2eJG0P83p6@!Wyt=Rn>5W@6~R4#eg5K(f~71#WzP zY+yD=oALw0=Ns3L7Q z+lr3HB3omTu2^J)XBC>bjqh^{vA^gonKQ>`yxP^kp&y6tk*EE!#?`Sq*ZE^NZ;v&; zAjP@=u&0b3?&pdBl9qE3x{;*)49tQ&^p>P~Ru$p**u3|&t{^QQI^vkW#l5?s9uRK- zvYQVEVhx7~Y$F>AV-3Y~VhzKVH5|)+63;Lo9EzKgYHdpex$?UDtjfs2WNxGNarDlY zQxRCSai_CIfSa+|Y0#2j^eFoFAA?ngdi$@ywP52xd)Ni0g@f=O6zwXE9xYh(FSJ-z zXCubfrPd9btjN=`hL_Rs_OwZSA3d6i_;t^F{Jg^y@5A?xYfpQuDesx{-SUv^ZYqqX z2Bymc$|~<5z*8BsebG--T_p!D<|?x%t=E zHGB^l$C;dJXf)HO8aQLz;J@+u!m%njyp?uacdiTOzT&qUS6huQFrM5aZN{oi*Pci3 z+<+czh4Lt*lJCdZk*QfnV%*5hkTLfu>$-H;FFY)D)qZ1VO;3|X~VV!yW!(7X7Ix1F0= zO{Qnb#L*#sNcm>%ukB)UkC@_b{4z?hnoFD#o&9r3IA^?#EXDwKx(Kro9^^$Pjhm1B zdP!ZN=4h?cad*?y|Fb)V>dnndA z(wQ1)b;;k!AtuZXUBxUUE(vidh-&^YwWaglqjEn4JNmTc#`V`ha5UQz)pQjUik#=S zBKQ_Q5V-&|A+VC8fBI9$4PhtVF@Spgv4+(kuw$d-kC7HV60dN<+LJ(G2fgE?C{6#C zn92V$y|NCGZJn>>kcjA=!+r5~MWA{UmlW_rS3T2uRY)nEG=Hpu6TdcDH;P6uoTfRg zQ&sL!4l<=KY4miYh1rt&vkD&67#nSlZW0yB8a+J9vs`Df(W6D47^i1+v} z55ylYo6F%I#PfcH>j5jj`57=;%XwQfh*gEr&_3%!Y;Ckg*2(+^CoeKB3m zPx34t)L_~6(@(OIvUVlWz=078lEZJk9Q&Kj3W87_zx67OXu?Hl>~HfZTw=gVJVG48 z5NR>TR7V({x9NL>XZ2v;flidTck?S)`*O%L=?E{f!;>vAY-jj{9(oD4Q}e@(bEzR* zkm~edp8g*H@P#Bh^AUWFIbZ$OkJJob3fDePOeRt1tGvD8VwG~qGc%ncw%)qmSGzTG zJj;3AZNpRqpZxTw$(P#ThaqU9Eji&j<%Q-uEtpS#ob26dLAP3PDANLc^;>P~GgDen!)S2=)y+L}S& z{A7h0|F}k7#JK#QJX@X-R%FgTpp)>s6dUpc4)R!hwsCvJ6!9H=ugCQf{9Lu`_Q>`0 z0}nVhSrEb2caie5D8|(dvBOOvk6-W$#0^CG)kQaaac&Uz_+OSqb!p1`0>0Qm{m2a| z?<~3j#5^_QPTPRD`YXo`1GyrX(3&MB?)VrmyO2uEU^dt{t`$z9In{0NSo0Y;{jR^l z*7P~;%d(tI7Wk|}mZroWz-s*PKTkv$StqrhO6wqWen7@P(wcH$4!1x;LBia5Z#rT( zunnc5*vS8bR~BXFfak5zKOC^dn|O^Z(fK`5BG(+e3IVFtgfB=(pAM2#oggk*|cf zEAcFDHVc<7@VclIW~c2dI!bLvSVMi*X8a&JwZFq}&19hoc{GDxvp;L^JA=DZ)^?=h z(D9s@8Fx+KMnu^sYIvCy-Sn<06#J_%oF4|Ub&O_6?Y&H2>t!{lBn@v?sG6{uY3qHp z&ugYVubFmOGmV>0`Q#VBkL{)IU1)+(ZPs+p=&LCIe{D3gtzT+ACH$KLPEh_5<##^e zn49LnR+wk7cE2I1P?1e(Vw6f@KNkC&=titfguzraY|aX6eW>Qi&zJ92`Ne#SIiFHa zH){{!wGHM2g$fX|AwDyk$@Q`*1ori8AhgdBrlzObv%Z7x_L*vF4`V%3ZA^NqRn{1? ziIW_)&LE&Ozc#xQ&ddkZBu0_x&MXVjkR@Cr224X} z0@P`uRVTi#8P=bB?%f&oElwO7tbaLIi7h_)^U2TYHM@Sv*qYh(plCGiP-KXBwrMB~ zlAW_ltu_;cpV(y4i!4vWAv^K1XLRTaKGUZH+0NWfRJ>3v!Vn-w`BJ~7V} zY;N|vLB+XUZ8kCSZPmn56uB-1A)Yuglc997_62(_F?dF@;>&^4*>A;!6D=~XJk z5Q#}8vDF3*t@@ds)q%V@v8heSHtbO-JvXn@wF+60$tYWMq=m!e_+>2db;Z#8_svjq zsmmNl!jbl`pZ^ zO`i`x;DB_ml;tgfBIT`FCvaP)!(GOJf=YOQl6j8{!>9q1c{hl6X~{rZRAQdmuR8wn zHX#y@u2D6Viq`DzNPqh%j3C1}({M7_W;%^H5IH735$jC28!bDolt~Kp+cp)T4+GSq z7v_eCkQ4o7REM|P`LsBh$BRPiqC)#^QhYwU+bSb(aL=<%W2*Mryx~5n!H?kBMhO( zeE6O7A$-mzZPEh$>}3^MSz~?SlCc+shi!^h31SlCWLoCD%M5Lg@QY>{cuVi11Q~%; zRqD8f2e*(0vaqrUd1e9rD86mHE8kHo^`dr4_2Qv8tNf`DHy7HU1)!5>BabKxV-x8z0-dhM})NOElZY7Xtg{;!9mNfAVB6Y)MY#??|8Ol}Ad9?il=-EfV zPTx3>V@Sc+V8ziu?h#a&t*(@Jh^-zP^_Ki##Vg5N`R~KYMD44d=umP6toJX9kFA0p zRU}z2gBDkwY)o-4JfFZ%@^mQ=T2`pf=WY$uZuczRBM^Z4D^LmsKK;0BDi~kP=R=`f z%m<*Jq6Y^9BI(}h~C1oMWe@LPU@L#8zwc#dF#qDTTa?9lc8X)aa z?gY#lr@O8^@)rVIQ|P&eF2TIUwFtp;qG-B&6>s1W%aK*i4N`49{}vwF>XuSX8}Xp^ ztS{CkK-zO?O7fduXv}TOL1RvL8WZf*n0MdO9w+~6N)h*dOOM&RDACVsEul2s0`3!K ziyXsSE|@d=;8N$C{5K4_Yr|#pJkhlpF}SVy0m~V_L?g40Vs94|;@*dOklQZsL2o9J zIPqQn@qM)OeH0JhnnKi-5sXK`3E?kG?Iwc zvJUgcrQbCWu*zoMpiXRNmGy(UjE@coFw5|lVaor3sH{0L@I^cFAhm|prMyuFB)Sd) z&9FyN@yLplhGr{U+&DoH7g=X7qJHi1Vo@ce5+!6;VD8~%L!Ni%MEZKj4EuI_#dt%R zwI*nNa#CriI(Vav^sCQlG#)Z~+s?%)3popU10V4&li^vFJ*RVDp=b3Dj!&|ScEqL+ zl!K|TRBYd%5L9c&5~OGJO9zvlLBcGmJB+#$rk?o4IrQ|XmVsCCD61LpC|MIXLgy)! zE#>p7BNU&I=ULtDSv4V-F2wd{WfgVDf0Kh98gpVR>SEpZ~*=Ej&nAeG0k^^}BOnFxO0x4gqAFqBGFvPv@ zphF-U0|@M|Ur1l;%WLXO7oY5F7oXrV4LH(?LG${SWEm`S?Mz)f10jW{4xIs0xo8@% zN3Ss003gNzD(uDlH`PQAoOalQb{lqJ8)9qPAa-Dc+I@imV&B?Ops(6IC*}*pe5I_W z;!Rr!Rh@(bGbP!P_&=B zPZ`o~f9*EyLOIw=9$&l7n8s}Pv^^>K#=T4YIC=f%Sgy5;-IfL$KsG%wAnO6^^z`@t zk6`@=&;tXOX-5#4Z5iS=+mIgz$kEO`r=v)E1#xbZ-yx{K`|6iMJ?{Oep0@w7R*X>^ zPs|1-<4Mh#QO4hx%r#ryU15+a|2u!o(B5YAW;fRk#Lqj0@{Cb#d{8mbkX7lc{-bzv zqzE5Gj}&;89%4S=xCyu|jenLx3+DdYT`ibYeW_JnT3cVfco_5LXBj3NvGj{%q984W z;RY8G>z0+fF;EullqImS)s}rvbUhvezqgEG4O%}o-f!h}cC^Sy6ViudnT3Plzh~q< z8Qh+hy)1+|V5ihB-%*(iq|r|<#Dj`9DwGgNt+%aDxFD8IHXLMsd^e6roU3lR+rZgB zFayK(q7sTy3a1f>cB1HtkS?rA?V9KqweM+1fxRrSF$_eqTz!@&!a-P9UG+`X6 zI2xOm7s&k}-ThM=t$Wp>pk#?9l>e-c^4YM|t*v7q>}(254rs3=t}7MU*ccqdRd=#Z zWAjodSB72o4IhhUNMxV8->EP=Qz0Cg%a7)66HAJuePeP`8oW-Xm-?QslXn^+n7wR5 z3X_31-PntPZck&2`ZktsyZw(}Bz8XyUI(_H zCC~j_Q@^ZzjAbANVw_biFd~`nLHJxyCNL9Jg)*_!8Cq=lN?9FXn*5U7{ZhjEv~Aez z9f8`Hu%YpuCo2+jqrd&;c`TdSlJ}59XSw$o`YN0BYxDmD_DW5mefz=bZ~Xcv z&y)rZCMc&eWNis{9>&vfXQ0y_AIj}+zaEqe=E7S}sYXk}+VcEi6eTzd#ig1Kksg3N zZ;4GQKX^D+U*@Y_f9GZX*z_`O-xrsEX)Rx<0rwDVig6pF>eIDR)AL7ErF)qd0Y2czILR;U@mslpgNI$0= zsidudN@!(A0GF1Tl-J@6tp{dbSVAQ46cgsh$Fo(a1Fb84sqS#2{*4b>oAo?_Np_E} z2akL65iZ*^$D~;=$wB#ewe#^m|Kj5(d^EM-o|Mmt5gHRka^yx_ks#-xY4mtDdibI6 zwb9Pm(Zk=ogWGz&1D7InnB6H|%6l#5rzN-tK-b;PX@36Uegr{|No*=JaDhdCL0%F%b&3A@b)ml7|812H-I)G&*pyU#An90m&WY7?$z7>C2tC6AhX+Ic-wy@5kvY+Yf$ZCdIy?Rv zgGQcvI3G+|4mU;lLcd>24WYZ9`>J8eoRtAt=NEDM5=*lZI&-h5#kCA1VmFu9)U)#NC z{*n=XYr@C|qBqMRy4x5;&%efV@9)SF;!?y=d>X#_*(=}A3!gk`bT1mc#Ot#HBT>{@ zbPD^R+Rcs^4f&@HVg?_bUx+|aHz4AJW=HtiKx{fZulPf2PtY2gSOh;FvU22qeWF|9 z@Fi!YOB~o!;(}p8tB%7eimaw&N~E}Bf-(G>P+Cax;h~AqlqUb*GKbS8j^JZuP6eTW; zt4L*cXl~OiGPjcA586GIks#+7wR$L-NYnd^jYCG-?Pl%s$<-mQcCA_gMCK9}1CR85 z8nQ@7msXIHLAbp7ZDiQ(`%K9|M0mp`R81vjC$j_2!*fqFDVALBw`PJyTNhn`)|Wwt z$k~iLhm3Sdqr-scQZWru{)!8rGO^XbRqDV<0CU@@{B@`&m;0GwCu{}nFRnWkw zPrmq5k(t)Ck?aGPG*xv-4d|f`Am?|-W^f#lSML#{9&S@JKBQ0v>c`2c5L4dfw3iOX z4APoYH;wP}4jL@N7s4oXsB{2ZyCLj#EcH22WMS!r4H%YEW`%s*IPk=|y#Z5!Tv;Qe z1Td&$^~s|wI>~FIV$ujO$M#7urNsV~E{ozc&Mie(yYAplvOXo5lapQ<1;@VfGUzYz zQJ4d+lAZQ5tu02)9avnYeq=P-k?)$lS-1Vobt3ut!1$U}3{l3~Q2J!La3rIQ`*>Nk z=6}n(-Dd5}KUrsB;I$G+uI7`u8_S-m& zKq?%t*DWv_IXYh3uZ#Vd=>-ZuMBB(yfn zf$+}C%kDf>xa_L3*~|Q8*EY1i=V{~Ak*z9(3pd$8-Iu!EUh?zL%!%9pS<^qgqeRWuCMzIpDh?d)wj zn8=Hq=2?aQ3vIeik!eJ}p~xjf<|uM0k?Xl?6!U?Gc@%T1iU9bnY zL`q4BlhwX>E@`B}d-WQCZ8|v@qMG)nomyX4J+?Sj02<&G$ z@3~NiH>s`=mowsDJTMLu)~qF`HCAk5!+25LpEoNbuQB^$>GS*1=kJK@r|)zWm4~&(nruXyEN;sWTO)4^-Stds)*}H=HprUQ||B=xLK> zQ#EbGD^1)xtqNG4%^UWpmy})Ov$jMNSK#XvAZdhe(A&9u3zdC?|3I(v^+Lq%#I6^+ z-by+Qgf1mck~!g1WiwGts1*0?9#_5OqQ%kEI2G7T4o0E+Ydk-fPU9+!c}eMhZdyQ@ zN_^E96JJBamH2lgPTpZ^SIR5DnDXUwos>(IvM~_P|1mGv*Zq@DG6(=6?xYCGmF01# zA9WgTio}<$gg`aH56kzyi^U|;+}nchOwQ`xHXCi#cB-7 zoP&)}4C%!66`%b@w_u009w5Grv@f@c$_^tJ6Wf{ zhD}5B`wGQa*II8M+u*X`@pmb!KB%LH>Ic=ftiD_;L~VBt?}`Cv1@1YT)J z%mNs!h<_0Uc`4cC8Bw>y*L)kVvMrcLNrE1AsF_+`SF-{*T~v2OJb=y@K;I!#X9K3F z0TtN>oXtlX;I&Ta-M;!*1-_Gm`KXy-t9vAM0c49n4y>kM^8h>x@yVtJU-O>|h11(w zh&jEP+vshrMP{^y1ja_*s5$cgmdWPZ2{$F;ZLeYg%qwr=0!7dFiW$_259^Jo<{3DXpd@JQ^9VgF|sR0*Q0M6f3Hs z)f}t*a(qvo zp`unMt+QJ~qhAnzE@~BmCW9bsuOp0Q@r2u!sx>AKoW6eki+yQR=Y`biG&Iq-W&}Cw z=rpKNqDbxN=T&5v;dC~fQNyXf+CXpmn)~Q3+oQeqBj#y*Rr)ufP4#LoU-PTnW{LSs z;rdJ7sE^T!)wR`$q%YtN9XOsBOeziSe4V_JKMgMueEeml-Jm%pz(fQVPNI-nO-OYe zntD2?9U8iz3_AIhiYthmrV`j+uG>T@8w2a!E%qj-6Dh#Z@|N1(8{JEkh|h^W7$WliHbt-|dJu850;m826OZPQx;qG)L{4DZASlT+Fw<5EGO%$M6whO2ZTxDKsb-@mK8P0GPc0y!ToA=tdjzlA6O5&M-Enl5 z1;+YlqD%fr=?Ww63;nrK&jw?k^kPtNwoEEJsGIoAVC@bWw1l6f;%d!~L%Hdtn-jTt z1zFnS)$I-lM$|JC>6BBwiS%{E4$UgwMZIW!U5b6$8LEhhLel2fpHM|iZ@;h12>4y=UOm8p07n%UERWujjOf{2}Yvs^*!GfYke@b=ZM*1#lLJGxJZiSt?74H1X5S7I45=aiZQ8+ zSJb91Tv3<$&Wf?A2`k2>&Rr2qja^ZnI%mbXsk2s$PmNp=N_~4pLu%-X38_<8OiZ1$ zVp8ga73ZY}ulP>tm=))zj$Cm;s{e|~sY6#>nCiRYqExRHQ&QbmOiguLaj|dZX7>g< zZ{&{1p}v*Vh~U-1WCPsIQ;f<5xP2>q?s$^;R`&I++~L-!tHDamuuUU(n029V@jCV} zz!7E+gYvFk*e!1c*L7Soxx!p?xNhZ&ay4GT+6tg z;#$SEmaCO(1J_2bjBKXg{pcxU`Gfw4u8^FS?TrrgM&gv}8~3Gi_V?f|rbDC;W2W>q z{pKJ&^U_cLzxT`^0fV0LXM1KFKAh^D@#-AB+IpDPdFp8O(eFr1Lrf?Al6&=!?Haod zzfQ|-`b~>{S*4U=F+_iT4Z(GzY2CScarNaol&e42kzB`c4dyz5>m;sIxrTCmn`B^m6*W zWr4P3=d>*w)3&U(ZCPF0vaxN;#C>wrq0SvJ2amUDUR0O53ujZObmkJ9k~%qAMu-YOZU!zRz_%*G#UP zxo+Y5A=j;3cW~Xw)y&nx%Ac*VHo34Z6GX5sDzUO=4RVfVk<}W0TntxYp&)sVNmh`)U<$=`P!18law*;1t zN!=7!UYoiuu)HpHZD9G>)Rlqd<5HIemIqT)0?X@D=LeRbo0*} zt`@EY*H5|b;ku9Ozqo$O^#`tHTu*a7%k=`+2Chw9+qhoi+QIcEm)rp}HuP_3G^0jh zv&jFIqp4>+!O=c5cXNj@5vBcqHxF-XQ4Z%|8jr7gO$FeD;ul?}f3Y z9b;8vUdPzdQhwrN3-R%f3<@}?`fe}tJ+n1Ft}x(xW-M;rJ+MD~US=EPh)O#aBk&Ey zLFelRXH=m8Z@!2gON}9(Ip}Kpu@Fa`uV;0Gb0G^3A@mRCy%73e=FUUtV)I}TI&7!m z8j2Qg0>w}AbatI6`|tR#^TukeqykRTvf;G9(v&Sz7zIoQq%+M6p)lMx_GPy)T1vdp z4l!>rCn)BIwfuyD)15_bVq#gGFuWHbLyjB5v7P8c&+Hgi+AXgTjMIExXazH=*!h#& z-6-g{p^n5)AT9(@+-h^}0+$9%s%OtgW z*iPDQ+I*&M_O#-z?RJRTPRpkhyM2u!-?n=*iU(80I&+h$!qdnbxEKLuG5|#JF8wp} zX~sU@#3wY$-3Vo1>LIvIC=snKN~DD{UmE~h)S-u3eIPxG?%G5${C6l$ z5ZnFKzTZ55D^xULu63|AYO5IdHu^sTs5oE359X_NE;V7C$u}>Q;#TweWxzlOyJoLLjl<`cnO|HiSpZFVC9nLCMe^!-- zd&w!adfmwd(;R;h&8bKG?WUzCrHY4b;TxvysY<`AW-jjhaV1sCyPBJ0lpC zFI37W^WASvP}&hs=v(A{_%_tj8mlSXwmWgB5zlA84=FG~)iS*EeR8*jRPdqMNCdfbj``-UsySC4K|r zaTdnD)IVa(jBr1|E`5?siY@NNUd53Q^>p1hy7ulS1Lk)3ttYh2;ylD7e$O%Wsk2ny zI%0aKf6RY+-(ljD>xF^ca%#qQE`K&h4#@Y<_J~1De}kCP9K@Kev^aTTyv2zrQi$;# zz%jb^cMay&!ns+~zF{gjCc65s!g>z!kqZ<)G^VhjROAhS@2IjhB5B(PU}$91CPFYd z1=@mX(NG*QB3!N&y%F> z=Z;JJ-)j_k$5f4YX{n9B+{r7VFZ_U(<%KVft{G|?bRLv@%3v~$Fmx6f%=7ifs*jjl z%_~brLMZWxFV?4o|Ed#|VvGA)2|5vxY-ggjU^-@87FB7s2Q!e{Us*{U+F(wdrm)>q zpWKUjUeSdU{gA0p3m1;+$t0IBg4nTTf?95=eRZ1$l+-}9U5eZSsxvzeg(CPtjb<0{Y&LfC0XDiD6{&>!|{ z4dHy8H)P}`+afDIS6K>Tn@yU)cU6P&1RixM^KE*4>abCREs(uxd@IwV$~Z!n6MO<_ z8_}Zn!|2s|NB3SDE{g6Q9O>Sk zcPa0ACAYM6YfI30B>uI%a43bE!q(@MM{jpNK?pZdceaDP@srVS84_kY+a6$479z|! zs}DlAvu#lCJR#z2a5$28k46|fs_6UMqN9!=baF5;CO=f&>bvt%aEfZnz2H7E9*$<@ zIXY@s849UkzUBoysrwaY2eno#-E9`Rt$OGw-*QD zif_I#_2}ELOgZU-S;u=Rey0?~c@GqfEV^I!eN@spCUt5y#Q9@hN@f0+UU;AJNh5Yy ze(F-e;JZ^y)Avl#4@q=Qb$4I$RXhir1!4z;<@Yt6#B(fJ6rQX-Nky<^PqHSqw=i-f z)9Gw-nF)TII|_Xwl-1miBtu(;_Zs<@2GuX{15~RbK%;TIRHmP0WKAJ?b5v@iKhH0{ zq!Ed%u+9CHB3RU}4mowK@1Ry~?|R?DA2Y8eS80R1=%r&gnstkBWg!yEy1+ACq1fW7%dW{4Rkl>m{YOcI|8gbBneE7z1%8<`L!_g0#+kEe2N0xsZ{j_C8#`LK{XNbKKObkE7 zIFy(zN5%_Vj&0q7)oU)Jo`km8~JC=iPJuiW<7j(^tQaRa8Gt8D7`&W443FWv^CX3<5}8q!>Sq2HD={( z#&gzZyhZ?%gvHKJmcTrM&T>1WqeTBP7~q`wDr5QuW566`2pR(EI1wSrgdaEbA|^ZR zi(|~S_s%dJ;?wrVJv2o-_W)~F&X_|8vYFI5YIL}F z4sePun3$W-$HTN<8dcrpD>V13ff(Hz10#3)u?F8qXeT7G;2ndm*FzN@ZuhxJp)aS3 zjK4*+x;j!oud(uJau)0|smvM=7+EbI%WPtgnr+-W4fe!2QLXWLc}!T=aPacB4)Y}R zX5MtR8G8wtZ)v_Wr>$cdG@`+|$2vs4psqs#=|ylfcRj{$w2FO*@kj%0?vv&@va7!O zFX1{`a7&-)-1KQogJ}ZIw1yJbaxTQ~hKlv|4%_nE8=RL+cblkA?lo=N=;ZXh*oG=Q z7fi#3Ba`5LDW|%^dx|hiQ18ILdbznBX6+dzLtiH$?JKK0p+{rb=!Aj{V{q zpa@m0&w=8d^>(}U@3hiuRS0X*H!!uMp?)LJWLmqyyd=J8q7L1f%B%7k<`rr8;u93f zlu~04MV5R^kC)QpZtU--uTKfa?kLUE;i?Tm#IkTH1^`vjTZ_}DMT)QkC?@*l^VNaNFL@^wt@rNkz2|TmBAURtY4E6%?J(Lz0Dmx>}$eq3m1PZ z2ErFpCe1oOT77n8LQZFdh-XD-WaB?&`eV3xA7Q_UzL-c9I74Jzx;KBFv5V-%h~)V7 z*4R4*RygcJa{@2zpO6KumODd2DNYqRlLx0gwXt)!*!7%9lZF1R)H0qpaEhjFlciI{%JbOIf%F=Uu zEBoYCyuyJ$Y0lG%e1yn7j6UT9H={mcAgMwQWIkm9!vvXl!8a zuX2})P!J3Zo?6GMd741ESgWl{9>a&!bI6*PdN|_-$3Q;$^{KReM4-}^a}q_SPEpP> zm2s5P^x^Y>NcYY}-&G>Ca9iBD6hw*umj798KxuT1U6b}$CHPWD1{01{E4XjHG^*<= z)VI>Pi4p=`bOJE(8gJU^+73xP<>~GNWZ}aN@=h)oP6^U6-b? z2vu~?|IFf`klm499WhBRy;X(ua}ItQTBn$@a;PhJ{iYcoPnv*btoHaF=2BfH*;uIZ~ck%7BonlVMD;^EXWeG1C?K@-%`7pi!s0FMpQvLPOzUQ4jxwr718ay=j6m0nN zOH1yp?=t!@oku3vq9POk)4~FY$*HNK_ES}R!awzC0nus-KAM8jLQgb0@kB`<17B-U zb^erdScCFeXjQl-h{K2sSrcjssyxgp;Q?Ih71lHg2Kye%4wTj&tkx5;w8I@@fZgu6_>lrENKX4tHa8k~YG?CDz;A@{?!X~$Pt|IG z$x?#7Imq|~?Ho#J>EUQlp=#1Cu?+1XrvtLVug8hcf!TJ})aa_zb=qyn; zr~Nl#ZxnPEigB_@5r0M>)BdS?8-GS`*T9LfhQ{ev=?`WL0-5^?GQx z&?@zg$SCz_sZr`}@1oSZ6s4ZO7^NPBru|nq*K}}!@{lezp@_^OaUqRYoB<^NJF5$Y z)x>gfbrUEw-SPP+ov{nO5TSckP5f}fg!uD|MMU+X&y(xEDDZq+f3W_@ijD56L9w)= z4#*S%SuRhtiD1Bl2!X`Q+>f7^qjoJoC4R221oJsd@MRIe2 zcl9U2K;+!GVfaFr*R==dvPT<_l58SKe@&@qb-O!k??Jh&w?2^@N6*+7Cs`9Z0m-1h zj?oEPg2Cr9N<=ixzlX!v#mdoUJyJINWVDE|uwTD;^WaXo`JWoXkedq98Y#nDY}WUN zW%QM!%4YY<k%316jT>oqR*$|09lC2 z>^pimmVDhu76oa7hp^g33tup7TD`Tn_N7kyK+}OpHu(8Yl6JG?Syx?}EGJMMBZ%)kY5Up!+uT zQTJ_g-=|}x{ZmLA%PE;j4EMUc4SN&Jd=vhM`od<$e@;KwWWSMDeCG1xzdkoA3w7-P z@q@CA=^dSPdYv@#Y=1nkl-{j#K8F%GKHczuZEnw-_oZv&FG%1ZfW?Gl0w}dAg+68= zO8Z954fpHp_)nwNbA8Qck%m1V_N^}{pKimHbAAH0NpgOeu8mvl#wr*in93}Gz2!wm z+~#X8*3A#|OMK1p9I36k2|MY3t0@V86KxA=V%X-MNDG77^WNs(FpJn$&U|qNW&pbr z9CRL>!?U*+usvA)X3#h8O}FrJM?RyYBZM9qpC3xhSKk|~ zwKrAkzL$FkVbHw?Ake%8DNQ5J_{4DOD)zy0Uz+dx%Mw@!WcXbTj)6C#|1jPX><@<| z&%IIatk#q&FecU~;$kiuK4mm}B$S`_UvMv9_vA;|g?BWwV*hh3g<$Vl#_Xjx`bXRv zy^Wg}Z0tV3JD~J6H*$k%h*|3dSR*NeM6@$l>v-$&gRFNjL`nGHW8;B<1MU|fVU@Jx zsqu1{OZyvDmBUVX%m@XsJE~Zdxql-{3TCviJ1X@wnbDRy$~{XjiS+1y+AHia6*k5I z6@oKjH{1J8i^eJHJ2op6hEF=LV9JKrmya+K;se~6@`M-y#cY&&a@rpafHP!0j1v)k(SKAE|beQRIy zR%*v3Y^EF_O+-Cez(2vuT4(GG8%e*9Z+;^$j1AlTKdzAt+xGOkq7TFjxsm96Pj}U4 z`_gMfx5)`gobX@ULm1p%8^7h&+W5*QzSOp?yq_O;A9%C1A4gKV$0|uRiO{YrZU~_x zS!5o3s^_%WYVptf@DVonz*DMloGPqQHY+b}cmKo6E4hs~Ye<}zWaVs1y8-ujbAA(M zCZd-n$9(YFvK~@dU*dMVMm?7NiI?dSSh1%J+3pTR6P%hO$bN+eA$5Z-C_L7*H+2PA zFmEctJ=SPJD9QgPTHLw+!t_y0K;79XCCNs7h}CI*MtGju_%mX=sUy5%Ke2_d{@(5m z1`JTOGcqXE!%J5?;+7fG0Bv*q=)|=paWvBfC)gCNvhtxv4BmRzqn$ZF}mGMMii6Bkb#Iwt^4J<-R8+ye>~Ak)m&;8?_M0LTz;3YSUR=-g}j(k|-|(prET@D)aTGV~nivh|HkBhRfxys!V-GfAinkM?`$e zo3W30hCXE^cd?IfqAEV&zXYP|h&{{$;zrs%`MXNG#Vs77-|epAwt}^~P7{gX|H>?4 zE$rdHGmFT@UyeP*gp55zoo5d*8Zo+dje)rF4;IA#g606tvy7l?_P30v@#?mg5#M2B z+PPbzy4tm2fw7EOs&@N*R|@anU;&Y#Jp1ce8zi5iG`-1%|9@>C@gOb8AP03M@fKcf z+T^PiOXI2AlOM;U9*OB!al=BW4nY z84uZs`TxS+z{8j3z)rJ+i{Qt}_n57fhWmu#Pv~1Rp{8Oa%CR^d^YupKG4&(wGhYfK*VjCqpPHeuKHN)V@Pq%` z`5|Z9#6;o9#QFIR6`MtG4}U;Nxs96UEcQ9QCGvi%Qh6$Ba3f~_WeX!{ z9({^rnvqrAz8+MO>wyTw;gC^Zz1w%^pJC&atY#xJ7e$7!Y~4rxvE)xo`brIYEs1m( zd$=4CEAQ(JyeFVl2p7s9wxQy)aDSno!P%Cr?m_Z68BjHQ07r@fJa|#@k}k za)yaC7;wN^vb|B(7WtZ0CoQ4}d`;|O+BIw@O71~6-9m|*wdv+NaKr6%wC~=A+vThY z!SK03=U>4@Vi)a~=YItOjN9FxPJ_R(^B6vzQTsDm#!JOBJIUrbs-w9dU`e>CjXx(v zDbS{hjNf8*hYrC@{vNUCOwte{KHWsa%%m3{<|LF49l>A(2{U@^mK`iBZc4W^RbU#{ z?Np*da^4vHiJ!3^yso@VLg#dZ^v#4M^n~;&IeJ3hu$_dpyjOjk+y&66Mp|&kmV#i8 zdjcmRc>0lI#W>uo1kOC83S-Ryzsime9SvTrqrsp2w$l9Eq(K&RTHP8`$~>cBlo<^B zH=m2uwb+za@n}-^S4x(aWupA=-AUMS4t-(8bUj<6uDX2qo$N7_$re z)c8#@pi5`H5pw2DVW4>GZU0yjqY%;p&i%`AW*W=UM@oT#h!XiQN0g{8{@c)+MRB#vCAvRtSgR-sk`t` z9-Gm@s{n0vqatd*%+i*xSsM>1tSYreD(h6o)6I-&R4<*Sl87mdP)1s{c%^p>A1MU0 zpk|YN{~Sy6?boELIN+;Q;9vh07+dnsed!m*7*uEK%!)`EeXZ2wA*&{DaDv5b0{6ri z@X+Y0GiX&_1ml3BH}f#vfoT>EV7B2S`&_(XTtr8pVXi3+;}P5%?jDF@s!$+(sNLjY zbjgO&9Xy!%;Mi|U^4DLHBz>&cwMLoNQxNZ`{>A>EVNjN(`!^2Mk86XW(GpKl=;stc z+^pa7V>JA&-Yn@Z?oyRmaGR=({Lm>cezQKd?TU;(_J`bM^s&$7+~1^or$=;^b_Mg$ zFywbMMma|iN30Z+AoXrSgoAs8muKU+hz9ycFd@H@|=wd{ombL zhaS7As;{rkOcRwNsjPO{(=TWB3tjY?qE`{?R7e8|uFKVDPCG!K=@@!H&QRylZg$UT znh8=PnlgF^2-0jNO);}!_B9>w=Py;&c6Y-2+2wWibM9Fl zP+vv*#1apgcf)zd(K1E8J0*7gWlQ44sUh&G@KK~04%*xY-;+kGMjsCKuqAf1 zij_2aI=KX!na+wP0kmC^f%F|Y5`Z_JvhpXRm*$)ftCp4`+z;%H~xs`u0&l` zay}IXI~pZTY5L_>BAaeUR7Jk4$xsXOdgjz&uVxf@W{Pw#|3s^lyA6G?f*es(^}GgO z#LqVR9Bu^t2LyCA7DvaSvQo;l|Ln<3FL6s&@jobG{0{=ga_O#;m)!=*60PdB^ND6! z1+|Ag1s#m6ID5owhscDh^ZW<8Kf3lb(Q#hnUuyReAbX)19$Veh-eq_c^T`^6#L|Vn zJEJ_3epuMn1UTncbB<-e22is_f=l5Um zo%PNAO13RNPsA6Wr}bgWJW-e%7--0;cfjhuP1hl_dXtF-ON{<(iUtwAh{k`b zyJZ0VYV>Cz(H`4nQkzL1Uh9nW)MmxL`RvLlG;F|WTDWMN^kyCDcci%K18+;FXs#Y6 z!vB(QnTNjW%l(yI9lJrx#n?SsW!FkC+Z>c=$=~r{m0gTRQ`&h9tK=dcJdG48y1rJ? z^}=4tIaK9n7Ej%TQcmQVnqdhjlWdWyOTCpzhSY0hHTtf0Ayp*&pV)a2Uc%OItGnn( ziwXo2v$reCOZD?=|7GfTYT~FaS}ap-?TFb%i?!XI2=1lDx<|8^*)5zdrcl<|AB!&C z+AZ&UT(@vt3L>B3cLUeQT<7o{;>r#tZVbQBd}Z}jdr_=s)J}>wXl)EV1r!En`zxUK z-Di|mD4-0-oX|(8%~e3D{Or~WlVv`Z#OQ3T=TR_W2+xG_7%u`CF>R8sDWE%s`4ou- z)2TbhA4!@{>yN~-jG)i@ANiU-;yyTH$qqz0U(-801gb7CBhJZ)B~mUD8YWS4(jKv% zKTgWeTSF=%S(OYh?^^ZFjBjghFwmGAWLJN*X9gHHCu~e3l-WJ27-}|F7UmIJ#CU;X zX2QN5*UT95=A25gME`zT0T(7IrX8omOsi771sq(ttxO&2o>DKFWg7-*HEI74G7Q@p zu&S)yz&K*sjBtOJ?}y6TXuDgA#*{(IaEg3TZ=9lbPedn=n=EPb0v+A;vUyu?>$fHx41X; z)9-e-C+J1KIZyY`+57)z_1w{LkBkQDpk7N^`YkgXcE%M8tEq$P=N^U!0ROx9Sa|ap zO~C2t=b+sE^>YuAK$Tnl+>`j7ICmQnfQFUN)yy}3CVDQE_$WnZ3i%%Q*1YT~3NrGu z@vT9+4L4-mYd8U>=yEtnaD>szMKIDTdf-1i$E}`@GA_ZJ;%>bn?lP1rB;Wd%jG9Z| z+l=ZWaqhR${sMu~sc!3S-Fr+TPYq_boI71x4MpHTgA?`tKmVB;{E1C8L$Ms@ZFFKG z=e>~g^sf18;QbO%LJWQ;6;8~8GlYBARy{VD(HX8;ofp+dj_yhO|IKU?V%rqgN_e2L z5-I~t38XyDI+Nc(lF~5NxHU%z%I%n}GbA<)QRpZ;YdLfAJ1qM4$qFd+zHb38A}nT4 zIf3VlzixAjKq90mLSy$aRZT;7p!y~dS-O>onz)+p=@#z8-=c8$_AzC7sj>XcPt|DM zRb8L>nG&Cy=yPU6MMr(X>+am|3BEA{5T`0S6YmWxy899WyMc z_=%eiO#82fj|bz=MZsCBC*i{I^XHAY5oqty8=$?8Xf2yYX))?Bm5i9-TRsS*#p&YPcrv;>bJK)(`+2} z?A@)Iq_KDJAEG4(v3HNDTWAG~!vjs%O$Wih8b6#Q=I;=vi@sHGzozmU^F~UHptDHE z?9q`Jif9|U^4Wa8xrHZ?1&vfCGw%#W6()tgx>rz=gsk!8fuh8di^WmzvPGj;>UK3H zG;UDcIXuhYQv*3`8Vm#w+OFtov+VbT7c7Lk*af4akZe#N50pk%Tch26*s1EBXOR1A zQEahTEE`Um7uel47PY36jH~B=V^$j>C|<5}r)O4c(CgXOdU81&nP@ZKY@Dkvh_3D> zXh3QBd)6>j9q9HRCYdj6;+}#`M=w3C*URS`K*x14K9vw)I@C@=RxS1CBqinl=|E+G zU5b*WtGR0yJWu~)%{n{vRi)^ku(RG2KB-f`)C0Nqm|Ze{M0w>e{<(V3x_&QVvoCdg z*OXsv%vl`01Q#|ty{CjSUhYpNcXG!za5o$kRAOC^imen#e8okYp_ zos{@}Q@tyeE#)KUpPDj>g)xAeMBP#5&^b>VD|;M_Tg!u3hys&;>&KK()ffC$@zevD zFyC>%r5Bj8Z_gxKsbq)f?I9#<<;kw!zb@g<*@AK*?f-q9nnJbzWp3cyOSq{sCY9Oc zij)pUzK8JzHDgQ?iOW`lX;xI7tKalG77n3Voxs+mR6du=Lk>#Js!{3!^X%RN9GzX? zQzYzledl=BBBkb=^@G1|$mj>Ba+lE$-k)=Sp6+w(HJ7uRq|*NvXsyUxp1-f#TG~wN;Hq zT{Fm7JUK6V8)4hp3oClLKi!70Q(*;pT2;hJtT;+#JAOD(--&f^AF#aG=I#KIsS>-U zrCiq2t-{Rz#q6l@tgV)eixj#X{G)0m5Ted`#ORgKJJu&&5Rzo5g9H;DbtW4jrm^P0 z?i{SxaK3@qSNY-F2{o&^BmefH(bc;&#bc3(UE7B;=-VKd#?!Zh2t#WSvsOx;+bsGt zTw-UEcpQp{ptH5ZDE`zkcWW;Zstp6_>0Ru?asLv6v5(POeqB5Zt-`oMOg4MJ4+Q8% zmrzW;Q9JNiTZiKctBW>MQKV;k^S%0*x<%U2Su@R-Rr*5A>T$&*iWp$!2`eKbF^z@={8yYsUpwQ-PVnsp+&KK%r zK`8xIz_Y~knHr+csR=BzTlfplANn^6W6$Bo23PJMz^E{^Oc^arP!wp~A46{s`D?ASIRg*X8;~&OGs@ldIwt95aR?Ta{>Lb~&ROTyb%l&` zCQ5M&VHgL^m}akJi-9>g3ZT+9;;1^@}2h22ftgl5moBoRnGnh^s2Yeo*ATCCyLnr(pb1 z6C71SVo(FJhSj5c^uF|S;;`(Ogc5g|2&th&Q+bqE(6h+!X}E@z;=bm-6iLN>YMsR< zLD(ONeSx7xUHs?KxHz_bT_>&SI{?p-8lKj)mv8=)yfDUw&EfZ@G~G(TJO`(RZEjd9 z!aYfc1m2sU(zCq5e>czaPUow@e#C4;*P;fc1CF8t>YVj%@6Bvx_5||e*{X~np^abs zmB;YuSxZAwH@s_8zqyIjHA*cbW&v^(CZANxhH~BuVg$)O{0TazBG5>b6@H*DRfWN& zJ9>!4OJta1qscPp`zh?(akcT^N(Y;`u`0PzB_#ZxzN3P+y5CcsV|Zt9o)nFEO}>$_ zPu=r6-<%@#>K+bbwOD%GKF(0HKu6;jR|2M)E${k=ZBQQ-t|6`Xan@3?zAj^_c(rG# zxDCr?6R=i+@ygsF<|%{`GzES^R$tvd4|+s5W1VQ4Qaj=*qu1Ty&ZbuBb(xS%Xcx23 zV+l)~xuy%rU9;#X?-6T}>uIh}xQ5gB-~Jo6id;*%p5bceI+FJpc}%OMw7;Dw`q_gx z%z*D)iP%wW(lc|DzJ8LFoHZeHK-(CWFF(=X;dE`|iW@2zGsL|eZVFGRL0dtd5Rg`H z#70O-#@Gli#{Xvpy55WFgROu=% zMAlw7P*&!xW|jk|>kR0b_K~zBP)aID0@zF217-Qa#`O{#2xB(D-7w77wSS{o@sBvK zGID5e#39--CLr60Kq=>f&+6u^XL$ast91?V$qUE$xlHiX>(IfkgB3p-5FDLX19`;GhX;83mpVG5X$;Q@-oECM z+}KlA!b8!~usq%7R$Ziby63bUJ4iUEZ-j(*`xJ~<1miy_)$5{-@&>DP(g)he)clfrEblyb1QZQaU^yq} z$dI3a?k}a!pN=C8X6s(F5@Ti2iV`lEe$i;Q+*_md)q5gy>Kpgum9f;i5iT(v@kAWA zpq+sXcVQPtB`Q2*;t4|@6f2?R5nZo5$cY;>=m3~Y&o zf&!v3TE9=}x>9Ff3SEkS&U*%QY70EC>BPMQ45fPT%&8q8naXPN@@}T6yUL?{Prd;E zFpJMrZ*1Ipw2vu+6Ka>(1_6-yn!+#U&e!|-muX|rY=OHg=mK(fjaHXke|PoI4mme5 zQuo$7-*z7OBi+fCMS(Ll=uWI;&^Z^?sI4nb+n27`5qo~4x;uQlJB)9vgcqwCJ}>68 zvp^p!Hdepk3vF@MrMlS_7Mw*U7OXSH2)k%cvhZZ|hZ!fHGJ%Bc(-#J;mEs|806DSV zId$01=GHsp9+Z4XRd01qY{L<8%)sPt0j!SLNTq?*F4J?Mdfjcsgw{?KaNrG|4k+q2 zMfwFt41&QqGh#Z5OH(?qY^3}DY=lV?JbbSP7zDot3C^NUe)reJM z_I?u8q^jLvG??1)mUH*q!D5mv(#Y|gx<(ZV|1}9*RcQG;ZdHg-}detRir2)BUXJSDbV^Fj8B~Ek(oG7z(h6>^sDrM{Ja$?BVR;?Bz2_GI} zeo-F7FRV*|k0>^|yK9%*-L>19VlJ;taab5dy@t67aN$I7dlgru`|K&23C3#H0fSSd z&z`PDP$w30#|QfrU{f(r8$Xnsf&xPPeQ)ThKfEtRk>g0}Zts@w#3~Wj;*XdOBBq8( z@kXVGaUvqNa@QWyTHDM6k`wiMhYIBPW$$+*zk%33yhx6rP~`6i4%?R=<1AkK^1meA z5e*vJc=l~oyTzSF<5yKEXX6Idq-_o}yhDjIo+nMJFE`I|Q`53SHKWdK0pd{n;qs_* zY3D>ty_pTByujx#A}e8D>GHs?3w1|lk|^On>>XZ+tf?{$gE3Vmb*3%F-=KPa%gs%? zxu2Vvx>?N4jkUqYu6yU53@Vj@3AFhv-2aY3th(?jlhj}6wR`dN8C0Lk2deEBxgE4z7FN02-68-A zC64c})CvDZH}ivro!Qk__jk>(HSXUyBZGI5ATTb~06H5OUxvS?{jH}P81(>*`Us2_ zl;OU`={gcBEMd+!DFCx+szI~=nOVxQ-JNgC#&F45D3&d?kT30@VawH<)X9L=;w7Kr zfieJ4YJ@Hr=zNopszmb?;~L0LuqEh0X(6&OI&c3(uOOdS5YB*i>my*-^2z;{0eil( zrFwvUlOMx#6$8d+)KslFg25S{8rc1!FMK{F-YbmJlIJ+zL|8hT>dKS^&+Ucp7+8K$ zX|SXRs?$fRd+|-GTNSlfvcq=1Ns;ic)_mr=-ujv&gc=5q0W{4jD=5H_ z!QR>U!0C1l60eMWo+=1B|FI>Jie$U*D5GvpCwrbp`vF?du>Isx?nBPs!OUupngjP( z+B?3c>JNes$(*mpU1j*K(NHd=>(z7PXoT0I*%hRjcmoyE?li~%J`?#P#a@Es+)KPX zM0hZ`h$zD`UWRQs8UEsBILXWK9st&~{2|W}x_{9Ovdhp#g|^4;rnG!Fy}-ir46kYM z?dZPl7LKeDjQfuFd5M~uh%KQc6YhZ1sJ6qO%iK>EGRyA*q1uJ`5%r71|9&+>?KPsH zR#Pkv8uf`ADubUB+H$OA>_=_#So`O140|=bNkl~R4F$P7+tGBGxwqcA5E<=NYoxvS z3{V3)t~+yIdbLC@@L2b$!{~swhn$DYqpEDb2?2f0jURT)yPNAit}nR?KkAlO!quN^ z5Z4f{;aoLbA+E_>x$?q(`hU+77$xwhb-tCYu{9DJu$EdQsliwZ*0QTCaq-uVBp^vz z7dm$lT0CU*++Z)F4tt2E?9^LQH9G zefh8P(sT#M4$k6TmGZHyl}cv|4E&u??{zP^ixZ&MNVdUqrNbZ^(An%9o_VDkz3P~6 zD%MnCDl)}q*5|Cr<5K6R{pVD`S36>Q&_A>I$CAYZLG=&t7S)KbW%#QEogXW4gOhA< z-jNrj;-5E>HR$|w4j<|ZO0fsQmY8*fM#C^zxUK4SUQ&MCIK=a#)LE%_x>-!vH4(8w z7wV&zw81H4`2TDVb-0_xX$a`~85J7LH>G$^^(^_EryBTh&JsPqu7fORE?$Z0OM9IjJT$y~;(=%g zI33Af0gO`S%MI&$KpMH40m;4}>1`}|1#IyOaSl=lo0F;OCPVTLAY<&{-u4xO=BvJyXw&zkncO&U#isYIQ~;FDdNU(V^o~9A zb(tU(7LwBuHLm%SI53QWrhI`oTghFXJ8+303ZEBDOf1wwT~qDLf7ZAfIh9e#HptNy zss(Dd)tl=@d|nm9G<7Y>A0avGC^4N=pUUtKbbTd5OpV2+#@F2^?=dwI(u2irtfLqs zgd-Kdbz|E9yD8$Q;nVr#MJI77_lK0LLf=@CImVksyH+|Z--BxWw6^Hkd0_%o{9FqW zvFd7J+a8hHP4PChVB2g9e=g)KFv$}Bs!o7!qwG}|0%7Vw9YOFM`^Nk$WlZ%({saT` z)-gOJ0)HAce0Fz5VVTvJz{Zh7qI2`hIjaGaZCt6U^CGOgQ^AT)IGqmrq2HbW!NliT zurNWeBKZhdSx9X2by0Wg7&XtPspx1g9FUjnPl8NG=J2mf{B$;?_Jo{Q5w~>Wp8pjr z0yN>Rev(D~IJQy6jSX55H7c1##k_iOUuLFMX9peVY0WmLUk;Auv6KKX3I8&$pqUi3 z>TuehTU2$2Xwm<$mnL+OG+Oy)>)s!J6hOr51I5850Jp#T=Oy2>pAK@Y>eb}Tz1Iuh zGdUl54(^vg2N0Qz-k;zqaHxNG1dr1f=%4!gLIVe{--BoFQu+vdTZnb&Ug=2@49)U zr+-#f$mjD{#e7tv(g}kJ|NFV`lr7y(&RGm3wal~`u99hXrhnmY-gvl5{nUotJ4~<{S%W;H61eL0y74}1??GgByW7=5KN6{K5Cp?Q3s6(IsYZLJ?x_fXTA*4 zWH|s+K_i2HcbNtU5#Xy%mg^+3KQu|en!C+r&-UmCrol%h*C!_71i3L|@0o5zzVAkA z7#v-*IXdF(NI`pkPeNW34xWIR39D?ykVE43+L=<7$#^JIoEURd5}_8Na&2!Dv|Ss6 zHRO$s>MbYF60?BAi2RST8cJA2HvKGH9W995li|n`ks~+R{d=v$BnL9fdW!OG?w&`z zG?xY|JpFXReO6D6rN*r{Ngs(r^wnA#_TKD{xI#J-TNU+C64hrlZ)@PFMkfCmUHtP` zB+B3)`naTLOn`oNEd7P<$MmpT;$(2>?T;cq>7*!Uim;S{4UoW`BltXla-2nZ^&78bsnN^lp%ovYoXXL^;w_@`F;72=eQO?rcar zRX&6HsUb0ZUNCO=Ack%V#Lntwk8bK{Dvwf*{;6}ZfJGJBa$or_UbL_UrV}lvNEi(I znl5FVJkWAi`R#CY+nFLkz35E)HL504i)#j`JD8904!Y@=^@iUfY%+2QfCnCZl zpqdlB12O1_DoEB;E+L*`Tj&qWf(NkgW~eus zTFGz=$$yv^5ij0LTQaVMab%?Bv@8~?YGCZ%`&d3m+Y-Ap8_vqEqG;UP;zk=QBRmCNESm@x>nNUYD_QU_~Md`DgNLM*3EefSM`c zpFzd$RY0Nz1?TGR?vPT2FbJSM*@u!kcRc+UDD9bo`d=&!#k5!rFZd_-mcK~8Z704w zM-}&`kU;b~yA3-abQQiz9iBY7)$YP>|1C?!=yRi}BG1e&CTgrr37i_fcPUB!{fn7d z`DrB~J=QD&kf&{luZamgPcceFLB5qIpA||BXEj<(V6-pLixcPz&q*P>;I7*c_&fQ$D0^U$=~ch@^Z(Yv~Q*~lE1Fx&t{U_ zRH+*dGHO#(X%6^QXKbVZgP2H}DTkPm`FOg9D%Keu|0QLLcmDce4+^hXDd_)6UcSMU zr_f6F|4AeManE3@hjn=wY`hhgT_Yf2M7}#O!OY9!&7YDf2Z-?T%3BS6X$Zfek5x_O zGrBPhd#}j%B$yO&XRIvKv@_hdKK7iEKzz;fsVehirXpX{B3?->ipZsUEzK|@@m9|d z(fz4__40?W^=2+6D(BJiV(JbuFH7P`ez02#LnhJ_57&%}2B3@^y8>6J02~@m;m4eO zmQ)lx8D*$$S`%~*cTa+u20MCa4JrQ(`^}?e;ohfC$fGr!tYuqcEF&u{-aDs)_kN8Y znn3WL0QO60J4XOhI-v@em&1j|r`&9k5RzOiXNf|3;rtTS#76{~d?e8?9f$*eM^Zfl<>gyp&4BLIMG3Rya|>Py^&xKvNk zL@fZ>Mpz)xEbTvmVIS<6rw9FGP2RXcpmsXUM4!D*AHxO2OyV#m4q>V9)9MzrCW{8I zE6=y*!DaNvou_WFP#igI_ZoUtF8G#)nc_tN1u3Z1D)bu8}=l!9ev( z(tP$HG}IWf)K=GU*i*jhjn6Jd#C6ssW6V3Ty?w(ckc{ue;eK8U6U~xj$t!FM-~3a# zp_5+y*H>xht{*S)6=1l$8ADB5B8!+aNzh<@$FYylP&cPw*dsNp^z)m6jFh!N;3beO zW!C>vo>dKJy(%r!2qw7GKq~fNZ2tt~bv4Y9ayz~(7+6asdCptOA5zGgql5?=TAR4a zO0GvrXmW-A9|L`RGc9~NR;k*rhP zr+b?vfm1gH(f{-Y8wH*|w4KM%BW3|X4;mdb14pas=m2_P2j_=OL7$lRj{xT62kMCf z!Rp}^8FWM$rc`J0E|is~ zl_jPemDJt@#m?+RE$M#@;ka*Jq1BpQeqBlG49cUUkirlnIGn{a3SrBucng^DR+clU zD>cv=*AK(1aYv#AIDK3ni9$pANie>)nDr0A#$1Gs*q5X6#(*N2$@cdd5>|`_ASV}hifv!aQ|yH?+@jDu0kP4-;}tn{2^GUMgh){D}cc`L)KN{Xe%a# z6SFv7RxALOFE*qxvD2AsEayU(0RGvWZ8c9=sW(G!#t%?)}I>uH}0jcgY zlYTwXOoH*J$|tGT_}cOyzx5-=vd!E=D);^&LeopmF86NlZjT%H?smAGB9o_;E>h-( z4bVs8BF=DZXvL_FMavKt!ewqBb$Ft#mA&*VKgo19KuLC*1jV;%vIWXAcV|y4Kg1QZQqM=1LZ%yCUq`+p=Fdxz5)|RTt ziOX67&cBgWmvqK&fs}9=EqV-DJR7}i%5+Lr=rY`-g6VCMoq^cx1M(x^!(j?YPPt6Lv1+Y%{;havs&8~|eXa`0txrbT^e(GF>bbZ< z?S0Yx)#VZ`C%{wC(rgN!0TJJ^9uPjb)vPg&r_{p%i5x@7!K%oeNe;#bNaFkl!Qp(# z{;#iT8w1Y*`nr4R8Nw4nQSVMbT>#C*Kl0#BJe7l=s{P^RO!Ofc#K6l=taBz*Q30!2 z{B_l>Lr8(zx8#8~AT!|UQgt;yPh|hRoBT`2w!%e9q#qy_e!#;EM+lj z`KbrrM=NsRTcUP*_^eOA6ycx*Oj_J^{e>2g$FdH`5RfUT`&!z1o!4P3nj6?GV^ts^ zs{+p0Dt}T+;D8K;@-0VywpOSAY523ssH;JvGqHm68mnMvQ@OMiP# zdd2fL-TgLB2-?hIFimHppg!GkPIMGIRH$I;qM^8UCXkgh@Fg=FMgZ5db3%iOnf>|+ zd~4hw&Xf`PF7tL-a{1Sz<(2BU#t*IKSJAHCKrjq{?&gO~;mt#sg+KF^7F`RzLe67l zW{S3qO#4%;X+HBEQrDs+7m!2`OVtxIiB(z63o)yfx_6Ww-_o#Q;iK;h{ z6Hl>*itRc3W*tn~5-NI!%84=1v`I%#8}{e_HU-tTgp1xu`!C6s79Q2Lq~qkDsM3z4 zv^#F@r_$~!dMJ31^0fKn)$muWhSL78t2;q`oEe=8dI4%L3+iCm z6YM?Nlz(=n{4#Z2w)_mh_fnkd(pUCtbBevl7R$*D>x*o$gEBB@%M>B)SEdTn>$Lw) zR_oyQDrz9crz&u_Lw{ORcBgDjTpQIHiG(SJxl7_aQI697iO$PeSpw1sh}Mg*P2LqZMF z39del-^L9ZNu5JZn_$zK)!gpKD)APk<*&-@-5&Qda|f-%i2lJn^TBBVDh38yTUEt-XfE|NEiizv=tn$~zr{KRijS?OS??(gTh+L8 zx4MO=@s#kNR||ZrfLY+UpQxBb^Fut6+06Gge=+%oDu3c<`skI>YLfd)x|Hz;UYg97 zzmvbNYIq;=fA+3Eu&-t6Z!&qAh^cnRDJr?-dyTZDkKTJN*#KOctOKje^v@pq1Y6@T z$K*mi!n~||C#SuF_fF~~_d8yNhuN&tyJY>^)Lgg9034-dkhSoo{O8#GtGxWLnWPrr zZ*i|sfr;B@GJl1RUP510JeD(q{rMvL232ck{8WooTKT(Yh1@^6cV8$PwvzNtRh%f9 z0R(Pu)#D+6q`a?N+7+K3e7}0fv~Q^g-!KE8KYC6sYK}30ZnurBY5#g}0lBwUoy)pTeYOH7^_ zSXHKG(VJ0Iy?<*U7vvw9m!>zZKX;}-o_yQn&-CXQn|V=}%%|GSEeGq*SvEh?rD=PC z%|EG2{*O6d7=#~`9){Zd8G2|lN&Tf=@;`3#N13C?Rt>L;d=4P)-nR_kQLo}3+l;T* ze7iAkpxSrr;WyRTlE7kZ0t(x)$w1Tlr3nrVrm@fnw#mf1fY%+y<9T`9Z9vb6sSz8N-}e2JI2 ziz)84{nkZ4)A*tG=~z%*DJUe3G?iEKMiF-2z}3mOa!=jR_k*#Kd1jA_9eLuW`|7?n zJo14;*f~jbR_xJ+pBek49>X;sn*SNz@Ipq8+uqq9?A_$b5 z{L#Im!$%UpAducj38f&tg*cDv+3xI%K=7_iDtJfOv{9pd%^!>VN6+HKblwG4U06cy&A5!`!UYC^d<_b_N`nW{i0X+c*3|JJ8E$F2cu37_v1|X>vhEAj6lmF^_-~v zWIoPaO*$Jx@P5g%e++Ctsa%zIzc`IHxEA2%>R5T9L@ zBq|8N_`7Gb7u)P*oxt2+fHB9+_?l+1?n<&+R;D*rdJS=3!xIF=P(fo zruV~}Deq*;OFa&7zhs4KNWzWZU@*D76Cs~K(o|Pzs{5BpA95MP|%e8LU8@}Dyj^oSU zc{1bmT<6kbi_c9AKQ>sMn$>Ww)9+Yg-?H63W1!J3k5mY`g8DelU@Q`=-S9L{SQuD^ zZ1%6T|62y@k^a&{++1nW_lkabO1LQc<+RAR*)L1cj?B!O&o%OG@yc%R@1qOM1ZdX6hi6|I3g z>M%e9D@d%$u?`hg82@VHlscKS4sEEw%-PwPKVXIO#jsZ!oYU%B#vaL-e7R1VCzhRo z_=WxIjvL#@`HREhvXg^cZb#h|ZS)4>hXi)Fmid|<;Gy1`uVqOU3bchh1`}ueei=)p z#Fd3;-ujjCO6;;EP?dNGn)Nl^4Fe_Qr&il+pI@`c=!g4w`u5=r$QQ_-_TL6aGAo$6 zAcHQ0x(k zv9_wt*!2VtYgt&OYrZoj<|Y5i_c}rfw0y)<{fLD#dG-2 z3=rMCvnCQ}?0uXUYgMx#5I|kKKvM_Ced;z3kONRS5BNdhgX9&yOo1g{fzbmBe1SI< zD4b*qEOFo{?px>Tyi3g#kuntJXq$s+|4+U!6dU7gF%5B}Vb*Et(wKVt1+p&8b}Q~K zUe7-9@2|90l725lAtC2B*hK>xrN|>v5uRR;veKDUDog0&GxnGG&yPAwXt?vs@Mz6V55$t0apHDX%dKz>WMbXedRUHGbMN4tcw1uMe2ip6u?>|WlXN` zGqpczETrVAx{H!S@kh&D^ZQi!JLY$-bx_1gsy{-vJ4Xd;<-1LV_9{~~i_008)U(aq z%vTw!sn>lg?<#-Z{4Op>Bbmokzqp)5xNhz%M>jx<`^wjt-?in>n%}3&SDW8Q%cb2X z;Zx;EKDt?3j$opjhs&8eb@OOB)3a{wDu>}mr!EV z-1@{s;&~QRwHM=+{%$Id0)WN?;fwl|Cjv*MzRy@RbIwNh$bJBon2156-qaUt3$z!W zmFE^@U$@mK`b-FN=u&&_?Hp(K!C}IVEwdppuA6aAWK?-0_@iAK!NzX#$GbBV*L2PP zGc<9{Wa2k*p$%6M3NrpEgt~mg_;+#c4~vr{7zc>`g^2tdb+pd6@*H+9))UR*pV&Q6 zu#WFw-WZDT$27uPWaVMeF9susd~s={dwc$+CJ#BW?poIpNJ~cit62)PO7zGepZX2{ zJ^wA_jrvlYM|5TtCh>0GyCzt7saYE2NDIOEgEN^uf@uX6L54tPpipZHZ`-qafco6| zOWJ?yf4H%sEUJPXHO0K=LjmLZGw#~qp+9jbm z4cxY>T=?OOr+WT4|A)PIkB{@H?#5?jNtSi7QbQeEx%%Td4W(aN=cId2^Ar*6#~u&_=)Bm71&72&w$bbe~~qcC8iSl>#bIkNp7uB1=9l0GdZ zLAXVQUz^=6EZD>wwesOJr|#bXVM*>6q>$fcGR5S*FBHzmHCb`LV)cBW<@w&awx(F zAp2XN8}B8v|N8f6#pG8NRpx6~{3vgjPd&mO1B!o2`2DKu^{+~=F@x&5U>QN6ow~uE zC@O>J#e2W|Z}XNw;d}}EOzGd7oky^kwak9=1?hVhVglv>F&7IOjwaaHPutju?+Chf zf_o+Ab+SaXAKV&Sgh5%uiHDw|WiLq$JOD(RKKpBaO~+7{C1%++A`5mgU?T_%f!SMG z%jBJ?`w~g;t?U};cX^DBVSh3Imk;1uA?4&A{t-gW1n3)b(-!Lx0X%>f;R?#!{Qs@RB*>^g&Ng&ERJ9l+8!Dy&X;keJI8b9PpF#C9v(ar%u-`#_0;g_> z4++0ez!H-f;QVVlU_Atp0P6~(3A)qRxnm8PU;?nlhVi;~i)(;Kx7>6WL0#2C{{m5h zgD+0v@=6~Y3*{jhP_J|Hx7L7g$({JuaNBS3^R#(*7X!g8?)s~V4V}BL%GN#p_GP4R zXt0>s!kv8x4dzNDr;Pl&q61xa>&07a4WL!R4n{h6l=GSdrBehF?xDlcFlon={AASZ z55GlxnUstV>@eANk3UdYzvO$dj8Kyj*uNZ_zQVgtvXIRTzo4{oqaI)t~xS=dN#tt9wDj zamxyZ>P;26GG`OQ8HjIxbi;Ii>1JByVezQK3(vq*+eW{(iP4E%?0okAcy%@4AN)E)Fnoieh4Z-M<&y|0opNKKCJZ4p{KD**E?Y zF-NcgDePg=0QUR%mw&%fC)N=V4O}aPx%K7j?EqL7RB*QXISvt7x{1Y~A?ls)<64Za zT<6vga1-;je+wy)q1b50$uh7Sn43RBUIaba$*aU3_)-}Qd4^{k%4z8Swb@08?>c>> zBLILdM0K730wM)2u$HMxlz{>Rm{p*^;R1;J{efsWpa(>QV1~t~iZ)%deSgWu&!XPy zw|-v`P?VQ%{S~~2KNrDYS+CjtOWA(iOCj(5NDcnX*Uljun(B5_eglD@-6=6kH`or> z(XhKS1h&N5c7F=t!>8NI&X)|gpB^gfJdLyB2LKV^)6=tmhXw&6t#Z5515XGf`j0RqXMuf2)0h53>A)C4Orz#;DK zy#E78`cT!~aO~9=nBq8BZ54@V%M(Q}?xTGAP}Pl+#1eZXS)EI==tWw{Bgv)tNiLu^ z%rILD^wp3BOAM0iTs(0(UVr?1c-e(+aM?P{!k>u)pDu5TazXZcL`wl6z+fQ^OrD(-^rY_Fcj zjr|Y&w0Pn?yM~&O{@!6*K=vMH{?Yk^XwMI?lxFgSr2oLzeW5Iy+{b1v=4tDaPHp{i zyzkc5mp;DOoWBI;#TUy>!(V!^4Rm`kM4{pv8Yfc}cvu6#gTrnF613d-hjCk(Bih7j z@>%IMaxT`UeWOi_Mc|@oGhpC)!Lm!ax7xJuUOa6p(r(wZ^)@ZM7f%bCEEm(Uy+8d2 zX>Zf3rNyGUaN!a71ms0XYQM*F@ZE1JsDBCmU$y>L_@I-1!1}hs*E(N2VSUs|Z=bI{ zLLYR}wjzO3tCW3e_g|tTDD|fkceBQ(*_Ih-79Ogtg~|@A5lJ{VEobk}!mhUJV@UjX z$p`T_v9S8~cvdAei}0gq1A z*kKFE$CDUquv z4je2cuosJTh&vglu$KMW6ma}jd&Ok$n%TQh7y>Y+{bXvIW^b_pjR@f2ZJ*_eg%prH z`jM))b@nP7PzCNl229}Sh_`qG2+s5J)YzbDE6L5&&EALY8fgc5yy;6;a7Dx-Y-;9$ zpPH}#l{&1U_0O-t0HBQhlVLRHD_uB+OU0>^cjf%QXhor`WM`*C;#;$K=iz(T+TmTl6kx|Jk{#s_VP3Jzm!N=%Y2mFF%T_po)IU(MIPp;j*dIbHHSEE2Orpc$kcF}M%xeg;9f!A5j4vz-WfWJ$5%ih&k%z>8YR zaDo^5{`N;!Bkk{k9+Vg)XG+YRl41lsQS#U53afphOfpe1SbdKR0PVd!%P2e?aGS$r>boqU7Ju4BNM#;LY}} z2r+zf&1K(SK`b-}AQ>xhJ$ltHw)i^RV*j>@!1zD#{hIF6E3fD#pR^wzkAvX8ru&-d z{ndNEk?5XWS5H=O|E_OjRt<;W{2ep5|DoSDbMrVUvxuQMu0M&qm_``a$7;SbUw;=A zf%ElWkiTU>nECp0`CBP}m&)H(`FjWcI{xMn_5${xqAUc<{M>x)SE>CxUGh<^M{s_J z4g>(~m4(h<-ub}gX7(!pIQsXmaAyC4u!JqwV!VF^@4~8x+OBgF77sXTbNVP4=o7d^ zXu#ubllB|l+n{@#1(Srkzu$%%G0kMjGSq`v{4S5}!9`H>r%V0~^exdjSzHgghym*a zbBn`$(3wrmbyk18>PLv4_^0!w;V*I^CQHu4k0XQG>PIexe|8W|%!}_w6cA|!QP8U` zqsQ{)q>G67qD1^1BDyER&d)$S(e=PTm38m>m-HQ7J8sOrwC&6DwVy?F*N)QccX4Uw zeC;RUgN~Mv`aMRRE}j1dO5!>RI3fg|qwq+IcfjAh>-~@T}uDW;|We$c(2;n&6z?y%v8vU^e|W>Vi7ovAYQV7oWmAPS$({4sKJg zej8Q$K9Fc@TL`A7Fh2pN5;-F@S@LJ_Vp3FEAngVPkUN&%C2HiVaX3$u{0`C(rn9$U zM6nFK8|&GAo8U$`vpW#{M9FcaA^`z%fJc6!^BpBIgkVGaJ~$AcAF6r-US?a61{E&r z##cC)91Jh)B@r`UvL1geJq8}Ps@!ouRX_2-&NDXE3wRwUpSZyN5CE7cnLUPqh7dZ5 zQGmqc7y;71!!MGNCH-@_m=avHd^)y*sX_js0Os_e+0(Uu^|xs1GZV{q)INauKT*Ez zTL^yft0?24J^T>E@$0d(l!Y|LrLt^(dh$8$Q}F1=A0Q1bJD9`{+1F-^M3E>_kO1Eq zjAC1DF5G7^(E`}QQs;3ih4o55)bj-~oi16dm6f|x^V47_@nw`n6W9CoJ&d$3KX=KO zFaPS~Ux5O8S!|e>JpJponKdq%Ywr0{bnC7wyeCS&_iYRUepdMf_+x8^@&=We{OHYm z{dQpGeEm-OdxQMFQU12d-*h!&NF&^~OKj8t~-dL~^C99{I4?ppAE{1nF?Eh~$+u7U$)mNQ7$gYY034 zCcCc9VGf=_>dPec4Swp$sxk?=T0<~$$hnd}h=W+nTB%dqKFghFG?Gg1?YKv-QkwY9 zzh@v08qC*z$A)PQcn3NszmEhS5bdqc zqre9eYfR^#G=Jc48-DW|cz1niVjQNwfYl$fj>!(FPddP2pMeibnyq}lpD_>Z_nbe@ zxDK!L(HRfHHJ#h=z72}@ZI5<7LM)x1NBYa**_NxNlb4>IE(x&-nE9QZFHgh!%D3Rf z^vCrr=mUuac;wxG^8aucOCe{Yq5u~dz_W+eU-}+4YWEsA;DD{hsR&n8QH`|gZ_}`X zB7Grg3FxD7b;rvn9Qo$pyjH&X`peLO`TCX8|El%h0Z|95ijY?-`1kwr_uKOKN%{K; z`MX*Ez8!z<6lpzR2LQ$_xWRh=rq0RGWZCKcSIiE99GtQJhf+clJyG(Lzeef!X-qB( z8Ml4;&D%lKr|_jl?7a)0(i>bzf5djJlsv_xK`J)qS73r_|l6`98JAw&NLf zzoYI;>Q>g-_{-GoQ+K<%AJBBJUvJUyUs3nh)jg~333bQReUs+TsynRiA$5Dy-K*{v zbv<=gs#~h=PgdLZeox&_9hW|J?^1VI-K@HAQujf1A6EB$>V8<=PpbO`bstyvYwA9) z?)TK4Q}-2hi&ok8m8-j4-F50-scw_HJJoGhw@2MW>JF+qqV5TG-=gk2)qRh;?^pN3 z>i)61pHcTu)qP6czfkwv>dvbBlDa=rw^ZR>qwZRDuTZy1-5b@tS=~W(PpJE_y8lDn zC)NFk;)}zx9roo^xw>Gu`8x%cOI%22;&M{}zfEF^1jj#MqJS(Vbp>*L5xNs&W$^8cL+H4e8j}kwj*vH+V9e$ROBs$1>T5{p0cE za551`^2}zhzNtBm7q7kr4&HbAf4qJBqs)Y#ZD(OS*pDp|KODnr+{=!8=|pB?G$CEw z+1Ti1(S*!!A}y)xpHpw$5)SWaYwy^*ue0msTe|o6^hR#&>yP5P<3qRK5gQzeCx(w4 zO}_rv$mrO3>J8~kcH-XSCr;kiw6%F#%eB{CzkSCwje+9i^*>i$!(kRuKU6=od$%o_ z@9;?&-y}Y>vp#O4P3L$fI~GGflVe_cPro-58^ZV;k7c|}Ha3#j?A;ejr4n(SmSFc> zdH|ZKhQ}wy;(3{E(rj`xF+Kq_8S|w0k@4|k6Dcp5@y5opUS=Yd8c$~f<<>V}d+nX~ z?7Z8x%*V4-ES*VsZ3p_gdn3`qeI5P1-M4jMa7M@Pjg5Ffne<8Tb@iFoZM5|U%Gt?x zo!90y%3DxQI35QAWY{y7?)o`0k~k6@L0yOkn$#1LI{^ydiw`|%NJGO;`LAF$_02?R zqGZs;SZ$+U5SA}T{q)yMxT(LEF4DEoJ*MSssc)ipzYE`=AHL6p@6&uybtA?NWLs=x zWH2^#%!>`mK)8CZzS`>};$!CeDDMrY$46!Ad8zRXW;F&?<|hjCy!v`CIu2a%@WXpw zVknWkm$0e#4q?8;JuiH3A{{%D@cL5;gnM2TxWUxU)t9!3bQ-yEB{IM)^b|;46N_DQkRTj{s58kW1G6vKm_us+9n7Nzez4hI#r|z)y_d#{vqwZOC zKU(1br241SeM;Tu)O|tSId!)jNe;8Fo?x$^3XWwRiA zqre`t$)&L6a&fCFmn9CaT}0A@#tbc72L&Fso(6(S+sS$x8g%(Ve=q*4Z`yu>4>NI5 zNFEs*PjeFLf;0sBnM@o`jwQ3Qy4GhbX4h|RYTkC|J-e#Pz1??vyX#wa?ARgRyYVHl z#Lz@KAP1CZiO4%e`wj^IvNzn_&zHUgkaH*@ z%a71dgz<-Ir&ZTy1hb@lY-T8yN_d^oXvDK+d3(VXlwa-PHP##H)feK>D10nTBl7lQ`qt_9~c*e5c$?=J_ zhjx1DWaikG@^W)I3fPP3Gvd;EiFA5AErZSxI2i8h>FU{sIpQ-cnb^Gv1){85GEnS< z07Cyv-(%_5**9DMFqO3K+%M(?T%o}{4d5w6NmX(uNKBWO%F}Ws3(&HyiVwTir z!V}q}K=u2C2JQ6r#4^bt475&|D7cYy;tdmt3_%Aj0KV)*dJG-`2=GyGsXYn)5LD2X z8_O$`Rp-}Yj8x;=0k=Pq21*MaGP5_4;p78W%1oHZB+|kM*hz=a?&ohsF4loIFU*W zWp|pbO-;Zr6f`y=_^#nzFq|MGe@4vDqhbNS<*y}v3 zh?;#J@@ftMJ`G_^D(Nlh3V4k~+D>{Tc79ts`%C({)5#MZV?$`m*bzUy${BvR%NJF) zzrl}TOF;jRg0g^&wb4E40SU;H6tcIR>q17hZfUal7MAB}QFaVKw#}V*WE{~5{`1FG z{X6TAPB3g(h!NK0EEZqo_wBpo^I4nEiax(90Rkji@XizN&#d< zkA%;&Vwc5Bif_rW)P!;zB#d5cglSKL_>87T64?YYTYy+P-$&t1A#LAnfsPMgI~~5d zwE2E*KvRP>r_l+A3-W0?0F7k*7f9zP*}7$`;UHngvpEV|p;oOVSeCA!RXS@N;w%XF zZb>9k4dD^+zyvhPBLXyX6zymh{*~$+etPun!e6EWe8gp#gNKuko0J*ZYsr5mF-COu zI8_|IWH!v_Y$82|Wkk?WqJ?g5l-LF3q8`Zif)|vLU9IGWJWJEC9*re7n!0dUCzcMM1l9(R9HiIza;%3XnlO z9UcaOBC>cQtwJRs9?>(fO)_|J`=b33@@3(B$CH%&jwEBq4zwQ`86Of}MZvQP91-b2 zef%oISlbWz(54)P;0lSA3X;L(5rrud{sQZqh_b$TwY6z0+c!3D>BnY_nKv{to`J}Y zH!4`Rc;SqwFeI2FY6e^-Pyx8Ytnw3p3PyN?3A8G~SgK}-QJX;*##8bNLM5WKl;8 zJpp{ZEeD~}UQE=@UM$$T*f7u>0!I9#7PLQhA~`xSYWXiwHziICLHx87$(SC<@mb7K z&@3y!026bHr_BONw$2vplt1OlC+sxGILon8-YcuI;tATt75)S%p(7ONCv;xp9y0(3 zkU=yRGA8Js^d7|L;`t<%q{mFmqdc0g>9uMCvVv*I3g6EGhe~P$T&Ov6Uou7ii3a<| zi9v-2!}vP^ZX+1tC_%w8SfGb9=HS7D4FR6Vk^KN)rreOmbdOu%)6vtbJ#&L+ZuHDA zdFGAoFWN&I%wIpza~MPqk_}l48_M;5=-!KkAA}YXgY0WNGFY@__C>-6`a8ViV9qQy zI*pVPMxD2+#1zc(Hr3xZ;ceXQ!MV!STaeBY0YtW|=-VlbAl4J8P?#sH3gbJmFvx!{ z_<>(Xq%R7F2Y>9ZBhr7HiG=aT{yuWd^6zK9VEOfdPg-|I-RaL-|EWi;+w^(s8cl!Z z3q_|wD9X7h7q8_vX@2z@>d}kwh*AQstN*p+2bcdp>#}~f*ZunYyG^va-|SVfrh`(E z7ZWYe#-OJn?sP}(kScr zQ8GfuW1{me$jCN;zJs=sKXil#vkekCbem8y4q2_Ml)*}KRoedLm%gxR#D6kiTxJ9m zmNW;bP+?!&vgr}2#XAb&R0#RPN+g(tl$-W(0)@;UMxYTRfP7=|%+c5}DDuZ7LQ-2s zvK;e00X>idq-|7SbdW`oQwuC`J(%#0U{Q#9EzMiK1_xgueKLd?Z>+WoF@-)O^1@Y@ z>BT1!1Vtt>0uiS%H9mqhk9gvQMdZWe?<4t$9Bemj2itv0f~71oK6H!ofPG-uo?|0+I@t zSw@m{2>3BNicxmG*jyjSf8l9;!n@a!Vm}{yU6_we)A?sd-69E8I++aC!x668J>6Eu z>+VunudgF2<3qPU+AD*Faw7EOaeizFAYI(_ciqv!^d8b!4jV7{?Qf6nXE@vG#tmW( zkx$}ZOMY>LAsmTF`K~Ei9?3KGH`xTl_sw47giL?txk&sL4@$?_Ao%Aa9`i}jj9{Qh)$!U^niXo3C$h(@@g&LvIN9gUz%5g>L$E!)h2d zW3Dtng@SQFaZu#Ma{Q;uhul)$9t;j)(x^SLGaeY&WLZMHf9K|&Wwwq&=Yg^*7VG|= ziyuoI_kywDe*!}QOr6L;c^Mcl%-gt~x(dIG;cX0h6GdB7-_#Q4K4amd+4JHrvMUMw zq`w#D39hONNQMgy+sEOzTx@(iVRz4fW>j^_FJEqeE)si|5KL4N!Tu-4#wU&(6=Xw> zpPzmlvR{6Bj&DjxKR0d26N0K|#<3#UpN}VGbK{8&l)!NhdLCtq%|0RO9K9U*ro`tE zfue>`P~qm|UUO5E6b;>mTSeJ#Vxj`d<5YLmo8IAJ*1So?wyR`IaxignoJ=AYWa0cZ zQX2ef^F0pbD^)mdOR@`inw}WSVjol#aZ2%JN0@8_1V`2@)v_^`4pDZraB(EchY#_e ziEOl^4_v;iXi5}E-gb&qTymu=l#^ojf^>mw0A!=1iFgv+yHHHXMk1x!0wM3XE_mQj z*${!zzC@P0@T!NVR-Yh2Wtu|T>1K3pnwiL+{yRulF&FzuKf`TpLXXAGDP#w+#Y+`@ zu9ez9@=4e}h4f+f=XLtpa?**{gE9b7pdPc}^=l5`3s9gzf$th<`Ss@JCV>ui0;CvL z8JtHzQ7qpS2r-@A&M_bj&Lw;x+KIa2hA-?_q^}qK8ZvEdZPN1GIx#eoh@k|m?z#*@ z(2hX}8}T+FF$IAB+x_7*9kLNEn-rLP+%mQ(nk#QOd4vlr)eKCA=-&RumL}B;_IE`) zyq@s>j=lS%-d1C7J%Dz(iihpuC8}s8g!^x?JWIdDP77ylBe2hGDE3?N#^%ck0_}7f zz=G~Uy6Nx_Nmvphbp&L6b@dAR2|p<|797LA1T+{}Y=~1}E@EVA!OJDn9=E@s@7~kf z#~Rr0AV0xy*B?~n%WXjQY+&B9*lB7<@dh{6^qI020a)FOB*Bp8lslBr_n+EtV@?e?~) zNL}lg6}`%-j!X|05>91q{Q&Tihn$y;D@Y65T>xHzN%VH|8Nf&F6Ov(!uyAgw&s=3! z`+Cl77f;@MJf}PuPNqR45Rb`oIF(3q*{4QF_c8iA+IoB1!+p1N4hVuCOpe5dV%Vi` z@FK9ZV6$NOku%K*#P>(TSTRAb?MS0D_yyt_?74kJ%xrF&NFBEx=yGIl0bVffVt4@^ zmQ|J6YIMBVJajiE8$p~7oJ}!1a%9;zVtiz;NTiw!LhQaF1_3P0y~*(jm}-C_rXi0s zjq*ePMHK*A?~L0od%cyr=N_=`%qi=hReP+dPgsA=qt>1JGmU@Jx~*Te?#!QCSBp1u z_vQOt_lbEZ`MV$y5RiH}J*|cd)+8Sz%IG3o&N1j1S0) zn5v}{z~8*~4T+Er>OMR%GLoY{B(dO|u=@z&|=1shT2XV@?S#hkaIKV?Cl8Z@Qb#-6qe2bT>e!f1@`V8zJRM z#5uoY>mBsSw0E^d>7mOqyW7JONtgbv{nk&H{{Hx|{&+xN*P%G=2j6)|)+Ju139M zpAj%N)E8u~J*qD3Gw5%LsjGg}acBM2E!W@3`K>Tbi^v6{o%1hHjUj6;K(lBF^7IR1|uNBQ5F#ccX)CX zbXUs*9<(M-#JGlU!BUR-sP?2zWRrPK{>f%o5N}03KY{kk=)Ot@rUY5qqgZ+||L~@g>)mjXM8rbFj9HrH0K3z9E+|lVhtLT{(b7RrkG-&F+^mr_vjm z<_TQ`)lmxRjnEnnVmzjMxrx6qf4%XBL!u9M?f$QJ+ezeLG~KENH|`Hd+d6le#{GEq zcJ;KY;$Q7UkB@^D0f`+=LN!F9t@vcPhVa+c-PO?(bu`VpeoIU9maSFgQYrvq|5ufh ztq|ZS|LxRTBs~rRq`XF0?{@WcNGlrAn8xnj-dhet+;+sP)n{`psO?qT$D#7uBo*tQ zUNC85`GR!<8q1OK<6B_92+1i69-c^R8P7)8a`~JKMQkGO2yCDw2_hl!$`tdIKdI4i zKBVp@4gaY6o7De7^>0`Id(_{m{!VpWo8!qL!SN&GDQL9CPElwu6pP|QI0aF_J_iXt zes+M!Cys%I&lp{d8eMpOJv1kWjM+4n9HC6*+(L?WF*|COpG)`ZmeWkvwXNBJbQ?g_ zY~dgMz4O8R`7rl3J0Aun|L@F;)u^{sA2uQKc3b};bqCZvs_y;jzE#~b>b_6i532i7 zbw8=@=hS^n-KW%jR^8{-eL>whbzHmTdHZbaPybyMn|QumCy?^XAM z>V8z+#Dy3^{;s5_^w(Q&F#ca6GNsJltsCUv)~dxN^I>b9%ft!_l!sJe&Ly-VEz zb>r$DRd+<)th)EBdrI90)!oz8191x{+S@vNIo^G35jv*-Km?*TbSwLMy|5ZOQT&&< zXgGR6IB@9ey7t4&&)XmFy9Ifp65kWi5lg=gDwMRI{zyj$!n^l&_a2nm@DAd>zqj30Zw|G0 z^+%*co1nM5y`iJ$wyr*DE#x2Qg|NVMxej#nwDsK{iJG2{gMPY>-o5B|zd#@iy$1W; z0ksDWTyX@%w99A#h8VLhqzVIu;oGCY0i?so?F$1=dq5MrAZ+1xu(yXrn0BOV*Y~b) z8(`PnqvmerV1Kxua`{9`H>JR_#6d8jPoEebP7Woh^*s(B_(V^Qb18Z_rKzLKSRzq0 z7A~&K_&Gnd0Ud{WBhnxnE~}{~V>A>NlvShmG{QdiM4<_H{(MZ#SZs zW0+WY!`+Rx4Z*G7(kuqsh~s|ZW~jC5w>DpY=RLc%t`s&~)>GXKqF7_InFH3Y*|X#7HjDgdw_h{_Mqf_- z*miBcdoy292tpYn#n}|L;I1*R|G*v`wZZhIVt`s)jO%7k2k&5i^WkmHTiPw=LO%5O z)x-cY7p?dSylsP(R5sCpeT-}{y$BTf(U7di#3ofUqe)Cat0v7JsL#*uM_(j6^Gnu# z*$ci{{ekF<6u|u0UV~9MSkq&YR$!Lvw`<3OWEUyT_9q9A{JG2#xLAD)Q-R)LJEj2w zn;o?RI9(*e-gF|-og7T#_{FBLEY$ih{-PLQ|xi3P4qI|5|gog%jNX>4Li1rD-9|HIGAbfU0$dptxO@c-n<>Bq(B?j!yX;^Q!R~vGMb5Xzt6BU~UP`T$e6P<;x zQ^O}50*F1HQ@T6l88l-tKYx%dWsl{&= zm47O~IXf&aFERq?9+VJjI^7_u9RL~YJ%rtHu77H|Cn*p^m`_+)N|vlX88D8^tA*!} zm*x$Qm+UR-UfX77YSyA&K-x>Q7?89k)vp zumqQCKrCgu9s4vn+noGaYA0Ab8|cibsP7FPbnsh)Jv5*0&?v}S@miZr%Fd(7k&&cI z$Et0SJsQQ9vGQeOHm^vx>tJnkK`E$-%CDb?u{a!nEl0+-;G+4^ zd~DKKvLfgtTo=y7+7g%(dL-&RF`QO&BKq5#u57d#+#v%Mqx_tm>?VNgD%5MY2}y7; zyvO}%Zo05PkD4@4DNdmWTfSIVexxADaA_PwRnEg)kZ`JJ_09m_iE#Onc9#>XV z38K9Z##qk&J1QQ`(F+YMwjBMvz~t~oL&fGoxRY}A_a45bqazaT?z*jmqAC_5596Fuzn>Zl1%(=4?@WU`PD!zY8pgl~o*UXNk9b`)j*`H7p`Nsb%BUthXT1k1 zbkeI&3g-22Ptu+!k7+otMJx%Wn*1p4KdFA)wL>-E7qe5e69}*!!;Z09?b&v66C52o zI-%GP*;Kn_Ee&Op9MQ@+ZicE+=FS*S6Wc6Oy)wNrPxGrWXb(NW}ed z%ft>7v3_BQ;FP@`%KFTfE!afpayCRh6xapKxHRew^n-2WuMOPfVOr2anD^qmWdo~f zMp#pck8ty3Fg<<@_7AiO205)yZT5Lkzuk~4v1EbILw>;@;3#SEVS)*a5*vYK&EN0s zlQ|*F;cKkx(u^See=SBpGIWB55Lv}Q!{@H^?{Hv3nc;G1MhwlxD2T1f;285sj2|r7 zz=`>4kiztGPsOXHuitXD-~#Jea1PcUXvLt|J)WAq)^2}5-ITgD$Scp!iEq_3aL5Hi=!N9h|Fxf*3E@8p>ruQh~X3f}`sU}r6g;=M#~o~kSM(6T!0*L(5ATd<;9CptOcTr zSNklChA0H2e9rqg%yclD5Do>2z*{gqM^7=2d2*hMp%}D65iZEAXTxESYsBYfm5^oM=p70ZR!x$Vf!nl%3EOiPC~hkY=>u8JLJt$`K`~#8`SKR?xoI zO0gI;jOsVP%XxfpPR=C613EP90te8**IK#uhyAT^zd9CRZ9YLJ*{SpqF$$L~iVy6G z%q3xCm7LIc0BU?m*JFF+Hm~HKzc(7Oi^rTkXOL+(BI#Q76>UlmdL@*i7n3V%b$Q0P}#9f;4kc9 zJn$@dC$@7&2%e-*rqC`N_yixCTXm#?ZGboNQKSUoR9;#@))^$&?znXl#kr*t)9AX#WG+Lg`95TQ?^Hvv}jSfLj?62lH1?7s_v zoNE#H(HwQh#tXzvt6C;9U@~%~1t(NH>1XVwejdn4b9?qZDuj41D7)7KblyuFzg)Ol z=iinbhwfGG0D(T{81(EoDF*$<@(wHr+w4<{9P$sb-k{39f&?VhXrO% z-y)__$^qN|dsqnxL!|rx7{k}>#+6ZbfIstH|6Gw0$20mw;3T4f0hka71 zET*j}*bdHfM}+hE4H6yYGWtb@BP4-qr#-pPS&XH0=w9q=;9dZ)tv~Swa=!MWBmgj} zx2P~ex?$60xS2*GOXkLg9~hIP9l$GV>WN{qhy}eRbwYB#NUkeB45B~;&|e<+D+f`KErB7QmiJC=od2a^v-8)%Qj zTZxzr-)m(bfqg>8+%At)yDMWL{UZwZcM88ap;uSgZ~{_vQ0a*jPCrN>{!o9$Nfq>y z*LNrR86RW-99&(P6F^nZCi4@v%tPrUmdJDl=jl-Iak&&^5K9czk@DL^4~deDFnSOE z>^_dT+AAkCt+%a26k{C2!b0+-(*ox>LwE)a4j zy-=QRILm^>zB~T}_m5z_ZFd)Q;gkdi6F4e}R?_|sbW_j72*0Te<$`ccjdI3y3kOUB zwa+>DxE3pusHm5VXUlVO8tym8Usc%&{HSNfnPT29#0#6Sz*&r;ydk?cHd(O!NJ-5+ zc=7S^)J^s#M$<#x4NWGgv_>B4b9`Vqe<83C-hhb*i;k09StPaxG7;1%Z zF2%D8&mDMZZ}S^?{uiES@%#wSvJmz=@buuB`na`Ud`#UZ)qP6cX?34f_c?W+S9ez3 zIdzSm>#0%$wyN8%Zl}84>PFO!s(VOX++2(v+TVd_n|n0} zonaV4_UJLh$MCU&gJGNg284$-e2(w!HvE%#cc)M{16~~(&NOYB{wTuRG@NPnYxoEG zuHj4*(eN3*YdF)mW0C8M@UG!Z)2ZnXA-q$=nFh9YsNEQbhcyx3tyP>c@4tJIwd}9sNWj!}q;5xncUf+BSxFXqkLJs6f06U$i=? zK=kmA54|+w>RkDGT(sgXJ?k z%oqGPrhDYg&I5-HpG27B!#IwT5iZAWucqgC!JJZl93!6V;*aCfWnT@)Lbn5DESwNU z`0v*=gny5|6K-lAD)5YGcsIgt)%O~__v!nyfK|V~zZdTo3a~Z8`vDCnJl$P4gr~FP zB-~&oEQ&9O1?b%WUROq*~j4fmwE8TH&eF@k#^k3ct(GQBXD z?-{?>v?qq7g8FS+?|gMj^;f?$++7pIdj@hBn`7U=`L+M8|I^aHfnmE}a4KQl8ue>3 zb9Tsv&uMt;QR_cj;JWnA^|U_)Qfe>( z7RO}_)`I-J28)b`ln+x32VXy<2Dt1ZErRu@{+1na>9u{>HmhG03C z%vjC8FL&E{tY)D(O$HUp{$~Czpr4ML4`1@?ayn1PL*xB&h4tKtqptoi*h#PHq&;96 z?H-fE2JnTvYh<_m#uJtr|JUE!c1wSYY|u>L^sJRJ!O#Ma1$r=c*b;b*pI*c!Y6CbU zI4wsY-7BwZkL*f|_KFUX=`_wB#N~+P7o|IFlqbZ#Mq&fIlAFP6^L7)+8QlENl}jWq zC<}E7uZ=Qu>2yxLN>V6UkFg2bD0{mMrL*H{TKlLNvDv+FhV=0!c+w)*qW=r+p^n#9W0fkgQ8As=cIb^3 z`0$Cy&w@DVhGaZr+w>oAk7~MdT@!fAxL>=|fyW{{2@N(VDKc7m&%Im`6)6y^3GqT-8Fpi3AtG# zm&=w$IoIh~WnQUBZO8svhkNL|Etd)_5$w zYD?1;?moSA3$PRP!B_meoH=@sqZ`+P6B%s4g@fa;;SeQTj(#j`m-cXBmb~^I07D}h zC4ZXgeyz)H4HRfCg!4(T?~3T`Fg*1~~?$lD%=; z0)Y+}vfQ{|d>y*hwNc@jOCQ7&?MGl9>cv&+8VmQWdcOLsem~*WzP}L2@}GKr zH*!P7_kZr(_caJ{vO6OqzpL`#7p;3r{jR-d-fzPPe#^RN)pZ5W{kG=&9qZ1h>+YF# zu1oLS>G#?6&d>JJpsiQ~deF!{IP6LND=v08f=K{G7cS6T?NN6W3GeCZ?utSoz`GeK zkM8Zj6$5blJGx=AZ42r+6vpAz4upmK7};?sa-e5lhv~*?H>ThL)Sg~@o|dq=#$zbg z4WAzc=7as8aFkRVVUwZma9@-c)9MfBXuEpILna zHh{%uu@Pwap5FG`ae49GH`HfnKfWMb`>*Bk+Dx4IrLn-p+p#d6Z%-m$8L*q76ksjq zpar5Ko9^nze#cebFNv7=#{7EiFbC=dZwdW^y?Bc~(gePo!h7ceA|TA)7hFCkHORQ) z3~_jm-TTC`-0p_1J{d=dAwC`yE@lotbc=qAr#G{Nb4C@Uc0(~NKhP$CJMEAKlNByb1MehFT&j;E@ zLIBzTxjHb=egO<_GLsYL5lp)-oJn`TxyP;k%P1YI`JB=#bzQoe&*#&tvjz8JzZ$)o z(R{9)F}}10^sid~e;0jc{XzQv6Rlrp7~)>W!{3y`W3GU{^G+qk<4z^h(9tKV+G}84 zMY_uHex+$#3h56cLm~Zge%eej4SxmmiB~oiUgjugrkR#=$m0ustS=u8Ed42%C&MCs zLtWt}3j0xzQSj#)HW=krm2VM4YThX&73rUuCoI1Fca2va>J5&UyKjYiSLYG;6OAk! zuc>ErUDft_>Yw_W4fp=sx~^~Mrt|Up7hR7}eLcS%wwEn#*Md7byL-86bmC4py0`W1 z#mbBQgpO{3>kU{GbZL+r`(Y&F?d|K`Z$JBFf_{&SB0qA1-^Ie^rq_$b^9EQA$9)=6 zFWRf6%l#VScWo%ZLtZ6Th=+njx^HVuCzMD#^kQ{vqud$F7yR1n)!0fnT}tgo06V39 z0Y7z%%r82|KsfR8V)!c>D2ms}101%K0q?$E*|H(NOd32_De@>Q!_P5IAi4I?r>$Gh zpThLki8g*he&@x>@+t#BWAC3_-s1JcR$A@`wkxx(XG-rmJoU20qnY#8t+|koSJMT4 zn>+V+M)~7hh<)?^#g-FM_iF4%<+w#_kHrw$`-oYV7|lUcbLu4?3w1{E)(DDno!@Ij zGrPUFc+BgA7F0!YVJ5&yxl)l59VdP{QC)1ow4R;F+7CafEQhQGPO7;HtrjCx_8RqtTrq_FFFm4*Ksm{$H!LsI`cpDy_c z|BGI9|6QmQi`>^6>Ewhh7_WVDotm3#NY{aV7FccK+_G3d!{iO8n6kL_0`|+8qTI%i z)13AN_+!r}8{(4-V|}#$0qpe@nV$|JQZTNB%zc-P=?mJW#j;9ig8hz<=Yrii1^wVg zKtl_=Dm3_2(p_Yc%1^IKCXtAEy$kLkzgT%_uiWndxY1!V=OERYvXY4Tqa@1Uw%8?DS94i$tz(z6@x@7 zQv;uc;6>56gBoAof}NBV{|6_Ic$aV6;qL=GNZN&p2CLGaggbdiENR?|uP7)>96f;Z z>fMJScH$nd4CHT~;K4AGQz3*vPiP;AgVlCF)We0GFo?VZ)Ry+%LX63^S$1}$?CsCm`B$R#I9|B5AlyxuP7Q~Nf&7ZqJ*45T!7e_o z;ia0cM%`3FIi7~!ui>uTRtU>*J+r`1M4)otcd})Jtx&*=$BQRY5kjeaFE_vV-zt96t=o?=#hBFrDF2 z58-~8=T3|HA9bZDXvBx`$!CL^HZlq~HgW_XZOtAXMP+XoPa(wqn(PlZ*xO%F@5PYO z__g4_eKTR$uh{P%J6E&$4%FUqs9e4Yyjg3bL3|wwypIjpl=fq+4cHEHIqi&w z*sz)HC7>i5YI5eA_~1i=6-zB}?Ie}{<@(D6_+e=MesGvcbNS!K;yWWPTjSe@;ybnv zH#H45H?<^oY;S7Xv15B;Xn1=|^Yu~>(`r6I!R8MXAw}i)pM;L!XRje8%aF*s-%l?H z3=hyCU^jcV%iLsDFz8ig=HFd@K?(dWtc8=b3jM*C`Fz}Pwcrj5j3a;^BEIh4gJW|E z9ykxV=?}gjB{wP_WK`= z{A?S}9>e+`=b7b$1~_QnPdB-Sw{)g*x_qiEwRoTu9N}X-7n#qXz|e{^vm#ny zR8%v5zNp^{uS^>FsCFGmGS|oS;=J_Q}nK@T$%(wBpv$R;!1pRf_ zRGM}1TC*;?(yVJ)Vb*opMr=EP6Svj|S zrUnQ`xV55)y4A&|EDcx<6g4rd2}MYo(65;*jalksHbrQn!bC8p=(>h4SM17b=R^XDf?q z<5k6FElZ1AyNr1VPgd*A_k$Zi9*jw`K^}uVrZA7-RH>QnH0E#c)F@5`{dHF_Hg%yp zOvT)inZ;8jsp5!j^I5=TV;Nxb>_Jo9f_ou%7;_p=@$-N|S&=D=9}<7rPGdZVp^uCY z`h$5w#J`rsro6dS=R>JEv)z~v;Th5VLBCDEgy|z2t4mGw3uUG}yBN4yYP{>9Q^NB; z$p_p-oI#vf5Qnw}(uV5FOx-iJb1P<+O;=A6!GScP6~&-Gi%EZSu)f`xKg9E#w((5BA6i;umNu8$v@NA( z0EA==PuaK8AHku;;(rsuY8ZyGW_-{e%oD0EGS$r$Gd3^A26a{AImEoggE+p+v%^#$ma;Apief4DSd zac(KvT4hS2mBzaV@+F>{zh(c4b4xJ}RUqbtalpAi+ECddQ}&kP>7ps%%axVp$_u4t z!wZ;m*)mfdM_tjS=vS3FbJ&1e!`(&k`s(T$^6Y|W`xyqUH>9VQPRLOv&l|MmVOAD?xuyHWWug7_Wzq7Ck z`a{dh%<^ctS>Ce5EN@0hpe8atpkH&pY|L$^us5aog8tB&a{kN z(0B%&`wsg8LdEreK{)UskS@eEqfge+h|KjDFn?MqOyu`*rU_3}$^tF~{GqkwW-Z{p z7QDsU=n|}>WoB*Psu{s8&KAK{$9s9&(C%hq!Bt=YUVx$-s_w(pDVKbUC?E4f9T&?q@Tre zM(VPC&JP1T0%<8bd5|7xTXwu&?d| zf3|FiSvFWTS243>TKED@Z|Brfz%PGJg;thf&Md*4fs6USvbiP|T8ViC+J^T$ZaK87 z+^mW&Gpm4)tI|3qZZ3dc;Cs)e0jAEUd$9Hi|$V~s69fP1hw4q$+>`I{t z+OOt{0ozW@YmDnkjM2(8+sU@;e0O287J%m<|Dp6K^0+YxJnO!T`eZy8i~kJ>+r%)y zk?}!)Fi+j(!Fo_X>L%`OC;V$L?!Zxu`&{{qrAwB!mYec)=|Ejmg}Jn)GjwU_#!x;q z?HJB1K^Y$^Gamw6g#IfH4sED38?x1ALwu>(fHiLe>e!H8JGW+L)pTgp60<740b{Y= ztb$A<(n`wFvUO%A6f&PL3K{b~+qa;X)R6& zUu@ToEC?4S@U?h8DP`zed<5Q8IK2Ancr5M4S{Lvatc}8HOFJ)=8E<*W z{2HEj&RRUo^K%?g_NnTS`As~* z){k>Hhs=-hRK95I5BhDsi||dLW2sw0<^y=zHDA!LJa?0{6)+vRA!JVAxnI);{gz(V zfL<kOIBu8{JNn0Enx=p9SU zJDx4IG=FMIs%&6!q_hcmE3hmv)1@Kv_+r4|9|;4_vfzZ@?dfgR^ z&GPtK@KbeWd1#fXNZC5hq7Ko+q^oDDrYrp32u-@M4Bu7@nRFy%p4a+<{<=#`pqIGW zRE74MQZ3Izck6zu)ch9oE@jW;bOd0hRo@ffp=OllCf?i=GC#u8eqQk=;18`XGpj!l zpyW+hXGE^9HB&c+%r!Rw7c^hKAB}+yC>Zv0X#7;@x>9o;@XN{Gb#+B%ap)@Hd740{ zRZlcmI%E5_aYd-67>M)BnbLAdOUe61%gxkS z$W)DoOt+L_^8AvouWZI)iO5|SAQwG`yx_mgNC@Ax3z@gN`VnUkXA0s%=ofgBrqCM9 z`90_t=6>~C;4TT7CG@Wjnbq`DKSr0b3g}@qXa{AL&?Uv@l23q7nyQIxtSJT$RxW(& zz(mOW5}wK*p#P$W06+R{ndtKv{xLjj7zbP^^ylR@(AR(_8RRwKOQ}aQpfkZVb=%6# zWudJ_d0bbM)f1(b0S7NxWR`?>8ppRyar`z`nvIYnH$slwh_z#5-}<>rX6mL_Pt_`4 z<9M=C%ae%=Qh_-GIV329K#pjAIAligTws4>-Y*gV#}QWcPqvSY5Bh_7>NWzFp@tCV z{0#J4qDPCMs}`?YLapx$nM;2M)FjA`Wx{abz#wMA^LB{bHCPE=+DaoxChG( zErWio4_en$xs^ZSC9>Y0`K^%oAf9JgCi{l(~9p6R+gp6M)brtV6t>!Ajdhi8xm$csjZQ`Z)oYe6$go1w3~1bXDO z^jh@Dj$gHXsWE5Hh0KwUhs+gowl6`yUDK<8Yn5hsUr^qyFg2eFnN^<-nE}lg^w+H| zH5H#&Jm;hok13XeHv<_$Bi$SX1r*fz|C zKFd{xZYno7K^EVcU2LwwdRg9L^^c+DA+ww`WaUEs-O|%tLQiEr)`G_ZO(`D)o}{2n z#(>!Nh%7N+*ZajL_4SbX6rQ6$1l&k_qM)s)HyBQOuzfe={sZyj3MP&OFK9Cv~vdeE&tE} z9M}y0VUy24Gy#5f>%qT6CbFxUZR{o~aP|jeJ1eWsu)Oml{YIQ$f6C zJu+8b*kHV3=tTj;?jPCq2mN)Mmq5pSBW0->-L(1f;>XwJ{@Ofx;l|1so1Re(bqQ{OyN7Y=NlD0>!-gu@QvRy@y zIje2T^M^}K81t_V`AU%f)UqP;aXeP<3EV}vK{)kB`EkT|8Q4Wt}4P+j!1uJwbnHc`@l3bX=;>kAhdLDKg9PJj?W?VOaaBF}CGdiwlf< z9iAC2uh5^DpSXf?GnW*Zd-1q&3;IKg7n#L-FvmfpHm-q8F7-i=0sT-3U{JHM$lQbH z(2ofxz#Z{GvVw3lJCLrf4(0EF=c5-g9Jy;Xw))$$^4MpaoER*YObcOiC2zyNG zF$?^`JezaS6rt`+s1z~f}}px@44)yK+OV&~%&c<*oGS*LoqpdVYEpp~G*q`_B|nN>j; zJKmNs=9p#InyFnyW_)*%Ny#1uWQBk~ge^|!aAvsUF5`|l)U>w9wBtF)^z3)GT>J+S z_Bkzs@j-tukMs%iE~jH&Y$7|0%rQKoOE4JMmclUTuLEhMk0`_6^8g%c(C$_|ozzE2 z8HHiM??9T+iW0Np1F$F9Gn5e)nU#BLQq`ENB9lJs?~Y=pq}=Y0 zUZ^##?M3DeJTR`*GWS5E6Gi63cr1Mc4uNLo zhhbd_q_Mp6Qt%?+Nz=-Y%KlYNP`{I^dwUkvQz#>}c9E<>O9nZPMI1M~_Fx{IeN&P7Jf16l z=J2{8jQ!7+nyFJoX5X992H8(1Z7dAK+zzCvgAK&G(2jhWMCrph@?#Qz?4t3li_mY@ z8^s)ZH(-wEGgw!Mr+I#D+W;>48pY%I*PuVtQf68%z%B*6tI!(SrU>7JwF`6n9P-WK zS^2*$T!Vh$znV*CeEo8EiJAFz)bktQahRTaV6ptB6A%cN9Tk7(6YtQ9hT2QlQV@i8Y8D;f_8WxL0j>3%lcsFLNH8iV+J;ItpFTK zFb1g)7n$F{^PuEW_$&xFdFgyzEZ~^8hYh?q_n{(Fi}KuFQNAB-DojJ(D+~G+)iv&8 zMdmyn+jpj4Y^H)?f?JEt%twpN_5UwubSdy}T`_Ehk|jImR4|oNa(KYbI6`)#Za^tX%5X5H7$_oD!$qrNj3F7SF-_v8j(qqw}(EPu8P zG;WFT?`QuGu*P#r%MAKuuN?Ed26MvSD@Pwve_3R{gU9W!2mPTHA+ur+`VucU@e7sa zFyK-JIJ$k3(4|YvrGuBu)y=G)uAN$uT0XEWBDN*u#X`%BjI8dQ%Q}nw3!M2gAOC)l z*|doGfUzqKBVB;b`g1?Pyu%ZbblCqd@CVDWdM3zez~z;|VcAQE-2T{w3iCzGFWo2h ztxrQeWu^ynPVFhV=MxFFm6r*Zdqf_l`k^S;pr^axf1X@ zyTe@T06NeBELUbDY%y2EzOW2-hiAc`E?d3G+|M#ZMmQ+`ZiKy8%Vm7fpO?pwe!@5m z<(b0rKoc$&Zf*8H0h_&#T$CSe!hw#u_f(npWUn&sj9+a|wOoV!^UbEPFWbSl*u9ww z?7zbvA-V+n?`38~vp{gljx+W1fpK2H#H@eIs=1Xj%cpCmmZqu(Dw`^>mWqvF%SyBQ z;d~C+>Z!auJ=GNH=%#YB>B1$(dtsef^Xz)i(hZnXmzr}|E;9YETVx(9(J=}5mDg(u zCCf|_b1M&@Xj2GUf;GkN1HS;h!WOI}cs6sbBc4O9pno@>?F?f(3jD!5zHCUERtlXC z^vzZtgFPA6G*{-irbXsyJnwP!VBXUI44$)EPoY1UNA+SezFsT~`^jq;nb+f)W&U!| zKFlw+-xS8lItbeTY>D}Gr2hz>vc>Eh5Eb-0FHB$nerLxb(~QUJoIq;>et^UPElj8k zdn%+w+@Bg`DBig@wH)sHO0yn1?)6#7X~3EFz?t>UBCk!?(M}KHVylO6>Yq{_i`W|T zG8o~%4g)6e)BPd*GjRV5ektF=II@K68R!b-G3I4^9@?|WOyQY~suw33{&F2Jo-#GM>5rlhR37IB5t=K=D`)bH| zPle1Bp48JJa|%!E*I?6w=iHwoz6<&b>>o~J|L`mX%V|8cgFXk@IduTM_%ZC?Ae{## z=@g!GDeN!30p;MC#?zWcD=vYK1-cLFJE-%aPGbrWbt=>`nahjJnJeJOQ&V4LydL=B zQonNw`D-$0FP;>hX*>gAMdJWD_u zFTry)o}GBQ@!W~$7@qs^OyYSzo{!`CJf5%Mc@EEa@%%HMpW!Kkecn1e4S4q8iQu^h z&oMmr;dv0x`|+H^^Cdjb;&~CzPw`Y@%aOiRU<;x8r#ap5Ma*%mp4U0#z*m)m?1L40v2q0lpEuim5g= zW|>)TR+yEh)~qtC%^Fi@)|yMqIFKpq+ zsFpA3@|tNA4aa>z+W-zEZr=Yt?VWviRMoxjS6+zGV$ay3#-g_)2L+CA5kXQ;z~KNI zYSd9uq>5%jG9i(WjCrA<#X41Lxz%dgf|pv<0WvWr5K=`%r9Sbc7!_;uAWd7r!BQLF zPDhJ1D(d-s_gW(xd(P>-&vXB{_j%6fm(O?BtiATyYrke@|9-o|4Ns;pvpVg2vh*EV zf3~QR$M=w{|8KiN`=6~VTUC!Y$c(YSq?}BU1oZlQY^#=&V)d}n!`8NKA`@BcZ2-1N zX>Q$QOAjCIrH2pq(!+*y>E{M~X_NnB?=dfjuW(y*XnouF{^fQhhjqJ2!DHpb>3_Cq z+Y)VchZa~*ak`fDn07gMSMPg!_$Kc1K-!w_O}V#UnlRrHWL|Bfa!pY<#}xR=jysmm zDQn|*kj$%?vevmdW<1~RWnN`mV+Jm@=dw&$hC69juWYr&>s|Lg<+#=HE=R8t|GdUE zl3oWK^W5h_$3n;5?(?vFU+uWmai!x9SMOoRnBzvrwT^Me^WAh?3v54LbbQtEUB{7p zU#r&`$IBdx9M`*gUM2py!Il5e@pi|AW2bo`0qTE`B@Esl>m?r{9I^Grf*- z{F38|j;A^nI!<+*<#@5y8T@TOEJm*yY&o*y*+xZ}9l%Yu3cM3fC)6sH8`3ee%eq zvB&@ANXV78+A?vs_s5&v=PsZ*&K+iY#ktGOBj3Jx z?Mntt@nDwosy=QvfRr!ujwsOBl@JYqN#bdebQQ9 zZa5lUtY-#Q>4DXzJ?ngq!=?6toTgp1N!(EB9>!$MLnAKd>~elX<3&2Nk{aQXg?jJ+ zgVC8&%Jcu+lrxO!7%?x}I5*l%9C8&I;2Y{Ms%Tnlsz%JMi$-I!Tg|=tytaB7|0=)C z9L~Q?b;1ZH@%8WSEapWvqKk-(=ZRO;sIV=V3&{>JyY!ri-ld%BGc)x(${CfUxowOx zPbG?>_U~=^WhC<>iGwRQyKE^7S5nbb6IMDI^Moy%$u&T8RK;e{mdGm+h1p@>^8l~r z#)f5H6`me5kMzBnUTihq^V@kqSUuO8m(QIshiv|J#OWgc4yS!S7$*Zoe zYoVj&SWWLWb#GRAd2PeOay|2uV|HG3rLu(Zhge><9&>9h&f`4GMNVCA`JvgE2lBW+ ztBfr(=0Q1K(U^eRtgl=WGw0@2FN(5?FU+fs@z4tMQr@*qdIWQfB_W#t7aR9`(`!kN z*Hrv(%$0f7UalOTLS$Q_`IF>WBx8cs@jQ@+G%XhmWI&+3iaM^l%}uyg+q4;_nz4nA zi%letZs-!!eBJ9wrTHi?bB(ffL`8E$E!%fqHBsY>`SQEDfv|Z-?ss`CupT2}y7Kfq z3XJ*H2xYIA3u`5c+f_94G%0fYi;Q_>g^?F)$GqnH%6D%HncQlKE8@iXU1alD`Pr-@Bj((ZdQ54lBPxFJd={C z>vGENAgZTkUp5z7N_)xQG_M}=#vP+Jxj~Gyes$es$=bAE{wS*XHL^BH+C!+e><^_ zQs%~`q0v2RF!R|dS>9t(qu~ax4fCmrl%W|KLdQL%e& z6(i;h?e7nFpBisW(;;a z@6S=8+!f1?qYm{w;uH8bI9R~=Z#SQh-XlO`rgfaeYW03Ah&3;1h8A$>i90jdD0;EHRw4(+l4}#h3`5FS*PT zq&8#lcSzHwU?$&$xDy4*0U_=~lkjOcdc0j1VYuG;*aTzlMp{P+n9u&AHUqGLOhvPp zHV9vy$dMnPg87r|82a-TD4)TnC`7&har9(+m)6Du0jQPeYgBLO7mNzzGKeh%Mho{q5q7PI2|3p`*U$9 zN5_{gWO*nT&p;dTVlCQ^kHMZ&yX?L2I~UpQGY&WE=PCN&&r{&fVW8XwbLMlu%-$T2 z3-G(};#Fur-k*QLpPNBB8s0)AXJ&fpQZ(dOh{_<#^nOkKq0J2Ij@t zFVG6g#p3TVR`?LS9eKw$xCd!@(=h)=j#KnM2!~d%zu?nw@%Pzo@iF+&4>+FEP9OZ! zN|r@+VEli&eF3JO7YBc6k25Kl$paxijMgx%xDRbqJ8Ha;#zgR918T)5l59I2)Yr84u)lQLF)4({-D~^c&qbh| z1YhoATlzze$J6j7c(JORYdd@!e0w8fjZeePn^-USUU*>-Z8EPgymK?_fbs-<9eL{- zX7U$^H=#P}hcdRF0&(;&7!%6HQnUpxMo>RqTy`JFedZg7Zy=r1({RlF))&J2 zk0Ol!st4YNNh5rq7smyg0@A5R5#=vE`5S2cGu0n=kz16C9(cpN3C9$v9AF0M6RM zc;fx}0iu*;atDYVsF*t9b`-{oe?}2}8n!>hzK8FH#G+|eA;d^A$ThB z-jlTKFcbSN9`ii=BXQv3I5d)&=|cDyr0WxZOu1ssUqXG<7vDy^@M*aI1=ca`_rm^v zwaY#L6&tVq_+#Y#@$`zLe*pz(M?88r=Mua)9Tnrn)u~zsXxbn@)7(VEudWd1g*e}gRih2@#2V-Ef2sW_t|YD07t!Q+YiFaosYmT z@3-qc2(LrxPaL-V(Ow6};TQfyzZstZT#vkU1n(GP`(VBaIQw8Fe% z_s{!tYADx6Ll!ZU^k4iw+KF$2zd_zU2s1e&#Jn8hV_7b7J~|yAfj1!avkgwkBMyyr zLhw1H?ad!cuDJ8(zd(%P8#x~L;!E-3A5alqeBYH5WM(EH9h3dJ36!&7&(Zvr&$cc8 z(-(=$)OHSEL}mCCZ21SK<@ghanVbUt+yTlda2Fb+zSxc4!i(F`L3|&qJBC;_j#vIX z2g-l&4yvJ!_z7B$_vbWFu7kZ_=6(mt#dpvaJVBdg(Xm;4nkui%X}SatimvIZk9Qbd5QN-vGsdl4mUGt zOakyqq&6dP_f)suVH&9&@iKB5w6k9Pc?^`_U@NihE68ymK8V)f{juzddw&(Ry_w^2 zbclBF;twxi9Pn-Mb#fQ<(Y`;2fpQx}(GYEl8_+>~8Y*XlH&!r{%R#*O!YmV{j<^U- z!u#_yD1XBsnoqg-4ywbO(k!zbY5f*n#28*ee<=6o5>SqT7*Th4g}>!`2N-8hoBvC9a@F==NC|(fp10VC+&zoLUs5y zc-=y~9^-IfrQJ4S@Cl@Sb^zW{#d0#=1Y8)k!0tgOv4{j?c_J$*8BYcu(~Yz&y8ox-TJ}fEbsx}jq`&EOKhWc7C*`^?XmTUR9K7xi`Yzgq*S$XZ zP4pkH`+cgB+SGkN&$x2k|MM~0PaV^oWv)lsr{eJP7Pnu(OwIs*P5|W!NPe5;BEDXH z1eM~&w(qc9cyar3)*ZeNzVdxO!~0{v{qf=c*m1>_pK>$zAQ4wCPDU&6;xx1hFD^js z_y~LyX&L+Ay|=OrP~Hot-^My(z9Be(ykiK=`LW&R1Mq7i~Hn@>{{Ceyd2H-u;`(xG>%YJ1qW1i0O_(Oa@-XBk|IQ!83EHmZe zH&GZLfzKju9l^a@IOkHHg1K95AN=w4io1Vs8~YA*#Anb-`~aNy5Zi#-gyj$0Z94*Q zeZ;oY1}AK{^Gd)MA7$TXc~kJsU$I{>ZCdPSTDsQoJ%ZP~VEf>YwO1_u+o+joJ-$dCd>nTFE87IV7oN6@YX*E6rgwA9$BW1Rj`KA> z2p`yE&wYJx7-_y{FF7BOj+6eFdd1*J_R$v8is9E;cf_}gttcNap7@3>FNBvMwHbkT zBX9qGljG7`c0B#@_=?kCj0$N-tV6T#F?bhJJHdbBnD-9LNI5?anO>x0nt0M*Z2LiY z^8q_WKHD?RfFmXeYkyL(XL%ajeGo!q$TIHj z2wuf~<&*FcID}%vmHXq?{W0zScz1s+ykg`ZLnkt=xC0g9#Q_w;i@!%yW-tfp^elRJJFN)Ubt|iU0#3eykhDfLd&TmK8@P&1Ms)Rsn>Dc z;*U>P-1?QMgmSS7Ex`L@%N29J8}(8yet@3D`{Tp?vEzy^M&st{k;=BKHEHws_FQ3tE8}KSXQsx?lSi?$y?DUiWQ3gS7AH z{_Ueqv0nFae-C-bF_?+l7oSAqnXkAX730NQ$Fn`)#WhIfeXwQ%ed2E-e>}C~u=h@( zKOgWn8vGP($BPS!h^xUz;003|L%cs;T(RXT6rfxjMq}~f7r3vz2=9*#R~-3jR7SaY zC#u1VooG4UA1AN)`Sf{=5$i$>hKT3Ki+hlFY=H$c?0yl1^PSJ!+papg$Nj5F`;P8) z|MpB`;u+^S>_Dp13n$I8`;I$j(oXZyj6 zial4kKL%Z~=_Qr)hc?A)&=5Wb?@&4YOu&Mu-S&d838`=XxN*gpKchOdFMfh*@Zyox zY#aChoQE`?5jf<08cwdEP3nZ;YNR@SFi>l~7;%08uC8NSqJ9GQ*R$>72jHkijsqM^ z{c+}sKYy=GoF-- z-$o(4xY~KK%lTfoC2rTRKi*ui>Gi0LX~p%Z1}{G0y!e*$;z6{WI_3uYkFpBAOLYg)K-}*Vn+#G&Of;-mQ zvE_N$=K4Eroj82S`4n7vm)-YyR=1gPx2@xkWmnw${b-Q+iaG0;2VOiLnNQeO;ZaHU zf4)!i$Ac?Qyaes1j#!F@@#0*R|0ywau-29PW7QS2{u*kdzW9}UI7Z{eF{lskkDFIK z{U6X+%EjY4Id0(n@$rhC4|cQNs~pZo&G;}>T)FqXCCtQ~irmZIu@m1&oAt>z3{60Sl%qI+u_9f z+3)CQ2tJ2&edUi|SIqm=EnH8~rWiu2@M7r$T*u(WUm}(J%BNav#j9QjMz4$n_EdyvM5XCj;L zJ!;E&jt{Gr^#f$f% zVLZ?7HOpVHedc+*X7r18JcIClq-FHS%`1L>*Dls8(~2LV<#=)GON<*n1V`<*$EYBD z0p;@@Uxp*61loufA4BTr04)8Z-S^{gD^htHZW&^q@S;0wsx-{A=#vklXJ5Jr)v^~cLAj{eUmO&xK>r)+PzL_xwEQ31XUb{)mBAMcNg zSA6{JFYvbxGWA9Bn_&P^$J$0)wz#QS60{c-P#f8T?8nO1xoJ&6~OE}$*E zn0*p|>)^$a$k3*EGE$vFc<#x>R8by+|6@#!cYX}Q+mV*N50?I84gnR6KiqsO$6&la zwp=mh50B>_F3QEDCNMA7O8_4IPdVQGJOTIvr1jMXUq)&_1urmckyx4|fniu>KX%hmXHU%Ww!JVM zMmoQnbNKFll3f=e_y?rz#-!#?Nt!wde|_A>&nN3Bd7 zhfllmo$$_L*0=ft??-B93%q0+-`i0hfjy`VpMuk;+crb+<`82-`7Q8ir2Tg%%>EbJ zpgazrL&JD8li&Q1#w`rZtQ>P9G3w&+Nb@a#PoiSV#Xq_7AsC$P#s|)KJ^>YLpQe5B z73Wh>aq}t{M`yhGf9b`slI{eJi5I{k2z;+B!?&lXx@F%r)iwJKJ1_n`1697xR7&|DKQ6>zBEI=8gq^ z&o@W#?hw!OKIL9ds6P5%Bbjd&?KPW5Q)}wYB3i5EYAj02^`_c1P?^&nbqcvQEF?Bf zT*$vX`-<4~EHjfjOH9mE@O~M+so-7In?)I;5oIJR%{S`w2XN6AT^r# zFGfre&r+mj3!l^xs3fZKfls4w`Q+yJ+0UI zmi+Mf8xtb(pIj6TM|Typ7K<>F1a8CYG09W?9BX-V$m}$5?L~@kQR6<5_Bq=-F%)Un3Jiko*Ke z=A`@tlX0_{s-C)0mO^VmqgBuP*Z3~-)_yU|bEr<}b8CG%Bd%8s84%QuT9#ZbXp6Ar z|9jg`75%PcDs5k%Ujl7u&5Xasc^2ZAKYG;o*;aOFFED1Tw+EH+sruZ+G!;yHXgfJF zw^zHEDL$`tNnOyzYfl@;v*E`DqxHn!5Yjkp+&9Y3Kci?|kQlZ4stSbzO&hl?+BELG z=|>+`On`rM$-=s2K^mxUnl`Sbv3_b3PjOyS(R9X=+RDa;riSWf9{syyYDLqMGnbw{ zF1VzkzP6gMJr`?e=SEY(U@?!jZE9BVp=)i--)ZffG2V1c*%T$_zP5Rpdryf%)|m^T zl#R7Z3AbDnZTjp(|HrdinN%TMrO~B4wY*OMP8(OzG^c)P!{TV;xL`}|j7o)aO&eET zLGaJG;8~xwQ+(EcK9}ON4(n6#S)VN&lNX z*45Y5-!;%R*p=!U>PmN+?qK)$?!xY(Zo;+L2l$DotDx&|+l_T83OB*$sYE)_x4wV< z!1}@Usr5ta)9Z)Vn+^FJ0vpmBOnZL2`WSCt+1}Q^=Ci)`(c6Lc!S+=9PB#R0bQE+1JH~eub`*7lI!ZdYL5+MR+ymLACAG`RkBr{9^=<0kG_YxKQ)<)D zru3%aO{OQmC(u*S6YLq^Q`l3~6Y44H3HQwJiS*R;#ClqL;yo*SOfT0Nw3k`dLrWUm zVES^*NjLWv)&u`rawiDUzIokcdZZfM)kxuI{vAZsDNJx)8ropdlr4jPc3C`g37p2ia^ T6K#n#i9~|p|9<^{G6Me&)@uu5 literal 0 HcmV?d00001 diff --git a/ext/curl/lib/libcurl_imp.lib b/ext/curl/lib/libcurl_imp.lib new file mode 100644 index 0000000000000000000000000000000000000000..917274da363c0fce1d268071d1f3dc2745147e40 GIT binary patch literal 13608 zcmc&)O>7)R7Jf;9fMa%z6Y}HuXYBa@cWlo%F`KoV-GDYCn8-Lc#L)J%jCQ2wuJ`Jz z`nvvJy{h)`Rj;wLx7M|gF@KA><)zEZ+5Ga7Y0oNkX*Ih-$=AC8;4Z+qHGm6y0DbQP z3_l_n`2s-7tN298-BR)~K2b)osF40|NrvxXIU#){%ijYCxs)XtzYic~1fM7q+a%*Z zqK>k#LNfm)fRJoA$-)D)ql|q-GXEOpqb&C*c@v)~qYp{OUO*hmD7Ht);2_DshX6t{ zS4jr%p&jMoPb34^F(0LWmt?32AY>5pg$#X7GW9cnkcqEIrjb|3R6ofa_MebxYrf8M-tYwP9BLbIqB?8`xy$ z1sX%Jo)LY-aw_#ksoL75#%P7-?-~HDaYWNVXw5)WO^8-G=D>hx%|K{!+04p;Rt=1% zR~W6D7|pLiTIVRwcpt5C#DO-dYP8BQ%~r$z+rC$(?hP~(Q8gh-WtcnV+O}85X;@P8 zpBwDBLdCDB2^tob#yEbb?0ePyx&hZVhL+Q~?(H}Ev?e`@3g1)~l4=yw^jo#MYL7%I zLljr{8^Slx+tE8HOBAW!Sq_IpYD*M}i#A;45~(dwWO2W~=T(bk-saF2MK^qG1bb7` zUFR`c>r%B}S?YQ`s-KN^C+fJ%&QBTHtLUEVIT+H2g~Kz_%1KGh*d- zi$U{=gbXp<-#tB%2`2Rz${2V8YEulW2bxx*>>O>1Va3Q78osCcmue1;F?_SO^H)8p z1Xh+d$bz@i3icUZKxecqS5|()EQHoriB_rN*Y?#^Be*ujuzaDZnrZ39kY2M{x}!Eo zLMjVGHut=S&t@LeV<^r{IAw_;^2*Wz!xI`ucF+_0ep-ig70rJ=Dde_eJI~AV_bL%;Qlzq>nPh306*e$WdY#J zd4O(=!yjP!qcMQjQ13zfnebeHG}nH`lpKk*HPa^p5hSdX!{zU zKTiRCh4nwg7`7kNkLIvErazd(a;SeggR$={@}azqc^jy&Vcrk;4qJ-=Z{YKNN?fXCr?a21|{0k{bL za1MIl_s|Q^z!c*&cd^B1yZ=;*1+EHcr z5q)EubC7sjR2^@%KE<$Vg(-Rz5jN}_?$@Ibi(5gHICikQRUBv;uY?gItE*xOUn{l{ zTgAn^Xww!z+d^--GrK~M_=ZDn*wRGGXcJ;M_VeiEqk1g!j52;D=85lUR4iGHz&la) z4Hc7{K|D`yPqCb~dw(pl3eGIN4*N;UGR_Cqfj+Q|blW|gGNNOkcI#MN64}MsODEh> zc?s-3c@{Hm@#B_lsVBNGFq~(23JKk2Xp5cewW|yvvX4SFcJhjyi{wEfED$}6#Eulv z^Ov4pVHH77kcnMbXh$bA0v8u(!nu(WT*+vMCe6x~QJM~@dWCaa*agg=g-6@KWyvyu zvm(tnz6bM`^7MoyE$WoGH5L&YugP5yD-}8)!)P9Qw8LQ#)77vH9!MMRg=6F~MV~P& z89KgH6&RyUHtj}OVRlptTnA+bPD80ie}|4j3m0>W>t!yHN}=n&ws)B7Zgaj^F3V8;*KW;otIO*C{PnKlJ15|upZK>=9RrLR z={H~WxA*UizM@pPhKc)HQO|Yg`O|y}&mi6?M!NJngOC59{8#BG@VO4z&lC=C#p&0L zk6%zUPFU9=^VxBH_F^0?ey3QYCg60>Nwnd$wfu?lZ{6*EskGCmHEX-A%;e@&=H`u! z7gfIT5Mc2lLIA^Uk?O&}kINKef%TnQrH=3V*4^*m7kt041VS;47owZI4j^B5Pg`W; zh(}>^0)vGhz~n=VuxG>{^%J!N9tpzVao9xVP{DNDiPFWIn`ovi<@J{qF;kAqneuOZ zEqY!2dSRI<#jp)B#byeg(dZ{;%D=Rto?@o_CNm((xZ=qW`?}4?gVeotMIC=L;rjXx z30%GASYLVFQDS|H^~Hbdu&&paI7!Q&z_SFwl8gaZI??RU2VWgcwhVF|GuXoTigBdo%dIN69BG}HoW~756}xe= z)N9+-mnRH19T;*tmQK`{xy$yp;k68M@sX1e7poppw=8$X&V^S$==M1mMYNoZr4zNw z)r(J-nZq+( z2$Jka`jRKI4~SFssL86XLpdEwC+kxO z+gOB+{_lk`@X<`h=IGIJ8=Jnq=A`LYg~O*!*je#zoKv!NvUawK@G>}tlMzPSK_fMv zV`WU4eELqGQKVlPu2ny6^2rYdoQ$Ot^`+}HIumdOA0t@xhiF%u`6=#2WhnO9Xo2*5XFLoSY z#EJUR6%{R+Tt~4BIQ-RegzZTDm1Eb*1$_Kt9_v5unUnRUqjh0auLOMh zBV +#include +#include + +namespace openspace { + +class DownloadEngine { +public: + typedef std::function DownloadFinishedCallback; + + static bool downloadFile( + const std::string& url, + const ghoul::filesystem::File& file, + DownloadFinishedCallback callback = DownloadFinishedCallback() + ); +}; + +} // namespace openspace + +#endif // __DOWNLOADENGINE_H__ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2c1f8518cb..927c51df83 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -30,6 +30,7 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/abuffer/abuffersinglelinked.cpp ${OPENSPACE_BASE_DIR}/src/abuffer/abuffervisualizer.cpp ${OPENSPACE_BASE_DIR}/src/engine/configurationmanager.cpp + ${OPENSPACE_BASE_DIR}/src/engine/downloadengine.cpp ${OPENSPACE_BASE_DIR}/src/engine/logfactory.cpp ${OPENSPACE_BASE_DIR}/src/engine/moduleengine.cpp ${OPENSPACE_BASE_DIR}/src/engine/openspaceengine.cpp @@ -95,6 +96,7 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/abuffer/abuffersinglelinked.h ${OPENSPACE_BASE_DIR}/include/openspace/abuffer/abuffervisualizer.h ${OPENSPACE_BASE_DIR}/include/openspace/engine/configurationmanager.h + ${OPENSPACE_BASE_DIR}/include/openspace/engine/downloadengine.h ${OPENSPACE_BASE_DIR}/include/openspace/engine/logfactory.h ${OPENSPACE_BASE_DIR}/include/openspace/engine/moduleengine.h ${OPENSPACE_BASE_DIR}/include/openspace/engine/openspaceengine.h diff --git a/src/engine/downloadengine.cpp b/src/engine/downloadengine.cpp new file mode 100644 index 0000000000..7fb0a22fd0 --- /dev/null +++ b/src/engine/downloadengine.cpp @@ -0,0 +1,73 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include + +#ifdef OPENSPACE_CURL_ENABLED +#include +#endif + +namespace { + const std::string _loggerCat = "DownloadEngine"; + + size_t writeData(void* ptr, size_t size, size_t nmemb, FILE* stream) { + size_t written; + written = fwrite(ptr, size, nmemb, stream); + return written; + } +} + +namespace openspace { + +bool DownloadEngine::downloadFile( + const std::string& url, + const ghoul::filesystem::File& file, + DownloadFinishedCallback callback) +{ + CURL* curl = curl_easy_init(); + if (curl) { + FILE* fp = fopen(file.path().c_str(), "wb"); + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); + CURLcode res = curl_easy_perform(curl); + curl_easy_cleanup(curl); + fclose(fp); + + if (res != CURLE_OK) { + LERROR("Error downloading file 'url': " << curl_easy_strerror(res)); + return false; + } + + if (callback) + callback(file); + return true; + } +} + +} // namespace openspace diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 7197d9249d..6d6b57e369 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -72,7 +73,6 @@ #endif #endif - using namespace openspace::scripting; using namespace ghoul::filesystem; using namespace ghoul::logging; @@ -366,6 +366,9 @@ bool OpenSpaceEngine::initialize() { // Load a light and a monospaced font loadFonts(); + ghoul::filesystem::File f("d:/foo.txt"); + DownloadEngine::downloadFile("http://curl.haxx.se/libcurl/c/example.html", f); + _gui->initialize(); return true; diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 048f9249f3..e1f986226f 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -173,6 +173,25 @@ function (add_external_dependencies) target_include_directories(libOpenSpace SYSTEM PUBLIC ${SPICE_INCLUDE_DIRS}) target_link_libraries(libOpenSpace ${SPICE_LIBRARIES}) + # Curl + if (WIN32) + set(CURL_ROOT_DIR "${OPENSPACE_EXT_DIR}/curl") + target_include_directories(libOpenSpace SYSTEM PUBLIC ${CURL_ROOT_DIR}/include) + target_link_libraries(libOpenSpace ${CURL_ROOT_DIR}/lib/libcurl_imp.lib) + target_compile_definitions(libOpenSpace PUBLIC "OPENSPACE_CURL_ENABLED" "CURL_STATICLIB") + copy_files(OpenSpace "${CURL_ROOT_DIR}/lib/libcurl.dll") + else () + find_package(curl) + if (CURL_FOUND) + target_include_directories(libOpenSpace SYSTEM PUBLIC ${CURL_INCLUDE_DIRS}) + target_link_libraries(libOpenSpace ${CURL_LIBRARIES}) + target_compile_definitions(libOpenSpace PUBLIC "OPENSPACE_CURL_ENABLED") + endif () + endif() + + # add_subdirectory(${OPENSPACE_EXT_DIR}/curl) + # target_link_libraries(libOpenSpace + endfunction () From 38c24774926244066fd59718485871265a3777fc Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 27 May 2015 17:14:20 +0200 Subject: [PATCH 085/329] Rename DownloadEngine to DownloadManager --- .../{downloadengine.h => downloadmanager.h} | 20 +++++++++++++------ openspace.cfg | 1 + src/CMakeLists.txt | 4 ++-- ...downloadengine.cpp => downloadmanager.cpp} | 14 ++++++++++--- src/engine/openspaceengine.cpp | 14 +++++++++---- 5 files changed, 38 insertions(+), 15 deletions(-) rename include/openspace/engine/{downloadengine.h => downloadmanager.h} (83%) rename src/engine/{downloadengine.cpp => downloadmanager.cpp} (91%) diff --git a/include/openspace/engine/downloadengine.h b/include/openspace/engine/downloadmanager.h similarity index 83% rename from include/openspace/engine/downloadengine.h rename to include/openspace/engine/downloadmanager.h index 2ee3fad372..c72ef39c34 100644 --- a/include/openspace/engine/downloadengine.h +++ b/include/openspace/engine/downloadmanager.h @@ -22,26 +22,34 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#ifndef __DOWNLOADENGINE_H__ -#define __DOWNLOADENGINE_H__ +#ifndef __DOWNLOADMANAGER_H__ +#define __DOWNLOADMANAGER_H__ +#include #include #include #include namespace openspace { -class DownloadEngine { +class DownloadManager : public ghoul::Singleton { public: - typedef std::function DownloadFinishedCallback; + typedef std::function DownloadFinishedCallback; - static bool downloadFile( + DownloadManager(std::string requestURL); + + bool downloadFile( const std::string& url, const ghoul::filesystem::File& file, DownloadFinishedCallback callback = DownloadFinishedCallback() ); + +private: + std::string _requestURL; }; +#define DlManager (openspace::DownloadManager::ref()) + } // namespace openspace -#endif // __DOWNLOADENGINE_H__ +#endif // __DOWNLOADMANAGER_H__ diff --git a/openspace.cfg b/openspace.cfg index 1fb1965c3e..d9325aa65b 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -53,6 +53,7 @@ return { Type = "text", File = "${BASE_PATH}/Properties.txt" }, + DownloadRequestURL = "http://openspace.itn.liu.se/request.cgi", RenderingMethod = "ABufferSingleLinked" -- On Windows and Unix -- RenderingMethod = "ABufferFrameBuffer" -- On Mac due to OpenGL 4.1 restrictions } \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 927c51df83..e63c0e4361 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -30,7 +30,7 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/abuffer/abuffersinglelinked.cpp ${OPENSPACE_BASE_DIR}/src/abuffer/abuffervisualizer.cpp ${OPENSPACE_BASE_DIR}/src/engine/configurationmanager.cpp - ${OPENSPACE_BASE_DIR}/src/engine/downloadengine.cpp + ${OPENSPACE_BASE_DIR}/src/engine/downloadmanager.cpp ${OPENSPACE_BASE_DIR}/src/engine/logfactory.cpp ${OPENSPACE_BASE_DIR}/src/engine/moduleengine.cpp ${OPENSPACE_BASE_DIR}/src/engine/openspaceengine.cpp @@ -96,7 +96,7 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/abuffer/abuffersinglelinked.h ${OPENSPACE_BASE_DIR}/include/openspace/abuffer/abuffervisualizer.h ${OPENSPACE_BASE_DIR}/include/openspace/engine/configurationmanager.h - ${OPENSPACE_BASE_DIR}/include/openspace/engine/downloadengine.h + ${OPENSPACE_BASE_DIR}/include/openspace/engine/downloadmanager.h ${OPENSPACE_BASE_DIR}/include/openspace/engine/logfactory.h ${OPENSPACE_BASE_DIR}/include/openspace/engine/moduleengine.h ${OPENSPACE_BASE_DIR}/include/openspace/engine/openspaceengine.h diff --git a/src/engine/downloadengine.cpp b/src/engine/downloadmanager.cpp similarity index 91% rename from src/engine/downloadengine.cpp rename to src/engine/downloadmanager.cpp index 7fb0a22fd0..023142cdfb 100644 --- a/src/engine/downloadengine.cpp +++ b/src/engine/downloadmanager.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include @@ -32,7 +32,7 @@ #endif namespace { - const std::string _loggerCat = "DownloadEngine"; + const std::string _loggerCat = "DownloadManager"; size_t writeData(void* ptr, size_t size, size_t nmemb, FILE* stream) { size_t written; @@ -43,7 +43,14 @@ namespace { namespace openspace { -bool DownloadEngine::downloadFile( +DownloadManager::DownloadManager(std::string requestURL) + : _requestURL(std::move(requestURL)) +{ + curl_global_init(CURL_GLOBAL_ALL); + // Check if URL is accessible +} + +bool DownloadManager::downloadFile( const std::string& url, const ghoul::filesystem::File& file, DownloadFinishedCallback callback) @@ -70,4 +77,5 @@ bool DownloadEngine::downloadFile( } } + } // namespace openspace diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 6d6b57e369..14f7c8d481 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -47,7 +47,7 @@ #include #include #include -#include +#include #include #include @@ -88,6 +88,7 @@ namespace { const std::string KeyRenderingMethod = "RenderingMethod"; const std::string DefaultRenderingMethod = "ABufferSingleLinked"; + const std::string KeyDownloadRequestURL = "DownloadRequestURL"; const std::string DefaultOpenGlVersion = "4.3"; @@ -301,8 +302,13 @@ bool OpenSpaceEngine::initialize() { SysCap.detectCapabilities(); SysCap.logCapabilities(); + std::string requestURL = ""; + bool success = configurationManager()->getValue(KeyDownloadRequestURL, requestURL); + if (success) + DownloadManager::initialize(requestURL); + // Load SPICE time kernel - bool success = loadSpiceKernels(); + success = loadSpiceKernels(); if (!success) return false; @@ -366,8 +372,8 @@ bool OpenSpaceEngine::initialize() { // Load a light and a monospaced font loadFonts(); - ghoul::filesystem::File f("d:/foo.txt"); - DownloadEngine::downloadFile("http://curl.haxx.se/libcurl/c/example.html", f); + //ghoul::filesystem::File f("d:/foo.txt"); + //DownloadEngine::downloadFile("http://curl.haxx.se/libcurl/c/example.html", f); _gui->initialize(); From 2d145faced808b2863996629e7cbcd056680beb2 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 28 May 2015 11:46:54 +0200 Subject: [PATCH 086/329] Set the fisheye border color to black --- src/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index e2340f90e9..7adc2126a2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -106,6 +106,8 @@ int main(int argc, char** argv) { _sgctEngine->setExternalControlCallback(mainExternalControlCallback); _sgctEngine->setCharCallbackFunction(mainCharCallback); + _sgctEngine->setFisheyeClearColor(0.f, 0.f, 0.f); + // set encode and decode functions // NOTE: starts synchronizing before init functions sgct::SharedData::instance()->setEncodeFunction(mainEncodeFun); From fc3faab5f1f1593c25ca72ee0831f95f0f43e222 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 28 May 2015 20:33:22 +0200 Subject: [PATCH 087/329] Replace commandline argument with automatically detecting the supported OpenGL version --- include/openspace/engine/openspaceengine.h | 2 +- src/engine/openspaceengine.cpp | 15 +------- src/main.cpp | 44 ++++++++++++++-------- 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/include/openspace/engine/openspaceengine.h b/include/openspace/engine/openspaceengine.h index 11f1eee190..08a25948d9 100644 --- a/include/openspace/engine/openspaceengine.h +++ b/include/openspace/engine/openspaceengine.h @@ -59,7 +59,7 @@ namespace scripting { class OpenSpaceEngine { public: - static bool create(int argc, char** argv, std::vector& sgctArguments, std::string& openGlVersion); + static bool create(int argc, char** argv, std::vector& sgctArguments); static void destroy(); static OpenSpaceEngine& ref(); diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 7197d9249d..d38555a20e 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -96,7 +96,6 @@ namespace { struct { std::string configurationName; std::string sgctConfigurationName; - std::string openGlVersion; } commandlineArgumentPlaceholders; } @@ -148,8 +147,7 @@ OpenSpaceEngine& OpenSpaceEngine::ref() { bool OpenSpaceEngine::create( int argc, char** argv, - std::vector& sgctArguments, - std::string& openGlVersion) + std::vector& sgctArguments) { ghoul::initialize(); @@ -260,10 +258,6 @@ bool OpenSpaceEngine::create( sgctConfigurationPath = commandlineArgumentPlaceholders.sgctConfigurationName; } - openGlVersion = commandlineArgumentPlaceholders.openGlVersion; - if (openGlVersion != DefaultOpenGlVersion) - LINFO("Using OpenGL version " << openGlVersion); - // Prepend the outgoing sgctArguments with the program name // as well as the configuration file that sgct is supposed to use sgctArguments.insert(sgctArguments.begin(), argv[0]); @@ -401,13 +395,6 @@ bool OpenSpaceEngine::gatherCommandlineArguments() { "the OpenSpace configuration file"); _commandlineParser->addCommand(sgctConfigFileCommand); - commandlineArgumentPlaceholders.openGlVersion = DefaultOpenGlVersion; - CommandlineCommand* openGlVersionCommand = new SingleCommand( - &commandlineArgumentPlaceholders.openGlVersion, - "-ogl", "-o", - "Sets the OpenGL version that is to be used; valid values are '4.2' and '4.3'"); - _commandlineParser->addCommand(openGlVersionCommand); - return true; } diff --git a/src/main.cpp b/src/main.cpp index 7adc2126a2..117e7d435f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -47,6 +47,21 @@ void mainDecodeFun(); void mainExternalControlCallback(const char * receivedChars, int size); void mainLogCallback(const char* msg); +std::pair supportedOpenGLVersion () { + glfwInit(); + glfwWindowHint(GLFW_VISIBLE, GL_FALSE); + GLFWwindow* offscreen = glfwCreateWindow(128, 128, "", nullptr, nullptr); + glfwMakeContextCurrent(offscreen); + + int major, minor; + glGetIntegerv(GL_MAJOR_VERSION, &major); + glGetIntegerv(GL_MINOR_VERSION, &minor); + + glfwDestroyWindow(offscreen); + glfwWindowHint(GLFW_VISIBLE, GL_TRUE); + return { major, minor }; +} + //temporary post-FX functions, TODO make a more permanent solution to this @JK void postFXPass(); void setupPostFX(); @@ -60,13 +75,13 @@ namespace { } int main(int argc, char** argv) { + auto glVersion = supportedOpenGLVersion(); + // create the OpenSpace engine and get arguments for the sgct engine std::vector sgctArguments; - std::string openGlVersion = ""; const bool success = openspace::OpenSpaceEngine::create( argc, argv, - sgctArguments, - openGlVersion + sgctArguments ); if (!success) return EXIT_FAILURE; @@ -115,21 +130,20 @@ int main(int argc, char** argv) { // try to open a window LDEBUG("Initialize SGCT Engine"); -#ifdef __APPLE__ - sgct::Engine::RunMode rm = sgct::Engine::RunMode::OpenGL_4_1_Core_Profile; -#else - std::map versionMapping = { - { "4.2", sgct::Engine::RunMode::OpenGL_4_2_Core_Profile }, - { "4.3", sgct::Engine::RunMode::OpenGL_4_3_Core_Profile }, - { "4.4", sgct::Engine::RunMode::OpenGL_4_4_Core_Profile }, - { "4.5", sgct::Engine::RunMode::OpenGL_4_5_Core_Profile } + std::map, sgct::Engine::RunMode> versionMapping = { + { { 3, 3 }, sgct::Engine::RunMode::OpenGL_3_3_Core_Profile }, + { { 4, 0 }, sgct::Engine::RunMode::OpenGL_4_0_Core_Profile }, + { { 4, 1 }, sgct::Engine::RunMode::OpenGL_4_1_Core_Profile }, + { { 4, 2 }, sgct::Engine::RunMode::OpenGL_4_2_Core_Profile }, + { { 4, 3 }, sgct::Engine::RunMode::OpenGL_4_3_Core_Profile }, + { { 4, 4 }, sgct::Engine::RunMode::OpenGL_4_4_Core_Profile }, + { { 4, 5 }, sgct::Engine::RunMode::OpenGL_4_5_Core_Profile } }; - if (versionMapping.find(openGlVersion) == versionMapping.end()) { - LFATAL("Requested OpenGL version " << openGlVersion << " not supported"); + if (versionMapping.find(glVersion) == versionMapping.end()) { + LFATAL("Requested OpenGL version " << glVersion.first << "." << glVersion.second << " not supported"); return EXIT_FAILURE; } - sgct::Engine::RunMode rm = versionMapping[openGlVersion]; -#endif + sgct::Engine::RunMode rm = versionMapping[glVersion]; const bool initSuccess = _sgctEngine->init(rm); if (!initSuccess) { LFATAL("Initializing failed"); From 1923558f6ce48dd4c7d05ac9f7221ce69a6eac4c Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 28 May 2015 20:43:53 +0200 Subject: [PATCH 088/329] Fix OpenSpaceTest --- tests/main.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/main.cpp b/tests/main.cpp index ede4213fc6..cf285d1568 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -53,8 +53,7 @@ namespace { int main(int argc, char** argv) { std::vector args; - std::string glVersion; - openspace::OpenSpaceEngine::create(argc, argv, args, glVersion); + openspace::OpenSpaceEngine::create(argc, argv, args); //LogManager::initialize(LogManager::LogLevel::Debug); From 37a54ab22b586b342fe7a625e4bdda30f8841df4 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 28 May 2015 20:44:12 +0200 Subject: [PATCH 089/329] Move specification of RenderingMethod into renderengine --- include/openspace/rendering/renderengine.h | 2 +- src/engine/openspaceengine.cpp | 14 +++++--------- src/rendering/renderengine.cpp | 10 +++++++++- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/include/openspace/rendering/renderengine.h b/include/openspace/rendering/renderengine.h index 8466028514..4056dc2e56 100644 --- a/include/openspace/rendering/renderengine.h +++ b/include/openspace/rendering/renderengine.h @@ -51,7 +51,7 @@ public: RenderEngine(); ~RenderEngine(); - bool initialize(const std::string& renderingMethod); + bool initialize(); void setSceneGraph(Scene* sceneGraph); Scene* scene(); diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index d38555a20e..dcbb2d859e 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -86,11 +86,6 @@ namespace { const std::string _sgctConfigArgumentCommand = "-config"; - const std::string KeyRenderingMethod = "RenderingMethod"; - const std::string DefaultRenderingMethod = "ABufferSingleLinked"; - - const std::string DefaultOpenGlVersion = "4.3"; - const int CacheVersion = 1; struct { @@ -336,10 +331,11 @@ bool OpenSpaceEngine::initialize() { _renderEngine->setSceneGraph(sceneGraph); // initialize the RenderEngine - if (_configurationManager->hasKeyAndValue(KeyRenderingMethod)) - _renderEngine->initialize(_configurationManager->value(KeyRenderingMethod)); - else - _renderEngine->initialize(DefaultRenderingMethod); + _renderEngine->initialize(); + // if (_configurationManager->hasKeyAndValue(KeyRenderingMethod)) + // _renderEngine->initialize(_configurationManager->value(KeyRenderingMethod)); + // else + // _renderEngine->initialize(DefaultRenderingMethod); sceneGraph->initialize(); diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 60bcc157c4..63f2f0c28b 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #ifdef GHOUL_USE_DEVIL @@ -76,6 +77,9 @@ namespace { const std::string _loggerCat = "RenderEngine"; + const std::string KeyRenderingMethod = "RenderingMethod"; + const std::string DefaultRenderingMethod = "ABufferSingleLinked"; + const std::map RenderingMethods = { { "ABufferFrameBuffer", ABUFFER_FRAMEBUFFER}, { "ABufferSingleLinked", ABUFFER_SINGLE_LINKED }, @@ -130,7 +134,11 @@ RenderEngine::~RenderEngine() { ghoul::SharedMemory::remove(PerformanceMeasurementSharedData); } -bool RenderEngine::initialize(const std::string& renderingMethod) { +bool RenderEngine::initialize() { + std::string renderingMethod = DefaultRenderingMethod; + if (OsEng.configurationManager()->hasKeyAndValue(KeyRenderingMethod)) + renderingMethod = OsEng.configurationManager()->value(KeyRenderingMethod); + auto it = RenderingMethods.find(renderingMethod); if (it == RenderingMethods.end()) { LFATAL("Rendering method '" << renderingMethod << "' not among the available " From f9d4e0e1f1af97d8bba68fe675c78ef03ff8246a Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 28 May 2015 22:25:58 +0200 Subject: [PATCH 090/329] Clean test main.cpp --- tests/main.cpp | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/tests/main.cpp b/tests/main.cpp index cf285d1568..40252d4f44 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -55,25 +55,6 @@ int main(int argc, char** argv) { std::vector args; openspace::OpenSpaceEngine::create(argc, argv, args); - - //LogManager::initialize(LogManager::LogLevel::Debug); - //LogMgr.addLog(new ConsoleLog); - - //FileSystem::initialize(); - //std::string configurationFilePath = ""; - //LDEBUG("Finding configuration"); - //if (!openspace::OpenSpaceEngine::findConfiguration(configurationFilePath)) { - // LFATAL("Could not find OpenSpace configuration file!"); - // assert(false); - //} - ////LINFO("Configuration file found: " << FileSys.absolutePath(configurationFilePath)); - - //openspace::ConfigurationManager manager; - //manager.loadFromFile(configurationFilePath); - - - //openspace::FactoryManager::initialize(); - testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } From 4373cb2fbc3511e5a2939242bb3d4777bd59f83d Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 28 May 2015 22:35:49 +0200 Subject: [PATCH 091/329] Update ghoul version --- ext/ghoul | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/ghoul b/ext/ghoul index c660e8dd75..1b751cb854 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit c660e8dd75beae2a827cbc04d19987e45b23f4fb +Subproject commit 1b751cb854857effa7ba7648476633913bb60d6c From f8fedb4426aa7103a36716332ea267096bd574dd Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 28 May 2015 22:50:37 +0200 Subject: [PATCH 092/329] Move openspace-data to data --- .gitmodules | 4 ++-- openspace-data => data | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename openspace-data => data (100%) diff --git a/.gitmodules b/.gitmodules index e78869f917..60281862f8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,8 +1,8 @@ [submodule "ext/ghoul"] path = ext/ghoul url = https://github.com/OpenSpace/Ghoul.git -[submodule "openspace-data"] - path = openspace-data +[submodule "data"] + path = data url = git@openspace.itn.liu.se:/openspace-data [submodule "modules/kameleon/ext/kameleon"] path = modules/kameleon/ext/kameleon diff --git a/openspace-data b/data similarity index 100% rename from openspace-data rename to data From 2efe1d0dc4011596728dff26a9a5b78cf893d18e Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 3 Jun 2015 10:13:29 +0200 Subject: [PATCH 093/329] Fixed the function to get supported OGL version so that it works on OS X --- src/main.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 117e7d435f..f2a8805202 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -49,6 +49,17 @@ void mainLogCallback(const char* msg); std::pair supportedOpenGLVersion () { glfwInit(); + + //On OS X we need to explicitly set the version and specify that we are using CORE profile + //to be able to use glGetIntegerv(GL_MAJOR_VERSION, &major) and glGetIntegerv(GL_MINOR_VERSION, &minor) + //explicitly setting to OGL 3.3 CORE works since all Mac's now support at least 3.3 +#if __APPLE__ + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); +#endif + glfwWindowHint(GLFW_VISIBLE, GL_FALSE); GLFWwindow* offscreen = glfwCreateWindow(128, 128, "", nullptr, nullptr); glfwMakeContextCurrent(offscreen); From a9e3044d6ad0a39a72211e5dbe3a3bd4ee1928d5 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 3 Jun 2015 12:36:20 +0200 Subject: [PATCH 094/329] Changed openspace-data file token to point to correct data folder Print detected OpenGL version --- openspace.cfg | 2 +- src/main.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/openspace.cfg b/openspace.cfg index 1fb1965c3e..2f7446833a 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -14,7 +14,7 @@ return { SCRIPTS = "${BASE_PATH}/scripts", SHADERS = "${BASE_PATH}/shaders", SHADERS_GENERATED = "${SHADERS}/generated", - OPENSPACE_DATA = "${BASE_PATH}/openspace-data", + OPENSPACE_DATA = "${BASE_PATH}/data", MODULES = "${BASE_PATH}/modules", TESTDIR = "${BASE_PATH}/tests", CONFIG = "${BASE_PATH}/config", diff --git a/src/main.cpp b/src/main.cpp index f2a8805202..0edbe2edc9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -96,6 +96,8 @@ int main(int argc, char** argv) { ); if (!success) return EXIT_FAILURE; + + LINFO("Detected OpenGL version: " << glVersion.first << "." << glVersion.second); // create sgct engine c arguments int newArgc = static_cast(sgctArguments.size()); From 8ad7702b75d108b87c6988ff9c9764a7e21cf109 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 3 Jun 2015 13:31:42 +0200 Subject: [PATCH 095/329] Automatically detect which rendering method to use based on the available OpenGL version --- ext/ghoul | 2 +- include/openspace/abuffer/abuffer.h | 1 - openspace.cfg | 3 +-- src/engine/openspaceengine.cpp | 2 +- src/rendering/renderengine.cpp | 15 ++++++++++++++- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index 1b751cb854..712da50047 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 1b751cb854857effa7ba7648476633913bb60d6c +Subproject commit 712da500477f762a443c09fd23198e93f3958a7a diff --git a/include/openspace/abuffer/abuffer.h b/include/openspace/abuffer/abuffer.h index 82d50e37dd..f8e1550a29 100644 --- a/include/openspace/abuffer/abuffer.h +++ b/include/openspace/abuffer/abuffer.h @@ -45,7 +45,6 @@ namespace openspace { class ABuffer { public: - struct fragmentData { GLfloat _position[3]; GLfloat _color[4]; diff --git a/openspace.cfg b/openspace.cfg index 2f7446833a..7b156a7b68 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -53,6 +53,5 @@ return { Type = "text", File = "${BASE_PATH}/Properties.txt" }, - RenderingMethod = "ABufferSingleLinked" -- On Windows and Unix - -- RenderingMethod = "ABufferFrameBuffer" -- On Mac due to OpenGL 4.1 restrictions + -- RenderingMethod = "ABufferFrameBuffer" } \ No newline at end of file diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index dcbb2d859e..6264d9db7c 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -289,7 +289,7 @@ bool OpenSpaceEngine::initialize() { SysCap.addComponent(new ghoul::systemcapabilities::OpenGLCapabilitiesComponent); SysCap.detectCapabilities(); SysCap.logCapabilities(); - + // Load SPICE time kernel bool success = loadSpiceKernels(); if (!success) diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 63f2f0c28b..f4677942f7 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -48,6 +48,8 @@ #include #include #include +#include +#include #include #ifdef GHOUL_USE_DEVIL @@ -136,8 +138,19 @@ RenderEngine::~RenderEngine() { bool RenderEngine::initialize() { std::string renderingMethod = DefaultRenderingMethod; - if (OsEng.configurationManager()->hasKeyAndValue(KeyRenderingMethod)) + bool overwritingDefaultRenderingMethod = false; + if (OsEng.configurationManager()->hasKeyAndValue(KeyRenderingMethod)) { renderingMethod = OsEng.configurationManager()->value(KeyRenderingMethod); + overwritingDefaultRenderingMethod = true; + } + else { + using Version = ghoul::systemcapabilities::OpenGLCapabilitiesComponent::Version; + + if (OpenGLCap.openGLVersion() < Version(4,3)) { + LINFO("Falling back to framebuffer implementation due to OpenGL limitations"); + renderingMethod = "ABufferFrameBuffer"; + } + } auto it = RenderingMethods.find(renderingMethod); if (it == RenderingMethods.end()) { From f3b18d53f1f29312dbbb0cfe0974d0eae0ebd0f2 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 3 Jun 2015 15:56:51 +0200 Subject: [PATCH 096/329] Make the moduleregistration file not depend on the absolute path --- support/cmake/module_definition.cmake | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/support/cmake/module_definition.cmake b/support/cmake/module_definition.cmake index 59b381864f..ee16a9e8dd 100644 --- a/support/cmake/module_definition.cmake +++ b/support/cmake/module_definition.cmake @@ -189,8 +189,11 @@ endfunction () function (write_module_name module_name) string(TOLOWER ${module_name} module_name_lower) + set(MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/${module_name_lower}module.h) + string(REPLACE "${OPENSPACE_BASE_DIR}/" "" MODULE_PATH ${MODULE_PATH}) + file(WRITE ${CMAKE_BINARY_DIR}/modules/${module_name_lower}/modulename.cmake "set(MODULE_NAME ${module_name}Module)\n" - "set(MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/${module_name_lower}module.h)" + "set(MODULE_PATH ${MODULE_PATH})" ) endfunction () From 68ef7c939cb18808ae61b72acc396c33f0017263 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 3 Jun 2015 17:45:05 +0200 Subject: [PATCH 097/329] Cleanup moduleregistration file --- support/cmake/module_registration.template | 4 +--- support/cmake/support_macros.cmake | 5 +---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/support/cmake/module_registration.template b/support/cmake/module_registration.template index 58d73f69a7..5349553d0b 100644 --- a/support/cmake/module_registration.template +++ b/support/cmake/module_registration.template @@ -6,12 +6,10 @@ #define __MODULE_REGISTRATION_H__ @MODULE_HEADERS@ - namespace openspace { std::vector AllModules = { -@MODULE_CLASSES@ -}; +@MODULE_CLASSES@}; } // namespace openspace diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 048f9249f3..1c538ad3fc 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -324,10 +324,7 @@ function (handle_internal_modules) #"#endif\n\n" ) - list(APPEND MODULE_CLASSES - "new ${MODULE_NAME},\n" - ) - + list(APPEND MODULE_CLASSES " new ${MODULE_NAME},\n") endif () endforeach () From 7492fa2f6ccd33a2ef1e59bd0b798e11a36efcdf Mon Sep 17 00:00:00 2001 From: Michal Marcinkowski Date: Wed, 3 Jun 2015 19:10:34 -0400 Subject: [PATCH 098/329] New Additions: Shadow cylinders extending from planet terminator in opposite dir of sun Plane that displays the global texture map of a planet as projections appear ^latter is an addition to RenderablePlane class, a renderable plane can have boolean keyword "ProjectionListener" - determines whether or not it displays --- data | 2 +- include/openspace/util/spicemanager.h | 36 ++++ modules/base/rendering/renderableplane.cpp | 36 +++- modules/base/rendering/renderableplane.h | 2 + modules/newhorizons/newhorizonsmodule.cpp | 2 + .../newhorizons/rendering/renderablefov.cpp | 12 +- .../rendering/renderableplanetprojection.h | 2 +- .../rendering/renderableshadowcylinder.cpp | 202 ++++++++++++++++++ .../rendering/renderableshadowcylinder.h | 97 +++++++++ .../shaders/terminatorshadow_fs.glsl | 47 ++++ .../shaders/terminatorshadow_vs.glsl | 60 ++++++ scripts/bind_keys.lua | 2 + scripts/default_settings.lua | 1 + src/util/spicemanager.cpp | 39 ++++ 14 files changed, 530 insertions(+), 10 deletions(-) create mode 100644 modules/newhorizons/rendering/renderableshadowcylinder.cpp create mode 100644 modules/newhorizons/rendering/renderableshadowcylinder.h create mode 100644 modules/newhorizons/shaders/terminatorshadow_fs.glsl create mode 100644 modules/newhorizons/shaders/terminatorshadow_vs.glsl diff --git a/data b/data index 54af421ff5..f3928948f2 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 54af421ff583392c559748983251da6521c73c8a +Subproject commit f3928948f25ac520240a4c7a6ac280b278ddf3b7 diff --git a/include/openspace/util/spicemanager.h b/include/openspace/util/spicemanager.h index c03113dc79..a0d2a1001c 100644 --- a/include/openspace/util/spicemanager.h +++ b/include/openspace/util/spicemanager.h @@ -630,6 +630,42 @@ public: */ bool getFieldOfView(int instrument, std::string& fovShape, std::string& frameName, glm::dvec3& boresightVector, std::vector& bounds) const; + + /** + This routine computes a set of points on the umbral or penumbral terminator of + a specified target body, where SPICE models the target shape as an ellipsoid. + \param numberOfPoints - number of points along terminator returned by this method + \param terminatorType - is a string indicating the type of terminator to compute: + umbral or penumbral. The umbral terminator is the boundary of the portion of the + ellipsoid surface in total shadow. The penumbral terminator is the boundary of + the portion of the surface that is completely illuminated. Note that in astronomy + references, the unqualified word "terminator" refers to the umbral terminator. + Here, the unqualified word refers to either type of terminator. + \param lightSource - name of body acting as light source + \param observer - name of bodserving body + \param target - name of target body + \param frame - name of the reference frame relative to which the output terminator + points are expressed. + \param aberrationCorrection - correction for light time and/or stellar aberration + \param ephemerisTime - the epoch of participation of the observer + \param targetEpoch - is the "target epoch.", time it takes for + \param observerPosition - is the vector from the target body at targetEpoch + \param terminatorPoints - an array of points on the umbral or penumbral terminator + of the ellipsoid, as specified by the input argument `numberOfPoints' + For further, more specific details please refer to + http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/edterm_c.html + */ + bool getTerminatorEllipse(const int numberOfPoints, + const std::string terminatorType, + const std::string lightSource, + const std::string observer, + const std::string target, + const std::string frame, + const std::string aberrationCorrection, + double ephemerisTime, + double& targetEpoch, + glm::dvec3& observerPosition, + std::vector& terminatorPoints); /** * This function adds a frame to a body diff --git a/modules/base/rendering/renderableplane.cpp b/modules/base/rendering/renderableplane.cpp index 4bb27bf458..ef7108646b 100644 --- a/modules/base/rendering/renderableplane.cpp +++ b/modules/base/rendering/renderableplane.cpp @@ -28,6 +28,10 @@ #include #include +#include +#include +#include + #include #include #include @@ -51,6 +55,7 @@ RenderablePlane::RenderablePlane(const ghoul::Dictionary& dictionary) : Renderable(dictionary) , _texturePath("texture", "Texture") , _billboard("billboard", "Billboard", false) + , _projectionListener("projectionListener", "DisplayProjections", false) , _size("size", "Size", glm::vec2(1,1), glm::vec2(0.f), glm::vec2(1.f, 25.f)) , _origin(Origin::Center) , _shader(nullptr) @@ -63,6 +68,10 @@ RenderablePlane::RenderablePlane(const ghoul::Dictionary& dictionary) dictionary.getValue("Size", size); _size = size; + if (dictionary.hasKey("Name")){ + dictionary.getValue("Name", _nodeName); + } + std::string origin; if (dictionary.getValue("Origin", origin)) { if (origin == "LowerLeft") { @@ -87,6 +96,13 @@ RenderablePlane::RenderablePlane(const ghoul::Dictionary& dictionary) if (dictionary.getValue("Billboard", billboard)) { _billboard = billboard; } + if (dictionary.hasKey("ProjectionListener")){ + bool projectionListener = false; + if (dictionary.getValue("ProjectionListener", projectionListener)) { + _projectionListener = projectionListener; + } + } + std::string texturePath = ""; bool success = dictionary.getValue("Texture", texturePath); @@ -147,8 +163,12 @@ bool RenderablePlane::deinitialize() { glDeleteBuffers(1, &_vertexPositionBuffer); _vertexPositionBuffer = 0; - delete _texture; - _texture = nullptr; + if (!_projectionListener){ + // its parents job to kill texture + // iff projectionlistener + delete _texture; + _texture = nullptr; + } delete _textureFile; _textureFile = nullptr; @@ -166,6 +186,18 @@ void RenderablePlane::render(const RenderData& data) { // Activate shader _shader->activate(); + if (_projectionListener){ + //get parent node-texture and set with correct dimensions + SceneGraphNode* textureNode = OsEng.renderEngine()->scene()->sceneGraphNode(_nodeName)->parent(); + if (textureNode != nullptr){ + RenderablePlanetProjection *t = static_cast(textureNode->renderable()); + _texture = t->baseTexture(); + float h = _texture->height(); + float w = _texture->width(); + float scale = h / w; + transform = glm::scale(transform, glm::vec3(1.f, scale, 1.f)); + } + } _shader->setUniform("ViewProjection", data.camera.viewProjectionMatrix()); _shader->setUniform("ModelTransform", transform); diff --git a/modules/base/rendering/renderableplane.h b/modules/base/rendering/renderableplane.h index 60effc4c5e..e4301cd7f0 100644 --- a/modules/base/rendering/renderableplane.h +++ b/modules/base/rendering/renderableplane.h @@ -68,9 +68,11 @@ private: properties::StringProperty _texturePath; properties::BoolProperty _billboard; + properties::BoolProperty _projectionListener; properties::Vec2Property _size; Origin _origin; + std::string _nodeName; bool _planeIsDirty; diff --git a/modules/newhorizons/newhorizonsmodule.cpp b/modules/newhorizons/newhorizonsmodule.cpp index 4ec0ff04d6..8a1570fcf2 100644 --- a/modules/newhorizons/newhorizonsmodule.cpp +++ b/modules/newhorizons/newhorizonsmodule.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -63,6 +64,7 @@ bool NewHorizonsModule::initialize() { auto fRenderable = FactoryManager::ref().factory(); ghoul_assert(fRenderable, "No renderable factory existed"); + fRenderable->registerClass("RenderableShadowCylinder"); fRenderable->registerClass("RenderableCrawlingLine"); fRenderable->registerClass("RenderableFov"); fRenderable->registerClass("RenderablePlaneProjection"); diff --git a/modules/newhorizons/rendering/renderablefov.cpp b/modules/newhorizons/rendering/renderablefov.cpp index 43e8a941a4..bc5f378de9 100644 --- a/modules/newhorizons/rendering/renderablefov.cpp +++ b/modules/newhorizons/rendering/renderablefov.cpp @@ -531,12 +531,12 @@ void RenderableFov::render(const RenderData& data) { psc position; double lt; SpiceManager::ref().getTargetPosition(_fovTarget, - _spacecraft, - _frame, - _aberrationCorrection, - _time, - position, - lt); + _spacecraft, + _frame, + _aberrationCorrection, + _time, + position, + lt); //if aimed 80 deg away from target, dont draw white square if (glm::dot(glm::normalize(aim), glm::normalize(position.vec3())) < 0.2){ diff --git a/modules/newhorizons/rendering/renderableplanetprojection.h b/modules/newhorizons/rendering/renderableplanetprojection.h index 01c8971fb3..de9a15d541 100644 --- a/modules/newhorizons/rendering/renderableplanetprojection.h +++ b/modules/newhorizons/rendering/renderableplanetprojection.h @@ -67,6 +67,7 @@ public: void render(const RenderData& data) override; void update(const UpdateData& data) override; + ghoul::opengl::Texture* baseTexture() { return _texture; }; protected: @@ -91,7 +92,6 @@ private: properties::BoolProperty _performProjection; properties::BoolProperty _clearAllProjections; - ghoul::opengl::ProgramObject* _programObject; ghoul::opengl::ProgramObject* _fboProgramObject; diff --git a/modules/newhorizons/rendering/renderableshadowcylinder.cpp b/modules/newhorizons/rendering/renderableshadowcylinder.cpp new file mode 100644 index 0000000000..e0cda86132 --- /dev/null +++ b/modules/newhorizons/rendering/renderableshadowcylinder.cpp @@ -0,0 +1,202 @@ +/***************************************************************************************** +* * +* OpenSpace * +* * +* Copyright (c) 2014-2015 * +* * +* Permission is hereby granted, free of charge, to any person obtaining a copy of this * +* software and associated documentation files (the "Software"), to deal in the Software * +* without restriction, including without limitation the rights to use, copy, modify, * +* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * +* permit persons to whom the Software is furnished to do so, subject to the following * +* conditions: * +* * +* The above copyright notice and this permission notice shall be included in all copies * +* or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * +* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * +* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * +* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * +* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * +****************************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +namespace { + const std::string _loggerCat = "RenderablePlane"; + const std::string _keyType = "TerminatorType"; + const std::string _keyLightSource = "LightSource"; + const std::string _keyObserver = "Observer"; + const std::string _keyBody = "Body"; + const std::string _keyBodyFrame = "BodyFrame"; + const std::string _keyMainFrame = "MainFrame"; + const std::string _keyAberration = "Aberration"; +} + +namespace openspace { + RenderableShadowCylinder::RenderableShadowCylinder(const ghoul::Dictionary& dictionary) + : Renderable(dictionary) + , _numberOfPoints("amountOfPoints", "Points", 190, 1, 300) + , _shadowLength("shadowLength", "Shadow Length", 0.1, 0.0, 0.5) + , _shader(nullptr) + , _vao(0) + , _vbo(0) +{ + addProperty(_numberOfPoints); + addProperty(_shadowLength); + + bool success = dictionary.getValue(_keyType, _terminatorType); + ghoul_assert(success, ""); + success = dictionary.getValue(_keyLightSource, _lightSource); + ghoul_assert(success, ""); + success = dictionary.getValue(_keyObserver, _observer); + ghoul_assert(success, ""); + success = dictionary.getValue(_keyBody, _body); + ghoul_assert(success, ""); + success = dictionary.getValue(_keyBodyFrame, _bodyFrame); + ghoul_assert(success, ""); + success = dictionary.getValue(_keyMainFrame, _mainFrame); + ghoul_assert(success, ""); + success = dictionary.getValue(_keyAberration, _aberration); + ghoul_assert(success, ""); +} + +RenderableShadowCylinder::~RenderableShadowCylinder() { +} + +bool RenderableShadowCylinder::isReady() const { + bool ready = true; + if (!_shader) + ready &= false; + return ready; +} + +bool RenderableShadowCylinder::initialize() { + glGenVertexArrays(1, &_vao); // generate array + glGenBuffers(1, &_vbo); // generate buffer + createCylinder(); + + bool completeSuccess = true; + _shader = ghoul::opengl::ProgramObject::Build("ShadowProgram", + "${MODULE_NEWHORIZONS}/shaders/terminatorshadow_vs.glsl", + "${MODULE_NEWHORIZONS}/shaders/terminatorshadow_fs.glsl"); + if (!_shader) + return false; + return completeSuccess; +} + +bool RenderableShadowCylinder::deinitialize() { + glDeleteVertexArrays(1, &_vao); + _vao = 0; + glDeleteBuffers(1, &_vbo); + _vbo = 0; + delete _shader; + _shader = nullptr; + return true; +} + +void RenderableShadowCylinder::render(const RenderData& data){ + glm::mat4 _transform = glm::mat4(1.0); + for (int i = 0; i < 3; i++){ + for (int j = 0; j < 3; j++){ + _transform[i][j] = static_cast(_stateMatrix[i][j]); + } + } + // Activate shader + _shader->activate(); + + _shader->setUniform("ViewProjection", data.camera.viewProjectionMatrix()); + _shader->setUniform("ModelTransform", _transform); + setPscUniforms(_shader, &data.camera, data.position); + + glBindVertexArray(_vao); + glDrawArrays(GL_TRIANGLE_STRIP, 0, static_cast(_vertices.size())); + glBindVertexArray(0); + + _shader->deactivate(); +} + +void RenderableShadowCylinder::update(const UpdateData& data) { + openspace::SpiceManager::ref().getPositionTransformMatrix(_bodyFrame, _mainFrame, data.time, _stateMatrix); + _time = data.time; + if (_shader->isDirty()) + _shader->rebuildFromFile(); + createCylinder(); +} + +glm::vec4 psc_addition(glm::vec4 v1, glm::vec4 v2) { + float k = 10.f; + float ds = v2.w - v1.w; + if (ds >= 0) { + float p = pow(k, -ds); + return glm::vec4(v1.x*p + v2.x, v1.y*p + v2.y, v1.z*p + v2.z, v2.w); + } + else { + float p = pow(k, ds); + return glm::vec4(v1.x + v2.x*p, v1.y + v2.y*p, v1.z + v2.z*p, v1.w); + } +} + +void RenderableShadowCylinder::createCylinder() { + double targetEpoch; + glm::dvec3 observerPosition; + std::vector terminatorPoints; + SpiceManager::ref().getTerminatorEllipse(_numberOfPoints, + _terminatorType, + _lightSource, + _observer, + _body, + _bodyFrame, + _aberration, + _time, + targetEpoch, + observerPosition, + terminatorPoints); + + glm::dvec3 vecLightSource; + double lt; + bool performs = SpiceManager::ref().getTargetPosition(_body, _lightSource, _mainFrame, _aberration, _time, vecLightSource, lt); + + glm::dmat3 _stateMatrix; + openspace::SpiceManager::ref().getPositionTransformMatrix(_bodyFrame, _mainFrame, _time, _stateMatrix); + + _stateMatrix = glm::inverse(_stateMatrix); + vecLightSource = _stateMatrix * vecLightSource; + + vecLightSource *= _shadowLength; + _vertices.clear(); + + psc endpoint = psc::CreatePowerScaledCoordinate(vecLightSource.x, vecLightSource.y, vecLightSource.z); + for (auto v : terminatorPoints){ + _vertices.push_back(CylinderVBOLayout(v[0], v[1], v[2], v[3])); + glm::vec4 f = psc_addition(v.vec4(), endpoint.vec4()); + _vertices.push_back(CylinderVBOLayout(f[0], f[1], f[2], f[3])); + } + _vertices.push_back(_vertices[0]); + _vertices.push_back(_vertices[1]); + + glBindVertexArray(_vao); // bind array + glBindBuffer(GL_ARRAY_BUFFER, _vbo); // bind buffer + glBufferData(GL_ARRAY_BUFFER, _vertices.size() * sizeof(CylinderVBOLayout), NULL, GL_DYNAMIC_DRAW); // orphaning the buffer, sending NULL data. + glBufferSubData(GL_ARRAY_BUFFER, 0, _vertices.size() * sizeof(CylinderVBOLayout), &_vertices[0]); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); + glBindVertexArray(0); +} + +} // namespace openspace diff --git a/modules/newhorizons/rendering/renderableshadowcylinder.h b/modules/newhorizons/rendering/renderableshadowcylinder.h new file mode 100644 index 0000000000..b5caac7f9f --- /dev/null +++ b/modules/newhorizons/rendering/renderableshadowcylinder.h @@ -0,0 +1,97 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef RENDERABLESHADOWCYLINDER_H_ +#define RENDERABLESHADOWCYLINDER_H_ + +#include + +#include +#include +#include + +namespace ghoul { + namespace filesystem { + class File; + } + namespace opengl { + class ProgramObject; + class Texture; + } +} + +namespace openspace { + struct LinePoint; + class RenderableShadowCylinder : public Renderable { + + public: + RenderableShadowCylinder(const ghoul::Dictionary& dictionary); + ~RenderableShadowCylinder(); + + bool initialize() override; + bool deinitialize() override; + + bool isReady() const override; + + void render(const RenderData& data) override; + void update(const UpdateData& data) override; + + private: + struct CylinderVBOLayout { + CylinderVBOLayout(double a1, double a2, double a3, double a4){ + x = a1; + y = a2; + z = a3; + e = a4; + } + float x, y, z, e; + }; + + void createCylinder(); + properties::IntProperty _numberOfPoints; + properties::FloatProperty _shadowLength; + + ghoul::opengl::ProgramObject* _shader; + + glm::dmat3 _stateMatrix; + + GLuint _vao; + GLuint _vbo; + + std::vector _vertices; + + std::string _terminatorType; + std::string _lightSource; + std::string _observer; + std::string _body; + std::string _bodyFrame; + std::string _mainFrame; + std::string _aberration; + + double _time; + }; + +} // namespace openspace +#endif // RENDERABLESHADOWCYLINDER_H_ + diff --git a/modules/newhorizons/shaders/terminatorshadow_fs.glsl b/modules/newhorizons/shaders/terminatorshadow_fs.glsl new file mode 100644 index 0000000000..6fcfc4d90c --- /dev/null +++ b/modules/newhorizons/shaders/terminatorshadow_fs.glsl @@ -0,0 +1,47 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014 * + * * + * 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. * + ****************************************************************************************/ + +#version __CONTEXT__ + +in vec4 vs_point_position; +in vec4 vs_point_velocity; +//in float fade; +//uniform float forceFade; + +uniform vec3 color; + +in vec4 vs_color; + +#include "ABuffer/abufferStruct.hglsl" +#include "ABuffer/abufferAddToBuffer.hglsl" +#include "PowerScaling/powerScaling_fs.hglsl" + +void main() { + vec4 position = vs_point_position; + float depth = pscDepth(position); + + vec4 c = vs_color; + ABufferStruct_t frag = createGeometryFragment(c, position, depth); + addToBuffer(frag); +} \ No newline at end of file diff --git a/modules/newhorizons/shaders/terminatorshadow_vs.glsl b/modules/newhorizons/shaders/terminatorshadow_vs.glsl new file mode 100644 index 0000000000..7f5398e74f --- /dev/null +++ b/modules/newhorizons/shaders/terminatorshadow_vs.glsl @@ -0,0 +1,60 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014 * + * * + * 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. * + ****************************************************************************************/ + +#version __CONTEXT__ + +uniform mat4 ViewProjection; +uniform mat4 ModelTransform; +uniform vec4 objectVelocity; + + + +layout(location = 0) in vec4 in_point_position; + +out vec4 vs_point_position; +out vec4 vs_color; +//out float fade; + +uniform uint nVertices; +//uniform float lineFade; + +#include "PowerScaling/powerScaling_vs.hglsl" + +void main() { + //float id = float(gl_VertexID) / float(nVertices * lineFade); + //fade = 1.0 - id; + + if(mod(gl_VertexID,2) == 0.f){ + vs_color = vec4(1,1,1,0.5); + }else{ + vs_color = vec4(0); + } + + vec4 tmp = in_point_position; + //tmp = psc_to_meter(tmp, vec2(1,0.f)); + vec4 position = pscTransform(tmp, ModelTransform); + vs_point_position = tmp; + position = ViewProjection * position; + gl_Position = z_normalization(position); +} \ No newline at end of file diff --git a/scripts/bind_keys.lua b/scripts/bind_keys.lua index 2c7e85b413..63a4b18286 100644 --- a/scripts/bind_keys.lua +++ b/scripts/bind_keys.lua @@ -53,6 +53,8 @@ openspace.bindKey("6", "openspace.time.setTime('2015-07-14T12:04:35.00'); opensp openspace.bindKey("7", "openspace.time.setTime('2015-07-14T15:02:46.00'); openspace.time.setDeltaTime(100)") ]]-- +openspace.bindKey("i", "local b = openspace.getPropertyValue('PlutoTexture.renderable.enabled'); openspace.setPropertyValue('PlutoTexture.renderable.enabled', not b)") + openspace.bindKey("q", "local b = openspace.getPropertyValue('SunMarker.renderable.enabled'); openspace.setPropertyValue('SunMarker.renderable.enabled', not b)") openspace.bindKey("e", "local b = openspace.getPropertyValue('EarthMarker.renderable.enabled'); openspace.setPropertyValue('EarthMarker.renderable.enabled', not b)") diff --git a/scripts/default_settings.lua b/scripts/default_settings.lua index 38a6dcc523..0f3f2ffcb1 100644 --- a/scripts/default_settings.lua +++ b/scripts/default_settings.lua @@ -8,6 +8,7 @@ openspace.setPropertyValue("SunMarker.renderable.enabled", true) openspace.setPropertyValue("EarthMarker.renderable.enabled", true) openspace.setPropertyValue("Constellation Bounds.renderable.enabled", false) openspace.setPropertyValue("PlutoTrail.renderable.enabled", false) +openspace.setPropertyValue("PlutoTexture.renderable.enabled", false) openspace.setPropertyValue("MilkyWay.renderable.transparency", 0.75) openspace.setPropertyValue("MilkyWay.renderable.segments", 50) diff --git a/src/util/spicemanager.cpp b/src/util/spicemanager.cpp index 1e5219021c..912d9f7cf2 100644 --- a/src/util/spicemanager.cpp +++ b/src/util/spicemanager.cpp @@ -957,6 +957,45 @@ bool SpiceManager::getFieldOfView(int instrument, return true; } +bool SpiceManager::getTerminatorEllipse(const int numberOfPoints, + const std::string terminatorType, + const std::string lightSource, + const std::string observer, + const std::string target, + const std::string frame, + const std::string aberrationCorrection, + double ephemerisTime, + double& targetEpoch, + glm::dvec3& observerPosition, + std::vector& terminatorPoints){ + + double(*tpoints)[3] = new double[numberOfPoints][3]; + + edterm_c(terminatorType.c_str(), + lightSource.c_str(), + target.c_str(), + ephemerisTime, + frame.c_str(), + aberrationCorrection.c_str(), + observer.c_str(), + numberOfPoints, + &targetEpoch, + glm::value_ptr(observerPosition), + (double(*)[3])tpoints ); + + bool hasError = checkForError("Error getting " + terminatorType + + "terminator for'" + target + "'"); + if (hasError) + return false; + + for (int i = 0; i < numberOfPoints; i++){ + psc point = psc::CreatePowerScaledCoordinate(tpoints[i][0], tpoints[i][1], tpoints[i][2]); + point[3] += 3; + terminatorPoints.push_back(point); + } + + return true; +} std::string SpiceManager::frameFromBody(const std::string body) const { From 16e09e5802fe29b2ad1d666fd4cd6894b1a61c0b Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 4 Jun 2015 01:44:31 +0200 Subject: [PATCH 099/329] Remove static code analysis warnings --- modules/base/rendering/renderablepath.cpp | 4 ++-- src/rendering/renderengine.cpp | 14 ++++++++------ src/util/spicemanager.cpp | 16 +++++++--------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/modules/base/rendering/renderablepath.cpp b/modules/base/rendering/renderablepath.cpp index 4321bcccf5..9ccdf8622b 100644 --- a/modules/base/rendering/renderablepath.cpp +++ b/modules/base/rendering/renderablepath.cpp @@ -212,7 +212,7 @@ void RenderablePath::calculatePath(std::string observer) { return; double lightTime; - bool correctPosition = true; +// bool correctPosition = true; psc pscPos; double currentTime = _start; @@ -222,7 +222,7 @@ void RenderablePath::calculatePath(std::string observer) { //float g = _lineColor[1]; //float b = _lineColor[2]; for (int i = 0; i < segments; i++) { - correctPosition = SpiceManager::ref().getTargetPosition(_target, observer, _frame, "NONE", currentTime, pscPos, lightTime); + SpiceManager::ref().getTargetPosition(_target, observer, _frame, "NONE", currentTime, pscPos, lightTime); pscPos[3] += 3; //if (!correctPosition) { diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index f4677942f7..d915d3d1bb 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -138,14 +138,15 @@ RenderEngine::~RenderEngine() { bool RenderEngine::initialize() { std::string renderingMethod = DefaultRenderingMethod; - bool overwritingDefaultRenderingMethod = false; - if (OsEng.configurationManager()->hasKeyAndValue(KeyRenderingMethod)) { + + // If the user specified a rendering method that he would like to use, use that + if (OsEng.configurationManager()->hasKeyAndValue(KeyRenderingMethod)) renderingMethod = OsEng.configurationManager()->value(KeyRenderingMethod); - overwritingDefaultRenderingMethod = true; - } else { using Version = ghoul::systemcapabilities::OpenGLCapabilitiesComponent::Version; - + + // The default rendering method has a requirement of OpenGL 4.3, so if we are + // below that, we will fall back to frame buffer operation if (OpenGLCap.openGLVersion() < Version(4,3)) { LINFO("Falling back to framebuffer implementation due to OpenGL limitations"); renderingMethod = "ABufferFrameBuffer"; @@ -329,7 +330,8 @@ void RenderEngine::postSynchronizationPreDraw() { } // converts the quaternion used to rotation matrices - _mainCamera->compileViewRotationMatrix(); + if (_mainCamera) + _mainCamera->compileViewRotationMatrix(); // update and evaluate the scene starting from the root node _sceneGraph->update({ diff --git a/src/util/spicemanager.cpp b/src/util/spicemanager.cpp index 1e5219021c..9ac6933b6b 100644 --- a/src/util/spicemanager.cpp +++ b/src/util/spicemanager.cpp @@ -316,9 +316,9 @@ bool SpiceManager::getNaifId(const std::string& body, int& id) const { } else { SpiceBoolean success; - SpiceInt sid = id; - bods2c_c(body.c_str(), &sid, &success); - id = sid; +// SpiceInt sid = id; + bods2c_c(body.c_str(), &id, &success); +// id = sid; if (success == SPICEFALSE) LERROR("Could not find NAIF ID of body '" + body + "'"); return (success == SPICETRUE); @@ -331,9 +331,7 @@ bool SpiceManager::getFrameId(const std::string& frame, int& id) const { return false; } else { - SpiceInt sid = id; - namfrm_c(frame.c_str(), &sid); - id = sid; + namfrm_c(frame.c_str(), &id); bool hasError = SpiceManager::checkForError("Error getting id for frame '" + frame + "'"); return !hasError; } @@ -403,7 +401,7 @@ bool SpiceManager::getValue(const std::string& body, const std::string& value, } bool SpiceManager::spacecraftClockToET(const std::string& craftIdCode, double& craftTicks, double& et){ - int craftID; + int craftID = -1; getNaifId(craftIdCode, craftID); sct2e_c(craftID, craftTicks, &et); bool hasError = checkForError("Error transforming spacecraft clock of '" + craftIdCode + "' at time " + std::to_string(craftTicks)); @@ -1010,8 +1008,8 @@ bool SpiceManager::checkForError(std::string errorMessage) { bool SpiceManager::getPlanetEllipsoid(std::string planetName, float &a, float &b, float &c) { SpiceDouble radii[3]; - SpiceInt n; - int id; + SpiceInt n = -1; + int id = -1; getNaifId(planetName, id); if (bodfnd_c(id, "RADII")) { From 0069b88f7ca3bc9d12df1e234f3a6c18434a6654 Mon Sep 17 00:00:00 2001 From: Michal Marcinkowski Date: Wed, 3 Jun 2015 19:53:40 -0400 Subject: [PATCH 100/329] Fixing jenkins error --- modules/newhorizons/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/newhorizons/CMakeLists.txt b/modules/newhorizons/CMakeLists.txt index d25a88fd44..d2ae173fa3 100644 --- a/modules/newhorizons/CMakeLists.txt +++ b/modules/newhorizons/CMakeLists.txt @@ -25,6 +25,7 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) set(HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableshadowcylinder.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometryprojection.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablecrawlingline.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefov.h @@ -45,6 +46,7 @@ set(HEADER_FILES source_group("Header Files" FILES ${HEADER_FILES}) set(SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableshadowcylinder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometryprojection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablecrawlingline.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefov.cpp @@ -64,6 +66,8 @@ set(SOURCE_FILES source_group("Source Files" FILES ${SOURCE_FILES}) set(SHADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/terminatorshadow_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/terminatorshadow_vs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/crawlingline_fs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/crawlingline_vs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/fov_fs.glsl From 0953b35bb661854a99dfebedd9f960f3f091abab Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 4 Jun 2015 01:54:59 +0200 Subject: [PATCH 101/329] Add RenderableShadowCylinders to the New Horizons CMakeLists file --- modules/newhorizons/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/newhorizons/CMakeLists.txt b/modules/newhorizons/CMakeLists.txt index d25a88fd44..fa6a7a06b4 100644 --- a/modules/newhorizons/CMakeLists.txt +++ b/modules/newhorizons/CMakeLists.txt @@ -30,6 +30,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefov.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplaneprojection.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplanetprojection.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableshadowcylinder.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometryprojection.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/writeToTexture.h ${CMAKE_CURRENT_SOURCE_DIR}/util/decoder.h @@ -50,6 +51,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefov.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplaneprojection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplanetprojection.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableshadowcylinder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometryprojection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/decoder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/hongkangparser.cpp @@ -70,6 +72,8 @@ set(SHADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/shaders/fov_vs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/projectiveTexture_fs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/projectiveTexture_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/terminatorshadow_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/terminatorshadow_vs.glsl ) source_group("Shader Files" FILES ${SHADER_FILES}) From d8f7db46e85d38af3640cc5db9868035985840b8 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 4 Jun 2015 01:57:21 +0200 Subject: [PATCH 102/329] Fix CMakeLists to be in proper order --- modules/newhorizons/CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/newhorizons/CMakeLists.txt b/modules/newhorizons/CMakeLists.txt index eb1e729a23..fa6a7a06b4 100644 --- a/modules/newhorizons/CMakeLists.txt +++ b/modules/newhorizons/CMakeLists.txt @@ -25,7 +25,6 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) set(HEADER_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableshadowcylinder.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometryprojection.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablecrawlingline.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefov.h @@ -47,7 +46,6 @@ set(HEADER_FILES source_group("Header Files" FILES ${HEADER_FILES}) set(SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableshadowcylinder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometryprojection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablecrawlingline.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefov.cpp @@ -68,8 +66,6 @@ set(SOURCE_FILES source_group("Source Files" FILES ${SOURCE_FILES}) set(SHADER_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/terminatorshadow_fs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/terminatorshadow_vs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/crawlingline_fs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/crawlingline_vs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/fov_fs.glsl From 1bcf11412e8b23a5511f569da83b8a0a4738e2e9 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Thu, 4 Jun 2015 12:53:32 +0200 Subject: [PATCH 103/329] adding remotecontroller class, changes to CMake to include it and (temporary) functionality to use it, should be declared in CFG file --- .../interaction/interactionhandler.h | 3 + .../openspace/interaction/remotecontroller.h | 58 ++++++++++++++++ src/CMakeLists.txt | 2 + src/engine/openspaceengine.cpp | 3 + src/interaction/interactionhandler.cpp | 11 ++- src/interaction/remotecontroller.cpp | 68 +++++++++++++++++++ 6 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 include/openspace/interaction/remotecontroller.h create mode 100644 src/interaction/remotecontroller.cpp diff --git a/include/openspace/interaction/interactionhandler.h b/include/openspace/interaction/interactionhandler.h index 2b56ebe881..183ae0c0ca 100644 --- a/include/openspace/interaction/interactionhandler.h +++ b/include/openspace/interaction/interactionhandler.h @@ -76,6 +76,7 @@ #include #include +#include #include @@ -94,6 +95,7 @@ public: void setKeyboardController(KeyboardController* controller); void setMouseController(MouseController* controller); + void setRemoteController(RemoteController* controller); void addController(Controller* controller); void lockControls(); @@ -171,6 +173,7 @@ private: KeyboardController* _keyboardController; MouseController* _mouseController; + RemoteController* _remoteController; std::vector _controllers; }; diff --git a/include/openspace/interaction/remotecontroller.h b/include/openspace/interaction/remotecontroller.h new file mode 100644 index 0000000000..e23f020c75 --- /dev/null +++ b/include/openspace/interaction/remotecontroller.h @@ -0,0 +1,58 @@ +/***************************************************************************************** +* * +* OpenSpace * +* * +* Copyright (c) 2014-2015 * +* * +* Permission is hereby granted, free of charge, to any person obtaining a copy of this * +* software and associated documentation files (the "Software"), to deal in the Software * +* without restriction, including without limitation the rights to use, copy, modify, * +* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * +* permit persons to whom the Software is furnished to do so, subject to the following * +* conditions: * +* * +* The above copyright notice and this permission notice shall be included in all copies * +* or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * +* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * +* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * +* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * +* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * +****************************************************************************************/ + +#ifndef __REMOTECONTROLLER_H__ +#define __REMOTECONTROLLER_H__ + +#include +#include + +#include + +namespace openspace { + namespace interaction { + + struct ControllerKeyFrame{ + glm::mat4 _viewRotationMatrix; + psc _position; + double _timeStamp; + }; + + class RemoteController : public Controller { + public: + RemoteController(); + virtual ~RemoteController(); + virtual void update(const double& dt); + virtual void sendKeyFrame(); + virtual void keyFrameReceived(const ControllerKeyFrame& keyframe); + protected: + bool _isBroadcasting; + double _lastTimeStap; + std::ifstream ff; + }; + + } // namespace interaction +} // namespace openspace + +#endif // __REMOTECONTROLLER_H__ \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2c1f8518cb..0e1b46fb41 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,6 +34,7 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/engine/moduleengine.cpp ${OPENSPACE_BASE_DIR}/src/engine/openspaceengine.cpp ${OPENSPACE_BASE_DIR}/src/interaction/controller.cpp + ${OPENSPACE_BASE_DIR}/src/interaction/remotecontroller.cpp ${OPENSPACE_BASE_DIR}/src/interaction/deviceidentifier.cpp ${OPENSPACE_BASE_DIR}/src/interaction/interactionhandler.cpp ${OPENSPACE_BASE_DIR}/src/interaction/interactionhandler_lua.inl @@ -99,6 +100,7 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/engine/moduleengine.h ${OPENSPACE_BASE_DIR}/include/openspace/engine/openspaceengine.h ${OPENSPACE_BASE_DIR}/include/openspace/interaction/controller.h + ${OPENSPACE_BASE_DIR}/include/openspace/interaction/remotecontroller.h ${OPENSPACE_BASE_DIR}/include/openspace/interaction/deviceidentifier.h ${OPENSPACE_BASE_DIR}/include/openspace/interaction/interactionhandler.h ${OPENSPACE_BASE_DIR}/include/openspace/interaction/keyboardcontroller.h diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 6264d9db7c..95c69e3f34 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -350,6 +350,9 @@ bool OpenSpaceEngine::initialize() { //_interactionHandler.setMouseController(new interaction::TrackballMouseController); _interactionHandler->setMouseController(new interaction::OrbitalMouseController); + //@TODO fix this -JK + _interactionHandler->setRemoteController(new interaction::RemoteController); + // Run start up scripts runStartupScripts(); diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index 76668e95e0..3c4ae90b30 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -253,6 +253,7 @@ InteractionHandler::InteractionHandler() , _invertRotation(false) , _keyboardController(nullptr) , _mouseController(nullptr) + , _remoteController(nullptr) { } @@ -277,6 +278,13 @@ void InteractionHandler::setMouseController(MouseController* controller) { _mouseController->setHandler(this); } +void InteractionHandler::setRemoteController(RemoteController* controller) { + assert(controller); + delete _remoteController; + _remoteController = controller; + _remoteController->setHandler(this); +} + void InteractionHandler::addController(Controller* controller) { assert(controller); _controllers.push_back(controller); @@ -347,6 +355,7 @@ void InteractionHandler::unlockControls() { void InteractionHandler::update(double deltaTime) { _deltaTime = deltaTime; _mouseController->update(deltaTime); + _remoteController->update(deltaTime); } void InteractionHandler::setFocusNode(SceneGraphNode* node) { @@ -463,7 +472,7 @@ void InteractionHandler::orbit(const float &dx, const float &dy, const float &dz _camera->setFocusPosition(origin); _camera->setPosition(target); _camera->rotate(glm::quat_cast(transform)); - + unlockControls(); } diff --git a/src/interaction/remotecontroller.cpp b/src/interaction/remotecontroller.cpp new file mode 100644 index 0000000000..7d770ebb72 --- /dev/null +++ b/src/interaction/remotecontroller.cpp @@ -0,0 +1,68 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include + +#include + +#include + +namespace openspace { +namespace interaction { + + RemoteController::RemoteController() + : _isBroadcasting(false), + _lastTimeStap(0.0) + { + //ff.open("path.txt"); + } + + RemoteController::~RemoteController(){ + + } + + void RemoteController::update(const double& dt){ + ControllerKeyFrame kf; + kf._position = _handler->camera()->position(); + kf._viewRotationMatrix = _handler->camera()->viewRotationMatrix(); + kf._timeStamp = Time::ref().currentTime(); + + std::string write = std::to_string(kf._position.vec4().x) + "\t" + std::to_string(kf._position.vec4().y) + "\t" + std::to_string(kf._position.vec4().z) + "\t" + std::to_string(kf._position.vec4().w) + "\n"; + //write += + printf("%s", write.c_str()); + } + + void RemoteController::sendKeyFrame(){ + + } + + void RemoteController::keyFrameReceived(const ControllerKeyFrame& keyframe){ + + } + + +} // namespace interaction +} // namespace openspace From 17498c28c637c54445b9929f61af5bebed918463 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 5 Jun 2015 01:45:15 +0200 Subject: [PATCH 104/329] Minor cleanups --- include/openspace/scripting/scriptengine.h | 2 +- src/engine/openspaceengine.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/include/openspace/scripting/scriptengine.h b/include/openspace/scripting/scriptengine.h index f1f2e79754..e8aa0e0c52 100644 --- a/include/openspace/scripting/scriptengine.h +++ b/include/openspace/scripting/scriptengine.h @@ -92,7 +92,7 @@ private: lua_State* _state; std::set _registeredLibraries; - + //sync variables std::mutex _mutex; std::vector _queuedScripts; diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 6264d9db7c..a1547d463a 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -484,7 +484,6 @@ void OpenSpaceEngine::runSettingsScripts() { runScripts(scripts); } - void OpenSpaceEngine::loadFonts() { sgct_text::FontManager::FontPath local = sgct_text::FontManager::FontPath::FontPath_Local; From 8de6d9db05d085fa44adbc0b1a7dda7dc3b35fcd Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 5 Jun 2015 14:25:35 +0200 Subject: [PATCH 105/329] Moving global blackout factor into RenderEngine Cleanup of Abuffer implementations --- include/openspace/abuffer/abuffer.h | 2 +- .../openspace/abuffer/abufferframebuffer.h | 2 ++ shaders/ABuffer/abufferResolveFragment.glsl | 4 ++- src/abuffer/abuffer.cpp | 32 +++++++------------ src/abuffer/abufferframebuffer.cpp | 3 ++ src/abuffer/abuffersinglelinked.cpp | 14 ++++---- src/main.cpp | 32 ------------------- src/rendering/renderengine.cpp | 2 +- 8 files changed, 28 insertions(+), 63 deletions(-) diff --git a/include/openspace/abuffer/abuffer.h b/include/openspace/abuffer/abuffer.h index f8e1550a29..1e69977821 100644 --- a/include/openspace/abuffer/abuffer.h +++ b/include/openspace/abuffer/abuffer.h @@ -55,7 +55,7 @@ public: ABuffer(); virtual ~ABuffer(); - virtual void resolve(); + virtual void resolve(float blackoutFactor); virtual bool initialize() = 0; virtual bool reinitialize(); diff --git a/include/openspace/abuffer/abufferframebuffer.h b/include/openspace/abuffer/abufferframebuffer.h index c348c777f6..604c5fa5d6 100644 --- a/include/openspace/abuffer/abufferframebuffer.h +++ b/include/openspace/abuffer/abufferframebuffer.h @@ -36,6 +36,8 @@ public: virtual ~ABufferFramebuffer(); virtual bool initialize(); + void resolve(float blackoutFactor); + virtual void clear(); virtual void preRender(); virtual void postRender(); diff --git a/shaders/ABuffer/abufferResolveFragment.glsl b/shaders/ABuffer/abufferResolveFragment.glsl index d9e45834f9..dc6fedcb07 100644 --- a/shaders/ABuffer/abufferResolveFragment.glsl +++ b/shaders/ABuffer/abufferResolveFragment.glsl @@ -56,6 +56,8 @@ const float stepSize = 0.01; const float samplingRate = 1.0; uniform float ALPHA_LIMIT = 0.99; +uniform float blackoutFactor = 0.0; + // Math defintions #define M_E 2.7182818284590452354 @@ -307,7 +309,7 @@ void main() { out_color = vec4(texCoord,0.0,1.0); int frag_count = build_local_fragments_list(); sort_fragments_list(frag_count); - out_color = calculate_final_color(frag_count); + out_color = blackoutFactor * calculate_final_color(frag_count); } // ================================================================================ diff --git a/src/abuffer/abuffer.cpp b/src/abuffer/abuffer.cpp index 700413430d..8367e0660b 100644 --- a/src/abuffer/abuffer.cpp +++ b/src/abuffer/abuffer.cpp @@ -55,20 +55,17 @@ namespace openspace { ABuffer::ABuffer() : _validShader(false) , _resolveShader(nullptr) - , _volumeStepFactor(0.0f) + , _volumeStepFactor(0.f) { updateDimensions(); } ABuffer::~ABuffer() { - - if(_resolveShader) - delete _resolveShader; + delete _resolveShader; - for(auto file: _samplerFiles) { + for (auto file: _samplerFiles) delete file; - } } bool ABuffer::initializeABuffer() { @@ -88,14 +85,14 @@ bool ABuffer::initializeABuffer() { if (!_resolveShader) return false; _resolveShader->setProgramObjectCallback(shaderCallback); + // Remove explicit callback and use programobject isDirty instead ---abock -#ifndef __APPLE__ // ============================ // GEOMETRY (quad) // ============================ const GLfloat size = 1.0f; - const GLfloat vertex_data[] = { // square of two triangles (sigh) - // x y z w s t + const GLfloat vertex_data[] = { + // x y s t -size, -size, 0.0f, 1.0f, size, size, 0.0f, 1.0f, -size, size, 0.0f, 1.0f, @@ -111,20 +108,17 @@ bool ABuffer::initializeABuffer() { glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*4, reinterpret_cast(0)); glEnableVertexAttribArray(0); -#endif return true; } bool ABuffer::reinitialize() { - // set the total resolution for all viewports updateDimensions(); return reinitializeInternal(); } -void ABuffer::resolve() { -#ifndef __APPLE__ - if( ! _validShader) { +void ABuffer::resolve(float blackoutFactor) { + if (!_validShader) { generateShaderSource(); updateShader(); _validShader = true; @@ -134,13 +128,14 @@ void ABuffer::resolve() { return; _resolveShader->activate(); + _resolveShader->setUniform("blackoutFactor", blackoutFactor); int startAt = 0; - for(int i = 0; i < _volumes.size(); ++i) { + for (int i = 0; i < _volumes.size(); ++i) { glActiveTexture(GL_TEXTURE0 + i); _volumes.at(i).second->bind(); startAt = i + 1; } - for(int i = 0; i < _transferFunctions.size(); ++i) { + for (int i = 0; i < _transferFunctions.size(); ++i) { glActiveTexture(GL_TEXTURE0 + startAt + i); _transferFunctions.at(i).second->bind(); } @@ -158,7 +153,6 @@ void ABuffer::resolve() { glDrawArrays(GL_TRIANGLES, 0, 6); _resolveShader->deactivate(); -#endif } void ABuffer::addVolume(const std::string& tag,ghoul::opengl::Texture* volume) { @@ -212,8 +206,7 @@ bool ABuffer::updateShader() { } void ABuffer::generateShaderSource() { - - for(int i = 0; i < _samplerFiles.size(); ++i) { + for (int i = 0; i < _samplerFiles.size(); ++i) { std::string line, source = ""; std::ifstream samplerFile(_samplerFiles.at(i)->path()); if(samplerFile.is_open()) { @@ -233,7 +226,6 @@ void ABuffer::generateShaderSource() { } void ABuffer::openspaceHeaders() { - std::ofstream f(absPath(generatedHeadersPath)); f << "#define MAX_VOLUMES " << std::to_string(_samplers.size()) << "\n" << "#define MAX_TF " << _transferFunctions.size() << "\n"; diff --git a/src/abuffer/abufferframebuffer.cpp b/src/abuffer/abufferframebuffer.cpp index e2c13d7cae..e7a174e51e 100644 --- a/src/abuffer/abufferframebuffer.cpp +++ b/src/abuffer/abufferframebuffer.cpp @@ -59,6 +59,9 @@ void ABufferFramebuffer::preRender() { void ABufferFramebuffer::postRender() { } + +void ABufferFramebuffer::resolve(float blackoutFactor) { +} std::vector ABufferFramebuffer::pixelData() { return std::vector(); diff --git a/src/abuffer/abuffersinglelinked.cpp b/src/abuffer/abuffersinglelinked.cpp index 89750f6ced..834f54ad07 100644 --- a/src/abuffer/abuffersinglelinked.cpp +++ b/src/abuffer/abuffersinglelinked.cpp @@ -56,11 +56,11 @@ ABufferSingleLinked::ABufferSingleLinked() {} ABufferSingleLinked::~ABufferSingleLinked() { - glDeleteTextures(1,&_anchorPointerTexture); - glDeleteTextures(1,&_fragmentTexture); - glDeleteBuffers(1,&_anchorPointerTextureInitializer); - glDeleteBuffers(1,&_atomicCounterBuffer); - glDeleteBuffers(1,&_anchorPointerTextureInitializer); + glDeleteTextures(1, &_anchorPointerTexture); + glDeleteTextures(1, &_fragmentTexture); + glDeleteBuffers(1, &_anchorPointerTextureInitializer); + glDeleteBuffers(1, &_atomicCounterBuffer); + glDeleteBuffers(1, &_anchorPointerTextureInitializer); } bool ABufferSingleLinked::initialize() { @@ -123,7 +123,6 @@ void ABufferSingleLinked::clear() { } void ABufferSingleLinked::preRender() { - // Bind head-pointer image for read-write glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, _atomicCounterBuffer); glBindImageTexture(0, _anchorPointerTexture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_R32UI); @@ -135,7 +134,6 @@ void ABufferSingleLinked::postRender() { } std::vector ABufferSingleLinked::pixelData() { - unsigned int* anchorTexture = new unsigned int[_totalPixels]; unsigned int* fragmentBuffer = new unsigned int[_totalPixels*MAX_LAYERS*4]; @@ -201,4 +199,4 @@ std::vector ABufferSingleLinked::pixelData() return d; } -} // openspace \ No newline at end of file +} // openspace diff --git a/src/main.cpp b/src/main.cpp index 0edbe2edc9..7862d73111 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -73,12 +73,6 @@ std::pair supportedOpenGLVersion () { return { major, minor }; } -//temporary post-FX functions, TODO make a more permanent solution to this @JK -void postFXPass(); -void setupPostFX(); -GLint _postFXTexLoc; -GLint _postFXOpacityLoc; - #include namespace { @@ -195,9 +189,6 @@ void mainInitFunc() { std::cin.ignore(100); exit(EXIT_FAILURE); } - - //temporary post-FX solution, TODO add a more permanent solution @JK - setupPostFX(); } void mainPreSyncFunc() { @@ -272,26 +263,3 @@ void mainLogCallback(const char* msg){ // Remove the trailing \n that is passed along LINFOC("SGCT", message.substr(0, std::max(message.size() - 1, 0))); } - -void postFXPass(){ - glUniform1i(_postFXTexLoc, 0); - if (OsEng.isMaster()) - glUniform1f(_postFXOpacityLoc, 1.f); - else - glUniform1f(_postFXOpacityLoc, OsEng.renderEngine()->globalBlackOutFactor()); -} - -void setupPostFX(){ -#ifndef __APPLE__ - sgct::PostFX fx[1]; - sgct::ShaderProgram *shader; - fx[0].init("OpacityControl", absPath("${SHADERS}/postFX_vs.glsl"), absPath("${SHADERS}/postFX_fs.glsl")); - fx[0].setUpdateUniformsFunction(postFXPass); - shader = fx[0].getShaderProgram(); - shader->bind(); - _postFXTexLoc = shader->getUniformLocation("Tex"); - _postFXOpacityLoc = shader->getUniformLocation("Opacity"); - shader->unbind(); - _sgctEngine->addPostFX(fx[0]); -#endif -} diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index d915d3d1bb..3d6f87acce 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -395,7 +395,7 @@ void RenderEngine::render(const glm::mat4 &projectionMatrix, const glm::mat4 &vi glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - _abuffer->resolve(); + _abuffer->resolve(_globalBlackOutFactor); glDisable(GL_BLEND); } else { From a798edf19858738bee34cf7c82a85d41ffa4eeed Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 5 Jun 2015 16:27:31 +0200 Subject: [PATCH 106/329] Added missing space for a message logging of model geometry Update Ghoul --- ext/ghoul | 2 +- modules/base/rendering/modelgeometry.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index 712da50047..03983ed78e 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 712da500477f762a443c09fd23198e93f3958a7a +Subproject commit 03983ed78edab3e068c2c3a1f4457a9a163f3161 diff --git a/modules/base/rendering/modelgeometry.cpp b/modules/base/rendering/modelgeometry.cpp index a390bae9a2..7630c78c30 100644 --- a/modules/base/rendering/modelgeometry.cpp +++ b/modules/base/rendering/modelgeometry.cpp @@ -155,7 +155,7 @@ bool ModelGeometry::loadObj(const std::string& filename){ // file for the next run } else { - LINFO("Cache for Model'" << filename << "' not found"); + LINFO("Cache for Model '" << filename << "' not found"); } LINFO("Loading Model file '" << filename << "'"); bool success = loadModel(filename); From d231a5f681a174d81e9a8311a54ae1d0a361a705 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 6 Jun 2015 11:48:07 +0200 Subject: [PATCH 107/329] Move openspace-based applications into their own subfolder --- CMakeLists.txt | 5 ++- apps/OpenSpace/CMakeLists.txt | 46 +++++++++++++++++++ {src => apps/OpenSpace}/main.cpp | 0 src/CMakeLists.txt | 4 -- support/cmake/support_macros.cmake | 72 +++++++++++++++++------------- 5 files changed, 91 insertions(+), 36 deletions(-) create mode 100644 apps/OpenSpace/CMakeLists.txt rename {src => apps/OpenSpace}/main.cpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8144a87665..87a8e22943 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ project (OpenSpace) message(STATUS "Generating OpenSpace project") set(OPENSPACE_BASE_DIR "${PROJECT_SOURCE_DIR}") +set(OPENSPACE_APPS_DIR "${OPENSPACE_BASE_DIR}/apps") set(OPENSPACE_EXT_DIR "${OPENSPACE_BASE_DIR}/ext") set(OPENSPACE_MODULE_DIR "${OPENSPACE_BASE_DIR}/modules") set(OPENSPACE_CMAKE_EXT_DIR "${OPENSPACE_BASE_DIR}/support/cmake") @@ -48,9 +49,9 @@ option(OPENSPACE_BUILD_GUI_APPLICATIONS "Build GUI Applications" OFF) include(src/CMakeLists.txt) -create_openspace_targets() -set_compile_settings() +create_openspace_target() add_external_dependencies() +handle_applications() if (MSVC) option(OPENSPACE_ENABLE_VLD "Enable the Visual Leak Detector" OFF) diff --git a/apps/OpenSpace/CMakeLists.txt b/apps/OpenSpace/CMakeLists.txt new file mode 100644 index 0000000000..066514f85b --- /dev/null +++ b/apps/OpenSpace/CMakeLists.txt @@ -0,0 +1,46 @@ +######################################################################################### +# # +# OpenSpace # +# # +# Copyright (c) 2014-2015 # +# # +# 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. # +######################################################################################### + +add_executable(OpenSpace ${OPENSPACE_APPS_DIR}/OpenSpace/main.cpp) +target_include_directories(OpenSpace PUBLIC ${OPENSPACE_BASE_DIR}/include) +target_link_libraries(OpenSpace libOpenSpace) + +if (MSVC) + if (OPENSPACE_WARNINGS_AS_ERRORS) + target_compile_options(OpenSpace PUBLIC "/WX") + endif () + + set_target_properties(OpenSpace PROPERTIES LINK_FLAGS + "/NODEFAULTLIB:LIBCMTD.lib /NODEFAULTLIB:LIBCMT.lib" + ) +elseif (APPLE) + if (OPENSPACE_WARNINGS_AS_ERRORS) + target_compile_options(OpenSpace PUBLIC "-Werror") + endif () +elseif (UNIX) + if (OPENSPACE_WARNINGS_AS_ERRORS) + target_compile_options(OpenSpace PUBLIC "-Werror") + endif () + target_compile_options(OpenSpace PUBLIC "-ggdb" "-Wall" "-Wno-long-long" "-pedantic" "-Wextra") +endif () diff --git a/src/main.cpp b/apps/OpenSpace/main.cpp similarity index 100% rename from src/main.cpp rename to apps/OpenSpace/main.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2c1f8518cb..f8134bd3b4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -83,10 +83,6 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/util/time_lua.inl ) -set(OPENSPACE_MAIN - ${OPENSPACE_BASE_DIR}/src/main.cpp -) - set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/abuffer/abuffer.h ${OPENSPACE_BASE_DIR}/include/openspace/abuffer/abufferdynamic.h diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 1c538ad3fc..3029a03653 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -64,59 +64,51 @@ endfunction () -function (create_openspace_targets) +function (create_openspace_target) add_library(libOpenSpace STATIC ${OPENSPACE_HEADER} ${OPENSPACE_SOURCE}) target_include_directories(libOpenSpace PUBLIC ${OPENSPACE_BASE_DIR}/include) target_include_directories(libOpenSpace PUBLIC ${OPENSPACE_BASE_DIR}) target_include_directories(libOpenSpace PUBLIC ${CMAKE_BINARY_DIR}/_generated/include) - add_executable(OpenSpace ${OPENSPACE_MAIN}) - target_include_directories(OpenSpace PUBLIC ${OPENSPACE_BASE_DIR}/include) - target_link_libraries(OpenSpace libOpenSpace) + set_compile_settings(libOpenSpace) endfunction () -function (set_compile_settings) +function (set_compile_settings project) if (MSVC) - target_compile_options(libOpenSpace PUBLIC "/MP" "/wd4201" "/wd4127") + target_compile_options(${project} PUBLIC "/MP" "/wd4201" "/wd4127") if (OPENSPACE_WARNINGS_AS_ERRORS) - target_compile_options(libOpenSpace PUBLIC "/WX") - target_compile_options(OpenSpace PUBLIC "/WX") + target_compile_options(${project} PUBLIC "/WX") endif () - - set_target_properties(OpenSpace PROPERTIES LINK_FLAGS - "/NODEFAULTLIB:LIBCMTD.lib /NODEFAULTLIB:LIBCMT.lib" - ) elseif (APPLE) - target_compile_definitions(libOpenSpace PUBLIC "__APPLE__") + target_compile_definitions(${project} PUBLIC "__APPLE__") include (CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) mark_as_advanced(COMPILER_SUPPORTS_CXX11, COMPILER_SUPPORTS_CXX0X) if (COMPILER_SUPPORTS_CXX11) - target_compile_options(libOpenSpace PUBLIC "-std=c++11") + target_compile_options(${project} PUBLIC "-std=c++11") elseif (COMPILER_SUPPORTS_CXX0X) - target_compile_options(libOpenSpace PUBLIC "-std=c++0x") + target_compile_options(${project} PUBLIC "-std=c++0x") else () message(FATAL_ERROR "Compiler does not have C++11 support") endif () if (OPENSPACE_WARNINGS_AS_ERRORS) - target_compile_options(libOpenSpace PUBLIC "-Werror") - target_compile_options(OpenSpace PUBLIC "-Werror") + target_compile_options(${project} PUBLIC "-Werror") endif () - target_compile_options(libOpenSpace PUBLIC "-stdlib=libc++") + target_compile_options(${project} PUBLIC "-stdlib=libc++") - target_include_directories(libOpenSpace PUBLIC "/Developer/Headers/FlatCarbon") + target_include_directories(${project} PUBLIC "/Developer/Headers/FlatCarbon") find_library(COREFOUNDATION_LIBRARY CoreFoundation) find_library(CARBON_LIBRARY Carbon) find_library(COCOA_LIBRARY Carbon) find_library(APP_SERVICES_LIBRARY ApplicationServices) mark_as_advanced(CARBON_LIBRARY COCOA_LIBRARY APP_SERVICES_LIBRARY) - target_link_libraries(libOpenSpace + target_link_libraries(${project} ${CARBON_LIBRARY} ${COREFOUNDATION_LIBRARY} ${COCOA_LIBRARY} @@ -128,20 +120,18 @@ function (set_compile_settings) CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) mark_as_advanced(COMPILER_SUPPORTS_CXX11, COMPILER_SUPPORTS_CXX0X) if (COMPILER_SUPPORTS_CXX11) - target_compile_options(libOpenSpace PUBLIC "-std=c++11") + target_compile_options(${project} PUBLIC "-std=c++11") elseif (COMPILER_SUPPORTS_CXX0X) - target_compile_options(libOpenSpace PUBLIC "-std=c++0x") + target_compile_options(${project} PUBLIC "-std=c++0x") else () message(FATAL_ERROR "Compiler does not have C++11 support") endif () if (OPENSPACE_WARNINGS_AS_ERRORS) - target_compile_options(libOpenSpace PUBLIC "-Werror") - target_compile_options(OpenSpace PUBLIC "-Werror") + target_compile_options(${project} PUBLIC "-Werror") endif () - target_compile_options(libOpenSpace PUBLIC "-ggdb" "-Wall" "-Wno-long-long" "-pedantic" "-Wextra") - target_compile_options(OpenSpace PUBLIC "-ggdb" "-Wall" "-Wno-long-long" "-pedantic" "-Wextra") + target_compile_options(${project} PUBLIC "-ggdb" "-Wall" "-Wno-long-long" "-pedantic" "-Wextra") endif () endfunction () @@ -172,18 +162,35 @@ function (add_external_dependencies) find_package(Spice REQUIRED) target_include_directories(libOpenSpace SYSTEM PUBLIC ${SPICE_INCLUDE_DIRS}) target_link_libraries(libOpenSpace ${SPICE_LIBRARIES}) - endfunction () +function (handle_applications) + set(applications "") + option(OPENSPACE_APPLICATION_OPENSPACE "Main OpenSpace Application" ON) + if (OPENSPACE_APPLICATION_OPENSPACE) + include(${OPENSPACE_APPS_DIR}/OpenSpace/CMakeLists.txt) + list(APPEND applications "OpenSpace") + endif () + set(OPENSPACE_APPLICATIONS ${applications} PARENT_SCOPE) + + message(STATUS "Applications:") + foreach (app ${applications}) + message(STATUS "\t${app}") + endforeach () +endfunction() + + function (handle_option_vld) if (OPENSPACE_ENABLE_VLD) target_compile_definitions(libOpenSpace PUBLIC "OPENSPACE_ENABLE_VLD") target_link_libraries(libOpenSpace ${OPENSPACE_EXT_DIR}/vld/lib/vld.lib) target_include_directories(libOpenSpace PUBLIC ${OPENSPACE_EXT_DIR}/vld) - copy_files(OpenSpace "${OPENSPACE_EXT_DIR}/vld/bin/vld_x64.dll") + foreach (app ${OPENSPACE_APPLCATIONS}) + copy_files(${app} "${OPENSPACE_EXT_DIR}/vld/bin/vld_x64.dll") + endforeach () endif () endfunction () @@ -298,7 +305,10 @@ function (handle_internal_modules) set(MODULE_HEADERS "") set(MODULE_CLASSES "") - message(STATUS ${sortedModules}) + message(STATUS "Included modules:") + foreach (module ${sortedModules}) + message(STATUS "\t${module}") + endforeach () # Add subdirectories in the correct order foreach (module ${sortedModules}) @@ -306,7 +316,9 @@ function (handle_internal_modules) if (${optionName}) create_library_name(${module} libraryName) add_subdirectory(${OPENSPACE_MODULE_DIR}/${module}) - target_link_libraries(OpenSpace ${libraryName}) + foreach (app ${OPENSPACE_APPLICATIONS}) + target_link_libraries(${app} ${libraryName}) + endforeach () target_link_libraries(libOpenSpace ${libraryName}) create_define_name(${module} defineName) target_compile_definitions(libOpenSpace PUBLIC "${defineName}") From e56d58a54899c40b21f145c1b41c874c2c36376e Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 7 Jun 2015 19:15:45 +0200 Subject: [PATCH 108/329] Move timelineview application into app folder --- CMakeLists.txt | 2 +- apps/OpenSpace/CMakeLists.txt | 24 ++----- apps/TimelineView/CMakeLists.txt | 62 +++++++++++++++++ .../TimelineView}/common.h | 0 .../TimelineView}/configurationwidget.cpp | 0 .../TimelineView}/configurationwidget.h | 0 .../TimelineView}/controlwidget.cpp | 0 .../TimelineView}/controlwidget.h | 0 .../TimelineView}/informationwidget.cpp | 0 .../TimelineView}/informationwidget.h | 0 .../TimelineView}/main.cpp | 0 .../TimelineView}/mainwindow.cpp | 0 .../TimelineView}/mainwindow.h | 0 .../TimelineView}/timelinewidget.cpp | 0 .../TimelineView}/timelinewidget.h | 0 gui/CMakeLists.txt | 3 - gui/timelineview/CMakeLists.txt | 22 ------ support/cmake/support_macros.cmake | 69 ++++++++++++++++--- 18 files changed, 128 insertions(+), 54 deletions(-) create mode 100644 apps/TimelineView/CMakeLists.txt rename {gui/timelineview => apps/TimelineView}/common.h (100%) rename {gui/timelineview => apps/TimelineView}/configurationwidget.cpp (100%) rename {gui/timelineview => apps/TimelineView}/configurationwidget.h (100%) rename {gui/timelineview => apps/TimelineView}/controlwidget.cpp (100%) rename {gui/timelineview => apps/TimelineView}/controlwidget.h (100%) rename {gui/timelineview => apps/TimelineView}/informationwidget.cpp (100%) rename {gui/timelineview => apps/TimelineView}/informationwidget.h (100%) rename {gui/timelineview => apps/TimelineView}/main.cpp (100%) rename {gui/timelineview => apps/TimelineView}/mainwindow.cpp (100%) rename {gui/timelineview => apps/TimelineView}/mainwindow.h (100%) rename {gui/timelineview => apps/TimelineView}/timelinewidget.cpp (100%) rename {gui/timelineview => apps/TimelineView}/timelinewidget.h (100%) delete mode 100644 gui/CMakeLists.txt delete mode 100644 gui/timelineview/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 87a8e22943..f6735892f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # ######################################################################################### -cmake_minimum_required (VERSION 3.0) +cmake_minimum_required (VERSION 3.0 FATAL_ERROR) project (OpenSpace) message(STATUS "Generating OpenSpace project") diff --git a/apps/OpenSpace/CMakeLists.txt b/apps/OpenSpace/CMakeLists.txt index 066514f85b..a49ee281e8 100644 --- a/apps/OpenSpace/CMakeLists.txt +++ b/apps/OpenSpace/CMakeLists.txt @@ -22,25 +22,15 @@ # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # ######################################################################################### -add_executable(OpenSpace ${OPENSPACE_APPS_DIR}/OpenSpace/main.cpp) -target_include_directories(OpenSpace PUBLIC ${OPENSPACE_BASE_DIR}/include) -target_link_libraries(OpenSpace libOpenSpace) +set(APPLICATION_NAME OpenSpace) +set(APPLICATION_LINK_TO_OPENSPACE ON) + +add_executable(${APPLICATION_NAME} ${OPENSPACE_APPS_DIR}/OpenSpace/main.cpp) +target_include_directories(${APPLICATION_NAME} PUBLIC ${OPENSPACE_BASE_DIR}/include) +target_link_libraries(${APPLICATION_NAME} libOpenSpace) if (MSVC) - if (OPENSPACE_WARNINGS_AS_ERRORS) - target_compile_options(OpenSpace PUBLIC "/WX") - endif () - - set_target_properties(OpenSpace PROPERTIES LINK_FLAGS + set_target_properties(${APPLICATION_NAME} PROPERTIES LINK_FLAGS "/NODEFAULTLIB:LIBCMTD.lib /NODEFAULTLIB:LIBCMT.lib" ) -elseif (APPLE) - if (OPENSPACE_WARNINGS_AS_ERRORS) - target_compile_options(OpenSpace PUBLIC "-Werror") - endif () -elseif (UNIX) - if (OPENSPACE_WARNINGS_AS_ERRORS) - target_compile_options(OpenSpace PUBLIC "-Werror") - endif () - target_compile_options(OpenSpace PUBLIC "-ggdb" "-Wall" "-Wno-long-long" "-pedantic" "-Wextra") endif () diff --git a/apps/TimelineView/CMakeLists.txt b/apps/TimelineView/CMakeLists.txt new file mode 100644 index 0000000000..284192421e --- /dev/null +++ b/apps/TimelineView/CMakeLists.txt @@ -0,0 +1,62 @@ +######################################################################################### +# # +# OpenSpace # +# # +# Copyright (c) 2014-2015 # +# # +# 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. # +######################################################################################### + +set(APPLICATION_NAME TimelineView) +set(APPLICATION_LINK_TO_OPENSPACE OFF) + +set(SOURCE_FILES + ${OPENSPACE_APPS_DIR}/TimelineView/main.cpp + ${OPENSPACE_APPS_DIR}/TimelineView/mainwindow.cpp + ${OPENSPACE_APPS_DIR}/TimelineView/configurationwidget.cpp + ${OPENSPACE_APPS_DIR}/TimelineView/informationwidget.cpp + ${OPENSPACE_APPS_DIR}/TimelineView/controlwidget.cpp + ${OPENSPACE_APPS_DIR}/TimelineView/timelinewidget.cpp +) + +set(HEADER_FILES + ${OPENSPACE_APPS_DIR}/TimelineView/mainwindow.h + ${OPENSPACE_APPS_DIR}/TimelineView/configurationwidget.h + ${OPENSPACE_APPS_DIR}/TimelineView/informationwidget.h + ${OPENSPACE_APPS_DIR}/TimelineView/controlwidget.h + ${OPENSPACE_APPS_DIR}/TimelineView/timelinewidget.h +) + +find_package(Qt5Widgets) +find_package(Qt5Network) + +qt5_wrap_cpp(MOC_FILES ${HEADER_FILES}) + +add_executable(${APPLICATION_NAME} MACOSX_BUNDLE ${SOURCE_FILES} ${HEADER_FILES} ${MOC_FILES}) + +target_link_libraries(${APPLICATION_NAME} + Qt5::Widgets + Qt5::Network +) + +if (APPLE) +INSTALL(CODE " + include(BundleUtilities) + fixup_bundle(\"/Users/alex/Development/OpenSpace/bin/openspace/Debug/TimelineView.app/Contents/MacOS/TimelineView\" \"/Users/alex/Development/OpenSpace/bin/openspace/Debug/TimelineView.app/Contents/plugins/platforms/libqcocoa.dylib\" \"\") + " COMPONENT Runtime) +endif () diff --git a/gui/timelineview/common.h b/apps/TimelineView/common.h similarity index 100% rename from gui/timelineview/common.h rename to apps/TimelineView/common.h diff --git a/gui/timelineview/configurationwidget.cpp b/apps/TimelineView/configurationwidget.cpp similarity index 100% rename from gui/timelineview/configurationwidget.cpp rename to apps/TimelineView/configurationwidget.cpp diff --git a/gui/timelineview/configurationwidget.h b/apps/TimelineView/configurationwidget.h similarity index 100% rename from gui/timelineview/configurationwidget.h rename to apps/TimelineView/configurationwidget.h diff --git a/gui/timelineview/controlwidget.cpp b/apps/TimelineView/controlwidget.cpp similarity index 100% rename from gui/timelineview/controlwidget.cpp rename to apps/TimelineView/controlwidget.cpp diff --git a/gui/timelineview/controlwidget.h b/apps/TimelineView/controlwidget.h similarity index 100% rename from gui/timelineview/controlwidget.h rename to apps/TimelineView/controlwidget.h diff --git a/gui/timelineview/informationwidget.cpp b/apps/TimelineView/informationwidget.cpp similarity index 100% rename from gui/timelineview/informationwidget.cpp rename to apps/TimelineView/informationwidget.cpp diff --git a/gui/timelineview/informationwidget.h b/apps/TimelineView/informationwidget.h similarity index 100% rename from gui/timelineview/informationwidget.h rename to apps/TimelineView/informationwidget.h diff --git a/gui/timelineview/main.cpp b/apps/TimelineView/main.cpp similarity index 100% rename from gui/timelineview/main.cpp rename to apps/TimelineView/main.cpp diff --git a/gui/timelineview/mainwindow.cpp b/apps/TimelineView/mainwindow.cpp similarity index 100% rename from gui/timelineview/mainwindow.cpp rename to apps/TimelineView/mainwindow.cpp diff --git a/gui/timelineview/mainwindow.h b/apps/TimelineView/mainwindow.h similarity index 100% rename from gui/timelineview/mainwindow.h rename to apps/TimelineView/mainwindow.h diff --git a/gui/timelineview/timelinewidget.cpp b/apps/TimelineView/timelinewidget.cpp similarity index 100% rename from gui/timelineview/timelinewidget.cpp rename to apps/TimelineView/timelinewidget.cpp diff --git a/gui/timelineview/timelinewidget.h b/apps/TimelineView/timelinewidget.h similarity index 100% rename from gui/timelineview/timelinewidget.h rename to apps/TimelineView/timelinewidget.h diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt deleted file mode 100644 index bcaf2ef502..0000000000 --- a/gui/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -cmake_minimum_required(VERSION 2.8.11) - -add_subdirectory(timelineview) \ No newline at end of file diff --git a/gui/timelineview/CMakeLists.txt b/gui/timelineview/CMakeLists.txt deleted file mode 100644 index 241d78d868..0000000000 --- a/gui/timelineview/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -cmake_minimum_required(VERSION 2.8.11) - -project(TimelineView) -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -set(CMAKE_AUTOMOC ON) -find_package(Qt5Widgets) -find_package(Qt5Network) -if (APPLE) -add_executable(TimelineView MACOSX_BUNDLE main.cpp mainwindow.cpp configurationwidget.cpp informationwidget.cpp controlwidget.cpp timelinewidget.cpp) -else (APPLE) -add_executable(TimelineView main.cpp mainwindow.cpp configurationwidget.cpp informationwidget.cpp controlwidget.cpp timelinewidget.cpp) -endif () - -target_link_libraries(TimelineView Qt5::Widgets Qt5::Network) - -if (APPLE) -INSTALL(CODE " - include(BundleUtilities) - fixup_bundle(\"/Users/alex/Development/OpenSpace/bin/openspace/Debug/TimelineView.app/Contents/MacOS/TimelineView\" \"/Users/alex/Development/OpenSpace/bin/openspace/Debug/TimelineView.app/Contents/plugins/platforms/libqcocoa.dylib\" \"\") - " COMPONENT Runtime) -endif () diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 3029a03653..6b703962a2 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -168,17 +168,55 @@ endfunction () function (handle_applications) set(applications "") - option(OPENSPACE_APPLICATION_OPENSPACE "Main OpenSpace Application" ON) - if (OPENSPACE_APPLICATION_OPENSPACE) - include(${OPENSPACE_APPS_DIR}/OpenSpace/CMakeLists.txt) - list(APPEND applications "OpenSpace") - endif () + set(applications_link_to_openspace "") + + file(GLOB appDirs RELATIVE ${OPENSPACE_APPS_DIR} ${OPENSPACE_APPS_DIR}/*) + list(REMOVE_ITEM appDirs ".DS_Store") # Removing the .DS_Store present on Mac + + set(DEFAULT_APPLICATIONS + "OpenSpace" + ) + mark_as_advanced(DEFAULT_APPLICATIONS) + + foreach (app ${appDirs}) + string(TOUPPER ${app} upper_app) + list (FIND DEFAULT_APPLICATIONS "${app}" _index) + if (${_index} GREATER -1) + # App is a default application + option(OPENSPACE_APPLICATION_${upper_app} "${app} Application" ON) + else () + option(OPENSPACE_APPLICATION_${upper_app} "${app} Application" OFF) + endif() + if (OPENSPACE_APPLICATION_${upper_app}) + unset(APPLICATION_NAME) + unset(APPLICATION_LINK_TO_OPENSPACE) + include(${OPENSPACE_APPS_DIR}/${app}/CMakeLists.txt) + set_compile_settings(${APPLICATION_NAME}) + list(APPEND applications ${APPLICATION_NAME}) + list(APPEND applications_link_to_openspace ${APPLICATION_LINK_TO_OPENSPACE}) + unset(APPLICATION_NAME) + unset(APPLICATION_LINK_TO_OPENSPACE) + endif () + endforeach () + + + # option(OPENSPACE_APPLICATION_OPENSPACE "Main OpenSpace Application" ON) + # if (OPENSPACE_APPLICATION_OPENSPACE) + # include(${OPENSPACE_APPS_DIR}/OpenSpace/CMakeLists.txt) + # list(APPEND applications "OpenSpace") + # endif () set(OPENSPACE_APPLICATIONS ${applications} PARENT_SCOPE) + set(OPENSPACE_APPLICATIONS_LINK_REQUEST ${applications_link_to_openspace} PARENT_SCOPE) message(STATUS "Applications:") - foreach (app ${applications}) - message(STATUS "\t${app}") - endforeach () + list(LENGTH applications len1) + math(EXPR len2 "${len1} - 1") + + foreach(val RANGE ${len2}) + list(GET applications ${val} val1) + list(GET applications_link_to_openspace ${val} val2) + message(STATUS "\t${val1} (Link: ${val2})") + endforeach() endfunction() @@ -316,9 +354,18 @@ function (handle_internal_modules) if (${optionName}) create_library_name(${module} libraryName) add_subdirectory(${OPENSPACE_MODULE_DIR}/${module}) - foreach (app ${OPENSPACE_APPLICATIONS}) - target_link_libraries(${app} ${libraryName}) - endforeach () + + list(LENGTH OPENSPACE_APPLICATIONS len1) + math(EXPR len2 "${len1} - 1") + + foreach(val RANGE ${len2}) + list(GET OPENSPACE_APPLICATIONS ${val} val1) + list(GET OPENSPACE_APPLICATIONS_LINK_REQUEST ${val} val2) + if (${val2}) + target_link_libraries(${app} ${libraryName}) + endif () + endforeach() + target_link_libraries(libOpenSpace ${libraryName}) create_define_name(${module} defineName) target_compile_definitions(libOpenSpace PUBLIC "${defineName}") From 94ed72bce785023cd7e5694a402531c953aac4e3 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 7 Jun 2015 19:19:20 +0200 Subject: [PATCH 109/329] Update copyright notice --- apps/TimelineView/main.cpp | 2 +- apps/TimelineView/mainwindow.cpp | 2 +- apps/TimelineView/mainwindow.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/TimelineView/main.cpp b/apps/TimelineView/main.cpp index f864703957..9bdce7b3ab 100644 --- a/apps/TimelineView/main.cpp +++ b/apps/TimelineView/main.cpp @@ -2,7 +2,7 @@ * * * OpenSpace * * * - * Copyright (c) 2014 * + * Copyright (c) 2014-2015 * * * * 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 * diff --git a/apps/TimelineView/mainwindow.cpp b/apps/TimelineView/mainwindow.cpp index 242f916a53..87c030c662 100644 --- a/apps/TimelineView/mainwindow.cpp +++ b/apps/TimelineView/mainwindow.cpp @@ -2,7 +2,7 @@ * * * OpenSpace * * * - * Copyright (c) 2014 * + * Copyright (c) 2014-2015 * * * * 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 * diff --git a/apps/TimelineView/mainwindow.h b/apps/TimelineView/mainwindow.h index 23d85fec0b..c998082e39 100644 --- a/apps/TimelineView/mainwindow.h +++ b/apps/TimelineView/mainwindow.h @@ -2,7 +2,7 @@ * * * OpenSpace * * * - * Copyright (c) 2014 * + * Copyright (c) 2014-2015 * * * * 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 * From e17b39a851c128fea33b36f507f1118cc87d94d4 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 7 Jun 2015 19:20:56 +0200 Subject: [PATCH 110/329] Cleanup string generation --- apps/TimelineView/main.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/TimelineView/main.cpp b/apps/TimelineView/main.cpp index 9bdce7b3ab..463578f67e 100644 --- a/apps/TimelineView/main.cpp +++ b/apps/TimelineView/main.cpp @@ -149,8 +149,6 @@ int main(int argc, char** argv) { app.setStyleSheet(style); - std::string s = style.toStdString(); - MainWindow window; window.show(); From b19069547464c609e1e6a47d6f889965108605e2 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 8 Jun 2015 01:08:58 +0200 Subject: [PATCH 111/329] Add initial version of launcher --- apps/Launcher/CMakeLists.txt | 66 +++++ apps/Launcher/files.qrc | 5 + apps/Launcher/images/header.png | Bin 0 -> 13192 bytes apps/Launcher/informationwidget.cpp | 29 +++ apps/Launcher/informationwidget.h | 36 +++ apps/Launcher/main.cpp | 155 ++++++++++++ apps/Launcher/mainwindow.cpp | 377 ++++++++++++++++++++++++++++ apps/Launcher/mainwindow.h | 82 ++++++ apps/Launcher/shortcutwidget.cpp | 29 +++ apps/Launcher/shortcutwidget.h | 36 +++ apps/Launcher/syncwidget.cpp | 25 ++ apps/Launcher/syncwidget.h | 34 +++ 12 files changed, 874 insertions(+) create mode 100644 apps/Launcher/CMakeLists.txt create mode 100644 apps/Launcher/files.qrc create mode 100644 apps/Launcher/images/header.png create mode 100644 apps/Launcher/informationwidget.cpp create mode 100644 apps/Launcher/informationwidget.h create mode 100644 apps/Launcher/main.cpp create mode 100644 apps/Launcher/mainwindow.cpp create mode 100644 apps/Launcher/mainwindow.h create mode 100644 apps/Launcher/shortcutwidget.cpp create mode 100644 apps/Launcher/shortcutwidget.h create mode 100644 apps/Launcher/syncwidget.cpp create mode 100644 apps/Launcher/syncwidget.h diff --git a/apps/Launcher/CMakeLists.txt b/apps/Launcher/CMakeLists.txt new file mode 100644 index 0000000000..f0c902e10d --- /dev/null +++ b/apps/Launcher/CMakeLists.txt @@ -0,0 +1,66 @@ +######################################################################################### +# # +# OpenSpace # +# # +# Copyright (c) 2014-2015 # +# # +# 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. # +######################################################################################### + +set(APPLICATION_NAME Launcher) +set(APPLICATION_LINK_TO_OPENSPACE ON) + +set(SOURCE_FILES + ${OPENSPACE_APPS_DIR}/Launcher/main.cpp + ${OPENSPACE_APPS_DIR}/Launcher/informationwidget.cpp + ${OPENSPACE_APPS_DIR}/Launcher/mainwindow.cpp + ${OPENSPACE_APPS_DIR}/Launcher/shortcutwidget.cpp + ${OPENSPACE_APPS_DIR}/Launcher/syncwidget.cpp +) + +set(HEADER_FILES + ${OPENSPACE_APPS_DIR}/Launcher/informationwidget.h + ${OPENSPACE_APPS_DIR}/Launcher/mainwindow.h + ${OPENSPACE_APPS_DIR}/Launcher/shortcutwidget.h + ${OPENSPACE_APPS_DIR}/Launcher/syncwidget.h +) + +find_package(Qt5Widgets) +find_package(Qt5Network) + +qt5_wrap_cpp(MOC_FILES ${HEADER_FILES}) +qt5_add_resources(RESOURCE_FILES ${OPENSPACE_APPS_DIR}/Launcher/files.qrc) + +add_executable(${APPLICATION_NAME} MACOSX_BUNDLE + ${SOURCE_FILES} + ${HEADER_FILES} + ${MOC_FILES} + ${RESOURCE_FILES} +) + +target_link_libraries(${APPLICATION_NAME} + Qt5::Widgets + Qt5::Network +) + +if (APPLE) +INSTALL(CODE " + include(BundleUtilities) + fixup_bundle(\"/Users/alex/Development/OpenSpace/bin/openspace/Debug/Launcher.app/Contents/MacOS/Launcher\" \"/Users/alex/Development/OpenSpace/bin/openspace/Debug/TimelineView.app/Contents/plugins/platforms/libqcocoa.dylib\" \"\") + " COMPONENT Runtime) +endif () diff --git a/apps/Launcher/files.qrc b/apps/Launcher/files.qrc new file mode 100644 index 0000000000..5661c6a39f --- /dev/null +++ b/apps/Launcher/files.qrc @@ -0,0 +1,5 @@ + + + images/header.png + + diff --git a/apps/Launcher/images/header.png b/apps/Launcher/images/header.png new file mode 100644 index 0000000000000000000000000000000000000000..21ced755c89b5a0a44e690bfb1850408da984cf8 GIT binary patch literal 13192 zcmeHtWmH^S(pwX5pvUC&e1gsCV=qaYC=K|w*G$jV5lK|#T?1NZTW2*5S_3&aQt z3SY=tTwFy~T%1hB+2M`#TMH;CtH6VF4;5_X)+2B3&#hhE%zTzQ&PwqdK|dM!wld?W zFeTxzza!Bh29eRrio#*T#!F&6M-YX>fz73Gp2^wjIBNbeRdIL3lv7suc&N^?+`cGq z<}0iY8xR`it)qh;2_@<%qw%gVkh`%xn3Bf}dJT$!42oX#=nw=dvaq*@d0d=JgC9Hc z)4RXYfjl@t`f|@sqtD@>*o(JSs$@n@!bx(i3`eU&>7Yyy!+%9YA>CEHf0>Rgrb{MC zdRyTO*7ksp;@{g`*nEJTQ3&jfdxI#3Bss<$rdPEkpSP<$98I6BfvMjWfxRrmRW`mX#5CO3@DT3YWOq4!E|dVZFy}Qd46cQSOmDFKaTeBzEw!hOFG!+qbomWvj@Gj)`jwe&^{`h zecR7TBcmzu-|FUpR;kRV@`Yr%EJEkru|f4HLq<+~3mI$B13GB4$1&LaojV)x$e?k@ zU@%RfGXr93y`0Eku>L4(fz`hM6u6`zlPLTH@6dkz-{<&yg?27`pQgN@?=jPA( zMD1f)uwf*{WyhT8(CC8(J`$8*v>>>Nd*nVFqvCwl{%+>u=olJ4(T9*Y6CzfzR4Rq% zMeqVIK7Zi*>SOw&8cqjcAG;h|3)%LATi0P1$(QV|WK+UwJKlhewMa*jCSHT*m9RX) z0^PoA7{}&d<~BGZPKEF+7Gy_7LFXJp6H4Jwyjkyw!w7ON!lP@%)7ltU~GWtZT~ zg;5DBNhqP$V6cJW3iFCukjfutv{J1jSa@*|9sSBOZ*XjQdvW{aInpg5LdcKqev@;y z=u~tC{vx=^cRPIwrZcSG8gTT`^f>e!^ekoIGMKUm4IzyU4F(Nw9sY83Efm`*l&&IE z`5NsSj~bS0V@F;h7A25n;8|bcx}1|;(_K?a(-GK95L6W`5+2i=)6KK??AYa+Wol|F zXexawdP;SwxP-7Yqy(?Da~$28$|};T%}ULR-iqI<$VzxjZ5(OLX1ryLW-NKksF=8T znMwr%?m0i4$1C=aL1T(wxw#_B3E9cCarnus0^2-q(W_(|EleC(+VQvurqF04>6Q81G@kvP0K=~s5jOx4)&P`1#T zfSN3)1KM-qQ(@PWF7P~=efFz(R%p|8UZy19%)O!SOeOodDdjMC+y zT9sOgS^_f*Gw?HK+}+&c+`$cX4bpamb{+?=2jTn42h;~K^CHeBZpR+m&JqF({4)af z0y}(JZhCz5{+wsq<)33+GS6V~v{J}YgthgwJ!bdpWq1S{X&Q45-yGH-E|N%*B$4Q6 z^Ji}f^0aZbkAN$@KY8MLR^61`VBfS|Xr3`$8eUBvGn_1*&z`VcL9k8m9feJJ#u%%~ zkIDL(OStf{_HmbpUy)+svl9(~2=MqpGz7_@2BJz#KW-m}bw>$bD(oGic^t{S)FZ1p z$Mdl56t-P99JbZhc4k$5S#-LGy(0b@J#*;P3b0+fI)?>L^z6{*x9Q=CI8qN!%+3r z_}=Tu^=OVIOXpD^p*^olu`q-0H!gE7<6Ft{igK+3*Mmwa4~$m~{%Ri$$MwD&rKvaT zZ)$RBR_OX(2=?8yory4C8-umXR3=p!boA9i$|6ej+pWBve#TD}_Il0Sw%tB_i2blR zn4A(qh(k!2F_(c<*;OfFtm`1WAUK}4IN_#wZ==XyW@|)f6=Cn~F?_J__U^~!mp!@J zsulDmRnG)4MOr%o{JNGni?V|xskpJ2NP|c|WX-+% z;2^L%a9E(j!w9Vfbp&l5ZTN-$iNXxu>~zA1A(!NF&AY zOKnO!2q*jdxG0#Gn;AH@*}n43vpgL$ha~n+WmzxpcymZ`#AFkBvh&$;-0km-7Y~lM zWw&P`r{A3m{<>N5*+u>tu>{}!>N~~V>x1}ywY}PI=T-UTl>Dl4qv6g=pY{`))8Wyi zNX^~pc&^I&-bQu@@7ar4+7-Tb{dvU2xy2zL<*Sa<;NuWNQZA8b`+0{7pYUtfQ@+;3VZuHs1DMP@Ev`{(&5A4cq7-Sgk?eG@zWB9`j? z?oOl-Ds#d_SQyI39x7lVP)$HVWtTo(glo|sAydt#i|zpwif8k_@Jr#EOX!9S>`&-2 zw5;gy+@O-%8a`eQLFi7=Wl=1tPjX$DZ?<>0DMJ^iFsjMgEm_w><@cxzcIJc92dLI7y*X%6QP32Hm`v@;FN;Q+4!PPnZfg*fKIlmW z^-lFJhNLKvq`Q+gztyrgGKsTDGj&_ypQIVROf;jEmfX`#Pd20%s&O`+_k{#G|F| zV|kTN(d&F9tzl=Q-5{|j7dzMG?-6b(Eqk{GgV1ElJ*y4#Wp2hw_IkO}?hN4)20}A@ z!fZ8c@3V1J$9>m@G9KzS=g*f$J-^`nLVh0MCT}*lYJAe!0Csu1Ik3B=U*@*y5lA?l zp-#$5%GJ0ovV47i^J(AxTxpbN&SR1^$(!`*!q1SG_j>f96G{HPe0L7TM66eTP6>%C zk`&Ho<>{#5F3=CB>*6UcPUt#kIQ$=MhVZ^ns%h^ihdJuli`eGb6Lg^lIc+KR*;|Q2 zcxmHZ;yxv`hSG*==ftEO(Lyr2P`+Y)1VYt#+avmOMRvT)@jk<({Eh>FWk<-FN6s6@Y4ub5DD%0%oHxvf2mk*AKmEK ziky`{x)K(7D7jCAeFuXRbRKLc?(@p*Ywg!TigXI^{NB8j*TQ{YHb9u~Fq0BcNnMms zm7Txx{n;zZ)GyJS2XQFY(4DUpp;ek!C~UGA)k&KmI;YGo&6uaJW{Tg9GeZZwVoV@ zwSE6k`eR31dF?Zda#Z&FktE?x4v2kVQ%S?mR)g!>tLa6a0-TEJvHIOcv%P186B#Q< zgCYv{KZJLDl_2a*D64EsLJtQwXg}KD5Ced+_3M;zo1BdsYLat5@emHulBPB>wDo#ZMD0}q6zJ2 ze-W{)?+Jymc9|RLgh?qbr#_ES>S)NxN)C9Y5uPJ-*<4H~f+;F`Ix{LO#%$`%swe`Z z&SGU!oil~ZyRTOY7j6$foD6=7y3-CF{)s#Msi8v5Zf90*F{5g=na#zeVnZ6QBjdC5 zX|h(8MUJKa)InrmyZ}nDp*UKI@zMh;%(5kz>{R6-Zn^}rQCCxYav9c_us694`a+BF z`B7O}sFkgeHO7m)eys`Yv$K1;#L#0~$|J4qtlc^@Gn2LovZJzVXg7Z7ud%o)-9Ee4 zUa*VEieLVAL?N<%Npk1{krM=?c9FV6h=-7%IIUnz5|9GELqSa^(uj_O$OW`))25+i>jOorHxO_79fDheur zX3B*ar%pw5Pkh9VR)Q)eL;e~38YyN*#i|a+H9*)AFER*p4ATakVM4esP8W*%`U9AZ zoeH^B3?UZ&C!DAFM-{a$y3>MyJo$A?t{}2(M6_% zk%!3j4%AJI5yl%xC&@{Ei;fjK8%j)t`#mtRXi0=P_DetKPhE4YQ*LL3W~JurP&(5@ z{(zphP-#;XN7gO8EY2p?ChS#sqzfPCj4T*>F3 z>N4xn>qzQAbr*G4);x%uaMnRwZ+q7+Y;^T2I1@R@d^)9hKAZ37#4im|p;6_dHKK8&J(XS*!xYn6lE1&DIrY3gmd zYoFfBpB3zN!u7lE(LskVIlcZ- zkU}UiQFStfTqzSZbj(DIb96>rBHSfhOzc{$0o+}T1@vF&!W5bGOU(CD?^JtKsKg2+ z&BGLwXQ=z)Gy0M9;8cmn@yBW8D#U4vXmM$07#kRsFnKUVX#5m?G=mho6)42>GG6)k z#G}R+Q-x4>%FD`m%4^Cf%Hv6c`V%ekY3KBcEhAlT2pZBiE-9co`BmnHZ5^ zo8Zkb%5+3#Ob1bcF3K%KFC5FOF6%Gc&ebX4EqkXip%Sj$Qh9Eqt1~a&EO%3i)E-_c z*Dw$}z@mvWZ8dFMc7ztDQd0G6EQlZ?y+SW;p^4vUSw=|RR|a-MWmaZld;GenG#6Aj zUwI=duf`zpNzzt4qx_(1x#U4hTBc9CSjAldY0_~r zqevvWgp|+kX9s9ysCz$i9@&M&h4k#r*(k0YZXe?Z#+u|`$<)cnT0UCPTC?RHy0$u& zdNK2?bG(ZNb@O#epA~C~8}b`I*L-TwFGM%Vd?0>S9$YV;^tCKM(JW}A8Y?QHUAl<AlgXC_z9 z;gi-w{ zm@2UBD~Prr)mPF|Vpi}=$xBd6tIhIy_v*b$y86Pf>nktO+vSa=KCGD0H!_w#Ez5Jp zgj8K#i%gP#ri@6%NxtCC$jr)WkIzp~Pe67yboM@*JH40ccPy3pT+)EAy07j7iQG=R=g_yT7;#UmPNYg=%$nrWHV$V( zd6`A8PRGDo}cRGGAU_ zeo(nlHdyZMKYqJjA>Wqb%Y;4*&4MTWl4CI(Uw7C9M`Ef9r+g3 zKHE;!p|n%Zo9lAd`IG5hJ$G`$c|(+~)~wee{KDCyM`f!KX*0Q7{yxqAo37!VXmR2c zertX+{*;y%tqU!>Et9Q0Ugch14kxPvo%PFm6W&$IwMPc4Vx7j29y@Be%_tBeX=-b} zTd{SlAFI>xaO{psAM32+<#BPVl1Ipq>=FZUU=&=`(8_Gv>UqF9+2)s9@x73T7zvUr zPghm~*4v*ozczlc71f;AplzUTc+eKm(9^Ods3r`m)2JIx-cJ@6`;S}?3wq{V6j&s? zal2d&s?QeZk0$0ArswC#r!9i*A@_%EL-#j$O?XZ$TDXjO_AKE{$xI)UDN>SC6jNG_ zkvbUt;Emury&f0VE~0n@9Zpy8bxsV^bf*pMbj=J04em~74i2_kPHzi6T#w>c+ZU^s z@s|?k82w|sNKgDuf=@!CV+2XhMEn*{7YHB)r#lmt-CI4QpNtthmTu{$cOi+1qwUUwrvwGah71O*4T z?*<1y5J0WTL%pDbc#yMeBr-BGL1*(f{AvN5aCz z%-Pz})!M`AZbYX>)1AqtA8h5qyRx1APX>;JA~@ABI%z(JO$CoF8ttStZ04NMh$ zlJcuqgDu|bNLbrh*t-C02=lV>3jQ_ze?0l`ihs=1{qIaRPVPTv{_*7ZOhJ|>2mWyA z?`Zuc1;QnaB*^lg&;yTzRC}PHKr*rtVj5uRgJsXRn%@??PM*E|w4b-uP4U_q13Rea zeTk}=8MZ2CkZPBR=r^t)DGad}1M~zG1W|F+mB-J5FtMg2uq9$8U&u>-Av4h_6lKy! zZWFvhi8J>8b$Pk8IW%;mG?6$mpM5j5^h5Y6yHH9@?2GJ2vH&O;#Q$7SpJ#5Wl)nrA zQ-(+dCu5F}_}@W@WNVpFVUaF|QeA%vMJc}Gi$X!eq5pH)0_J7zvBOCHDZ~Wz{!2M8 zttiEYK?(-zA66j7_Ol}VM-8B`iw_zTwAB(BfboxRKo}tLf$d+$k35g<=bg2Hi~Mr| zD)z`fOoBsqR0@#jZDyu4`LCUD=wxxGFa8q)z^4Eg7%KJ=HxM-Cp9_S&F#FRJxJ)dv zIH$@%MEpM&073=(XH@=wsGeTg|7)T0oTn&J&HqqmGcAXNjEp6sn87_f^KoL~=*QO( zi;dnWH4P1_3~syl)6>(s%f4R3ImXzO6xxHuM&&jd0oQG^i{0tZ>`UwG>piw=#wm*p zb_ofz%6cGLG4j7RoTw`dNZFh-aA&o%Q}AqvW1`yh>$V>5dcTE*1-s=4k>|t3>}G!) zCHZ9+4dUEOl}39*&&zqs&5pan1igBjc~Aj)S$sUE=h-mN=I2pi%8zXLs!@L(3sE9? z8*FTB^ZophWG4NACZ~;`>9&L(A#z;GJ5wzHM&rrmJX%PsYj zWFime32oQw5w$K`O2vy}$G`V@EGRINnjO#3HEt+NV7A%8R3Va9DKoY>$4{^0;ZlE+ zeu~nhU34^C$YOV@)OkkN&j7V}b>#eDQMl&AUJY^LOO`qDFKmB)$&BRHj}DQC&)|D7rj$z$VU zte?HWUo*taPrgNP97<&)*O`Y4+=h4EzfJBbDf2VQb3ujweOlLb;)woK)u9(nB4lF_0{=TcYmK5tEyIJOqdYWh-dBf7ag@>n z@znAXReUw|2F~B9+5PUpJ4?+jCZT%D6j(Cig@rIhHRV4l4O_XLI3k8Kc=QR{sVaAN z#`C4ue~|;K;NI11O}z27cE+o_$KdM=@F*Lm}qZ zILF$}%^U6hNVXOqU_G>pKU-t(f|);Z`=xLFTZH5my1Fd)RcgaWlHXCe$B+?-V{12E zX!mZszdsutk$E^970GM#K%p<(LA!rwXHT#4+!)|?$L ze9^n*a=}(Ir_fA)JmeWJ4o=w4u*n?wqM6EWu>nV@y%>1srXK1C2hpS=%>Iuz#c!9I zRL^NJP34zv8Ojs(7H{T88<*WP+(!2%W;c!fACka^yPy4PRrAta)a(X%R(&YHefw5g znXSW8#%*CkQlVw03j#dv4}uC)_@Hvs3Go{bL0ix}??twqDUbV}Ksz{7p)VftbdCns zz64`ESEv=3elIKey~t4@HtKEL7!Bs%%e7w-6H&?6`p6m!aABKLsGShIp5By&?sfnO%^F8FdMeuA8BZ3 z`4)Yy>|HHV%;-6j_%)itqHXA=$=~|fe$n-AumzzIBi1j!!~>=L+UyPmSKzFD7oLbCHvlyY|rWxw_5w7{>`_sXMir2&J56U{E zZ`1d~j%VNqDPlEl8G7vJd)T2#?>vF*F;TAjIkCVH*gH03rAY=hPhlzZ?e+Sv!AW$s z(`D3VebHQpt!{fWr`H=Xgei+nqRKJg-O}Q>FPIKe%XX{k(7I5|(VxN)=J|ND_37Nr zX{5AFmAT>jmSVHojfy*Sf6j-78Nh>M3^tnDj=2C zQvGtxy=5H&O0whg9LSlzh~3cWC?uyv1LD}+sb)vH7dAFJ4}u5KH4M#O!JB9JW$`Qz zQ={n!AF-L6|Kd{7+u{6auGhW*X(THLa)nMacH?5Zw}+l`uJQ@1W@Q6_iW@xGUZ@@_ zY}0#KZFY3u*T*b}-o0eL(oVhXbtAmx^A^e(*_*iJqm4{Iu#+q9>;_k(JVjL&8Z%A7 z24SB;d-f|FQHxsAOI)E&p_0_yi^;cya<#k)HQG3h-%H94YHOCT<>wkKD5bQotZSq4 zgo<u$c`jWGt!hMb z^{&MJsZZLg-suwB5O@GoJ1SrpEkkqzsN@Ajnr>E!k!94nJ?e}OkJfG#njhP0n#vp z+;3y&KU)ecQuw0Viirg-`Ccuv0|~Z=Be=zNXZ@`=;#0bO;+X6#q7(_9>X)GbI4}t` zitzy3(#Mc*5!-eX0ybdH3sPahWy2bw-4_I`^hn!Z52YgUe+<9W^y^kk7v4v z_Kg_Y*X!zhXA(;!ihwJ=yZhD7*vH`QgH$_4fQ=^YRkkWE)Z60WuOw+3y}bS4Ia_HM zIOtnR#Os(l{%yZ(`bC0T$dHsGIXiHiap1vW4EIG545L+}L46kn4>FUO4o zASlfbmy58o)8>rPVG3jjNq-}Kk4k{8`RlnIP7pL6+`z!V&&?EL+y`O}&bWVSN>Lwp z8T0kUt2!d+J$N~Jd4Ojz1v&w|Pq;c;?sxeW8Umaq!P_(~ina2t8nZqaGvK_sn+W}0 zgbW=GI3D10jtYtYmR?;9(9D-tWdbgfsqc~T1{kW^;xmHZ3x;RMONfyv!2qiyn11~r zxnkZ%0UT4^JN#=LZ?OL*VJDGlNkI zH8u6gVJNo#s9-t;_NON@E(-^wm*=Pv|0+k708kXTdtdUWkSq-F1d|B&H-rD{@C0N5 zk#)aBR{c{biV3(x#mo5WzaBwB3jm?o-^a`QQy54Ev*gG3w6>$>T7t50TyGSiHj@8J zo9F4{-H~=#G0gw&6k9DeRB+lY)J>#6u|*AO_}@E|Y%TAJQV{bwQ&lemScOvF*jx+b z;lLj|!Gqn&UKhLXmpStnH#awBEA;ANt~{@gf7ndt_+HKj?x|bH0OT>fw@D~YJGzH6 zCG}VKfOm<(#_8Va*hldsLf5k1WNvtbl-GsU%+8)8CpC&M-D&>{pDrH2CvP6ER$+su zl-~s_**!Oyj~`&DY@%SrR40Vhu2h*Xkj45wI$EWYaVOI1en8Jf>pT_#g#eCZYp%wu7NCV)7T<3#v%L@FfVycg9EYKxu8!03j*g-8dL3C8 zC`}T{CA{{{&D`gzO`X4@@bs5ye(G^eS1b6E!w&|^Eljos>&aexx!M!!rR_*~;SOf+ zi(P7C--{$55g4G72!f?-U9OZ5TC1sbb6WVfuIGxX9|qllEFfA0KcQrPdAOV*qj4Uk zsxr<)0n|?~^nOY#18;7#kJMj5Kuc(IV@BUW(OV>K}6t=YqIyNpGGU+$qD<3@7 zmfJB=k_oB2wCoEW{O%WvO}T2CrzCYzif*?*LPvylaPf>&Iv;O8mFqVu@w@F}&jD45 zi1A^|US*d>gZ3>iU=Ph=AI1rJfT$1LgmAq=sZ;R!do2g)fk-|eaGE4u-Mqkukb(az}@n?Q_U&dR-;1Rx#s!uI6Q7KH2UA+-3yY&RsxGl2} z9RPz#&#`E(#}ei2<~sd5x7{#rb8P81D;ihg(>N>#!mw!7bhF$TF~``-&E`Y$Tp~u; z_-C(m%I;b+xonf$fGlm~XSTcXwC{aWnT$`9+YT(Mf;yUWBzvu{8V z{bEmNhAk`1RxZyb&Is$T<%N@3tK=U{Oc&~`C#sA^=Ig8zv>Al~zZ<@P5|cIqI(M>x zbu5=vb2dVoAN?VZ&Vx*i{T3;PG{ge#3QIG*+b=h88l%=7e09=g5gr*xVklJ35f0111=teDGMIA`M#EW;@A-p5m;4yD1_Wp_6xaTjmon&7N>5dJoRG&UOU4Qt{pSbbwiZLv09Y8}&;0ef(eX5PEf^?w(As4P z|GQy5$0CsCdAN-^3NiP{YU{3!w-@lsZ?{LWCo-_14CF=iJ ziT<=3&CiMc)w1qJitT@wWG3(*0|Awx3V=E6Ua0@-rJDjI<^TFHu0f!RKcwL$&JXrI Q{lq0JsU%VP$|UH20s7Q2NdN!< literal 0 HcmV?d00001 diff --git a/apps/Launcher/informationwidget.cpp b/apps/Launcher/informationwidget.cpp new file mode 100644 index 0000000000..50769447ec --- /dev/null +++ b/apps/Launcher/informationwidget.cpp @@ -0,0 +1,29 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include "informationwidget.h" + +InformationWidget::InformationWidget(QWidget* parent) + : QTextEdit(parent) +{} diff --git a/apps/Launcher/informationwidget.h b/apps/Launcher/informationwidget.h new file mode 100644 index 0000000000..21565322ea --- /dev/null +++ b/apps/Launcher/informationwidget.h @@ -0,0 +1,36 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __INFORMATIONWIDGET_H__ +#define __INFORMATIONWIDGET_H__ + +#include + +class InformationWidget : public QTextEdit { +Q_OBJECT +public: + InformationWidget(QWidget* parent); +}; + +#endif // __INFORMATIONWIDGET_H__ diff --git a/apps/Launcher/main.cpp b/apps/Launcher/main.cpp new file mode 100644 index 0000000000..b4c17f6d04 --- /dev/null +++ b/apps/Launcher/main.cpp @@ -0,0 +1,155 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include "mainwindow.h" + +//static const QString style = R"style( +//QWidget { +// background-color: rgb(80, 80, 80); +// font-family: Helvetica; +//} +// +//QGroupBox { +// background-color: qlineargradient( +// x1: 0, y1: 0, x2: 0, y2: 1, +// stop: 0 #858585, +// stop: 1 #959595); +// border: 2px solid gray; +// border-radius: 5px; +// margin-top: 4ex; +// font-size: bold 12px; +//} +// +//QGroupBox::title { +// background-color: #E0E0E0; +// border: 2px solid gray; +// border-radius: 5px; +// subcontrol-origin: margin; +// subcontrol-position: top center; +// padding: 0 10px; +//} +// +//QLineEdit { +// color: lightgray; +//} +// +//QSlider::groove:horizontal { +// border: 1px solid #999999; +// height: 8px; /* the groove expands to the size of the slider by default. by giving it a height, it has a fixed size */ +// background: qlineargradient( +// x1:0, y1:0, x2:1, y2:0, +// stop:0 #c4c4c4, +// stop:0.5 #555555, +// stop:1 #c4c4c4 +// ); +// margin: 2px 0; +//} +// +//QSlider::handle:horizontal { +// background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #b4b4b4, stop:1 #8f8f8f); +// border: 1px solid #5c5c5c; +// width: 18px; +// margin: -2px 0; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ +// border-radius: 3px; +//} +// +//QPushButton { +// background-color: lightgray; +// border-style: outset; +// border-width: 0.5px; +// border-radius: 5px; +// border-color: black; +// font: bold 12px; +// min-width: 10em; +//} +// +//QPushButton#connection { +// background-color: lightgreen; +//} +// +//QPushButton#connection:pressed { +// background-color: green; +//} +// +// +//QPushButton#pause, QPushButton#play { +// padding: 5px; +//} +// +//QPushButton#pause:pressed, QPushButton#play:pressed, QPushButton:pressed { +// background-color: darkgray; +// border-style: inset; +//} +// +//QCombobox { +// border: 1px solid gray; +// border-radius: 3px; +// padding: 1px 18px 1px 3px; +// min-width: 6em; +//} +// +//QComboBox:editable { +// background: lightgrey; +//} +// +//QComboBox QAbstractItemView { +// border: 2px solid darkgray; +// border-radius: 5px; +// background-color: #a8a8a8; +// selection-background-color: #a8a8a8; +//} +// +//QLabel#label { +// font-size: 13px; +// background-color: transparent; +// font-variant: small-caps; +//} +// +//QLabel#value { +// font-family: monospace; +// font-weight: bold; +// font-size: 14px; +// background-color: transparent; +//} +// +//QWidget#background { +// background-color: transparent; +//} +// +//QTextEdit { +// font-family: monospace; +//} +//)style"; + +int main(int argc, char** argv) { + QApplication app(argc, argv); + +// app.setStyleSheet(style); + MainWindow window; + window.show(); + + return app.exec(); +} diff --git a/apps/Launcher/mainwindow.cpp b/apps/Launcher/mainwindow.cpp new file mode 100644 index 0000000000..70b8624581 --- /dev/null +++ b/apps/Launcher/mainwindow.cpp @@ -0,0 +1,377 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include "mainwindow.h" + +#include "informationwidget.h" + +#include +#include +#include +#include +#include +#include + +namespace { + const QSize WindowSize = QSize(640, 480); +} + +MainWindow::MainWindow() + : QWidget(nullptr) + , _informationWidget(nullptr) +{ + setFixedSize(WindowSize); + + QGridLayout* layout = new QGridLayout; + + QLabel* image = new QLabel; + QPixmap p = QPixmap(":/images/header.png"); + image->setPixmap(p.scaledToWidth(WindowSize.width())); + layout->addWidget(image, 0, 0, 1, 2); + + _informationWidget = new InformationWidget(this); + layout->addWidget(_informationWidget, 1, 0, 2, 1); + + QWidget* container = new QWidget; + { + QGridLayout* layout = new QGridLayout; + + QLabel* shortcutLabel = new QLabel("Keyboard Shortcuts:"); + layout->addWidget(shortcutLabel, 0, 0); + QPushButton* shortcutButton = new QPushButton("Open..."); + QObject::connect(shortcutButton, SIGNAL(clicked(bool)), + this, SLOT(shortcutButtonPressed()) + ); + layout->addWidget(shortcutButton, 0, 1); + + QLabel* sceneSelectionLabel = new QLabel("Scenes:"); + layout->addWidget(sceneSelectionLabel, 1, 0); + QComboBox* sceneSelection = new QComboBox; + layout->addWidget(sceneSelection); + + container->setLayout(layout); + } + layout->addWidget(container, 1, 1); + + container = new QWidget; + { + QBoxLayout* layout = new QHBoxLayout; + + QPushButton* cancelButton = new QPushButton("Cancel"); + layout->addWidget(cancelButton); + + QPushButton* syncButton = new QPushButton("Sync"); + layout->addWidget(syncButton); + + QPushButton* startButton = new QPushButton("Start"); + layout->addWidget(startButton); + + container->setLayout(layout); + } + layout->addWidget(container, 2, 1); + + setLayout(layout); +} + +MainWindow::~MainWindow() { + delete _informationWidget; +} + +void MainWindow::shortcutButtonPressed() { + +} + +//MainWindow::MainWindow() +// : QWidget(nullptr) +// , _configurationWidget(nullptr) +// , _timeControlWidget(nullptr) +// , _informationWidget(nullptr) +// , _timelineWidget(nullptr) +// , _socket(nullptr) +//{ +// setWindowTitle("OpenSpace Timeline"); +// +// _configurationWidget = new ConfigurationWidget(this); +// _configurationWidget->setMinimumWidth(350); +// _timeControlWidget = new ControlWidget(this); +// _timeControlWidget->setMinimumWidth(350); +// _informationWidget = new InformationWidget(this); +// _informationWidget->setMinimumWidth(350); +// _timelineWidget = new TimelineWidget(this); +// +// QGridLayout* layout = new QGridLayout; +// layout->addWidget(_configurationWidget, 0, 0); +// layout->addWidget(_timeControlWidget, 1, 0); +// layout->addWidget(_informationWidget, 2, 0); +// layout->addWidget(_timelineWidget, 0, 1, 3, 1); +// +// layout->setColumnStretch(1, 5); +// +// +// QObject::connect( +// _configurationWidget, SIGNAL(connect(QString, QString)), +// this, SLOT(onConnect(QString, QString)) +// ); +// QObject::connect( +// _configurationWidget, SIGNAL(disconnect()), +// this, SLOT(onDisconnect()) +// ); +// +// QObject::connect( +// _timeControlWidget, SIGNAL(scriptActivity(QString)), +// this, SLOT(sendScript(QString)) +// ); +// +// setLayout(layout); +// +// _configurationWidget->socketDisconnected(); +// _timeControlWidget->socketDisconnected(); +// _informationWidget->socketDisconnected(); +// _timelineWidget->socketDisconnected(); +//} +// +//MainWindow::~MainWindow() { +// delete _socket; +//} +// +//void MainWindow::onConnect(QString host, QString port) { +// delete _socket; +// +// _socket = new QTcpSocket(this); +// QObject::connect(_socket, SIGNAL(readyRead()), SLOT(readTcpData())); +// QObject::connect(_socket, SIGNAL(connected()), SLOT(onSocketConnected())); +// QObject::connect(_socket, SIGNAL(disconnected()), SLOT(onSocketDisconnected())); +// +// _socket->connectToHost(host, port.toUInt()); +//} +// +//void MainWindow::onDisconnect() { +// delete _socket; +// _socket = nullptr; +//} +// +//void MainWindow::readTcpData() { +// static const uint16_t MessageTypeStatus = 0; +// static const uint16_t MessageTypePlayBookHongKang = 2; +// static const uint16_t MessageTypePlayBookLabel = 3; +// +// QByteArray data = _socket->readAll(); +// +// if (QString(data) == "Connected to SGCT!\r\n") +// return; +// if (QString(data) == "OK\r\n") +// return; +// +// QByteArray messageTypeData = data.left(2); +// union { +// uint16_t value; +// std::array data; +// } messageType; +// std::memcpy(messageType.data.data(), messageTypeData.data(), sizeof(uint16_t)); +// +// switch (messageType.value) { +// case MessageTypeStatus: +// break; +// case MessageTypePlayBookHongKang: +// qDebug() << "Hong Kang Playbook received"; +// break; +// case MessageTypePlayBookLabel: +// qDebug() << "Label Playbook received"; +// break; +// default: +// qDebug() << "Unknown message of type '" << messageType.value << "'"; +// } +// +// switch (messageType.value) { +// case MessageTypeStatus: +// { +// if (_hasHongKangTimeline && _hasLabelTimeline) +// handleStatusMessage(data.mid(2)); +// break; +// } +// case MessageTypePlayBookHongKang: +// case MessageTypePlayBookLabel: +// { +// const char* payloadDebug = data.mid(2).data(); +// +// size_t beginning = 0; +// uint32_t size = readFromBuffer(data.mid(2).data(), beginning); +// +// //qDebug() << "Begin reading data"; +// while (_socket->waitForReadyRead() && data.size() < int(size)) { +// //qDebug() << "."; +// data = data.append(_socket->readAll()); +// //data = data.append(_socket->read(int(size) - data.size())); +// QThread::msleep(50); +// } +// //qDebug() << "Finished reading data. Handling playbook"; +// +// handlePlaybook(data.mid(2)); +// +// //qDebug() << "Finished handling playbook"; +// +// if (messageType.value == MessageTypePlayBookHongKang) +// _hasHongKangTimeline = true; +// if (messageType.value == MessageTypePlayBookLabel) +// _hasLabelTimeline = true; +// +// if (_hasHongKangTimeline && _hasLabelTimeline) { +// fullyConnected(); +// } +// +// break; +// } +// default: +// qDebug() << QString(data); +// } +// +//} +// +//void MainWindow::handleStatusMessage(QByteArray data) { +// const char* buffer = data.data(); +// +// union { +// double value; +// std::array buffer; +// } et; +// std::memmove(et.buffer.data(), buffer, sizeof(double)); +// +// std::vector timeString(24); +// std::memmove(timeString.data(), buffer + sizeof(double), 24); +// +// union { +// double value; +// std::array buffer; +// } delta; +// std::memmove(delta.buffer.data(), buffer + sizeof(double) + 24, sizeof(double)); +// +// _timeControlWidget->update( +// QString::fromStdString(std::string(timeString.begin(), timeString.end())), +// QString::number(delta.value) +// ); +// _timelineWidget->setCurrentTime(std::string(timeString.begin(), timeString.end()), et.value); +//} +// +//std::vector instrumentsFromId(uint16_t instrumentId, std::map instrumentMap) { +// std::vector results; +// for (int i = 0; i < 16; ++i) { +// uint16_t testValue = 1 << i; +// if ((testValue & instrumentId) != 0) { +// std::string t = instrumentMap.at(testValue); +// if (t.empty()) +// qDebug() << "Empty instrument"; +// results.push_back(t); +// } +// } +// return results; +//} +// +//void MainWindow::handlePlaybook(QByteArray data) { +// char* buffer = data.data(); +// size_t currentReadLocation = 0; +// +// uint32_t totalData = readFromBuffer(buffer, currentReadLocation); +// +// uint8_t nTargets = readFromBuffer(buffer, currentReadLocation); +// qDebug() << "Targets: " << nTargets; +// std::map targetMap; +// for (uint8_t i = 0; i < nTargets; ++i) { +// uint8_t id = readFromBuffer(buffer, currentReadLocation); +// std::string value = readFromBuffer(buffer, currentReadLocation); +// qDebug() << QString::fromStdString(value); +// targetMap[id] = value; +// } +// +// uint8_t nInstruments = readFromBuffer(buffer, currentReadLocation); +// qDebug() << "Instruments: " << nInstruments; +// std::map instrumentMap; +// for (uint8_t i = 0; i < nInstruments; ++i) { +// uint16_t id = readFromBuffer(buffer, currentReadLocation); +// std::string value = readFromBuffer(buffer, currentReadLocation); +// qDebug() << QString::fromStdString(value); +// instrumentMap[id] = value; +// } +// +// uint32_t nImages = readFromBuffer(buffer, currentReadLocation); +// std::vector images; +// for (uint32_t i = 0; i < nImages; ++i) { +// Image image; +// image.beginning = readFromBuffer(buffer, currentReadLocation); +// image.ending = readFromBuffer(buffer, currentReadLocation); +// +// image.beginningString = readFromBuffer(buffer, currentReadLocation); +// image.endingString = readFromBuffer(buffer, currentReadLocation); +// +// uint8_t targetId = readFromBuffer(buffer, currentReadLocation); +// uint16_t instrumentId = readFromBuffer(buffer, currentReadLocation); +// image.target = targetMap[targetId]; +// image.instruments = instrumentsFromId(instrumentId, instrumentMap); +// if (image.instruments.empty()) +// qDebug() << "Instruments were empty"; +// images.push_back(image); +// } +// +// _timelineWidget->setData(std::move(images), std::move(targetMap), std::move(instrumentMap)); +// +//} +// +//void MainWindow::sendScript(QString script) { +// if (_socket) { +// _socket->write(("0" + script + "\r\n").toLatin1()); +// //QByteArray data = (QString("0") + script).toLocal8Bit(); +// //qDebug() << data; +// //_socket->write(data); +// //QThread::msleep(25); +// } +// //_socket->write(("0" + script + "\r\n").toLatin1()); +// //_socket->write(("0" + script + "\0").toLatin1()); +//} +// +//void MainWindow::onSocketConnected() { +// _socket->write(QString("1\r\n").toLatin1()); +// //_socket->write(QString("1").toLatin1()); +// +//} +// +//void MainWindow::onSocketDisconnected() { +// _configurationWidget->socketDisconnected(); +// _timeControlWidget->socketDisconnected(); +// _informationWidget->socketDisconnected(); +// _timelineWidget->socketDisconnected(); +// +// _informationWidget->logInformation("Disconnected."); +//} +// +//std::string MainWindow::nextTarget() const { +// return _timelineWidget->nextTarget(); +//} +// +//void MainWindow::fullyConnected() { +// _informationWidget->logInformation("Connected to " + _socket->peerName() + " on port " + QString::number(_socket->peerPort()) + "."); +// +// _configurationWidget->socketConnected(); +// _timeControlWidget->socketConnected(); +// _informationWidget->socketConnected(); +// _timelineWidget->socketConnected(); +//} diff --git a/apps/Launcher/mainwindow.h b/apps/Launcher/mainwindow.h new file mode 100644 index 0000000000..89d2a21ce8 --- /dev/null +++ b/apps/Launcher/mainwindow.h @@ -0,0 +1,82 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __MAINWINDOW_H__ +#define __MAINWINDOW_H__ + +#include +#include + +class InformationWidget; + +class MainWindow : public QWidget { +Q_OBJECT +public: + MainWindow(); + ~MainWindow(); + +private slots: + void shortcutButtonPressed(); + +private: + InformationWidget* _informationWidget; +}; + +//class MainWindow : public QWidget { +//Q_OBJECT +//public: +// MainWindow(); +// ~MainWindow(); +// +// std::string nextTarget() const; +// +//public slots: +// void sendScript(QString script); +// +//private slots: +// void onConnect(QString host, QString port); +// void onDisconnect(); +// +// void onSocketConnected(); +// void onSocketDisconnected(); +// +// void readTcpData(); +// void handleStatusMessage(QByteArray data); +// void handlePlaybook(QByteArray data); +// +// void fullyConnected(); +// +//private: +// ConfigurationWidget* _configurationWidget; +// ControlWidget* _timeControlWidget; +// InformationWidget* _informationWidget; +// TimelineWidget* _timelineWidget; +// +// QTcpSocket* _socket; +// +// bool _hasHongKangTimeline = false; +// bool _hasLabelTimeline = false; +//}; + +#endif // __MAINWINDOW_H__ diff --git a/apps/Launcher/shortcutwidget.cpp b/apps/Launcher/shortcutwidget.cpp new file mode 100644 index 0000000000..899b60c82e --- /dev/null +++ b/apps/Launcher/shortcutwidget.cpp @@ -0,0 +1,29 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include "shortcutwidget.h" + +ShortcutWidget::ShortcutWidget(QWidget* parent) + : QWidget(parent) +{} diff --git a/apps/Launcher/shortcutwidget.h b/apps/Launcher/shortcutwidget.h new file mode 100644 index 0000000000..5a06a0d388 --- /dev/null +++ b/apps/Launcher/shortcutwidget.h @@ -0,0 +1,36 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __SHORTCUTWIDGET_H__ +#define __SHORTCUTWIDGET_H__ + +#include + +class ShortcutWidget : public QWidget { +Q_OBJECT +public: + ShortcutWidget(QWidget* parent); +}; + +#endif // __SHORTCUTWIDGET_H__ diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp new file mode 100644 index 0000000000..d7414b95da --- /dev/null +++ b/apps/Launcher/syncwidget.cpp @@ -0,0 +1,25 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include "syncwidget.h" diff --git a/apps/Launcher/syncwidget.h b/apps/Launcher/syncwidget.h new file mode 100644 index 0000000000..a5744859da --- /dev/null +++ b/apps/Launcher/syncwidget.h @@ -0,0 +1,34 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __SYNCWIDGET_H__ +#define __SYNCWIDGET_H__ + +#include + +class SyncWidget : public QWidget { + +}; + +#endif // __SYNCWIDGET_H__ From 4c415ef50257b4d59a1ec9f11a271aeb6b1d8337 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 8 Jun 2015 01:55:38 +0200 Subject: [PATCH 112/329] Some more work restructuring the launcher --- apps/Launcher/CMakeLists.txt | 2 -- apps/Launcher/informationwidget.cpp | 29 -------------------- apps/Launcher/informationwidget.h | 36 ------------------------- apps/Launcher/mainwindow.cpp | 42 ++++++++++++++++++++++++++--- apps/Launcher/mainwindow.h | 17 +++++++++--- 5 files changed, 53 insertions(+), 73 deletions(-) delete mode 100644 apps/Launcher/informationwidget.cpp delete mode 100644 apps/Launcher/informationwidget.h diff --git a/apps/Launcher/CMakeLists.txt b/apps/Launcher/CMakeLists.txt index f0c902e10d..2cf98ae128 100644 --- a/apps/Launcher/CMakeLists.txt +++ b/apps/Launcher/CMakeLists.txt @@ -27,14 +27,12 @@ set(APPLICATION_LINK_TO_OPENSPACE ON) set(SOURCE_FILES ${OPENSPACE_APPS_DIR}/Launcher/main.cpp - ${OPENSPACE_APPS_DIR}/Launcher/informationwidget.cpp ${OPENSPACE_APPS_DIR}/Launcher/mainwindow.cpp ${OPENSPACE_APPS_DIR}/Launcher/shortcutwidget.cpp ${OPENSPACE_APPS_DIR}/Launcher/syncwidget.cpp ) set(HEADER_FILES - ${OPENSPACE_APPS_DIR}/Launcher/informationwidget.h ${OPENSPACE_APPS_DIR}/Launcher/mainwindow.h ${OPENSPACE_APPS_DIR}/Launcher/shortcutwidget.h ${OPENSPACE_APPS_DIR}/Launcher/syncwidget.h diff --git a/apps/Launcher/informationwidget.cpp b/apps/Launcher/informationwidget.cpp deleted file mode 100644 index 50769447ec..0000000000 --- a/apps/Launcher/informationwidget.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2015 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#include "informationwidget.h" - -InformationWidget::InformationWidget(QWidget* parent) - : QTextEdit(parent) -{} diff --git a/apps/Launcher/informationwidget.h b/apps/Launcher/informationwidget.h deleted file mode 100644 index 21565322ea..0000000000 --- a/apps/Launcher/informationwidget.h +++ /dev/null @@ -1,36 +0,0 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2015 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#ifndef __INFORMATIONWIDGET_H__ -#define __INFORMATIONWIDGET_H__ - -#include - -class InformationWidget : public QTextEdit { -Q_OBJECT -public: - InformationWidget(QWidget* parent); -}; - -#endif // __INFORMATIONWIDGET_H__ diff --git a/apps/Launcher/mainwindow.cpp b/apps/Launcher/mainwindow.cpp index 70b8624581..70c07e54b5 100644 --- a/apps/Launcher/mainwindow.cpp +++ b/apps/Launcher/mainwindow.cpp @@ -24,22 +24,25 @@ #include "mainwindow.h" -#include "informationwidget.h" - #include #include #include #include +#include #include #include namespace { const QSize WindowSize = QSize(640, 480); + + const QString NewsURL = "http://openspace.itn.liu.se/news.txt"; } MainWindow::MainWindow() : QWidget(nullptr) + , _newsReply(nullptr) , _informationWidget(nullptr) + , _networkManager(new QNetworkAccessManager) { setFixedSize(WindowSize); @@ -50,7 +53,9 @@ MainWindow::MainWindow() image->setPixmap(p.scaledToWidth(WindowSize.width())); layout->addWidget(image, 0, 0, 1, 2); - _informationWidget = new InformationWidget(this); + + _informationWidget = new QTextEdit(this); + _informationWidget->setReadOnly(true); layout->addWidget(_informationWidget, 1, 0, 2, 1); QWidget* container = new QWidget; @@ -92,16 +97,47 @@ MainWindow::MainWindow() layout->addWidget(container, 2, 1); setLayout(layout); + + initialize(); } MainWindow::~MainWindow() { delete _informationWidget; + delete _networkManager; +} + +void MainWindow::initialize() { + // Get the news information + QNetworkRequest request; + request.setUrl(QUrl(NewsURL)); + + _newsReply = _networkManager->get(request); + connect(_newsReply, SIGNAL(finished()), + this, SLOT(newsReadyRead()) + ); + connect(_newsReply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(newsNetworkError()) + ); } void MainWindow::shortcutButtonPressed() { } +void MainWindow::newsNetworkError() { + QString error = _newsReply->errorString(); + _informationWidget->setText(error); + _newsReply->deleteLater(); +} + +void MainWindow::newsReadyRead() { + QByteArray data = _newsReply->readAll(); + QString news = QString::fromLatin1(data); + _informationWidget->setText(news); + _newsReply->deleteLater(); +} + + //MainWindow::MainWindow() // : QWidget(nullptr) // , _configurationWidget(nullptr) diff --git a/apps/Launcher/mainwindow.h b/apps/Launcher/mainwindow.h index 89d2a21ce8..e30f0815a7 100644 --- a/apps/Launcher/mainwindow.h +++ b/apps/Launcher/mainwindow.h @@ -26,9 +26,11 @@ #define __MAINWINDOW_H__ #include -#include -class InformationWidget; +#include +#include + +class QNetworkAccessManager; class MainWindow : public QWidget { Q_OBJECT @@ -38,9 +40,18 @@ public: private slots: void shortcutButtonPressed(); + + void newsNetworkError(); + void newsReadyRead(); private: - InformationWidget* _informationWidget; + void initialize(); + + QNetworkReply* _newsReply; + + QTextEdit* _informationWidget; + + QNetworkAccessManager* _networkManager; }; //class MainWindow : public QWidget { From 511f103430e953e1e67ec0ae29dc671dd6a9303d Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 10 Jun 2015 02:04:41 +0200 Subject: [PATCH 113/329] Make APPLICATION_LINK_TO_OPENSPACE actually include the libOpenSpace and Ghoul as well --- support/cmake/support_macros.cmake | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 6b703962a2..bf3a1d45c0 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -192,6 +192,29 @@ function (handle_applications) unset(APPLICATION_LINK_TO_OPENSPACE) include(${OPENSPACE_APPS_DIR}/${app}/CMakeLists.txt) set_compile_settings(${APPLICATION_NAME}) + + if (APPLICATION_LINK_TO_OPENSPACE) + get_property( + OPENSPACE_INCLUDE_DIR + TARGET libOpenSpace + PROPERTY INTERFACE_INCLUDE_DIRECTORIES + ) + target_include_directories(${APPLICATION_NAME} PUBLIC + "${OPENSPACE_BASE_DIR}" + ${OPENSPACE_INCLUDE_DIR} + ) + + get_property( + OPENSPACE_DEFINES + TARGET libOpenSpace + PROPERTY INTERFACE_COMPILE_DEFINITIONS + ) + target_compile_definitions(${APPLICATION_NAME} PUBLIC ${OPENSPACE_DEFINES}) + + target_link_libraries(${APPLICATION_NAME} Ghoul) + target_link_libraries(${APPLICATION_NAME} libOpenSpace) + endif () + list(APPEND applications ${APPLICATION_NAME}) list(APPEND applications_link_to_openspace ${APPLICATION_LINK_TO_OPENSPACE}) unset(APPLICATION_NAME) @@ -362,7 +385,7 @@ function (handle_internal_modules) list(GET OPENSPACE_APPLICATIONS ${val} val1) list(GET OPENSPACE_APPLICATIONS_LINK_REQUEST ${val} val2) if (${val2}) - target_link_libraries(${app} ${libraryName}) + target_link_libraries(${app} ${libraryName}) endif () endforeach() From fab3b3e177fc53daab6536e05ef717b4489a3ede Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 10 Jun 2015 02:05:20 +0200 Subject: [PATCH 114/329] Move FileSystem initialization into ghoul::initialize --- ext/ghoul | 2 +- src/engine/openspaceengine.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index 03983ed78e..eb66778e0a 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 03983ed78edab3e068c2c3a1f4457a9a163f3161 +Subproject commit eb66778e0aaf1809fff1a1476872594a24ac0ac3 diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index a1547d463a..a95e058501 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -157,7 +157,6 @@ bool OpenSpaceEngine::create( LogMgr.addLog(new ConsoleLog); LDEBUG("Initialize FileSystem"); - ghoul::filesystem::FileSystem::initialize(); #ifdef __APPLE__ ghoul::filesystem::File app(argv[0]); From 726a8e84efe30eac4b4d120e19d35b22bc037f56 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 10 Jun 2015 02:05:52 +0200 Subject: [PATCH 115/329] Adding parsing of scene files to Launcher application --- apps/Launcher/mainwindow.cpp | 66 ++++++++++++++++++++----- apps/Launcher/mainwindow.h | 17 ++++++- apps/Launcher/syncwidget.cpp | 95 ++++++++++++++++++++++++++++++++++++ apps/Launcher/syncwidget.h | 8 +++ 4 files changed, 174 insertions(+), 12 deletions(-) diff --git a/apps/Launcher/mainwindow.cpp b/apps/Launcher/mainwindow.cpp index 70c07e54b5..138a083a13 100644 --- a/apps/Launcher/mainwindow.cpp +++ b/apps/Launcher/mainwindow.cpp @@ -24,11 +24,13 @@ #include "mainwindow.h" +#include #include +#include #include #include #include -#include +#include #include #include @@ -36,13 +38,23 @@ namespace { const QSize WindowSize = QSize(640, 480); const QString NewsURL = "http://openspace.itn.liu.se/news.txt"; + + const QString ModulesDirectory = "../data/scene"; // temporary ---abock + +#ifdef WIN32 + const QString OpenSpaceExecutable = "OpenSpace.exe"; +#else + const QString OpenSpaceExecutable = "OpenSpace"; +#endif } MainWindow::MainWindow() : QWidget(nullptr) , _newsReply(nullptr) , _informationWidget(nullptr) - , _networkManager(new QNetworkAccessManager) + , _scenes(nullptr) + , _shortcutWidget(nullptr) + , _syncWidget(nullptr) { setFixedSize(WindowSize); @@ -53,7 +65,6 @@ MainWindow::MainWindow() image->setPixmap(p.scaledToWidth(WindowSize.width())); layout->addWidget(image, 0, 0, 1, 2); - _informationWidget = new QTextEdit(this); _informationWidget->setReadOnly(true); layout->addWidget(_informationWidget, 1, 0, 2, 1); @@ -72,8 +83,8 @@ MainWindow::MainWindow() QLabel* sceneSelectionLabel = new QLabel("Scenes:"); layout->addWidget(sceneSelectionLabel, 1, 0); - QComboBox* sceneSelection = new QComboBox; - layout->addWidget(sceneSelection); + _scenes = new QComboBox; + layout->addWidget(_scenes); container->setLayout(layout); } @@ -84,12 +95,24 @@ MainWindow::MainWindow() QBoxLayout* layout = new QHBoxLayout; QPushButton* cancelButton = new QPushButton("Cancel"); + QObject::connect( + cancelButton, SIGNAL(clicked(bool)), + QApplication::instance(), SLOT(quit()) + ); layout->addWidget(cancelButton); QPushButton* syncButton = new QPushButton("Sync"); + QObject::connect( + syncButton, SIGNAL(clicked(bool)), + this, SLOT(syncButtonPressed()) + ); layout->addWidget(syncButton); QPushButton* startButton = new QPushButton("Start"); + QObject::connect( + startButton, SIGNAL(clicked(bool)), + this, SLOT(startButtonPressed()) + ); layout->addWidget(startButton); container->setLayout(layout); @@ -103,7 +126,6 @@ MainWindow::MainWindow() MainWindow::~MainWindow() { delete _informationWidget; - delete _networkManager; } void MainWindow::initialize() { @@ -111,17 +133,40 @@ void MainWindow::initialize() { QNetworkRequest request; request.setUrl(QUrl(NewsURL)); - _newsReply = _networkManager->get(request); - connect(_newsReply, SIGNAL(finished()), + _newsReply = _networkManager.get(request); + QObject::connect(_newsReply, SIGNAL(finished()), this, SLOT(newsReadyRead()) ); - connect(_newsReply, SIGNAL(error(QNetworkReply::NetworkError)), + QObject::connect(_newsReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(newsNetworkError()) ); + + _shortcutWidget.hide(); + _syncWidget.hide(); + + QDir d(ModulesDirectory); + d.setFilter(QDir::Files); + + QFileInfoList list = d.entryInfoList(); + for (const QFileInfo& i : list) { + _sceneFiles.insert(i.fileName(), i.absoluteFilePath()); + _scenes->addItem(i.fileName()); + } } void MainWindow::shortcutButtonPressed() { - + _shortcutWidget.show(); +} + +void MainWindow::syncButtonPressed() { + QString currentScene = _scenes->currentText(); + _syncWidget.setSceneFile(_sceneFiles[currentScene]); + _syncWidget.show(); +} + +void MainWindow::startButtonPressed() { + QProcess* p = new QProcess(this); + p->start(OpenSpaceExecutable); } void MainWindow::newsNetworkError() { @@ -137,7 +182,6 @@ void MainWindow::newsReadyRead() { _newsReply->deleteLater(); } - //MainWindow::MainWindow() // : QWidget(nullptr) // , _configurationWidget(nullptr) diff --git a/apps/Launcher/mainwindow.h b/apps/Launcher/mainwindow.h index e30f0815a7..109bc4b0b4 100644 --- a/apps/Launcher/mainwindow.h +++ b/apps/Launcher/mainwindow.h @@ -27,9 +27,15 @@ #include +#include "shortcutwidget.h" +#include "syncwidget.h" + +#include +#include #include #include +class QComboBox; class QNetworkAccessManager; class MainWindow : public QWidget { @@ -40,6 +46,8 @@ public: private slots: void shortcutButtonPressed(); + void syncButtonPressed(); + void startButtonPressed(); void newsNetworkError(); void newsReadyRead(); @@ -50,8 +58,15 @@ private: QNetworkReply* _newsReply; QTextEdit* _informationWidget; + + QComboBox* _scenes; + QMap _sceneFiles; + + ShortcutWidget _shortcutWidget; + SyncWidget _syncWidget; + - QNetworkAccessManager* _networkManager; + QNetworkAccessManager _networkManager; }; //class MainWindow : public QWidget { diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index d7414b95da..30babbe899 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -23,3 +23,98 @@ ****************************************************************************************/ #include "syncwidget.h" + +#include +#include +#include + +#include +#include +#include + +namespace { +} + +SyncWidget::SyncWidget(QWidget* parent) + : QWidget(parent) +{ + setFixedSize(500, 500); + + ghoul::initialize(); + +} + +void SyncWidget::setSceneFile(QString scene) { + clear(); + + qDebug() << scene; + + ghoul::Dictionary sceneDictionary; + ghoul::lua::loadDictionaryFromFile( + scene.toStdString(), + sceneDictionary + ); + + ghoul::Dictionary modules; + bool success = sceneDictionary.getValue("Modules", modules); + qDebug() << success; + + QStringList modulesList; + for (int i = 1; i < modules.size(); ++i) { + std::string module = modules.value(std::to_string(i)); + modulesList.append(QString::fromStdString(module)); + } + qDebug() << modulesList; + + QDir sceneDir(scene); + sceneDir.cdUp(); + for (QString module : modulesList) { + QString moduleFile = sceneDir.absoluteFilePath(module + "/" + module + ".mod"); + QString dataFile = sceneDir.absoluteFilePath(module + "/" + module + ".data"); + + qDebug() << module; + qDebug() << moduleFile << QFileInfo(moduleFile).exists(); + qDebug() << dataFile << QFileInfo(dataFile).exists(); + + if (QFileInfo(dataFile).exists()) { + ghoul::Dictionary dataDictionary; + ghoul::lua::loadDictionaryFromFile(dataFile.toStdString(), dataDictionary); + + ghoul::Dictionary directFiles; + ghoul::Dictionary torrentFiles; + + bool found = dataDictionary.getValue("Files", directFiles); + if (found) { + QStringList files; + for (int i = 1; i < directFiles.size(); ++i) { + std::string f = directFiles.value(std::to_string(i)); + files.append(QString::fromStdString(f)); + } + handleDirectFiles(module, files); + } + + found = dataDictionary.getValue("Torrents", torrentFiles); + if (found) { + QStringList torrents; + for (int i = 1; i < torrentFiles.size(); ++i) { + std::string f = torrentFiles.value(std::to_string(i)); + torrents.append(QString::fromStdString(f)); + } + handleTorrentFiles(module, torrents); + } + } + } +} + +void SyncWidget::clear() { + + +} + +void SyncWidget::handleDirectFiles(QString module, QStringList files) { + qDebug() << files; +} + +void SyncWidget::handleTorrentFiles(QString module, QStringList torrents) { + qDebug() << torrents; +} diff --git a/apps/Launcher/syncwidget.h b/apps/Launcher/syncwidget.h index a5744859da..ef206914f4 100644 --- a/apps/Launcher/syncwidget.h +++ b/apps/Launcher/syncwidget.h @@ -28,7 +28,15 @@ #include class SyncWidget : public QWidget { +public: + SyncWidget(QWidget* parent); + void setSceneFile(QString scene); + +private: + void clear(); + void handleDirectFiles(QString module, QStringList files); + void handleTorrentFiles(QString module, QStringList torrents); }; #endif // __SYNCWIDGET_H__ From bf58c8ab758076aae456cc16481a548e1b3713c8 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 10 Jun 2015 02:37:37 +0200 Subject: [PATCH 116/329] Correctly copy libraries Combine SyncWidget and openspace::DownloadManager --- apps/Launcher/syncwidget.cpp | 8 ++++++++ apps/Launcher/syncwidget.h | 1 + ext/ghoul | 2 +- include/openspace/engine/configurationmanager.h | 1 + src/engine/configurationmanager.cpp | 1 + src/engine/openspaceengine.cpp | 2 +- support/cmake/support_macros.cmake | 5 ++++- 7 files changed, 17 insertions(+), 3 deletions(-) diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 30babbe899..7d6371a1b6 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -24,6 +24,8 @@ #include "syncwidget.h" +#include + #include #include #include @@ -42,6 +44,12 @@ SyncWidget::SyncWidget(QWidget* parent) ghoul::initialize(); + openspace::DownloadManager::initialize("http://openspace.itn.liu.se/request.cgi"); +} + +SyncWidget::~SyncWidget() { + openspace::DownloadManager::deinitialize(); + ghoul::deinitialize(); } void SyncWidget::setSceneFile(QString scene) { diff --git a/apps/Launcher/syncwidget.h b/apps/Launcher/syncwidget.h index ef206914f4..be78e6a332 100644 --- a/apps/Launcher/syncwidget.h +++ b/apps/Launcher/syncwidget.h @@ -30,6 +30,7 @@ class SyncWidget : public QWidget { public: SyncWidget(QWidget* parent); + ~SyncWidget(); void setSceneFile(QString scene); diff --git a/ext/ghoul b/ext/ghoul index c660e8dd75..eb66778e0a 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit c660e8dd75beae2a827cbc04d19987e45b23f4fb +Subproject commit eb66778e0aaf1809fff1a1476872594a24ac0ac3 diff --git a/include/openspace/engine/configurationmanager.h b/include/openspace/engine/configurationmanager.h index 2013bb3aa6..bd11a2dd23 100644 --- a/include/openspace/engine/configurationmanager.h +++ b/include/openspace/engine/configurationmanager.h @@ -50,6 +50,7 @@ public: static const std::string KeyLogImmediateFlush; static const std::string KeyLogs; static const std::string KeyDisableMasterRendering; + static const std::string KeyDownloadRequestURL; bool loadFromFile(const std::string& filename); diff --git a/src/engine/configurationmanager.cpp b/src/engine/configurationmanager.cpp index 65f9f26f81..5c2c9bf3ed 100644 --- a/src/engine/configurationmanager.cpp +++ b/src/engine/configurationmanager.cpp @@ -58,6 +58,7 @@ const std::string ConfigurationManager::KeyLogLevel = "Logging.LogLevel"; const std::string ConfigurationManager::KeyLogImmediateFlush = "Logging.ImmediateFlush"; const std::string ConfigurationManager::KeyLogs = "Logging.Logs"; const std::string ConfigurationManager::KeyDisableMasterRendering = "DisableRenderingOnMaster"; +const std::string ConfigurationManager::KeyDownloadRequestURL = "DownloadRequestURL"; bool ConfigurationManager::loadFromFile(const std::string& filename) { using ghoul::filesystem::FileSystem; diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 37abd2ac32..193d67e0f1 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -290,7 +290,7 @@ bool OpenSpaceEngine::initialize() { SysCap.logCapabilities(); std::string requestURL = ""; - bool success = configurationManager()->getValue(KeyDownloadRequestURL, requestURL); + bool success = configurationManager()->getValue(ConfigurationManager::KeyDownloadRequestURL, requestURL); if (success) DownloadManager::initialize(requestURL); diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 3813976a8b..4ee6a9c93f 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -166,10 +166,10 @@ function (add_external_dependencies) # Curl if (WIN32) set(CURL_ROOT_DIR "${OPENSPACE_EXT_DIR}/curl") + set(CURL_ROOT_DIR "${OPENSPACE_EXT_DIR}/curl" PARENT_SCOPE) target_include_directories(libOpenSpace SYSTEM PUBLIC ${CURL_ROOT_DIR}/include) target_link_libraries(libOpenSpace ${CURL_ROOT_DIR}/lib/libcurl_imp.lib) target_compile_definitions(libOpenSpace PUBLIC "OPENSPACE_CURL_ENABLED" "CURL_STATICLIB") - copy_files(OpenSpace "${CURL_ROOT_DIR}/lib/libcurl.dll") else () find_package(curl) if (CURL_FOUND) @@ -437,6 +437,9 @@ endfunction () function (copy_dynamic_libraries) if (WIN32) + + copy_files(OpenSpace "${CURL_ROOT_DIR}/lib/libcurl.dll") + # Copy DLLs needed by Ghoul into the executable directory ghl_copy_shared_libraries(OpenSpace ${OPENSPACE_EXT_DIR}/ghoul) From 3fee6e8baf907957ff5c00d1ac603f03add18fcb Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 10 Jun 2015 19:03:06 +0200 Subject: [PATCH 117/329] Added more logging text to detect errors --- src/engine/openspaceengine.cpp | 6 ++++++ src/rendering/renderengine.cpp | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index a95e058501..8261b3c3f5 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -355,8 +355,11 @@ bool OpenSpaceEngine::initialize() { // Load a light and a monospaced font loadFonts(); + LINFO("Initializing GUI"); _gui->initialize(); + + LINFO("Finished initializing"); return true; } @@ -564,8 +567,11 @@ gui::GUI* OpenSpaceEngine::gui() { } bool OpenSpaceEngine::initializeGL() { + LINFO("Initializing Rendering Engine"); bool success = _renderEngine->initializeGL(); + LINFO("Initializing OnScreen GUI GL"); _gui->initializeGL(); + LINFO("Finished initializing OpenGL"); return success; } diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index d915d3d1bb..75e1f7ed9a 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -280,14 +280,17 @@ bool RenderEngine::initializeGL() { _mainCamera->setMaxFov(maxFov); } + LINFO("Initializing ABuffer"); _abuffer->initialize(); + LINFO("Initializing Log"); _log = new ScreenLog(); ghoul::logging::LogManager::ref().addLog(_log); + LINFO("Initializing Visualizer"); _visualizer = new ABufferVisualizer(); - // successful init + LINFO("Finished initializing GL"); return true; } From 56472de07c367ee76df74b8d761caf3c40436b2b Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 10 Jun 2015 20:13:16 +0200 Subject: [PATCH 118/329] Include external libraries as system headers to remove warnings Add libtorrent library to the Launcher application --- apps/Launcher/CMakeLists.txt | 35 +- apps/Launcher/ext/libtorrent/AUTHORS | 26 + apps/Launcher/ext/libtorrent/CMakeLists.txt | 361 + apps/Launcher/ext/libtorrent/COPYING | 28 + apps/Launcher/ext/libtorrent/ChangeLog | 1305 +++ apps/Launcher/ext/libtorrent/LICENSE | 103 + apps/Launcher/ext/libtorrent/NEWS | 1 + apps/Launcher/ext/libtorrent/README | 16 + .../Launcher/ext/libtorrent/ed25519/readme.md | 165 + .../ext/libtorrent/ed25519/src/add_scalar.cpp | 56 + .../ext/libtorrent/ed25519/src/fe.cpp | 1501 +++ apps/Launcher/ext/libtorrent/ed25519/src/fe.h | 41 + .../ext/libtorrent/ed25519/src/fixedint.h | 6 + .../ext/libtorrent/ed25519/src/ge.cpp | 467 + apps/Launcher/ext/libtorrent/ed25519/src/ge.h | 74 + .../libtorrent/ed25519/src/key_exchange.cpp | 80 + .../ext/libtorrent/ed25519/src/keypair.cpp | 16 + .../ext/libtorrent/ed25519/src/precomp_data.h | 1392 +++ .../ext/libtorrent/ed25519/src/sc.cpp | 809 ++ apps/Launcher/ext/libtorrent/ed25519/src/sc.h | 13 + .../ext/libtorrent/ed25519/src/seed.cpp | 41 + .../ext/libtorrent/ed25519/src/sha512.cpp | 279 + .../ext/libtorrent/ed25519/src/sha512.h | 22 + .../ext/libtorrent/ed25519/src/sign.cpp | 31 + .../ext/libtorrent/ed25519/src/verify.cpp | 77 + apps/Launcher/ext/libtorrent/ed25519/test.c | 149 + .../include/libtorrent/ConvertUTF.h | 165 + .../ext/libtorrent/include/libtorrent/GeoIP.h | 179 + .../libtorrent/include/libtorrent/Makefile.am | 155 + .../libtorrent/include/libtorrent/Makefile.in | 805 ++ .../include/libtorrent/add_torrent_params.hpp | 402 + .../libtorrent/include/libtorrent/address.hpp | 77 + .../libtorrent/include/libtorrent/alert.hpp | 319 + .../include/libtorrent/alert_dispatcher.hpp | 50 + .../include/libtorrent/alert_manager.hpp | 114 + .../include/libtorrent/alert_types.hpp | 1759 +++ .../libtorrent/include/libtorrent/alloca.hpp | 54 + .../include/libtorrent/allocator.hpp | 79 + .../libtorrent/include/libtorrent/assert.hpp | 89 + .../include/libtorrent/aux_/session_impl.hpp | 1286 +++ .../include/libtorrent/bandwidth_limit.hpp | 101 + .../include/libtorrent/bandwidth_manager.hpp | 116 + .../libtorrent/bandwidth_queue_entry.hpp | 73 + .../include/libtorrent/bandwidth_socket.hpp | 51 + .../libtorrent/include/libtorrent/bencode.hpp | 465 + .../include/libtorrent/bitfield.hpp | 360 + .../include/libtorrent/bloom_filter.hpp | 81 + .../include/libtorrent/broadcast_socket.hpp | 156 + .../include/libtorrent/bt_peer_connection.hpp | 461 + .../libtorrent/include/libtorrent/buffer.hpp | 224 + .../include/libtorrent/build_config.hpp | 73 + .../include/libtorrent/chained_buffer.hpp | 124 + .../libtorrent/include/libtorrent/config.hpp | 572 + .../include/libtorrent/connection_queue.hpp | 150 + .../include/libtorrent/copy_ptr.hpp | 69 + .../include/libtorrent/create_torrent.hpp | 505 + .../include/libtorrent/deadline_timer.hpp | 103 + .../libtorrent/include/libtorrent/debug.hpp | 219 + .../include/libtorrent/disk_buffer_holder.hpp | 101 + .../include/libtorrent/disk_buffer_pool.hpp | 145 + .../include/libtorrent/disk_io_thread.hpp | 534 + .../libtorrent/include/libtorrent/ed25519.hpp | 32 + .../libtorrent/include/libtorrent/entry.hpp | 310 + .../include/libtorrent/enum_net.hpp | 83 + .../libtorrent/include/libtorrent/error.hpp | 62 + .../include/libtorrent/error_code.hpp | 562 + .../include/libtorrent/escape_string.hpp | 111 + .../libtorrent/include/libtorrent/export.hpp | 89 + .../include/libtorrent/extensions.hpp | 440 + .../include/libtorrent/extensions/logger.hpp | 60 + .../libtorrent/extensions/lt_trackers.hpp | 63 + .../extensions/metadata_transfer.hpp | 69 + .../libtorrent/extensions/smart_ban.hpp | 66 + .../libtorrent/extensions/ut_metadata.hpp | 68 + .../include/libtorrent/extensions/ut_pex.hpp | 67 + .../libtorrent/include/libtorrent/file.hpp | 333 + .../include/libtorrent/file_pool.hpp | 123 + .../include/libtorrent/file_storage.hpp | 586 + .../include/libtorrent/fingerprint.hpp | 129 + .../libtorrent/include/libtorrent/gzip.hpp | 133 + .../libtorrent/include/libtorrent/hasher.hpp | 139 + .../include/libtorrent/http_connection.hpp | 225 + .../include/libtorrent/http_parser.hpp | 180 + .../libtorrent/http_seed_connection.hpp | 137 + .../include/libtorrent/http_stream.hpp | 124 + .../libtorrent/http_tracker_connection.hpp | 117 + .../include/libtorrent/i2p_stream.hpp | 238 + .../include/libtorrent/identify_client.hpp | 67 + .../libtorrent/instantiate_connection.hpp | 54 + .../include/libtorrent/intrusive_ptr_base.hpp | 84 + .../include/libtorrent/invariant_check.hpp | 81 + .../ext/libtorrent/include/libtorrent/io.hpp | 170 + .../include/libtorrent/io_service.hpp | 77 + .../include/libtorrent/io_service_fwd.hpp | 77 + .../include/libtorrent/ip_filter.hpp | 362 + .../include/libtorrent/ip_voter.hpp | 131 + .../libtorrent/kademlia/dht_observer.hpp | 48 + .../libtorrent/kademlia/dht_tracker.hpp | 191 + .../include/libtorrent/kademlia/find_data.hpp | 100 + .../include/libtorrent/kademlia/get_item.hpp | 92 + .../include/libtorrent/kademlia/get_peers.hpp | 108 + .../include/libtorrent/kademlia/item.hpp | 137 + .../include/libtorrent/kademlia/logging.hpp | 140 + .../include/libtorrent/kademlia/msg.hpp | 65 + .../include/libtorrent/kademlia/node.hpp | 330 + .../libtorrent/kademlia/node_entry.hpp | 131 + .../include/libtorrent/kademlia/node_id.hpp | 78 + .../include/libtorrent/kademlia/observer.hpp | 181 + .../include/libtorrent/kademlia/refresh.hpp | 71 + .../libtorrent/kademlia/routing_table.hpp | 235 + .../libtorrent/kademlia/rpc_manager.hpp | 126 + .../kademlia/traversal_algorithm.hpp | 143 + .../include/libtorrent/lazy_entry.hpp | 453 + .../ext/libtorrent/include/libtorrent/lsd.hpp | 109 + .../include/libtorrent/magnet_uri.hpp | 84 + .../ext/libtorrent/include/libtorrent/max.hpp | 123 + .../libtorrent/include/libtorrent/natpmp.hpp | 177 + .../include/libtorrent/packet_buffer.hpp | 117 + .../include/libtorrent/parse_url.hpp | 61 + .../include/libtorrent/pe_crypto.hpp | 205 + .../libtorrent/include/libtorrent/peer.hpp | 63 + .../include/libtorrent/peer_connection.hpp | 1316 +++ .../libtorrent/include/libtorrent/peer_id.hpp | 48 + .../include/libtorrent/peer_info.hpp | 444 + .../include/libtorrent/peer_request.hpp | 58 + .../libtorrent/piece_block_progress.hpp | 57 + .../include/libtorrent/piece_picker.hpp | 654 ++ .../libtorrent/include/libtorrent/policy.hpp | 549 + .../include/libtorrent/proxy_base.hpp | 256 + .../libtorrent/include/libtorrent/ptime.hpp | 139 + .../libtorrent/include/libtorrent/puff.hpp | 33 + .../libtorrent/include/libtorrent/random.hpp | 40 + .../ext/libtorrent/include/libtorrent/rss.hpp | 277 + .../libtorrent/include/libtorrent/session.hpp | 1064 ++ .../include/libtorrent/session_settings.hpp | 1569 +++ .../include/libtorrent/session_status.hpp | 265 + .../include/libtorrent/settings.hpp | 66 + .../include/libtorrent/sha1_hash.hpp | 307 + .../include/libtorrent/size_type.hpp | 53 + .../include/libtorrent/sliding_average.hpp | 114 + .../libtorrent/include/libtorrent/socket.hpp | 209 + .../include/libtorrent/socket_io.hpp | 169 + .../include/libtorrent/socket_type.hpp | 337 + .../include/libtorrent/socket_type_fwd.hpp | 42 + .../include/libtorrent/socks5_stream.hpp | 192 + .../include/libtorrent/ssl_stream.hpp | 308 + .../libtorrent/include/libtorrent/stat.hpp | 384 + .../libtorrent/include/libtorrent/storage.hpp | 844 ++ .../include/libtorrent/storage_defs.hpp | 84 + .../include/libtorrent/string_util.hpp | 74 + .../libtorrent/include/libtorrent/thread.hpp | 89 + .../libtorrent/include/libtorrent/time.hpp | 129 + .../include/libtorrent/timestamp_history.hpp | 81 + .../libtorrent/include/libtorrent/tommath.h | 584 + .../include/libtorrent/tommath_class.h | 1000 ++ .../include/libtorrent/tommath_superclass.h | 84 + .../libtorrent/include/libtorrent/torrent.hpp | 1437 +++ .../include/libtorrent/torrent_handle.hpp | 1602 +++ .../include/libtorrent/torrent_info.hpp | 763 ++ .../include/libtorrent/tracker_manager.hpp | 307 + .../include/libtorrent/udp_socket.hpp | 311 + .../libtorrent/udp_tracker_connection.hpp | 144 + .../include/libtorrent/union_endpoint.hpp | 134 + .../libtorrent/include/libtorrent/upnp.hpp | 393 + .../libtorrent/include/libtorrent/utf8.hpp | 77 + .../include/libtorrent/utp_socket_manager.hpp | 174 + .../include/libtorrent/utp_stream.hpp | 464 + .../libtorrent/include/libtorrent/version.hpp | 47 + .../libtorrent/web_connection_base.hpp | 167 + .../libtorrent/web_peer_connection.hpp | 165 + .../include/libtorrent/xml_parse.hpp | 71 + .../libtorrent-rasterbar-cmake.pc.in | 6 + .../ext/libtorrent/src/ConvertUTF.cpp | 539 + apps/Launcher/ext/libtorrent/src/GeoIP.c | 1077 ++ apps/Launcher/ext/libtorrent/src/Makefile.am | 141 + apps/Launcher/ext/libtorrent/src/Makefile.in | 1116 ++ apps/Launcher/ext/libtorrent/src/alert.cpp | 1246 +++ .../ext/libtorrent/src/alert_manager.cpp | 196 + .../Launcher/ext/libtorrent/src/allocator.cpp | 201 + apps/Launcher/ext/libtorrent/src/asio.cpp | 23 + apps/Launcher/ext/libtorrent/src/asio_ssl.cpp | 22 + apps/Launcher/ext/libtorrent/src/assert.cpp | 277 + .../ext/libtorrent/src/bandwidth_limit.cpp | 94 + .../ext/libtorrent/src/bandwidth_manager.cpp | 241 + .../libtorrent/src/bandwidth_queue_entry.cpp | 74 + .../ext/libtorrent/src/bloom_filter.cpp | 76 + .../ext/libtorrent/src/broadcast_socket.cpp | 450 + .../ext/libtorrent/src/bt_peer_connection.cpp | 3408 ++++++ .../ext/libtorrent/src/chained_buffer.cpp | 159 + .../ext/libtorrent/src/connection_queue.cpp | 350 + .../ext/libtorrent/src/create_torrent.cpp | 657 ++ .../ext/libtorrent/src/disk_buffer_holder.cpp | 70 + .../ext/libtorrent/src/disk_buffer_pool.cpp | 242 + .../ext/libtorrent/src/disk_io_thread.cpp | 2537 +++++ apps/Launcher/ext/libtorrent/src/entry.cpp | 627 ++ apps/Launcher/ext/libtorrent/src/enum_net.cpp | 1066 ++ .../ext/libtorrent/src/error_code.cpp | 355 + .../ext/libtorrent/src/escape_string.cpp | 641 ++ apps/Launcher/ext/libtorrent/src/file.cpp | 2412 +++++ .../Launcher/ext/libtorrent/src/file_pool.cpp | 326 + .../ext/libtorrent/src/file_storage.cpp | 831 ++ apps/Launcher/ext/libtorrent/src/gzip.cpp | 268 + apps/Launcher/ext/libtorrent/src/hasher.cpp | 136 + .../ext/libtorrent/src/http_connection.cpp | 932 ++ .../ext/libtorrent/src/http_parser.cpp | 542 + .../libtorrent/src/http_seed_connection.cpp | 461 + .../ext/libtorrent/src/http_stream.cpp | 175 + .../src/http_tracker_connection.cpp | 544 + .../ext/libtorrent/src/i2p_stream.cpp | 524 + .../ext/libtorrent/src/identify_client.cpp | 428 + .../libtorrent/src/instantiate_connection.cpp | 141 + .../Launcher/ext/libtorrent/src/ip_filter.cpp | 98 + apps/Launcher/ext/libtorrent/src/ip_voter.cpp | 194 + .../libtorrent/src/kademlia/dht_tracker.cpp | 766 ++ .../ext/libtorrent/src/kademlia/find_data.cpp | 179 + .../ext/libtorrent/src/kademlia/get_item.cpp | 283 + .../ext/libtorrent/src/kademlia/get_peers.cpp | 313 + .../ext/libtorrent/src/kademlia/item.cpp | 224 + .../ext/libtorrent/src/kademlia/logging.cpp | 55 + .../ext/libtorrent/src/kademlia/node.cpp | 1291 +++ .../ext/libtorrent/src/kademlia/node_id.cpp | 231 + .../ext/libtorrent/src/kademlia/refresh.cpp | 110 + .../libtorrent/src/kademlia/routing_table.cpp | 1146 ++ .../libtorrent/src/kademlia/rpc_manager.cpp | 522 + .../src/kademlia/traversal_algorithm.cpp | 593 + .../ext/libtorrent/src/lazy_bdecode.cpp | 706 ++ apps/Launcher/ext/libtorrent/src/logger.cpp | 237 + apps/Launcher/ext/libtorrent/src/lsd.cpp | 325 + .../ext/libtorrent/src/lt_trackers.cpp | 414 + .../ext/libtorrent/src/magnet_uri.cpp | 254 + .../ext/libtorrent/src/metadata_transfer.cpp | 582 + apps/Launcher/ext/libtorrent/src/mpi.c | 9514 ++++++++++++++++ apps/Launcher/ext/libtorrent/src/natpmp.cpp | 702 ++ .../ext/libtorrent/src/packet_buffer.cpp | 215 + .../Launcher/ext/libtorrent/src/parse_url.cpp | 135 + .../Launcher/ext/libtorrent/src/pe_crypto.cpp | 373 + .../ext/libtorrent/src/peer_connection.cpp | 6108 +++++++++++ .../ext/libtorrent/src/piece_picker.cpp | 2731 +++++ apps/Launcher/ext/libtorrent/src/policy.cpp | 2029 ++++ apps/Launcher/ext/libtorrent/src/puff.cpp | 842 ++ apps/Launcher/ext/libtorrent/src/random.cpp | 72 + apps/Launcher/ext/libtorrent/src/rss.cpp | 683 ++ apps/Launcher/ext/libtorrent/src/session.cpp | 1417 +++ .../ext/libtorrent/src/session_impl.cpp | 6658 ++++++++++++ apps/Launcher/ext/libtorrent/src/settings.cpp | 142 + apps/Launcher/ext/libtorrent/src/sha1.cpp | 330 + .../Launcher/ext/libtorrent/src/smart_ban.cpp | 389 + .../Launcher/ext/libtorrent/src/socket_io.cpp | 104 + .../ext/libtorrent/src/socket_type.cpp | 336 + .../ext/libtorrent/src/socks5_stream.cpp | 600 ++ apps/Launcher/ext/libtorrent/src/stat.cpp | 50 + apps/Launcher/ext/libtorrent/src/storage.cpp | 3139 ++++++ .../ext/libtorrent/src/string_util.cpp | 169 + apps/Launcher/ext/libtorrent/src/thread.cpp | 173 + apps/Launcher/ext/libtorrent/src/time.cpp | 254 + .../ext/libtorrent/src/timestamp_history.cpp | 106 + apps/Launcher/ext/libtorrent/src/torrent.cpp | 9598 +++++++++++++++++ .../ext/libtorrent/src/torrent_handle.cpp | 914 ++ .../ext/libtorrent/src/torrent_info.cpp | 1473 +++ .../ext/libtorrent/src/tracker_manager.cpp | 357 + .../ext/libtorrent/src/udp_socket.cpp | 1485 +++ .../libtorrent/src/udp_tracker_connection.cpp | 724 ++ apps/Launcher/ext/libtorrent/src/upnp.cpp | 1603 +++ .../ext/libtorrent/src/ut_metadata.cpp | 613 ++ apps/Launcher/ext/libtorrent/src/ut_pex.cpp | 690 ++ apps/Launcher/ext/libtorrent/src/utf8.cpp | 104 + .../ext/libtorrent/src/utp_socket_manager.cpp | 479 + .../ext/libtorrent/src/utp_stream.cpp | 3525 ++++++ .../libtorrent/src/web_connection_base.cpp | 205 + .../libtorrent/src/web_peer_connection.cpp | 1050 ++ .../Launcher/ext/libtorrent/src/xml_parse.cpp | 218 + support/cmake/handle_external_library.cmake | 2 +- 272 files changed, 137134 insertions(+), 13 deletions(-) create mode 100644 apps/Launcher/ext/libtorrent/AUTHORS create mode 100644 apps/Launcher/ext/libtorrent/CMakeLists.txt create mode 100644 apps/Launcher/ext/libtorrent/COPYING create mode 100644 apps/Launcher/ext/libtorrent/ChangeLog create mode 100644 apps/Launcher/ext/libtorrent/LICENSE create mode 100644 apps/Launcher/ext/libtorrent/NEWS create mode 100644 apps/Launcher/ext/libtorrent/README create mode 100644 apps/Launcher/ext/libtorrent/ed25519/readme.md create mode 100644 apps/Launcher/ext/libtorrent/ed25519/src/add_scalar.cpp create mode 100644 apps/Launcher/ext/libtorrent/ed25519/src/fe.cpp create mode 100644 apps/Launcher/ext/libtorrent/ed25519/src/fe.h create mode 100644 apps/Launcher/ext/libtorrent/ed25519/src/fixedint.h create mode 100644 apps/Launcher/ext/libtorrent/ed25519/src/ge.cpp create mode 100644 apps/Launcher/ext/libtorrent/ed25519/src/ge.h create mode 100644 apps/Launcher/ext/libtorrent/ed25519/src/key_exchange.cpp create mode 100644 apps/Launcher/ext/libtorrent/ed25519/src/keypair.cpp create mode 100644 apps/Launcher/ext/libtorrent/ed25519/src/precomp_data.h create mode 100644 apps/Launcher/ext/libtorrent/ed25519/src/sc.cpp create mode 100644 apps/Launcher/ext/libtorrent/ed25519/src/sc.h create mode 100644 apps/Launcher/ext/libtorrent/ed25519/src/seed.cpp create mode 100644 apps/Launcher/ext/libtorrent/ed25519/src/sha512.cpp create mode 100644 apps/Launcher/ext/libtorrent/ed25519/src/sha512.h create mode 100644 apps/Launcher/ext/libtorrent/ed25519/src/sign.cpp create mode 100644 apps/Launcher/ext/libtorrent/ed25519/src/verify.cpp create mode 100644 apps/Launcher/ext/libtorrent/ed25519/test.c create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/ConvertUTF.h create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/GeoIP.h create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/Makefile.am create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/Makefile.in create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/add_torrent_params.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/address.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/alert.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/alert_dispatcher.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/alert_manager.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/alert_types.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/alloca.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/allocator.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/assert.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/aux_/session_impl.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_limit.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_manager.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_queue_entry.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_socket.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/bencode.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/bitfield.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/bloom_filter.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/broadcast_socket.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/bt_peer_connection.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/buffer.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/build_config.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/chained_buffer.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/config.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/connection_queue.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/copy_ptr.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/create_torrent.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/deadline_timer.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/debug.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/disk_buffer_holder.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/disk_buffer_pool.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/disk_io_thread.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/ed25519.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/entry.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/enum_net.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/error.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/error_code.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/escape_string.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/export.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/extensions.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/extensions/logger.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/extensions/lt_trackers.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/extensions/metadata_transfer.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/extensions/smart_ban.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/extensions/ut_metadata.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/extensions/ut_pex.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/file.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/file_pool.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/file_storage.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/fingerprint.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/gzip.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/hasher.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/http_connection.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/http_parser.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/http_seed_connection.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/http_stream.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/http_tracker_connection.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/i2p_stream.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/identify_client.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/instantiate_connection.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/intrusive_ptr_base.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/invariant_check.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/io.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/io_service.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/io_service_fwd.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/ip_filter.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/ip_voter.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/dht_observer.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/dht_tracker.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/find_data.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/get_item.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/get_peers.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/item.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/logging.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/msg.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/node.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/node_entry.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/node_id.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/observer.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/refresh.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/routing_table.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/rpc_manager.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/traversal_algorithm.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/lazy_entry.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/lsd.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/magnet_uri.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/max.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/natpmp.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/packet_buffer.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/parse_url.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/pe_crypto.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/peer.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/peer_connection.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/peer_id.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/peer_info.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/peer_request.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/piece_block_progress.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/piece_picker.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/policy.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/proxy_base.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/ptime.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/puff.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/random.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/rss.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/session.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/session_settings.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/session_status.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/settings.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/sha1_hash.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/size_type.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/sliding_average.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/socket.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/socket_io.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/socket_type.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/socket_type_fwd.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/socks5_stream.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/ssl_stream.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/stat.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/storage.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/storage_defs.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/string_util.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/thread.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/time.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/timestamp_history.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/tommath.h create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/tommath_class.h create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/tommath_superclass.h create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/torrent.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/torrent_handle.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/torrent_info.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/tracker_manager.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/udp_socket.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/udp_tracker_connection.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/union_endpoint.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/upnp.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/utf8.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/utp_socket_manager.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/utp_stream.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/version.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/web_connection_base.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/web_peer_connection.hpp create mode 100644 apps/Launcher/ext/libtorrent/include/libtorrent/xml_parse.hpp create mode 100644 apps/Launcher/ext/libtorrent/libtorrent-rasterbar-cmake.pc.in create mode 100644 apps/Launcher/ext/libtorrent/src/ConvertUTF.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/GeoIP.c create mode 100644 apps/Launcher/ext/libtorrent/src/Makefile.am create mode 100644 apps/Launcher/ext/libtorrent/src/Makefile.in create mode 100644 apps/Launcher/ext/libtorrent/src/alert.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/alert_manager.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/allocator.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/asio.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/asio_ssl.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/assert.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/bandwidth_limit.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/bandwidth_manager.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/bandwidth_queue_entry.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/bloom_filter.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/broadcast_socket.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/bt_peer_connection.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/chained_buffer.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/connection_queue.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/create_torrent.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/disk_buffer_holder.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/disk_buffer_pool.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/disk_io_thread.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/entry.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/enum_net.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/error_code.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/escape_string.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/file.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/file_pool.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/file_storage.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/gzip.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/hasher.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/http_connection.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/http_parser.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/http_seed_connection.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/http_stream.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/http_tracker_connection.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/i2p_stream.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/identify_client.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/instantiate_connection.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/ip_filter.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/ip_voter.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/kademlia/dht_tracker.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/kademlia/find_data.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/kademlia/get_item.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/kademlia/get_peers.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/kademlia/item.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/kademlia/logging.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/kademlia/node.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/kademlia/node_id.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/kademlia/refresh.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/kademlia/routing_table.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/kademlia/rpc_manager.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/kademlia/traversal_algorithm.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/lazy_bdecode.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/logger.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/lsd.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/lt_trackers.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/magnet_uri.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/metadata_transfer.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/mpi.c create mode 100644 apps/Launcher/ext/libtorrent/src/natpmp.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/packet_buffer.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/parse_url.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/pe_crypto.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/peer_connection.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/piece_picker.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/policy.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/puff.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/random.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/rss.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/session.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/session_impl.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/settings.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/sha1.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/smart_ban.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/socket_io.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/socket_type.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/socks5_stream.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/stat.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/storage.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/string_util.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/thread.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/time.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/timestamp_history.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/torrent.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/torrent_handle.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/torrent_info.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/tracker_manager.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/udp_socket.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/udp_tracker_connection.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/upnp.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/ut_metadata.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/ut_pex.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/utf8.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/utp_socket_manager.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/utp_stream.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/web_connection_base.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/web_peer_connection.cpp create mode 100644 apps/Launcher/ext/libtorrent/src/xml_parse.cpp diff --git a/apps/Launcher/CMakeLists.txt b/apps/Launcher/CMakeLists.txt index 2cf98ae128..8c58247d0c 100644 --- a/apps/Launcher/CMakeLists.txt +++ b/apps/Launcher/CMakeLists.txt @@ -25,24 +25,28 @@ set(APPLICATION_NAME Launcher) set(APPLICATION_LINK_TO_OPENSPACE ON) +include (${OPENSPACE_CMAKE_EXT_DIR}/handle_external_library.cmake) + +set(application_path ${OPENSPACE_APPS_DIR}/Launcher) + set(SOURCE_FILES - ${OPENSPACE_APPS_DIR}/Launcher/main.cpp - ${OPENSPACE_APPS_DIR}/Launcher/mainwindow.cpp - ${OPENSPACE_APPS_DIR}/Launcher/shortcutwidget.cpp - ${OPENSPACE_APPS_DIR}/Launcher/syncwidget.cpp + ${application_path}/main.cpp + ${application_path}/mainwindow.cpp + ${application_path}/shortcutwidget.cpp + ${application_path}/syncwidget.cpp ) set(HEADER_FILES - ${OPENSPACE_APPS_DIR}/Launcher/mainwindow.h - ${OPENSPACE_APPS_DIR}/Launcher/shortcutwidget.h - ${OPENSPACE_APPS_DIR}/Launcher/syncwidget.h + ${application_path}/mainwindow.h + ${application_path}/shortcutwidget.h + ${application_path}/syncwidget.h ) find_package(Qt5Widgets) find_package(Qt5Network) qt5_wrap_cpp(MOC_FILES ${HEADER_FILES}) -qt5_add_resources(RESOURCE_FILES ${OPENSPACE_APPS_DIR}/Launcher/files.qrc) +qt5_add_resources(RESOURCE_FILES ${application_path}/files.qrc) add_executable(${APPLICATION_NAME} MACOSX_BUNDLE ${SOURCE_FILES} @@ -57,8 +61,15 @@ target_link_libraries(${APPLICATION_NAME} ) if (APPLE) -INSTALL(CODE " - include(BundleUtilities) - fixup_bundle(\"/Users/alex/Development/OpenSpace/bin/openspace/Debug/Launcher.app/Contents/MacOS/Launcher\" \"/Users/alex/Development/OpenSpace/bin/openspace/Debug/TimelineView.app/Contents/plugins/platforms/libqcocoa.dylib\" \"\") - " COMPONENT Runtime) + INSTALL(CODE " + include(BundleUtilities) + fixup_bundle(\"/Users/alex/Development/OpenSpace/bin/openspace/Debug/Launcher.app/Contents/MacOS/Launcher\" \"/Users/alex/Development/OpenSpace/bin/openspace/Debug/TimelineView.app/Contents/plugins/platforms/libqcocoa.dylib\" \"\") + " COMPONENT Runtime) endif () + +# Libtorrent +set(encryption OFF CACHE BOOL "") +set(shared OFF CACHE BOOL "") +include_external_library(${APPLICATION_NAME} torrent-rasterbar ${application_path}/ext/libtorrent) + +copy_files(${APPLICATION_NAME} "${CURL_ROOT_DIR}/lib/libcurl.dll") diff --git a/apps/Launcher/ext/libtorrent/AUTHORS b/apps/Launcher/ext/libtorrent/AUTHORS new file mode 100644 index 0000000000..b26128709f --- /dev/null +++ b/apps/Launcher/ext/libtorrent/AUTHORS @@ -0,0 +1,26 @@ +Written by Arvid Norberg. Copyright (c) 2003-2014 + +Contributions by: +Andrei Kurushin +Steven Siloti +Thomas Fischer +Massaroddel +Tianhao Qiu. +Shyam +Magnus Jonsson +Daniel Wallin +Cory Nelson +Stas Khirman +Ryan Norton +Andrew Resch + +Building and maintainance of the autotools scripts: +Michael Wojciechowski +Peter Koeleman + +Thanks to Reimond Retz for bugfixes, suggestions and testing + +Thanks to University of UmeŒ for providing development and test hardware. + +Project is hosted by sourceforge. + diff --git a/apps/Launcher/ext/libtorrent/CMakeLists.txt b/apps/Launcher/ext/libtorrent/CMakeLists.txt new file mode 100644 index 0000000000..9bb7b2cd6f --- /dev/null +++ b/apps/Launcher/ext/libtorrent/CMakeLists.txt @@ -0,0 +1,361 @@ +cmake_minimum_required(VERSION 2.6) +project(libtorrent) +set (SOVERSION "8") +set (VERSION "1.0.5") + +set(sources + web_connection_base + alert + alert_manager + allocator + asio + assert + bandwidth_limit + bandwidth_manager + bandwidth_queue_entry + bloom_filter + chained_buffer + connection_queue + create_torrent + disk_buffer_holder + entry + error_code + file_storage + lazy_bdecode + escape_string + string_util + file + gzip + hasher + http_connection + http_stream + http_parser + i2p_stream + identify_client + ip_filter + ip_voter + peer_connection + bt_peer_connection + web_peer_connection + http_seed_connection + instantiate_connection + natpmp + packet_buffer + piece_picker + policy + puff + random + rss + session + session_impl + settings + socket_io + socket_type + socks5_stream + stat + storage + time + timestamp_history + torrent + torrent_handle + torrent_info + tracker_manager + http_tracker_connection + utf8 + udp_tracker_connection + udp_socket + upnp + utp_socket_manager + utp_stream + logger + file_pool + lsd + disk_buffer_pool + disk_io_thread + enum_net + broadcast_socket + magnet_uri + parse_url + ConvertUTF + thread + xml_parse + +# -- extensions -- + metadata_transfer + ut_pex + ut_metadata + smart_ban + lt_trackers +) + +# -- kademlia -- +set(kademlia_sources + dht_tracker + node + refresh + rpc_manager + find_data + node_id + routing_table + traversal_algorithm + logging + item + get_peers + get_item +) + +# -- ed25519 -- +set(ed25519_sources + add_scalar + fe + ge + key_exchange + keypair + sc + seed + sha512 + sign + verify +) + +set(includes include ed25519/src) + +option(shared "build libtorrent as a shared library" ON) +option(static_runtime "build libtorrent with static runtime" OFF) +option(tcmalloc "link against google performance tools tcmalloc" OFF) +option(pool-allocators "Uses a pool allocator for disk and piece buffers" ON) +option(encryption "link against openssl and enable encryption" ON) +option(geoip "link against LGPL GeoIP code from Maxmind, to enable geoip database support" OFF) +option(dht "enable support for Mainline DHT" ON) +option(resolve-countries "enable support for resolving countries from peer IPs" ON) +option(unicode "enable unicode support" ON) +option(deprecated-functions "enable deprecated functions for backwards compatibility" ON) +option(exceptions "build with exception support" ON) +option(logging "build with logging" OFF) +option(verbose-logging "build with verbose logging" OFF) +option(build_tests "build tests" OFF) + +set(CMAKE_CONFIGURATION_TYPES Debug Release RelWithDebInfo) + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release FORCE) +endif() + +# add_definitions() doesn't seem to let you say wich build type to apply it to +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DTORRENT_DEBUG") +if(UNIX) + set(CMAKE_C_FLAGS_RELWITHDEBINFO "-Os -g") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") +endif() + +if (build_tests) + # this will make some internal functions available in the + # DLL interface, for the tests to access + add_definitions(-DTORRENT_EXPORT_EXTRA) +endif (build_tests) + +include_directories(${includes}) + +if (encryption) + list(APPEND sources pe_crypto asio_ssl) + if(NOT DEFINED OPENSSL_INCLUDE_DIR OR NOT DEFINED OPENSSL_LIBRARIES) + FIND_PACKAGE(OpenSSL REQUIRED) + endif() + add_definitions(-DTORRENT_USE_OPENSSL) + include_directories(${OPENSSL_INCLUDE_DIR}) +else() + add_definitions(-DTORRENT_DISABLE_ENCRYPTION) + list(APPEND sources sha1) +endif (encryption) + +if (logging) + add_definitions(-DTORRENT_LOGGING) +endif() +if (verbose-logging) + add_definitions(-DTORRENT_VERBOSE_LOGGING) +endif() + +foreach(s ${sources}) + list(APPEND sources2 src/${s}) +endforeach(s) + +if (dht) + foreach(s ${kademlia_sources}) + list(APPEND sources2 src/kademlia/${s}) + endforeach(s) + foreach(s ${ed25519_sources}) + list(APPEND sources2 ed25519/src/${s}) + endforeach(s) +else() + add_definitions(-DTORRENT_DISABLE_DHT) +endif() + +if(NOT MSVC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") + set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -fvisibility-inlines-hidden") +endif() + +if (shared) + add_definitions(-DTORRENT_BUILDING_SHARED) + add_library(torrent-rasterbar SHARED ${sources2}) +else (shared) + if(static_runtime) + # fix /MT flag: + set(CompilerFlags + CMAKE_CXX_FLAGS + CMAKE_CXX_FLAGS_DEBUG + CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS_RELEASE + CMAKE_C_FLAGS + CMAKE_C_FLAGS_DEBUG + CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_C_FLAGS_RELEASE + ) + foreach(CompilerFlag ${CompilerFlags}) + string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") + endforeach() + endif() + add_library(torrent-rasterbar STATIC ${sources2}) +endif() + +# Boost +if(NOT DEFINED Boost_INCLUDE_DIR OR NOT DEFINED Boost_LIBRARIES) + FIND_PACKAGE( Boost COMPONENTS system thread date_time chrono) +endif() +include_directories(${Boost_INCLUDE_DIR}) +target_link_libraries(torrent-rasterbar ${Boost_LIBRARIES}) + +# this works around a bug in asio in boost-1.39 +#add_definitions(-DBOOST_ASIO_HASH_MAP_BUCKETS=1021 -D__USE_W32_SOCKETS -DWIN32_LEAN_AND_MEAN ) + +if(NOT static_runtime) + add_definitions(-DBOOST_ASIO_DYN_LINK -DBOOST_DATE_TIME_DYN_LINK -DBOOST_THREAD_DYN_LINK -DBOOST_SYSTEM_DYN_LINK -DBOOST_CHRONO_DYN_LINK) +else() + add_definitions(-DBOOST_ASIO_SEPARATE_COMPILATION) +endif() + +if (WIN32) + target_link_libraries(torrent-rasterbar wsock32 ws2_32) + add_definitions(-D_WIN32_WINNT=0x0600) + if (MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") # multicore compilation + endif() +endif() + +if (encryption) + target_link_libraries(torrent-rasterbar ${OPENSSL_LIBRARIES}) +endif() + +if (NOT pool-allocators) + add_definitions(-DTORRENT_DISABLE_POOL_ALLOCATOR) +endif() + +if (NOT geoip) + add_definitions(-DTORRENT_DISABLE_GEO_IP) +endif() + +if (NOT resolve-countries) + add_definitions(-DTORRENT_DISABLE_RESOLVE_COUNTRIES) +endif() + +if (unicode) + add_definitions(-DUNICODE -D_UNICODE) +endif() + +if (NOT deprecated-functions) + add_definitions(-DTORRENT_NO_DEPRECATE) +endif() + +if (exceptions) + if (MSVC) + add_definitions(/EHsc) + else (MSVC) + add_definitions(-fexceptions) + endif (MSVC) +else() + if (MSVC) + add_definitions(-D_HAS_EXCEPTIONS=0) + else (MSVC) + add_definitions(-fno-exceptions) + endif (MSVC) +endif() + +if (MSVC) +# disable bogus deprecation warnings on msvc8 + add_definitions(-D_SCL_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_DEPRECATE) +# these compiler settings just makes the compiler standard conforming + add_definitions(/Zc:wchar_t /Zc:forScope) +# for multi-core compilation + add_definitions(/MP) + +#$(SolutionDir)msvc,release:/OPT:ICF=5 +#$(SolutionDir)msvc,release:/OPT:REF +else() + add_definitions(-Wno-c++11-extensions) +endif() + +add_definitions(-D_FILE_OFFSET_BITS=64) +add_definitions(-DBOOST_EXCEPTION_DISABLE) +add_definitions(-DBOOST_ASIO_ENABLE_CANCELIO) + +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + add_definitions(-fcolor-diagnostics) +endif() + +if (tcmalloc) + target_link_libraries(torrent-rasterbar tcmalloc) +endif() + +set_target_properties(torrent-rasterbar PROPERTIES + SOVERSION ${SOVERSION}) + +get_property (COMPILETIME_OPTIONS_LIST + DIRECTORY ${CMAKE_CURRENT_SOURCE_DIRECTORY} + PROPERTY COMPILE_DEFINITIONS + ) +foreach (s ${COMPILETIME_OPTIONS_LIST}) + set (COMPILETIME_OPTIONS "${COMPILETIME_OPTIONS} -D${s}") +endforeach (s) + +configure_file(libtorrent-rasterbar-cmake.pc.in libtorrent-rasterbar.pc) + +string (COMPARE EQUAL "${CMAKE_SIZEOF_VOID_P}" "8" IS64BITS) + +if (IS64BITS AND RESPECTLIB64) + set (LIBDIR "lib64") +else() + set (LIBDIR "lib") +endif() + +install(TARGETS torrent-rasterbar DESTINATION ${LIBDIR}) +install(DIRECTORY include/libtorrent + DESTINATION include + PATTERN ".svn" EXCLUDE) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libtorrent-rasterbar.pc DESTINATION ${LIBDIR}/pkgconfig) + +# === set up examples directory as an independent project === +#file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/examples) +#configure_file(examples/run_cmake.sh.in examples/run_cmake.sh) +# to build the examples, run examples/run_cmake.sh after building libtorrent + +# === build tests === +if(build_tests) + FILE(GLOB tests RELATIVE "${PROJECT_SOURCE_DIR}" "test/test_*.cpp") + add_library(test_common STATIC test/main.cpp test/setup_transfer.cpp + test/dht_server.cpp test/udp_tracker.cpp test/peer_server.cpp + test/web_seed_suite.cpp) + enable_testing() + + foreach(s ${tests}) + get_filename_component (sn ${s} NAME_WE) + add_executable(${sn} ${s}) + target_link_libraries(${sn} torrent-rasterbar test_common) + add_test(${sn} ${s}) + endforeach(s) + +# add_executable(test_upnp test/test_upnp.cpp) +# target_link_libraries(test_upnp torrent-rasterbar) + +# add_executable(test_natpmp test/test_natpmp.cpp) +# target_link_libraries(test_natpmp torrent-rasterbar) +endif() diff --git a/apps/Launcher/ext/libtorrent/COPYING b/apps/Launcher/ext/libtorrent/COPYING new file mode 100644 index 0000000000..2dcc295795 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/COPYING @@ -0,0 +1,28 @@ +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of Rasterbar Software nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + diff --git a/apps/Launcher/ext/libtorrent/ChangeLog b/apps/Launcher/ext/libtorrent/ChangeLog new file mode 100644 index 0000000000..19c97bc575 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ChangeLog @@ -0,0 +1,1305 @@ +1.0.5 release + + * improve ip_voter to avoid flapping + * fixed bug when max_peerlist_size was set to 0 + * fix issues with missing exported symbols when building dll + * fix division by zero bug in edge case while connecting peers + +1.0.4 release + + * fix bug in python binding for file_progress on torrents with no metadata + * fix assert when removing a connected web seed + * fix bug in tracker timeout logic + * switch UPnP post back to HTTP 1.1 + * support conditional DHT get + * OpenSSL build fixes + * fix DHT scrape bug + +1.0.3 release + + * python binding build fix for boost-1.57.0 + * add --enable-export-all option to configure script, to export all symbols + from libtorrent + * fix if_nametoindex build error on windows + * handle overlong utf-8 sequences + * fix link order bug in makefile for python binding + * fix bug in interest calculation, causing premature disconnects + * tweak flag_override_resume_data semantics to make more sense (breaks + backwards compatibility of edge-cases) + * improve DHT bootstrapping and periodic refresh + * improve DHT maintanence performance (by pinging instead of full lookups) + * fix bug in DHT routing table node-id prefix optimization + * fix incorrect behavior of flag_use_resume_save_path + * fix protocol race-condition in super seeding mode + * support read-only DHT nodes + * remove unused partial hash DHT lookups + * remove potentially privacy leaking extension (non-anonymous mode) + * peer-id connection ordering fix in anonymous mode + * mingw fixes + +1.0.2 release + + * added missing force_proxy to python binding + * anonymous_mode defaults to false + * make DHT DOS detection more forgiving to bursts + * support IPv6 multicast in local service discovery + * simplify CAS function in DHT put + * support IPv6 traffic class (via the TOS setting) + * made uTP re-enter slow-start after time-out + * fixed uTP upload performance issue + * fix missing support for DHT put salt + +1.0.1 release + + * fix alignment issue in bitfield + * improved error handling of gzip + * fixed crash when web seeds redirect + * fix compiler warnings + +1.0 release + + * fix bugs in convert_to/from_native() on windows + * fix support for web servers not supporting keepalive + * support storing save_path in resume data + * don't use full allocation on network drives (on windows) + * added clear_piece_deadlines() to remove all piece deadlines + * improve queuing logic of inactive torrents (dont_count_slow_torrents) + * expose optimistic unchoke logic to plugins + * fix issue with large UDP packets on windows + * remove set_ratio() feature + * improve piece_deadline/streaming + * honor pieces with priority 7 in sequential download mode + * simplified building python bindings + * make ignore_non_routers more forgiving in the case there are no UPnP + devices at a known router. Should improve UPnP compatibility. + * include reason in peer_blocked_alert + * support magnet links wrapped in .torrent files + * rate limiter optimization + * rate limiter overflow fix (for very high limits) + * non-auto-managed torrents no longer count against the torrent limits + * handle DHT error responses correctly + * allow force_announce to only affect a single tracker + * add moving_storage field to torrent_status + * expose UPnP and NAT-PMP mapping in session object + * DHT refactoring and support for storing arbitrary data with put and get + * support building on android + * improved support for web seeds that don't support keep-alive + * improve DHT routing table to return better nodes (lower RTT and closer + to target) + * don't use pointers to resume_data and file_priorities in + add_torrent_params + * allow moving files to absolute paths, out of the download directory + * make move_storage more generic to allow both overwriting files as well + as taking existing ones + * fix choking issue at high upload rates + * optimized rate limiter + * make disk cache pool allocator configurable + * fix library ABI to not depend on logging being enabled + * use hex encoding instead of base32 in create_magnet_uri + * include name, save_path and torrent_file in torrent_status, for + improved performance + * separate anonymous mode and force-proxy mode, and tighten it up a bit + * add per-tracker scrape information to announce_entry + * report errors in read_piece_alert + * DHT memory optimization + * improve DHT lookup speed + * improve support for windows XP and earlier + * introduce global connection priority for improved swarm performance + * make files deleted alert non-discardable + * make built-in sha functions not conflict with libcrypto + * improve web seed hash failure case + * improve DHT lookup times + * uTP path MTU discovery improvements + * optimized the torrent creator optimizer to scale significantly better + with more files + * fix uTP edge case where udp socket buffer fills up + * fix nagle implementation in uTP + + * fix bug in error handling in protocol encryption + +0.16.18 release + + * fix uninitialized values in DHT DOS mitigation + * fix error handling in file::phys_offset + * fix bug in HTTP scrape response parsing + * enable TCP keepalive for socks5 connection for UDP associate + * fix python3 support + * fix bug in lt_donthave extension + * expose i2p_alert to python. cleaning up of i2p connection code + * fixed overflow and download performance issue when downloading at high rates + * fixed bug in add_torrent_alert::message for magnet links + * disable optimistic disconnects when connection limit is low + * improved error handling of session::listen_on + * suppress initial 'completed' announce to trackers added with replace_trackers + after becoming a seed + * SOCKS4 fix for trying to connect over IPv6 + * fix saving resume data when removing all trackers + * fix bug in udp_socket when changing socks5 proxy quickly + +0.16.17 release + + * don't fall back on wildcard port in UPnP + * fix local service discovery for magnet links + * fix bitfield issue in file_storage + * added work-around for MingW issue in file I/O + * fixed sparse file detection on windows + * fixed bug in gunzip + * fix to use proxy settings when adding .torrent file from URL + * fix resume file issue related to daylight savings time on windows + * improve error checking in lazy_bdecode + +0.16.16 release + + * add missing add_files overload to the python bindings + * improve error handling in http gunzip + * fix debug logging for banning web seeds + * improve support for de-selected files in full allocation mode + * fix dht_bootstrap_alert being posted + * SetFileValidData fix on windows (prevents zero-fill) + * fix minor lock_files issue on unix + +0.16.15 release + + * fix mingw time_t 64 bit issue + * fix use of SetFileValidData on windows + * fix crash when using full allocation storage mode + * improve error_code and error_category support in python bindings + * fix python binding for external_ip_alert + +0.16.14 release + + * make lt_tex more robust against bugs and malicious behavior + * HTTP chunked encoding fix + * expose file_granularity flag to python bindings + * fix DHT memory error + * change semantics of storage allocation to allocate on first write rather + than on startup (behaves better with changing file priorities) + * fix resend logic in response to uTP SACK messages + * only act on uTP RST packets with correct ack_nr + * make uTP errors log in normal log mode (not require verbose) + * deduplicate web seed entries from torrent files + * improve error reporting from lazy_decode() + +0.16.13 release + + * fix auto-manage issue when pausing session + * fix bug in non-sparse mode on windows, causing incorrect file errors to + be generated + * fix set_name() on file_storage actually affecting save paths + * fix large file support issue on mingw + * add some error handling to set_piece_hashes() + * fix completed-on timestamp to not be clobbered on each startup + * fix deadlock caused by some UDP tracker failures + * fix potential integer overflow issue in timers on windows + * minor fix to peer_proportional mixed_mode algorithm (TCP limit could go + too low) + * graceful pause fix + * i2p fixes + * fix issue when loading certain malformed .torrent files + * pass along host header with http proxy requests and possible + http_connection shutdown hang + +0.16.12 release + + * fix building with C++11 + * fix IPv6 support in UDP socket (uTP) + * fix mingw build issues + * increase max allowed outstanding piece requests from peers + * uTP performance improvement. only fast retransmit one packet at a time + * improve error message for 'file too short' + * fix piece-picker stat bug when only selecting some files for download + * fix bug in async_add_torrent when settings file_priorities + * fix boost-1.42 support for python bindings + * fix memory allocation issue (virtual addres space waste) on windows + +0.16.11 release + + * fix web seed URL double escape issue + * fix string encoding issue in alert messages + * fix SSL authentication issue + * deprecate std::wstring overloads. long live utf-8 + * improve time-critical pieces feature (streaming) + * introduce bandwidth exhaustion attack-mitigation in allowed-fast pieces + * python binding fix issue where torrent_info objects where destructing when + their torrents were deleted + * added missing field to scrape_failed_alert in python bindings + * GCC 4.8 fix + * fix proxy failure semantics with regards to anonymous mode + * fix round-robin seed-unchoke algorithm + * add bootstrap.sh to generage configure script and run configure + * fix bug in SOCK5 UDP support + * fix issue where torrents added by URL would not be started immediately + +0.16.10 release + + * fix encryption level handle invalid values + * add a number of missing functions to the python binding + * fix typo in Jamfile for building shared libraries + * prevent tracker exchange for magnet links before metadata is received + * fix crash in make_magnet_uri when generating links longer than 1024 + characters + * fix hanging issue when closing files on windows (completing a download) + * fix piece picking edge case that could cause torrents to get stuck at + hash failure + * try unencrypted connections first, and fall back to encryption if it + fails (performance improvement) + * add missing functions to python binding (flush_cache(), remap_files() + and orig_files()) + * improve handling of filenames that are invalid on windows + * support 'implied_port' in DHT announce_peer + * don't use pool allocator for disk blocks (cache may now return pages + to the kernel) + +0.16.9 release + + * fix long filename truncation on windows + * distinguish file open mode when checking files and downloading/seeding + with bittorrent. updates storage interface + * improve file_storage::map_file when dealing with invalid input + * improve handling of invalid utf-8 sequences in strings in torrent files + * handle more cases of broken .torrent files + * fix bug filename collision resolver + * fix bug in filename utf-8 verification + * make need_save_resume() a bit more robust + * fixed sparse flag manipulation on windows + * fixed streaming piece picking issue + +0.16.8 release + + * make rename_file create missing directories for new filename + * added missing python function: parse_magnet_uri + * fix alerts.all_categories in python binding + * fix torrent-abort issue which would cancel name lookups of other torrents + * make torrent file parser reject invalid path elements earlier + * fixed piece picker bug when using pad-files + * fix read-piece response for cancelled deadline-pieces + * fixed file priority vector-overrun + * fix potential packet allocation alignment issue in utp + * make 'close_redudnant_connections' cover more cases + * set_piece_deadline() also unfilters the piece (if its priority is 0) + * add work-around for bug in windows vista and earlier in + GetOverlappedResult + * fix traversal algorithm leak in DHT + * fix string encoding conversions on windows + * take torrent_handle::query_pieces into account in torrent_handle::statue() + * honor trackers responding with 410 + * fixed merkle tree torrent creation bug + * fixed crash with empty url-lists in torrent files + * added missing max_connections() function to python bindings + +0.16.7 release + + * fix string encoding in error messages + * handle error in read_piece and set_piece_deadline when torrent is removed + * DHT performance improvement + * attempt to handle ERROR_CANT_WAIT disk error on windows + * improve peers exchanged over PEX + * fixed rare crash in ut_metadata extension + * fixed files checking issue + * added missing pop_alerts() to python bindings + * fixed typos in configure script, inversing some feature-enable/disable flags + * added missing flag_update_subscribe to python bindings + * active_dht_limit, active_tracker_limit and active_lsd_limit now + interpret -1 as infinite + +0.16.6 release + + * fixed verbose log error for NAT holepunching + * fix a bunch of typos in python bindings + * make get_settings available in the python binding regardless of + deprecated functions + * fix typo in python settings binding + * fix possible dangling pointer use in peer list + * fix support for storing arbitrary data in the DHT + * fixed bug in uTP packet circle buffer + * fix potential crash when using torrent_handle::add_piece + * added missing add_torrent_alert to python binding + +0.16.5 release + + * udp socket refcounter fix + * added missing async_add_torrent to python bindings + * raised the limit for bottled http downloads to 2 MiB + * add support for magnet links and URLs in python example client + * fixed typo in python bindings' add_torrent_params + * introduce a way to add built-in plugins from python + * consistently disconnect the same peer when two peers simultaneously connect + * fix local endpoint queries for uTP connections + * small optimization to local peer discovery to ignore our own broadcasts + * try harder to bind the udp socket (uTP, DHT, UDP-trackers, LSD) to the + same port as TCP + * relax file timestamp requirements for accepting resume data + * fix performance issue in web seed downloader (coalescing of blocks + sometimes wouldn't work) + * web seed fixes (better support for torrents without trailing / in + web seeds) + * fix some issues with SSL over uTP connections + * fix UDP trackers trying all endpoints behind the hostname + +0.16.4 release + + * raise the default number of torrents allowed to announce to trackers + to 1600 + * improve uTP slow start behavior + * fixed UDP socket error causing it to fail on Win7 + * update use of boost.system to not use deprecated functions + * fix GIL issue in python bindings. Deprecated extension support in python + * fixed bug where setting upload slots to -1 would not mean infinite + * extend the UDP tracker protocol to include the request string from the + tracker URL + * fix mingw build for linux crosscompiler + +0.16.3 release + + * fix python binding backwards compatibility in replace_trackers + * fix possible starvation in metadata extension + * fix crash when creating torrents and optimizing file order with pad files + * disable support for large MTUs in uTP until it is more reliable + * expose post_torrent_updates and state_update_alert to python bindings + * fix incorrect SSL error messages + * fix windows build of shared library with openssl + * fix race condition causing shutdown hang + +0.16.2 release + + * fix permissions issue on linux with noatime enabled for non-owned files + * use random peer IDs in anonymous mode + * fix move_storage bugs + * fix unnecessary dependency on boost.date_time when building boost.asio as separate compilation + * always use SO_REUSEADDR and deprecate the flag to turn it on + * add python bindings for SSL support + * minor uTP tweaks + * fix end-game mode issue when some files are selected to not be downloaded + * improve uTP slow start + * make uTP less aggressive resetting cwnd when idle + +0.16.1 release + + * fixed crash when providing corrupt resume data + * fixed support for boost-1.44 + * fixed reversed semantics of queue_up() and queue_down() + * added missing functions to python bindings (file_priority(), set_dht_settings()) + * fixed low_prio_disk support on linux + * fixed time critical piece accounting in the request queue + * fixed semantics of rate_limit_utp to also ignore per-torrent limits + * fixed piece sorting bug of deadline pieces + * fixed python binding build on Mac OS and BSD + * fixed UNC path normalization (on windows, unless UNC paths are disabled) + * fixed possible crash when enabling multiple connections per IP + * fixed typo in win vista specific code, breaking the build + * change default of rate_limit_utp to true + * fixed DLL export issue on windows (when building a shared library linking statically against boost) + * fixed FreeBSD build + * fixed web seed performance issue with pieces > 1 MiB + * fixed unchoke logic when using web seeds + * fixed compatibility with older versions of boost (down to boost 1.40) + +0.16 release + + * support torrents with more than 262000 pieces + * make tracker back-off configurable + * don't restart the swarm after downloading metadata from magnet links + * lower the default tracker retry intervals + * support banning web seeds sending corrupt data + * don't let hung outgoing connection attempts block incoming connections + * improve SSL torrent support by using SNI and a single SSL listen socket + * improved peer exchange performance by sharing incoming connections which advertize listen port + * deprecate set_ratio(), and per-peer rate limits + * add web seed support for torrents with pad files + * introduced a more scalable API for torrent status updates (post_torrent_updates()) and updated client_test to use it + * updated the API to add_torrent_params turning all bools into flags of a flags field + * added async_add_torrent() function to significantly improve performance when + adding many torrents + * change peer_states to be a bitmask (bw_limit, bw_network, bw_disk) + * changed semantics of send_buffer_watermark_factor to be specified as a percentage + * add incoming_connection_alert for logging all successful incoming connections + * feature to encrypt peer connections with a secret AES-256 key stored in .torrent file + * deprecated compact storage allocation + * close files in separate thread on systems where close() may block (Mac OS X for instance) + * don't create all directories up front when adding torrents + * support DHT scrape + * added support for fadvise/F_RDADVISE for improved disk read performance + * introduced pop_alerts() which pops the entire alert queue in a single call + * support saving metadata in resume file, enable it by default for magnet links + * support for receiving multi announce messages for local peer discovery + * added session::listen_no_system_port flag to prevent libtorrent from ever binding the listen socket to port 0 + * added option to not recheck on missing or incomplete resume data + * extended stats logging with statistics=on builds + * added new session functions to more efficiently query torrent status + * added alerts for added and removed torrents + * expanded plugin interface to support session wide states + * made the metadata block requesting algorithm more robust against hash check failures + * support a separate option to use proxies for peers or not + * pausing the session now also pauses checking torrents + * moved alert queue size limit into session_settings + * added support for DHT rss feeds (storing only) + * added support for RSS feeds + * fixed up some edge cases in DHT routing table and improved unit test of it + * added error category and error codes for HTTP errors + * made the DHT implementation slightly more robust against routing table poisoning and node ID spoofing + * support chunked encoding in http downloads (http_connection) + * support adding torrents by url to the .torrent file + * support CDATA tags in xml parser + * use a python python dictionary for settings instead of session_settings object (in python bindings) + * optimized metadata transfer (magnet link) startup time (shaved off about 1 second) + * optimized swarm startup time (shaved off about 1 second) + * support DHT name lookup + * optimized memory usage of torrent_info and file_storage, forcing some API changes + around file_storage and file_entry + * support trackerid tracker extension + * graceful peer disconnect mode which finishes transactions before disconnecting peers + * support chunked encoding for web seeds + * uTP protocol support + * resistance towards certain flood attacks + * support chunked encoding for web seeds (only for BEP 19, web seeds) + * optimized session startup time + * support SSL for web seeds, through all proxies + * support extending web seeds with custom authorization and extra headers + * settings that are not changed from the default values are not saved + in the session state + * made seeding choking algorithm configurable + * deprecated setters for max connections, max half-open, upload and download + rates and unchoke slots. These are now set through session_settings + * added functions to query an individual peer's upload and download limit + * full support for BEP 21 (event=paused) + * added share-mode feature for improving share ratios + * merged all proxy settings into a single one + * improved SOCKS5 support by proxying hostname lookups + * improved support for multi-homed clients + * added feature to not count downloaded bytes from web seeds in stats + * added alert for incoming local service discovery messages + * added option to set file priorities when adding torrents + * removed the session mutex for improved performance + * added upload and download activity timer stats for torrents + * made the reuse-address flag configurable on the listen socket + * moved UDP trackers over to use a single socket + * added feature to make asserts log to a file instead of breaking the process + (production asserts) + * optimized disk I/O cache clearing + * added feature to ask a torrent if it needs to save its resume data or not + * added setting to ignore file modification time when loading resume files + * support more fine-grained torrent states between which peer sources it + announces to + * supports calculating sha1 file-hashes when creating torrents + * made the send_buffer_watermark performance warning more meaningful + * supports complete_ago extension + * dropped zlib as a dependency and builds using puff.c instead + * made the default cache size depend on available physical RAM + * added flags to torrent::status() that can filter which values are calculated + * support 'explicit read cache' which keeps a specific set of pieces + in the read cache, without implicitly caching other pieces + * support sending suggest messages based on what's in the read cache + * clear sparse flag on files that complete on windows + * support retry-after header for web seeds + * replaced boost.filesystem with custom functions + * replaced dependency on boost.thread by asio's internal thread primitives + * added support for i2p torrents + * cleaned up usage of MAX_PATH and related macros + * made it possible to build libtorrent without RTTI support + * added support to build with libgcrypt and a shipped version of libtommath + * optimized DHT routing table memory usage + * optimized disk cache to work with large caches + * support variable number of optimistic unchoke slots and to dynamically + adjust based on the total number of unchoke slots + * support for BitTyrant choker algorithm + * support for automatically start torrents when they receive an + incoming connection + * added more detailed instrumentation of the disk I/O thread + +0.15.11 release + + * fixed web seed bug, sometimes causing infinite loops + * fixed race condition when setting session_settings immediately after creating session + * give up immediately when failing to open a listen socket (report the actual error) + * restored ABI compatibility with 0.15.9 + * added missing python bindings for create_torrent and torrent_info + +0.15.10 release + + * fix 'parameter incorrect' issue when using unbuffered IO on windows + * fixed UDP socket error handling on windows + * fixed peer_tos (type of service) setting + * fixed crash when loading resume file with more files than the torrent in it + * fix invalid-parameter error on windows when disabling filesystem disk cache + * fix connection queue issue causing shutdown delays + * fixed mingw build + * fix overflow bug in progress_ppm field + * don't filter local peers received from a non-local tracker + * fix python deadlock when using python extensions + * fixed small memory leak in DHT + +0.15.9 release + + * added some functions missing from the python binding + * fixed rare piece picker bug + * fixed invalid torrent_status::finished_time + * fixed bugs in dont-have and upload-only extension messages + * don't open files in random-access mode (speeds up hashing) + +0.15.8 release + + * allow NULL to be passed to create_torrent::set_comment and create_torrent::set_creator + * fix UPnP issue for routers with multiple PPPoE connections + * fix issue where event=stopped announces wouldn't be sent when closing session + * fix possible hang in file::readv() on windows + * fix CPU busy loop issue in tracker announce logic + * honor IOV_MAX when using writev and readv + * don't post 'operation aborted' UDP errors when changing listen port + * fix tracker retry logic, where in some configurations the next tier would not be tried + * fixed bug in http seeding logic (introduced in 0.15.7) + * add support for dont-have extension message + * fix for set_piece_deadline + * add reset_piece_deadline function + * fix merkle tree torrent assert + +0.15.7 release + + * exposed set_peer_id to python binding + * improve support for merkle tree torrent creation + * exposed comparison operators on torrent_handle to python + * exposed alert error_codes to python + * fixed bug in announce_entry::next_announce_in and min_announce_in + * fixed sign issue in set_alert_mask signature + * fixed unaligned disk access for unbuffered I/O in windows + * support torrents whose name is empty + * fixed connection limit to take web seeds into account as well + * fixed bug when receiving a have message before having the metadata + * fixed python bindings build with disabled DHT support + * fixed BSD file allocation issue + * fixed bug in session::delete_files option to remove_torrent + +0.15.6 release + + * fixed crash in udp trackers when using SOCKS5 proxy + * fixed reconnect delay when leaving upload only mode + * fixed default values being set incorrectly in add_torrent_params through add_magnet_uri in python bindings + * implemented unaligned write (for unbuffered I/O) + * fixed broadcast_lsd option + * fixed udp-socket race condition when using a proxy + * end-game mode optimizations + * fixed bug in udp_socket causing it to issue two simultaneous async. read operations + * fixed mingw build + * fixed minor bug in metadata block requester (for magnet links) + * fixed race condition in iconv string converter + * fixed error handling in torrent_info constructor + * fixed bug in torrent_info::remap_files + * fix python binding for wait_for_alert + * only apply privileged port filter to DHT-only peers + +0.15.5 release + + * support DHT extension to report external IPs + * fixed rare crash in http_connection's error handling + * avoid connecting to peers listening on ports < 1024 + * optimized piece picking to not cause busy loops in some end-game modes + * fixed python bindings for tcp::endpoint + * fixed edge case of pad file support + * limit number of torrents tracked by DHT + * fixed bug when allow_multiple_connections_per_ip was enabled + * potential WOW64 fix for unbuffered I/O (windows) + * expose set_alert_queue_size_limit to python binding + * support dht nodes in magnet links + * support 100 Continue HTTP responses + * changed default choker behavior to use 8 unchoke slots (instead of being rate based) + * fixed error reporting issue in disk I/O thread + * fixed file allocation issues on linux + * fixed filename encoding and decoding issue on platforms using iconv + * reports redundant downloads to tracker, fixed downloaded calculation to + be more stable when not including redundant. Improved redundant data accounting + to be more accurate + * fixed bugs in http seed connection and added unit test for it + * fixed error reporting when fallocate fails + * deprecate support for separate proxies for separate kinds of connections + +0.15.4 release + + * fixed piece picker issue triggered by hash failure and timed out requests to the piece + * fixed optimistic unchoke issue when setting per torrent unchoke limits + * fixed UPnP shutdown issue + * fixed UPnP DeletePortmapping issue + * fixed NAT-PMP issue when adding the same mapping multiple times + * no peers from tracker when stopping is no longer an error + * improved web seed retry behavior + * fixed announce issue + +0.15.3 release + + * fixed announce bug where event=completed would not be sent if it violated the + min-announce of the tracker + * fixed limitation in rate limiter + * fixed build error with boost 1.44 + +0.15.2 release + + * updated compiler to msvc 2008 for python binding + * restored default fail_limit to unlimited on all trackers + * fixed rate limit bug for DHT + * fixed SOCKS5 bug for routing UDP packets + * fixed bug on windows when verifying resume data for a torrent where + one of its directories had been removed + * fixed race condition in peer-list with DHT + * fix force-reannounce and tracker retry issue + +0.15.1 release + + * fixed rare crash when purging the peer list + * fixed race condition around m_abort in session_impl + * fixed bug in web_peer_connection which could cause a hang when downloading + from web servers + * fixed bug in metadata extensions combined with encryption + * refactored socket reading code to not use async. operations unnecessarily + * some timer optimizations + * removed the reuse-address flag on the listen socket + * fixed bug where local peer discovery and DHT wouldn't be announced to without trackers + * fixed bug in bdecoder when decoding invalid messages + * added build warning when building with UNICODE but the standard library + doesn't provide std::wstring + * fixed add_node python binding + * fixed issue where trackers wouldn't tried immediately when the previous one failed + * fixed synchronization issue between download queue and piece picker + * fixed bug in udp tracker scrape response parsing + * fixed bug in the disk thread that could get triggered under heavy load + * fixed bug in add_piece() that would trigger asserts + * fixed vs 2010 build + * recognizes more clients in identify_client() + * fixed bug where trackers wouldn't be retried if they failed + * slight performance fix in disk elevator algorithm + * fixed potential issue where a piece could be checked twice + * fixed build issue on windows related to GetCompressedSize() + * fixed deadlock when starting torrents with certain invalid tracker URLs + * fixed iterator bug in disk I/O thread + * fixed FIEMAP support on linux + * fixed strict aliasing warning on gcc + * fixed inconsistency when creating torrents with symlinks + * properly detect windows version to initialize half-open connection limit + * fixed bug in url encoder where $ would not be encoded + +0.15 release + + * introduced a session state save mechanism. load_state() and save_state(). + this saves all session settings and state (except torrents) + * deprecated dht_state functions and merged it with the session state + * added support for multiple trackers in magnet links + * added support for explicitly flushing the disk cache + * added torrent priority to affect bandwidth allocation for its peers + * reduced the number of floating point operations (to better support + systems without FPU) + * added new alert when individual files complete + * added support for storing symbolic links in .torrent files + * added support for uTorrent interpretation of multi-tracker torrents + * handle torrents with duplicate filenames + * piece timeouts are adjusted to download rate limits + * encodes urls in torrent files that needs to be encoded + * fixed not passing &supportcrypto=1 when encryption is disabled + * introduced an upload mode, which torrents are switched into when + it hits a disk write error, instead of stopping the torrent. + this lets libtorrent keep uploading the parts it has when it + encounters a disk-full error for instance + * improved disk error handling and expanded use of error_code in + error reporting. added a bandwidth state, bw_disk, when waiting + for the disk io thread to catch up writing buffers + * improved read cache memory efficiency + * added another cache flush algorithm to write the largest + contiguous blocks instead of the least recently used + * introduced a mechanism to be lighter on the disk when checking torrents + * applied temporary memory storage optimization to when checking + a torrent as well + * removed hash_for_slot() from storage_interface. It is now implemented + by using the readv() function from the storage implementation + * improved IPv6 support by announcing twice when necessary + * added feature to set a separate global rate limit for local peers + * added preset settings for low memory environments and seed machines + min_memory_usage() and high_performance_seeder() + * optimized overall memory usage for DHT nodes and requests, peer + entries and disk buffers + * change in API for block_info in partial_piece_info, instead of + accessing 'peer', call 'peer()' + * added support for fully automatic unchoker (no need to specify + number of upload slots). This is on by default + * added support for changing socket buffer sizes through + session_settings + * added support for merkle hash tree torrents (.merkle.torrent) + * added 'seed mode', which assumes that all files are complete + and checks hashes lazily, as blocks are requested + * added new extension for file attributes (executable and hidden) + * added support for unbuffered I/O for aligned files + * added workaround for sparse file issue on Windows Vista + * added new lt_trackers extension to exchange trackers between + peers + * added support for BEP 17 http seeds + * added read_piece() to read pieces from torrent storage + * added option for udp tracker preference + * added super seeding + * added add_piece() function to inject data from external sources + * add_tracker() function added to torrent_handle + * if there is no working tracker, current_tracker is the + tracker that is currently being tried + * torrents that are checking can now be paused, which will + pause the checking + * introduced another torrent state, checking_resume_data, which + the torrent is in when it's first added, and is comparing + the files on disk with the resume data + * DHT bandwidth usage optimizations + * rate limited DHT send socket + * tracker connections are now also subject to IP filtering + * improved optimistic unchoke logic + * added monitoring of the DHT lookups + * added bandwidth reports for estimated TCP/IP overhead and DHT + * includes DHT traffic in the rate limiter + * added support for bitcomet padding files + * improved support for sparse files on windows + * added ability to give seeding torrents preference to active slots + * added torrent_status::finished_time + * automatically caps files and connections by default to rlimit + * added session::is_dht_running() function + * added torrent_handle::force_dht_announce() + * added torrent_info::remap_files() + * support min_interval tracker extension + * added session saving and loading functions + * added support for min-interval in tracker responses + * only keeps one outstanding duplicate request per peer + reduces waste download, specifically when streaming + * added support for storing per-peer rate limits across reconnects + * improved fallocate support + * fixed magnet link issue when using resume data + * support disk I/O priority settings + * added info_hash to torrent_deleted_alert + * improved LSD performance and made the interval configurable + * improved UDP tracker support by caching connect tokens + * fast piece optimization + +release 0.14.10 + + * fixed udp tracker race condition + * added support for torrents with odd piece sizes + * fixed issue with disk read cache not being cleared when removing torrents + * made the DHT socket bind to the same interface as the session + * fixed issue where an http proxy would not be used on redirects + * Solaris build fixes + * disabled buggy disconnect_peers feature + +release 0.14.9 + + * disabled feature to drop requests after having been skipped too many times + * fixed range request bug for files larger than 2 GB in web seeds + * don't crash when trying to create torrents with 0 files + * fixed big_number __init__ in python bindings + * fixed optimistic unchoke timer + * fixed bug where torrents with incorrectly formatted web seed URLs would be + connected multiple times + * fixed MinGW support + * fixed DHT bootstrapping issue + * fixed UDP over SOCKS5 issue + * added support for "corrupt" tracker announce + * made end-game mode less aggressive + +release 0.14.8 + + * ignore unkown metadata messages + * fixed typo that would sometimes prevent queued torrents to be checked + * fixed bug in auto-manager where active_downloads and active_seeds would + sometimes be used incorrectly + * force_recheck() no longer crashes on torrents with no metadata + * fixed broadcast socket regression from 0.14.7 + * fixed hang in NATPMP when shut down while waiting for a response + * fixed some more error handling in bdecode + +release 0.14.7 + + * fixed deadlock in natpmp + * resume data alerts are always posted, regardless of alert mask + * added wait_for_alert to python binding + * improved invalid filename character replacement + * improved forward compatibility in DHT + * added set_piece_hashes that takes a callback to the python binding + * fixed division by zero in get_peer_info() + * fixed bug where pieces may have been requested before the metadata + was received + * fixed incorrect error when deleting files from a torrent where + not all files have been created + * announces torrents immediately to the DHT when it's started + * fixed bug in add_files that would fail to recurse if the path + ended with a / + * fixed bug in error handling when parsing torrent files + * fixed file checking bug when renaming a file before checking the torrent + * fixed race conditon when receiving metadata from swarm + * fixed assert in ut_metadata plugin + * back-ported some fixes for building with no exceptions + * fixed create_torrent when passing in a path ending with / + * fixed move_storage when source doesn't exist + * fixed DHT state save bug for node-id + * fixed typo in python binding session_status struct + * broadcast sockets now join every network interface (used for UPnP and + local peer discovery) + +release 0.14.6 + + * various missing include fixes to be buildable with boost 1.40 + * added missing functions to python binding related to torrent creation + * fixed to add filename on web seed urls that lack it + * fixed BOOST_ASIO_HASH_MAP_BUCKETS define for boost 1.39 + * fixed checking of fast and suggest messages when used with magnet links + * fixed bug where web seeds would not disconnect if being resolved when + the torrent was paused + * fixed download piece performance bug in piece picker + * fixed bug in connect candidate counter + * replaces invalid filename characters with . + * added --with-libgeoip option to configure script to allow building and + linking against system wide library + * fixed potential pure virtual function call in extensions on shutdown + * fixed disk buffer leak in smart_ban extension + +release 0.14.5 + + * fixed bug when handling malformed webseed urls and an http proxy + * fixed bug when setting unlimited upload or download rates for torrents + * fix to make torrent_status::list_peers more accurate. + * fixed memory leak in disk io thread when not using the cache + * fixed bug in connect candidate counter + * allow 0 upload slots + * fixed bug in rename_file(). The new name would not always be saved in + the resume data + * fixed resume data compatibility with 0.13 + * fixed rare piece-picker bug + * fixed bug where one allowed-fast message would be sent even when + disabled + * fixed race condition in UPnP which could lead to crash + * fixed inversed seed_time ratio logic + * added get_ip_filter() to session + +release 0.14.4 + + * connect candidate calculation fix + * tightened up disk cache memory usage + * fixed magnet link parser to accept hex-encoded info-hashes + * fixed inverted logic when picking which peers to connect to + (should mean a slight performance improvement) + * fixed a bug where a failed rename_file() would leave the storage + in an error state which would pause the torrent + * fixed case when move_storage() would fail. Added a new alert + to be posted when it does + * fixed crash bug when shutting down while checking a torrent + * fixed handling of web seed urls that didn't end with a + slash for multi-file torrents + * lowered the default connection speed to 10 connection attempts + per second + * optimized memory usage when checking files fails + * fixed bug when checking a torrent twice + * improved handling of out-of-memory conditions in disk I/O thread + * fixed bug when force-checking a torrent with partial pieces + * fixed memory leak in disk cache + * fixed torrent file path vulnerability + * fixed upnp + * fixed bug when dealing with clients that drop requests (i.e. BitComet) + fixes assert as well + +release 0.14.3 + + * added python binding for create_torrent + * fixed boost-1.38 build + * fixed bug where web seeds would be connected before the files + were checked + * fixed filename bug when using wide characters + * fixed rare crash in peer banning code + * fixed potential HTTP compatibility issue + * fixed UPnP crash + * fixed UPnP issue where the control url contained the base url + * fixed a replace_trackers bug + * fixed bug where the DHT port mapping would not be removed when + changing DHT port + * fixed move_storage bug when files were renamed to be moved out + of the root directory + * added error handling for set_piece_hashes + * fixed missing include in enum_if.cpp + * fixed dual IP stack issue + * fixed issue where renamed files were sometimes not saved in resume data + * accepts tracker responses with no 'peers' field, as long as 'peers6' + is present + * fixed CIDR-distance calculation in the precense of IPv6 peers + * save partial resume data for torrents that are queued for checking + or checking, to maintain stats and renamed files + * Don't try IPv6 on windows if it's not installed + * move_storage fix + * fixed potential crash on shutdown + * fixed leaking exception from bdecode on malformed input + * fixed bug where connection would hang when receiving a keepalive + * fixed bug where an asio exception could be thrown when resolving + peer countries + * fixed crash when shutting down while checking a torrent + * fixed potential crash in connection_queue when a peer_connection + fail to open its socket + +release 0.14.2 + + * added missing functions to the python bindings torrent_info::map_file, + torrent_info::map_block and torrent_info::file_at_offset. + * removed support for boost-1.33 and earlier (probably didn't work) + * fixed potential freezes issues at shutdown + * improved error message for python setup script + * fixed bug when torrent file included announce-list, but no valid + tracker urls + * fixed bug where the files requested from web seeds would be the + renamed file names instead of the original file names in the torrent. + * documentation fix of queing section + * fixed potential issue in udp_socket (affected udp tracker support) + * made name, comment and created by also be subject to utf-8 error + correction (filenames already were) + * fixed dead-lock when settings DHT proxy + * added missing export directives to lazy_entry + * fixed disk cache expiry settings bug (if changed, it would be set + to the cache size) + * fixed bug in http_connection when binding to a particular IP + * fixed typo in python binding (torrent_handle::piece_prioritize should + be torrent_handle::piece_priorities) + * fixed race condition when saving DHT state + * fixed bugs related to lexical_cast being locale dependent + * added support for SunPro C++ compiler + * fixed bug where messeges sometimes could be encrypted in the + wrong order, for encrypted connections. + * fixed race condition where torrents could get stuck waiting to + get checked + * fixed mapped files bug where it wouldn't be properly restored + from resume data properly + * removed locale dependency in xml parser (caused asserts on windows) + * fixed bug when talking to https 1.0 servers + * fixed UPnP bug that could cause stack overflow + +release 0.14.1 + + * added converter for python unicode strings to utf-8 paths + * fixed bug in http downloader where the host field did not + include the port number + * fixed headers to not depend on NDEBUG, which would prohibit + linking a release build of libtorrent against a debug application + * fixed bug in disk I/O thread that would make the thread + sometimes quit when an error occurred + * fixed DHT bug + * fixed potential shutdown crash in disk_io_thread + * fixed usage of deprecated boost.filsystem functions + * fixed http_connection unit test + * fixed bug in DHT when a DHT state was loaded + * made rate limiter change in 0.14 optional (to take estimated + TCP/IP overhead into account) + * made the python plugin buildable through the makefile + * fixed UPnP bug when url base ended with a slash and + path started with a slash + * fixed various potentially leaking exceptions + * fixed problem with removing torrents that are checking + * fixed documentation bug regarding save_resume_data() + * added missing documentation on torrent creation + * fixed bugs in python client examples + * fixed missing dependency in package-config file + * fixed shared geoip linking in Jamfile + * fixed python bindings build on windows and made it possible + to generate a windows installer + * fixed bug in NAT-PMP implementation + +release 0.14 + + * deprecated add_torrent() in favor of a new add_torrent() + that takes a struct with parameters instead. Torrents + are paused and auto managed by default. + * removed 'connecting_to_tracker' torrent state. This changes + the enum values for the other states. + * Improved seeding and choking behavior. + * Fixed rare buffer overrun bug when calling get_download_queue + * Fixed rare bug where torrent could be put back into downloading + state even though it was finished, after checking files. + * Fixed rename_file to work before the file on disk has been + created. + * Fixed bug in tracker connections in case of errors caused + in the connection constructor. + * Updated alert system to be filtered by category instead of + severity level. Alerts can generate a message through + alert::message(). + * Session constructor will now start dht, upnp, natpmp, lsd by + default. Flags can be passed in to the constructor to not + do this, if these features are to be enabled and disabled + at a later point. + * Removed 'connecting_to_tracker' torrent state + * Fix bug where FAST pieces were cancelled on choke + * Fixed problems with restoring piece states when hash failed. + * Minimum peer reconnect time fix. Peers with no failures would + reconnect immediately. + * Improved web seed error handling + * DHT announce fixes and off-by-one loop fix + * Fixed UPnP xml parse bug where it would ignore the port number + for the control url. + * Fixed bug in torrent writer where the private flag was added + outside of the info dictionary + * Made the torrent file parser less strict of what goes in the + announce-list entry + * Fixed type overflow bug where some statistics was incorrectly + reported for file larger than 2 GB + * boost-1.35 support + * Fixed bug in statistics from web server peers where it sometimes + could report too many bytes downloaded. + * Fixed bug where statistics from the last second was lost when + disconnecting a peer. + * receive buffer optimizations (memcpy savings and memory savings) + * Support for specifying the TOS byte for peer traffic. + * Basic support for queueing of torrents. + * Better bias to give connections to downloading torrents + with fewer peers. + * Optimized resource usage (removed the checking thread) + * Support to bind outgoing connections to specific ports + * Disk cache support. + * New, more memory efficient, piece picker with sequential download + support (instead of the more complicated sequential download threshold). + * Auto Upload slots. Automtically opens up more slots if + upload limit is not met. + * Improved NAT-PMP support by querying the default gateway + * Improved UPnP support by ignoring routers not on the clients subnet. + +release 0.13 + + * Added scrape support + * Added add_extension() to torrent_handle. Can instantiate + extensions for torrents while downloading + * Added support for remove_torrent to delete the files as well + * Fixed issue with failing async_accept on windows + * DHT improvements, proper error messages are now returned when + nodes sends bad packets + * Optimized the country table used to resolve country of peers + * Copying optimization for sending data. Data is no longer copied from + the disk I/O buffer to the send buffer. + * Buffer optimization to use a raw buffer instead of std::vector + * Improved file storage to use sparse files + * Updated python bindings + * Added more clients to the identifiable clients list. + * Torrents can now be started in paused state (to better support queuing) + * Improved IPv6 support (support for IPv6 extension to trackers and + listens on both IPv6 and IPv4 interfaces). + * Improved asserts used. Generates a stacktrace on linux + * Piece picker optimizations and improvements + * Improved unchoker, connection limit and rate limiter + * Support for FAST extension + * Fixed invalid calculation in DHT node distance + * Fixed bug in URL parser that failed to parse IPv6 addresses + * added peer download rate approximation + * added port filter for outgoing connection (to prevent + triggering firewalls) + * made most parameters configurable via session_settings + * added encryption support + * added parole mode for peers whose data fails the hash check. + * optimized heap usage in piece-picker and web seed downloader. + * fixed bug in DHT where older write tokens weren't accepted. + * added support for sparse files. + * introduced speed categories for peers and pieces, to separate + slow and fast peers. + * added a half-open tcp connection limit that takes all connections + in to account, not just peer connections. + * added alerts for filtered IPs. + * added support for SOCKS4 and 5 proxies and HTTP CONNECT proxies. + * fixed proper distributed copies calculation. + * added option to use openssl for sha-1 calculations. + * optimized the piece picker in the case where a peer is a seed. + * added support for local peer discovery + * removed the dependency on the compiled boost.date_time library + * deprecated torrent_info::print() + * added UPnP support + * fixed problem where peer interested flags were not updated correctly + when pieces were filtered + * improvements to ut_pex messages, including support for seed flag + * prioritizes upload bandwidth to peers that might send back data + * the following functions have been deprecated: + void torrent_handle::filter_piece(int index, bool filter) const; + void torrent_handle::filter_pieces(std::vector const& pieces) const; + bool torrent_handle::is_piece_filtered(int index) const; + std::vector torrent_handle::filtered_pieces() const; + void torrent_handle::filter_files(std::vector const& files) const; + + instead, use the piece_priority functions. + + * added support for NAT-PMP + * added support for piece priorities. Piece filtering is now set as + a priority + * Fixed crash when last piece was smaller than one block and reading + fastresume data for that piece + * Makefiles should do a better job detecting boost + * Fixed crash when all tracker urls are removed + * Log files can now be created at user supplied path + * Log files failing to create is no longer fatal + * Fixed dead-lock in torrent_handle + * Made it build with boost 1.34 on windows + * Fixed bug in URL parser that failed to parse IPv6 addresses + * Fixed bug in DHT, related to IPv6 nodes + * DHT accepts transaction IDs that have garbage appended to them + * DHT logs messages that it fails to decode + +release 0.12 + + * fixes to make the DHT more compatible + * http seed improvements including error reporting and url encoding issues. + * fixed bug where directories would be left behind when moving storage + in some cases. + * fixed crashing bug when restarting or stopping the DHT. + * added python binding, using boost.python + * improved character conversion on windows when strings are not utf-8. + * metadata extension now respects the private flag in the torrent. + * made the DHT to only be used as a fallback to trackers by default. + * added support for HTTP redirection support for web seeds. + * fixed race condition when accessing a torrent that was checking its + fast resume data. + * fixed a bug in the DHT which could be triggered if the network was + dropped or extremely rare cases. + * if the download rate is limited, web seeds will now only use left-over + bandwidth after all bt peers have used up as much bandwidth as they can. + * added the possibility to have libtorrent resolve the countries of + the peers in torrents. + * improved the bandwidth limiter (it now implements a leaky bucket/node bucket). + * improved the HTTP seed downloader to report accurate progress. + * added more client peer-id signatures to be recognized. + * added support for HTTP servers that skip the CR before the NL at line breaks. + * fixed bug in the HTTP code that only accepted headers case sensitive. + * fixed bug where one of the session constructors didn't initialize boost.filesystem. + * fixed bug when the initial checking of a torrent fails with an exception. + * fixed bug in DHT code which would send incorrect announce messages. + * fixed bug where the http header parser was case sensitive to the header + names. + * Implemented an optmization which frees the piece_picker once a torrent + turns into a seed. + * Added support for uT peer exchange extension, implemented by Massaroddel. + * Modified the quota management to offer better bandwidth balancing + between peers. + * logging now supports multiple sessions (different sessions now log + to different directories). + * fixed random number generator seed problem, generating the same + peer-id for sessions constructed the same second. + * added an option to accept multiple connections from the same IP. + * improved tracker logging. + * moved the file_pool into session. The number of open files is now + limited per session. + * fixed uninitialized private flag in torrent_info + * fixed long standing issue with file.cpp on windows. Replaced the low level + io functions used on windows. + * made it possible to associate a name with torrents without metadata. + * improved http-downloading performance by requesting entire pieces via + http. + * added plugin interface for extensions. And changed the interface for + enabling extensions. + +release 0.11 + + * added support for incorrectly encoded paths in torrent files + (assumes Latin-1 encoding and converts to UTF-8). + * added support for destructing session objects asynchronously. + * fixed bug with file_progress() with files = 0 bytes + * fixed a race condition bug in udp_tracker_connection that could + cause a crash. + * fixed bug occuring when increasing the sequenced download threshold + with max availability lower than previous threshold. + * fixed an integer overflow bug occuring when built with gcc 4.1.x + * fixed crasing bug when closing while checking a torrent + * fixed bug causing a crash with a torrent with piece length 0 + * added an extension to the DHT network protocol to support the + exchange of nodes with IPv6 addresses. + * modified the ip_filter api slightly to support IPv6 + * modified the api slightly to make sequenced download threshold + a per torrent-setting. + * changed the address type to support IPv6 + * fixed bug in piece picker which would not behave as + expected with regard to sequenced download threshold. + * fixed bug with file_progress() with files > 2 GB. + * added --enable-examples option to configure script. + * fixed problem with the resource distribution algorithm + (controlling e.g upload/download rates). + * fixed incorrect asserts in storage related to torrents with + zero-sized files. + * added support for trackerless torrents (with kademlia DHT). + * support for torrents with the private flag set. + * support for torrents containing bootstrap nodes for the + DHT network. + * fixed problem with the configure script on FreeBSD. + * limits the pipelining used on url-seeds. + * fixed problem where the shutdown always would delay for + session_settings::stop_tracker_timeout seconds. + * session::listen_on() won't reopen the socket in case the port and + interface is the same as the one currently in use. + * added http proxy support for web seeds. + * fixed problem where upload and download stats could become incorrect + in case of high cpu load. + * added more clients to the identifiable list. + * fixed fingerprint parser to cope with latest Mainline versions. + +release 0.10 + + * fixed a bug where the requested number of peers in a tracker request could + be too big. + * fixed a bug where empty files were not created in full allocation mode. + * fixed a bug in storage that would, in rare cases, fail to do a + complete check. + * exposed more settings for tweaking parameters in the piece-picker, + downloader and uploader (http_settings replaced by session_settings). + * tweaked default settings to improve high bandwidth transfers. + * improved the piece picker performance and made it possible to download + popular pieces in sequence to improve disk performance. + * added the possibility to control upload and download limits per peer. + * fixed problem with re-requesting skipped pieces when peer was sending pieces + out of fifo-order. + * added support for http seeding (the GetRight protocol) + * renamed identifiers called 'id' in the public interface to support linking + with Objective.C++ + * changed the extensions protocol to use the new one, which is also + implemented by uTorrent. + * factorized the peer_connection and added web_peer_connection which is + able to download from http-sources. + * converted the network code to use asio (resulted in slight api changes + dealing with network addresses). + * made libtorrent build in vc7 (patches from Allen Zhao) + * fixed bug caused when binding outgoing connections to a non-local interface. + * add_torrent() will now throw if called while the session object is + being closed. + * added the ability to limit the number of simultaneous half-open + TCP connections. Flags in peer_info has been added. + +release 0.9.1 + + * made the session disable file name checks within the boost.filsystem library + * fixed race condition in the sockets + * strings that are invalid utf-8 strings are now decoded with the + local codepage on windows + * added the ability to build libtorrent both as a shared library + * client_test can now monitor a directory for torrent files and automatically + start and stop downloads while running + * fixed problem with file_size() when building on windows with unicode support + * added a new torrent state, allocating + * added a new alert, metadata_failed_alert + * changed the interface to session::add_torrent for some speed optimizations. + * greatly improved the command line control of the example client_test. + * fixed bug where upload rate limit was not being applied. + * files that are being checked will no longer stall files that don't need + checking. + * changed the way libtorrent identifies support for its excentions + to look for 'ext' at the end of the peer-id. + * improved performance by adding a circle buffer for the send buffer. + * fixed bugs in the http tracker connection when using an http proxy. + * fixed problem with storage's file pool when creating torrents and then + starting to seed them. + * hard limit on remote request queue and timeout on requests (a timeout + triggers rerequests). This makes libtorrent work much better with + "broken" clients like BitComet which may ignore requests. + +Initial release 0.9 + + * multitracker support + * serves multiple torrents on a single port and a single thread + * supports http proxies and proxy authentication + * gzipped tracker-responses + * block level piece picker + * queues torrents for file check, instead of checking all of them in parallel + * uses separate threads for checking files and for main downloader + * upload and download rate limits + * piece-wise, unordered, incremental file allocation + * fast resume support + * supports files > 2 gigabytes + * supports the no_peer_id=1 extension + * support for udp-tracker protocol + * number of connections limit + * delays sending have messages + * can resume pieces downloaded in any order + * adjusts the length of the request queue depending on download rate + * supports compact=1 + * selective downloading + * ip filter + diff --git a/apps/Launcher/ext/libtorrent/LICENSE b/apps/Launcher/ext/libtorrent/LICENSE new file mode 100644 index 0000000000..2c361d159f --- /dev/null +++ b/apps/Launcher/ext/libtorrent/LICENSE @@ -0,0 +1,103 @@ +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ + +puff.c +Copyright (C) 2002, 2003 Mark Adler +For conditions of distribution and use, see copyright notice in puff.h +version 1.7, 3 Mar 2003 + +puff.c is a simple inflate written to be an unambiguous way to specify the +deflate format. It is not written for speed but rather simplicity. As a +side benefit, this code might actually be useful when small code is more +important than speed, such as bootstrap applications. For typical deflate +data, zlib's inflate() is about four times as fast as puff(). zlib's +inflate compiles to around 20K on my machine, whereas puff.c compiles to +around 4K on my machine (a PowerPC using GNU cc). If the faster decode() +function here is used, then puff() is only twice as slow as zlib's +inflate(). + +All dynamically allocated memory comes from the stack. The stack required +is less than 2K bytes. This code is compatible with 16-bit int's and +assumes that long's are at least 32 bits. puff.c uses the short data type, +assumed to be 16 bits, for arrays in order to to conserve memory. The code +works whether integers are stored big endian or little endian. + +In the comments below are "Format notes" that describe the inflate process +and document some of the less obvious aspects of the format. This source +code is meant to supplement RFC 1951, which formally describes the deflate +format: + + http://www.zlib.org/rfc-deflate.html + +------------------------------------------------------------------------------ + +GeoIP.c + +Copyright (C) 2006 MaxMind LLC + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +------------------------------------------------------------------------------ + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/apps/Launcher/ext/libtorrent/NEWS b/apps/Launcher/ext/libtorrent/NEWS new file mode 100644 index 0000000000..58378c1553 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/NEWS @@ -0,0 +1 @@ +See ChangeLog diff --git a/apps/Launcher/ext/libtorrent/README b/apps/Launcher/ext/libtorrent/README new file mode 100644 index 0000000000..089d37122b --- /dev/null +++ b/apps/Launcher/ext/libtorrent/README @@ -0,0 +1,16 @@ +libtorrent is an open source C++ library implementing the BitTorrent protocol, +along with most popular extensions, making it suitable for real world +deployment. It is configurable to be able to fit both servers and embedded +devices. + +The main goals of libtorrent are to be efficient and easy to use. + +See docs/index.html for more detailed build and usage instructions. + +To build with boost-build, run: + + b2 + +See docs/building.html for more details on how to build and which configuration +options are available. + diff --git a/apps/Launcher/ext/libtorrent/ed25519/readme.md b/apps/Launcher/ext/libtorrent/ed25519/readme.md new file mode 100644 index 0000000000..b202892773 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/readme.md @@ -0,0 +1,165 @@ +Ed25519 +======= + +This is a portable implementation of [Ed25519](http://ed25519.cr.yp.to/) based +on the SUPERCOP "ref10" implementation. Additionally there is key exchanging +and scalar addition included to further aid building a PKI using Ed25519. All +code is in the public domain. + +All code is pure ANSI C without any dependencies, except for the random seed +generation which uses standard OS cryptography APIs (`CryptGenRandom` on +Windows, `/dev/urandom` on nix). If you wish to be entirely portable define +`ED25519_NO_SEED`. This disables the `ed25519_create_seed` function, so if your +application requires key generation you must supply your own seeding function +(which is simply a 256 bit (32 byte) cryptographic random number generator). + + +Performance +----------- + +On a Windows machine with an Intel Pentium B970 @ 2.3GHz I got the following +speeds (running on only one a single core): + + Seed generation: 64us (15625 per second) + Key generation: 88us (11364 per second) + Message signing (short message): 87us (11494 per second) + Message verifying (short message): 228us (4386 per second) + Scalar addition: 100us (10000 per second) + Key exchange: 220us (4545 per second) + +The speeds on other machines may vary. Sign/verify times will be higher with +longer messages. The implementation significantly benefits from 64 bit +architectures, if possible compile as 64 bit. + + +Usage +----- + +Simply add all .c and .h files in the `src/` folder to your project and include +`ed25519.h` in any file you want to use the API. If you prefer to use a shared +library, only copy `ed25519.h` and define `ED25519_DLL` before importing. A +windows DLL is pre-built. + +There are no defined types for seeds, private keys, public keys, shared secrets +or signatures. Instead simple `unsigned char` buffers are used with the +following sizes: + +```c +unsigned char seed[32]; +unsigned char signature[64]; +unsigned char public_key[32]; +unsigned char private_key[64]; +unsigned char scalar[32]; +unsigned char shared_secret[32]; +``` + +API +--- + +```c +int ed25519_create_seed(unsigned char *seed); +``` + +Creates a 32 byte random seed in `seed` for key generation. `seed` must be a +writable 32 byte buffer. Returns 0 on success, and nonzero on failure. + +```c +void ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed); +``` + +Creates a new key pair from the given seed. `public_key` must be a writable 32 +byte buffer, `private_key` must be a writable 64 byte buffer and `seed` must be +a 32 byte buffer. + +```c +void ed25519_sign(unsigned char *signature, + const unsigned char *message, size_t message_len, + const unsigned char *public_key, const unsigned char *private_key); +``` + +Creates a signature of the given message with the given key pair. `signature` +must be a writable 64 byte buffer. `message` must have at least `message_len` +bytes to be read. + +```c +int ed25519_verify(const unsigned char *signature, + const unsigned char *message, size_t message_len, + const unsigned char *public_key); +``` + +Verifies the signature on the given message using `public_key`. `signature` +must be a readable 64 byte buffer. `message` must have at least `message_len` +bytes to be read. Returns 1 if the signature matches, 0 otherwise. + +```c +void ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, + const unsigned char *scalar); +``` + +Adds `scalar` to the given key pair where scalar is a 32 byte buffer (possibly +generated with `ed25519_create_seed`), generating a new key pair. You can +calculate the public key sum without knowing the private key and vice versa by +passing in `NULL` for the key you don't know. This is useful for enforcing +randomness on a key pair by a third party while only knowing the public key, +among other things. Warning: the last bit of the scalar is ignored - if +comparing scalars make sure to clear it with `scalar[31] &= 127`. + + +```c +void ed25519_key_exchange(unsigned char *shared_secret, + const unsigned char *public_key, const unsigned char *private_key); +``` + +Performs a key exchange on the given public key and private key, producing a +shared secret. It is recommended to hash the shared secret before using it. +`shared_secret` must be a 32 byte writable buffer where the shared secret will +be stored. + +Example +------- + +```c +unsigned char seed[32], public_key[32], private_key[64], signature[64]; +unsigned char other_public_key[32], other_private_key[64], shared_secret[32]; +const unsigned char message[] = "TEST MESSAGE"; + +/* create a random seed, and a key pair out of that seed */ +if (ed25519_create_seed(seed)) { + printf("error while generating seed\n"); + exit(1); +} + +ed25519_create_keypair(public_key, private_key, seed); + +/* create signature on the message with the key pair */ +ed25519_sign(signature, message, strlen(message), public_key, private_key); + +/* verify the signature */ +if (ed25519_verify(signature, message, strlen(message), public_key)) { + printf("valid signature\n"); +} else { + printf("invalid signature\n"); +} + +/* create a dummy keypair to use for a key exchange, normally you'd only have +the public key and receive it through some communication channel */ +if (ed25519_create_seed(seed)) { + printf("error while generating seed\n"); + exit(1); +} + +ed25519_create_keypair(other_public_key, other_private_key, seed); + +/* do a key exchange with other_public_key */ +ed25519_key_exchange(shared_secret, other_public_key, private_key); + +/* + the magic here is that ed25519_key_exchange(shared_secret, public_key, + other_private_key); would result in the same shared_secret +*/ + +``` + +License +------- +All code is in the public domain. diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/add_scalar.cpp b/apps/Launcher/ext/libtorrent/ed25519/src/add_scalar.cpp new file mode 100644 index 0000000000..72cc852b93 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/add_scalar.cpp @@ -0,0 +1,56 @@ +#include "libtorrent/ed25519.hpp" +#include "ge.h" +#include "sc.h" + + +/* see http://crypto.stackexchange.com/a/6215/4697 */ +void ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar) { + const unsigned char SC_1[32] = {1}; /* scalar with value 1 */ + + unsigned char n[32]; + ge_p3 nB; + ge_p1p1 A_p1p1; + ge_p3 A; + ge_p3 public_key_unpacked; + ge_cached T; + + int i; + + /* copy the scalar and clear highest bit */ + for (i = 0; i < 31; ++i) { + n[i] = scalar[i]; + } + n[31] = scalar[31] & 127; + + /* private key: a = n + t */ + if (private_key) { + sc_muladd(private_key, SC_1, n, private_key); + } + + /* public key: A = nB + T */ + if (public_key) { + /* if we know the private key we don't need a point addition, which is faster */ + /* using a "timing attack" you could find out wether or not we know the private + key, but this information seems rather useless - if this is important pass + public_key and private_key seperately in 2 function calls */ + if (private_key) { + ge_scalarmult_base(&A, private_key); + } else { + /* unpack public key into T */ + ge_frombytes_negate_vartime(&public_key_unpacked, public_key); + fe_neg(public_key_unpacked.X, public_key_unpacked.X); // undo negate + fe_neg(public_key_unpacked.T, public_key_unpacked.T); // undo negate + ge_p3_to_cached(&T, &public_key_unpacked); + + /* calculate n*B */ + ge_scalarmult_base(&nB, n); + + /* A = n*B + T */ + ge_add(&A_p1p1, &nB, &T); + ge_p1p1_to_p3(&A, &A_p1p1); + } + + /* pack public key */ + ge_p3_tobytes(public_key, &A); + } +} diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/fe.cpp b/apps/Launcher/ext/libtorrent/ed25519/src/fe.cpp new file mode 100644 index 0000000000..b5889daef8 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/fe.cpp @@ -0,0 +1,1501 @@ +#include "fixedint.h" +#include "fe.h" + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4146 ) /* warning C4146: unary minus operator applied to unsigned type, result still unsigned */ +#pragma warning(disable : 4244 ) /* warning C4244: '=' : conversion from 'int64_t' to 'int32_t', possible loss of data */ +#endif // _MSC_VER + +/* + helper functions +*/ +static uint64_t load_3(const unsigned char *in) { + uint64_t result; + + result = (uint64_t) in[0]; + result |= ((uint64_t) in[1]) << 8; + result |= ((uint64_t) in[2]) << 16; + + return result; +} + +static uint64_t load_4(const unsigned char *in) { + uint64_t result; + + result = (uint64_t) in[0]; + result |= ((uint64_t) in[1]) << 8; + result |= ((uint64_t) in[2]) << 16; + result |= ((uint64_t) in[3]) << 24; + + return result; +} + + + +/* + h = 0 +*/ + +void fe_0(fe h) { + h[0] = 0; + h[1] = 0; + h[2] = 0; + h[3] = 0; + h[4] = 0; + h[5] = 0; + h[6] = 0; + h[7] = 0; + h[8] = 0; + h[9] = 0; +} + + + +/* + h = 1 +*/ + +void fe_1(fe h) { + h[0] = 1; + h[1] = 0; + h[2] = 0; + h[3] = 0; + h[4] = 0; + h[5] = 0; + h[6] = 0; + h[7] = 0; + h[8] = 0; + h[9] = 0; +} + + + +/* + h = f + g + Can overlap h with f or g. + + Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + + Postconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +void fe_add(fe h, const fe f, const fe g) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t h0 = f0 + g0; + int32_t h1 = f1 + g1; + int32_t h2 = f2 + g2; + int32_t h3 = f3 + g3; + int32_t h4 = f4 + g4; + int32_t h5 = f5 + g5; + int32_t h6 = f6 + g6; + int32_t h7 = f7 + g7; + int32_t h8 = f8 + g8; + int32_t h9 = f9 + g9; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} + + + +/* + Replace (f,g) with (g,g) if b == 1; + replace (f,g) with (f,g) if b == 0. + + Preconditions: b in {0,1}. +*/ + +void fe_cmov(fe f, const fe g, unsigned int b) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t x0 = f0 ^ g0; + int32_t x1 = f1 ^ g1; + int32_t x2 = f2 ^ g2; + int32_t x3 = f3 ^ g3; + int32_t x4 = f4 ^ g4; + int32_t x5 = f5 ^ g5; + int32_t x6 = f6 ^ g6; + int32_t x7 = f7 ^ g7; + int32_t x8 = f8 ^ g8; + int32_t x9 = f9 ^ g9; + + b = (unsigned int) (- (int) b); /* silence warning */ + x0 &= b; + x1 &= b; + x2 &= b; + x3 &= b; + x4 &= b; + x5 &= b; + x6 &= b; + x7 &= b; + x8 &= b; + x9 &= b; + + f[0] = f0 ^ x0; + f[1] = f1 ^ x1; + f[2] = f2 ^ x2; + f[3] = f3 ^ x3; + f[4] = f4 ^ x4; + f[5] = f5 ^ x5; + f[6] = f6 ^ x6; + f[7] = f7 ^ x7; + f[8] = f8 ^ x8; + f[9] = f9 ^ x9; +} + +/* + Replace (f,g) with (g,f) if b == 1; + replace (f,g) with (f,g) if b == 0. + + Preconditions: b in {0,1}. +*/ + +void fe_cswap(fe f,fe g,unsigned int b) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t x0 = f0 ^ g0; + int32_t x1 = f1 ^ g1; + int32_t x2 = f2 ^ g2; + int32_t x3 = f3 ^ g3; + int32_t x4 = f4 ^ g4; + int32_t x5 = f5 ^ g5; + int32_t x6 = f6 ^ g6; + int32_t x7 = f7 ^ g7; + int32_t x8 = f8 ^ g8; + int32_t x9 = f9 ^ g9; + b = -b; // warning C4146: unary minus operator applied to unsigned type, result still unsigned + x0 &= b; + x1 &= b; + x2 &= b; + x3 &= b; + x4 &= b; + x5 &= b; + x6 &= b; + x7 &= b; + x8 &= b; + x9 &= b; + f[0] = f0 ^ x0; + f[1] = f1 ^ x1; + f[2] = f2 ^ x2; + f[3] = f3 ^ x3; + f[4] = f4 ^ x4; + f[5] = f5 ^ x5; + f[6] = f6 ^ x6; + f[7] = f7 ^ x7; + f[8] = f8 ^ x8; + f[9] = f9 ^ x9; + g[0] = g0 ^ x0; + g[1] = g1 ^ x1; + g[2] = g2 ^ x2; + g[3] = g3 ^ x3; + g[4] = g4 ^ x4; + g[5] = g5 ^ x5; + g[6] = g6 ^ x6; + g[7] = g7 ^ x7; + g[8] = g8 ^ x8; + g[9] = g9 ^ x9; +} + + + +/* + h = f +*/ + +void fe_copy(fe h, const fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + + h[0] = f0; + h[1] = f1; + h[2] = f2; + h[3] = f3; + h[4] = f4; + h[5] = f5; + h[6] = f6; + h[7] = f7; + h[8] = f8; + h[9] = f9; +} + + + +/* + Ignores top bit of h. +*/ + +void fe_frombytes(fe h, const unsigned char *s) { + int64_t h0 = load_4(s); + int64_t h1 = load_3(s + 4) << 6; + int64_t h2 = load_3(s + 7) << 5; + int64_t h3 = load_3(s + 10) << 3; + int64_t h4 = load_3(s + 13) << 2; + int64_t h5 = load_4(s + 16); + int64_t h6 = load_3(s + 20) << 7; + int64_t h7 = load_3(s + 23) << 5; + int64_t h8 = load_3(s + 26) << 4; + int64_t h9 = (load_3(s + 29) & 8388607) << 2; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + carry9 = (h9 + (int64_t) (1 << 24)) >> 25; + h0 += carry9 * 19; + h9 -= carry9 << 25; + carry1 = (h1 + (int64_t) (1 << 24)) >> 25; + h2 += carry1; + h1 -= carry1 << 25; + carry3 = (h3 + (int64_t) (1 << 24)) >> 25; + h4 += carry3; + h3 -= carry3 << 25; + carry5 = (h5 + (int64_t) (1 << 24)) >> 25; + h6 += carry5; + h5 -= carry5 << 25; + carry7 = (h7 + (int64_t) (1 << 24)) >> 25; + h8 += carry7; + h7 -= carry7 << 25; + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + carry2 = (h2 + (int64_t) (1 << 25)) >> 26; + h3 += carry2; + h2 -= carry2 << 26; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry6 = (h6 + (int64_t) (1 << 25)) >> 26; + h7 += carry6; + h6 -= carry6 << 26; + carry8 = (h8 + (int64_t) (1 << 25)) >> 26; + h9 += carry8; + h8 -= carry8 << 26; + + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + + + +void fe_invert(fe out, const fe z) { + fe t0; + fe t1; + fe t2; + fe t3; + int i; + + fe_sq(t0, z); + + for (i = 1; i < 1; ++i) { + fe_sq(t0, t0); + } + + fe_sq(t1, t0); + + for (i = 1; i < 2; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t1, z, t1); + fe_mul(t0, t0, t1); + fe_sq(t2, t0); + + for (i = 1; i < 1; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t1, t2); + fe_sq(t2, t1); + + for (i = 1; i < 5; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t2, t1); + fe_sq(t2, t1); + + for (i = 1; i < 10; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t2, t2, t1); + fe_sq(t3, t2); + + for (i = 1; i < 20; ++i) { + fe_sq(t3, t3); + } + + fe_mul(t2, t3, t2); + fe_sq(t2, t2); + + for (i = 1; i < 10; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t2, t1); + fe_sq(t2, t1); + + for (i = 1; i < 50; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t2, t2, t1); + fe_sq(t3, t2); + + for (i = 1; i < 100; ++i) { + fe_sq(t3, t3); + } + + fe_mul(t2, t3, t2); + fe_sq(t2, t2); + + for (i = 1; i < 50; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t2, t1); + fe_sq(t1, t1); + + for (i = 1; i < 5; ++i) { + fe_sq(t1, t1); + } + + fe_mul(out, t1, t0); +} + + + +/* + return 1 if f is in {1,3,5,...,q-2} + return 0 if f is in {0,2,4,...,q-1} + + Preconditions: + |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +int fe_isnegative(const fe f) { + unsigned char s[32]; + + fe_tobytes(s, f); + + return s[0] & 1; +} + + + +/* + return 1 if f == 0 + return 0 if f != 0 + + Preconditions: + |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +int fe_isnonzero(const fe f) { + unsigned char s[32]; + unsigned char r; + + fe_tobytes(s, f); + + r = s[0]; + #define F(i) r |= s[i] + F(1); + F(2); + F(3); + F(4); + F(5); + F(6); + F(7); + F(8); + F(9); + F(10); + F(11); + F(12); + F(13); + F(14); + F(15); + F(16); + F(17); + F(18); + F(19); + F(20); + F(21); + F(22); + F(23); + F(24); + F(25); + F(26); + F(27); + F(28); + F(29); + F(30); + F(31); + #undef F + + return r != 0; +} + + + +/* + h = f * g + Can overlap h with f or g. + + Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + |g| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + + Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. + */ + + /* + Notes on implementation strategy: + + Using schoolbook multiplication. + Karatsuba would save a little in some cost models. + + Most multiplications by 2 and 19 are 32-bit precomputations; + cheaper than 64-bit postcomputations. + + There is one remaining multiplication by 19 in the carry chain; + one *19 precomputation can be merged into this, + but the resulting data flow is considerably less clean. + + There are 12 carries below. + 10 of them are 2-way parallelizable and vectorizable. + Can get away with 11 carries, but then data flow is much deeper. + + With tighter constraints on inputs can squeeze carries into int32. +*/ + +void fe_mul(fe h, const fe f, const fe g) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t g1_19 = 19 * g1; /* 1.959375*2^29 */ + int32_t g2_19 = 19 * g2; /* 1.959375*2^30; still ok */ + int32_t g3_19 = 19 * g3; + int32_t g4_19 = 19 * g4; + int32_t g5_19 = 19 * g5; + int32_t g6_19 = 19 * g6; + int32_t g7_19 = 19 * g7; + int32_t g8_19 = 19 * g8; + int32_t g9_19 = 19 * g9; + int32_t f1_2 = 2 * f1; + int32_t f3_2 = 2 * f3; + int32_t f5_2 = 2 * f5; + int32_t f7_2 = 2 * f7; + int32_t f9_2 = 2 * f9; + int64_t f0g0 = f0 * (int64_t) g0; + int64_t f0g1 = f0 * (int64_t) g1; + int64_t f0g2 = f0 * (int64_t) g2; + int64_t f0g3 = f0 * (int64_t) g3; + int64_t f0g4 = f0 * (int64_t) g4; + int64_t f0g5 = f0 * (int64_t) g5; + int64_t f0g6 = f0 * (int64_t) g6; + int64_t f0g7 = f0 * (int64_t) g7; + int64_t f0g8 = f0 * (int64_t) g8; + int64_t f0g9 = f0 * (int64_t) g9; + int64_t f1g0 = f1 * (int64_t) g0; + int64_t f1g1_2 = f1_2 * (int64_t) g1; + int64_t f1g2 = f1 * (int64_t) g2; + int64_t f1g3_2 = f1_2 * (int64_t) g3; + int64_t f1g4 = f1 * (int64_t) g4; + int64_t f1g5_2 = f1_2 * (int64_t) g5; + int64_t f1g6 = f1 * (int64_t) g6; + int64_t f1g7_2 = f1_2 * (int64_t) g7; + int64_t f1g8 = f1 * (int64_t) g8; + int64_t f1g9_38 = f1_2 * (int64_t) g9_19; + int64_t f2g0 = f2 * (int64_t) g0; + int64_t f2g1 = f2 * (int64_t) g1; + int64_t f2g2 = f2 * (int64_t) g2; + int64_t f2g3 = f2 * (int64_t) g3; + int64_t f2g4 = f2 * (int64_t) g4; + int64_t f2g5 = f2 * (int64_t) g5; + int64_t f2g6 = f2 * (int64_t) g6; + int64_t f2g7 = f2 * (int64_t) g7; + int64_t f2g8_19 = f2 * (int64_t) g8_19; + int64_t f2g9_19 = f2 * (int64_t) g9_19; + int64_t f3g0 = f3 * (int64_t) g0; + int64_t f3g1_2 = f3_2 * (int64_t) g1; + int64_t f3g2 = f3 * (int64_t) g2; + int64_t f3g3_2 = f3_2 * (int64_t) g3; + int64_t f3g4 = f3 * (int64_t) g4; + int64_t f3g5_2 = f3_2 * (int64_t) g5; + int64_t f3g6 = f3 * (int64_t) g6; + int64_t f3g7_38 = f3_2 * (int64_t) g7_19; + int64_t f3g8_19 = f3 * (int64_t) g8_19; + int64_t f3g9_38 = f3_2 * (int64_t) g9_19; + int64_t f4g0 = f4 * (int64_t) g0; + int64_t f4g1 = f4 * (int64_t) g1; + int64_t f4g2 = f4 * (int64_t) g2; + int64_t f4g3 = f4 * (int64_t) g3; + int64_t f4g4 = f4 * (int64_t) g4; + int64_t f4g5 = f4 * (int64_t) g5; + int64_t f4g6_19 = f4 * (int64_t) g6_19; + int64_t f4g7_19 = f4 * (int64_t) g7_19; + int64_t f4g8_19 = f4 * (int64_t) g8_19; + int64_t f4g9_19 = f4 * (int64_t) g9_19; + int64_t f5g0 = f5 * (int64_t) g0; + int64_t f5g1_2 = f5_2 * (int64_t) g1; + int64_t f5g2 = f5 * (int64_t) g2; + int64_t f5g3_2 = f5_2 * (int64_t) g3; + int64_t f5g4 = f5 * (int64_t) g4; + int64_t f5g5_38 = f5_2 * (int64_t) g5_19; + int64_t f5g6_19 = f5 * (int64_t) g6_19; + int64_t f5g7_38 = f5_2 * (int64_t) g7_19; + int64_t f5g8_19 = f5 * (int64_t) g8_19; + int64_t f5g9_38 = f5_2 * (int64_t) g9_19; + int64_t f6g0 = f6 * (int64_t) g0; + int64_t f6g1 = f6 * (int64_t) g1; + int64_t f6g2 = f6 * (int64_t) g2; + int64_t f6g3 = f6 * (int64_t) g3; + int64_t f6g4_19 = f6 * (int64_t) g4_19; + int64_t f6g5_19 = f6 * (int64_t) g5_19; + int64_t f6g6_19 = f6 * (int64_t) g6_19; + int64_t f6g7_19 = f6 * (int64_t) g7_19; + int64_t f6g8_19 = f6 * (int64_t) g8_19; + int64_t f6g9_19 = f6 * (int64_t) g9_19; + int64_t f7g0 = f7 * (int64_t) g0; + int64_t f7g1_2 = f7_2 * (int64_t) g1; + int64_t f7g2 = f7 * (int64_t) g2; + int64_t f7g3_38 = f7_2 * (int64_t) g3_19; + int64_t f7g4_19 = f7 * (int64_t) g4_19; + int64_t f7g5_38 = f7_2 * (int64_t) g5_19; + int64_t f7g6_19 = f7 * (int64_t) g6_19; + int64_t f7g7_38 = f7_2 * (int64_t) g7_19; + int64_t f7g8_19 = f7 * (int64_t) g8_19; + int64_t f7g9_38 = f7_2 * (int64_t) g9_19; + int64_t f8g0 = f8 * (int64_t) g0; + int64_t f8g1 = f8 * (int64_t) g1; + int64_t f8g2_19 = f8 * (int64_t) g2_19; + int64_t f8g3_19 = f8 * (int64_t) g3_19; + int64_t f8g4_19 = f8 * (int64_t) g4_19; + int64_t f8g5_19 = f8 * (int64_t) g5_19; + int64_t f8g6_19 = f8 * (int64_t) g6_19; + int64_t f8g7_19 = f8 * (int64_t) g7_19; + int64_t f8g8_19 = f8 * (int64_t) g8_19; + int64_t f8g9_19 = f8 * (int64_t) g9_19; + int64_t f9g0 = f9 * (int64_t) g0; + int64_t f9g1_38 = f9_2 * (int64_t) g1_19; + int64_t f9g2_19 = f9 * (int64_t) g2_19; + int64_t f9g3_38 = f9_2 * (int64_t) g3_19; + int64_t f9g4_19 = f9 * (int64_t) g4_19; + int64_t f9g5_38 = f9_2 * (int64_t) g5_19; + int64_t f9g6_19 = f9 * (int64_t) g6_19; + int64_t f9g7_38 = f9_2 * (int64_t) g7_19; + int64_t f9g8_19 = f9 * (int64_t) g8_19; + int64_t f9g9_38 = f9_2 * (int64_t) g9_19; + int64_t h0 = f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38; + int64_t h1 = f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19; + int64_t h2 = f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38; + int64_t h3 = f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19; + int64_t h4 = f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38; + int64_t h5 = f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19; + int64_t h6 = f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38; + int64_t h7 = f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19; + int64_t h8 = f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38; + int64_t h9 = f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0 ; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + + carry1 = (h1 + (int64_t) (1 << 24)) >> 25; + h2 += carry1; + h1 -= carry1 << 25; + carry5 = (h5 + (int64_t) (1 << 24)) >> 25; + h6 += carry5; + h5 -= carry5 << 25; + + carry2 = (h2 + (int64_t) (1 << 25)) >> 26; + h3 += carry2; + h2 -= carry2 << 26; + carry6 = (h6 + (int64_t) (1 << 25)) >> 26; + h7 += carry6; + h6 -= carry6 << 26; + + carry3 = (h3 + (int64_t) (1 << 24)) >> 25; + h4 += carry3; + h3 -= carry3 << 25; + carry7 = (h7 + (int64_t) (1 << 24)) >> 25; + h8 += carry7; + h7 -= carry7 << 25; + + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry8 = (h8 + (int64_t) (1 << 25)) >> 26; + h9 += carry8; + h8 -= carry8 << 26; + + carry9 = (h9 + (int64_t) (1 << 24)) >> 25; + h0 += carry9 * 19; + h9 -= carry9 << 25; + + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + + +/* +h = f * 121666 +Can overlap h with f. + +Preconditions: + |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + +Postconditions: + |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +*/ + +void fe_mul121666(fe h, fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int64_t h0 = f0 * (int64_t) 121666; + int64_t h1 = f1 * (int64_t) 121666; + int64_t h2 = f2 * (int64_t) 121666; + int64_t h3 = f3 * (int64_t) 121666; + int64_t h4 = f4 * (int64_t) 121666; + int64_t h5 = f5 * (int64_t) 121666; + int64_t h6 = f6 * (int64_t) 121666; + int64_t h7 = f7 * (int64_t) 121666; + int64_t h8 = f8 * (int64_t) 121666; + int64_t h9 = f9 * (int64_t) 121666; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + carry9 = (h9 + (int64_t) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + carry1 = (h1 + (int64_t) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry3 = (h3 + (int64_t) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry5 = (h5 + (int64_t) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + carry7 = (h7 + (int64_t) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry0 = (h0 + (int64_t) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry2 = (h2 + (int64_t) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry4 = (h4 + (int64_t) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry6 = (h6 + (int64_t) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + carry8 = (h8 + (int64_t) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} + + +/* +h = -f + +Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + +Postconditions: + |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +*/ + +void fe_neg(fe h, const fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t h0 = -f0; + int32_t h1 = -f1; + int32_t h2 = -f2; + int32_t h3 = -f3; + int32_t h4 = -f4; + int32_t h5 = -f5; + int32_t h6 = -f6; + int32_t h7 = -f7; + int32_t h8 = -f8; + int32_t h9 = -f9; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} + + +void fe_pow22523(fe out, const fe z) { + fe t0; + fe t1; + fe t2; + int i; + fe_sq(t0, z); + + for (i = 1; i < 1; ++i) { + fe_sq(t0, t0); + } + + fe_sq(t1, t0); + + for (i = 1; i < 2; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t1, z, t1); + fe_mul(t0, t0, t1); + fe_sq(t0, t0); + + for (i = 1; i < 1; ++i) { + fe_sq(t0, t0); + } + + fe_mul(t0, t1, t0); + fe_sq(t1, t0); + + for (i = 1; i < 5; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t0, t1, t0); + fe_sq(t1, t0); + + for (i = 1; i < 10; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t1, t1, t0); + fe_sq(t2, t1); + + for (i = 1; i < 20; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t2, t1); + fe_sq(t1, t1); + + for (i = 1; i < 10; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t0, t1, t0); + fe_sq(t1, t0); + + for (i = 1; i < 50; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t1, t1, t0); + fe_sq(t2, t1); + + for (i = 1; i < 100; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t2, t1); + fe_sq(t1, t1); + + for (i = 1; i < 50; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t0, t1, t0); + fe_sq(t0, t0); + + for (i = 1; i < 2; ++i) { + fe_sq(t0, t0); + } + + fe_mul(out, t0, z); + return; +} + + +/* +h = f * f +Can overlap h with f. + +Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + +Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. +*/ + +/* +See fe_mul.c for discussion of implementation strategy. +*/ + +void fe_sq(fe h, const fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t f0_2 = 2 * f0; + int32_t f1_2 = 2 * f1; + int32_t f2_2 = 2 * f2; + int32_t f3_2 = 2 * f3; + int32_t f4_2 = 2 * f4; + int32_t f5_2 = 2 * f5; + int32_t f6_2 = 2 * f6; + int32_t f7_2 = 2 * f7; + int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */ + int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */ + int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */ + int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */ + int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */ + int64_t f0f0 = f0 * (int64_t) f0; + int64_t f0f1_2 = f0_2 * (int64_t) f1; + int64_t f0f2_2 = f0_2 * (int64_t) f2; + int64_t f0f3_2 = f0_2 * (int64_t) f3; + int64_t f0f4_2 = f0_2 * (int64_t) f4; + int64_t f0f5_2 = f0_2 * (int64_t) f5; + int64_t f0f6_2 = f0_2 * (int64_t) f6; + int64_t f0f7_2 = f0_2 * (int64_t) f7; + int64_t f0f8_2 = f0_2 * (int64_t) f8; + int64_t f0f9_2 = f0_2 * (int64_t) f9; + int64_t f1f1_2 = f1_2 * (int64_t) f1; + int64_t f1f2_2 = f1_2 * (int64_t) f2; + int64_t f1f3_4 = f1_2 * (int64_t) f3_2; + int64_t f1f4_2 = f1_2 * (int64_t) f4; + int64_t f1f5_4 = f1_2 * (int64_t) f5_2; + int64_t f1f6_2 = f1_2 * (int64_t) f6; + int64_t f1f7_4 = f1_2 * (int64_t) f7_2; + int64_t f1f8_2 = f1_2 * (int64_t) f8; + int64_t f1f9_76 = f1_2 * (int64_t) f9_38; + int64_t f2f2 = f2 * (int64_t) f2; + int64_t f2f3_2 = f2_2 * (int64_t) f3; + int64_t f2f4_2 = f2_2 * (int64_t) f4; + int64_t f2f5_2 = f2_2 * (int64_t) f5; + int64_t f2f6_2 = f2_2 * (int64_t) f6; + int64_t f2f7_2 = f2_2 * (int64_t) f7; + int64_t f2f8_38 = f2_2 * (int64_t) f8_19; + int64_t f2f9_38 = f2 * (int64_t) f9_38; + int64_t f3f3_2 = f3_2 * (int64_t) f3; + int64_t f3f4_2 = f3_2 * (int64_t) f4; + int64_t f3f5_4 = f3_2 * (int64_t) f5_2; + int64_t f3f6_2 = f3_2 * (int64_t) f6; + int64_t f3f7_76 = f3_2 * (int64_t) f7_38; + int64_t f3f8_38 = f3_2 * (int64_t) f8_19; + int64_t f3f9_76 = f3_2 * (int64_t) f9_38; + int64_t f4f4 = f4 * (int64_t) f4; + int64_t f4f5_2 = f4_2 * (int64_t) f5; + int64_t f4f6_38 = f4_2 * (int64_t) f6_19; + int64_t f4f7_38 = f4 * (int64_t) f7_38; + int64_t f4f8_38 = f4_2 * (int64_t) f8_19; + int64_t f4f9_38 = f4 * (int64_t) f9_38; + int64_t f5f5_38 = f5 * (int64_t) f5_38; + int64_t f5f6_38 = f5_2 * (int64_t) f6_19; + int64_t f5f7_76 = f5_2 * (int64_t) f7_38; + int64_t f5f8_38 = f5_2 * (int64_t) f8_19; + int64_t f5f9_76 = f5_2 * (int64_t) f9_38; + int64_t f6f6_19 = f6 * (int64_t) f6_19; + int64_t f6f7_38 = f6 * (int64_t) f7_38; + int64_t f6f8_38 = f6_2 * (int64_t) f8_19; + int64_t f6f9_38 = f6 * (int64_t) f9_38; + int64_t f7f7_38 = f7 * (int64_t) f7_38; + int64_t f7f8_38 = f7_2 * (int64_t) f8_19; + int64_t f7f9_76 = f7_2 * (int64_t) f9_38; + int64_t f8f8_19 = f8 * (int64_t) f8_19; + int64_t f8f9_38 = f8 * (int64_t) f9_38; + int64_t f9f9_38 = f9 * (int64_t) f9_38; + int64_t h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38; + int64_t h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38; + int64_t h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19; + int64_t h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38; + int64_t h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38; + int64_t h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38; + int64_t h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19; + int64_t h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38; + int64_t h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38; + int64_t h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry1 = (h1 + (int64_t) (1 << 24)) >> 25; + h2 += carry1; + h1 -= carry1 << 25; + carry5 = (h5 + (int64_t) (1 << 24)) >> 25; + h6 += carry5; + h5 -= carry5 << 25; + carry2 = (h2 + (int64_t) (1 << 25)) >> 26; + h3 += carry2; + h2 -= carry2 << 26; + carry6 = (h6 + (int64_t) (1 << 25)) >> 26; + h7 += carry6; + h6 -= carry6 << 26; + carry3 = (h3 + (int64_t) (1 << 24)) >> 25; + h4 += carry3; + h3 -= carry3 << 25; + carry7 = (h7 + (int64_t) (1 << 24)) >> 25; + h8 += carry7; + h7 -= carry7 << 25; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry8 = (h8 + (int64_t) (1 << 25)) >> 26; + h9 += carry8; + h8 -= carry8 << 26; + carry9 = (h9 + (int64_t) (1 << 24)) >> 25; + h0 += carry9 * 19; + h9 -= carry9 << 25; + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + + +/* +h = 2 * f * f +Can overlap h with f. + +Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + +Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. +*/ + +/* +See fe_mul.c for discussion of implementation strategy. +*/ + +void fe_sq2(fe h, const fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t f0_2 = 2 * f0; + int32_t f1_2 = 2 * f1; + int32_t f2_2 = 2 * f2; + int32_t f3_2 = 2 * f3; + int32_t f4_2 = 2 * f4; + int32_t f5_2 = 2 * f5; + int32_t f6_2 = 2 * f6; + int32_t f7_2 = 2 * f7; + int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */ + int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */ + int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */ + int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */ + int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */ + int64_t f0f0 = f0 * (int64_t) f0; + int64_t f0f1_2 = f0_2 * (int64_t) f1; + int64_t f0f2_2 = f0_2 * (int64_t) f2; + int64_t f0f3_2 = f0_2 * (int64_t) f3; + int64_t f0f4_2 = f0_2 * (int64_t) f4; + int64_t f0f5_2 = f0_2 * (int64_t) f5; + int64_t f0f6_2 = f0_2 * (int64_t) f6; + int64_t f0f7_2 = f0_2 * (int64_t) f7; + int64_t f0f8_2 = f0_2 * (int64_t) f8; + int64_t f0f9_2 = f0_2 * (int64_t) f9; + int64_t f1f1_2 = f1_2 * (int64_t) f1; + int64_t f1f2_2 = f1_2 * (int64_t) f2; + int64_t f1f3_4 = f1_2 * (int64_t) f3_2; + int64_t f1f4_2 = f1_2 * (int64_t) f4; + int64_t f1f5_4 = f1_2 * (int64_t) f5_2; + int64_t f1f6_2 = f1_2 * (int64_t) f6; + int64_t f1f7_4 = f1_2 * (int64_t) f7_2; + int64_t f1f8_2 = f1_2 * (int64_t) f8; + int64_t f1f9_76 = f1_2 * (int64_t) f9_38; + int64_t f2f2 = f2 * (int64_t) f2; + int64_t f2f3_2 = f2_2 * (int64_t) f3; + int64_t f2f4_2 = f2_2 * (int64_t) f4; + int64_t f2f5_2 = f2_2 * (int64_t) f5; + int64_t f2f6_2 = f2_2 * (int64_t) f6; + int64_t f2f7_2 = f2_2 * (int64_t) f7; + int64_t f2f8_38 = f2_2 * (int64_t) f8_19; + int64_t f2f9_38 = f2 * (int64_t) f9_38; + int64_t f3f3_2 = f3_2 * (int64_t) f3; + int64_t f3f4_2 = f3_2 * (int64_t) f4; + int64_t f3f5_4 = f3_2 * (int64_t) f5_2; + int64_t f3f6_2 = f3_2 * (int64_t) f6; + int64_t f3f7_76 = f3_2 * (int64_t) f7_38; + int64_t f3f8_38 = f3_2 * (int64_t) f8_19; + int64_t f3f9_76 = f3_2 * (int64_t) f9_38; + int64_t f4f4 = f4 * (int64_t) f4; + int64_t f4f5_2 = f4_2 * (int64_t) f5; + int64_t f4f6_38 = f4_2 * (int64_t) f6_19; + int64_t f4f7_38 = f4 * (int64_t) f7_38; + int64_t f4f8_38 = f4_2 * (int64_t) f8_19; + int64_t f4f9_38 = f4 * (int64_t) f9_38; + int64_t f5f5_38 = f5 * (int64_t) f5_38; + int64_t f5f6_38 = f5_2 * (int64_t) f6_19; + int64_t f5f7_76 = f5_2 * (int64_t) f7_38; + int64_t f5f8_38 = f5_2 * (int64_t) f8_19; + int64_t f5f9_76 = f5_2 * (int64_t) f9_38; + int64_t f6f6_19 = f6 * (int64_t) f6_19; + int64_t f6f7_38 = f6 * (int64_t) f7_38; + int64_t f6f8_38 = f6_2 * (int64_t) f8_19; + int64_t f6f9_38 = f6 * (int64_t) f9_38; + int64_t f7f7_38 = f7 * (int64_t) f7_38; + int64_t f7f8_38 = f7_2 * (int64_t) f8_19; + int64_t f7f9_76 = f7_2 * (int64_t) f9_38; + int64_t f8f8_19 = f8 * (int64_t) f8_19; + int64_t f8f9_38 = f8 * (int64_t) f9_38; + int64_t f9f9_38 = f9 * (int64_t) f9_38; + int64_t h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38; + int64_t h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38; + int64_t h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19; + int64_t h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38; + int64_t h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38; + int64_t h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38; + int64_t h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19; + int64_t h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38; + int64_t h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38; + int64_t h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + h0 += h0; + h1 += h1; + h2 += h2; + h3 += h3; + h4 += h4; + h5 += h5; + h6 += h6; + h7 += h7; + h8 += h8; + h9 += h9; + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry1 = (h1 + (int64_t) (1 << 24)) >> 25; + h2 += carry1; + h1 -= carry1 << 25; + carry5 = (h5 + (int64_t) (1 << 24)) >> 25; + h6 += carry5; + h5 -= carry5 << 25; + carry2 = (h2 + (int64_t) (1 << 25)) >> 26; + h3 += carry2; + h2 -= carry2 << 26; + carry6 = (h6 + (int64_t) (1 << 25)) >> 26; + h7 += carry6; + h6 -= carry6 << 26; + carry3 = (h3 + (int64_t) (1 << 24)) >> 25; + h4 += carry3; + h3 -= carry3 << 25; + carry7 = (h7 + (int64_t) (1 << 24)) >> 25; + h8 += carry7; + h7 -= carry7 << 25; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry8 = (h8 + (int64_t) (1 << 25)) >> 26; + h9 += carry8; + h8 -= carry8 << 26; + carry9 = (h9 + (int64_t) (1 << 24)) >> 25; + h0 += carry9 * 19; + h9 -= carry9 << 25; + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + + +/* +h = f - g +Can overlap h with f or g. + +Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + +Postconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +void fe_sub(fe h, const fe f, const fe g) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t h0 = f0 - g0; + int32_t h1 = f1 - g1; + int32_t h2 = f2 - g2; + int32_t h3 = f3 - g3; + int32_t h4 = f4 - g4; + int32_t h5 = f5 - g5; + int32_t h6 = f6 - g6; + int32_t h7 = f7 - g7; + int32_t h8 = f8 - g8; + int32_t h9 = f9 - g9; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} + + + +/* +Preconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + +Write p=2^255-19; q=floor(h/p). +Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). + +Proof: + Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. + Also have |h-2^230 h9|<2^231 so |19 2^(-255)(h-2^230 h9)|<1/4. + + Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). + Then 0> 25; + q = (h0 + q) >> 26; + q = (h1 + q) >> 25; + q = (h2 + q) >> 26; + q = (h3 + q) >> 25; + q = (h4 + q) >> 26; + q = (h5 + q) >> 25; + q = (h6 + q) >> 26; + q = (h7 + q) >> 25; + q = (h8 + q) >> 26; + q = (h9 + q) >> 25; + /* Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. */ + h0 += 19 * q; + /* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */ + carry0 = h0 >> 26; + h1 += carry0; + h0 -= carry0 << 26; + carry1 = h1 >> 25; + h2 += carry1; + h1 -= carry1 << 25; + carry2 = h2 >> 26; + h3 += carry2; + h2 -= carry2 << 26; + carry3 = h3 >> 25; + h4 += carry3; + h3 -= carry3 << 25; + carry4 = h4 >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry5 = h5 >> 25; + h6 += carry5; + h5 -= carry5 << 25; + carry6 = h6 >> 26; + h7 += carry6; + h6 -= carry6 << 26; + carry7 = h7 >> 25; + h8 += carry7; + h7 -= carry7 << 25; + carry8 = h8 >> 26; + h9 += carry8; + h8 -= carry8 << 26; + carry9 = h9 >> 25; + h9 -= carry9 << 25; + + /* h10 = carry9 */ + /* + Goal: Output h0+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. + Have h0+...+2^230 h9 between 0 and 2^255-1; + evidently 2^255 h10-2^255 q = 0. + Goal: Output h0+...+2^230 h9. + */ + s[0] = (unsigned char) ((h0 >> 0) & 0xff); + s[1] = (unsigned char) ((h0 >> 8) & 0xff); + s[2] = (unsigned char) ((h0 >> 16) & 0xff); + s[3] = (unsigned char) (((h0 >> 24) | (h1 << 2)) & 0xff); + s[4] = (unsigned char) ((h1 >> 6) & 0xff); + s[5] = (unsigned char) ((h1 >> 14) & 0xff); + s[6] = (unsigned char) (((h1 >> 22) | (h2 << 3)) & 0xff); + s[7] = (unsigned char) ((h2 >> 5) & 0xff); + s[8] = (unsigned char) ((h2 >> 13) & 0xff); + s[9] = (unsigned char) (((h2 >> 21) | (h3 << 5)) & 0xff); + s[10] = (unsigned char) ((h3 >> 3) & 0xff); + s[11] = (unsigned char) ((h3 >> 11) & 0xff); + s[12] = (unsigned char) (((h3 >> 19) | (h4 << 6)) & 0xff); + s[13] = (unsigned char) ((h4 >> 2) & 0xff); + s[14] = (unsigned char) ((h4 >> 10) & 0xff); + s[15] = (unsigned char) ((h4 >> 18) & 0xff); + s[16] = (unsigned char) ((h5 >> 0) & 0xff); + s[17] = (unsigned char) ((h5 >> 8) & 0xff); + s[18] = (unsigned char) ((h5 >> 16) & 0xff); + s[19] = (unsigned char) (((h5 >> 24) | (h6 << 1)) & 0xff); + s[20] = (unsigned char) ((h6 >> 7) & 0xff); + s[21] = (unsigned char) ((h6 >> 15) & 0xff); + s[22] = (unsigned char) (((h6 >> 23) | (h7 << 3)) & 0xff); + s[23] = (unsigned char) ((h7 >> 5) & 0xff); + s[24] = (unsigned char) ((h7 >> 13) & 0xff); + s[25] = (unsigned char) (((h7 >> 21) | (h8 << 4)) & 0xff); + s[26] = (unsigned char) ((h8 >> 4) & 0xff); + s[27] = (unsigned char) ((h8 >> 12) & 0xff); + s[28] = (unsigned char) (((h8 >> 20) | (h9 << 6)) & 0xff); + s[29] = (unsigned char) ((h9 >> 2) & 0xff); + s[30] = (unsigned char) ((h9 >> 10) & 0xff); + s[31] = (unsigned char) ((h9 >> 18) & 0xff); +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/fe.h b/apps/Launcher/ext/libtorrent/ed25519/src/fe.h new file mode 100644 index 0000000000..b4b62d2828 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/fe.h @@ -0,0 +1,41 @@ +#ifndef FE_H +#define FE_H + +#include "fixedint.h" + + +/* + fe means field element. + Here the field is \Z/(2^255-19). + An element t, entries t[0]...t[9], represents the integer + t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9]. + Bounds on each t[i] vary depending on context. +*/ + + +typedef int32_t fe[10]; + + +void fe_0(fe h); +void fe_1(fe h); + +void fe_frombytes(fe h, const unsigned char *s); +void fe_tobytes(unsigned char *s, const fe h); + +void fe_copy(fe h, const fe f); +int fe_isnegative(const fe f); +int fe_isnonzero(const fe f); +void fe_cmov(fe f, const fe g, unsigned int b); +void fe_cswap(fe f, fe g, unsigned int b); + +void fe_neg(fe h, const fe f); +void fe_add(fe h, const fe f, const fe g); +void fe_invert(fe out, const fe z); +void fe_sq(fe h, const fe f); +void fe_sq2(fe h, const fe f); +void fe_mul(fe h, const fe f, const fe g); +void fe_mul121666(fe h, fe f); +void fe_pow22523(fe out, const fe z); +void fe_sub(fe h, const fe f, const fe g); + +#endif diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/fixedint.h b/apps/Launcher/ext/libtorrent/ed25519/src/fixedint.h new file mode 100644 index 0000000000..c7f608c4b8 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/fixedint.h @@ -0,0 +1,6 @@ +#include + +using boost::uint64_t; +using boost::int64_t; +using boost::int32_t; + diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/ge.cpp b/apps/Launcher/ext/libtorrent/ed25519/src/ge.cpp new file mode 100644 index 0000000000..3c342b1d21 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/ge.cpp @@ -0,0 +1,467 @@ +#include "ge.h" +#include "precomp_data.h" + + +/* +r = p + q +*/ + +void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { + fe t0; + fe_add(r->X, p->Y, p->X); + fe_sub(r->Y, p->Y, p->X); + fe_mul(r->Z, r->X, q->YplusX); + fe_mul(r->Y, r->Y, q->YminusX); + fe_mul(r->T, q->T2d, p->T); + fe_mul(r->X, p->Z, q->Z); + fe_add(t0, r->X, r->X); + fe_sub(r->X, r->Z, r->Y); + fe_add(r->Y, r->Z, r->Y); + fe_add(r->Z, t0, r->T); + fe_sub(r->T, t0, r->T); +} + + +static void slide(signed char *r, const unsigned char *a) { + int i; + int b; + int k; + + for (i = 0; i < 256; ++i) { + r[i] = 1 & (a[i >> 3] >> (i & 7)); + } + + for (i = 0; i < 256; ++i) + if (r[i]) { + for (b = 1; b <= 6 && i + b < 256; ++b) { + if (r[i + b]) { + if (r[i] + (r[i + b] << b) <= 15) { + r[i] += r[i + b] << b; + r[i + b] = 0; + } else if (r[i] - (r[i + b] << b) >= -15) { + r[i] -= r[i + b] << b; + + for (k = i + b; k < 256; ++k) { + if (!r[k]) { + r[k] = 1; + break; + } + + r[k] = 0; + } + } else { + break; + } + } + } + } +} + +/* +r = a * A + b * B +where a = a[0]+256*a[1]+...+256^31 a[31]. +and b = b[0]+256*b[1]+...+256^31 b[31]. +B is the Ed25519 base point (x,4/5) with x positive. +*/ + +void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b) { + signed char aslide[256]; + signed char bslide[256]; + ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */ + ge_p1p1 t; + ge_p3 u; + ge_p3 A2; + int i; + slide(aslide, a); + slide(bslide, b); + ge_p3_to_cached(&Ai[0], A); + ge_p3_dbl(&t, A); + ge_p1p1_to_p3(&A2, &t); + ge_add(&t, &A2, &Ai[0]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[1], &u); + ge_add(&t, &A2, &Ai[1]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[2], &u); + ge_add(&t, &A2, &Ai[2]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[3], &u); + ge_add(&t, &A2, &Ai[3]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[4], &u); + ge_add(&t, &A2, &Ai[4]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[5], &u); + ge_add(&t, &A2, &Ai[5]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[6], &u); + ge_add(&t, &A2, &Ai[6]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[7], &u); + ge_p2_0(r); + + for (i = 255; i >= 0; --i) { + if (aslide[i] || bslide[i]) { + break; + } + } + + for (; i >= 0; --i) { + ge_p2_dbl(&t, r); + + if (aslide[i] > 0) { + ge_p1p1_to_p3(&u, &t); + ge_add(&t, &u, &Ai[aslide[i] / 2]); + } else if (aslide[i] < 0) { + ge_p1p1_to_p3(&u, &t); + ge_sub(&t, &u, &Ai[(-aslide[i]) / 2]); + } + + if (bslide[i] > 0) { + ge_p1p1_to_p3(&u, &t); + ge_madd(&t, &u, &Bi[bslide[i] / 2]); + } else if (bslide[i] < 0) { + ge_p1p1_to_p3(&u, &t); + ge_msub(&t, &u, &Bi[(-bslide[i]) / 2]); + } + + ge_p1p1_to_p2(r, &t); + } +} + + +static const fe d = { + -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116 +}; + +static const fe sqrtm1 = { + -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482 +}; + +int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s) { + fe u; + fe v; + fe v3; + fe vxx; + fe check; + fe_frombytes(h->Y, s); + fe_1(h->Z); + fe_sq(u, h->Y); + fe_mul(v, u, d); + fe_sub(u, u, h->Z); /* u = y^2-1 */ + fe_add(v, v, h->Z); /* v = dy^2+1 */ + fe_sq(v3, v); + fe_mul(v3, v3, v); /* v3 = v^3 */ + fe_sq(h->X, v3); + fe_mul(h->X, h->X, v); + fe_mul(h->X, h->X, u); /* x = uv^7 */ + fe_pow22523(h->X, h->X); /* x = (uv^7)^((q-5)/8) */ + fe_mul(h->X, h->X, v3); + fe_mul(h->X, h->X, u); /* x = uv^3(uv^7)^((q-5)/8) */ + fe_sq(vxx, h->X); + fe_mul(vxx, vxx, v); + fe_sub(check, vxx, u); /* vx^2-u */ + + if (fe_isnonzero(check)) { + fe_add(check, vxx, u); /* vx^2+u */ + + if (fe_isnonzero(check)) { + return -1; + } + + fe_mul(h->X, h->X, sqrtm1); + } + + if (fe_isnegative(h->X) == (s[31] >> 7)) { + fe_neg(h->X, h->X); + } + + fe_mul(h->T, h->X, h->Y); + return 0; +} + + +/* +r = p + q +*/ + +void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) { + fe t0; + fe_add(r->X, p->Y, p->X); + fe_sub(r->Y, p->Y, p->X); + fe_mul(r->Z, r->X, q->yplusx); + fe_mul(r->Y, r->Y, q->yminusx); + fe_mul(r->T, q->xy2d, p->T); + fe_add(t0, p->Z, p->Z); + fe_sub(r->X, r->Z, r->Y); + fe_add(r->Y, r->Z, r->Y); + fe_add(r->Z, t0, r->T); + fe_sub(r->T, t0, r->T); +} + + +/* +r = p - q +*/ + +void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) { + fe t0; + + fe_add(r->X, p->Y, p->X); + fe_sub(r->Y, p->Y, p->X); + fe_mul(r->Z, r->X, q->yminusx); + fe_mul(r->Y, r->Y, q->yplusx); + fe_mul(r->T, q->xy2d, p->T); + fe_add(t0, p->Z, p->Z); + fe_sub(r->X, r->Z, r->Y); + fe_add(r->Y, r->Z, r->Y); + fe_sub(r->Z, t0, r->T); + fe_add(r->T, t0, r->T); +} + + +/* +r = p +*/ + +void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p) { + fe_mul(r->X, p->X, p->T); + fe_mul(r->Y, p->Y, p->Z); + fe_mul(r->Z, p->Z, p->T); +} + + + +/* +r = p +*/ + +void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p) { + fe_mul(r->X, p->X, p->T); + fe_mul(r->Y, p->Y, p->Z); + fe_mul(r->Z, p->Z, p->T); + fe_mul(r->T, p->X, p->Y); +} + + +void ge_p2_0(ge_p2 *h) { + fe_0(h->X); + fe_1(h->Y); + fe_1(h->Z); +} + + + +/* +r = 2 * p +*/ + +void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p) { + fe t0; + + fe_sq(r->X, p->X); + fe_sq(r->Z, p->Y); + fe_sq2(r->T, p->Z); + fe_add(r->Y, p->X, p->Y); + fe_sq(t0, r->Y); + fe_add(r->Y, r->Z, r->X); + fe_sub(r->Z, r->Z, r->X); + fe_sub(r->X, t0, r->Y); + fe_sub(r->T, r->T, r->Z); +} + + +void ge_p3_0(ge_p3 *h) { + fe_0(h->X); + fe_1(h->Y); + fe_1(h->Z); + fe_0(h->T); +} + + +/* +r = 2 * p +*/ + +void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p) { + ge_p2 q; + ge_p3_to_p2(&q, p); + ge_p2_dbl(r, &q); +} + + + +/* +r = p +*/ + +static const fe d2 = { + -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199 +}; + +void ge_p3_to_cached(ge_cached *r, const ge_p3 *p) { + fe_add(r->YplusX, p->Y, p->X); + fe_sub(r->YminusX, p->Y, p->X); + fe_copy(r->Z, p->Z); + fe_mul(r->T2d, p->T, d2); +} + + +/* +r = p +*/ + +void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p) { + fe_copy(r->X, p->X); + fe_copy(r->Y, p->Y); + fe_copy(r->Z, p->Z); +} + + +void ge_p3_tobytes(unsigned char *s, const ge_p3 *h) { + fe recip; + fe x; + fe y; + fe_invert(recip, h->Z); + fe_mul(x, h->X, recip); + fe_mul(y, h->Y, recip); + fe_tobytes(s, y); + s[31] ^= fe_isnegative(x) << 7; +} + + +static unsigned char equal(signed char b, signed char c) { + unsigned char ub = b; + unsigned char uc = c; + unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */ + uint64_t y = x; /* 0: yes; 1..255: no */ + y -= 1; /* large: yes; 0..254: no */ + y >>= 63; /* 1: yes; 0: no */ + return (unsigned char) y; +} + +static unsigned char negative(signed char b) { + uint64_t x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */ + x >>= 63; /* 1: yes; 0: no */ + return (unsigned char) x; +} + +static void cmov(ge_precomp *t, ge_precomp *u, unsigned char b) { + fe_cmov(t->yplusx, u->yplusx, b); + fe_cmov(t->yminusx, u->yminusx, b); + fe_cmov(t->xy2d, u->xy2d, b); +} + + +static void select(ge_precomp *t, int pos, signed char b) { + ge_precomp minust; + unsigned char bnegative = negative(b); + unsigned char babs = b - (((-bnegative) & b) << 1); + fe_1(t->yplusx); + fe_1(t->yminusx); + fe_0(t->xy2d); + cmov(t, &base[pos][0], equal(babs, 1)); + cmov(t, &base[pos][1], equal(babs, 2)); + cmov(t, &base[pos][2], equal(babs, 3)); + cmov(t, &base[pos][3], equal(babs, 4)); + cmov(t, &base[pos][4], equal(babs, 5)); + cmov(t, &base[pos][5], equal(babs, 6)); + cmov(t, &base[pos][6], equal(babs, 7)); + cmov(t, &base[pos][7], equal(babs, 8)); + fe_copy(minust.yplusx, t->yminusx); + fe_copy(minust.yminusx, t->yplusx); + fe_neg(minust.xy2d, t->xy2d); + cmov(t, &minust, bnegative); +} + +/* +h = a * B +where a = a[0]+256*a[1]+...+256^31 a[31] +B is the Ed25519 base point (x,4/5) with x positive. + +Preconditions: + a[31] <= 127 +*/ + +void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) { + signed char e[64]; + signed char carry; + ge_p1p1 r; + ge_p2 s; + ge_precomp t; + int i; + + for (i = 0; i < 32; ++i) { + e[2 * i + 0] = (a[i] >> 0) & 15; + e[2 * i + 1] = (a[i] >> 4) & 15; + } + + /* each e[i] is between 0 and 15 */ + /* e[63] is between 0 and 7 */ + carry = 0; + + for (i = 0; i < 63; ++i) { + e[i] += carry; + carry = e[i] + 8; + carry >>= 4; + e[i] -= carry << 4; + } + + e[63] += carry; + /* each e[i] is between -8 and 8 */ + ge_p3_0(h); + + for (i = 1; i < 64; i += 2) { + select(&t, i / 2, e[i]); + ge_madd(&r, h, &t); + ge_p1p1_to_p3(h, &r); + } + + ge_p3_dbl(&r, h); + ge_p1p1_to_p2(&s, &r); + ge_p2_dbl(&r, &s); + ge_p1p1_to_p2(&s, &r); + ge_p2_dbl(&r, &s); + ge_p1p1_to_p2(&s, &r); + ge_p2_dbl(&r, &s); + ge_p1p1_to_p3(h, &r); + + for (i = 0; i < 64; i += 2) { + select(&t, i / 2, e[i]); + ge_madd(&r, h, &t); + ge_p1p1_to_p3(h, &r); + } +} + + +/* +r = p - q +*/ + +void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { + fe t0; + + fe_add(r->X, p->Y, p->X); + fe_sub(r->Y, p->Y, p->X); + fe_mul(r->Z, r->X, q->YminusX); + fe_mul(r->Y, r->Y, q->YplusX); + fe_mul(r->T, q->T2d, p->T); + fe_mul(r->X, p->Z, q->Z); + fe_add(t0, r->X, r->X); + fe_sub(r->X, r->Z, r->Y); + fe_add(r->Y, r->Z, r->Y); + fe_sub(r->Z, t0, r->T); + fe_add(r->T, t0, r->T); +} + + +void ge_tobytes(unsigned char *s, const ge_p2 *h) { + fe recip; + fe x; + fe y; + fe_invert(recip, h->Z); + fe_mul(x, h->X, recip); + fe_mul(y, h->Y, recip); + fe_tobytes(s, y); + s[31] ^= fe_isnegative(x) << 7; +} diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/ge.h b/apps/Launcher/ext/libtorrent/ed25519/src/ge.h new file mode 100644 index 0000000000..17fde2df1f --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/ge.h @@ -0,0 +1,74 @@ +#ifndef GE_H +#define GE_H + +#include "fe.h" + + +/* +ge means group element. + +Here the group is the set of pairs (x,y) of field elements (see fe.h) +satisfying -x^2 + y^2 = 1 + d x^2y^2 +where d = -121665/121666. + +Representations: + ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z + ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT + ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T + ge_precomp (Duif): (y+x,y-x,2dxy) +*/ + +typedef struct { + fe X; + fe Y; + fe Z; +} ge_p2; + +typedef struct { + fe X; + fe Y; + fe Z; + fe T; +} ge_p3; + +typedef struct { + fe X; + fe Y; + fe Z; + fe T; +} ge_p1p1; + +typedef struct { + fe yplusx; + fe yminusx; + fe xy2d; +} ge_precomp; + +typedef struct { + fe YplusX; + fe YminusX; + fe Z; + fe T2d; +} ge_cached; + +void ge_p3_tobytes(unsigned char *s, const ge_p3 *h); +void ge_tobytes(unsigned char *s, const ge_p2 *h); +int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s); + +void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q); +void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q); +void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b); +void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q); +void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q); +void ge_scalarmult_base(ge_p3 *h, const unsigned char *a); + +void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p); +void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p); +void ge_p2_0(ge_p2 *h); +void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p); +void ge_p3_0(ge_p3 *h); +void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p); +void ge_p3_to_cached(ge_cached *r, const ge_p3 *p); +void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p); + +#endif diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/key_exchange.cpp b/apps/Launcher/ext/libtorrent/ed25519/src/key_exchange.cpp new file mode 100644 index 0000000000..b0b0907582 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/key_exchange.cpp @@ -0,0 +1,80 @@ +#include "libtorrent/ed25519.hpp" +#include "fe.h" + +void ed25519_key_exchange(unsigned char *shared_secret + , const unsigned char *public_key, const unsigned char *private_key) { + unsigned char e[32]; + unsigned int i; + + fe x1; + fe x2; + fe z2; + fe x3; + fe z3; + fe tmp0; + fe tmp1; + + int pos; + unsigned int swap; + unsigned int b; + + /* copy the private key and make sure it's valid */ + for (i = 0; i < 32; ++i) { + e[i] = private_key[i]; + } + + e[0] &= 248; + e[31] &= 63; + e[31] |= 64; + + /* unpack the public key and convert edwards to montgomery */ + /* due to CodesInChaos: montgomeryX = (edwardsY + 1)*inverse(1 - edwardsY) mod p */ + fe_frombytes(x1, public_key); + fe_1(tmp1); + fe_add(tmp0, x1, tmp1); + fe_sub(tmp1, tmp1, x1); + fe_invert(tmp1, tmp1); + fe_mul(x1, tmp0, tmp1); + + fe_1(x2); + fe_0(z2); + fe_copy(x3, x1); + fe_1(z3); + + swap = 0; + for (pos = 254; pos >= 0; --pos) { + b = e[pos / 8] >> (pos & 7); + b &= 1; + swap ^= b; + fe_cswap(x2, x3, swap); + fe_cswap(z2, z3, swap); + swap = b; + + /* from montgomery.h */ + fe_sub(tmp0, x3, z3); + fe_sub(tmp1, x2, z2); + fe_add(x2, x2, z2); + fe_add(z2, x3, z3); + fe_mul(z3, tmp0, x2); + fe_mul(z2, z2, tmp1); + fe_sq(tmp0, tmp1); + fe_sq(tmp1, x2); + fe_add(x3, z3, z2); + fe_sub(z2, z3, z2); + fe_mul(x2, tmp1, tmp0); + fe_sub(tmp1, tmp1, tmp0); + fe_sq(z2, z2); + fe_mul121666(z3, tmp1); + fe_sq(x3, x3); + fe_add(tmp0, tmp0, z3); + fe_mul(z3, x1, z2); + fe_mul(z2, tmp1, tmp0); + } + + fe_cswap(x2, x3, swap); + fe_cswap(z2, z3, swap); + + fe_invert(z2, z2); + fe_mul(x2, x2, z2); + fe_tobytes(shared_secret, x2); +} diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/keypair.cpp b/apps/Launcher/ext/libtorrent/ed25519/src/keypair.cpp new file mode 100644 index 0000000000..7d2d143384 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/keypair.cpp @@ -0,0 +1,16 @@ +#include "libtorrent/ed25519.hpp" +#include "sha512.h" +#include "ge.h" + + +void ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed) { + ge_p3 A; + + sha512(seed, 32, private_key); + private_key[0] &= 248; + private_key[31] &= 63; + private_key[31] |= 64; + + ge_scalarmult_base(&A, private_key); + ge_p3_tobytes(public_key, &A); +} diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/precomp_data.h b/apps/Launcher/ext/libtorrent/ed25519/src/precomp_data.h new file mode 100644 index 0000000000..ea00e4cc66 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/precomp_data.h @@ -0,0 +1,1392 @@ +static ge_precomp Bi[8] = { + { + { 25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605 }, + { -12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378 }, + { -8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546 }, + }, + { + { 15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024 }, + { 16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574 }, + { 30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357 }, + }, + { + { 10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380 }, + { 4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306 }, + { 19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942 }, + }, + { + { 5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766 }, + { -30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701 }, + { 28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300 }, + }, + { + { -22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877 }, + { -6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951 }, + { 4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784 }, + }, + { + { -25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436 }, + { 25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918 }, + { 23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877 }, + }, + { + { -33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800 }, + { -25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305 }, + { -13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300 }, + }, + { + { -3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876 }, + { -24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619 }, + { -3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683 }, + }, +}; + + +/* base[i][j] = (j+1)*256^i*B */ +static ge_precomp base[32][8] = { + { + { + { 25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605 }, + { -12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378 }, + { -8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546 }, + }, + { + { -12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303 }, + { -21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081 }, + { 26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697 }, + }, + { + { 15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024 }, + { 16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574 }, + { 30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357 }, + }, + { + { -17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540 }, + { 23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397 }, + { 7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325 }, + }, + { + { 10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380 }, + { 4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306 }, + { 19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942 }, + }, + { + { -15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777 }, + { -8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737 }, + { -18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652 }, + }, + { + { 5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766 }, + { -30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701 }, + { 28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300 }, + }, + { + { 14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726 }, + { -7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955 }, + { 27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425 }, + }, + }, + { + { + { -13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171 }, + { 27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510 }, + { 17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660 }, + }, + { + { -10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639 }, + { 29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963 }, + { 5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950 }, + }, + { + { -27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568 }, + { 12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335 }, + { 25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628 }, + }, + { + { -26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007 }, + { -2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772 }, + { -22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653 }, + }, + { + { 2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567 }, + { 13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686 }, + { 21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372 }, + }, + { + { -13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887 }, + { -23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954 }, + { -29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953 }, + }, + { + { 24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833 }, + { -16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532 }, + { -22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876 }, + }, + { + { 2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268 }, + { 33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214 }, + { 1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038 }, + }, + }, + { + { + { 6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800 }, + { 4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645 }, + { -4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664 }, + }, + { + { 1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933 }, + { -25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182 }, + { -17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222 }, + }, + { + { -18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991 }, + { 20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880 }, + { 9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092 }, + }, + { + { -16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295 }, + { 19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788 }, + { 8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553 }, + }, + { + { -15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026 }, + { 11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347 }, + { -18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033 }, + }, + { + { -23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395 }, + { -27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278 }, + { 1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890 }, + }, + { + { 32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995 }, + { -30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596 }, + { -11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891 }, + }, + { + { 31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060 }, + { 11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608 }, + { -20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606 }, + }, + }, + { + { + { 7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389 }, + { -19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016 }, + { -11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341 }, + }, + { + { -22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505 }, + { 14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553 }, + { -28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655 }, + }, + { + { 15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220 }, + { 12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631 }, + { -4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099 }, + }, + { + { 26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556 }, + { 14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749 }, + { 236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930 }, + }, + { + { 1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391 }, + { 5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253 }, + { 20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066 }, + }, + { + { 24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958 }, + { -11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082 }, + { -28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383 }, + }, + { + { -30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521 }, + { -11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807 }, + { 23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948 }, + }, + { + { 9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134 }, + { -32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455 }, + { 27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629 }, + }, + }, + { + { + { -8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069 }, + { -32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746 }, + { 24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919 }, + }, + { + { 11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837 }, + { 8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906 }, + { -28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771 }, + }, + { + { -25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817 }, + { 10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098 }, + { 10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409 }, + }, + { + { -12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504 }, + { -26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727 }, + { 28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420 }, + }, + { + { -32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003 }, + { -1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605 }, + { -30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384 }, + }, + { + { -26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701 }, + { -23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683 }, + { 29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708 }, + }, + { + { -3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563 }, + { -19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260 }, + { -5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387 }, + }, + { + { -19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672 }, + { 23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686 }, + { -24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665 }, + }, + }, + { + { + { 11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182 }, + { -31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277 }, + { 14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628 }, + }, + { + { -4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474 }, + { -26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539 }, + { -25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822 }, + }, + { + { -10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970 }, + { 19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756 }, + { -24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508 }, + }, + { + { -26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683 }, + { -10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655 }, + { -20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158 }, + }, + { + { -4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125 }, + { -15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839 }, + { -20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664 }, + }, + { + { 27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294 }, + { -18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899 }, + { -11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070 }, + }, + { + { 3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294 }, + { -15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949 }, + { -21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083 }, + }, + { + { 31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420 }, + { -5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940 }, + { 29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396 }, + }, + }, + { + { + { -12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567 }, + { 20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127 }, + { -16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294 }, + }, + { + { -12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887 }, + { 22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964 }, + { 16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195 }, + }, + { + { 9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244 }, + { 24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999 }, + { -1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762 }, + }, + { + { -18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274 }, + { -33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236 }, + { -16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605 }, + }, + { + { -13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761 }, + { -22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884 }, + { -6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482 }, + }, + { + { -24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638 }, + { -11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490 }, + { -32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170 }, + }, + { + { 5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736 }, + { 10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124 }, + { -17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392 }, + }, + { + { 8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029 }, + { 6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048 }, + { 28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958 }, + }, + }, + { + { + { 24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593 }, + { 26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071 }, + { -11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692 }, + }, + { + { 11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687 }, + { -160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441 }, + { -20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001 }, + }, + { + { -938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460 }, + { -19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007 }, + { -21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762 }, + }, + { + { 15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005 }, + { -9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674 }, + { 4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035 }, + }, + { + { 7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590 }, + { -2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957 }, + { -30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812 }, + }, + { + { 33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740 }, + { -18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122 }, + { -27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158 }, + }, + { + { 8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885 }, + { 26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140 }, + { 19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857 }, + }, + { + { 801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155 }, + { 19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260 }, + { 19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483 }, + }, + }, + { + { + { -3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677 }, + { 32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815 }, + { 22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751 }, + }, + { + { -16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203 }, + { -11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208 }, + { 1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230 }, + }, + { + { 16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850 }, + { -21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389 }, + { -9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968 }, + }, + { + { -11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689 }, + { 14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880 }, + { 5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304 }, + }, + { + { 30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632 }, + { -3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412 }, + { 20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566 }, + }, + { + { -20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038 }, + { -26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232 }, + { -1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943 }, + }, + { + { 17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856 }, + { 23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738 }, + { 15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971 }, + }, + { + { -27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718 }, + { -13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697 }, + { -11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883 }, + }, + }, + { + { + { 5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912 }, + { -26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358 }, + { 3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849 }, + }, + { + { 29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307 }, + { -14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977 }, + { -6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335 }, + }, + { + { -29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644 }, + { -22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616 }, + { -27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735 }, + }, + { + { -21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099 }, + { 29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341 }, + { -936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336 }, + }, + { + { -23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646 }, + { 31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425 }, + { -17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388 }, + }, + { + { -31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743 }, + { -16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822 }, + { -8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462 }, + }, + { + { 18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985 }, + { 9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702 }, + { -22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797 }, + }, + { + { 21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293 }, + { 27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100 }, + { 19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688 }, + }, + }, + { + { + { 12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186 }, + { 2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610 }, + { -2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707 }, + }, + { + { 7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220 }, + { 915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025 }, + { 32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044 }, + }, + { + { 32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992 }, + { -4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027 }, + { 21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197 }, + }, + { + { 8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901 }, + { 31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952 }, + { 19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878 }, + }, + { + { -28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390 }, + { 32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730 }, + { 2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730 }, + }, + { + { -19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180 }, + { -30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272 }, + { -15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715 }, + }, + { + { -22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970 }, + { -31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772 }, + { -17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865 }, + }, + { + { 15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750 }, + { 20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373 }, + { 32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348 }, + }, + }, + { + { + { 9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144 }, + { -22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195 }, + { 5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086 }, + }, + { + { -13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684 }, + { -8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518 }, + { -2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233 }, + }, + { + { -5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793 }, + { -2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794 }, + { 580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435 }, + }, + { + { 23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921 }, + { 13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518 }, + { 2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563 }, + }, + { + { 14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278 }, + { -27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024 }, + { 4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030 }, + }, + { + { 10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783 }, + { 27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717 }, + { 6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844 }, + }, + { + { 14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333 }, + { 16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048 }, + { 22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760 }, + }, + { + { -4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760 }, + { -15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757 }, + { -2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112 }, + }, + }, + { + { + { -19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468 }, + { 3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184 }, + { 10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289 }, + }, + { + { 15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066 }, + { 24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882 }, + { 13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226 }, + }, + { + { 16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101 }, + { 29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279 }, + { -6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811 }, + }, + { + { 27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709 }, + { 20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714 }, + { -2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121 }, + }, + { + { 9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464 }, + { 12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847 }, + { 13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400 }, + }, + { + { 4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414 }, + { -15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158 }, + { 17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045 }, + }, + { + { -461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415 }, + { -5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459 }, + { -31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079 }, + }, + { + { 21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412 }, + { -20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743 }, + { -14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836 }, + }, + }, + { + { + { 12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022 }, + { 18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429 }, + { -6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065 }, + }, + { + { 30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861 }, + { 10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000 }, + { -33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101 }, + }, + { + { 32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815 }, + { 29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642 }, + { 10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966 }, + }, + { + { 25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574 }, + { -21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742 }, + { -18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689 }, + }, + { + { 12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020 }, + { -10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772 }, + { 3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982 }, + }, + { + { -14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953 }, + { -16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218 }, + { -17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265 }, + }, + { + { 29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073 }, + { -3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325 }, + { -11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798 }, + }, + { + { -4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870 }, + { -7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863 }, + { -13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927 }, + }, + }, + { + { + { -2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267 }, + { -9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663 }, + { 22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862 }, + }, + { + { -25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673 }, + { 15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943 }, + { 15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020 }, + }, + { + { -4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238 }, + { 11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064 }, + { 14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795 }, + }, + { + { 15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052 }, + { -10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904 }, + { 29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531 }, + }, + { + { -13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979 }, + { -5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841 }, + { 10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431 }, + }, + { + { 10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324 }, + { -31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940 }, + { 10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320 }, + }, + { + { -15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184 }, + { 14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114 }, + { 30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878 }, + }, + { + { 12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784 }, + { -2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091 }, + { -16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585 }, + }, + }, + { + { + { -8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208 }, + { 10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864 }, + { 17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661 }, + }, + { + { 7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233 }, + { 26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212 }, + { -12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525 }, + }, + { + { -24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068 }, + { 9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397 }, + { -8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988 }, + }, + { + { 5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889 }, + { 32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038 }, + { 14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697 }, + }, + { + { 20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875 }, + { -25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905 }, + { -25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656 }, + }, + { + { 11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818 }, + { 27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714 }, + { 10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203 }, + }, + { + { 20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931 }, + { -30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024 }, + { -23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084 }, + }, + { + { -1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204 }, + { 20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817 }, + { 27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667 }, + }, + }, + { + { + { 11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504 }, + { -12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768 }, + { -19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255 }, + }, + { + { 6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790 }, + { 1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438 }, + { -22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333 }, + }, + { + { 17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971 }, + { 31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905 }, + { 29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409 }, + }, + { + { 12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409 }, + { 6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499 }, + { -8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363 }, + }, + { + { 28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664 }, + { -11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324 }, + { -21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940 }, + }, + { + { 13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990 }, + { -17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914 }, + { -25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290 }, + }, + { + { 24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257 }, + { -6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433 }, + { -16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236 }, + }, + { + { -12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045 }, + { 11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093 }, + { -1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347 }, + }, + }, + { + { + { -28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191 }, + { -15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507 }, + { -12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906 }, + }, + { + { 3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018 }, + { -16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109 }, + { -23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926 }, + }, + { + { -24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528 }, + { 8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625 }, + { -32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286 }, + }, + { + { 2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033 }, + { 27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866 }, + { 21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896 }, + }, + { + { 30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075 }, + { 26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347 }, + { -22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437 }, + }, + { + { -5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165 }, + { -18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588 }, + { -32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193 }, + }, + { + { -19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017 }, + { -28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883 }, + { 21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961 }, + }, + { + { 8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043 }, + { 29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663 }, + { -20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362 }, + }, + }, + { + { + { -33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860 }, + { 2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466 }, + { -24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063 }, + }, + { + { -26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997 }, + { -1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295 }, + { -13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369 }, + }, + { + { 9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385 }, + { 18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109 }, + { 2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906 }, + }, + { + { 4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424 }, + { -19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185 }, + { 7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962 }, + }, + { + { -7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325 }, + { 10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593 }, + { 696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404 }, + }, + { + { -11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644 }, + { 17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801 }, + { 26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804 }, + }, + { + { -31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884 }, + { -586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577 }, + { -9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849 }, + }, + { + { 32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473 }, + { -8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644 }, + { -2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319 }, + }, + }, + { + { + { -11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599 }, + { -9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768 }, + { -27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084 }, + }, + { + { -27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328 }, + { -15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369 }, + { 20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920 }, + }, + { + { 12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815 }, + { -32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025 }, + { -21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397 }, + }, + { + { -20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448 }, + { 6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981 }, + { 30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165 }, + }, + { + { 32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501 }, + { 17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073 }, + { -1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861 }, + }, + { + { 14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845 }, + { -1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211 }, + { 18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870 }, + }, + { + { 10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096 }, + { 33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803 }, + { -32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168 }, + }, + { + { 30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965 }, + { -14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505 }, + { 18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598 }, + }, + }, + { + { + { 5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782 }, + { 5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900 }, + { -31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479 }, + }, + { + { -12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208 }, + { 8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232 }, + { 17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719 }, + }, + { + { 16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271 }, + { -4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326 }, + { -8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132 }, + }, + { + { 14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300 }, + { 8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570 }, + { 15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670 }, + }, + { + { -2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994 }, + { -12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913 }, + { 31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317 }, + }, + { + { -25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730 }, + { 842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096 }, + { -4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078 }, + }, + { + { -15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411 }, + { -19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905 }, + { -9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654 }, + }, + { + { -28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870 }, + { -23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498 }, + { 12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579 }, + }, + }, + { + { + { 14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677 }, + { 10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647 }, + { -2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743 }, + }, + { + { -25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468 }, + { 21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375 }, + { -25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155 }, + }, + { + { 6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725 }, + { -12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612 }, + { -10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943 }, + }, + { + { -30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944 }, + { 30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928 }, + { 9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406 }, + }, + { + { 22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139 }, + { -8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963 }, + { -31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693 }, + }, + { + { 1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734 }, + { -448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680 }, + { -24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410 }, + }, + { + { -9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931 }, + { -16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654 }, + { 22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710 }, + }, + { + { 29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180 }, + { -26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684 }, + { -10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895 }, + }, + }, + { + { + { 22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501 }, + { -11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413 }, + { 6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880 }, + }, + { + { -8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874 }, + { 22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962 }, + { -7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899 }, + }, + { + { 21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152 }, + { 9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063 }, + { 7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080 }, + }, + { + { -9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146 }, + { -17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183 }, + { -19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133 }, + }, + { + { -32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421 }, + { -3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622 }, + { -4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197 }, + }, + { + { 2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663 }, + { 31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753 }, + { 4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755 }, + }, + { + { -9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862 }, + { -26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118 }, + { 26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171 }, + }, + { + { 15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380 }, + { 16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824 }, + { 28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270 }, + }, + }, + { + { + { -817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438 }, + { -31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584 }, + { -594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562 }, + }, + { + { 30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471 }, + { 18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610 }, + { 19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269 }, + }, + { + { -30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650 }, + { 14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369 }, + { 19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461 }, + }, + { + { 30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462 }, + { -5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793 }, + { -2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218 }, + }, + { + { -24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226 }, + { 18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019 }, + { -15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037 }, + }, + { + { 31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171 }, + { -17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132 }, + { -28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841 }, + }, + { + { 21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181 }, + { -33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210 }, + { -1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040 }, + }, + { + { 3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935 }, + { 24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105 }, + { -28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814 }, + }, + }, + { + { + { 793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852 }, + { 5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581 }, + { -4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646 }, + }, + { + { 10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844 }, + { 10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025 }, + { 27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453 }, + }, + { + { -23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068 }, + { 4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192 }, + { -17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921 }, + }, + { + { -9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259 }, + { -12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426 }, + { -5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072 }, + }, + { + { -17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305 }, + { 13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832 }, + { 28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943 }, + }, + { + { -16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011 }, + { 24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447 }, + { 17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494 }, + }, + { + { -28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245 }, + { -20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859 }, + { 28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915 }, + }, + { + { 16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707 }, + { 10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848 }, + { -11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224 }, + }, + }, + { + { + { -25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391 }, + { 15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215 }, + { -23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101 }, + }, + { + { 23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713 }, + { 21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849 }, + { -7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930 }, + }, + { + { -29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940 }, + { -21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031 }, + { -17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404 }, + }, + { + { -25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243 }, + { -23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116 }, + { -24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525 }, + }, + { + { -23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509 }, + { -10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883 }, + { 15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865 }, + }, + { + { -3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660 }, + { 4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273 }, + { -28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138 }, + }, + { + { -25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560 }, + { -10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135 }, + { 2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941 }, + }, + { + { -4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739 }, + { 18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756 }, + { -30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819 }, + }, + }, + { + { + { -6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347 }, + { -27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028 }, + { 21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075 }, + }, + { + { 16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799 }, + { -2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609 }, + { -25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817 }, + }, + { + { -23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989 }, + { -30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523 }, + { 4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278 }, + }, + { + { 31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045 }, + { 19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377 }, + { 24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480 }, + }, + { + { 17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016 }, + { 510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426 }, + { 18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525 }, + }, + { + { 13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396 }, + { 9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080 }, + { 12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892 }, + }, + { + { 15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275 }, + { 11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074 }, + { 20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140 }, + }, + { + { -16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717 }, + { -1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101 }, + { 24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127 }, + }, + }, + { + { + { -12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632 }, + { -26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415 }, + { -31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160 }, + }, + { + { 31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876 }, + { 22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625 }, + { -15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478 }, + }, + { + { 27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164 }, + { 26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595 }, + { -7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248 }, + }, + { + { -16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858 }, + { 15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193 }, + { 8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184 }, + }, + { + { -18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942 }, + { -1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635 }, + { 21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948 }, + }, + { + { 11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935 }, + { -25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415 }, + { -15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416 }, + }, + { + { -7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018 }, + { 4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778 }, + { 366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659 }, + }, + { + { -24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385 }, + { 18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503 }, + { 476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329 }, + }, + }, + { + { + { 20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056 }, + { -13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838 }, + { 24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948 }, + }, + { + { -3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691 }, + { -15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118 }, + { -23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517 }, + }, + { + { -20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269 }, + { -6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904 }, + { -23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589 }, + }, + { + { -28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193 }, + { -7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910 }, + { -30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930 }, + }, + { + { -7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667 }, + { 25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481 }, + { -9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876 }, + }, + { + { 22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640 }, + { -8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278 }, + { -21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112 }, + }, + { + { 26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272 }, + { 17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012 }, + { -10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221 }, + }, + { + { 30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046 }, + { 13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345 }, + { -19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310 }, + }, + }, + { + { + { 19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937 }, + { 31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636 }, + { -9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008 }, + }, + { + { -2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429 }, + { -15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576 }, + { 31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066 }, + }, + { + { -9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490 }, + { -12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104 }, + { 33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053 }, + }, + { + { 31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275 }, + { -20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511 }, + { 22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095 }, + }, + { + { -28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439 }, + { 23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939 }, + { -23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424 }, + }, + { + { 2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310 }, + { 3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608 }, + { -32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079 }, + }, + { + { -23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101 }, + { 21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418 }, + { 18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576 }, + }, + { + { 30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356 }, + { 9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996 }, + { -26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099 }, + }, + }, + { + { + { -26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728 }, + { -13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658 }, + { -10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242 }, + }, + { + { -21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001 }, + { -4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766 }, + { 18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373 }, + }, + { + { 26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458 }, + { -17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628 }, + { -13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657 }, + }, + { + { -23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062 }, + { 25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616 }, + { 31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014 }, + }, + { + { 24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383 }, + { -25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814 }, + { -20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718 }, + }, + { + { 30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417 }, + { 2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222 }, + { 33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444 }, + }, + { + { -20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597 }, + { 23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970 }, + { 1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799 }, + }, + { + { -5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647 }, + { 13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511 }, + { -29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032 }, + }, + }, + { + { + { 9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834 }, + { -23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461 }, + { 29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062 }, + }, + { + { -25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516 }, + { -20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547 }, + { -24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240 }, + }, + { + { -17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038 }, + { -33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741 }, + { 16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103 }, + }, + { + { -19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747 }, + { -1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323 }, + { 31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016 }, + }, + { + { -14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373 }, + { 15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228 }, + { -2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141 }, + }, + { + { 16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399 }, + { 11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831 }, + { -185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376 }, + }, + { + { -32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313 }, + { -18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958 }, + { -6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577 }, + }, + { + { -22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743 }, + { 29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684 }, + { -20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476 }, + }, + }, +}; + diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/sc.cpp b/apps/Launcher/ext/libtorrent/ed25519/src/sc.cpp new file mode 100644 index 0000000000..d04c92d52f --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/sc.cpp @@ -0,0 +1,809 @@ +#include "fixedint.h" +#include "sc.h" + +static uint64_t load_3(const unsigned char *in) { + uint64_t result; + + result = (uint64_t) in[0]; + result |= ((uint64_t) in[1]) << 8; + result |= ((uint64_t) in[2]) << 16; + + return result; +} + +static uint64_t load_4(const unsigned char *in) { + uint64_t result; + + result = (uint64_t) in[0]; + result |= ((uint64_t) in[1]) << 8; + result |= ((uint64_t) in[2]) << 16; + result |= ((uint64_t) in[3]) << 24; + + return result; +} + +/* +Input: + s[0]+256*s[1]+...+256^63*s[63] = s + +Output: + s[0]+256*s[1]+...+256^31*s[31] = s mod l + where l = 2^252 + 27742317777372353535851937790883648493. + Overwrites s in place. +*/ + +void sc_reduce(unsigned char *s) { + int64_t s0 = 2097151 & load_3(s); + int64_t s1 = 2097151 & (load_4(s + 2) >> 5); + int64_t s2 = 2097151 & (load_3(s + 5) >> 2); + int64_t s3 = 2097151 & (load_4(s + 7) >> 7); + int64_t s4 = 2097151 & (load_4(s + 10) >> 4); + int64_t s5 = 2097151 & (load_3(s + 13) >> 1); + int64_t s6 = 2097151 & (load_4(s + 15) >> 6); + int64_t s7 = 2097151 & (load_3(s + 18) >> 3); + int64_t s8 = 2097151 & load_3(s + 21); + int64_t s9 = 2097151 & (load_4(s + 23) >> 5); + int64_t s10 = 2097151 & (load_3(s + 26) >> 2); + int64_t s11 = 2097151 & (load_4(s + 28) >> 7); + int64_t s12 = 2097151 & (load_4(s + 31) >> 4); + int64_t s13 = 2097151 & (load_3(s + 34) >> 1); + int64_t s14 = 2097151 & (load_4(s + 36) >> 6); + int64_t s15 = 2097151 & (load_3(s + 39) >> 3); + int64_t s16 = 2097151 & load_3(s + 42); + int64_t s17 = 2097151 & (load_4(s + 44) >> 5); + int64_t s18 = 2097151 & (load_3(s + 47) >> 2); + int64_t s19 = 2097151 & (load_4(s + 49) >> 7); + int64_t s20 = 2097151 & (load_4(s + 52) >> 4); + int64_t s21 = 2097151 & (load_3(s + 55) >> 1); + int64_t s22 = 2097151 & (load_4(s + 57) >> 6); + int64_t s23 = (load_4(s + 60) >> 3); + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + int64_t carry10; + int64_t carry11; + int64_t carry12; + int64_t carry13; + int64_t carry14; + int64_t carry15; + int64_t carry16; + + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + s23 = 0; + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + s22 = 0; + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + s21 = 0; + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + s20 = 0; + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + s19 = 0; + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + s18 = 0; + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry12 = (s12 + (1 << 20)) >> 21; + s13 += carry12; + s12 -= carry12 << 21; + carry14 = (s14 + (1 << 20)) >> 21; + s15 += carry14; + s14 -= carry14 << 21; + carry16 = (s16 + (1 << 20)) >> 21; + s17 += carry16; + s16 -= carry16 << 21; + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= carry11 << 21; + carry13 = (s13 + (1 << 20)) >> 21; + s14 += carry13; + s13 -= carry13 << 21; + carry15 = (s15 + (1 << 20)) >> 21; + s16 += carry15; + s15 -= carry15 << 21; + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + s17 = 0; + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + s16 = 0; + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + s15 = 0; + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + s14 = 0; + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + s13 = 0; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = (s0 + (1 << 20)) >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry2 = (s2 + (1 << 20)) >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry4 = (s4 + (1 << 20)) >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry1 = (s1 + (1 << 20)) >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry3 = (s3 + (1 << 20)) >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry5 = (s5 + (1 << 20)) >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= carry11 << 21; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry11 = s11 >> 21; + s12 += carry11; + s11 -= carry11 << 21; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 << 21; + + s[0] = (unsigned char) ((s0 >> 0) & 0xff); + s[1] = (unsigned char) ((s0 >> 8) & 0xff); + s[2] = (unsigned char) (((s0 >> 16) | (s1 << 5)) & 0xff); + s[3] = (unsigned char) ((s1 >> 3) & 0xff); + s[4] = (unsigned char) ((s1 >> 11) & 0xff); + s[5] = (unsigned char) (((s1 >> 19) | (s2 << 2)) & 0xff); + s[6] = (unsigned char) ((s2 >> 6) & 0xff); + s[7] = (unsigned char) (((s2 >> 14) | (s3 << 7)) & 0xff); + s[8] = (unsigned char) ((s3 >> 1) & 0xff); + s[9] = (unsigned char) ((s3 >> 9) & 0xff); + s[10] = (unsigned char) (((s3 >> 17) | (s4 << 4)) & 0xff); + s[11] = (unsigned char) ((s4 >> 4) & 0xff); + s[12] = (unsigned char) ((s4 >> 12) & 0xff); + s[13] = (unsigned char) (((s4 >> 20) | (s5 << 1)) & 0xff); + s[14] = (unsigned char) ((s5 >> 7) & 0xff); + s[15] = (unsigned char) (((s5 >> 15) | (s6 << 6)) & 0xff); + s[16] = (unsigned char) ((s6 >> 2) & 0xff); + s[17] = (unsigned char) ((s6 >> 10) & 0xff); + s[18] = (unsigned char) (((s6 >> 18) | (s7 << 3)) & 0xff); + s[19] = (unsigned char) ((s7 >> 5) & 0xff); + s[20] = (unsigned char) ((s7 >> 13) & 0xff); + s[21] = (unsigned char) ((s8 >> 0) & 0xff); + s[22] = (unsigned char) ((s8 >> 8) & 0xff); + s[23] = (unsigned char) (((s8 >> 16) | (s9 << 5)) & 0xff); + s[24] = (unsigned char) ((s9 >> 3) & 0xff); + s[25] = (unsigned char) ((s9 >> 11) & 0xff); + s[26] = (unsigned char) (((s9 >> 19) | (s10 << 2)) & 0xff); + s[27] = (unsigned char) ((s10 >> 6) & 0xff); + s[28] = (unsigned char) (((s10 >> 14) | (s11 << 7)) & 0xff); + s[29] = (unsigned char) ((s11 >> 1) & 0xff); + s[30] = (unsigned char) ((s11 >> 9) & 0xff); + s[31] = (unsigned char) ((s11 >> 17) & 0xff); +} + + + +/* +Input: + a[0]+256*a[1]+...+256^31*a[31] = a + b[0]+256*b[1]+...+256^31*b[31] = b + c[0]+256*c[1]+...+256^31*c[31] = c + +Output: + s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l + where l = 2^252 + 27742317777372353535851937790883648493. +*/ + +void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c) { + int64_t a0 = 2097151 & load_3(a); + int64_t a1 = 2097151 & (load_4(a + 2) >> 5); + int64_t a2 = 2097151 & (load_3(a + 5) >> 2); + int64_t a3 = 2097151 & (load_4(a + 7) >> 7); + int64_t a4 = 2097151 & (load_4(a + 10) >> 4); + int64_t a5 = 2097151 & (load_3(a + 13) >> 1); + int64_t a6 = 2097151 & (load_4(a + 15) >> 6); + int64_t a7 = 2097151 & (load_3(a + 18) >> 3); + int64_t a8 = 2097151 & load_3(a + 21); + int64_t a9 = 2097151 & (load_4(a + 23) >> 5); + int64_t a10 = 2097151 & (load_3(a + 26) >> 2); + int64_t a11 = (load_4(a + 28) >> 7); + int64_t b0 = 2097151 & load_3(b); + int64_t b1 = 2097151 & (load_4(b + 2) >> 5); + int64_t b2 = 2097151 & (load_3(b + 5) >> 2); + int64_t b3 = 2097151 & (load_4(b + 7) >> 7); + int64_t b4 = 2097151 & (load_4(b + 10) >> 4); + int64_t b5 = 2097151 & (load_3(b + 13) >> 1); + int64_t b6 = 2097151 & (load_4(b + 15) >> 6); + int64_t b7 = 2097151 & (load_3(b + 18) >> 3); + int64_t b8 = 2097151 & load_3(b + 21); + int64_t b9 = 2097151 & (load_4(b + 23) >> 5); + int64_t b10 = 2097151 & (load_3(b + 26) >> 2); + int64_t b11 = (load_4(b + 28) >> 7); + int64_t c0 = 2097151 & load_3(c); + int64_t c1 = 2097151 & (load_4(c + 2) >> 5); + int64_t c2 = 2097151 & (load_3(c + 5) >> 2); + int64_t c3 = 2097151 & (load_4(c + 7) >> 7); + int64_t c4 = 2097151 & (load_4(c + 10) >> 4); + int64_t c5 = 2097151 & (load_3(c + 13) >> 1); + int64_t c6 = 2097151 & (load_4(c + 15) >> 6); + int64_t c7 = 2097151 & (load_3(c + 18) >> 3); + int64_t c8 = 2097151 & load_3(c + 21); + int64_t c9 = 2097151 & (load_4(c + 23) >> 5); + int64_t c10 = 2097151 & (load_3(c + 26) >> 2); + int64_t c11 = (load_4(c + 28) >> 7); + int64_t s0; + int64_t s1; + int64_t s2; + int64_t s3; + int64_t s4; + int64_t s5; + int64_t s6; + int64_t s7; + int64_t s8; + int64_t s9; + int64_t s10; + int64_t s11; + int64_t s12; + int64_t s13; + int64_t s14; + int64_t s15; + int64_t s16; + int64_t s17; + int64_t s18; + int64_t s19; + int64_t s20; + int64_t s21; + int64_t s22; + int64_t s23; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + int64_t carry10; + int64_t carry11; + int64_t carry12; + int64_t carry13; + int64_t carry14; + int64_t carry15; + int64_t carry16; + int64_t carry17; + int64_t carry18; + int64_t carry19; + int64_t carry20; + int64_t carry21; + int64_t carry22; + + s0 = c0 + a0 * b0; + s1 = c1 + a0 * b1 + a1 * b0; + s2 = c2 + a0 * b2 + a1 * b1 + a2 * b0; + s3 = c3 + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0; + s4 = c4 + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0; + s5 = c5 + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0; + s6 = c6 + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + a6 * b0; + s7 = c7 + a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 + a6 * b1 + a7 * b0; + s8 = c8 + a0 * b8 + a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 + a6 * b2 + a7 * b1 + a8 * b0; + s9 = c9 + a0 * b9 + a1 * b8 + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 + a6 * b3 + a7 * b2 + a8 * b1 + a9 * b0; + s10 = c10 + a0 * b10 + a1 * b9 + a2 * b8 + a3 * b7 + a4 * b6 + a5 * b5 + a6 * b4 + a7 * b3 + a8 * b2 + a9 * b1 + a10 * b0; + s11 = c11 + a0 * b11 + a1 * b10 + a2 * b9 + a3 * b8 + a4 * b7 + a5 * b6 + a6 * b5 + a7 * b4 + a8 * b3 + a9 * b2 + a10 * b1 + a11 * b0; + s12 = a1 * b11 + a2 * b10 + a3 * b9 + a4 * b8 + a5 * b7 + a6 * b6 + a7 * b5 + a8 * b4 + a9 * b3 + a10 * b2 + a11 * b1; + s13 = a2 * b11 + a3 * b10 + a4 * b9 + a5 * b8 + a6 * b7 + a7 * b6 + a8 * b5 + a9 * b4 + a10 * b3 + a11 * b2; + s14 = a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 + a9 * b5 + a10 * b4 + a11 * b3; + s15 = a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 + a10 * b5 + a11 * b4; + s16 = a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5; + s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6; + s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7; + s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8; + s20 = a9 * b11 + a10 * b10 + a11 * b9; + s21 = a10 * b11 + a11 * b10; + s22 = a11 * b11; + s23 = 0; + carry0 = (s0 + (1 << 20)) >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry2 = (s2 + (1 << 20)) >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry4 = (s4 + (1 << 20)) >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry12 = (s12 + (1 << 20)) >> 21; + s13 += carry12; + s12 -= carry12 << 21; + carry14 = (s14 + (1 << 20)) >> 21; + s15 += carry14; + s14 -= carry14 << 21; + carry16 = (s16 + (1 << 20)) >> 21; + s17 += carry16; + s16 -= carry16 << 21; + carry18 = (s18 + (1 << 20)) >> 21; + s19 += carry18; + s18 -= carry18 << 21; + carry20 = (s20 + (1 << 20)) >> 21; + s21 += carry20; + s20 -= carry20 << 21; + carry22 = (s22 + (1 << 20)) >> 21; + s23 += carry22; + s22 -= carry22 << 21; + carry1 = (s1 + (1 << 20)) >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry3 = (s3 + (1 << 20)) >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry5 = (s5 + (1 << 20)) >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= carry11 << 21; + carry13 = (s13 + (1 << 20)) >> 21; + s14 += carry13; + s13 -= carry13 << 21; + carry15 = (s15 + (1 << 20)) >> 21; + s16 += carry15; + s15 -= carry15 << 21; + carry17 = (s17 + (1 << 20)) >> 21; + s18 += carry17; + s17 -= carry17 << 21; + carry19 = (s19 + (1 << 20)) >> 21; + s20 += carry19; + s19 -= carry19 << 21; + carry21 = (s21 + (1 << 20)) >> 21; + s22 += carry21; + s21 -= carry21 << 21; + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + s23 = 0; + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + s22 = 0; + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + s21 = 0; + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + s20 = 0; + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + s19 = 0; + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + s18 = 0; + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry12 = (s12 + (1 << 20)) >> 21; + s13 += carry12; + s12 -= carry12 << 21; + carry14 = (s14 + (1 << 20)) >> 21; + s15 += carry14; + s14 -= carry14 << 21; + carry16 = (s16 + (1 << 20)) >> 21; + s17 += carry16; + s16 -= carry16 << 21; + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= carry11 << 21; + carry13 = (s13 + (1 << 20)) >> 21; + s14 += carry13; + s13 -= carry13 << 21; + carry15 = (s15 + (1 << 20)) >> 21; + s16 += carry15; + s15 -= carry15 << 21; + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + s17 = 0; + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + s16 = 0; + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + s15 = 0; + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + s14 = 0; + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + s13 = 0; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = (s0 + (1 << 20)) >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry2 = (s2 + (1 << 20)) >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry4 = (s4 + (1 << 20)) >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry1 = (s1 + (1 << 20)) >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry3 = (s3 + (1 << 20)) >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry5 = (s5 + (1 << 20)) >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= carry11 << 21; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry11 = s11 >> 21; + s12 += carry11; + s11 -= carry11 << 21; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 << 21; + + s[0] = (unsigned char) ((s0 >> 0) & 0xff); + s[1] = (unsigned char) ((s0 >> 8) & 0xff); + s[2] = (unsigned char) (((s0 >> 16) | (s1 << 5)) & 0xff); + s[3] = (unsigned char) ((s1 >> 3) & 0xff); + s[4] = (unsigned char) ((s1 >> 11) & 0xff); + s[5] = (unsigned char) (((s1 >> 19) | (s2 << 2)) & 0xff); + s[6] = (unsigned char) ((s2 >> 6) & 0xff); + s[7] = (unsigned char) (((s2 >> 14) | (s3 << 7)) & 0xff); + s[8] = (unsigned char) ((s3 >> 1) & 0xff); + s[9] = (unsigned char) ((s3 >> 9) & 0xff); + s[10] = (unsigned char) (((s3 >> 17) | (s4 << 4)) & 0xff); + s[11] = (unsigned char) ((s4 >> 4) & 0xff); + s[12] = (unsigned char) ((s4 >> 12) & 0xff); + s[13] = (unsigned char) (((s4 >> 20) | (s5 << 1)) & 0xff); + s[14] = (unsigned char) ((s5 >> 7) & 0xff); + s[15] = (unsigned char) (((s5 >> 15) | (s6 << 6)) & 0xff); + s[16] = (unsigned char) ((s6 >> 2) & 0xff); + s[17] = (unsigned char) ((s6 >> 10) & 0xff); + s[18] = (unsigned char) (((s6 >> 18) | (s7 << 3)) & 0xff); + s[19] = (unsigned char) ((s7 >> 5) & 0xff); + s[20] = (unsigned char) ((s7 >> 13) & 0xff); + s[21] = (unsigned char) ((s8 >> 0) & 0xff); + s[22] = (unsigned char) ((s8 >> 8) & 0xff); + s[23] = (unsigned char) (((s8 >> 16) | (s9 << 5)) & 0xff); + s[24] = (unsigned char) ((s9 >> 3) & 0xff); + s[25] = (unsigned char) ((s9 >> 11) & 0xff); + s[26] = (unsigned char) (((s9 >> 19) | (s10 << 2)) & 0xff); + s[27] = (unsigned char) ((s10 >> 6) & 0xff); + s[28] = (unsigned char) (((s10 >> 14) | (s11 << 7)) & 0xff); + s[29] = (unsigned char) ((s11 >> 1) & 0xff); + s[30] = (unsigned char) ((s11 >> 9) & 0xff); + s[31] = (unsigned char) ((s11 >> 17) & 0xff); +} diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/sc.h b/apps/Launcher/ext/libtorrent/ed25519/src/sc.h new file mode 100644 index 0000000000..0c058e532b --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/sc.h @@ -0,0 +1,13 @@ +#ifndef SC_H +#define SC_H + +/* +The set of scalars is \Z/l +where l = 2^252 + 27742317777372353535851937790883648493. +*/ + +void sc_reduce(unsigned char *s); +void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c); + +#endif + diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/seed.cpp b/apps/Launcher/ext/libtorrent/ed25519/src/seed.cpp new file mode 100644 index 0000000000..7619c66bcb --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/seed.cpp @@ -0,0 +1,41 @@ +#include "libtorrent/ed25519.hpp" + +#ifndef ED25519_NO_SEED + +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +int ed25519_create_seed(unsigned char *seed) { +#ifdef _WIN32 + HCRYPTPROV prov; + + if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { + return 1; + } + + if (!CryptGenRandom(prov, 32, seed)) { + CryptReleaseContext(prov, 0); + return 1; + } + + CryptReleaseContext(prov, 0); +#else + FILE *f = fopen("/dev/urandom", "rb"); + + if (f == NULL) { + return 1; + } + + fread(seed, 1, 32, f); + fclose(f); +#endif + + return 0; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/sha512.cpp b/apps/Launcher/ext/libtorrent/ed25519/src/sha512.cpp new file mode 100644 index 0000000000..3d659030de --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/sha512.cpp @@ -0,0 +1,279 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#include "fixedint.h" +#include "sha512.h" + +#ifndef UINT64_C +#define UINT64_C(x) x ## LL +#endif + +/* the K array */ +static const uint64_t K[80] = { + UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd), + UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc), + UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019), + UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118), + UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe), + UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2), + UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1), + UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694), + UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3), + UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65), + UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483), + UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5), + UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210), + UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4), + UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725), + UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70), + UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926), + UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df), + UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8), + UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b), + UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001), + UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30), + UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910), + UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8), + UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53), + UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8), + UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb), + UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3), + UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60), + UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec), + UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9), + UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b), + UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207), + UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178), + UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6), + UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b), + UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493), + UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c), + UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a), + UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817) +}; + +/* Various logical functions */ + +#define ROR64c(x, y) \ + ( ((((x)&UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)(y)&UINT64_C(63))) | \ + ((x)<<((uint64_t)(64-((y)&UINT64_C(63)))))) & UINT64_C(0xFFFFFFFFFFFFFFFF)) + +#define STORE64H(x, y) \ + { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \ + (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \ + (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ + (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } + +#define LOAD64H(x, y) \ + { x = (((uint64_t)((y)[0] & 255))<<56)|(((uint64_t)((y)[1] & 255))<<48) | \ + (((uint64_t)((y)[2] & 255))<<40)|(((uint64_t)((y)[3] & 255))<<32) | \ + (((uint64_t)((y)[4] & 255))<<24)|(((uint64_t)((y)[5] & 255))<<16) | \ + (((uint64_t)((y)[6] & 255))<<8)|(((uint64_t)((y)[7] & 255))); } + + +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) ROR64c(x, n) +#define R(x, n) (((x) &UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)n)) +#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) +#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) +#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) +#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) +#ifndef MIN + #define MIN(x, y) ( ((x)<(y))?(x):(y) ) +#endif + +/* compress 1024-bits */ +static int sha512_compress(sha512_context *md, unsigned char *buf) +{ + uint64_t S[8], W[80], t0, t1; + int i; + + /* copy state into S */ + for (i = 0; i < 8; i++) { + S[i] = md->state[i]; + } + + /* copy the state into 1024-bits into W[0..15] */ + for (i = 0; i < 16; i++) { + LOAD64H(W[i], buf + (8*i)); + } + + /* fill W[16..79] */ + for (i = 16; i < 80; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; + } + +/* Compress */ + #define RND(a,b,c,d,e,f,g,h,i) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c);\ + d += t0; \ + h = t0 + t1; + + for (i = 0; i < 80; i += 8) { + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7); + } + + #undef RND + + + + /* feedback */ + for (i = 0; i < 8; i++) { + md->state[i] = md->state[i] + S[i]; + } + + return 0; +} + + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return 0 if successful +*/ +int sha512_init(sha512_context * md) { + if (md == NULL) return 1; + + md->curlen = 0; + md->length = 0; + md->state[0] = UINT64_C(0x6a09e667f3bcc908); + md->state[1] = UINT64_C(0xbb67ae8584caa73b); + md->state[2] = UINT64_C(0x3c6ef372fe94f82b); + md->state[3] = UINT64_C(0xa54ff53a5f1d36f1); + md->state[4] = UINT64_C(0x510e527fade682d1); + md->state[5] = UINT64_C(0x9b05688c2b3e6c1f); + md->state[6] = UINT64_C(0x1f83d9abfb41bd6b); + md->state[7] = UINT64_C(0x5be0cd19137e2179); + + return 0; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return 0 if successful +*/ +int sha512_update (sha512_context * md, const unsigned char *in, size_t inlen) +{ + size_t n; + size_t i; + int err; + if (md == NULL) return 1; + if (in == NULL) return 1; + if (md->curlen > sizeof(md->buf)) { + return 1; + } + while (inlen > 0) { + if (md->curlen == 0 && inlen >= 128) { + if ((err = sha512_compress (md, (unsigned char *)in)) != 0) { + return err; + } + md->length += 128 * 8; + in += 128; + inlen -= 128; + } else { + n = MIN(inlen, (128 - md->curlen)); + + for (i = 0; i < n; i++) { + md->buf[i + md->curlen] = in[i]; + } + + + md->curlen += n; + in += n; + inlen -= n; + if (md->curlen == 128) { + if ((err = sha512_compress (md, md->buf)) != 0) { + return err; + } + md->length += 8*128; + md->curlen = 0; + } + } + } + return 0; +} + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (64 bytes) + @return 0 if successful +*/ + int sha512_final(sha512_context * md, unsigned char *out) + { + int i; + + if (md == NULL) return 1; + if (out == NULL) return 1; + + if (md->curlen >= sizeof(md->buf)) { + return 1; + } + + /* increase the length of the message */ + md->length += md->curlen * UINT64_C(8); + + /* append the '1' bit */ + md->buf[md->curlen++] = (unsigned char)0x80; + + /* if the length is currently above 112 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->curlen > 112) { + while (md->curlen < 128) { + md->buf[md->curlen++] = (unsigned char)0; + } + sha512_compress(md, md->buf); + md->curlen = 0; + } + + /* pad upto 120 bytes of zeroes + * note: that from 112 to 120 is the 64 MSB of the length. We assume that you won't hash + * > 2^64 bits of data... :-) + */ +while (md->curlen < 120) { + md->buf[md->curlen++] = (unsigned char)0; +} + + /* store length */ +STORE64H(md->length, md->buf+120); +sha512_compress(md, md->buf); + + /* copy output */ +for (i = 0; i < 8; i++) { + STORE64H(md->state[i], out+(8*i)); +} + +return 0; +} + +int sha512(const unsigned char *message, size_t message_len, unsigned char *out) +{ + sha512_context ctx; + int ret; + if ((ret = sha512_init(&ctx))) return ret; + if ((ret = sha512_update(&ctx, message, message_len))) return ret; + if ((ret = sha512_final(&ctx, out))) return ret; + return 0; +} diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/sha512.h b/apps/Launcher/ext/libtorrent/ed25519/src/sha512.h new file mode 100644 index 0000000000..0cdd0a3d58 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/sha512.h @@ -0,0 +1,22 @@ +#ifndef SHA512_H +#define SHA512_H + +#include + +#include "fixedint.h" + +/* state */ +typedef struct sha512_context_ { + uint64_t length, state[8]; + size_t curlen; + unsigned char buf[128]; +} sha512_context; + + +int sha512_init(sha512_context * md); +int sha512_final(sha512_context * md, unsigned char *out); +int sha512_update(sha512_context * md, const unsigned char *in, size_t inlen); +int sha512(const unsigned char *message, size_t message_len, unsigned char *out); + +#endif + diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/sign.cpp b/apps/Launcher/ext/libtorrent/ed25519/src/sign.cpp new file mode 100644 index 0000000000..6badd2c031 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/sign.cpp @@ -0,0 +1,31 @@ +#include "libtorrent/ed25519.hpp" +#include "sha512.h" +#include "ge.h" +#include "sc.h" + + +void ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key) { + sha512_context hash; + unsigned char hram[64]; + unsigned char r[64]; + ge_p3 R; + + + sha512_init(&hash); + sha512_update(&hash, private_key + 32, 32); + sha512_update(&hash, message, message_len); + sha512_final(&hash, r); + + sc_reduce(r); + ge_scalarmult_base(&R, r); + ge_p3_tobytes(signature, &R); + + sha512_init(&hash); + sha512_update(&hash, signature, 32); + sha512_update(&hash, public_key, 32); + sha512_update(&hash, message, message_len); + sha512_final(&hash, hram); + + sc_reduce(hram); + sc_muladd(signature + 32, hram, private_key, r); +} diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/verify.cpp b/apps/Launcher/ext/libtorrent/ed25519/src/verify.cpp new file mode 100644 index 0000000000..37b4ed573a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/verify.cpp @@ -0,0 +1,77 @@ +#include "libtorrent/ed25519.hpp" +#include "sha512.h" +#include "ge.h" +#include "sc.h" + +static int consttime_equal(const unsigned char *x, const unsigned char *y) { + unsigned char r = 0; + + r = x[0] ^ y[0]; + #define F(i) r |= x[i] ^ y[i] + F(1); + F(2); + F(3); + F(4); + F(5); + F(6); + F(7); + F(8); + F(9); + F(10); + F(11); + F(12); + F(13); + F(14); + F(15); + F(16); + F(17); + F(18); + F(19); + F(20); + F(21); + F(22); + F(23); + F(24); + F(25); + F(26); + F(27); + F(28); + F(29); + F(30); + F(31); + #undef F + + return !r; +} + +int ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key) { + unsigned char h[64]; + unsigned char checker[32]; + sha512_context hash; + ge_p3 A; + ge_p2 R; + + if (signature[63] & 224) { + return 0; + } + + if (ge_frombytes_negate_vartime(&A, public_key) != 0) { + return 0; + } + + sha512_init(&hash); + sha512_update(&hash, signature, 32); + sha512_update(&hash, public_key, 32); + sha512_update(&hash, message, message_len); + sha512_final(&hash, h); + + sc_reduce(h); + ge_double_scalarmult_vartime(&R, h, &A, signature + 32); + ge_tobytes(checker, &R); + + if (!consttime_equal(checker, signature)) { + return 0; + } + + return 1; +} diff --git a/apps/Launcher/ext/libtorrent/ed25519/test.c b/apps/Launcher/ext/libtorrent/ed25519/test.c new file mode 100644 index 0000000000..a56d202575 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/test.c @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +//#define ED25519_DLL +#include "src/ed25519.h" + +#include "src/ge.h" +#include "src/sc.h" + +const char message[] = "Hello, world!"; + + +int main(int argc, char *argv[]) { + unsigned char public_key[32], private_key[64], seed[32], scalar[32]; + unsigned char other_public_key[32], other_private_key[64]; + unsigned char shared_secret[32], other_shared_secret[32]; + unsigned char signature[64]; + + clock_t start; + clock_t end; + int i; + + /* create a random seed, and a keypair out of that seed */ + ed25519_create_seed(seed); + ed25519_create_keypair(public_key, private_key, seed); + + /* create signature on the message with the keypair */ + ed25519_sign(signature, message, strlen(message), public_key, private_key); + + /* verify the signature */ + if (ed25519_verify(signature, message, strlen(message), public_key)) { + printf("valid signature\n"); + } else { + printf("invalid signature\n"); + } + + /* create scalar and add it to the keypair */ + ed25519_create_seed(scalar); + ed25519_add_scalar(public_key, private_key, scalar); + + /* create signature with the new keypair */ + ed25519_sign(signature, message, strlen(message), public_key, private_key); + + /* verify the signature with the new keypair */ + if (ed25519_verify(signature, message, strlen(message), public_key)) { + printf("valid signature\n"); + } else { + printf("invalid signature\n"); + } + + /* make a slight adjustment and verify again */ + signature[44] ^= 0x10; + if (ed25519_verify(signature, message, strlen(message), public_key)) { + printf("did not detect signature change\n"); + } else { + printf("correctly detected signature change\n"); + } + + /* generate two keypairs for testing key exchange */ + ed25519_create_seed(seed); + ed25519_create_keypair(public_key, private_key, seed); + ed25519_create_seed(seed); + ed25519_create_keypair(other_public_key, other_private_key, seed); + + /* create two shared secrets - from both perspectives - and check if they're equal */ + ed25519_key_exchange(shared_secret, other_public_key, private_key); + ed25519_key_exchange(other_shared_secret, public_key, other_private_key); + + for (i = 0; i < 32; ++i) { + if (shared_secret[i] != other_shared_secret[i]) { + printf("key exchange was incorrect\n"); + break; + } + } + + if (i == 32) { + printf("key exchange was correct\n"); + } + + /* test performance */ + printf("testing seed generation performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_create_seed(seed); + } + end = clock(); + + printf("%fus per seed\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + + printf("testing key generation performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_create_keypair(public_key, private_key, seed); + } + end = clock(); + + printf("%fus per keypair\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + printf("testing sign performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_sign(signature, message, strlen(message), public_key, private_key); + } + end = clock(); + + printf("%fus per signature\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + printf("testing verify performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_verify(signature, message, strlen(message), public_key); + } + end = clock(); + + printf("%fus per signature\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + + printf("testing keypair scalar addition performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_add_scalar(public_key, private_key, scalar); + } + end = clock(); + + printf("%fus per keypair\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + printf("testing public key scalar addition performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_add_scalar(public_key, NULL, scalar); + } + end = clock(); + + printf("%fus per key\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + printf("testing key exchange performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_key_exchange(shared_secret, other_public_key, private_key); + } + end = clock(); + + printf("%fus per shared secret\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + return 0; +} diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/ConvertUTF.h b/apps/Launcher/ext/libtorrent/include/libtorrent/ConvertUTF.h new file mode 100644 index 0000000000..e95a5cdeae --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/ConvertUTF.h @@ -0,0 +1,165 @@ +/* + * Copyright 2001-2004 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + +/* --------------------------------------------------------------------- + + Conversions between UTF32, UTF-16, and UTF-8. Header file. + + Several funtions are included here, forming a complete set of + conversions between the three formats. UTF-7 is not included + here, but is handled in a separate source file. + + Each of these routines takes pointers to input buffers and output + buffers. The input buffers are const. + + Each routine converts the text between *sourceStart and sourceEnd, + putting the result into the buffer between *targetStart and + targetEnd. Note: the end pointers are *after* the last item: e.g. + *(sourceEnd - 1) is the last item. + + The return result indicates whether the conversion was successful, + and if not, whether the problem was in the source or target buffers. + (Only the first encountered problem is indicated.) + + After the conversion, *sourceStart and *targetStart are both + updated to point to the end of last text successfully converted in + the respective buffers. + + Input parameters: + sourceStart - pointer to a pointer to the source buffer. + The contents of this are modified on return so that + it points at the next thing to be converted. + targetStart - similarly, pointer to pointer to the target buffer. + sourceEnd, targetEnd - respectively pointers to the ends of the + two buffers, for overflow checking only. + + These conversion functions take a ConversionFlags argument. When this + flag is set to strict, both irregular sequences and isolated surrogates + will cause an error. When the flag is set to lenient, both irregular + sequences and isolated surrogates are converted. + + Whether the flag is strict or lenient, all illegal sequences will cause + an error return. This includes sequences such as: , , + or in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code + must check for illegal sequences. + + When the flag is set to lenient, characters over 0x10FFFF are converted + to the replacement character; otherwise (when the flag is set to strict) + they constitute an error. + + Output parameters: + The value "sourceIllegal" is returned from some routines if the input + sequence is malformed. When "sourceIllegal" is returned, the source + value will point to the illegal value that caused the problem. E.g., + in UTF-8 when a sequence is malformed, it points to the start of the + malformed sequence. + + Author: Mark E. Davis, 1994. + Rev History: Rick McGowan, fixes & updates May 2001. + Fixes & updates, Sept 2001. + +------------------------------------------------------------------------ */ + +/* --------------------------------------------------------------------- + The following 4 definitions are compiler-specific. + The C standard does not guarantee that wchar_t has at least + 16 bits, so wchar_t is no less portable than unsigned short! + All should be unsigned values to avoid sign extension during + bit mask & shift operations. +------------------------------------------------------------------------ */ + +#ifdef __cplusplus +#include "libtorrent/config.hpp" +// these are standard C types, but they might +// not be available in c++ +#include +typedef boost::uint32_t UTF32; +typedef boost::uint16_t UTF16; +typedef boost::uint8_t UTF8; +extern "C" { +#else +#define TORRENT_EXTRA_EXPORT +#ifdef _MSC_VER +// msvc doesn't seem to have stdint.h +typedef unsigned __int32 UTF32; +typedef unsigned __int16 UTF16; +typedef unsigned __int8 UTF8; +#else +#include +typedef uint32_t UTF32; +typedef uint16_t UTF16; +typedef uint8_t UTF8; +#endif +#endif + +typedef unsigned char Boolean; /* 0 or 1 */ + +/* Some fundamental constants */ +#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD +#define UNI_MAX_BMP (UTF32)0x0000FFFF +#define UNI_MAX_UTF16 (UTF32)0x0010FFFF +#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF +#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF + +typedef enum { + conversionOK, /* conversion successful */ + sourceExhausted, /* partial character in source, but hit end */ + targetExhausted, /* insuff. room in target for conversion */ + sourceIllegal /* source sequence is illegal/malformed */ +} ConversionResult; + +typedef enum { + strictConversion = 0, + lenientConversion +} ConversionFlags; + +TORRENT_EXTRA_EXPORT ConversionResult ConvertUTF8toUTF16 ( + const UTF8** sourceStart, const UTF8* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); + +TORRENT_EXTRA_EXPORT ConversionResult ConvertUTF16toUTF8 ( + const UTF16** sourceStart, const UTF16* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); + +TORRENT_EXTRA_EXPORT ConversionResult ConvertUTF8toUTF32 ( + const UTF8** sourceStart, const UTF8* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); + +TORRENT_EXTRA_EXPORT ConversionResult ConvertUTF32toUTF8 ( + const UTF32** sourceStart, const UTF32* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); + +TORRENT_EXTRA_EXPORT ConversionResult ConvertUTF16toUTF32 ( + const UTF16** sourceStart, const UTF16* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); + +TORRENT_EXTRA_EXPORT ConversionResult ConvertUTF32toUTF16 ( + const UTF32** sourceStart, const UTF32* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); + +TORRENT_EXTRA_EXPORT Boolean isLegalUTF8Sequence(const UTF8 *source, + const UTF8 *sourceEnd); + +#ifdef __cplusplus +} +#endif +/* --------------------------------------------------------------------- */ diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/GeoIP.h b/apps/Launcher/ext/libtorrent/include/libtorrent/GeoIP.h new file mode 100644 index 0000000000..420a22de05 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/GeoIP.h @@ -0,0 +1,179 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */ +/* GeoIP.h + * + * Copyright (C) 2006 MaxMind LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef GEOIP_H +#define GEOIP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include /* for fstat */ + +#define SEGMENT_RECORD_LENGTH 3 +#define STANDARD_RECORD_LENGTH 3 +#define ORG_RECORD_LENGTH 4 +#define MAX_RECORD_LENGTH 4 +#define NUM_DB_TYPES 20 + +typedef struct GeoIPTag { + FILE *GeoIPDatabase; + char *file_path; + unsigned char *cache; + unsigned char *index_cache; + unsigned int *databaseSegments; + char databaseType; + time_t mtime; + int flags; + off_t size; + char record_length; + int charset; /* 0 iso-8859-1 1 utf8 */ + int record_iter; /* used in GeoIP_next_record */ + int netmask; /* netmask of last lookup - set using depth in _GeoIP_seek_record */ +} GeoIP; + + +typedef enum { + GEOIP_CHARSET_ISO_8859_1 = 0, + GEOIP_CHARSET_UTF8 = 1 +} GeoIPCharset; + +typedef struct GeoIPRegionTag { + char country_code[3]; + char region[3]; +} GeoIPRegion; + +typedef enum { + GEOIP_STANDARD = 0, + GEOIP_MEMORY_CACHE = 1, + GEOIP_CHECK_CACHE = 2, + GEOIP_INDEX_CACHE = 4, + GEOIP_MMAP_CACHE = 8 +} GeoIPOptions; + +typedef enum { + GEOIP_COUNTRY_EDITION = 1, + GEOIP_REGION_EDITION_REV0 = 7, + GEOIP_CITY_EDITION_REV0 = 6, + GEOIP_ORG_EDITION = 5, + GEOIP_ISP_EDITION = 4, + GEOIP_CITY_EDITION_REV1 = 2, + GEOIP_REGION_EDITION_REV1 = 3, + GEOIP_PROXY_EDITION = 8, + GEOIP_ASNUM_EDITION = 9, + GEOIP_NETSPEED_EDITION = 10, + GEOIP_DOMAIN_EDITION = 11 +} GeoIPDBTypes; + +typedef enum { + GEOIP_ANON_PROXY = 1, + GEOIP_HTTP_X_FORWARDED_FOR_PROXY = 2, + GEOIP_HTTP_CLIENT_IP_PROXY = 3 +} GeoIPProxyTypes; + +typedef enum { + GEOIP_UNKNOWN_SPEED = 0, + GEOIP_DIALUP_SPEED = 1, + GEOIP_CABLEDSL_SPEED = 2, + GEOIP_CORPORATE_SPEED = 3 +} GeoIPNetspeedValues; + +extern char **GeoIPDBFileName; +extern const char * GeoIPDBDescription[NUM_DB_TYPES]; +extern const char *GeoIPCountryDBFileName; +extern const char *GeoIPRegionDBFileName; +extern const char *GeoIPCityDBFileName; +extern const char *GeoIPOrgDBFileName; +extern const char *GeoIPISPDBFileName; + +extern const char GeoIP_country_code[253][3]; +extern const char GeoIP_country_code3[253][4]; +extern const char * GeoIP_country_name[253]; +extern const char GeoIP_country_continent[253][3]; + +#ifdef DLL +#define GEOIP_API __declspec(dllexport) +#else +#define GEOIP_API +#endif /* DLL */ + +GEOIP_API void GeoIP_setup_custom_directory(char *dir); +GEOIP_API GeoIP* GeoIP_open_type (int type, int flags); +GEOIP_API GeoIP* GeoIP_new(int flags); +GEOIP_API GeoIP* GeoIP_open(const char * filename, int flags); +GEOIP_API int GeoIP_db_avail(int type); +GEOIP_API void GeoIP_delete(GeoIP* gi); +GEOIP_API const char *GeoIP_country_code_by_addr (GeoIP* gi, const char *addr); +GEOIP_API const char *GeoIP_country_code_by_name (GeoIP* gi, const char *host); +GEOIP_API const char *GeoIP_country_code3_by_addr (GeoIP* gi, const char *addr); +GEOIP_API const char *GeoIP_country_code3_by_name (GeoIP* gi, const char *host); +GEOIP_API const char *GeoIP_country_name_by_addr (GeoIP* gi, const char *addr); +GEOIP_API const char *GeoIP_country_name_by_name (GeoIP* gi, const char *host); +GEOIP_API const char *GeoIP_country_name_by_ipnum (GeoIP* gi, unsigned long ipnum); +GEOIP_API const char *GeoIP_country_code_by_ipnum (GeoIP* gi, unsigned long ipnum); +GEOIP_API const char *GeoIP_country_code3_by_ipnum (GeoIP* gi, unsigned long ipnum); + +/* Deprecated - for backwards compatibility only */ +GEOIP_API int GeoIP_country_id_by_addr (GeoIP* gi, const char *addr); +GEOIP_API int GeoIP_country_id_by_name (GeoIP* gi, const char *host); +GEOIP_API char *GeoIP_org_by_addr (GeoIP* gi, const char *addr); +GEOIP_API char *GeoIP_org_by_name (GeoIP* gi, const char *host); +/* End deprecated */ + +GEOIP_API int GeoIP_id_by_addr (GeoIP* gi, const char *addr); +GEOIP_API int GeoIP_id_by_name (GeoIP* gi, const char *host); +GEOIP_API int GeoIP_id_by_ipnum (GeoIP* gi, unsigned long ipnum); + +GEOIP_API GeoIPRegion * GeoIP_region_by_addr (GeoIP* gi, const char *addr); +GEOIP_API GeoIPRegion * GeoIP_region_by_name (GeoIP* gi, const char *host); +GEOIP_API GeoIPRegion * GeoIP_region_by_ipnum (GeoIP *gi, unsigned long ipnum); + +/* Warning - don't call this after GeoIP_assign_region_by_inetaddr calls */ +GEOIP_API void GeoIPRegion_delete (GeoIPRegion *gir); + +GEOIP_API void GeoIP_assign_region_by_inetaddr(GeoIP* gi, unsigned long inetaddr, GeoIPRegion *gir); + +/* Used to query GeoIP Organization, ISP and AS Number databases */ +GEOIP_API char *GeoIP_name_by_ipnum (GeoIP* gi, unsigned long ipnum); +GEOIP_API char *GeoIP_name_by_addr (GeoIP* gi, const char *addr); +GEOIP_API char *GeoIP_name_by_name (GeoIP* gi, const char *host); + +GEOIP_API char *GeoIP_database_info (GeoIP* gi); +GEOIP_API unsigned char GeoIP_database_edition (GeoIP* gi); + +GEOIP_API int GeoIP_charset (GeoIP* gi); +GEOIP_API int GeoIP_set_charset (GeoIP* gi, int charset); + +GEOIP_API int GeoIP_last_netmask (GeoIP* gi); + +/* Convert region code to region name */ +GEOIP_API const char * GeoIP_region_name_by_code(const char *country_code, const char *region_code); + +/* Get timezone from country and region code */ +GEOIP_API const char * GeoIP_time_zone_by_country_and_region(const char *country_code, const char *region_code); + +#ifdef __cplusplus +} +#endif + +#endif /* GEOIP_H */ diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/Makefile.am b/apps/Launcher/ext/libtorrent/include/libtorrent/Makefile.am new file mode 100644 index 0000000000..1dc28660af --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/Makefile.am @@ -0,0 +1,155 @@ +includedir = @includedir@/libtorrent + +if WITH_SHIPPED_GEOIP +GEOIP_H = GeoIP.h +endif + +nobase_include_HEADERS = \ + address.hpp \ + add_torrent_params.hpp \ + alert.hpp \ + alert_manager.hpp \ + alert_dispatcher.hpp \ + alert_types.hpp \ + alloca.hpp \ + allocator.hpp \ + assert.hpp \ + bandwidth_limit.hpp \ + bandwidth_manager.hpp \ + bandwidth_socket.hpp \ + bandwidth_queue_entry.hpp \ + bencode.hpp \ + bitfield.hpp \ + bloom_filter.hpp \ + broadcast_socket.hpp \ + bt_peer_connection.hpp \ + buffer.hpp \ + build_config.hpp \ + chained_buffer.hpp \ + config.hpp \ + connection_queue.hpp \ + ConvertUTF.h \ + copy_ptr.hpp \ + create_torrent.hpp \ + deadline_timer.hpp \ + debug.hpp \ + disk_buffer_holder.hpp \ + disk_buffer_pool.hpp \ + disk_io_thread.hpp \ + ed25519.hpp \ + entry.hpp \ + enum_net.hpp \ + error.hpp \ + error_code.hpp \ + escape_string.hpp \ + export.hpp \ + extensions.hpp \ + file.hpp \ + file_pool.hpp \ + file_storage.hpp \ + fingerprint.hpp \ + gzip.hpp \ + hasher.hpp \ + http_connection.hpp \ + http_parser.hpp \ + http_seed_connection.hpp \ + http_stream.hpp \ + http_tracker_connection.hpp \ + i2p_stream.hpp \ + identify_client.hpp \ + instantiate_connection.hpp \ + intrusive_ptr_base.hpp \ + invariant_check.hpp \ + io.hpp \ + io_service.hpp \ + io_service_fwd.hpp \ + ip_filter.hpp \ + ip_voter.hpp \ + lazy_entry.hpp \ + lsd.hpp \ + magnet_uri.hpp \ + max.hpp \ + natpmp.hpp \ + packet_buffer.hpp \ + parse_url.hpp \ + pe_crypto.hpp \ + peer_connection.hpp \ + peer.hpp \ + peer_id.hpp \ + peer_info.hpp \ + peer_request.hpp \ + piece_block_progress.hpp \ + piece_picker.hpp \ + policy.hpp \ + proxy_base.hpp \ + ptime.hpp \ + puff.hpp \ + random.hpp \ + rss.hpp \ + session.hpp \ + session_settings.hpp \ + session_status.hpp \ + settings.hpp \ + sha1_hash.hpp \ + size_type.hpp \ + sliding_average.hpp \ + socket.hpp \ + socket_io.hpp \ + socket_type.hpp \ + socket_type_fwd.hpp \ + socks5_stream.hpp \ + ssl_stream.hpp \ + stat.hpp \ + storage.hpp \ + storage_defs.hpp \ + string_util.hpp \ + thread.hpp \ + time.hpp \ + timestamp_history.hpp \ + torrent_handle.hpp \ + torrent.hpp \ + torrent_info.hpp \ + tracker_manager.hpp \ + udp_socket.hpp \ + udp_tracker_connection.hpp \ + union_endpoint.hpp \ + upnp.hpp \ + utp_socket_manager.hpp \ + utp_stream.hpp \ + utf8.hpp \ + version.hpp \ + web_connection_base.hpp \ + web_peer_connection.hpp \ + xml_parse.hpp \ + \ + $(GEOIP_H) \ + tommath.h \ + tommath_class.h \ + tommath_superclass.h \ + \ + aux_/session_impl.hpp \ + \ + extensions/logger.hpp \ + extensions/lt_trackers.hpp \ + extensions/metadata_transfer.hpp \ + extensions/smart_ban.hpp \ + extensions/ut_metadata.hpp \ + extensions/ut_pex.hpp \ + \ + kademlia/dht_tracker.hpp \ + kademlia/dht_observer.hpp \ + kademlia/find_data.hpp \ + kademlia/logging.hpp \ + kademlia/msg.hpp \ + kademlia/node.hpp \ + kademlia/node_entry.hpp \ + kademlia/node_id.hpp \ + kademlia/observer.hpp \ + kademlia/refresh.hpp \ + kademlia/routing_table.hpp \ + kademlia/rpc_manager.hpp \ + kademlia/traversal_algorithm.hpp \ + kademlia/item.hpp \ + kademlia/get_item.hpp \ + kademlia/get_peers.hpp + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/Makefile.in b/apps/Launcher/ext/libtorrent/include/libtorrent/Makefile.in new file mode 100644 index 0000000000..fdab33e100 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/Makefile.in @@ -0,0 +1,805 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = include/libtorrent +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_python.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_check_geoip.m4 \ + $(top_srcdir)/m4/ax_check_openssl.m4 \ + $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/gettext-lib.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkgconfig.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__nobase_include_HEADERS_DIST) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__nobase_include_HEADERS_DIST = address.hpp add_torrent_params.hpp \ + alert.hpp alert_manager.hpp alert_dispatcher.hpp \ + alert_types.hpp alloca.hpp allocator.hpp assert.hpp \ + bandwidth_limit.hpp bandwidth_manager.hpp bandwidth_socket.hpp \ + bandwidth_queue_entry.hpp bencode.hpp bitfield.hpp \ + bloom_filter.hpp broadcast_socket.hpp bt_peer_connection.hpp \ + buffer.hpp build_config.hpp chained_buffer.hpp config.hpp \ + connection_queue.hpp ConvertUTF.h copy_ptr.hpp \ + create_torrent.hpp deadline_timer.hpp debug.hpp \ + disk_buffer_holder.hpp disk_buffer_pool.hpp disk_io_thread.hpp \ + ed25519.hpp entry.hpp enum_net.hpp error.hpp error_code.hpp \ + escape_string.hpp export.hpp extensions.hpp file.hpp \ + file_pool.hpp file_storage.hpp fingerprint.hpp gzip.hpp \ + hasher.hpp http_connection.hpp http_parser.hpp \ + http_seed_connection.hpp http_stream.hpp \ + http_tracker_connection.hpp i2p_stream.hpp identify_client.hpp \ + instantiate_connection.hpp intrusive_ptr_base.hpp \ + invariant_check.hpp io.hpp io_service.hpp io_service_fwd.hpp \ + ip_filter.hpp ip_voter.hpp lazy_entry.hpp lsd.hpp \ + magnet_uri.hpp max.hpp natpmp.hpp packet_buffer.hpp \ + parse_url.hpp pe_crypto.hpp peer_connection.hpp peer.hpp \ + peer_id.hpp peer_info.hpp peer_request.hpp \ + piece_block_progress.hpp piece_picker.hpp policy.hpp \ + proxy_base.hpp ptime.hpp puff.hpp random.hpp rss.hpp \ + session.hpp session_settings.hpp session_status.hpp \ + settings.hpp sha1_hash.hpp size_type.hpp sliding_average.hpp \ + socket.hpp socket_io.hpp socket_type.hpp socket_type_fwd.hpp \ + socks5_stream.hpp ssl_stream.hpp stat.hpp storage.hpp \ + storage_defs.hpp string_util.hpp thread.hpp time.hpp \ + timestamp_history.hpp torrent_handle.hpp torrent.hpp \ + torrent_info.hpp tracker_manager.hpp udp_socket.hpp \ + udp_tracker_connection.hpp union_endpoint.hpp upnp.hpp \ + utp_socket_manager.hpp utp_stream.hpp utf8.hpp version.hpp \ + web_connection_base.hpp web_peer_connection.hpp xml_parse.hpp \ + GeoIP.h tommath.h tommath_class.h tommath_superclass.h \ + aux_/session_impl.hpp extensions/logger.hpp \ + extensions/lt_trackers.hpp extensions/metadata_transfer.hpp \ + extensions/smart_ban.hpp extensions/ut_metadata.hpp \ + extensions/ut_pex.hpp kademlia/dht_tracker.hpp \ + kademlia/dht_observer.hpp kademlia/find_data.hpp \ + kademlia/logging.hpp kademlia/msg.hpp kademlia/node.hpp \ + kademlia/node_entry.hpp kademlia/node_id.hpp \ + kademlia/observer.hpp kademlia/refresh.hpp \ + kademlia/routing_table.hpp kademlia/rpc_manager.hpp \ + kademlia/traversal_algorithm.hpp kademlia/item.hpp \ + kademlia/get_item.hpp kademlia/get_peers.hpp +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(includedir)" +HEADERS = $(nobase_include_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_PYTHON_LIB = @BOOST_PYTHON_LIB@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +COMPILETIME_OPTIONS = @COMPILETIME_OPTIONS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEBUGFLAGS = @DEBUGFLAGS@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GEOIP_CFLAGS = @GEOIP_CFLAGS@ +GEOIP_LIBS = @GEOIP_LIBS@ +GREP = @GREP@ +ICONV_LIBS = @ICONV_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTERFACE_VERSION_INFO = @INTERFACE_VERSION_INFO@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_INCLUDES = @OPENSSL_INCLUDES@ +OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_INSTALL_PARAMS = @PYTHON_INSTALL_PARAMS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@/libtorrent +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +@WITH_SHIPPED_GEOIP_TRUE@GEOIP_H = GeoIP.h +nobase_include_HEADERS = \ + address.hpp \ + add_torrent_params.hpp \ + alert.hpp \ + alert_manager.hpp \ + alert_dispatcher.hpp \ + alert_types.hpp \ + alloca.hpp \ + allocator.hpp \ + assert.hpp \ + bandwidth_limit.hpp \ + bandwidth_manager.hpp \ + bandwidth_socket.hpp \ + bandwidth_queue_entry.hpp \ + bencode.hpp \ + bitfield.hpp \ + bloom_filter.hpp \ + broadcast_socket.hpp \ + bt_peer_connection.hpp \ + buffer.hpp \ + build_config.hpp \ + chained_buffer.hpp \ + config.hpp \ + connection_queue.hpp \ + ConvertUTF.h \ + copy_ptr.hpp \ + create_torrent.hpp \ + deadline_timer.hpp \ + debug.hpp \ + disk_buffer_holder.hpp \ + disk_buffer_pool.hpp \ + disk_io_thread.hpp \ + ed25519.hpp \ + entry.hpp \ + enum_net.hpp \ + error.hpp \ + error_code.hpp \ + escape_string.hpp \ + export.hpp \ + extensions.hpp \ + file.hpp \ + file_pool.hpp \ + file_storage.hpp \ + fingerprint.hpp \ + gzip.hpp \ + hasher.hpp \ + http_connection.hpp \ + http_parser.hpp \ + http_seed_connection.hpp \ + http_stream.hpp \ + http_tracker_connection.hpp \ + i2p_stream.hpp \ + identify_client.hpp \ + instantiate_connection.hpp \ + intrusive_ptr_base.hpp \ + invariant_check.hpp \ + io.hpp \ + io_service.hpp \ + io_service_fwd.hpp \ + ip_filter.hpp \ + ip_voter.hpp \ + lazy_entry.hpp \ + lsd.hpp \ + magnet_uri.hpp \ + max.hpp \ + natpmp.hpp \ + packet_buffer.hpp \ + parse_url.hpp \ + pe_crypto.hpp \ + peer_connection.hpp \ + peer.hpp \ + peer_id.hpp \ + peer_info.hpp \ + peer_request.hpp \ + piece_block_progress.hpp \ + piece_picker.hpp \ + policy.hpp \ + proxy_base.hpp \ + ptime.hpp \ + puff.hpp \ + random.hpp \ + rss.hpp \ + session.hpp \ + session_settings.hpp \ + session_status.hpp \ + settings.hpp \ + sha1_hash.hpp \ + size_type.hpp \ + sliding_average.hpp \ + socket.hpp \ + socket_io.hpp \ + socket_type.hpp \ + socket_type_fwd.hpp \ + socks5_stream.hpp \ + ssl_stream.hpp \ + stat.hpp \ + storage.hpp \ + storage_defs.hpp \ + string_util.hpp \ + thread.hpp \ + time.hpp \ + timestamp_history.hpp \ + torrent_handle.hpp \ + torrent.hpp \ + torrent_info.hpp \ + tracker_manager.hpp \ + udp_socket.hpp \ + udp_tracker_connection.hpp \ + union_endpoint.hpp \ + upnp.hpp \ + utp_socket_manager.hpp \ + utp_stream.hpp \ + utf8.hpp \ + version.hpp \ + web_connection_base.hpp \ + web_peer_connection.hpp \ + xml_parse.hpp \ + \ + $(GEOIP_H) \ + tommath.h \ + tommath_class.h \ + tommath_superclass.h \ + \ + aux_/session_impl.hpp \ + \ + extensions/logger.hpp \ + extensions/lt_trackers.hpp \ + extensions/metadata_transfer.hpp \ + extensions/smart_ban.hpp \ + extensions/ut_metadata.hpp \ + extensions/ut_pex.hpp \ + \ + kademlia/dht_tracker.hpp \ + kademlia/dht_observer.hpp \ + kademlia/find_data.hpp \ + kademlia/logging.hpp \ + kademlia/msg.hpp \ + kademlia/node.hpp \ + kademlia/node_entry.hpp \ + kademlia/node_id.hpp \ + kademlia/observer.hpp \ + kademlia/refresh.hpp \ + kademlia/routing_table.hpp \ + kademlia/rpc_manager.hpp \ + kademlia/traversal_algorithm.hpp \ + kademlia/item.hpp \ + kademlia/get_item.hpp \ + kademlia/get_peers.hpp + +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign include/libtorrent/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign include/libtorrent/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-nobase_includeHEADERS: $(nobase_include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ + fi; \ + $(am__nobase_list) | while read dir files; do \ + xfiles=; for file in $$files; do \ + if test -f "$$file"; then xfiles="$$xfiles $$file"; \ + else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \ + test -z "$$xfiles" || { \ + test "x$$dir" = x. || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)/$$dir'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)/$$dir"; }; \ + echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(includedir)/$$dir'"; \ + $(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(includedir)/$$dir" || exit $$?; }; \ + done + +uninstall-nobase_includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ + $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \ + dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-nobase_includeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-nobase_includeHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man \ + install-nobase_includeHEADERS install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-nobase_includeHEADERS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/add_torrent_params.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/add_torrent_params.hpp new file mode 100644 index 0000000000..f7fa71eeee --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/add_torrent_params.hpp @@ -0,0 +1,402 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ADD_TORRENT_PARAMS_HPP_INCLUDED +#define TORRENT_ADD_TORRENT_PARAMS_HPP_INCLUDED + +#include +#include +#include + +#include "libtorrent/storage_defs.hpp" +#include "libtorrent/peer_id.hpp" // sha1_hash +#include "libtorrent/version.hpp" + +#ifndef TORRENT_DISABLE_EXTENSIONS +#include "libtorrent/extensions.hpp" +#endif + +namespace libtorrent +{ + class torrent_info; + class torrent; + struct torrent_plugin; + + // The add_torrent_params is a parameter pack for adding torrents to a + // session. The key fields when adding a torrent are: + // + // * ti - when you have a .torrent file + // * url - when you have a magnet link or http URL to the .torrent file + // * info_hash - when all you have is an info-hash (this is similar to a + // magnet link) + // + // one of those fields need to be set. Another mandatory field is + // ``save_path``. The add_torrent_params object is passed into one of the + // ``session::add_torrent()`` overloads or ``session::async_add_torrent()``. + // + // If you only specify the info-hash, the torrent file will be downloaded + // from peers, which requires them to support the metadata extension. For + // the metadata extension to work, libtorrent must be built with extensions + // enabled (``TORRENT_DISABLE_EXTENSIONS`` must not be defined). It also + // takes an optional ``name`` argument. This may be left empty in case no + // name should be assigned to the torrent. In case it's not, the name is + // used for the torrent as long as it doesn't have metadata. See + // ``torrent_handle::name``. + // + struct TORRENT_EXPORT add_torrent_params + { + // The constructor can be used to initialize the storage constructor, + // which determines the storage mechanism for the downloaded or seeding + // data for the torrent. For more information, see the ``storage`` field. + add_torrent_params(storage_constructor_type sc = default_storage_constructor) + : version(LIBTORRENT_VERSION_NUM) +#ifndef TORRENT_NO_DEPRECATE + , tracker_url(0) +#endif + , storage_mode(storage_mode_sparse) + , storage(sc) + , userdata(0) +#ifndef TORRENT_NO_DEPRECATE + , flags(flag_ignore_flags | default_flags) +#else + , flags(default_flags) +#endif + , max_uploads(-1) + , max_connections(-1) + , upload_limit(-1) + , download_limit(-1) +#ifndef TORRENT_NO_DEPRECATE + , seed_mode(false) + , override_resume_data(false) + , upload_mode(false) + , share_mode(false) + , apply_ip_filter(true) + , paused(true) + , auto_managed(true) + , duplicate_is_error(false) + , merge_resume_trackers(false) +#endif + { + } + +#ifndef TORRENT_NO_DEPRECATE + void update_flags() const + { + if (flags != (flag_ignore_flags | default_flags)) return; + + boost::uint64_t& f = const_cast(flags); + f = flag_update_subscribe; + if (seed_mode) f |= flag_seed_mode; + if (override_resume_data) f |= flag_override_resume_data; + if (upload_mode) f |= flag_upload_mode; + if (share_mode) f |= flag_share_mode; + if (apply_ip_filter) f |= flag_apply_ip_filter; + if (paused) f |= flag_paused; + if (auto_managed) f |= flag_auto_managed; + if (duplicate_is_error) f |= flag_duplicate_is_error; + if (merge_resume_trackers) f |= flag_merge_resume_trackers; + } +#endif + + // values for the ``flags`` field + enum flags_t + { + // If ``flag_seed_mode`` is set, libtorrent will assume that all files + // are present for this torrent and that they all match the hashes in + // the torrent file. Each time a peer requests to download a block, + // the piece is verified against the hash, unless it has been verified + // already. If a hash fails, the torrent will automatically leave the + // seed mode and recheck all the files. The use case for this mode is + // if a torrent is created and seeded, or if the user already know + // that the files are complete, this is a way to avoid the initial + // file checks, and significantly reduce the startup time. + // + // Setting ``flag_seed_mode`` on a torrent without metadata (a + // .torrent file) is a no-op and will be ignored. + // + // If resume data is passed in with this torrent, the seed mode saved + // in there will override the seed mode you set here. + flag_seed_mode = 0x001, + + // If ``flag_override_resume_data`` is set, flags set for this torrent + // in this ``add_torrent_params`` object will take precedence over + // whatever states are saved in the resume data. For instance, the + // ``paused``, ``auto_managed``, ``sequential_download``, ``seed_mode``, + // ``super_seeding``, ``max_uploads``, ``max_connections``, + // ``upload_limit`` and ``download_limit`` are all affected by this + // flag. The intention of this flag is to have any field in + // add_torrent_params configuring the torrent override the corresponding + // configuration from the resume file, with the one exception of save + // resume data, which has its own flag (for historic reasons). + flag_override_resume_data = 0x002, + + // If ``flag_upload_mode`` is set, the torrent will be initialized in + // upload-mode, which means it will not make any piece requests. This + // state is typically entered on disk I/O errors, and if the torrent + // is also auto managed, it will be taken out of this state + // periodically. This mode can be used to avoid race conditions when + // adjusting priorities of pieces before allowing the torrent to start + // downloading. + // + // If the torrent is auto-managed (``flag_auto_managed``), the torrent + // will eventually be taken out of upload-mode, regardless of how it + // got there. If it's important to manually control when the torrent + // leaves upload mode, don't make it auto managed. + flag_upload_mode = 0x004, + + // determines if the torrent should be added in *share mode* or not. + // Share mode indicates that we are not interested in downloading the + // torrent, but merley want to improve our share ratio (i.e. increase + // it). A torrent started in share mode will do its best to never + // download more than it uploads to the swarm. If the swarm does not + // have enough demand for upload capacity, the torrent will not + // download anything. This mode is intended to be safe to add any + // number of torrents to, without manual screening, without the risk + // of downloading more than is uploaded. + // + // A torrent in share mode sets the priority to all pieces to 0, + // except for the pieces that are downloaded, when pieces are decided + // to be downloaded. This affects the progress bar, which might be set + // to "100% finished" most of the time. Do not change file or piece + // priorities for torrents in share mode, it will make it not work. + // + // The share mode has one setting, the share ratio target, see + // ``session_settings::share_mode_target`` for more info. + flag_share_mode = 0x008, + + // determines if the IP filter should apply to this torrent or not. By + // default all torrents are subject to filtering by the IP filter + // (i.e. this flag is set by default). This is useful if certain + // torrents needs to be excempt for some reason, being an auto-update + // torrent for instance. + flag_apply_ip_filter = 0x010, + + // specifies whether or not the torrent is to be started in a paused + // state. I.e. it won't connect to the tracker or any of the peers + // until it's resumed. This is typically a good way of avoiding race + // conditions when setting configuration options on torrents before + // starting them. + flag_paused = 0x020, + + // If the torrent is auto-managed (``flag_auto_managed``), the torrent + // may be resumed at any point, regardless of how it paused. If it's + // important to manually control when the torrent is paused and + // resumed, don't make it auto managed. + // + // If ``flag_auto_managed`` is set, the torrent will be queued, + // started and seeded automatically by libtorrent. When this is set, + // the torrent should also be started as paused. The default queue + // order is the order the torrents were added. They are all downloaded + // in that order. For more details, see queuing_. + // + // If you pass in resume data, the auto_managed state of the torrent + // when the resume data was saved will override the auto_managed state + // you pass in here. You can override this by setting + // ``override_resume_data``. + flag_auto_managed = 0x040, + flag_duplicate_is_error = 0x080, + + // defaults to off and specifies whether tracker URLs loaded from + // resume data should be added to the trackers in the torrent or + // replace the trackers. + flag_merge_resume_trackers = 0x100, + + // on by default and means that this torrent will be part of state + // updates when calling post_torrent_updates(). + flag_update_subscribe = 0x200, + + // sets the torrent into super seeding mode. If the torrent is not a + // seed, this flag has no effect. It has the same effect as calling + // ``torrent_handle::super_seeding(true)`` on the torrent handle + // immediately after adding it. + flag_super_seeding = 0x400, + + // sets the sequential download state for the torrent. It has the same + // effect as calling ``torrent_handle::sequential_download(true)`` on + // the torrent handle immediately after adding it. + flag_sequential_download = 0x800, + + // if this flag is set, the save path from the resume data file, if + // present, is honored. This defaults to not being set, in which + // case the save_path specified in add_torrent_params is always used. + flag_use_resume_save_path = 0x1000, + + // internal + default_flags = flag_update_subscribe | flag_auto_managed | flag_paused | flag_apply_ip_filter +#ifndef TORRENT_NO_DEPRECATE + , flag_ignore_flags = 0x80000000 +#endif + }; + + // filled in by the constructor and should be left untouched. It + // is used for forward binary compatibility. + int version; + + // torrent_info object with the torrent to add. Unless the url or + // info_hash is set, this is required to be initiazlied. + boost::intrusive_ptr ti; + +#ifndef TORRENT_NO_DEPRECATE + char const* tracker_url; +#endif + // If the torrent doesn't have a tracker, but relies on the DHT to find + // peers, the ``trackers`` can specify tracker URLs for the torrent. + std::vector trackers; + + // url seeds to be added to the torrent (`BEP 17`_). + std::vector url_seeds; + + // a list of hostname and port pairs, representing DHT nodes to be added + // to the session (if DHT is enabled). The hostname may be an IP address. + std::vector > dht_nodes; + std::string name; + + // the path where the torrent is or will be stored. Note that this may + // alos be stored in resume data. If you want the save path saved in + // the resume data to be used, you need to set the + // flag_use_resume_save_path flag. + // + // .. note:: + // On windows this path (and other paths) are interpreted as UNC + // paths. This means they must use backslashes as directory separators + // and may not contain the special directories "." or "..". + std::string save_path; + + // The optional parameter, ``resume_data`` can be given if up to date + // fast-resume data is available. The fast-resume data can be acquired + // from a running torrent by calling save_resume_data() on + // torrent_handle. See fast-resume_. The ``vector`` that is passed in + // will be swapped into the running torrent instance with + // ``std::vector::swap()``. + std::vector resume_data; + + // One of the values from storage_mode_t. For more information, see + // storage-allocation_. + storage_mode_t storage_mode; + + // can be used to customize how the data is stored. The default storage + // will simply write the data to the files it belongs to, but it could be + // overridden to save everything to a single file at a specific location + // or encrypt the content on disk for instance. For more information + // about the storage_interface that needs to be implemented for a custom + // storage, see storage_interface. + storage_constructor_type storage; + + // The ``userdata`` parameter is optional and will be passed on to the + // extension constructor functions, if any (see `add_extension()`_). + void* userdata; + + // can be set to control the initial file priorities when adding a + // torrent. The semantics are the same as for + // ``torrent_handle::prioritize_files()``. + std::vector file_priorities; + + // torrent extension construction functions can be added to this vector + // to have them be added immediately when the torrent is constructed. + // This may be desired over the torrent_handle::add_extension() in order + // to avoid race conditions. For instance it may be important to have the + // plugin catch events that happen very early on after the torrent is + // created. + std::vector(torrent*, void*)> > + extensions; + + // the default tracker id to be used when announcing to trackers. By + // default this is empty, and no tracker ID is used, since this is an + // optional argument. If a tracker returns a tracker ID, that ID is used + // instead of this. + std::string trackerid; + + // If you specify a ``url``, the torrent will be set in + // ``downloading_metadata`` state until the .torrent file has been + // downloaded. If there's any error while downloading, the torrent will + // be stopped and the torrent error state (``torrent_status::error``) + // will indicate what went wrong. The ``url`` may refer to a magnet link + // or a regular http URL. + // + // If it refers to an HTTP URL, the info-hash for the added torrent will + // not be the true info-hash of the .torrent. Instead a placeholder, + // unique, info-hash is used which is later updated once the .torrent + // file has been downloaded. + // + // Once the info-hash change happens, a torrent_update_alert is posted. + std::string url; + + // if ``uuid`` is specified, it is used to find duplicates. If another + // torrent is already running with the same UUID as the one being added, + // it will be considered a duplicate. This is mainly useful for RSS feed + // items which has UUIDs specified. + std::string uuid; + + // should point to the URL of the RSS feed this torrent comes from, + // if it comes from an RSS feed. + std::string source_feed_url; + + // flags controlling aspects of this torrent and how it's added. See + // flags_t for details. + boost::uint64_t flags; + + // set this to the info hash of the torrent to add in case the info-hash + // is the only known property of the torrent. i.e. you don't have a + // .torrent file nor a magnet link. + sha1_hash info_hash; + + // ``max_uploads``, ``max_connections``, ``upload_limit``, + // ``download_limit`` correspond to the ``set_max_uploads()``, + // ``set_max_connections()``, ``set_upload_limit()`` and + // ``set_download_limit()`` functions on torrent_handle. These values let + // you initialize these settings when the torrent is added, instead of + // calling these functions immediately following adding it. + // + // -1 means unlimited on these settings just like their counterpart + // functions on torrent_handle + int max_uploads; + int max_connections; + int upload_limit; + int download_limit; + +#ifndef TORRENT_NO_DEPRECATE + bool seed_mode; + bool override_resume_data; + bool upload_mode; + bool share_mode; + bool apply_ip_filter; + bool paused; + bool auto_managed; + bool duplicate_is_error; + bool merge_resume_trackers; +#endif + + }; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/address.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/address.hpp new file mode 100644 index 0000000000..1a7aa4cc32 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/address.hpp @@ -0,0 +1,77 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ADDRESS_HPP_INCLUDED +#define TORRENT_ADDRESS_HPP_INCLUDED + +#include +#include "libtorrent/config.hpp" + +#ifdef __OBJC__ +#define Protocol Protocol_ +#endif + +#if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN +// asio assumes that the windows error codes are defined already +#include +#endif + +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif + +#ifdef __OBJC__ +#undef Protocol +#endif + +namespace libtorrent +{ + +#if BOOST_VERSION < 103500 + typedef ::asio::ip::address address; + typedef ::asio::ip::address_v4 address_v4; +#if TORRENT_USE_IPV6 + typedef ::asio::ip::address_v6 address_v6; +#endif +#else + typedef boost::asio::ip::address address; + typedef boost::asio::ip::address_v4 address_v4; +#if TORRENT_USE_IPV6 + typedef boost::asio::ip::address_v6 address_v6; +#endif +#endif +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/alert.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/alert.hpp new file mode 100644 index 0000000000..6d50f031c3 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/alert.hpp @@ -0,0 +1,319 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg, Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ALERT_HPP_INCLUDED +#define TORRENT_ALERT_HPP_INCLUDED + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include + +// OVERVIEW +// +// The pop_alerts() function on session is the main interface for retrieving +// alerts (warnings, messages and errors from libtorrent). If no alerts have +// been posted by libtorrent pop_alert() will return an empty list. +// +// By default, only errors are reported. set_alert_mask() can be used to +// specify which kinds of events should be reported. The alert mask is +// comprised by bits from the category_t enum. +// +// Every alert belongs to one or more category. There is a small cost involved +// in posting alerts. Only alerts that belong to an enabled category are +// posted. Setting the alert bitmask to 0 will disable all alerts (except those +// that are non-discardable). +// +// There are other alert base classes that some alerts derive from, all the +// alerts that are generated for a specific torrent are derived from +// torrent_alert, and tracker events derive from tracker_alert. +// + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/ptime.hpp" +#include "libtorrent/config.hpp" + +#ifndef TORRENT_NO_DEPRECATE +#ifndef BOOST_NO_TYPEID +#include +#endif +#endif // TORRENT_NO_DEPRECATE + +#ifndef TORRENT_MAX_ALERT_TYPES +#define TORRENT_MAX_ALERT_TYPES 15 +#endif + +namespace libtorrent { + + // The ``alert`` class is the base class that specific messages are derived from. + class TORRENT_EXPORT alert + { + public: + +#ifndef TORRENT_NO_DEPRECATE + // only here for backwards compatibility + enum severity_t { debug, info, warning, critical, fatal, none }; +#endif + + // these are bits for the alert_mask used by the session. See set_alert_mask(). + enum category_t + { + // Enables alerts that report an error. This includes: + // + // * tracker errors + // * tracker warnings + // * file errors + // * resume data failures + // * web seed errors + // * .torrent files errors + // * listen socket errors + // * port mapping errors + error_notification = 0x1, + + // Enables alerts when peers send invalid requests, get banned or + // snubbed. + peer_notification = 0x2, + + // Enables alerts for port mapping events. For NAT-PMP and UPnP. + port_mapping_notification = 0x4, + + // Enables alerts for events related to the storage. File errors and + // synchronization events for moving the storage, renaming files etc. + storage_notification = 0x8, + + // Enables all tracker events. Includes announcing to trackers, + // receiving responses, warnings and errors. + tracker_notification = 0x10, + + // Low level alerts for when peers are connected and disconnected. + debug_notification = 0x20, + + // Enables alerts for when a torrent or the session changes state. + status_notification = 0x40, + + // Alerts for when blocks are requested and completed. Also when + // pieces are completed. + progress_notification = 0x80, + + // Alerts when a peer is blocked by the ip blocker or port blocker. + ip_block_notification = 0x100, + + // Alerts when some limit is reached that might limit the download + // or upload rate. + performance_warning = 0x200, + + // Alerts on events in the DHT node. For incoming searches or + // bootstrapping being done etc. + dht_notification = 0x400, + + // If you enable these alerts, you will receive a stats_alert + // approximately once every second, for every active torrent. + // These alerts contain all statistics counters for the interval since + // the lasts stats alert. + stats_notification = 0x800, + + // Alerts on RSS related events, like feeds being updated, feed error + // conditions and successful RSS feed updates. Enabling this categoty + // will make you receive rss_alert alerts. + rss_notification = 0x1000, + + // The full bitmask, representing all available categories. + // + // since the enum is signed, make sure this isn't + // interpreted as -1. For instance, boost.python + // does that and fails when assigning it to an + // unsigned parameter. + all_categories = 0x7fffffff + }; + + // hidden + alert(); + // hidden + virtual ~alert(); + + // a timestamp is automatically created in the constructor + ptime timestamp() const; + + // returns an integer that is unique to this alert type. It can be + // compared against a specific alert by querying a static constant called ``alert_type`` + // in the alert. It can be used to determine the run-time type of an alert* in + // order to cast to that alert type and access specific members. + // + // e.g: + // + // .. code:: c++ + // + // std::auto_ptr a = ses.pop_alert(); + // switch (a->type()) + // { + // case read_piece_alert::alert_type: + // { + // read_piece_alert* p = (read_piece_alert*)a.get(); + // if (p->ec) { + // // read_piece failed + // break; + // } + // // use p + // break; + // } + // case file_renamed_alert::alert_type: + // { + // // etc... + // } + // } + virtual int type() const = 0; + + // returns a string literal describing the type of the alert. It does + // not include any information that might be bundled with the alert. + virtual char const* what() const = 0; + + // generate a string describing the alert and the information bundled + // with it. This is mainly intended for debug and development use. It is not suitable + // to use this for applications that may be localized. Instead, handle each alert + // type individually and extract and render the information from the alert depending + // on the locale. + virtual std::string message() const = 0; + + // returns a bitmask specifying which categories this alert belong to. + virtual int category() const = 0; + + // determines whether or not an alert is allowed to be discarded + // when the alert queue is full. There are a few alerts which may not be discared, + // since they would break the user contract, such as save_resume_data_alert. + virtual bool discardable() const { return true; } + +#ifndef TORRENT_NO_DEPRECATE + TORRENT_DEPRECATED_PREFIX + severity_t severity() const TORRENT_DEPRECATED { return warning; } +#endif + + // returns a pointer to a copy of the alert. + virtual std::auto_ptr clone() const = 0; + + private: + ptime m_timestamp; + }; + +#ifndef BOOST_NO_EXCEPTIONS +#ifndef TORRENT_NO_DEPRECATE + struct TORRENT_EXPORT unhandled_alert : std::exception + { + unhandled_alert() {} + }; + +#ifndef BOOST_NO_TYPEID + + namespace detail { + + struct void_; + + template + void handle_alert_dispatch( + const std::auto_ptr& alert_, const Handler& handler + , const std::type_info& typeid_ + , T0*, BOOST_PP_ENUM_SHIFTED_BINARY_PARAMS(TORRENT_MAX_ALERT_TYPES, T, *p)) + { + if (typeid_ == typeid(T0)) + handler(*static_cast(alert_.get())); + else + handle_alert_dispatch(alert_, handler, typeid_ + , BOOST_PP_ENUM_SHIFTED_PARAMS( + TORRENT_MAX_ALERT_TYPES, p), (void_*)0); + } + + template + void handle_alert_dispatch( + const std::auto_ptr& + , const Handler& + , const std::type_info& + , BOOST_PP_ENUM_PARAMS(TORRENT_MAX_ALERT_TYPES, void_* BOOST_PP_INTERCEPT)) + { + throw unhandled_alert(); + } + + } // namespace detail + + template + struct TORRENT_EXPORT handle_alert + { + template + handle_alert(const std::auto_ptr& alert_ + , const Handler& handler) + { + #define ALERT_POINTER_TYPE(z, n, text) (BOOST_PP_CAT(T, n)*)0 + + detail::handle_alert_dispatch(alert_, handler, typeid(*alert_) + , BOOST_PP_ENUM(TORRENT_MAX_ALERT_TYPES, ALERT_POINTER_TYPE, _)); + + #undef ALERT_POINTER_TYPE + } + }; + +#endif // BOOST_NO_TYPEID +#endif // TORRENT_NO_DEPRECATE +#endif // BOOST_NO_EXCEPTIONS + +// When you get an alert, you can use ``alert_cast<>`` to attempt to cast the pointer to a +// more specific alert type, in order to query it for more information. +template +T* alert_cast(alert* a) +{ + if (a == 0) return 0; + if (a->type() == T::alert_type) return static_cast(a); + return 0; +} +template +T const* alert_cast(alert const* a) +{ + if (a == 0) return 0; + if (a->type() == T::alert_type) return static_cast(a); + return 0; +} + +} // namespace libtorrent + +#endif // TORRENT_ALERT_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/alert_dispatcher.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/alert_dispatcher.hpp new file mode 100644 index 0000000000..62b7321733 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/alert_dispatcher.hpp @@ -0,0 +1,50 @@ +/* + +Copyright (c) 2012-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ALERT_DISPATCHER_HPP_INCLUDED +#define TORRENT_ALERT_DISPATCHER_HPP_INCLUDED + +namespace libtorrent +{ + class alert; + + struct alert_dispatcher + { + // return true if the alert was swallowed (i.e. + // ownership was taken over). In this case, the + // alert will not be passed on to any one else + virtual bool post_alert(alert* a) = 0; + }; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/alert_manager.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/alert_manager.hpp new file mode 100644 index 0000000000..798e555bbf --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/alert_manager.hpp @@ -0,0 +1,114 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg, Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ALERT_MANAGER_HPP_INCLUDED +#define TORRENT_ALERT_MANAGER_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/thread.hpp" + +#include +#include +#include + +namespace libtorrent { + +#ifndef TORRENT_DISABLE_EXTENSIONS + struct plugin; +#endif + + class TORRENT_EXTRA_EXPORT alert_manager + { + public: + alert_manager(int queue_limit + , boost::uint32_t alert_mask = alert::error_notification); + ~alert_manager(); + + void post_alert(const alert& alert_); + void post_alert_ptr(alert* alert_); + bool pending() const; + std::auto_ptr get(); + void get_all(std::deque* alerts); + + template + bool should_post() const + { + mutex::scoped_lock lock(m_mutex); + if (m_alerts.size() >= m_queue_size_limit) return false; + return (m_alert_mask & T::static_category) != 0; + } + + bool should_post(alert const* a) const + { + return (m_alert_mask & a->category()) != 0; + } + + alert const* wait_for_alert(time_duration max_wait); + + void set_alert_mask(boost::uint32_t m) + { + mutex::scoped_lock lock(m_mutex); + m_alert_mask = m; + } + + int alert_mask() const { return m_alert_mask; } + + size_t alert_queue_size_limit() const { return m_queue_size_limit; } + size_t set_alert_queue_size_limit(size_t queue_size_limit_); + + void set_dispatch_function(boost::function)> const&); + +#ifndef TORRENT_DISABLE_EXTENSIONS + void add_extension(boost::shared_ptr ext); +#endif + + private: + void post_impl(std::auto_ptr& alert_, mutex::scoped_lock& l); + + std::deque m_alerts; + mutable mutex m_mutex; + condition_variable m_condition; + boost::uint32_t m_alert_mask; + size_t m_queue_size_limit; + boost::function)> m_dispatch; + +#ifndef TORRENT_DISABLE_EXTENSIONS + typedef std::list > ses_extension_list_t; + ses_extension_list_t m_ses_extensions; +#endif + }; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/alert_types.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/alert_types.hpp new file mode 100644 index 0000000000..7cba5ce991 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/alert_types.hpp @@ -0,0 +1,1759 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ALERT_TYPES_HPP_INCLUDED +#define TORRENT_ALERT_TYPES_HPP_INCLUDED + +#include "libtorrent/alert.hpp" +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/identify_client.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/stat.hpp" +#include "libtorrent/rss.hpp" // for feed_handle + +// lines reserved for future includes +// the type-ids of the alert types +// are derived from the line on which +// they are declared + + + + +namespace libtorrent +{ + + // user defined alerts should use IDs greater than this + const static int user_alert_id = 10000; + + // This is a base class for alerts that are associated with a + // specific torrent. It contains a handle to the torrent. + struct TORRENT_EXPORT torrent_alert: alert + { + // internal + torrent_alert(torrent_handle const& h); + + // internal + const static int alert_type = 1; + virtual std::string message() const; + + // The torrent_handle pointing to the torrent this + // alert is associated with. + torrent_handle handle; + }; + + // The peer alert is a base class for alerts that refer to a specific peer. It includes all + // the information to identify the peer. i.e. ``ip`` and ``peer-id``. + struct TORRENT_EXPORT peer_alert: torrent_alert + { + // internal + peer_alert(torrent_handle const& h, tcp::endpoint const& i + , peer_id const& pi); + + const static int alert_type = 2; + const static int static_category = alert::peer_notification; + virtual int category() const { return static_category; } + virtual std::string message() const; + + // The peer's IP address and port. + tcp::endpoint ip; + + // the peer ID, if known. + peer_id pid; + }; + + // This is a base class used for alerts that are associated with a + // specific tracker. It derives from torrent_alert since a tracker + // is also associated with a specific torrent. + struct TORRENT_EXPORT tracker_alert: torrent_alert + { + // internal + tracker_alert(torrent_handle const& h + , std::string const& u); + + const static int alert_type = 3; + const static int static_category = alert::tracker_notification; + virtual int category() const { return static_category; } + virtual std::string message() const; + + // The tracker URL + std::string url; + }; + +#define TORRENT_DEFINE_ALERT(name) \ + const static int alert_type = __LINE__; \ + virtual int type() const { return alert_type; } \ + virtual std::auto_ptr clone() const \ + { return std::auto_ptr(new name(*this)); } \ + virtual int category() const { return static_category; } \ + virtual char const* what() const { return #name; } + + // The ``torrent_added_alert`` is posted once every time a torrent is successfully + // added. It doesn't contain any members of its own, but inherits the torrent handle + // from its base class. + // It's posted when the ``status_notification`` bit is set in the alert_mask. + struct TORRENT_EXPORT torrent_added_alert: torrent_alert + { + // internal + torrent_added_alert(torrent_handle const& h); + + TORRENT_DEFINE_ALERT(torrent_added_alert); + const static int static_category = alert::status_notification; + virtual std::string message() const; + }; + + // The ``torrent_removed_alert`` is posted whenever a torrent is removed. Since + // the torrent handle in its baseclass will always be invalid (since the torrent + // is already removed) it has the info hash as a member, to identify it. + // It's posted when the ``status_notification`` bit is set in the alert_mask. + // + // Even though the ``handle`` member doesn't point to an existing torrent anymore, + // it is still useful for comparing to other handles, which may also no + // longer point to existing torrents, but to the same non-existing torrents. + // + // The ``torrent_handle`` acts as a ``weak_ptr``, even though its object no + // longer exists, it can still compare equal to another weak pointer which + // points to the same non-existent object. + struct TORRENT_EXPORT torrent_removed_alert: torrent_alert + { + // internal + torrent_removed_alert(torrent_handle const& h, sha1_hash const& ih); + + TORRENT_DEFINE_ALERT(torrent_removed_alert); + const static int static_category = alert::status_notification; + virtual std::string message() const; + sha1_hash info_hash; + }; + + // This alert is posted when the asynchronous read operation initiated by + // a call to torrent_handle::read_piece() is completed. If the read failed, the torrent + // is paused and an error state is set and the buffer member of the alert + // is 0. If successful, ``buffer`` points to a buffer containing all the data + // of the piece. ``piece`` is the piece index that was read. ``size`` is the + // number of bytes that was read. + // + // If the operation fails, ec will indicat what went wrong. + struct TORRENT_EXPORT read_piece_alert: torrent_alert + { + // internal + read_piece_alert(torrent_handle const& h + , int p, boost::shared_array d, int s); + read_piece_alert(torrent_handle h, int p, error_code e); + + TORRENT_DEFINE_ALERT(read_piece_alert); + + const static int static_category = alert::storage_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + error_code ec; + boost::shared_array buffer; + int piece; + int size; + }; + + // This is posted whenever an individual file completes its download. i.e. + // All pieces overlapping this file have passed their hash check. + struct TORRENT_EXPORT file_completed_alert: torrent_alert + { + // internal + file_completed_alert(torrent_handle const& h + , int idx); + + TORRENT_DEFINE_ALERT(file_completed_alert); + + const static int static_category = alert::progress_notification; + virtual std::string message() const; + + // refers to the index of the file that completed. + int index; + }; + + // This is posted as a response to a torrent_handle::rename_file() call, if the rename + // operation succeeds. + struct TORRENT_EXPORT file_renamed_alert: torrent_alert + { + // internal + file_renamed_alert(torrent_handle const& h + , std::string const& n + , int idx); + + TORRENT_DEFINE_ALERT(file_renamed_alert); + + const static int static_category = alert::storage_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + std::string name; + + // refers to the index of the file that was renamed, + // ``name`` is the new name of the file. + int index; + }; + + // This is posted as a response to a torrent_handle::rename_file() call, if the rename + // operation failed. + struct TORRENT_EXPORT file_rename_failed_alert: torrent_alert + { + // internal + file_rename_failed_alert(torrent_handle const& h + , int idx + , error_code ec); + + TORRENT_DEFINE_ALERT(file_rename_failed_alert); + + const static int static_category = alert::storage_notification; + + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // refers to the index of the file that was supposed to be renamed, + // ``error`` is the error code returned from the filesystem. + int index; + error_code error; + }; + + // This alert is generated when a limit is reached that might have a negative impact on + // upload or download rate performance. + struct TORRENT_EXPORT performance_alert: torrent_alert + { + enum performance_warning_t + { + + // This warning means that the number of bytes queued to be written to disk + // exceeds the max disk byte queue setting (``session_settings::max_queued_disk_bytes``). + // This might restrict the download rate, by not queuing up enough write jobs + // to the disk I/O thread. When this alert is posted, peer connections are + // temporarily stopped from downloading, until the queued disk bytes have fallen + // below the limit again. Unless your ``max_queued_disk_bytes`` setting is already + // high, you might want to increase it to get better performance. + outstanding_disk_buffer_limit_reached, + + // This is posted when libtorrent would like to send more requests to a peer, + // but it's limited by ``session_settings::max_out_request_queue``. The queue length + // libtorrent is trying to achieve is determined by the download rate and the + // assumed round-trip-time (``session_settings::request_queue_time``). The assumed + // rount-trip-time is not limited to just the network RTT, but also the remote disk + // access time and message handling time. It defaults to 3 seconds. The target number + // of outstanding requests is set to fill the bandwidth-delay product (assumed RTT + // times download rate divided by number of bytes per request). When this alert + // is posted, there is a risk that the number of outstanding requests is too low + // and limits the download rate. You might want to increase the ``max_out_request_queue`` + // setting. + outstanding_request_limit_reached, + + // This warning is posted when the amount of TCP/IP overhead is greater than the + // upload rate limit. When this happens, the TCP/IP overhead is caused by a much + // faster download rate, triggering TCP ACK packets. These packets eat into the + // rate limit specified to libtorrent. When the overhead traffic is greater than + // the rate limit, libtorrent will not be able to send any actual payload, such + // as piece requests. This means the download rate will suffer, and new requests + // can be sent again. There will be an equilibrium where the download rate, on + // average, is about 20 times the upload rate limit. If you want to maximize the + // download rate, increase the upload rate limit above 5% of your download capacity. + upload_limit_too_low, + + // This is the same warning as ``upload_limit_too_low`` but referring to the download + // limit instead of upload. This suggests that your download rate limit is mcuh lower + // than your upload capacity. Your upload rate will suffer. To maximize upload rate, + // make sure your download rate limit is above 5% of your upload capacity. + download_limit_too_low, + + // We're stalled on the disk. We want to write to the socket, and we can write + // but our send buffer is empty, waiting to be refilled from the disk. + // This either means the disk is slower than the network connection + // or that our send buffer watermark is too small, because we can + // send it all before the disk gets back to us. + // The number of bytes that we keep outstanding, requested from the disk, is calculated + // as follows:: + // + // min(512, max(upload_rate * send_buffer_watermark_factor / 100, send_buffer_watermark)) + // + // If you receive this alert, you migth want to either increase your ``send_buffer_watermark`` + // or ``send_buffer_watermark_factor``. + send_buffer_watermark_too_low, + + // If the half (or more) of all upload slots are set as optimistic unchoke slots, this + // warning is issued. You probably want more regular (rate based) unchoke slots. + too_many_optimistic_unchoke_slots, + + // If the disk write queue ever grows larger than half of the cache size, this warning + // is posted. The disk write queue eats into the total disk cache and leaves very little + // left for the actual cache. This causes the disk cache to oscillate in evicting large + // portions of the cache before allowing peers to download any more, onto the disk write + // queue. Either lower ``max_queued_disk_bytes`` or increase ``cache_size``. + too_high_disk_queue_limit, + + bittyrant_with_no_uplimit, + + // This is generated if outgoing peer connections are failing because of *address in use* + // errors, indicating that ``session_settings::outgoing_ports`` is set and is too small of + // a range. Consider not using the ``outgoing_ports`` setting at all, or widen the range to + // include more ports. + too_few_outgoing_ports, + + too_few_file_descriptors, + + num_warnings + }; + + // internal + performance_alert(torrent_handle const& h + , performance_warning_t w); + + TORRENT_DEFINE_ALERT(performance_alert); + + const static int static_category = alert::performance_warning; + + virtual std::string message() const; + + performance_warning_t warning_code; + }; + + // Generated whenever a torrent changes its state. + struct TORRENT_EXPORT state_changed_alert: torrent_alert + { + // internal + state_changed_alert(torrent_handle const& h + , torrent_status::state_t st + , torrent_status::state_t prev_st); + + TORRENT_DEFINE_ALERT(state_changed_alert); + + const static int static_category = alert::status_notification; + + virtual std::string message() const; + + // the new state of the torrent. + torrent_status::state_t state; + + // the previous state. + torrent_status::state_t prev_state; + }; + + // This alert is generated on tracker time outs, premature disconnects, + // invalid response or a HTTP response other than "200 OK". From the alert + // you can get the handle to the torrent the tracker belongs to. + // + // The ``times_in_row`` member says how many times in a row this tracker has + // failed. ``status_code`` is the code returned from the HTTP server. 401 + // means the tracker needs authentication, 404 means not found etc. If the + // tracker timed out, the code will be set to 0. + struct TORRENT_EXPORT tracker_error_alert: tracker_alert + { + // internal + tracker_error_alert(torrent_handle const& h + , int times + , int status + , std::string const& u + , error_code const& e + , std::string const& m); + + TORRENT_DEFINE_ALERT(tracker_error_alert); + + const static int static_category = alert::tracker_notification | alert::error_notification; + virtual std::string message() const; + + int times_in_row; + int status_code; + error_code error; + std::string msg; + }; + + // This alert is triggered if the tracker reply contains a warning field. + // Usually this means that the tracker announce was successful, but the + // tracker has a message to the client. + struct TORRENT_EXPORT tracker_warning_alert: tracker_alert + { + // internal + tracker_warning_alert(torrent_handle const& h + , std::string const& u + , std::string const& m); + + TORRENT_DEFINE_ALERT(tracker_warning_alert); + + const static int static_category = alert::tracker_notification | alert::error_notification; + virtual std::string message() const; + + // contains the warning message from the tracker. + std::string msg; + }; + + // This alert is generated when a scrape request succeeds. + struct TORRENT_EXPORT scrape_reply_alert: tracker_alert + { + // internal + scrape_reply_alert(torrent_handle const& h + , int incomp + , int comp + , std::string const& u); + + TORRENT_DEFINE_ALERT(scrape_reply_alert); + + virtual std::string message() const; + + // the data returned in the scrape response. These numbers + // may be -1 if the reponse was malformed. + int incomplete; + int complete; + }; + + // If a scrape request fails, this alert is generated. This might be due + // to the tracker timing out, refusing connection or returning an http response + // code indicating an error. + struct TORRENT_EXPORT scrape_failed_alert: tracker_alert + { + // internal + scrape_failed_alert(torrent_handle const& h + , std::string const& u + , error_code const& e); + + scrape_failed_alert(torrent_handle const& h + , std::string const& u + , std::string const& m); + + TORRENT_DEFINE_ALERT(scrape_failed_alert); + + const static int static_category = alert::tracker_notification | alert::error_notification; + virtual std::string message() const; + + // contains a message describing the error. + std::string msg; + }; + + // This alert is only for informational purpose. It is generated when a tracker announce + // succeeds. It is generated regardless what kind of tracker was used, be it UDP, HTTP or + // the DHT. + struct TORRENT_EXPORT tracker_reply_alert: tracker_alert + { + // internal + tracker_reply_alert(torrent_handle const& h + , int np + , std::string const& u); + + TORRENT_DEFINE_ALERT(tracker_reply_alert); + + virtual std::string message() const; + + // tells how many peers the tracker returned in this response. This is + // not expected to be more thant the ``num_want`` settings. These are not necessarily + // all new peers, some of them may already be connected. + int num_peers; + }; + + // This alert is generated each time the DHT receives peers from a node. ``num_peers`` + // is the number of peers we received in this packet. Typically these packets are + // received from multiple DHT nodes, and so the alerts are typically generated + // a few at a time. + struct TORRENT_EXPORT dht_reply_alert: tracker_alert + { + // internal + dht_reply_alert(torrent_handle const& h + , int np); + + TORRENT_DEFINE_ALERT(dht_reply_alert); + + virtual std::string message() const; + + int num_peers; + }; + + // This alert is generated each time a tracker announce is sent (or attempted to be sent). + // There are no extra data members in this alert. The url can be found in the base class + // however. + struct TORRENT_EXPORT tracker_announce_alert: tracker_alert + { + // internal + tracker_announce_alert(torrent_handle const& h + , std::string const& u, int e); + + TORRENT_DEFINE_ALERT(tracker_announce_alert); + + virtual std::string message() const; + + // specifies what event was sent to the tracker. It is defined as: + // + // 0. None + // 1. Completed + // 2. Started + // 3. Stopped + int event; + }; + + // This alert is generated when a finished piece fails its hash check. You can get the handle + // to the torrent which got the failed piece and the index of the piece itself from the alert. + struct TORRENT_EXPORT hash_failed_alert: torrent_alert + { + // internal + hash_failed_alert( + torrent_handle const& h + , int index); + + TORRENT_DEFINE_ALERT(hash_failed_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + + int piece_index; + }; + + // This alert is generated when a peer is banned because it has sent too many corrupt pieces + // to us. ``ip`` is the endpoint to the peer that was banned. + struct TORRENT_EXPORT peer_ban_alert: peer_alert + { + // internal + peer_ban_alert(torrent_handle h, tcp::endpoint const& ep + , peer_id const& peer_id); + + TORRENT_DEFINE_ALERT(peer_ban_alert); + + virtual std::string message() const; + }; + + // This alert is generated when a peer is unsnubbed. Essentially when it was snubbed for stalling + // sending data, and now it started sending data again. + struct TORRENT_EXPORT peer_unsnubbed_alert: peer_alert + { + // internal + peer_unsnubbed_alert(torrent_handle h, tcp::endpoint const& ep + , peer_id const& peer_id); + + TORRENT_DEFINE_ALERT(peer_unsnubbed_alert); + + virtual std::string message() const; + }; + + // This alert is generated when a peer is snubbed, when it stops sending data when we request + // it. + struct TORRENT_EXPORT peer_snubbed_alert: peer_alert + { + // internal + peer_snubbed_alert(torrent_handle h, tcp::endpoint const& ep + , peer_id const& peer_id); + + TORRENT_DEFINE_ALERT(peer_snubbed_alert); + + virtual std::string message() const; + }; + + // This alert is generated when a peer sends invalid data over the peer-peer protocol. The peer + // will be disconnected, but you get its ip address from the alert, to identify it. + struct TORRENT_EXPORT peer_error_alert: peer_alert + { + // internal + peer_error_alert(torrent_handle const& h, tcp::endpoint const& ep + , peer_id const& peer_id, error_code const& e); + + TORRENT_DEFINE_ALERT(peer_error_alert); + + const static int static_category = alert::peer_notification; + virtual std::string message() const; + + // tells you what error caused this alert. + error_code error; + +#ifndef TORRENT_NO_DEPRECATE + std::string msg; +#endif + }; + + // This alert is posted every time an outgoing peer connect attempts succeeds. + struct TORRENT_EXPORT peer_connect_alert: peer_alert + { + // internal + peer_connect_alert(torrent_handle h, tcp::endpoint const& ep + , peer_id const& peer_id, int type); + + TORRENT_DEFINE_ALERT(peer_connect_alert); + + const static int static_category = alert::debug_notification; + virtual std::string message() const; + + int socket_type; + }; + + // This alert is generated when a peer is disconnected for any reason (other than the ones + // covered by peer_error_alert ). + struct TORRENT_EXPORT peer_disconnected_alert: peer_alert + { + // internal + peer_disconnected_alert(torrent_handle const& h, tcp::endpoint const& ep + , peer_id const& peer_id, error_code const& e); + + TORRENT_DEFINE_ALERT(peer_disconnected_alert); + + const static int static_category = alert::debug_notification; + virtual std::string message() const; + + // tells you what error caused peer to disconnect. + error_code error; + +#ifndef TORRENT_NO_DEPRECATE + std::string msg; +#endif + }; + + // This is a debug alert that is generated by an incoming invalid piece request. + // ``ip`` is the address of the peer and the ``request`` is the actual incoming + // request from the peer. See peer_request for more info. + struct TORRENT_EXPORT invalid_request_alert: peer_alert + { + // internal + invalid_request_alert(torrent_handle const& h, tcp::endpoint const& ep + , peer_id const& peer_id, peer_request const& r); + + TORRENT_DEFINE_ALERT(invalid_request_alert); + + virtual std::string message() const; + + peer_request request; + }; + + // This alert is generated when a torrent switches from being a downloader to a seed. + // It will only be generated once per torrent. It contains a torrent_handle to the + // torrent in question. + struct TORRENT_EXPORT torrent_finished_alert: torrent_alert + { + // internal + torrent_finished_alert(const torrent_handle& h); + + TORRENT_DEFINE_ALERT(torrent_finished_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + }; + + // this alert is posted every time a piece completes downloading + // and passes the hash check. This alert derives from torrent_alert + // which contains the torrent_handle to the torrent the piece belongs to. + struct TORRENT_EXPORT piece_finished_alert: torrent_alert + { + // internal + piece_finished_alert( + const torrent_handle& h + , int piece_num); + + TORRENT_DEFINE_ALERT(piece_finished_alert); + + const static int static_category = alert::progress_notification; + virtual std::string message() const; + + // the index of the piece that finished + int piece_index; + }; + + // This alert is generated when a peer rejects or ignores a piece request. + struct TORRENT_EXPORT request_dropped_alert: peer_alert + { + // internal + request_dropped_alert(const torrent_handle& h, tcp::endpoint const& ep + , peer_id const& peer_id, int block_num, int piece_num); + + TORRENT_DEFINE_ALERT(request_dropped_alert); + + const static int static_category = alert::progress_notification + | alert::peer_notification; + virtual std::string message() const; + + int block_index; + int piece_index; + }; + + // This alert is generated when a block request times out. + struct TORRENT_EXPORT block_timeout_alert: peer_alert + { + // internal + block_timeout_alert(const torrent_handle& h, tcp::endpoint const& ep + , peer_id const& peer_id, int block_num, int piece_num); + + TORRENT_DEFINE_ALERT(block_timeout_alert); + + const static int static_category = alert::progress_notification + | alert::peer_notification; + virtual std::string message() const; + + int block_index; + int piece_index; + }; + + // This alert is generated when a block request receives a response. + struct TORRENT_EXPORT block_finished_alert: peer_alert + { + // internal + block_finished_alert(const torrent_handle& h, tcp::endpoint const& ep + , peer_id const& peer_id, int block_num, int piece_num); + + TORRENT_DEFINE_ALERT(block_finished_alert); + + const static int static_category = alert::progress_notification; + virtual std::string message() const; + + int block_index; + int piece_index; + }; + + // This alert is generated when a block request is sent to a peer. + struct TORRENT_EXPORT block_downloading_alert: peer_alert + { + // internal + block_downloading_alert(const torrent_handle& h, tcp::endpoint const& ep + , peer_id const& peer_id, char const* speedmsg, int block_num, int piece_num); + + TORRENT_DEFINE_ALERT(block_downloading_alert); + + const static int static_category = alert::progress_notification; + virtual std::string message() const; + + char const* peer_speedmsg; + int block_index; + int piece_index; + }; + + // This alert is generated when a block is received that was not requested or + // whose request timed out. + struct TORRENT_EXPORT unwanted_block_alert: peer_alert + { + // internal + unwanted_block_alert(const torrent_handle& h, tcp::endpoint const& ep + , peer_id const& peer_id, int block_num, int piece_num); + + TORRENT_DEFINE_ALERT(unwanted_block_alert); + + virtual std::string message() const; + + int block_index; + int piece_index; + }; + + // The ``storage_moved_alert`` is generated when all the disk IO has completed and the + // files have been moved, as an effect of a call to ``torrent_handle::move_storage``. This + // is useful to synchronize with the actual disk. The ``path`` member is the new path of + // the storage. + struct TORRENT_EXPORT storage_moved_alert: torrent_alert + { + // internal + storage_moved_alert(torrent_handle const& h, std::string const& p); + + TORRENT_DEFINE_ALERT(storage_moved_alert); + + const static int static_category = alert::storage_notification; + virtual std::string message() const; + + std::string path; + }; + + // The ``storage_moved_failed_alert`` is generated when an attempt to move the storage, + // via torrent_handle::move_storage(), fails. + struct TORRENT_EXPORT storage_moved_failed_alert: torrent_alert + { + // internal + storage_moved_failed_alert(torrent_handle const& h, error_code const& e); + + TORRENT_DEFINE_ALERT(storage_moved_failed_alert); + + const static int static_category = alert::storage_notification; + virtual std::string message() const; + + error_code error; + }; + + // This alert is generated when a request to delete the files of a torrent complete. + // + // The ``info_hash`` is the info-hash of the torrent that was just deleted. Most of + // the time the torrent_handle in the ``torrent_alert`` will be invalid by the time + // this alert arrives, since the torrent is being deleted. The ``info_hash`` member + // is hence the main way of identifying which torrent just completed the delete. + // + // This alert is posted in the ``storage_notification`` category, and that bit + // needs to be set in the alert_mask. + struct TORRENT_EXPORT torrent_deleted_alert: torrent_alert + { + // internal + torrent_deleted_alert(torrent_handle const& h, sha1_hash const& ih); + + TORRENT_DEFINE_ALERT(torrent_deleted_alert); + + const static int static_category = alert::storage_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + sha1_hash info_hash; + }; + + // This alert is generated when a request to delete the files of a torrent fails. + // Just removing a torrent from the session cannot fail + struct TORRENT_EXPORT torrent_delete_failed_alert: torrent_alert + { + // internal + torrent_delete_failed_alert(torrent_handle const& h, error_code const& e + , sha1_hash const& ih); + + TORRENT_DEFINE_ALERT(torrent_delete_failed_alert); + + const static int static_category = alert::storage_notification + | alert::error_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // tells you why it failed. + error_code error; + + // the info hash of the torrent whose files failed to be deleted + sha1_hash info_hash; + +#ifndef TORRENT_NO_DEPRECATE + std::string msg; +#endif + }; + + // This alert is generated as a response to a ``torrent_handle::save_resume_data`` request. + // It is generated once the disk IO thread is done writing the state for this torrent. + struct TORRENT_EXPORT save_resume_data_alert: torrent_alert + { + // internal + save_resume_data_alert(boost::shared_ptr const& rd + , torrent_handle const& h); + + TORRENT_DEFINE_ALERT(save_resume_data_alert); + + const static int static_category = alert::storage_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // points to the resume data. + boost::shared_ptr resume_data; + }; + + // This alert is generated instead of ``save_resume_data_alert`` if there was an error + // generating the resume data. ``error`` describes what went wrong. + struct TORRENT_EXPORT save_resume_data_failed_alert: torrent_alert + { + // internal + save_resume_data_failed_alert(torrent_handle const& h + , error_code const& e); + + TORRENT_DEFINE_ALERT(save_resume_data_failed_alert); + + const static int static_category = alert::storage_notification + | alert::error_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + error_code error; + +#ifndef TORRENT_NO_DEPRECATE + std::string msg; +#endif + }; + + // This alert is generated as a response to a ``torrent_handle::pause`` request. It is + // generated once all disk IO is complete and the files in the torrent have been closed. + // This is useful for synchronizing with the disk. + struct TORRENT_EXPORT torrent_paused_alert: torrent_alert + { + // internal + torrent_paused_alert(torrent_handle const& h); + + TORRENT_DEFINE_ALERT(torrent_paused_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + }; + + // This alert is generated as a response to a torrent_handle::resume() request. It is + // generated when a torrent goes from a paused state to an active state. + struct TORRENT_EXPORT torrent_resumed_alert: torrent_alert + { + // internal + torrent_resumed_alert(torrent_handle const& h); + + TORRENT_DEFINE_ALERT(torrent_resumed_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + }; + + // This alert is posted when a torrent completes checking. i.e. when it transitions + // out of the ``checking files`` state into a state where it is ready to start downloading + struct TORRENT_EXPORT torrent_checked_alert: torrent_alert + { + // internal + torrent_checked_alert(torrent_handle const& h); + + TORRENT_DEFINE_ALERT(torrent_checked_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + }; + + // This alert is generated when a HTTP seed name lookup fails. + struct TORRENT_EXPORT url_seed_alert: torrent_alert + { + // internal + url_seed_alert( + torrent_handle const& h + , std::string const& u + , error_code const& e); + url_seed_alert( + torrent_handle const& h + , std::string const& u + , std::string const& m); + + TORRENT_DEFINE_ALERT(url_seed_alert); + + const static int static_category = alert::peer_notification | alert::error_notification; + virtual std::string message() const; + + // the HTTP seed that failed + std::string url; + + // the error message, potentially from the server + std::string msg; + }; + + // If the storage fails to read or write files that it needs access to, this alert is + // generated and the torrent is paused. + struct TORRENT_EXPORT file_error_alert: torrent_alert + { + // internal + file_error_alert( + std::string const& f + , torrent_handle const& h + , error_code const& e); + + TORRENT_DEFINE_ALERT(file_error_alert); + + const static int static_category = alert::status_notification + | alert::error_notification + | alert::storage_notification; + virtual std::string message() const; + + // the path to the file that was accessed when the error occurred. + std::string file; + + // the error code describing the error. + error_code error; + +#ifndef TORRENT_NO_DEPRECATE + std::string msg; +#endif + }; + + // This alert is generated when the metadata has been completely received and the info-hash + // failed to match it. i.e. the metadata that was received was corrupt. libtorrent will + // automatically retry to fetch it in this case. This is only relevant when running a + // torrent-less download, with the metadata extension provided by libtorrent. + struct TORRENT_EXPORT metadata_failed_alert: torrent_alert + { + // internal + metadata_failed_alert(const torrent_handle& h, error_code e); + + TORRENT_DEFINE_ALERT(metadata_failed_alert); + + const static int static_category = alert::error_notification; + virtual std::string message() const; + + // the error that occurred + error_code error; + }; + + // This alert is generated when the metadata has been completely received and the torrent + // can start downloading. It is not generated on torrents that are started with metadata, but + // only those that needs to download it from peers (when utilizing the libtorrent extension). + // + // There are no additional data members in this alert. + // + // Typically, when receiving this alert, you would want to save the torrent file in order + // to load it back up again when the session is restarted. Here's an example snippet of + // code to do that:: + // + // torrent_handle h = alert->handle(); + // if (h.is_valid()) { + // boost::intrusive_ptr ti = h.torrent_file(); + // create_torrent ct(*ti); + // entry te = ct.generate(); + // std::vector buffer; + // bencode(std::back_inserter(buffer), te); + // FILE* f = fopen((to_hex(ti->info_hash().to_string()) + ".torrent").c_str(), "wb+"); + // if (f) { + // fwrite(&buffer[0], 1, buffer.size(), f); + // fclose(f); + // } + // } + // + struct TORRENT_EXPORT metadata_received_alert: torrent_alert + { + // internal + metadata_received_alert( + const torrent_handle& h); + + TORRENT_DEFINE_ALERT(metadata_received_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + }; + + // This alert is posted when there is an error on the UDP socket. The + // UDP socket is used for all uTP, DHT and UDP tracker traffic. It's + // global to the session. + struct TORRENT_EXPORT udp_error_alert: alert + { + // internal + udp_error_alert( + udp::endpoint const& ep + , error_code const& ec); + + TORRENT_DEFINE_ALERT(udp_error_alert); + + const static int static_category = alert::error_notification; + virtual std::string message() const; + + // the source address associated with the error (if any) + udp::endpoint endpoint; + + // the error code describing the error + error_code error; + }; + + // Whenever libtorrent learns about the machines external IP, this alert is + // generated. The external IP address can be acquired from the tracker (if it + // supports that) or from peers that supports the extension protocol. + // The address can be accessed through the ``external_address`` member. + struct TORRENT_EXPORT external_ip_alert: alert + { + // internal + external_ip_alert(address const& ip); + + TORRENT_DEFINE_ALERT(external_ip_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + + // the IP address that is believed to be our external IP + address external_address; + }; + + // This alert is generated when none of the ports, given in the port range, to + // session can be opened for listening. The ``endpoint`` member is the + // interface and port that failed, ``error`` is the error code describing + // the failure. + // + // libtorrent may sometimes try to listen on port 0, if all other ports failed. + // Port 0 asks the operating system to pick a port that's free). If that fails + // you may see a listen_failed_alert with port 0 even if you didn't ask to + // listen on it. + struct TORRENT_EXPORT listen_failed_alert: alert + { + enum socket_type_t { tcp, tcp_ssl, udp, i2p, socks5 }; + + // internal + listen_failed_alert( + tcp::endpoint const& ep + , int op + , error_code const& ec + , socket_type_t t); + + TORRENT_DEFINE_ALERT(listen_failed_alert); + + const static int static_category = alert::status_notification | alert::error_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // the endpoint libtorrent attempted to listen on + tcp::endpoint endpoint; + + // the error the system returned + error_code error; + + enum op_t + { + parse_addr, open, bind, listen, get_peer_name, accept + }; + + // the specific low level operation that failed. See op_t. + int operation; + + // the type of listen socket this alert refers to. + socket_type_t sock_type; + }; + + // This alert is posted when the listen port succeeds to be opened on a + // particular interface. ``endpoint`` is the endpoint that successfully + // was opened for listening. + struct TORRENT_EXPORT listen_succeeded_alert: alert + { + enum socket_type_t { tcp, tcp_ssl, udp }; + + // internal + listen_succeeded_alert(tcp::endpoint const& ep, socket_type_t t); + + TORRENT_DEFINE_ALERT(listen_succeeded_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // the endpoint libtorrent ended up listening on. The address + // refers to the local interface and the port is the listen port. + tcp::endpoint endpoint; + + // the type of listen socket this alert refers to. + socket_type_t sock_type; + }; + + // This alert is generated when a NAT router was successfully found but some + // part of the port mapping request failed. It contains a text message that + // may help the user figure out what is wrong. This alert is not generated in + // case it appears the client is not running on a NAT:ed network or if it + // appears there is no NAT router that can be remote controlled to add port + // mappings. + struct TORRENT_EXPORT portmap_error_alert: alert + { + // internal + portmap_error_alert(int i, int t, error_code const& e); + + TORRENT_DEFINE_ALERT(portmap_error_alert); + + const static int static_category = alert::port_mapping_notification + | alert::error_notification; + virtual std::string message() const; + + // refers to the mapping index of the port map that failed, i.e. + // the index returned from add_mapping(). + int mapping; + + // is 0 for NAT-PMP and 1 for UPnP. + int map_type; + + // tells you what failed. + error_code error; +#ifndef TORRENT_NO_DEPRECATE + std::string msg; +#endif + }; + + // This alert is generated when a NAT router was successfully found and + // a port was successfully mapped on it. On a NAT:ed network with a NAT-PMP + // capable router, this is typically generated once when mapping the TCP + // port and, if DHT is enabled, when the UDP port is mapped. + struct TORRENT_EXPORT portmap_alert: alert + { + // internal + portmap_alert(int i, int port, int t); + + TORRENT_DEFINE_ALERT(portmap_alert); + + const static int static_category = alert::port_mapping_notification; + virtual std::string message() const; + + // refers to the mapping index of the port map that failed, i.e. + // the index returned from add_mapping(). + int mapping; + + // the external port allocated for the mapping. + int external_port; + + // 0 for NAT-PMP and 1 for UPnP. + int map_type; + }; + + // This alert is generated to log informational events related to either + // UPnP or NAT-PMP. They contain a log line and the type (0 = NAT-PMP + // and 1 = UPnP). Displaying these messages to an end user is only useful + // for debugging the UPnP or NAT-PMP implementation. + struct TORRENT_EXPORT portmap_log_alert: alert + { + // internal + portmap_log_alert(int t, std::string const& m); + + TORRENT_DEFINE_ALERT(portmap_log_alert); + + const static int static_category = alert::port_mapping_notification; + virtual std::string message() const; + + int map_type; + std::string msg; + }; + + // This alert is generated when a fastresume file has been passed to add_torrent() but the + // files on disk did not match the fastresume file. The error_code explains the reason why the + // resume file was rejected. + struct TORRENT_EXPORT fastresume_rejected_alert: torrent_alert + { + // internal + fastresume_rejected_alert(torrent_handle const& h + , error_code const& e); + + TORRENT_DEFINE_ALERT(fastresume_rejected_alert); + + const static int static_category = alert::status_notification + | alert::error_notification; + virtual std::string message() const; + + error_code error; + +#ifndef TORRENT_NO_DEPRECATE + std::string msg; +#endif + }; + + // This alert is posted when an incoming peer connection, or a peer that's about to be added + // to our peer list, is blocked for some reason. This could be any of: + // + // * the IP filter + // * i2p mixed mode restrictions (a normal peer is not allowed on an i2p swarm) + // * the port filter + // * the peer has a low port and ``no_connect_privileged_ports`` is enabled + // * the protocol of the peer is blocked (uTP/TCP blocking) + struct TORRENT_EXPORT peer_blocked_alert: torrent_alert + { + // internal + peer_blocked_alert(torrent_handle const& h, address const& i + , int r); + + TORRENT_DEFINE_ALERT(peer_blocked_alert); + + const static int static_category = alert::ip_block_notification; + virtual std::string message() const; + + // the address that was blocked. + address ip; + + enum reason_t + { + ip_filter, + port_filter, + i2p_mixed, + privileged_ports, + utp_disabled, + tcp_disabled + }; + + int reason; + }; + + // This alert is generated when a DHT node announces to an info-hash on our + // DHT node. It belongs to the ``dht_notification`` category. + struct TORRENT_EXPORT dht_announce_alert: alert + { + // internal + dht_announce_alert(address const& i, int p + , sha1_hash const& ih); + + TORRENT_DEFINE_ALERT(dht_announce_alert); + + const static int static_category = alert::dht_notification; + virtual std::string message() const; + + address ip; + int port; + sha1_hash info_hash; + }; + + // This alert is generated when a DHT node sends a ``get_peers`` message to + // our DHT node. It belongs to the ``dht_notification`` category. + struct TORRENT_EXPORT dht_get_peers_alert: alert + { + // internal + dht_get_peers_alert(sha1_hash const& ih); + + TORRENT_DEFINE_ALERT(dht_get_peers_alert); + + const static int static_category = alert::dht_notification; + virtual std::string message() const; + + sha1_hash info_hash; + }; + + // This alert is posted approximately once every second, and it contains + // byte counters of most statistics that's tracked for torrents. Each active + // torrent posts these alerts regularly. + struct TORRENT_EXPORT stats_alert: torrent_alert + { + // internal + stats_alert(torrent_handle const& h, int interval + , stat const& s); + + TORRENT_DEFINE_ALERT(stats_alert); + + const static int static_category = alert::stats_notification; + virtual std::string message() const; + + enum stats_channel + { + upload_payload, + upload_protocol, + download_payload, + download_protocol, +#ifndef TORRENT_DISABLE_FULL_STATS + upload_ip_protocol, + upload_dht_protocol, + upload_tracker_protocol, + download_ip_protocol, + download_dht_protocol, + download_tracker_protocol, +#endif + num_channels + }; + + // an array of samples. The enum describes what each sample is a + // measurement of. All of these are raw, and not smoothing is performed. + int transferred[num_channels]; + + // the number of milliseconds during which these stats were collected. + // This is typically just above 1000, but if CPU is limited, it may be + // higher than that. + int interval; + }; + + // This alert is posted when the disk cache has been flushed for a specific + // torrent as a result of a call to torrent_handle::flush_cache(). This + // alert belongs to the ``storage_notification`` category, which must be + // enabled to let this alert through. The alert is also posted when removing + // a torrent from the session, once the outstanding cache flush is complete + // and the torrent does no longer have any files open. + struct TORRENT_EXPORT cache_flushed_alert: torrent_alert + { + // internal + cache_flushed_alert(torrent_handle const& h); + + TORRENT_DEFINE_ALERT(cache_flushed_alert); + + const static int static_category = alert::storage_notification; + }; + + // This alert is posted when a bittorrent feature is blocked because of the + // anonymous mode. For instance, if the tracker proxy is not set up, no + // trackers will be used, because trackers can only be used through proxies + // when in anonymous mode. + struct TORRENT_EXPORT anonymous_mode_alert: torrent_alert + { + // internal + anonymous_mode_alert(torrent_handle const& h + , int k, std::string const& s); + + TORRENT_DEFINE_ALERT(anonymous_mode_alert); + + const static int static_category = alert::error_notification; + virtual std::string message() const; + + enum kind_t + { + // means that there's no proxy set up for tracker + // communication and the tracker will not be contacted. + // The tracker which this failed for is specified in the ``str`` member. + tracker_not_anonymous = 0 + }; + + // specifies what error this is, see kind_t. + int kind; + std::string str; + }; + + // This alert is generated when we receive a local service discovery message + // from a peer for a torrent we're currently participating in. + struct TORRENT_EXPORT lsd_peer_alert: peer_alert + { + // internal + lsd_peer_alert(torrent_handle const& h + , tcp::endpoint const& i); + + TORRENT_DEFINE_ALERT(lsd_peer_alert); + + const static int static_category = alert::peer_notification; + virtual std::string message() const; + }; + + // This alert is posted whenever a tracker responds with a ``trackerid``. + // The tracker ID is like a cookie. The libtorrent will store the tracker ID + // for this tracker and repeat it in subsequent announces. + struct TORRENT_EXPORT trackerid_alert: tracker_alert + { + // internal + trackerid_alert(torrent_handle const& h + , std::string const& u + , const std::string& id); + + TORRENT_DEFINE_ALERT(trackerid_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + + // The tracker ID returned by the tracker + std::string trackerid; + }; + + // This alert is posted when the initial DHT bootstrap is done. + struct TORRENT_EXPORT dht_bootstrap_alert: alert + { + // internal + dht_bootstrap_alert(); + + TORRENT_DEFINE_ALERT(dht_bootstrap_alert); + + const static int static_category = alert::dht_notification; + virtual std::string message() const; + }; + + // This alert is posted on RSS feed events such as start of RSS feed updates, + // successful completed updates and errors during updates. + // + // This alert is only posted if the ``rss_notifications`` category is enabled + // in the alert_mask. + struct TORRENT_EXPORT rss_alert: alert + { + // internal + rss_alert(feed_handle h, std::string const& u, int s, error_code const& ec); + + TORRENT_DEFINE_ALERT(rss_alert); + + const static int static_category = alert::rss_notification; + virtual std::string message() const; + + enum state_t + { + // An update of this feed was just initiated, it will either succeed + // or fail soon. + state_updating, + + // The feed just completed a successful update, there may be new items + // in it. If you're adding torrents manually, you may want to request + // the feed status of the feed and look through the ``items`` vector. + state_updated, + + // An error just occurred. See the ``error`` field for information on + // what went wrong. + state_error + }; + + // the handle to the feed which generated this alert. + feed_handle handle; + + // a short cut to access the url of the feed, without + // having to call feed_handle::get_settings(). + std::string url; + + // one of the values from rss_alert::state_t. + int state; + + // an error code used for when an error occurs on the feed. + error_code error; + }; + + // This is posted whenever a torrent is transitioned into the error state. + struct TORRENT_EXPORT torrent_error_alert: torrent_alert + { + // internal + torrent_error_alert(torrent_handle const& h + , error_code const& e); + + TORRENT_DEFINE_ALERT(torrent_error_alert); + + const static int static_category = alert::error_notification | alert::status_notification; + virtual std::string message() const; + + // specifies which error the torrent encountered. + error_code error; + }; + + // This is always posted for SSL torrents. This is a reminder to the client that + // the torrent won't work unless torrent_handle::set_ssl_certificate() is called with + // a valid certificate. Valid certificates MUST be signed by the SSL certificate + // in the .torrent file. + struct TORRENT_EXPORT torrent_need_cert_alert: torrent_alert + { + // internal + torrent_need_cert_alert(torrent_handle const& h); + + TORRENT_DEFINE_ALERT(torrent_need_cert_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + error_code error; + }; + + // The incoming connection alert is posted every time we successfully accept + // an incoming connection, through any mean. The most straigh-forward ways + // of accepting incoming connections are through the TCP listen socket and + // the UDP listen socket for uTP sockets. However, connections may also be + // accepted ofer a Socks5 or i2p listen socket, or via a torrent specific + // listen socket for SSL torrents. + struct TORRENT_EXPORT incoming_connection_alert: alert + { + // internal + incoming_connection_alert(int t, tcp::endpoint const& i); + + TORRENT_DEFINE_ALERT(incoming_connection_alert); + + const static int static_category = alert::peer_notification; + virtual std::string message() const; + + // tells you what kind of socket the connection was accepted + // as: + // + // 0. none (no socket instantiated) + // 1. TCP + // 2. Socks5 + // 3. HTTP + // 4. uTP + // 5. i2p + // 6. SSL/TCP + // 7. SSL/Socks5 + // 8. HTTPS (SSL/HTTP) + // 9. SSL/uTP + // + int socket_type; + + // is the IP address and port the connection came from. + tcp::endpoint ip; + }; + + // This alert is always posted when a torrent was attempted to be added + // and contains the return status of the add operation. The torrent handle of the new + // torrent can be found in the base class' ``handle`` member. If adding + // the torrent failed, ``error`` contains the error code. + struct TORRENT_EXPORT add_torrent_alert : torrent_alert + { + // internal + add_torrent_alert(torrent_handle h, add_torrent_params const& p, error_code ec); + + TORRENT_DEFINE_ALERT(add_torrent_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // a copy of the parameters used when adding the torrent, it can be used + // to identify which invocation to ``async_add_torrent()`` caused this alert. + add_torrent_params params; + + // set to the error, if one occurred while adding the torrent. + error_code error; + }; + + // This alert is only posted when requested by the user, by calling session::post_torrent_updates() + // on the session. It contains the torrent status of all torrents that changed + // since last time this message was posted. Its category is ``status_notification``, but + // it's not subject to filtering, since it's only manually posted anyway. + struct TORRENT_EXPORT state_update_alert : alert + { + TORRENT_DEFINE_ALERT(state_update_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // contains the torrent status of all torrents that changed since last time + // this message was posted. Note that you can map a torrent status to a specific torrent + // via its ``handle`` member. The receiving end is suggested to have all torrents sorted + // by the torrent_handle or hashed by it, for efficient updates. + std::vector status; + }; + + // When a torrent changes its info-hash, this alert is posted. This only happens in very + // specific cases. For instance, when a torrent is downloaded from a URL, the true info + // hash is not known immediately. First the .torrent file must be downloaded and parsed. + // + // Once this download completes, the ``torrent_update_alert`` is posted to notify the client + // of the info-hash changing. + struct TORRENT_EXPORT torrent_update_alert : torrent_alert + { + // internal + torrent_update_alert(torrent_handle h, sha1_hash const& old_hash, sha1_hash const& new_hash); + + TORRENT_DEFINE_ALERT(torrent_update_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // ``old_ih`` and ``new_ih`` are the previous and new info-hash for the torrent, respectively. + sha1_hash old_ih; + sha1_hash new_ih; + }; + + // This alert is posted every time a new RSS item (i.e. torrent) is received + // from an RSS feed. + // + // It is only posted if the ``rss_notifications`` category is enabled in the + // alert_mask. + struct TORRENT_EXPORT rss_item_alert : alert + { + // internal + rss_item_alert(feed_handle h, feed_item const& item); + + TORRENT_DEFINE_ALERT(rss_item_alert); + + const static int static_category = alert::rss_notification; + virtual std::string message() const; + + feed_handle handle; + feed_item item; + }; + + // posted when something fails in the DHT. This is not necessarily a fatal + // error, but it could prevent proper operation + struct TORRENT_EXPORT dht_error_alert: alert + { + // internal + dht_error_alert(int op, error_code const& ec); + + TORRENT_DEFINE_ALERT(dht_error_alert); + + const static int static_category = alert::error_notification + | alert::dht_notification; + virtual std::string message() const; + + // the error code + error_code error; + + enum op_t + { + unknown, + hostname_lookup + }; + + // the operation that failed + op_t operation; + }; + + // this alert is posted as a response to a call to session::get_item(), + // specifically the overload for looking up immutable items in the DHT. + struct TORRENT_EXPORT dht_immutable_item_alert: alert + { + dht_immutable_item_alert(sha1_hash const& t, entry const& i); + + TORRENT_DEFINE_ALERT(dht_immutable_item_alert); + + const static int static_category = alert::error_notification + | alert::dht_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // the target hash of the immutable item. This must + // match the sha-1 hash of the bencoded form of ``item``. + sha1_hash target; + + // the data for this item + entry item; + }; + + // this alert is posted as a response to a call to session::get_item(), + // specifically the overload for looking up mutable items in the DHT. + struct TORRENT_EXPORT dht_mutable_item_alert: alert + { + dht_mutable_item_alert(boost::array k + , boost::array sig + , boost::uint64_t sequence + , std::string const& s + , entry const& i); + + TORRENT_DEFINE_ALERT(dht_mutable_item_alert); + + const static int static_category = alert::error_notification + | alert::dht_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // the public key that was looked up + boost::array key; + + // the signature of the data. This is not the signature of the + // plain encoded form of the item, but it includes the sequence number + // and possibly the hash as well. See the dht_store document for more + // information. This is primarily useful for echoing back in a store + // request. + boost::array signature; + + // the sequence number of this item + boost::uint64_t seq; + + // the salf, if any, used to lookup and store this item. If no + // salt was used, this is an empty string + std::string salt; + + // the data for this item + entry item; + }; + + // this is posted when a DHT put operation completes. This is useful if the + // client is waiting for a put to complete before shutting down for instance. + struct TORRENT_EXPORT dht_put_alert: alert + { + // internal + dht_put_alert(sha1_hash const& t); + dht_put_alert(boost::array key + , boost::array sig + , std::string s + , boost::uint64_t sequence_number); + + TORRENT_DEFINE_ALERT(dht_put_alert); + + const static int static_category = alert::dht_notification; + virtual std::string message() const; + + // the target hash the item was stored under if this was an *immutable* + // item. + sha1_hash target; + + // if a mutable item was stored, these are the public key, signature, + // salt and sequence number the item was stored under. + boost::array public_key; + boost::array signature; + std::string salt; + boost::uint64_t seq; + }; + + // this alert is used to report errors in the i2p SAM connection + struct TORRENT_EXPORT i2p_alert : alert + { + i2p_alert(error_code const& ec); + + TORRENT_DEFINE_ALERT(i2p_alert); + + const static int static_category = alert::error_notification; + virtual std::string message() const; + + // the error that occurred in the i2p SAM connection + error_code error; + }; + +#undef TORRENT_DEFINE_ALERT + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/alloca.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/alloca.hpp new file mode 100644 index 0000000000..c5521ff469 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/alloca.hpp @@ -0,0 +1,54 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ALLOCA + +#include "libtorrent/config.hpp" + +#if defined TORRENT_WINDOWS || defined TORRENT_MINGW + +#include +#define TORRENT_ALLOCA(t, n) static_cast(_alloca(sizeof(t) * (n))) + +#elif defined TORRENT_BSD + +#include +#define TORRENT_ALLOCA(t, n) static_cast(alloca(sizeof(t) * (n))) + +#else + +#include +#define TORRENT_ALLOCA(t, n) static_cast(alloca(sizeof(t) * (n))) + +#endif + +#endif diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/allocator.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/allocator.hpp new file mode 100644 index 0000000000..b67816986a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/allocator.hpp @@ -0,0 +1,79 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ALLOCATOR_HPP_INCLUDED +#define TORRENT_ALLOCATOR_HPP_INCLUDED + +#include +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + TORRENT_EXTRA_EXPORT int page_size(); + + struct TORRENT_EXTRA_EXPORT page_aligned_allocator + { + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + static char* malloc(const size_type bytes); + static void free(char* block); + }; + + struct TORRENT_EXTRA_EXPORT aligned_holder + { + aligned_holder(): m_buf(0) {} + aligned_holder(int size): m_buf(page_aligned_allocator::malloc(size)) {} + ~aligned_holder() { if (m_buf) page_aligned_allocator::free(m_buf); } + char* get() const { return m_buf; } + void reset(char* buf = 0) + { + if (m_buf) page_aligned_allocator::free(m_buf); + m_buf = buf; + } + void swap(aligned_holder& h) + { + char* tmp = m_buf; + m_buf = h.m_buf; + h.m_buf = tmp; + } + private: + aligned_holder(aligned_holder const&); + aligned_holder& operator=(aligned_holder const&); + char* m_buf; + }; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/assert.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/assert.hpp new file mode 100644 index 0000000000..847648297c --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/assert.hpp @@ -0,0 +1,89 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ASSERT + +#include "libtorrent/config.hpp" + +#if defined TORRENT_DEBUG || defined TORRENT_ASIO_DEBUGGING \ + || TORRENT_RELEASE_ASSERTS || defined TORRENT_DEBUG_BUFFERS +#include +std::string demangle(char const* name); +TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth = 0); +#endif + +#if TORRENT_USE_ASSERTS + +#if TORRENT_PRODUCTION_ASSERTS +extern char const* libtorrent_assert_log; +#endif + +#if (defined __linux__ || defined __MACH__) && defined __GNUC__ && !TORRENT_USE_SYSTEM_ASSERT + +#if TORRENT_USE_IOSTREAM +#include +#endif + +TORRENT_EXPORT void assert_fail(const char* expr, int line, char const* file + , char const* function, char const* val, int kind = 0); + +#define TORRENT_ASSERT_PRECOND(x) \ + do { if (x) {} else assert_fail(#x, __LINE__, __FILE__, __PRETTY_FUNCTION__, "", 1); } while (false) + +#define TORRENT_ASSERT(x) \ + do { if (x) {} else assert_fail(#x, __LINE__, __FILE__, __PRETTY_FUNCTION__, "", 0); } while (false) + +#if TORRENT_USE_IOSTREAM +#define TORRENT_ASSERT_VAL(x, y) \ + do { if (x) {} else { std::stringstream __s__; __s__ << #y ": " << y; \ + assert_fail(#x, __LINE__, __FILE__, __PRETTY_FUNCTION__, __s__.str().c_str(), 0); } } while (false) +#else +#define TORRENT_ASSERT_VAL(x, y) TORRENT_ASSERT(x) +#endif + +#else +#include +#define TORRENT_ASSERT_PRECOND(x) assert(x) +#define TORRENT_ASSERT(x) assert(x) +#define TORRENT_ASSERT_VAL(x, y) assert(x) +#endif + +#else // TORRENT_USE_ASSERTS + +#define TORRENT_ASSERT_PRECOND(a) do {} while(false) +#define TORRENT_ASSERT(a) do {} while(false) +#define TORRENT_ASSERT_VAL(a, b) do {} while(false) + +#endif // TORRENT_USE_ASSERTS + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/aux_/session_impl.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/aux_/session_impl.hpp new file mode 100644 index 0000000000..18dab8b9bc --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/aux_/session_impl.hpp @@ -0,0 +1,1286 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SESSION_IMPL_HPP_INCLUDED +#define TORRENT_SESSION_IMPL_HPP_INCLUDED + +#include +#include +#include +#include +#include // for va_start, va_end + +#ifndef TORRENT_DISABLE_GEO_IP +#ifdef WITH_SHIPPED_GEOIP_H +#include "libtorrent/GeoIP.h" +#else +#include +#endif +#endif + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/ip_voter.hpp" +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/debug.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/ip_filter.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/session_status.hpp" +#include "libtorrent/add_torrent_params.hpp" +#include "libtorrent/stat.hpp" +#include "libtorrent/file_pool.hpp" +#include "libtorrent/bandwidth_manager.hpp" +#include "libtorrent/socket_type.hpp" +#include "libtorrent/connection_queue.hpp" +#include "libtorrent/disk_io_thread.hpp" +#include "libtorrent/udp_socket.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/thread.hpp" +#include "libtorrent/policy.hpp" // for policy::peer +#include "libtorrent/alert_manager.hpp" // for alert_manager +#include "libtorrent/deadline_timer.hpp" +#include "libtorrent/socket_io.hpp" // for print_address +#include "libtorrent/address.hpp" +#include "libtorrent/utp_socket_manager.hpp" +#include "libtorrent/bloom_filter.hpp" +#include "libtorrent/rss.hpp" +#include "libtorrent/alert_dispatcher.hpp" +#include "libtorrent/kademlia/dht_observer.hpp" + +#if TORRENT_COMPLETE_TYPES_REQUIRED +#include "libtorrent/peer_connection.hpp" +#endif + +#ifdef TORRENT_USE_OPENSSL +#include +#endif + +#if defined TORRENT_STATS && defined __MACH__ +#include +#include +#include +#include +#endif + +namespace libtorrent +{ + + struct plugin; + class upnp; + class natpmp; + class lsd; + struct fingerprint; + class torrent; + class alert; + + namespace dht + { + struct dht_tracker; + class item; + } + + struct bencode_map_entry; + + struct listen_socket_t + { + listen_socket_t(): external_port(0), ssl(false) {} + + // this is typically empty but can be set + // to the WAN IP address of NAT-PMP or UPnP router + address external_address; + + // this is typically set to the same as the local + // listen port. In case a NAT port forward was + // successfully opened, this will be set to the + // port that is open on the external (NAT) interface + // on the NAT box itself. This is the port that has + // to be published to peers, since this is the port + // the client is reachable through. + int external_port; + + // set to true if this is an SSL listen socket + bool ssl; + + // the actual socket + boost::shared_ptr sock; + }; + + namespace aux + { + struct session_impl; + +#if defined TORRENT_STATS && !defined __MACH__ + struct vm_statistics_data_t + { + boost::uint64_t active_count; + boost::uint64_t inactive_count; + boost::uint64_t wire_count; + boost::uint64_t free_count; + boost::uint64_t pageins; + boost::uint64_t pageouts; + boost::uint64_t faults; + }; +#endif + + struct thread_cpu_usage + { + ptime user_time; + ptime system_time; + }; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + struct tracker_logger; +#endif + + // used to initialize the g_current_time before + // anything else + struct initialize_timer + { + initialize_timer(); + }; + + TORRENT_EXPORT std::pair settings_map(); + + // this is the link between the main thread and the + // thread started to run the main downloader loop + struct TORRENT_EXTRA_EXPORT session_impl + : alert_dispatcher + , dht::dht_observer + , boost::noncopyable + , initialize_timer + , udp_socket_observer + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + // this needs to be destructed last, since other components may log + // things as they are being destructed. That's why it's declared at + // the top of session_impl + boost::shared_ptr m_logger; +#endif + + // the size of each allocation that is chained in the send buffer + enum { send_buffer_size = 128 }; + +#ifdef TORRENT_DEBUG + friend class ::libtorrent::peer_connection; +#endif + friend struct checker_impl; + friend class invariant_access; + typedef std::set > connection_map; + typedef std::map > torrent_map; + + session_impl( + std::pair listen_port_range + , fingerprint const& cl_fprint + , char const* listen_interface + , boost::uint32_t alert_mask); + virtual ~session_impl(); + void update_dht_announce_interval(); + void init(); + void start_session(); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + void set_log_path(std::string const& p) { m_logpath = p; } +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + void add_extension(boost::function( + torrent*, void*)> ext); + void add_ses_extension(boost::shared_ptr ext); +#endif +#if TORRENT_USE_ASSERTS + bool has_peer(peer_connection const* p) const + { + TORRENT_ASSERT(is_network_thread()); + return std::find_if(m_connections.begin(), m_connections.end() + , boost::bind(&boost::intrusive_ptr::get, _1) == p) + != m_connections.end(); + } + // this is set while the session is building the + // torrent status update message + bool m_posting_torrent_updates; +#endif + void main_thread(); + + void open_listen_port(int flags, error_code& ec); + + // prioritize this torrent to be allocated some connection + // attempts, because this torrent needs more peers. + // this is typically done when a torrent starts out and + // need the initial push to connect peers + void prioritize_connections(boost::weak_ptr t); + + // if we are listening on an IPv6 interface + // this will return one of the IPv6 addresses on this + // machine, otherwise just an empty endpoint + tcp::endpoint get_ipv6_interface() const; + tcp::endpoint get_ipv4_interface() const; + + void async_accept(boost::shared_ptr const& listener, bool ssl); + void on_accept_connection(boost::shared_ptr const& s + , boost::weak_ptr listener, error_code const& e, bool ssl); + void on_socks_accept(boost::shared_ptr const& s + , error_code const& e); + + void incoming_connection(boost::shared_ptr const& s); + +#if TORRENT_USE_ASSERTS + bool is_network_thread() const + { +#if defined BOOST_HAS_PTHREADS + if (m_network_thread == 0) return true; + return m_network_thread == pthread_self(); +#endif + return true; + } + bool is_not_network_thread() const + { +#if defined BOOST_HAS_PTHREADS + if (m_network_thread == 0) return true; + return m_network_thread != pthread_self(); +#endif + return true; + } +#endif + + feed_handle add_feed(feed_settings const& feed); + void remove_feed(feed_handle h); + void get_feeds(std::vector* f) const; + + boost::weak_ptr find_torrent(sha1_hash const& info_hash) const; + boost::weak_ptr find_torrent(std::string const& uuid) const; + boost::weak_ptr find_disconnect_candidate_torrent() const; + + peer_id const& get_peer_id() const { return m_peer_id; } + + void close_connection(peer_connection const* p, error_code const& ec); + + void set_settings(session_settings const& s); + session_settings const& settings() const { return m_settings; } + +#ifndef TORRENT_DISABLE_DHT + void add_dht_node_name(std::pair const& node); + void add_dht_node(udp::endpoint n); + void add_dht_router(std::pair const& node); + void set_dht_settings(dht_settings const& s); + dht_settings const& get_dht_settings() const { return m_dht_settings; } + void start_dht(); + void stop_dht(); + void start_dht(entry const& startup_state); + + // this is called for torrents when they are started + // it will prioritize them for announcing to + // the DHT, to get the initial peers quickly + void prioritize_dht(boost::weak_ptr t); + + void get_immutable_callback(sha1_hash target + , dht::item const& i); + void get_mutable_callback(dht::item const& i); + + void dht_get_immutable_item(sha1_hash const& target); + + void dht_get_mutable_item(boost::array key + , std::string salt = std::string()); + + void dht_put_item(entry data, sha1_hash target); + + void dht_put_mutable_item(boost::array key + , boost::function& + , boost::uint64_t&, std::string const&)> cb + , std::string salt = std::string()); + +#ifndef TORRENT_NO_DEPRECATE + entry dht_state() const; +#endif + void on_dht_announce(error_code const& e); + void on_dht_router_name_lookup(error_code const& e + , tcp::resolver::iterator host); +#endif + + void maybe_update_udp_mapping(int nat, int local_port, int external_port); + +#ifndef TORRENT_DISABLE_ENCRYPTION + void set_pe_settings(pe_settings const& settings); + pe_settings const& get_pe_settings() const { return m_pe_settings; } +#endif + + void on_port_map_log(char const* msg, int map_transport); + + void on_lsd_announce(error_code const& e); + + // called when a port mapping is successful, or a router returns + // a failure to map a port + void on_port_mapping(int mapping, address const& ip, int port + , error_code const& ec, int nat_transport); + + bool is_aborted() const { return m_abort; } + bool is_paused() const { return m_paused; } + + void pause(); + void resume(); + + void set_ip_filter(ip_filter const& f); + ip_filter const& get_ip_filter() const; + + void set_port_filter(port_filter const& f); + + void listen_on( + std::pair const& port_range + , error_code& ec + , const char* net_interface = 0 + , int flags = 0); + bool is_listening() const; + + torrent_handle add_torrent(add_torrent_params const&, error_code& ec); + torrent_handle add_torrent_impl(add_torrent_params const&, error_code& ec); + void async_add_torrent(add_torrent_params* params); + + void remove_torrent(torrent_handle const& h, int options); + void remove_torrent_impl(boost::shared_ptr tptr, int options); + + void get_torrent_status(std::vector* ret + , boost::function const& pred + , boost::uint32_t flags) const; + void refresh_torrent_status(std::vector* ret + , boost::uint32_t flags) const; + void post_torrent_updates(); + + std::vector get_torrents() const; + + void queue_check_torrent(boost::shared_ptr const& t); + void dequeue_check_torrent(boost::shared_ptr const& t); + + void set_alert_mask(boost::uint32_t m); + size_t set_alert_queue_size_limit(size_t queue_size_limit_); + std::auto_ptr pop_alert(); + void pop_alerts(std::deque* alerts); + void set_alert_dispatch(boost::function)> const&); + void post_alert(const alert& alert_); + + alert const* wait_for_alert(time_duration max_wait); + +#ifndef TORRENT_NO_DEPRECATE + int upload_rate_limit() const; + int download_rate_limit() const; + int local_upload_rate_limit() const; + int local_download_rate_limit() const; + + void set_local_download_rate_limit(int bytes_per_second); + void set_local_upload_rate_limit(int bytes_per_second); + void set_download_rate_limit(int bytes_per_second); + void set_upload_rate_limit(int bytes_per_second); + void set_max_half_open_connections(int limit); + void set_max_connections(int limit); + void set_max_uploads(int limit); + + int max_connections() const; + int max_uploads() const; + int max_half_open_connections() const; + +#endif + + int num_uploads() const { return m_num_unchoked; } + int num_connections() const + { return m_connections.size(); } + + void unchoke_peer(peer_connection& c); + void choke_peer(peer_connection& c); + + session_status status() const; + void set_peer_id(peer_id const& id); + void set_key(int key); + address listen_address() const; + boost::uint16_t listen_port() const; + boost::uint16_t ssl_listen_port() const; + + void abort(); + + torrent_handle find_torrent_handle(sha1_hash const& info_hash); + + void announce_lsd(sha1_hash const& ih, int port, bool broadcast = false); + + void save_state(entry* e, boost::uint32_t flags) const; + void load_state(lazy_entry const* e); + + void set_proxy(proxy_settings const& s); + proxy_settings const& proxy() const { return m_proxy; } + +#ifndef TORRENT_NO_DEPRECATE + void set_peer_proxy(proxy_settings const& s) { set_proxy(s); } + void set_web_seed_proxy(proxy_settings const& s) { set_proxy(s); } + void set_tracker_proxy(proxy_settings const& s) { set_proxy(s); } + proxy_settings const& peer_proxy() const { return proxy(); } + proxy_settings const& web_seed_proxy() const { return proxy(); } + proxy_settings const& tracker_proxy() const { return proxy(); } + +#ifndef TORRENT_DISABLE_DHT + void set_dht_proxy(proxy_settings const& s) { set_proxy(s); } + proxy_settings const& dht_proxy() const { return proxy(); } +#endif +#endif // TORRENT_NO_DEPRECATE + +#ifndef TORRENT_DISABLE_DHT + bool is_dht_running() const { return (m_dht.get() != NULL); } +#endif + +#if TORRENT_USE_I2P + void set_i2p_proxy(proxy_settings const& s); + void on_i2p_open(error_code const& ec); + proxy_settings const& i2p_proxy() const + { return m_i2p_conn.proxy(); } + void open_new_incoming_i2p_connection(); + void on_i2p_accept(boost::shared_ptr const& s + , error_code const& e); +#endif + +#ifndef TORRENT_DISABLE_GEO_IP + std::string as_name_for_ip(address const& a); + int as_for_ip(address const& a); + std::pair* lookup_as(int as); + void load_asnum_db(std::string file); + bool has_asnum_db() const { return m_asnum_db; } + + void load_country_db(std::string file); + bool has_country_db() const { return m_country_db; } + char const* country_for_ip(address const& a); + +#if TORRENT_USE_WSTRING +#ifndef TORRENT_NO_DEPRECATE + void load_asnum_dbw(std::wstring file); + void load_country_dbw(std::wstring file); +#endif // TORRENT_NO_DEPRECATE +#endif // TORRENT_USE_WSTRING +#endif // TORRENT_DISABLE_GEO_IP + + void start_lsd(); + natpmp* start_natpmp(); + upnp* start_upnp(); + + void stop_lsd(); + void stop_natpmp(); + void stop_upnp(); + + int add_port_mapping(int t, int external_port + , int local_port); + void delete_port_mapping(int handle); + + int next_port(); + + void add_redundant_bytes(size_type b, int reason) + { + TORRENT_ASSERT(b > 0); + m_total_redundant_bytes += b; + m_redundant_bytes[reason] += b; + } + + void add_failed_bytes(size_type b) + { + TORRENT_ASSERT(b > 0); + m_total_failed_bytes += b; + } + + char* allocate_buffer(); + void free_buffer(char* buf); + + char* allocate_disk_buffer(char const* category); + void free_disk_buffer(char* buf); + + enum + { + source_dht = 1, + source_peer = 2, + source_tracker = 4, + source_router = 8 + }; + + // implements dht_observer + virtual void set_external_address(address const& ip + , address const& source); + + void set_external_address(address const& ip + , int source_type, address const& source); + + external_ip const& external_address() const; + + bool can_write_to_disk() const + { return m_disk_thread.can_write(); } + + // used when posting synchronous function + // calls to session_impl and torrent objects + mutable libtorrent::mutex mut; + mutable libtorrent::condition_variable cond; + + void inc_disk_queue(int channel) + { + TORRENT_ASSERT(channel >= 0 && channel < 2); + ++m_disk_queues[channel]; + } + + void dec_disk_queue(int channel) + { + TORRENT_ASSERT(channel >= 0 && channel < 2); + TORRENT_ASSERT(m_disk_queues[channel] > 0); + --m_disk_queues[channel]; + } + + void inc_active_downloading() { ++m_num_active_downloading; } + void dec_active_downloading() + { + TORRENT_ASSERT(m_num_active_downloading > 0); + --m_num_active_downloading; + } + void inc_active_finished() { ++m_num_active_finished; } + void dec_active_finished() + { + TORRENT_ASSERT(m_num_active_finished > 0); + --m_num_active_finished; + } + +#if TORRENT_USE_ASSERTS + bool in_state_updates(boost::shared_ptr t) + { + return std::find_if(m_state_updates.begin(), m_state_updates.end() + , boost::bind(&boost::weak_ptr::lock, _1) == t) != m_state_updates.end(); + } +#endif + + void add_to_update_queue(boost::weak_ptr t) + { + TORRENT_ASSERT(std::find_if(m_state_updates.begin(), m_state_updates.end() + , boost::bind(&boost::weak_ptr::lock, _1) == t.lock()) == m_state_updates.end()); + m_state_updates.push_back(t); + } + +// private: + + // implements alert_dispatcher + virtual bool post_alert(alert* a); + + void update_connections_limit(); + void update_unchoke_limit(); + void trigger_auto_manage(); + void on_trigger_auto_manage(); + void update_rate_settings(); + + void update_disk_thread_settings(); + void on_lsd_peer(tcp::endpoint peer, sha1_hash const& ih); + void setup_socket_buffers(socket_type& s); + + // the settings for the client + session_settings m_settings; + + // this is a shared pool where policy_peer objects + // are allocated. It's a pool since we're likely + // to have tens of thousands of peers, and a pool + // saves significant overhead +#ifdef TORRENT_STATS + struct logging_allocator + { + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + static char* malloc(const size_type bytes) + { + allocated_bytes += bytes; + ++allocations; + return (char*)::malloc(bytes); + } + + static void free(char* const block) + { + --allocations; + return ::free(block); + } + + static int allocations; + static int allocated_bytes; + }; + boost::object_pool< + policy::ipv4_peer, logging_allocator> m_ipv4_peer_pool; +#if TORRENT_USE_IPV6 + boost::object_pool< + policy::ipv6_peer, logging_allocator> m_ipv6_peer_pool; +#endif +#if TORRENT_USE_I2P + boost::object_pool< + policy::i2p_peer, logging_allocator> m_i2p_peer_pool; +#endif +#else + boost::object_pool m_ipv4_peer_pool; +#if TORRENT_USE_IPV6 + boost::object_pool m_ipv6_peer_pool; +#endif +#if TORRENT_USE_I2P + boost::object_pool m_i2p_peer_pool; +#endif +#endif + + // this vector is used to store the block_info + // objects pointed to by partial_piece_info returned + // by torrent::get_download_queue. + std::vector m_block_info_storage; + +#ifndef TORRENT_DISABLE_POOL_ALLOCATOR + // this pool is used to allocate and recycle send + // buffers from. + boost::pool<> m_send_buffers; +#endif + + // the file pool that all storages in this session's + // torrents uses. It sets a limit on the number of + // open files by this session. + // file pool must be destructed after the torrents + // since they will still have references to it + // when they are destructed. + file_pool m_files; + + // this is where all active sockets are stored. + // the selector can sleep while there's no activity on + // them + mutable io_service m_io_service; + +#ifdef TORRENT_USE_OPENSSL + // this is a generic SSL context used when talking to + // unauthenticated HTTPS servers + asio::ssl::context m_ssl_ctx; +#endif + + // handles delayed alerts + alert_manager m_alerts; + + // handles disk io requests asynchronously + // peers have pointers into the disk buffer + // pool, and must be destructed before this + // object. The disk thread relies on the file + // pool object, and must be destructed before + // m_files. The disk io thread posts completion + // events to the io service, and needs to be + // constructed after it. + disk_io_thread m_disk_thread; + + // this is a list of half-open tcp connections + // (only outgoing connections) + // this has to be one of the last + // members to be destructed + connection_queue m_half_open; + + // the bandwidth manager is responsible for + // handing out bandwidth to connections that + // asks for it, it can also throttle the + // rate. + bandwidth_manager m_download_rate; + bandwidth_manager m_upload_rate; + + // the global rate limiter bandwidth channels + bandwidth_channel m_download_channel; + bandwidth_channel m_upload_channel; + + // bandwidth channels for local peers when + // rate limits are ignored. They are only + // throttled by these global rate limiters + // and they don't have a rate limit set by + // default + bandwidth_channel m_local_download_channel; + bandwidth_channel m_local_upload_channel; + + // all tcp peer connections are subject to these + // bandwidth limits. Local peers are excempted + // from this limit. The purpose is to be able to + // throttle TCP that passes over the internet + // bottleneck (i.e. modem) to avoid starving out + // uTP connections. + bandwidth_channel m_tcp_download_channel; + bandwidth_channel m_tcp_upload_channel; + + bandwidth_channel* m_bandwidth_channel[2]; + + // the number of peer connections that are waiting + // for the disk. one for each channel. + // upload_channel means waiting to read from disk + // and download_channel is waiting to write to disk + int m_disk_queues[2]; + + tracker_manager m_tracker_manager; + torrent_map m_torrents; + std::map > m_uuids; + + // counters of how many of the active (non-paused) torrents + // are finished and downloading. This is used to weigh the + // priority of downloading and finished torrents when connecting + // more peers. + int m_num_active_downloading; + int m_num_active_finished; + + typedef std::list > check_queue_t; + + // this has all torrents that wants to be checked in it + check_queue_t m_queued_for_checking; + + // this maps sockets to their peer_connection + // object. It is the complete list of all connected + // peers. + connection_map m_connections; + + // this list holds incoming connections while they + // are performing SSL handshake. When we shut down + // the session, all of these are disconnected, otherwise + // they would linger and stall or hang session shutdown + std::set > m_incoming_sockets; + + // peer connections are put here when disconnected to avoid + // race conditions with the disk thread. It's important that + // peer connections are destructed from the network thread, + // once a peer is disconnected, it's put in this list and + // every second their refcount is checked, and if it's 1, + // they are deleted (from the network thread) + std::vector > m_undead_peers; + + // filters incoming connections + ip_filter m_ip_filter; + + // filters outgoing connections + port_filter m_port_filter; + + // the peer id that is generated at the start of the session + peer_id m_peer_id; + + // the key is an id that is used to identify the + // client with the tracker only. It is randomized + // at startup + int m_key; + + // the number of retries we make when binding the + // listen socket. For each retry the port number + // is incremented by one + int m_listen_port_retries; + + // the ip-address of the interface + // we are supposed to listen on. + // if the ip is set to zero, it means + // that we should let the os decide which + // interface to listen on + tcp::endpoint m_listen_interface; + + // if we're listening on an IPv6 interface + // this is one of the non local IPv6 interfaces + // on this machine + tcp::endpoint m_ipv6_interface; + tcp::endpoint m_ipv4_interface; + + // since we might be listening on multiple interfaces + // we might need more than one listen socket + std::list m_listen_sockets; + +#if TORRENT_USE_I2P + i2p_connection m_i2p_conn; + boost::shared_ptr m_i2p_listen_socket; +#endif + +#ifdef TORRENT_USE_OPENSSL + void ssl_handshake(error_code const& ec, boost::shared_ptr s); +#endif + + // when as a socks proxy is used for peers, also + // listen for incoming connections on a socks connection + boost::shared_ptr m_socks_listen_socket; + boost::uint16_t m_socks_listen_port; + + void open_new_incoming_socks_connection(); + + void setup_listener(listen_socket_t* s, tcp::endpoint ep, int& retries + , bool v6_only, int flags, error_code& ec); + + // the proxy used for bittorrent + proxy_settings m_proxy; + +#ifndef TORRENT_DISABLE_DHT + entry m_dht_state; +#endif + + // the number of unchoked peers as set by the auto-unchoker + // this should always be >= m_max_uploads + int m_allowed_upload_slots; + + // the number of unchoked peers + int m_num_unchoked; + + // this is initialized to the unchoke_interval + // session_setting and decreased every second. + // when it reaches zero, it is reset to the + // unchoke_interval and the unchoke set is + // recomputed. + int m_unchoke_time_scaler; + + // this is used to decide when to recalculate which + // torrents to keep queued and which to activate + int m_auto_manage_time_scaler; + + // works like unchoke_time_scaler but it + // is only decresed when the unchoke set + // is recomputed, and when it reaches zero, + // the optimistic unchoke is moved to another peer. + int m_optimistic_unchoke_time_scaler; + + // works like unchoke_time_scaler. Each time + // it reaches 0, and all the connections are + // used, the worst connection will be disconnected + // from the torrent with the most peers + int m_disconnect_time_scaler; + + // when this scaler reaches zero, it will + // scrape one of the auto managed, paused, + // torrents. + int m_auto_scrape_time_scaler; + + // the index of the torrent that we'll + // refresh the next time + int m_next_explicit_cache_torrent; + + // this is a counter of the number of seconds until + // the next time the read cache is rotated, if we're + // using an explicit read read cache. + int m_cache_rotation_timer; + + // statistics gathered from all torrents. + stat m_stat; + + int m_peak_up_rate; + int m_peak_down_rate; + + void on_disk_queue(); + void on_tick(error_code const& e); + + void try_connect_more_peers(int num_downloads, int num_downloads_peers); + void auto_manage_torrents(std::vector& list + , int& dht_limit, int& tracker_limit, int& lsd_limit + , int& hard_limit, int type_limit); + void recalculate_auto_managed_torrents(); + void recalculate_unchoke_slots(int congested_torrents + , int uncongested_torrents); + void recalculate_optimistic_unchoke_slots(); + + ptime m_created; + boost::int64_t session_time() const { return total_seconds(time_now() - m_created); } + + ptime m_last_tick; + ptime m_last_second_tick; + // used to limit how often disk warnings are generated + ptime m_last_disk_performance_warning; + ptime m_last_disk_queue_performance_warning; + + // the last time we went through the peers + // to decide which ones to choke/unchoke + ptime m_last_choke; + + // the time when the next rss feed needs updating + ptime m_next_rss_update; + + // update any rss feeds that need updating and + // recalculate m_next_rss_update + void update_rss_feeds(); + + // when outgoing_ports is configured, this is the + // port we'll bind the next outgoing socket to + int m_next_port; + +#ifndef TORRENT_DISABLE_DHT + boost::intrusive_ptr m_dht; + dht_settings m_dht_settings; + + // these are used when starting the DHT + // (and bootstrapping it), and then erased + std::list m_dht_router_nodes; + + // this announce timer is used + // by the DHT. + deadline_timer m_dht_announce_timer; + + // the number of torrents there were when the + // update_dht_announce_interval() was last called. + // if the number of torrents changes significantly + // compared to this number, the DHT announce interval + // is updated again. This especially matters for + // small numbers. + int m_dht_interval_update_torrents; +#endif + + bool incoming_packet(error_code const& ec + , udp::endpoint const&, char const* buf, int size); + + // see m_external_listen_port. This is the same + // but for the udp port used by the DHT. + int m_external_udp_port; + + rate_limited_udp_socket m_udp_socket; + + utp_socket_manager m_utp_socket_manager; + + // the number of torrent connection boosts + // connections that have been made this second + // this is deducted from the connect speed + int m_boost_connections; + +#ifndef TORRENT_DISABLE_ENCRYPTION + pe_settings m_pe_settings; +#endif + + boost::intrusive_ptr m_natpmp; + boost::intrusive_ptr m_upnp; + boost::intrusive_ptr m_lsd; + + // mask is a bitmask of which protocols to remap on: + // 1: NAT-PMP + // 2: UPnP + void remap_tcp_ports(boost::uint32_t mask, int tcp_port, int ssl_port); + + // 0 is natpmp 1 is upnp + int m_tcp_mapping[2]; + int m_udp_mapping[2]; +#ifdef TORRENT_USE_OPENSSL + int m_ssl_mapping[2]; +#endif + + // the timer used to fire the tick + deadline_timer m_timer; + + // torrents are announced on the local network in a + // round-robin fashion. All torrents are cycled through + // within the LSD announce interval (which defaults to + // 5 minutes) + torrent_map::iterator m_next_lsd_torrent; + +#ifndef TORRENT_DISABLE_DHT + // torrents are announced on the DHT in a + // round-robin fashion. All torrents are cycled through + // within the DHT announce interval (which defaults to + // 15 minutes) + torrent_map::iterator m_next_dht_torrent; + + // torrents that don't have any peers + // when added should be announced to the DHT + // as soon as possible. Such torrents are put + // in this queue and get announced the next time + // the timer fires, instead of the next one in + // the round-robin sequence. + std::deque > m_dht_torrents; +#endif + + // torrents prioritized to get connection attempts + std::deque, int> > m_prio_torrents; + + // this announce timer is used + // by Local service discovery + deadline_timer m_lsd_announce_timer; + + tcp::resolver m_host_resolver; + + // the index of the torrent that will be offered to + // connect to a peer next time on_tick is called. + // This implements a round robin. + torrent_map::iterator m_next_connect_torrent; + + // this is the number of attempts of connecting to + // peers we have given to the torrent pointed to + // by m_next_connect_torrent. Once this reaches + // the number of connection attempts this particular + // torrent should have, the counter is reset and + // m_next_connect_torrent takes a step forward + // to give the next torrent its connection attempts. + int m_current_connect_attempts; + + // this is the round-robin cursor for peers that + // get to download again after the disk has been + // blocked + connection_map::iterator m_next_disk_peer; +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + +#ifdef TORRENT_DISK_STATS + void log_buffer_usage(); + // used to log send buffer usage statistics + std::ofstream m_buffer_usage_logger; + // the number of send buffers that are allocated + int m_buffer_allocations; +#endif + +#ifdef TORRENT_REQUEST_LOGGING + // used to log all requests from peers + FILE* m_request_log; +#endif + +#ifdef TORRENT_STATS + void rotate_stats_log(); + void print_log_line(int tick_interval_ms, ptime now); + void reset_stat_counters(); + void enable_stats_logging(bool s); + + bool m_stats_logging_enabled; + + // the last time we rotated the log file + ptime m_last_log_rotation; + + // logger used to write bandwidth usage statistics + FILE* m_stats_logger; + // sequence number for log file. Log files are + // rotated every hour and the sequence number is + // incremented by one + int m_log_seq; + // the number of peers that were disconnected this + // tick due to protocol error + int m_error_peers; + int m_disconnected_peers; + int m_eof_peers; + int m_connreset_peers; + int m_connrefused_peers; + int m_connaborted_peers; + int m_perm_peers; + int m_buffer_peers; + int m_unreachable_peers; + int m_broken_pipe_peers; + int m_addrinuse_peers; + int m_no_access_peers; + int m_invalid_arg_peers; + int m_aborted_peers; + + int m_piece_requests; + int m_max_piece_requests; + int m_invalid_piece_requests; + int m_choked_piece_requests; + int m_cancelled_piece_requests; + int m_piece_rejects; + + int m_error_incoming_peers; + int m_error_outgoing_peers; + int m_error_rc4_peers; + int m_error_encrypted_peers; + int m_error_tcp_peers; + int m_error_utp_peers; + // the number of times the piece picker fell through + // to the end-game mode + int m_end_game_piece_picker_blocks; + int m_piece_picker_blocks; + int m_piece_picks; + int m_reject_piece_picks; + int m_unchoke_piece_picks; + int m_incoming_redundant_piece_picks; + int m_incoming_piece_picks; + int m_end_game_piece_picks; + int m_snubbed_piece_picks; + int m_connect_timeouts; + int m_uninteresting_peers; + int m_timeout_peers; + int m_no_memory_peers; + int m_too_many_peers; + int m_transport_timeout_peers; + cache_status m_last_cache_status; + size_type m_last_failed; + size_type m_last_redundant; + size_type m_last_uploaded; + size_type m_last_downloaded; + int m_connection_attempts; + int m_num_banned_peers; + int m_banned_for_hash_failure; + vm_statistics_data_t m_last_vm_stat; + thread_cpu_usage m_network_thread_cpu_usage; + sliding_average<20> m_read_ops; + sliding_average<20> m_write_ops;; + enum + { + on_read_counter, + on_write_counter, + on_tick_counter, + on_lsd_counter, + on_lsd_peer_counter, + on_udp_counter, + on_accept_counter, + on_disk_queue_counter, + on_disk_read_counter, + on_disk_write_counter, + max_messages + }; + int m_num_messages[max_messages]; + // 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, + // 16384, 32768, 65536, 131072, 262144, 524288, 1048576 + int m_send_buffer_sizes[18]; + int m_recv_buffer_sizes[18]; +#endif + + // each second tick the timer takes a little + // bit longer than one second to trigger. The + // extra time it took is accumulated into this + // counter. Every time it exceeds 1000, torrents + // will tick their timers 2 seconds instead of one. + // this keeps the timers more accurate over time + // as a kind of "leap second" to adjust for the + // accumulated error + boost::uint16_t m_tick_residual; + + // the number of torrents that have apply_ip_filter + // set to false. This is typically 0 + int m_non_filtered_torrents; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + boost::shared_ptr create_log(std::string const& name + , int instance, bool append = true); + + void session_log(char const* fmt, ...) const; + + // this list of tracker loggers serves as tracker_callbacks when + // shutting down. This list is just here to keep them alive during + // whe shutting down process + std::list > m_tracker_loggers; + + std::string get_log_path() const + { return m_logpath; } + + std::string m_logpath; + + private: + +#endif +#ifdef TORRENT_UPNP_LOGGING + std::ofstream m_upnp_log; +#endif + + // state for keeping track of external IPs + external_ip m_external_ip; + +#ifndef TORRENT_DISABLE_EXTENSIONS + typedef std::list > ses_extension_list_t; + ses_extension_list_t m_ses_extensions; +#endif + +#ifndef TORRENT_DISABLE_GEO_IP + GeoIP* m_asnum_db; + GeoIP* m_country_db; + + // maps AS number to the peak download rate + // we've seen from it. Entries are never removed + // from this map. Pointers to its elements + // are kept in the policy::peer structures. + std::map m_as_peak; +#endif + + // total redundant and failed bytes + size_type m_total_failed_bytes; + size_type m_total_redundant_bytes; + + // this is set to true when a torrent auto-manage + // event is triggered, and reset whenever the message + // is delivered and the auto-manage is executed. + // there should never be more than a single pending auto-manage + // message in-flight at any given time. + bool m_pending_auto_manage; + + // this is also set to true when triggering an auto-manage + // of the torrents. However, if the normal auto-manage + // timer comes along and executes the auto-management, + // this is set to false, which means the triggered event + // no longer needs to execute the auto-management. + bool m_need_auto_manage; + + // set to true when the session object + // is being destructed and the thread + // should exit + bool m_abort; + + // is true if the session is paused + bool m_paused; + // is false by default and set to true when + // the first incoming connection is established + // this is used to know if the client is behind + // NAT or not. + bool m_incoming_connection; + + // redundant bytes per category + size_type m_redundant_bytes[7]; + + std::vector > m_feeds; + + // this is the set of (subscribed) torrents that have changed + // their states since the last time the user requested updates. + std::vector > m_state_updates; + + // the main working thread + boost::scoped_ptr m_thread; + +#if TORRENT_USE_ASSERTS && defined BOOST_HAS_PTHREADS + pthread_t m_network_thread; +#endif + }; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + struct tracker_logger : request_callback + { + tracker_logger(session_impl& ses); + void tracker_warning(tracker_request const& req + , std::string const& str); + void tracker_response(tracker_request const& + , libtorrent::address const& tracker_ip + , std::list
const& ip_list + , std::vector& peers + , int interval + , int min_interval + , int complete + , int incomplete + , int downloaded + , address const& external_ip + , std::string const& tracker_id); + void tracker_request_timed_out( + tracker_request const&); + void tracker_request_error(tracker_request const& r + , int response_code, error_code const& ec, const std::string& str + , int retry_interval); + void debug_log(const char* fmt, ...) const; + session_impl& m_ses; + }; +#endif + + } +} + + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_limit.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_limit.hpp new file mode 100644 index 0000000000..5316a9dea2 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_limit.hpp @@ -0,0 +1,101 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_BANDWIDTH_CHANNEL_HPP_INCLUDED +#define TORRENT_BANDWIDTH_CHANNEL_HPP_INCLUDED + +#include +#include + +#include "libtorrent/assert.hpp" + +namespace libtorrent { + +// member of peer_connection +struct TORRENT_EXTRA_EXPORT bandwidth_channel +{ + static const int inf = boost::integer_traits::const_max; + + bandwidth_channel(); + + // 0 means infinite + void throttle(int limit); + int throttle() const + { + TORRENT_ASSERT_VAL(m_limit < INT_MAX, m_limit); + return int(m_limit); + } + + int quota_left() const; + void update_quota(int dt_milliseconds); + + // this is used when connections disconnect with + // some quota left. It's returned to its bandwidth + // channels. + void return_quota(int amount); + void use_quota(int amount); + + // this is an optimization. If there is more than one second + // of quota built up in this channel, just apply it right away + // instead of introducing a delay to split it up evenly. This + // should especially help in situations where a single peer + // has a capacity under the rate limit, but would otherwise be + // held back by the latency of getting bandwidth from the limiter + bool need_queueing(int amount) + { + if (m_quota_left - amount < m_limit) return true; + m_quota_left -= amount; + return false; + } + + // used as temporary storage while distributing + // bandwidth + int tmp; + + // this is the number of bytes to distribute this round + int distribute_quota; + +private: + + // this is the amount of bandwidth we have + // been assigned without using yet. + boost::int64_t m_quota_left; + + // the limit is the number of bytes + // per second we are allowed to use. + boost::int64_t m_limit; +}; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_manager.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_manager.hpp new file mode 100644 index 0000000000..975ccca8fb --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_manager.hpp @@ -0,0 +1,116 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_BANDWIDTH_MANAGER_HPP_INCLUDED +#define TORRENT_BANDWIDTH_MANAGER_HPP_INCLUDED + +#include + +#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT +#include +#endif + +#include "libtorrent/socket.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/bandwidth_limit.hpp" +#include "libtorrent/bandwidth_queue_entry.hpp" +#include "libtorrent/thread.hpp" +#include "libtorrent/bandwidth_socket.hpp" +#include "libtorrent/ptime.hpp" + +using boost::intrusive_ptr; + + +namespace libtorrent { + +struct TORRENT_EXTRA_EXPORT bandwidth_manager +{ + bandwidth_manager(int channel +#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT + , bool log = false +#endif + ); + + void close(); + +#if TORRENT_USE_ASSERTS + bool is_queued(bandwidth_socket const* peer) const; +#endif + + int queue_size() const; + boost::int64_t queued_bytes() const; + + // non prioritized means that, if there's a line for bandwidth, + // others will cut in front of the non-prioritized peers. + // this is used by web seeds + // returns the number of bytes to assign to the peer, or 0 + // if the peer's 'assign_bandwidth' callback will be called later + int request_bandwidth(intrusive_ptr const& peer + , int blk, int priority + , bandwidth_channel* chan1 = 0 + , bandwidth_channel* chan2 = 0 + , bandwidth_channel* chan3 = 0 + , bandwidth_channel* chan4 = 0 + , bandwidth_channel* chan5 = 0); + +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + + void update_quotas(time_duration const& dt); + +private: + + // these are the consumers that want bandwidth + typedef std::vector queue_t; + queue_t m_queue; + // the number of bytes all the requests in queue are for + boost::int64_t m_queued_bytes; + + // this is the channel within the consumers + // that bandwidth is assigned to (upload or download) + int m_channel; + + bool m_abort; + +#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT + std::ofstream m_log; + ptime m_start; +#endif +}; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_queue_entry.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_queue_entry.hpp new file mode 100644 index 0000000000..641e1ebc41 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_queue_entry.hpp @@ -0,0 +1,73 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_BANDWIDTH_QUEUE_ENTRY_HPP_INCLUDED +#define TORRENT_BANDWIDTH_QUEUE_ENTRY_HPP_INCLUDED + +#include +#include "libtorrent/bandwidth_limit.hpp" +#include "libtorrent/bandwidth_socket.hpp" + +namespace libtorrent { + +struct TORRENT_EXTRA_EXPORT bw_request +{ + bw_request(boost::intrusive_ptr const& pe + , int blk, int prio); + + boost::intrusive_ptr peer; + // 1 is normal prio + int priority; + // the number of bytes assigned to this request so far + int assigned; + // once assigned reaches this, we dispatch the request function + int request_size; + + // the max number of rounds for this request to survive + // this ensures that requests gets responses at very low + // rate limits, when the requested size would take a long + // time to satisfy + int ttl; + + // loops over the bandwidth channels and assigns bandwidth + // from the most limiting one + int assign_bandwidth(); + + enum { max_bandwidth_channels = 5 }; + // we don't actually support more than 5 channels per peer + bandwidth_channel* channel[max_bandwidth_channels]; +}; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_socket.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_socket.hpp new file mode 100644 index 0000000000..486f8fac95 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_socket.hpp @@ -0,0 +1,51 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + + +#ifndef TORRENT_BANDWIDTH_SOCKET_HPP_INCLUDED +#define TORRENT_BANDWIDTH_SOCKET_HPP_INCLUDED + +#include "libtorrent/intrusive_ptr_base.hpp" + +namespace libtorrent +{ + struct bandwidth_socket + : public intrusive_ptr_base + { + virtual void assign_bandwidth(int channel, int amount) = 0; + virtual bool is_disconnecting() const = 0; + virtual ~bandwidth_socket() {} + }; +} + +#endif // TORRENT_BANDWIDTH_SOCKET_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/bencode.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/bencode.hpp new file mode 100644 index 0000000000..af2a3c66ee --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/bencode.hpp @@ -0,0 +1,465 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + + +#ifndef TORRENT_BENCODE_HPP_INCLUDED +#define TORRENT_BENCODE_HPP_INCLUDED + + + +// OVERVIEW +// +// Bencoding is a common representation in bittorrent used for +// for dictionary, list, int and string hierarchies. It's used +// to encode .torrent files and some messages in the network +// protocol. libtorrent also uses it to store settings, resume +// data and other state between sessions. +// +// Strings in bencoded structures are not necessarily representing +// text. Strings are raw byte buffers of a certain length. If a +// string is meant to be interpreted as text, it is required to +// be UTF-8 encoded. See `BEP 3`_. +// +// There are two mechanims to *decode* bencoded buffers in libtorrent. +// +// The most flexible one is bdecode(), which returns a structure +// represented by entry. When a buffer is decoded with this function, +// it can be discarded. The entry does not contain any references back +// to it. This means that bdecode() actually copies all the data out +// of the buffer and into its own hierarchy. This makes this +// function potentially expensive, if you're parsing large amounts +// of data. +// +// Another consideration is that bdecode() is a recursive parser. +// For this reason, in order to avoid DoS attacks by triggering +// a stack overflow, there is a recursion limit. This limit is +// a sanity check to make sure it doesn't run the risk of +// busting the stack. +// +// The second mechanism is lazy_bdecode(), which returns a +// bencoded structure represented by lazy_entry. This function +// builds a tree that points back into the original buffer. +// The returned lazy_entry will not be valid once the buffer +// it was parsed out of is discarded. +// +// Not only is this function more efficient because of less +// memory allocation and data copy, the parser is also not +// recursive, which means it probably performs a little bit +// better and can have a higher recursion limit on the structures +// it's parsing. + +#include +#include +#include +#include // for distance + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/entry.hpp" +#include "libtorrent/config.hpp" + +#include "libtorrent/assert.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/io.hpp" // for write_string + +namespace libtorrent +{ + +#ifndef TORRENT_NO_DEPRECATE + // thrown by bdecode() if the provided bencoded buffer does not contain + // valid encoding. + struct TORRENT_EXPORT invalid_encoding: std::exception + { + // hidden + virtual const char* what() const throw() { return "invalid bencoding"; } + }; +#endif + + namespace detail + { + // this is used in the template, so it must be available to the client + TORRENT_EXPORT char const* integer_to_str(char* buf, int size + , entry::integer_type val); + + template + int write_integer(OutIt& out, entry::integer_type val) + { + // the stack allocated buffer for keeping the + // decimal representation of the number can + // not hold number bigger than this: + BOOST_STATIC_ASSERT(sizeof(entry::integer_type) <= 8); + char buf[21]; + int ret = 0; + for (char const* str = integer_to_str(buf, 21, val); + *str != 0; ++str) + { + *out = *str; + ++out; + ++ret; + } + return ret; + } + + template + void write_char(OutIt& out, char c) + { + *out = c; + ++out; + } + + template + std::string read_until(InIt& in, InIt end, char end_token, bool& err) + { + std::string ret; + if (in == end) + { + err = true; + return ret; + } + while (*in != end_token) + { + ret += *in; + ++in; + if (in == end) + { + err = true; + return ret; + } + } + return ret; + } + + template + void read_string(InIt& in, InIt end, int len, std::string& str, bool& err) + { + TORRENT_ASSERT(len >= 0); + for (int i = 0; i < len; ++i) + { + if (in == end) + { + err = true; + return; + } + str += *in; + ++in; + } + } + + template + int bencode_recursive(OutIt& out, const entry& e) + { + int ret = 0; + switch(e.type()) + { + case entry::int_t: + write_char(out, 'i'); + ret += write_integer(out, e.integer()); + write_char(out, 'e'); + ret += 2; + break; + case entry::string_t: + ret += write_integer(out, e.string().length()); + write_char(out, ':'); + ret += write_string(e.string(), out); + ret += 1; + break; + case entry::list_t: + write_char(out, 'l'); + for (entry::list_type::const_iterator i = e.list().begin(); i != e.list().end(); ++i) + ret += bencode_recursive(out, *i); + write_char(out, 'e'); + ret += 2; + break; + case entry::dictionary_t: + write_char(out, 'd'); + for (entry::dictionary_type::const_iterator i = e.dict().begin(); + i != e.dict().end(); ++i) + { + // write key + ret += write_integer(out, i->first.length()); + write_char(out, ':'); + ret += write_string(i->first, out); + // write value + ret += bencode_recursive(out, i->second); + ret += 1; + } + write_char(out, 'e'); + ret += 2; + break; + default: + // trying to encode a structure with uninitialized values! + TORRENT_ASSERT_VAL(false, e.type()); + // do nothing + break; + } + return ret; + } + + template + void bdecode_recursive(InIt& in, InIt end, entry& ret, bool& err, int depth) + { + if (depth >= 100) + { + err = true; + return; + } + + if (in == end) + { + err = true; +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + return; + } + switch (*in) + { + + // ---------------------------------------------- + // integer + case 'i': + { + ++in; // 'i' + std::string val = read_until(in, end, 'e', err); + if (err) return; + TORRENT_ASSERT(*in == 'e'); + ++in; // 'e' + ret = entry(entry::int_t); + char* end_pointer; + ret.integer() = strtoll(val.c_str(), &end_pointer, 10); +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + if (end_pointer == val.c_str()) + { + err = true; + return; + } + } break; + + // ---------------------------------------------- + // list + case 'l': + { + ret = entry(entry::list_t); + ++in; // 'l' + while (*in != 'e') + { + ret.list().push_back(entry()); + entry& e = ret.list().back(); + bdecode_recursive(in, end, e, err, depth + 1); + if (err) + { +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + return; + } + if (in == end) + { + err = true; +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + return; + } + } +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + TORRENT_ASSERT(*in == 'e'); + ++in; // 'e' + } break; + + // ---------------------------------------------- + // dictionary + case 'd': + { + ret = entry(entry::dictionary_t); + ++in; // 'd' + while (*in != 'e') + { + entry key; + bdecode_recursive(in, end, key, err, depth + 1); + if (err || key.type() != entry::string_t) + { +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + return; + } + entry& e = ret[key.string()]; + bdecode_recursive(in, end, e, err, depth + 1); + if (err) + { +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + return; + } + if (in == end) + { + err = true; +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + return; + } + } +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + TORRENT_ASSERT(*in == 'e'); + ++in; // 'e' + } break; + + // ---------------------------------------------- + // string + default: + if (is_digit((unsigned char)*in)) + { + std::string len_s = read_until(in, end, ':', err); + if (err) + { +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + return; + } + TORRENT_ASSERT(*in == ':'); + ++in; // ':' + int len = atoi(len_s.c_str()); + ret = entry(entry::string_t); + read_string(in, end, len, ret.string(), err); + if (err) + { +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + return; + } + } + else + { + err = true; +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + return; + } +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + } + } + } + + // These functions will encode data to bencoded_ or decode bencoded_ data. + // + // If possible, lazy_bdecode() should be preferred over ``bdecode()``. + // + // The entry_ class is the internal representation of the bencoded data + // and it can be used to retrieve information, an entry_ can also be build by + // the program and given to ``bencode()`` to encode it into the ``OutIt`` + // iterator. + // + // The ``OutIt`` and ``InIt`` are iterators + // (InputIterator_ and OutputIterator_ respectively). They + // are templates and are usually instantiated as ostream_iterator_, + // back_insert_iterator_ or istream_iterator_. These + // functions will assume that the iterator refers to a character + // (``char``). So, if you want to encode entry ``e`` into a buffer + // in memory, you can do it like this:: + // + // std::vector buffer; + // bencode(std::back_inserter(buf), e); + // + // .. _InputIterator: http://www.sgi.com/tech/stl/InputIterator.html + // .. _OutputIterator: http://www.sgi.com/tech/stl/OutputIterator.html + // .. _ostream_iterator: http://www.sgi.com/tech/stl/ostream_iterator.html + // .. _back_insert_iterator: http://www.sgi.com/tech/stl/back_insert_iterator.html + // .. _istream_iterator: http://www.sgi.com/tech/stl/istream_iterator.html + // + // If you want to decode a torrent file from a buffer in memory, you can do it like this:: + // + // std::vector buffer; + // // ... + // entry e = bdecode(buf.begin(), buf.end()); + // + // Or, if you have a raw char buffer:: + // + // const char* buf; + // // ... + // entry e = bdecode(buf, buf + data_size); + // + // Now we just need to know how to retrieve information from the entry. + // + // If ``bdecode()`` encounters invalid encoded data in the range given to it + // it will return a default constructed ``entry`` object. + template int bencode(OutIt out, const entry& e) + { + return detail::bencode_recursive(out, e); + } + template entry bdecode(InIt start, InIt end) + { + entry e; + bool err = false; + detail::bdecode_recursive(start, end, e, err, 0); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(e.m_type_queried == false); +#endif + if (err) return entry(); + return e; + } + template entry bdecode(InIt start, InIt end, int& len) + { + entry e; + bool err = false; + InIt s = start; + detail::bdecode_recursive(start, end, e, err, 0); + len = std::distance(s, start); + TORRENT_ASSERT(len >= 0); + if (err) return entry(); + return e; + } +} + +#endif // TORRENT_BENCODE_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/bitfield.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/bitfield.hpp new file mode 100644 index 0000000000..2fbab64a98 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/bitfield.hpp @@ -0,0 +1,360 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_BITFIELD_HPP_INCLUDED +#define TORRENT_BITFIELD_HPP_INCLUDED + +#include "libtorrent/assert.hpp" +#include "libtorrent/config.hpp" +#include // for memset and memcpy +#include // for malloc, free and realloc +#include // uint32_t +#include // for min() + +namespace libtorrent +{ + // The bitfiled type stores any number of bits as a bitfield + // in a heap allocated or borrowed array. + struct TORRENT_EXPORT bitfield + { + // constructs a new bitfield. The default constructor creates an empty + // bitfield. ``bits`` is the size of the bitfield (specified in bits). + // ``val`` is the value to initialize the bits to. If not specified + // all bits are initialized to 0. + // + // The constructor taking a pointer ``b`` and ``bits`` copies a bitfield + // from the specified buffer, and ``bits`` number of bits (rounded up to + // the nearest byte boundry). + bitfield(): m_bytes(0), m_size(0), m_own(false) {} + bitfield(int bits): m_bytes(0), m_size(0), m_own(false) + { resize(bits); } + bitfield(int bits, bool val): m_bytes(0), m_size(0), m_own(false) + { resize(bits, val); } + bitfield(char const* b, int bits): m_bytes(0), m_size(0), m_own(false) + { assign(b, bits); } + bitfield(bitfield const& rhs): m_bytes(0), m_size(0), m_own(false) + { assign(rhs.bytes(), rhs.size()); } +#if __cplusplus > 199711L + bitfield(bitfield&& rhs): m_bytes(rhs.m_bytes), m_size(rhs.m_size), m_own(rhs.m_own) + { rhs.m_bytes = NULL; } +#endif + + // assigns a bitfield pointed to ``b`` of ``bits`` number of bits, without + // taking ownership of the buffer. This is a way to avoid copying data and + // yet provide a raw buffer to functions that may operate on the bitfield + // type. It is the user's responsibility to make sure the passed-in buffer's + // life time exceeds all uses of the bitfield. + void borrow_bytes(char* b, int bits) + { + dealloc(); + m_bytes = (unsigned char*)b; + m_size = bits; + m_own = false; + } + + // hidden + ~bitfield() { dealloc(); } + + // copy bitfield from buffer ``b`` of ``bits`` number of bits, rounded up to + // the nearest byte boundary. + void assign(char const* b, int bits) + { resize(bits); std::memcpy(m_bytes, b, (bits + 7) / 8); clear_trailing_bits(); } + + // query bit at ``index``. Returns true if bit is 1, otherwise false. + bool operator[](int index) const + { return get_bit(index); } + bool get_bit(int index) const + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_size); + return (m_bytes[index / 8] & (0x80 >> (index & 7))) != 0; + } + + // set bit at ``index`` to 0 (clear_bit) or 1 (set_bit). + void clear_bit(int index) + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_size); + m_bytes[index / 8] &= ~(0x80 >> (index & 7)); + } + void set_bit(int index) + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_size); + m_bytes[index / 8] |= (0x80 >> (index & 7)); + } + + // returns true if all bits in the bitfield are set + bool all_set() const + { + boost::uint8_t* bytes = m_bytes; + int num_bytes = m_size / 8; + int num_words = 0; + + // head + if (num_bytes >= 4) + { + switch (uintptr_t(bytes) & 0x3) + { + case 0: break; + case 1: + if (bytes[0] != 0xff) return false; + if (bytes[1] != 0xff) return false; + if (bytes[2] != 0xff) return false; + bytes += 3; + num_bytes -= 3; + break; + case 2: + if (bytes[0] != 0xff) return false; + if (bytes[1] != 0xff) return false; + bytes += 2; + num_bytes -= 2; + break; + case 3: + if (bytes[0] != 0xff) return false; + ++bytes; + --num_bytes; + break; + } + + num_words = num_bytes / 4; + + TORRENT_ASSERT((uintptr_t(bytes) & 0x3) == 0); + boost::uint32_t* words = (boost::uint32_t*)bytes; + for (int i = 0; i < num_words; ++i) + { + if (words[i] != 0xffffffff) return false; + } + } + + // tail + for (int i = num_words * 4; i < num_bytes; ++i) + { + if (bytes[i] != 0xff) return false; + } + int rest = m_size & 0x7; + if (rest > 0) + { + boost::uint8_t mask = (0xff << (8-rest)) & 0xff; + if ((bytes[num_bytes] & mask) != mask) return false; + } + return true; + } + + // returns the size of the bitfield in bits. + int size() const { return m_size; } + + // returns true if the bitfield has zero size. + bool empty() const { return m_size == 0; } + + // returns a pointer to the internal buffer of the bitfield. + char const* bytes() const { return (char*)m_bytes; } + + // copy operator + bitfield& operator=(bitfield const& rhs) + { + assign(rhs.bytes(), rhs.size()); + return *this; + } + + // count the number of bits in the bitfield that are set to 1. + int count() const + { + // 0000, 0001, 0010, 0011, 0100, 0101, 0110, 0111, + // 1000, 1001, 1010, 1011, 1100, 1101, 1110, 1111 + const static char num_bits[] = + { + 0, 1, 1, 2, 1, 2, 2, 3, + 1, 2, 2, 3, 2, 3, 3, 4 + }; + + int ret = 0; + const int num_bytes = m_size / 8; + for (int i = 0; i < num_bytes; ++i) + { + ret += num_bits[m_bytes[i] & 0xf] + num_bits[m_bytes[i] >> 4]; + } + + int rest = m_size - num_bytes * 8; + for (int i = 0; i < rest; ++i) + { + ret += (m_bytes[num_bytes] >> (7-i)) & 1; + } + TORRENT_ASSERT(ret <= m_size); + TORRENT_ASSERT(ret >= 0); + return ret; + } + + struct const_iterator + { + friend struct bitfield; + + typedef bool value_type; + typedef ptrdiff_t difference_type; + typedef bool const* pointer; + typedef bool& reference; + typedef std::forward_iterator_tag iterator_category; + + bool operator*() { return (*byte & bit) != 0; } + const_iterator& operator++() { inc(); return *this; } + const_iterator operator++(int) + { const_iterator ret(*this); inc(); return ret; } + const_iterator& operator--() { dec(); return *this; } + const_iterator operator--(int) + { const_iterator ret(*this); dec(); return ret; } + + const_iterator(): byte(0), bit(0x80) {} + bool operator==(const_iterator const& rhs) const + { return byte == rhs.byte && bit == rhs.bit; } + + bool operator!=(const_iterator const& rhs) const + { return byte != rhs.byte || bit != rhs.bit; } + + private: + void inc() + { + TORRENT_ASSERT(byte); + if (bit == 0x01) + { + bit = 0x80; + ++byte; + } + else + { + bit >>= 1; + } + } + void dec() + { + TORRENT_ASSERT(byte); + if (bit == 0x80) + { + bit = 0x01; + --byte; + } + else + { + bit <<= 1; + } + } + const_iterator(unsigned char const* ptr, int offset) + : byte(ptr), bit(0x80 >> offset) {} + unsigned char const* byte; + int bit; + }; + + const_iterator begin() const { return const_iterator(m_bytes, 0); } + const_iterator end() const { return const_iterator(m_bytes + m_size / 8, m_size & 7); } + + // set the size of the bitfield to ``bits`` length. If the bitfield is extended, + // the new bits are initialized to ``val``. + void resize(int bits, bool val) + { + int s = m_size; + int b = m_size & 7; + resize(bits); + if (s >= m_size) return; + int old_size_bytes = (s + 7) / 8; + int new_size_bytes = (m_size + 7) / 8; + if (val) + { + if (old_size_bytes && b) m_bytes[old_size_bytes - 1] |= (0xff >> b); + if (old_size_bytes < new_size_bytes) + std::memset(m_bytes + old_size_bytes, 0xff, new_size_bytes - old_size_bytes); + clear_trailing_bits(); + } + else + { + if (old_size_bytes < new_size_bytes) + std::memset(m_bytes + old_size_bytes, 0x00, new_size_bytes - old_size_bytes); + } + } + void resize(int bits) + { + TORRENT_ASSERT(bits >= 0); + const int b = (bits + 7) / 8; + if (m_bytes) + { + if (m_own) + { + m_bytes = (unsigned char*)std::realloc(m_bytes, b); + m_own = true; + } + else if (bits > m_size) + { + unsigned char* tmp = (unsigned char*)std::malloc(b); + std::memcpy(tmp, m_bytes, (std::min)(int(m_size + 7)/ 8, b)); + m_bytes = tmp; + m_own = true; + } + } + else if (bits > 0) + { + m_bytes = (unsigned char*)std::malloc(b); + m_own = true; + } + m_size = bits; + clear_trailing_bits(); + } + + // set all bits in the bitfield to 1 (set_all) or 0 (clear_all). + void set_all() + { + std::memset(m_bytes, 0xff, (m_size + 7) / 8); + clear_trailing_bits(); + } + void clear_all() + { + std::memset(m_bytes, 0x00, (m_size + 7) / 8); + } + + // make the bitfield empty, of zero size. + void clear() { dealloc(); m_size = 0; } + + private: + + void clear_trailing_bits() + { + // clear the tail bits in the last byte + if (m_size & 7) m_bytes[(m_size + 7) / 8 - 1] &= 0xff << (8 - (m_size & 7)); + } + + void dealloc() { if (m_own) std::free(m_bytes); m_bytes = 0; } + unsigned char* m_bytes; + int m_size:31; // in bits + bool m_own:1; + }; + +} + +#endif // TORRENT_BITFIELD_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/bloom_filter.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/bloom_filter.hpp new file mode 100644 index 0000000000..b93741ec15 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/bloom_filter.hpp @@ -0,0 +1,81 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_BLOOM_FILTER_HPP_INCLUDED +#define TORRENT_BLOOM_FILTER_HPP_INCLUDED + +#include +#include "libtorrent/peer_id.hpp" // for sha1_hash +#include "libtorrent/config.hpp" // for sha1_hash + +#include // for log() + +namespace libtorrent +{ + TORRENT_EXTRA_EXPORT void set_bits(boost::uint8_t const* b, boost::uint8_t* bits, int len); + TORRENT_EXTRA_EXPORT bool has_bits(boost::uint8_t const* b, boost::uint8_t const* bits, int len); + TORRENT_EXTRA_EXPORT int count_zero_bits(boost::uint8_t const* bits, int len); + + template + struct bloom_filter + { + bool find(sha1_hash const& k) const + { return has_bits(&k[0], bits, N); } + + void set(sha1_hash const& k) + { set_bits(&k[0], bits, N); } + + std::string to_string() const + { return std::string((char const*)&bits[0], N); } + + void from_string(char const* str) + { memcpy(bits, str, N); } + + void clear() { memset(bits, 0, N); } + + float size() const + { + const int c = (std::min)(count_zero_bits(bits, N), (N * 8) - 1); + const int m = N * 8; + return ::log(c / float(m)) / (2.f * ::log(1.f - 1.f/m)); + } + + bloom_filter() { clear(); } + + private: + boost::uint8_t bits[N]; + }; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/broadcast_socket.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/broadcast_socket.hpp new file mode 100644 index 0000000000..a2d4a609a0 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/broadcast_socket.hpp @@ -0,0 +1,156 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_BROADCAST_SOCKET_HPP_INCLUDED +#define TORRENT_BROADCAST_SOCKET_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include "libtorrent/io_service_fwd.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/error_code.hpp" +#include +#include +#include + +namespace libtorrent +{ + + TORRENT_EXTRA_EXPORT bool is_local(address const& a); + TORRENT_EXTRA_EXPORT bool is_loopback(address const& addr); + TORRENT_EXTRA_EXPORT bool is_multicast(address const& addr); + TORRENT_EXTRA_EXPORT bool is_any(address const& addr); + TORRENT_EXTRA_EXPORT bool is_teredo(address const& addr); + TORRENT_EXTRA_EXPORT int cidr_distance(address const& a1, address const& a2); + + // determines if the operating system supports IPv6 + TORRENT_EXTRA_EXPORT bool supports_ipv6(); + + TORRENT_EXTRA_EXPORT int common_bits(unsigned char const* b1 + , unsigned char const* b2, int n); + + TORRENT_EXTRA_EXPORT address guess_local_address(io_service&); + + typedef boost::function receive_handler_t; + + class TORRENT_EXTRA_EXPORT broadcast_socket + { + public: + broadcast_socket(udp::endpoint const& multicast_endpoint + , receive_handler_t const& handler); + ~broadcast_socket() { close(); } + + void open(io_service& ios, error_code& ec, bool loopback = true); + + enum flags_t { broadcast = 1 }; + void send(char const* buffer, int size, error_code& ec, int flags = 0); + + void close(); + int num_send_sockets() const { return m_unicast_sockets.size(); } + void enable_ip_broadcast(bool e); + + private: + + struct socket_entry + { + socket_entry(boost::shared_ptr const& s) + : socket(s), broadcast(false) {} + socket_entry(boost::shared_ptr const& s + , address_v4 const& mask): socket(s), netmask(mask), broadcast(false) {} + boost::shared_ptr socket; + char buffer[1500]; + udp::endpoint remote; + address_v4 netmask; + bool broadcast; + void close() + { + if (!socket) return; + error_code ec; + socket->close(ec); + } + bool can_broadcast() const + { + error_code ec; + return broadcast + && netmask != address_v4() + && socket->local_endpoint(ec).address().is_v4(); + } + address_v4 broadcast_address() const + { + error_code ec; +#if BOOST_VERSION < 104700 + return address_v4(socket->local_endpoint(ec).address().to_v4().to_ulong() | ((~netmask.to_ulong()) & 0xffffffff)); +#else + return address_v4::broadcast(socket->local_endpoint(ec).address().to_v4(), netmask); +#endif + } + }; + + void on_receive(socket_entry* s, error_code const& ec + , std::size_t bytes_transferred); + void open_unicast_socket(io_service& ios, address const& addr + , address_v4 const& mask); + void open_multicast_socket(io_service& ios, address const& addr + , bool loopback, error_code& ec); + + // if we're aborting, destruct the handler and return true + bool maybe_abort(); + + // these sockets are used to + // join the multicast group (on each interface) + // and receive multicast messages + std::list m_sockets; + // these sockets are not bound to any + // specific port and are used to + // send messages to the multicast group + // and receive unicast responses + std::list m_unicast_sockets; + udp::endpoint m_multicast_endpoint; + receive_handler_t m_on_receive; + + // the number of outstanding async operations + // we have on these sockets. The m_on_receive + // handler may not be destructed until this reaches + // 0, since it may be holding references to + // the broadcast_socket itself. + int m_outstanding_operations; + // when set to true, we're trying to shut down + // don't initiate new operations and once the + // outstanding counter reaches 0, destruct + // the handler object + bool m_abort; + }; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/bt_peer_connection.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/bt_peer_connection.hpp new file mode 100644 index 0000000000..e8b7b618ef --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/bt_peer_connection.hpp @@ -0,0 +1,461 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +Copyright (c) 2007-2014, Arvid Norberg, Un Shyam +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_BT_PEER_CONNECTION_HPP_INCLUDED +#define TORRENT_BT_PEER_CONNECTION_HPP_INCLUDED + +#include +#include +#include +#include + +#include "libtorrent/debug.hpp" + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/buffer.hpp" +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/stat.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/pe_crypto.hpp" + +namespace libtorrent +{ + class torrent; + + namespace detail + { + struct session_impl; + } + + class TORRENT_EXTRA_EXPORT bt_peer_connection + : public peer_connection + { + friend class invariant_access; + public: + + // this is the constructor where the we are the active part. + // The peer_conenction should handshake and verify that the + // other end has the correct id + bt_peer_connection( + aux::session_impl& ses + , boost::shared_ptr s + , tcp::endpoint const& remote + , policy::peer* peerinfo + , peer_id const& pid + , boost::weak_ptr t = boost::weak_ptr() + , bool outgoing = false); + + void start(); + + enum + { + // pex_msg = 1, + // metadata_msg = 2, + upload_only_msg = 3, + holepunch_msg = 4, + // recommend_msg = 5, + // comment_msg = 6, + dont_have_msg = 7, + share_mode_msg = 8 + }; + + ~bt_peer_connection(); + +#ifndef TORRENT_DISABLE_ENCRYPTION + bool supports_encryption() const + { return m_encrypted; } + bool rc4_encrypted() const + { return m_rc4_encrypted; } +#endif + + virtual int type() const { return peer_connection::bittorrent_connection; } + + enum message_type + { + // standard messages + msg_choke = 0, + msg_unchoke, + msg_interested, + msg_not_interested, + msg_have, + msg_bitfield, + msg_request, + msg_piece, + msg_cancel, + // DHT extension + msg_dht_port, + // FAST extension + msg_suggest_piece = 0xd, + msg_have_all, + msg_have_none, + msg_reject_request, + msg_allowed_fast, + + // extension protocol message + msg_extended = 20, + + num_supported_messages + }; + + enum hp_message_t + { + // msg_types + hp_rendezvous = 0, + hp_connect = 1, + hp_failed = 2, + + // error codes + hp_no_such_peer = 1, + hp_not_connected = 2, + hp_no_support = 3, + hp_no_self = 4 + }; + + // called from the main loop when this connection has any + // work to do. + + void on_sent(error_code const& error + , std::size_t bytes_transferred); + void on_receive(error_code const& error + , std::size_t bytes_transferred); + + virtual void get_specific_peer_info(peer_info& p) const; + virtual bool in_handshake() const; + +#ifndef TORRENT_DISABLE_EXTENSIONS + bool supports_holepunch() const { return m_holepunch_id != 0; } +#endif + + bool support_extensions() const { return m_supports_extensions; } + + // the message handlers are called + // each time a recv() returns some new + // data, the last time it will be called + // is when the entire packet has been + // received, then it will no longer + // be called. i.e. most handlers need + // to check how much of the packet they + // have received before any processing + void on_keepalive(); + void on_choke(int received); + void on_unchoke(int received); + void on_interested(int received); + void on_not_interested(int received); + void on_have(int received); + void on_bitfield(int received); + void on_request(int received); + void on_piece(int received); + void on_cancel(int received); + + // DHT extension + void on_dht_port(int received); + + // FAST extension + void on_suggest_piece(int received); + void on_have_all(int received); + void on_have_none(int received); + void on_reject_request(int received); + void on_allowed_fast(int received); +#ifndef TORRENT_DISABLE_EXTENSIONS + void on_holepunch(); + + void on_extended(int received); + + void on_extended_handshake(); +#endif + + typedef void (bt_peer_connection::*message_handler)(int received); + + // the following functions appends messages + // to the send buffer + void write_choke(); + void write_unchoke(); + void write_interested(); + void write_not_interested(); + void write_request(peer_request const& r); + void write_cancel(peer_request const& r); + void write_bitfield(); + void write_have(int index); + void write_piece(peer_request const& r, disk_buffer_holder& buffer); + void write_handshake(); +#ifndef TORRENT_DISABLE_EXTENSIONS + void write_extensions(); + void write_upload_only(); + void write_share_mode(); + void write_holepunch_msg(int type, tcp::endpoint const& ep, int error); +#endif + void write_metadata(std::pair req); + void write_metadata_request(std::pair req); + void write_keepalive(); + + // DHT extension + void write_dht_port(int listen_port); + + // FAST extension + void write_have_all(); + void write_have_none(); + void write_reject_request(peer_request const&); + void write_allow_fast(int piece); + void write_suggest(int piece); + + void on_connected(); + void on_metadata(); + +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; + ptime m_last_choke; +#endif + + private: + + bool dispatch_message(int received); + // returns the block currently being + // downloaded. And the progress of that + // block. If the peer isn't downloading + // a piece for the moment, the boost::optional + // will be invalid. + boost::optional downloading_piece_progress() const; + +#ifndef TORRENT_DISABLE_ENCRYPTION + + // if (is_local()), we are 'a' otherwise 'b' + // + // 1. a -> b dhkey, pad + // 2. b -> a dhkey, pad + // 3. a -> b sync, payload + // 4. b -> a sync, payload + // 5. a -> b payload + + void write_pe1_2_dhkey(); + void write_pe3_sync(); + void write_pe4_sync(int crypto_select); + + void write_pe_vc_cryptofield(char* write_buf, int len + , int crypto_field, int pad_size); + + // stream key (info hash of attached torrent) + // secret is the DH shared secret + // initializes m_enc_handler + void init_pe_rc4_handler(char const* secret, sha1_hash const& stream_key); + + // Returns offset at which bytestream (src, src + src_size) + // matches bytestream(target, target + target_size). + // If no sync found, return -1 + int get_syncoffset(char const* src, int src_size + , char const* target, int target_size) const; +#endif + +public: + + // these functions encrypt the send buffer if m_rc4_encrypted + // is true, otherwise it passes the call to the + // peer_connection functions of the same names + virtual void append_const_send_buffer(char const* buffer, int size); + virtual void send_buffer(char const* begin, int size, int flags = 0 + , void (*fun)(char*, int, void*) = 0, void* userdata = 0); + template + void bt_append_send_buffer(char* buffer, int size, Destructor const& destructor) + { +#ifndef TORRENT_DISABLE_ENCRYPTION + if (m_rc4_encrypted) + m_enc_handler->encrypt(buffer, size); +#endif + peer_connection::append_send_buffer(buffer, size, destructor, true); + } + +private: + + enum state + { +#ifndef TORRENT_DISABLE_ENCRYPTION + read_pe_dhkey = 0, + read_pe_syncvc, + read_pe_synchash, + read_pe_skey_vc, + read_pe_cryptofield, + read_pe_pad, + read_pe_ia, + init_bt_handshake, + read_protocol_identifier, +#else + read_protocol_identifier = 0, +#endif + read_info_hash, + read_peer_id, + + // handshake complete + read_packet_size, + read_packet + }; + +#ifndef TORRENT_DISABLE_ENCRYPTION + enum + { + handshake_len = 68, + dh_key_len = 96 + }; +#endif + + // state of on_receive + boost::uint8_t m_state; + + // this is set to true if the handshake from + // the peer indicated that it supports the + // extension protocol + bool m_supports_extensions:1; + bool m_supports_dht_port:1; + bool m_supports_fast:1; + +#if TORRENT_USE_ASSERTS + // this is set to true when the client's + // bitfield is sent to this peer + bool m_sent_bitfield:1; + + bool m_in_constructor:1; + + bool m_sent_handshake:1; +#endif + +#ifndef TORRENT_DISABLE_ENCRYPTION + // this is set to true after the encryption method has been + // succesfully negotiated (either plaintext or rc4), to signal + // automatic encryption/decryption. + bool m_encrypted:1; + + // true if rc4, false if plaintext + bool m_rc4_encrypted:1; +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + // the message ID for upload only message + // 0 if not supported + boost::uint8_t m_upload_only_id; + + // the message ID for holepunch messages + boost::uint8_t m_holepunch_id; +#endif + + std::string m_client_version; + + static const message_handler m_message_handler[num_supported_messages]; + + // the peer ID we advertise for ourself + peer_id m_our_peer_id; + + // this is a queue of ranges that describes + // where in the send buffer actual payload + // data is located. This is currently + // only used to be able to gather statistics + // seperately on payload and protocol data. + struct range + { + range(int s, int l) + : start(s) + , length(l) + { + TORRENT_ASSERT(s >= 0); + TORRENT_ASSERT(l > 0); + } + int start; + int length; + }; + static bool range_below_zero(const range& r) + { return r.start < 0; } + std::vector m_payloads; + + // we have suggested these pieces to the peer + // don't suggest it again + bitfield m_sent_suggested_pieces; + +#ifndef TORRENT_DISABLE_ENCRYPTION + // initialized during write_pe1_2_dhkey, and destroyed on + // creation of m_enc_handler. Cannot reinitialize once + // initialized. + boost::scoped_ptr m_dh_key_exchange; + + // if encryption is negotiated, this is used for + // encryption/decryption during the entire session. Destroyed + // if plaintext is selected + boost::scoped_ptr m_enc_handler; + + // (outgoing only) synchronize verification constant with + // remote peer, this will hold rc4_decrypt(vc). Destroyed + // after the sync step. + boost::scoped_array m_sync_vc; + + // (incoming only) synchronize hash with remote peer, holds + // the sync hash (hash("req1",secret)). Destroyed after the + // sync step. + boost::scoped_ptr m_sync_hash; + + // used to disconnect peer if sync points are not found within + // the maximum number of bytes + int m_sync_bytes_read; +#endif // #ifndef TORRENT_DISABLE_ENCRYPTION + +#ifndef TORRENT_DISABLE_EXTENSIONS + // the message ID for don't-have message + boost::uint8_t m_dont_have_id; + + // the message ID for share mode message + // 0 if not supported + boost::uint8_t m_share_mode_id; + + // the reserved bits received from the other peer + // in the bittorrent handshake + char m_reserved_bits[8]; +#endif + }; +} + +#endif // TORRENT_BT_PEER_CONNECTION_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/buffer.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/buffer.hpp new file mode 100644 index 0000000000..657ea355fd --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/buffer.hpp @@ -0,0 +1,224 @@ +/* +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of Rasterbar Software nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef LIBTORRENT_BUFFER_HPP +#define LIBTORRENT_BUFFER_HPP + +#include +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/assert.hpp" +#include // malloc/free/realloc + +namespace libtorrent { + +class buffer +{ +public: + struct interval + { + interval() + : begin(0) + , end(0) + {} + + interval(char* b, char* e) + : begin(b) + , end(e) + {} + + char operator[](int index) const + { + TORRENT_ASSERT(begin + index < end); + return begin[index]; + } + + int left() const + { + TORRENT_ASSERT(end >= begin); + TORRENT_ASSERT(end - begin < INT_MAX); + return int(end - begin); + } + + char* begin; + char* end; + }; + + struct const_interval + { + const_interval(interval const& i) + : begin(i.begin) + , end(i.end) + {} + + const_interval(char const* b, char const* e) + : begin(b) + , end(e) + {} + + char operator[](int index) const + { + TORRENT_ASSERT(begin + index < end); + return begin[index]; + } + + bool operator==(const const_interval& p_interval) + { + return begin == p_interval.begin + && end == p_interval.end; + } + + int left() const + { + TORRENT_ASSERT(end >= begin); + TORRENT_ASSERT(end - begin < INT_MAX); + return int(end - begin); + } + + char const* begin; + char const* end; + }; + + buffer(std::size_t n = 0) + : m_begin(0) + , m_end(0) + , m_last(0) + { + if (n) resize(n); + } + + buffer(buffer const& b) + : m_begin(0) + , m_end(0) + , m_last(0) + { + if (b.size() == 0) return; + resize(b.size()); + std::memcpy(m_begin, b.begin(), b.size()); + } + +#if __cplusplus > 199711L + buffer(buffer&& b): m_begin(b.m_begin), m_end(b.m_end), m_last(b.m_last) + { b.m_begin = b.m_end = b.m_last = NULL; } +#endif + + buffer& operator=(buffer const& b) + { + if (&b == this) return *this; + resize(b.size()); + if (b.size() == 0) return *this; + std::memcpy(m_begin, b.begin(), b.size()); + return *this; + } + + ~buffer() + { + std::free(m_begin); + } + + buffer::interval data() { return interval(m_begin, m_end); } + buffer::const_interval data() const { return const_interval(m_begin, m_end); } + + void resize(std::size_t n) + { + reserve(n); + m_end = m_begin + n; + } + + void insert(char* point, char const* first, char const* last) + { + std::size_t p = point - m_begin; + if (point == m_end) + { + resize(size() + last - first); + std::memcpy(m_begin + p, first, last - first); + return; + } + + resize(size() + last - first); + std::memmove(m_begin + p + (last - first), m_begin + p, last - first); + std::memcpy(m_begin + p, first, last - first); + } + + void erase(char* b, char* e) + { + TORRENT_ASSERT(e <= m_end); + TORRENT_ASSERT(b >= m_begin); + TORRENT_ASSERT(b <= e); + if (e == m_end) + { + resize(b - m_begin); + return; + } + std::memmove(b, e, m_end - e); + m_end = b + (m_end - e); + } + + void clear() { m_end = m_begin; } + std::size_t size() const { return m_end - m_begin; } + std::size_t capacity() const { return m_last - m_begin; } + void reserve(std::size_t n) + { + if (n <= capacity()) return; + TORRENT_ASSERT(n > 0); + + std::size_t s = size(); + m_begin = (char*)std::realloc(m_begin, n); + m_end = m_begin + s; + m_last = m_begin + n; + } + + bool empty() const { return m_begin == m_end; } + char& operator[](std::size_t i) { TORRENT_ASSERT(i < size()); return m_begin[i]; } + char const& operator[](std::size_t i) const { TORRENT_ASSERT(i < size()); return m_begin[i]; } + + char* begin() { return m_begin; } + char const* begin() const { return m_begin; } + char* end() { return m_end; } + char const* end() const { return m_end; } + + void swap(buffer& b) + { + using std::swap; + swap(m_begin, b.m_begin); + swap(m_end, b.m_end); + swap(m_last, b.m_last); + } +private: + char* m_begin; // first + char* m_end; // one passed end of size + char* m_last; // one passed end of allocation +}; + + +} + +#endif // LIBTORRENT_BUFFER_HPP + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/build_config.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/build_config.hpp new file mode 100644 index 0000000000..510a01a333 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/build_config.hpp @@ -0,0 +1,73 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_BUILD_CONFIG_HPP_INCLUDED +#define TORRENT_BUILD_CONFIG_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include +#include + +#if TORRENT_USE_BOOST_DATE_TIME +#define TORRENT_CFG_TIME boosttime_ +#elif TORRENT_USE_ABSOLUTE_TIME +#define TORRENT_CFG_TIME absolutetime_ +#elif TORRENT_USE_QUERY_PERFORMANCE_TIMER +#define TORRENT_CFG_TIME performancetimer_ +#elif TORRENT_USE_CLOCK_GETTIME +#define TORRENT_CFG_TIME clocktime_ +#elif TORRENT_USE_SYSTEM_TIME +#define TORRENT_CFG_TIME systime_ +#else +#error what timer is used? +#endif + +#if TORRENT_USE_IPV6 +#define TORRENT_CFG_IPV6 ipv6_ +#else +#define TORRENT_CFG_IPV6 noipv_- +#endif + +#ifdef TORRENT_NO_DEPRECATE +#define TORRENT_CFG_DEPR nodeprecate_ +#else +#define TORRENT_CFG_DEPR deprecated_ +#endif + +#define TORRENT_CFG \ + BOOST_PP_CAT(TORRENT_CFG_TIME, \ + TORRENT_CFG_DEPR) + +#define TORRENT_CFG_STRING BOOST_PP_STRINGIZE(TORRENT_CFG) + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/chained_buffer.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/chained_buffer.hpp new file mode 100644 index 0000000000..7fd5bf437b --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/chained_buffer.hpp @@ -0,0 +1,124 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_CHAINED_BUFFER_HPP_INCLUDED +#define TORRENT_CHAINED_BUFFER_HPP_INCLUDED + +#include "libtorrent/config.hpp" + +#include +#include +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif +#include +#include // for memcpy + +namespace libtorrent +{ +#if BOOST_VERSION >= 103500 + namespace asio = boost::asio; +#endif + struct TORRENT_EXTRA_EXPORT chained_buffer + { + chained_buffer(): m_bytes(0), m_capacity(0) + { +#if TORRENT_USE_ASSERTS + m_destructed = false; +#endif + } + + struct buffer_t + { + boost::function free; // destructs the buffer + char* buf; // the first byte of the buffer + char* start; // the first byte to send/receive in the buffer + int size; // the total size of the buffer + int used_size; // this is the number of bytes to send/receive + }; + + bool empty() const { return m_bytes == 0; } + int size() const { return m_bytes; } + int capacity() const { return m_capacity; } + + void pop_front(int bytes_to_pop); + + void append_buffer(char* buffer, int s, int used_size + , boost::function const& destructor); + + // returns the number of bytes available at the + // end of the last chained buffer. + int space_in_last_buffer(); + + // tries to copy the given buffer to the end of the + // last chained buffer. If there's not enough room + // it returns false + char* append(char const* buf, int s); + + // tries to allocate memory from the end + // of the last buffer. If there isn't + // enough room, returns 0 + char* allocate_appendix(int s); + + std::list const& build_iovec(int to_send); + + ~chained_buffer(); + + private: + + // this is the list of all the buffers we want to + // send + std::list m_vec; + + // this is the vector of buffers used when + // invoking the async write call + std::list m_tmp_vec; + + // this is the number of bytes in the send buf. + // this will always be equal to the sum of the + // size of all buffers in vec + int m_bytes; + + // the total size of all buffers in the chain + // including unused space + int m_capacity; + +#if TORRENT_USE_ASSERTS + bool m_destructed; +#endif + }; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/config.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/config.hpp new file mode 100644 index 0000000000..51a8a7b934 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/config.hpp @@ -0,0 +1,572 @@ +/* + +Copyright (c) 2005-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_CONFIG_HPP_INCLUDED +#define TORRENT_CONFIG_HPP_INCLUDED + +#if !defined _MSC_VER || _MSC_VER >= 1600 +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS 1 +#endif +#endif + +#include +#include +#include +#include // for snprintf +#include // for IOV_MAX + +#include "libtorrent/export.hpp" + +#if defined TORRENT_DEBUG_BUFFERS && !defined TORRENT_DISABLE_POOL_ALLOCATOR +#error TORRENT_DEBUG_BUFFERS only works if you also disable pool allocators with TORRENT_DISABLE_POOL_ALLOCATOR +#endif + +#if !defined BOOST_ASIO_SEPARATE_COMPILATION && !defined BOOST_ASIO_DYN_LINK +#define BOOST_ASIO_SEPARATE_COMPILATION +#endif + +#ifndef _MSC_VER +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS 1 +#endif +#include // for PRId64 et.al. +#endif + +#ifndef PRId64 +// MinGW uses microsofts runtime +#if defined _MSC_VER || defined __MINGW32__ +#define PRId64 "I64d" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIu32 "u" +#else +#define PRId64 "lld" +#define PRIu64 "llu" +#define PRIx64 "llx" +#define PRIu32 "u" +#endif +#endif + +// ======= GCC ========= + +#if defined __GNUC__ + +# if __GNUC__ >= 3 +# define TORRENT_DEPRECATED __attribute__ ((deprecated)) +# endif + +// ======= SUNPRO ========= + +#elif defined __SUNPRO_CC + +// SunPRO seems to have an overly-strict +// definition of POD types and doesn't +// seem to allow boost::array in unions +#define TORRENT_BROKEN_UNIONS 1 + +// ======= MSVC ========= + +#elif defined BOOST_MSVC + +#pragma warning(disable: 4258) +#pragma warning(disable: 4251) + +// class X needs to have dll-interface to be used by clients of class Y +#pragma warning(disable:4251) +// '_vsnprintf': This function or variable may be unsafe +#pragma warning(disable:4996) + +#define TORRENT_DEPRECATED_PREFIX __declspec(deprecated) + +#endif + + +// ======= PLATFORMS ========= + + +// set up defines for target environments +// ==== AMIGA === +#if defined __AMIGA__ || defined __amigaos__ || defined __AROS__ +#define TORRENT_AMIGA +#define TORRENT_USE_MLOCK 0 +#define TORRENT_USE_WRITEV 0 +#define TORRENT_USE_READV 0 +#define TORRENT_USE_IPV6 0 +#define TORRENT_USE_BOOST_THREAD 0 +#define TORRENT_USE_IOSTREAM 0 +// set this to 1 to disable all floating point operations +// (disables some float-dependent APIs) +#define TORRENT_NO_FPU 1 +#define TORRENT_USE_I2P 0 +#ifndef TORRENT_USE_ICONV +#define TORRENT_USE_ICONV 0 +#endif + +// ==== Darwin/BSD === +#elif (defined __APPLE__ && defined __MACH__) || defined __FreeBSD__ || defined __NetBSD__ \ + || defined __OpenBSD__ || defined __bsdi__ || defined __DragonFly__ \ + || defined __FreeBSD_kernel__ +#define TORRENT_BSD +// we don't need iconv on mac, because +// the locale is always utf-8 +#if defined __APPLE__ +#ifndef TORRENT_USE_ICONV +#define TORRENT_USE_ICONV 0 +#define TORRENT_USE_LOCALE 0 +#define TORRENT_CLOSE_MAY_BLOCK 1 + +#include + +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 +#ifdef TORRENT_USE_OPENSSL +#define TORRENT_USE_COMMONCRYPTO 1 +#endif // TORRENT_USE_OPENSSL +#endif // MAC_OS_X_VERSION_MIN_REQUIRED + +// execinfo.h is available in the MacOS X 10.5 SDK. +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 +#define TORRENT_USE_EXECINFO 1 +#endif + +#endif // __APPLE__ + +#else +// FreeBSD has a reasonable iconv signature +// unless we're on glibc +#ifndef __GLIBC__ +# define TORRENT_ICONV_ARG (const char**) +#endif +#endif +#define TORRENT_HAS_FALLOCATE 0 +#define TORRENT_USE_IFADDRS 1 +#define TORRENT_USE_SYSCTL 1 +#define TORRENT_USE_IFCONF 1 + + +// ==== LINUX === +#elif defined __linux__ +#define TORRENT_LINUX +#define TORRENT_USE_NETLINK 1 +#define TORRENT_USE_IFCONF 1 +#define TORRENT_HAS_SALEN 0 + +// ===== ANDROID ===== (almost linux, sort of) +#if defined __ANDROID__ +#define TORRENT_ANDROID +#define TORRENT_HAS_FALLOCATE 0 +#define TORRENT_USE_ICONV 0 +#define TORRENT_USE_IFADDRS 0 +#define TORRENT_USE_MEMALIGN 1 +#else +#define TORRENT_USE_IFADDRS 1 +#define TORRENT_USE_POSIX_MEMALIGN 1 +#endif + +#if __amd64__ || __i386__ +#define TORRENT_USE_EXECINFO 1 +#endif + +// ==== MINGW === +#elif defined __MINGW32__ +#define TORRENT_MINGW +#define TORRENT_WINDOWS +#ifndef TORRENT_USE_ICONV +# define TORRENT_USE_ICONV 0 +# define TORRENT_USE_LOCALE 1 +#endif +#define TORRENT_USE_RLIMIT 0 +#define TORRENT_USE_NETLINK 0 +#define TORRENT_USE_GETADAPTERSADDRESSES 1 +#define TORRENT_HAS_SALEN 0 +#define TORRENT_USE_GETIPFORWARDTABLE 1 +#ifndef TORRENT_USE_UNC_PATHS +# define TORRENT_USE_UNC_PATHS 1 +#endif + +// ==== WINDOWS === +#elif defined WIN32 +#define TORRENT_WINDOWS +#ifndef TORRENT_USE_GETIPFORWARDTABLE +#define TORRENT_USE_GETIPFORWARDTABLE 1 +#endif +#define TORRENT_USE_GETADAPTERSADDRESSES 1 +#define TORRENT_HAS_SALEN 0 +// windows has its own functions to convert +#ifndef TORRENT_USE_ICONV +#define TORRENT_USE_ICONV 0 +#define TORRENT_USE_LOCALE 1 +#endif +#define TORRENT_USE_RLIMIT 0 +#define TORRENT_HAS_FALLOCATE 0 +#ifndef TORRENT_USE_UNC_PATHS +#define TORRENT_USE_UNC_PATHS 1 +#endif + +// ==== SOLARIS === +#elif defined sun || defined __sun +#define TORRENT_SOLARIS +#define TORRENT_COMPLETE_TYPES_REQUIRED 1 +#define TORRENT_USE_IFCONF 1 +#define TORRENT_HAS_SALEN 0 + +// ==== BEOS === +#elif defined __BEOS__ || defined __HAIKU__ +#define TORRENT_BEOS +#include // B_PATH_NAME_LENGTH +#define TORRENT_HAS_FALLOCATE 0 +#define TORRENT_USE_MLOCK 0 +#ifndef TORRENT_USE_ICONV +#define TORRENT_USE_ICONV 0 +#endif + +// ==== GNU/Hurd === +#elif defined __GNU__ +#define TORRENT_HURD +#define TORRENT_USE_IFADDRS 1 +#define TORRENT_USE_IFCONF 1 + +// ==== eCS(OS/2) === +#elif defined __OS2__ +#define TORRENT_OS2 +#define TORRENT_HAS_FALLOCATE 0 +#define TORRENT_USE_IFCONF 1 +#define TORRENT_USE_SYSCTL 1 +#define TORRENT_USE_MLOCK 0 +#define TORRENT_USE_IPV6 0 +#define TORRENT_ICONV_ARG (const char**) +#define TORRENT_USE_WRITEV 0 +#define TORRENT_USE_READV 0 + +#else + +#ifdef _MSC_VER +#pragma message ( "unknown OS, assuming BSD" ) +#else +#warning "unknown OS, assuming BSD" +#endif + +#define TORRENT_BSD +#endif + +// on windows, NAME_MAX refers to Unicode characters +// on linux it refers to bytes (utf-8 encoded) +// TODO: Make this count Unicode characters instead of bytes on windows + +// windows +#if defined FILENAME_MAX +#define TORRENT_MAX_PATH FILENAME_MAX + +// beos +#elif defined B_PATH_NAME_LENGTH +#define TORRENT_MAX_PATH B_PATH_NAME_LENGTH + +// solaris +#elif defined MAXPATH +#define TORRENT_MAX_PATH MAXPATH + +// posix +#elif defined NAME_MAX +#define TORRENT_MAX_PATH NAME_MAX + +// none of the above +#else +// this is the maximum number of characters in a +// path element / filename on windows +#define TORRENT_MAX_PATH 255 + +#ifdef _MSC_VER +#pragma message ( "unknown platform, assuming the longest path is 255" ) +#else +#warning "unknown platform, assuming the longest path is 255" +#endif + +#endif + +#if defined TORRENT_WINDOWS && !defined TORRENT_MINGW + +#include + +// internal +#ifdef __cplusplus +inline +#else +static +#endif +int snprintf(char* buf, int len, char const* fmt, ...) +{ + va_list lp; + int ret; + va_start(lp, fmt); + ret = _vsnprintf(buf, len, fmt, lp); + va_end(lp); + if (ret < 0) { buf[len-1] = 0; ret = len-1; } + return ret; +} + +#define strtoll _strtoi64 +#else +#include +#endif + +#if (defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)) \ + && !defined (TORRENT_UPNP_LOGGING) && TORRENT_USE_IOSTREAM +#define TORRENT_UPNP_LOGGING +#endif + +#ifndef TORRENT_ICONV_ARG +#define TORRENT_ICONV_ARG (char**) +#endif + +// libiconv presence, not implemented yet +#ifndef TORRENT_USE_ICONV +#define TORRENT_USE_ICONV 1 +#endif + +#ifndef TORRENT_HAS_SALEN +#define TORRENT_HAS_SALEN 1 +#endif + +#ifndef TORRENT_USE_GETADAPTERSADDRESSES +#define TORRENT_USE_GETADAPTERSADDRESSES 0 +#endif + +#ifndef TORRENT_USE_NETLINK +#define TORRENT_USE_NETLINK 0 +#endif + +#ifndef TORRENT_USE_EXECINFO +#define TORRENT_USE_EXECINFO 0 +#endif + +#ifndef TORRENT_USE_SYSCTL +#define TORRENT_USE_SYSCTL 0 +#endif + +#ifndef TORRENT_USE_GETIPFORWARDTABLE +#define TORRENT_USE_GETIPFORWARDTABLE 0 +#endif + +#ifndef TORRENT_USE_LOCALE +#define TORRENT_USE_LOCALE 0 +#endif + +// set this to true if close() may block on your system +// Mac OS X does this if the file being closed is not fully +// allocated on disk yet for instance. When defined, the disk +// I/O subsytem will use a separate thread for closing files +#ifndef TORRENT_CLOSE_MAY_BLOCK +#define TORRENT_CLOSE_MAY_BLOCK 0 +#endif + +#ifndef TORRENT_BROKEN_UNIONS +#define TORRENT_BROKEN_UNIONS 0 +#endif + +#ifndef TORRENT_USE_WSTRING +#if !defined BOOST_NO_STD_WSTRING +#define TORRENT_USE_WSTRING 1 +#else +#define TORRENT_USE_WSTRING 0 +#endif // BOOST_NO_STD_WSTRING +#endif // TORRENT_USE_WSTRING + +#ifndef TORRENT_HAS_FALLOCATE +#define TORRENT_HAS_FALLOCATE 1 +#endif + +#ifndef TORRENT_DEPRECATED_PREFIX +#define TORRENT_DEPRECATED_PREFIX +#endif + +#ifndef TORRENT_USE_COMMONCRYPTO +#define TORRENT_USE_COMMONCRYPTO 0 +#endif + +#ifndef TORRENT_DEPRECATED +#define TORRENT_DEPRECATED +#endif + +#ifndef TORRENT_COMPLETE_TYPES_REQUIRED +#define TORRENT_COMPLETE_TYPES_REQUIRED 0 +#endif + +#ifndef TORRENT_USE_UNC_PATHS +#define TORRENT_USE_UNC_PATHS 0 +#endif + +#ifndef TORRENT_USE_RLIMIT +#define TORRENT_USE_RLIMIT 1 +#endif + +#ifndef TORRENT_USE_IFADDRS +#define TORRENT_USE_IFADDRS 0 +#endif + +#ifndef TORRENT_USE_IPV6 +#define TORRENT_USE_IPV6 1 +#endif + +#ifndef TORRENT_USE_MLOCK +#define TORRENT_USE_MLOCK 1 +#endif + +#ifndef TORRENT_USE_WRITEV +#define TORRENT_USE_WRITEV 1 +#endif + +#ifndef TORRENT_USE_READV +#define TORRENT_USE_READV 1 +#endif + +#ifndef TORRENT_NO_FPU +#define TORRENT_NO_FPU 0 +#endif + +#ifndef TORRENT_USE_IOSTREAM +#ifndef BOOST_NO_IOSTREAM +#define TORRENT_USE_IOSTREAM 1 +#else +#define TORRENT_USE_IOSTREAM 0 +#endif +#endif + +// if set to true, piece picker will use less RAM +// but only support up to ~260000 pieces in a torrent +#ifndef TORRENT_COMPACT_PICKER +#define TORRENT_COMPACT_PICKER 0 +#endif + +#ifndef TORRENT_USE_I2P +#define TORRENT_USE_I2P 1 +#endif + +#if !defined TORRENT_IOV_MAX +#ifdef IOV_MAX +#define TORRENT_IOV_MAX IOV_MAX +#else +#define TORRENT_IOV_MAX INT_MAX +#endif +#endif + +#if !defined(TORRENT_READ_HANDLER_MAX_SIZE) +# ifdef _GLIBCXX_DEBUG +# define TORRENT_READ_HANDLER_MAX_SIZE 400 +# else +// if this is not divisible by 8, we're wasting space +# define TORRENT_READ_HANDLER_MAX_SIZE 336 +# endif +#endif + +#if !defined(TORRENT_WRITE_HANDLER_MAX_SIZE) +# ifdef _GLIBCXX_DEBUG +# define TORRENT_WRITE_HANDLER_MAX_SIZE 400 +# else +// if this is not divisible by 8, we're wasting space +# define TORRENT_WRITE_HANDLER_MAX_SIZE 336 +# endif +#endif + +#if defined _MSC_VER && _MSC_VER <= 1200 +#define for if (false) {} else for +#endif + +#if TORRENT_BROKEN_UNIONS +#define TORRENT_UNION struct +#else +#define TORRENT_UNION union +#endif + +// determine what timer implementation we can use +// if one is already defined, don't pick one +// autmatically. This lets the user control this +// from the Jamfile +#if !defined TORRENT_USE_ABSOLUTE_TIME \ + && !defined TORRENT_USE_QUERY_PERFORMANCE_TIMER \ + && !defined TORRENT_USE_CLOCK_GETTIME \ + && !defined TORRENT_USE_BOOST_DATE_TIME \ + && !defined TORRENT_USE_ECLOCK \ + && !defined TORRENT_USE_SYSTEM_TIME + +#if defined __APPLE__ && defined __MACH__ +#define TORRENT_USE_ABSOLUTE_TIME 1 +#elif defined(_WIN32) || defined TORRENT_MINGW +#define TORRENT_USE_QUERY_PERFORMANCE_TIMER 1 +#elif defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0 +#define TORRENT_USE_CLOCK_GETTIME 1 +#elif defined(TORRENT_AMIGA) +#define TORRENT_USE_ECLOCK 1 +#elif defined(TORRENT_BEOS) +#define TORRENT_USE_SYSTEM_TIME 1 +#else +#define TORRENT_USE_BOOST_DATE_TIME 1 +#endif + +#endif + +// debug builds have asserts enabled by default, release +// builds have asserts if they are explicitly enabled by +// the release_asserts macro. +#ifndef TORRENT_USE_ASSERTS +#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS +#define TORRENT_USE_ASSERTS 1 +#else +#define TORRENT_USE_ASSERTS 0 +#endif +#endif // TORRENT_USE_ASSERTS + +#if defined TORRENT_DEBUG && TORRENT_USE_ASSERTS \ + && !defined TORRENT_DISABLE_INVARIANT_CHECKS +#define TORRENT_USE_INVARIANT_CHECKS 1 +#else +#define TORRENT_USE_INVARIANT_CHECKS 0 +#endif + +// for non-exception builds +#ifdef BOOST_NO_EXCEPTIONS +#define TORRENT_TRY if (true) +#define TORRENT_CATCH(x) else if (false) +#define TORRENT_CATCH_ALL else if (false) +#define TORRENT_DECLARE_DUMMY(x, y) x y +#else +#define TORRENT_TRY try +#define TORRENT_CATCH(x) catch(x) +#define TORRENT_CATCH_ALL catch(...) +#define TORRENT_DECLARE_DUMMY(x, y) +#endif // BOOST_NO_EXCEPTIONS + + +#endif // TORRENT_CONFIG_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/connection_queue.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/connection_queue.hpp new file mode 100644 index 0000000000..73be10d7ab --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/connection_queue.hpp @@ -0,0 +1,150 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_CONNECTION_QUEUE +#define TORRENT_CONNECTION_QUEUE + +#include +#include +#include +#include +#include "libtorrent/io_service.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/deadline_timer.hpp" + +#ifdef TORRENT_CONNECTION_LOGGING +#include +#endif + +#include "libtorrent/thread.hpp" + +namespace libtorrent +{ + +class TORRENT_EXTRA_EXPORT connection_queue : public boost::noncopyable +{ +public: + connection_queue(io_service& ios); + + // if there are no free slots, returns the negative + // number of queued up connections + int free_slots() const; + + void enqueue(boost::function const& on_connect + , boost::function const& on_timeout + , time_duration timeout, int priority = 0); + bool done(int ticket); + void limit(int limit); + int limit() const; + void close(); + int size() const { return m_queue.size(); } + int num_connecting() const { return m_num_connecting; } +#if defined TORRENT_ASIO_DEBUGGING + float next_timeout() const { return total_milliseconds(m_timer.expires_at() - time_now_hires()) / 1000.f; } + float max_timeout() const + { + ptime max_timeout = min_time(); + for (std::list::const_iterator i = m_queue.begin() + , end(m_queue.end()); i != end; ++i) + { + if (!i->connecting) continue; + if (i->expires > max_timeout) max_timeout = i->expires; + } + if (max_timeout == min_time()) return 0.f; + return total_milliseconds(max_timeout - time_now_hires()) / 1000.f; + } +#endif + +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + +private: + + typedef mutex mutex_t; + + void try_connect(mutex_t::scoped_lock& l); + void on_timeout(error_code const& e); + void on_try_connect(); + + struct entry + { + entry() + : expires(max_time()) + , ticket(0) + , connecting(false) + , priority(0) + {} + // called when the connection is initiated + // this is when the timeout countdown starts + boost::function on_connect; + // called if done hasn't been called within the timeout + // or if the connection queue aborts. This means there + // are 3 different interleaves of these function calls: + // 1. on_connect + // 2. on_connect, on_timeout + // 3. on_timeout + boost::function on_timeout; + ptime expires; + time_duration timeout; + boost::int32_t ticket; + bool connecting; + boost::uint8_t priority; + }; + + std::list m_queue; + + // the next ticket id a connection will be given + int m_next_ticket; + int m_num_connecting; + int m_half_open_limit; + bool m_abort; + + // the number of outstanding timers + int m_num_timers; + + deadline_timer m_timer; + + mutable mutex_t m_mutex; + +#ifdef TORRENT_DEBUG + bool m_in_timeout_function; +#endif +#ifdef TORRENT_CONNECTION_LOGGING + std::ofstream m_log; +#endif +}; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/copy_ptr.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/copy_ptr.hpp new file mode 100644 index 0000000000..2010b3f61b --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/copy_ptr.hpp @@ -0,0 +1,69 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_COPY_PTR +#define TORRENT_COPY_PTR + +namespace libtorrent +{ + template + struct copy_ptr + { + copy_ptr(): m_ptr(0) {} + copy_ptr(T* t): m_ptr(t) {} + copy_ptr(copy_ptr const& p): m_ptr(p.m_ptr ? new T(*p.m_ptr) : 0) {} + void reset(T* t = 0) { delete m_ptr; m_ptr = t; } + copy_ptr& operator=(copy_ptr const& p) + { + delete m_ptr; + m_ptr = p.m_ptr ? new T(*p.m_ptr) : 0; + return *this; + } + T* operator->() { return m_ptr; } + T const* operator->() const { return m_ptr; } + T& operator*() { return *m_ptr; } + T const& operator*() const { return *m_ptr; } + void swap(copy_ptr& p) + { + T* tmp = m_ptr; + m_ptr = p.m_ptr; + p.m_ptr = tmp; + } + operator bool() const { return m_ptr != 0; } + ~copy_ptr() { delete m_ptr; } + private: + T* m_ptr; + }; +} + +#endif // TORRENT_COPY_PTR + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/create_torrent.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/create_torrent.hpp new file mode 100644 index 0000000000..3ae4429ccf --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/create_torrent.hpp @@ -0,0 +1,505 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_CREATE_TORRENT_HPP_INCLUDED +#define TORRENT_CREATE_TORRENT_HPP_INCLUDED + +#include "libtorrent/bencode.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/file_storage.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/storage.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/utf8.hpp" +#include "libtorrent/allocator.hpp" +#include "libtorrent/file.hpp" // for combine_path etc. + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// OVERVIEW +// +// This section describes the functions and classes that are used +// to create torrent files. It is a layered API with low level classes +// and higher level convenience functions. A torrent is created in 4 +// steps: +// +// 1. first the files that will be part of the torrent are determined. +// 2. the torrent properties are set, such as tracker url, web seeds, +// DHT nodes etc. +// 3. Read through all the files in the torrent, SHA-1 all the data +// and set the piece hashes. +// 4. The torrent is bencoded into a file or buffer. +// +// If there are a lot of files and or deep directoy hierarchies to +// traverse, step one can be time consuming. +// +// Typically step 3 is by far the most time consuming step, since it +// requires to read all the bytes from all the files in the torrent. +// +// All of these classes and functions are declared by including +// ``libtorrent/create_torrent.hpp``. +// +// example: +// +// .. code:: c++ +// +// file_storage fs; +// +// // recursively adds files in directories +// add_files(fs, "./my_torrent"); +// +// create_torrent t(fs); +// t.add_tracker("http://my.tracker.com/announce"); +// t.set_creator("libtorrent example"); +// +// // reads the files and calculates the hashes +// set_piece_hashes(t, "."); +// +// ofstream out("my_torrent.torrent", std::ios_base::binary); +// bencode(std::ostream_iterator(out), t.generate()); +// +namespace libtorrent +{ + class torrent_info; + + // This class holds state for creating a torrent. After having added + // all information to it, call create_torrent::generate() to generate + // the torrent. The entry that's returned can then be bencoded into a + // .torrent file using bencode(). + struct TORRENT_EXPORT create_torrent + { + // flags for create_torrent::create_torrent(). + enum flags_t + { + // This will insert pad files to align the files to piece boundaries, for + // optimized disk-I/O. + optimize = 1 + + // This will create a merkle hash tree torrent. A merkle torrent cannot + // be opened in clients that don't specifically support merkle torrents. + // The benefit is that the resulting torrent file will be much smaller and + // not grow with more pieces. When this option is specified, it is + // recommended to have a fairly small piece size, say 64 kiB. + // When creating merkle torrents, the full hash tree is also generated + // and should be saved off separately. It is accessed through the + // create_torrent::merkle_tree() function. + , merkle = 2 + + // This will include the file modification time as part of the torrent. + // This is not enabled by default, as it might cause problems when you + // create a torrent from separate files with the same content, hoping to + // yield the same info-hash. If the files have different modification times, + // with this option enabled, you would get different info-hashes for the + // files. + , modification_time = 4 + + // If this flag is set, files that are symlinks get a symlink attribute + // set on them and their data will not be included in the torrent. This + // is useful if you need to reconstruct a file hierarchy which contains + // symlinks. + , symlinks = 8 + + // If this is set, the set_piece_hashes() function will, as it calculates + // the piece hashes, also calculate the file hashes and add those associated + // with each file. Note that unless you use the set_piece_hashes() function, + // this flag will have no effect. + , calculate_file_hashes = 16 + }; + + // The ``piece_size`` is the size of each piece in bytes. It must + // be a multiple of 16 kiB. If a piece size of 0 is specified, a + // piece_size will be calculated such that the torrent file is roughly 40 kB. + // + // If a ``pad_size_limit`` is specified (other than -1), any file larger than + // the specified number of bytes will be preceeded by a pad file to align it + // with the start of a piece. The pad_file_limit is ignored unless the + // ``optimize`` flag is passed. Typically it doesn't make sense to set this + // any lower than 4kiB. + // + // The overload that takes a ``torrent_info`` object will make a verbatim + // copy of its info dictionary (to preserve the info-hash). The copy of + // the info dictionary will be used by create_torrent::generate(). This means + // that none of the member functions of create_torrent that affects + // the content of the info dictionary (such as ``set_hash()``), will + // have any affect. + // + // The ``flags`` arguments specifies options for the torrent creation. It can + // be any combination of the flags defined by create_torrent::flags_t. + // + // ``alignment`` is used when pad files are enabled. This is the size + // eligible files are aligned to. The default is -1, which means the + // piece size of the torrent. + create_torrent(file_storage& fs, int piece_size = 0 + , int pad_file_limit = -1, int flags = optimize, int alignment = -1); + create_torrent(torrent_info const& ti); + + // internal + ~create_torrent(); + + // This function will generate the .torrent file as a bencode tree. In order to + // generate the flat file, use the bencode() function. + // + // It may be useful to add custom entries to the torrent file before bencoding it + // and saving it to disk. + // + // If anything goes wrong during torrent generation, this function will return + // an empty ``entry`` structure. You can test for this condition by querying the + // type of the entry: + // + // .. code:: c++ + // + // file_storage fs; + // // add file ... + // create_torrent t(fs); + // // add trackers and piece hashes ... + // e = t.generate(); + // + // if (e.type() == entry::undefined_t) + // { + // // something went wrong + // } + // + // For instance, you cannot generate a torrent with 0 files in it. If you don't add + // any files to the ``file_storage``, torrent generation will fail. + entry generate() const; + + // returns an immutable reference to the file_storage used to create + // the torrent from. + file_storage const& files() const { return m_files; } + + // Sets the comment for the torrent. The string ``str`` should be utf-8 encoded. + // The comment in a torrent file is optional. + void set_comment(char const* str); + + // Sets the creator of the torrent. The string ``str`` should be utf-8 encoded. + // This is optional. + void set_creator(char const* str); + + // This sets the SHA-1 hash for the specified piece (``index``). You are required + // to set the hash for every piece in the torrent before generating it. If you have + // the files on disk, you can use the high level convenience function to do this. + // See set_piece_hashes(). + void set_hash(int index, sha1_hash const& h); + + // This sets the sha1 hash for this file. This hash will end up under the key ``sha1`` + // associated with this file (for multi-file torrents) or in the root info dictionary + // for single-file torrents. + void set_file_hash(int index, sha1_hash const& h); + + // This adds a url seed to the torrent. You can have any number of url seeds. For a + // single file torrent, this should be an HTTP url, pointing to a file with identical + // content as the file of the torrent. For a multi-file torrent, it should point to + // a directory containing a directory with the same name as this torrent, and all the + // files of the torrent in it. + // + // The second function, ``add_http_seed()`` adds an HTTP seed instead. + void add_url_seed(std::string const& url); + void add_http_seed(std::string const& url); + + // This adds a DHT node to the torrent. This especially useful if you're creating a + // tracker less torrent. It can be used by clients to bootstrap their DHT node from. + // The node is a hostname and a port number where there is a DHT node running. + // You can have any number of DHT nodes in a torrent. + void add_node(std::pair const& node); + + // Adds a tracker to the torrent. This is not strictly required, but most torrents + // use a tracker as their main source of peers. The url should be an http:// or udp:// + // url to a machine running a bittorrent tracker that accepts announces for this torrent's + // info-hash. The tier is the fallback priority of the tracker. All trackers with tier 0 are + // tried first (in any order). If all fail, trackers with tier 1 are tried. If all of those + // fail, trackers with tier 2 are tried, and so on. + void add_tracker(std::string const& url, int tier = 0); + + // This function sets an X.509 certificate in PEM format to the torrent. This makes the + // torrent an *SSL torrent*. An SSL torrent requires that each peer has a valid certificate + // signed by this root certificate. For SSL torrents, all peers are connecting over SSL + // connections. For more information, see the section on ssl-torrents_. + // + // The string is not the path to the cert, it's the actual content of the certificate, + // loaded into a std::string. + void set_root_cert(std::string const& pem); + + // Sets and queries the private flag of the torrent. + // Torrents with the private flag set ask clients to not use any other + // sources than the tracker for peers, and to not advertize itself publicly, + // apart from the tracker. + void set_priv(bool p) { m_private = p; } + bool priv() const { return m_private; } + + // returns the number of pieces in the associated file_storage object. + int num_pieces() const { return m_files.num_pieces(); } + + // ``piece_length()`` returns the piece size of all pieces but the + // last one. ``piece_size()`` returns the size of the specified piece. + // these functions are just forwarding to the associated file_storage. + int piece_length() const { return m_files.piece_length(); } + int piece_size(int i) const { return m_files.piece_size(i); } + + // internal + bool should_add_file_hashes() const { return m_calculate_file_hashes; } + + // This function returns the merkle hash tree, if the torrent was created as a merkle + // torrent. The tree is created by ``generate()`` and won't be valid until that function + // has been called. When creating a merkle tree torrent, the actual tree itself has to + // be saved off separately and fed into libtorrent the first time you start seeding it, + // through the ``torrent_info::set_merkle_tree()`` function. From that point onwards, the + // tree will be saved in the resume data. + std::vector const& merkle_tree() const { return m_merkle_tree; } + + private: + + file_storage& m_files; + // if m_info_dict is initialized, it is + // used instead of m_files to generate + // the info dictionary + entry m_info_dict; + + // the urls to the trackers + typedef std::pair announce_entry; + std::vector m_urls; + + std::vector m_url_seeds; + std::vector m_http_seeds; + + std::vector m_piece_hash; + + std::vector m_filehashes; + + // if we're generating a merkle torrent, this is the + // merkle tree we got. This should be saved in fast-resume + // in order to start seeding the torrent + mutable std::vector m_merkle_tree; + + // dht nodes to add to the routing table/bootstrap from + typedef std::vector > nodes_t; + nodes_t m_nodes; + + // the hash that identifies this torrent + // is mutable because it's calculated + // lazily + mutable sha1_hash m_info_hash; + + // if a creation date is found in the torrent file + // this will be set to that, otherwise it'll be + // 1970, Jan 1 + time_t m_creation_date; + + // if a comment is found in the torrent file + // this will be set to that comment + std::string m_comment; + + // an optional string naming the software used + // to create the torrent file + std::string m_created_by; + + // this is the root cert for SSL torrents + std::string m_root_cert; + + // this is used when creating a torrent. If there's + // only one file there are cases where it's impossible + // to know if it should be written as a multifile torrent + // or not. e.g. test/test there's one file and one directory + // and they have the same name. + bool m_multifile:1; + + // this is true if the torrent is private. i.e., is should not + // be announced on the dht + bool m_private:1; + + // if set to one, a merkle torrent will be generated + bool m_merkle_torrent:1; + + // if set, include the 'mtime' modification time in the + // torrent file + bool m_include_mtime:1; + + // if set, symbolic links are declared as such in + // the torrent file. The full data of the pointed-to + // file is still included + bool m_include_symlinks:1; + + // this is only used by set_piece_hashes(). It will + // calculate sha1 hashes for each file and add it + // to the file list + bool m_calculate_file_hashes:1; + }; + + namespace detail + { + inline bool default_pred(std::string const&) { return true; } + + inline bool ignore_subdir(std::string const& leaf) + { return leaf == ".." || leaf == "."; } + + inline void nop(int) {} + + int get_file_attributes(std::string const& p); + std::string get_symlink_path(std::string const& p); + + // internal + TORRENT_EXPORT void add_files_impl(file_storage& fs, std::string const& p + , std::string const& l, boost::function pred + , boost::uint32_t flags); + } + + // Adds the file specified by ``path`` to the file_storage object. In case ``path`` + // refers to a diretory, files will be added recursively from the directory. + // + // If specified, the predicate ``p`` is called once for every file and directory that + // is encountered. files for which ``p`` returns true are added, and directories for + // which ``p`` returns true are traversed. ``p`` must have the following signature:: + // + // bool Pred(std::string const& p); + // + // The path that is passed in to the predicate is the full path of the file or + // directory. If no predicate is specified, all files are added, and all directories + // are traveresed. + // + // The ".." directory is never traversed. + // + // The ``flags`` argument should be the same as the flags passed to the `create_torrent`_ + // constructor. + template void add_files(file_storage& fs, std::string const& file, Pred p, boost::uint32_t flags = 0) + { + detail::add_files_impl(fs, parent_path(complete(file)), filename(file), p, flags); + } + inline void add_files(file_storage& fs, std::string const& file, boost::uint32_t flags = 0) + { + detail::add_files_impl(fs, parent_path(complete(file)), filename(file) + , detail::default_pred, flags); + } + + // This function will assume that the files added to the torrent file exists at path + // ``p``, read those files and hash the content and set the hashes in the ``create_torrent`` + // object. The optional function ``f`` is called in between every hash that is set. ``f`` + // must have the following signature:: + // + // void Fun(int); + // + // The overloads that don't take an ``error_code&`` may throw an exception in case of a + // file error, the other overloads sets the error code to reflect the error, if any. + TORRENT_EXPORT void set_piece_hashes(create_torrent& t, std::string const& p + , boost::function f, error_code& ec); + inline void set_piece_hashes(create_torrent& t, std::string const& p, error_code& ec) + { + set_piece_hashes(t, p, detail::nop, ec); + } +#ifndef BOOST_NO_EXCEPTIONS + inline void set_piece_hashes(create_torrent& t, std::string const& p) + { + error_code ec; + set_piece_hashes(t, p, detail::nop, ec); + if (ec) throw libtorrent_exception(ec); + } + template + void set_piece_hashes(create_torrent& t, std::string const& p, Fun f) + { + error_code ec; + set_piece_hashes(t, p, f, ec); + if (ec) throw libtorrent_exception(ec); + } +#endif + +#if TORRENT_USE_WSTRING + // wstring versions + + // all wstring APIs are deprecated since 0.16.11 + // instead, use the wchar -> utf8 conversion functions + // and pass in utf8 strings +#ifndef TORRENT_NO_DEPRECATE + + template + TORRENT_DEPRECATED_PREFIX + void TORRENT_DEPRECATED add_files(file_storage& fs, std::wstring const& wfile, Pred p, boost::uint32_t flags = 0) + { + std::string utf8; + wchar_utf8(wfile, utf8); + detail::add_files_impl(fs, parent_path(complete(utf8)) + , filename(utf8), p, flags); + } + + TORRENT_DEPRECATED_PREFIX + inline void TORRENT_DEPRECATED add_files(file_storage& fs, std::wstring const& wfile, boost::uint32_t flags = 0) + { + std::string utf8; + wchar_utf8(wfile, utf8); + detail::add_files_impl(fs, parent_path(complete(utf8)) + , filename(utf8), detail::default_pred, flags); + } + + void TORRENT_EXPORT set_piece_hashes(create_torrent& t, std::wstring const& p + , boost::function const& f, error_code& ec); + +#ifndef BOOST_NO_EXCEPTIONS + template + TORRENT_DEPRECATED_PREFIX + void TORRENT_DEPRECATED set_piece_hashes(create_torrent& t, std::wstring const& p, Fun f) + { + error_code ec; + set_piece_hashes(t, p, f, ec); + if (ec) throw libtorrent_exception(ec); + } + + TORRENT_DEPRECATED_PREFIX + inline void TORRENT_DEPRECATED set_piece_hashes(create_torrent& t, std::wstring const& p) + { + error_code ec; + set_piece_hashes(t, p, detail::nop, ec); + if (ec) throw libtorrent_exception(ec); + } +#endif + + TORRENT_DEPRECATED_PREFIX + inline void TORRENT_DEPRECATED set_piece_hashes(create_torrent& t, std::wstring const& p, error_code& ec) + { + set_piece_hashes(t, p, detail::nop, ec); + } +#endif // TORRENT_NO_DEPRECATE +#endif // TORRENT_USE_WSTRING + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/deadline_timer.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/deadline_timer.hpp new file mode 100644 index 0000000000..71c52c796a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/deadline_timer.hpp @@ -0,0 +1,103 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DEADLINE_TIMER_HPP_INCLUDED +#define TORRENT_DEADLINE_TIMER_HPP_INCLUDED + +#ifdef __OBJC__ +#define Protocol Protocol_ +#endif + +#if __GNUC__ < 3 +// in GCC 2.95 templates seems to have all symbols +// resolved as they are parsed, so the time_traits +// template actually needs the definitions it uses, +// even though it's never instantiated +#include +#else +#include +#endif + +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif + +#ifdef __OBJC__ +#undef Protocol +#endif + +#include "libtorrent/time.hpp" + +// asio time_traits +#if !TORRENT_USE_BOOST_DATE_TIME +#if BOOST_VERSION >= 103500 +namespace boost { +#endif +namespace asio +{ + template<> + struct time_traits + { + typedef libtorrent::ptime time_type; + typedef libtorrent::time_duration duration_type; + static time_type now() + { return time_type(libtorrent::time_now_hires()); } + static time_type add(time_type t, duration_type d) + { return time_type(t.time + d.diff);} + static duration_type subtract(time_type t1, time_type t2) + { return duration_type(t1 - t2); } + static bool less_than(time_type t1, time_type t2) + { return t1 < t2; } + static boost::posix_time::time_duration to_posix_duration( + duration_type d) + { return boost::posix_time::microseconds(libtorrent::total_microseconds(d)); } + }; +} +#if BOOST_VERSION >= 103500 +} +#endif +#endif + +namespace libtorrent +{ + +#if BOOST_VERSION < 103500 + typedef ::asio::basic_deadline_timer deadline_timer; +#else + typedef boost::asio::basic_deadline_timer deadline_timer; +#endif +} + +#endif // TORRENT_DEADLINE_TIMER_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/debug.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/debug.hpp new file mode 100644 index 0000000000..02a2af49ce --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/debug.hpp @@ -0,0 +1,219 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DEBUG_HPP_INCLUDED +#define TORRENT_DEBUG_HPP_INCLUDED + +#if defined TORRENT_ASIO_DEBUGGING + +#include "libtorrent/assert.hpp" +#include "libtorrent/thread.hpp" + +#include +#include + +std::string demangle(char const* name); + +namespace libtorrent +{ + struct async_t + { + async_t() : refs(0) {} + std::string stack; + int refs; + }; + + extern std::map _async_ops; + extern int _async_ops_nthreads; + extern mutex _async_ops_mutex; + + inline void add_outstanding_async(char const* name) + { + mutex::scoped_lock l(_async_ops_mutex); + async_t& a = _async_ops[name]; + if (a.stack.empty()) + { + char stack_text[10000]; + print_backtrace(stack_text, sizeof(stack_text), 9); + a.stack = stack_text; + } + ++a.refs; + } + + inline void complete_async(char const* name) + { + mutex::scoped_lock l(_async_ops_mutex); + async_t& a = _async_ops[name]; + TORRENT_ASSERT(a.refs > 0); + --a.refs; + } + + inline void async_inc_threads() + { + mutex::scoped_lock l(_async_ops_mutex); + ++_async_ops_nthreads; + } + + inline void async_dec_threads() + { + mutex::scoped_lock l(_async_ops_mutex); + --_async_ops_nthreads; + } + + inline int log_async() + { + mutex::scoped_lock l(_async_ops_mutex); + int ret = 0; + for (std::map::iterator i = _async_ops.begin() + , end(_async_ops.end()); i != end; ++i) + { + if (i->second.refs <= _async_ops_nthreads - 1) continue; + ret += i->second.refs; + printf("%s: (%d)\n%s\n", i->first.c_str(), i->second.refs, i->second.stack.c_str()); + } + return ret; + } +} + +#endif + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + +#include +#include "libtorrent/config.hpp" +#include "libtorrent/file.hpp" +#include "libtorrent/thread.hpp" + +#if TORRENT_USE_IOSTREAM +#include +#include +#include +#endif + +namespace libtorrent +{ + // DEBUG API + + struct logger + { +#if TORRENT_USE_IOSTREAM + // all log streams share a single file descriptor + // and re-opens the file for each log line + // these members are defined in session_impl.cpp + static std::ofstream log_file; + static std::string open_filename; + static mutex file_mutex; +#endif + + ~logger() + { + mutex::scoped_lock l(file_mutex); + log_file.close(); + open_filename.clear(); + } + + logger(std::string const& logpath, std::string const& filename + , int instance, bool append) + { + char log_name[512]; + snprintf(log_name, sizeof(log_name), "libtorrent_logs%d", instance); + std::string dir(complete(combine_path(combine_path(logpath, log_name), filename)) + ".log"); + error_code ec; + if (!exists(parent_path(dir))) + create_directories(parent_path(dir), ec); + m_filename = dir; + + mutex::scoped_lock l(file_mutex); + open(!append); + log_file << "\n\n\n*** starting log ***\n"; + } + + void move_log_file(std::string const& logpath, std::string const& new_name, int instance) + { + mutex::scoped_lock l(file_mutex); + if (open_filename == m_filename) + { + log_file.close(); + open_filename.clear(); + } + + char log_name[512]; + snprintf(log_name, sizeof(log_name), "libtorrent_logs%d", instance); + std::string dir(combine_path(combine_path(complete(logpath), log_name), new_name) + ".log"); + + error_code ec; + create_directories(parent_path(dir), ec); + + if (ec) + fprintf(stderr, "Failed to create logfile directory %s: %s\n" + , parent_path(dir).c_str(), ec.message().c_str()); + ec.clear(); + rename(m_filename, dir, ec); + if (ec) + fprintf(stderr, "Failed to move logfile %s: %s\n" + , parent_path(dir).c_str(), ec.message().c_str()); + + m_filename = dir; + } + +#if TORRENT_USE_IOSTREAM + void open(bool truncate) + { + if (open_filename == m_filename) return; + log_file.close(); + log_file.clear(); + log_file.open(m_filename.c_str(), truncate ? std::ios_base::trunc : std::ios_base::app); + open_filename = m_filename; + if (!log_file.good()) + fprintf(stderr, "Failed to open logfile %s: %s\n", m_filename.c_str(), strerror(errno)); + } +#endif + + template + logger& operator<<(T const& v) + { +#if TORRENT_USE_IOSTREAM + mutex::scoped_lock l(file_mutex); + open(false); + log_file << v; +#endif + return *this; + } + + std::string m_filename; + }; + +} + +#endif // TORRENT_VERBOSE_LOGGING || TORRENT_LOGGING || TORRENT_ERROR_LOGGING +#endif // TORRENT_DEBUG_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/disk_buffer_holder.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/disk_buffer_holder.hpp new file mode 100644 index 0000000000..80905cf7b5 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/disk_buffer_holder.hpp @@ -0,0 +1,101 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISK_BUFFER_HOLDER_HPP_INCLUDED +#define TORRENT_DISK_BUFFER_HOLDER_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" +#include + +namespace libtorrent +{ + + namespace aux { struct session_impl; } + struct disk_buffer_pool; + + // The disk buffer holder acts like a ``scoped_ptr`` that frees a disk buffer + // when it's destructed, unless it's released. ``release`` returns the disk + // buffer and transferres ownership and responsibility to free it to the caller. + // + // A disk buffer is freed by passing it to ``session_impl::free_disk_buffer()``. + // + // ``buffer()`` returns the pointer without transferring responsibility. If + // this buffer has been released, ``buffer()`` will return 0. + struct TORRENT_EXPORT disk_buffer_holder + { + // internal + disk_buffer_holder(aux::session_impl& ses, char* buf); + + // construct a buffer holder that will free the held buffer + // using a disk buffer pool directly (there's only one + // disk_buffer_pool per session) + disk_buffer_holder(disk_buffer_pool& disk_pool, char* buf); + + // frees any unreleased disk buffer held by this object + ~disk_buffer_holder(); + + // return the held disk buffer and clear it from the + // holder. The responsibility to free it is passed on + // to the caller + char* release(); + + // return a pointer to the held buffer + char* get() const { return m_buf; } + + // set the holder object to hold the specified buffer + // (or NULL by default). If it's already holding a + // disk buffer, it will first be freed. + void reset(char* buf = 0); + + // swap pointers of two disk buffer holders. + void swap(disk_buffer_holder& h) + { + TORRENT_ASSERT(&h.m_disk_pool == &m_disk_pool); + std::swap(h.m_buf, m_buf); + } + + typedef char* (disk_buffer_holder::*unspecified_bool_type)(); + + // internal + operator unspecified_bool_type() const + { return m_buf == 0? 0: &disk_buffer_holder::release; } + + private: + disk_buffer_pool& m_disk_pool; + char* m_buf; + }; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/disk_buffer_pool.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/disk_buffer_pool.hpp new file mode 100644 index 0000000000..9df167e20b --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/disk_buffer_pool.hpp @@ -0,0 +1,145 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISK_BUFFER_POOL +#define TORRENT_DISK_BUFFER_POOL + +#include + +#include "libtorrent/config.hpp" +#include "libtorrent/thread.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/allocator.hpp" + +#ifndef TORRENT_DISABLE_POOL_ALLOCATOR +#include +#endif + +#ifdef TORRENT_DISK_STATS +#include +#endif + +#if TORRENT_USE_ASSERTS || TORRENT_DISK_STATS +#include +#endif + +namespace libtorrent +{ + struct TORRENT_EXTRA_EXPORT disk_buffer_pool : boost::noncopyable + { + disk_buffer_pool(int block_size); +#if TORRENT_USE_ASSERTS + ~disk_buffer_pool(); +#endif + +#if TORRENT_USE_ASSERTS || TORRENT_DISK_STATS + bool is_disk_buffer(char* buffer + , mutex::scoped_lock& l) const; + bool is_disk_buffer(char* buffer) const; +#endif + + char* allocate_buffer(char const* category); + void free_buffer(char* buf); + void free_multiple_buffers(char** bufvec, int numbufs); + + int block_size() const { return m_block_size; } + +#ifdef TORRENT_STATS + int disk_allocations() const + { return m_allocations; } +#endif + +#ifdef TORRENT_DISK_STATS + std::ofstream m_disk_access_log; +#endif + + void release_memory(); + + int in_use() const { return m_in_use; } + + protected: + + void free_buffer_impl(char* buf, mutex::scoped_lock& l); + + // number of bytes per block. The BitTorrent + // protocol defines the block size to 16 KiB. + const int m_block_size; + + // number of disk buffers currently allocated + int m_in_use; + + session_settings m_settings; + + private: + + mutable mutex m_pool_mutex; + +#ifndef TORRENT_DISABLE_POOL_ALLOCATOR + // if this is true, all buffers are allocated + // from m_pool. If this is false, all buffers + // are allocated using page_aligned_allocator. + // if the settings change to prefer the other + // allocator, this bool will not switch over + // to match the settings until all buffers have + // been freed. That way, we never have a mixture + // of buffers allocated from different sources. + // in essence, this make the setting only take + // effect after a restart (which seems fine). + // or once the client goes idle for a while. + bool m_using_pool_allocator; + + // memory pool for read and write operations + // and disk cache + boost::pool m_pool; +#endif + +#if defined TORRENT_DISK_STATS || defined TORRENT_STATS + int m_allocations; +#endif +#ifdef TORRENT_DISK_STATS + public: + void rename_buffer(char* buf, char const* category); + protected: + boost::unordered_map m_categories; + boost::unordered_map m_buf_to_category; + std::ofstream m_log; + private: +#endif +#if TORRENT_USE_ASSERTS + int m_magic; +#endif + }; + +} + +#endif // TORRENT_DISK_BUFFER_POOL + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/disk_io_thread.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/disk_io_thread.hpp new file mode 100644 index 0000000000..975f855c6c --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/disk_io_thread.hpp @@ -0,0 +1,534 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISK_IO_THREAD +#define TORRENT_DISK_IO_THREAD + +#include "libtorrent/storage.hpp" +#include "libtorrent/allocator.hpp" +#include "libtorrent/io_service.hpp" +#include "libtorrent/sliding_average.hpp" + +#include +#include +#include +#include +#include +#include "libtorrent/config.hpp" +#include "libtorrent/thread.hpp" +#include "libtorrent/disk_buffer_pool.hpp" + +#include +#include +#include +#include + +namespace libtorrent +{ + using boost::multi_index::multi_index_container; + using boost::multi_index::ordered_non_unique; + using boost::multi_index::ordered_unique; + using boost::multi_index::indexed_by; + using boost::multi_index::member; + using boost::multi_index::const_mem_fun; + + struct cached_piece_info + { + // the piece index for this cache entry. + int piece; + + // holds one entry for each block in this piece. ``true`` represents + // the data for that block being in the disk cache and ``false`` means it's not. + std::vector blocks; + + // the time when a block was last written to this piece. The older + // a piece is, the more likely it is to be flushed to disk. + ptime last_use; + + // The index of the next block that needs to be hashed. + // Blocks are hashed as they are downloaded in order to not + // have to re-read them from disk once the piece is complete, to + // compare its hash against the hashes in the .torrent file. + int next_to_hash; + + enum kind_t { read_cache = 0, write_cache = 1 }; + + // specifies if this piece is part of the read cache or the write cache. + kind_t kind; + }; + + struct disk_io_job + { + disk_io_job() + : buffer(0) + , buffer_size(0) + , piece(0) + , offset(0) + , max_cache_line(0) + , cache_min_time(0) + , action(read) + {} + + enum action_t + { + read + , write + , hash + , move_storage + , release_files + , delete_files + , check_fastresume + , check_files + , save_resume_data + , rename_file + , abort_thread + , clear_read_cache + , abort_torrent + , update_settings + , read_and_hash + , cache_piece + , file_priority +#ifndef TORRENT_NO_DEPRECATE + , finalize_file +#endif + }; + + + char* buffer; + + // this is called when operation completes + boost::function callback; + + boost::intrusive_ptr storage; + + boost::shared_ptr resume_data; + + // the error code from the file operation + error_code error; + + // the time when this job was issued. This is used to + // keep track of disk I/O congestion + ptime start_time; + + // used for move_storage and rename_file. On errors, this is set + // to the error message + std::string str; + + // on error, this is set to the path of the + // file the disk operation failed on + std::string error_file; + + int buffer_size; + + // arguments used for read and write + // piece is used as flags for move_storage + int piece, offset; + + // if this is > 0, it specifies the max number of blocks to read + // ahead in the read cache for this access. This is only valid + // for 'read' actions + int max_cache_line; + + // if this is > 0, it may increase the minimum time the cache + // line caused by this operation stays in the cache + int cache_min_time; + + boost::uint8_t action; + }; + + // returns true if the fundamental operation + // of the given disk job is a read operation + bool is_read_operation(disk_io_job const& j); + + // this is true if the buffer field in the disk_io_job + // points to a disk buffer + bool operation_has_buffer(disk_io_job const& j); + + // this struct holds a number of statistics counters + // relevant for the disk io thread and disk cache. + struct TORRENT_EXPORT cache_status + { + // initializes all counters to 0 + cache_status() + : blocks_written(0) + , writes(0) + , blocks_read(0) + , blocks_read_hit(0) + , reads(0) + , queued_bytes(0) + , cache_size(0) + , read_cache_size(0) + , total_used_buffers(0) + , average_queue_time(0) + , average_read_time(0) + , average_write_time(0) + , average_hash_time(0) + , average_job_time(0) + , average_sort_time(0) + , job_queue_length(0) + , cumulative_job_time(0) + , cumulative_read_time(0) + , cumulative_write_time(0) + , cumulative_hash_time(0) + , cumulative_sort_time(0) + , total_read_back(0) + , read_queue_size(0) + {} + + // the total number of 16 KiB blocks written to disk + // since this session was started. + size_type blocks_written; + + // the total number of write operations performed since this + // session was started. + // + // The ratio (``blocks_written`` - ``writes``) / ``blocks_written`` represents + // the number of saved write operations per total write operations. i.e. a kind + // of cache hit ratio for the write cahe. + size_type writes; + + // the number of blocks that were requested from the + // bittorrent engine (from peers), that were served from disk or cache. + size_type blocks_read; + + // the number of blocks that was just copied from the read cache + // + // The ratio ``blocks_read_hit`` / ``blocks_read`` is the cache hit ratio + // for the read cache. + size_type blocks_read_hit; + + // the number of read operations used + size_type reads; + + // the number of bytes waiting, in the disk job queue, to be written + // or inserted into the disk cache + mutable size_type queued_bytes; + + // the number of 16 KiB blocks currently in the disk cache (both read and write). + // This includes both read and write cache. + int cache_size; + + // the number of 16KiB blocks in the read cache. + int read_cache_size; + + // the total number of buffers currently in use. + // This includes the read/write disk cache as well as send and receive buffers + // used in peer connections. + mutable int total_used_buffers; + + // the number of microseconds an average disk I/O job + // has to wait in the job queue before it get processed. + int average_queue_time; + + // the time read jobs takes on average to complete + // (not including the time in the queue), in microseconds. This only measures + // read cache misses. + int average_read_time; + + // the time write jobs takes to complete, on average, + // in microseconds. This does not include the time the job sits in the disk job + // queue or in the write cache, only blocks that are flushed to disk. + int average_write_time; + + // the time hash jobs takes to complete on average, in + // microseconds. Hash jobs include running SHA-1 on the data (which for the most + // part is done incrementally) and sometimes reading back parts of the piece. It + // also includes checking files without valid resume data. + int average_hash_time; + int average_job_time; + int average_sort_time; + + // the number of jobs in the job queue. + int job_queue_length; + + // the number of milliseconds spent in all disk jobs, and specific ones + // since the start of the session. Times are specified in milliseconds + boost::uint32_t cumulative_job_time; + boost::uint32_t cumulative_read_time; + boost::uint32_t cumulative_write_time; + boost::uint32_t cumulative_hash_time; + boost::uint32_t cumulative_sort_time; + + // the number of blocks that had to be read back from disk because + // they were flushed before the SHA-1 hash got to hash them. If this + // is large, a larger cache could significantly improve performance + int total_read_back; + + // number of read jobs in the disk job queue + int read_queue_size; + }; + + // this is a singleton consisting of the thread and a queue + // of disk io jobs + struct TORRENT_EXTRA_EXPORT disk_io_thread : disk_buffer_pool + { + disk_io_thread(io_service& ios + , boost::function const& queue_callback + , file_pool& fp + , int block_size = 16 * 1024); + ~disk_io_thread(); + + void abort(); + void join(); + + // aborts read operations + void stop(boost::intrusive_ptr s); + + // returns the disk write queue size + int add_job(disk_io_job const& j + , boost::function const& f + = boost::function()); + + // keep track of the number of bytes in the job queue + // at any given time. i.e. the sum of all buffer_size. + // this is used to slow down the download global download + // speed when the queue buffer size is too big. + size_type queue_buffer_size() const; + bool can_write() const; + + void get_cache_info(sha1_hash const& ih + , std::vector& ret) const; + + cache_status status() const; + + void thread_fun(); + +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + + struct cached_block_entry + { + cached_block_entry(): buf(0) {} + // the buffer pointer (this is a disk_pool buffer) + // or 0 + char* buf; + + // callback for when this block is flushed to disk + boost::function callback; + }; + + struct cached_piece_entry + { + int piece; + // storage this piece belongs to + boost::intrusive_ptr storage; + // the pointers to the block data + boost::shared_array blocks; + // the last time a block was writting to this piece + // plus the minimum amount of time the block is guaranteed + // to stay in the cache + ptime expire; + // the number of blocks in the cache for this piece + int num_blocks; + // used to determine if this piece should be flushed + int num_contiguous_blocks; + // this is the first block that has not yet been hashed + // by the partial hasher. When minimizing read-back, this + // is used to determine if flushing a range would force us + // to read it back later when hashing + int next_block_to_hash; + + std::pair storage_piece_pair() const + { return std::pair(storage.get(), piece); } + }; + + typedef multi_index_container< + cached_piece_entry, indexed_by< + ordered_unique + , &cached_piece_entry::storage_piece_pair> > + , ordered_non_unique > + > + > cache_t; + + typedef cache_t::nth_index<0>::type cache_piece_index_t; + typedef cache_t::nth_index<1>::type cache_lru_index_t; + + private: + + int add_job(disk_io_job const& j + , mutex::scoped_lock& l + , boost::function const& f + = boost::function()); + + bool test_error(disk_io_job& j); + void post_callback(disk_io_job const& j, int ret); + + // cache operations + cache_piece_index_t::iterator find_cached_piece( + cache_t& cache, disk_io_job const& j + , mutex::scoped_lock& l); + bool is_cache_hit(cached_piece_entry& p + , disk_io_job const& j, mutex::scoped_lock& l); + int copy_from_piece(cached_piece_entry& p, bool& hit + , disk_io_job const& j, mutex::scoped_lock& l); + + struct ignore_t + { + ignore_t(): piece(-1), storage(0) {} + ignore_t(int idx, piece_manager* st): piece(idx), storage(st) {} + int piece; + piece_manager* storage; + }; + + // write cache operations + enum options_t { dont_flush_write_blocks = 1, ignore_cache_size = 2 }; + int flush_cache_blocks(mutex::scoped_lock& l + , int blocks, ignore_t ignore = ignore_t(), int options = 0); + void flush_expired_pieces(); + int flush_contiguous_blocks(cached_piece_entry& p + , mutex::scoped_lock& l, int lower_limit = 0, bool avoid_readback = false); + int flush_range(cached_piece_entry& p, int start, int end, mutex::scoped_lock& l); + int cache_block(disk_io_job& j + , boost::function& handler + , int cache_expire + , mutex::scoped_lock& l); + + // read cache operations + int clear_oldest_read_piece(int num_blocks, ignore_t ignore + , mutex::scoped_lock& l); + int read_into_piece(cached_piece_entry& p, int start_block + , int options, int num_blocks, mutex::scoped_lock& l); + int cache_read_block(disk_io_job const& j, mutex::scoped_lock& l); + int free_piece(cached_piece_entry& p, mutex::scoped_lock& l); + int drain_piece_bufs(cached_piece_entry& p, std::vector& buf + , mutex::scoped_lock& l); + + enum cache_flags_t { + cache_only = 1 + }; + int try_read_from_cache(disk_io_job const& j, bool& hit, int flags = 0); + int read_piece_from_cache_and_hash(disk_io_job const& j, sha1_hash& h); + int cache_piece(disk_io_job const& j, cache_piece_index_t::iterator& p + , bool& hit, int options, mutex::scoped_lock& l); + + // this mutex only protects m_jobs, m_queue_buffer_size, + // m_exceeded_write_queue and m_abort + mutable mutex m_queue_mutex; + event m_signal; + bool m_abort; + bool m_waiting_to_shutdown; + std::deque m_jobs; + size_type m_queue_buffer_size; + + ptime m_last_file_check; + + // this protects the piece cache and related members + mutable mutex m_piece_mutex; + // write cache + cache_t m_pieces; + + // read cache + cache_t m_read_pieces; + + void flip_stats(ptime now); + + // total number of blocks in use by both the read + // and the write cache. This is not supposed to + // exceed m_cache_size + cache_status m_cache_stats; + + // keeps average queue time for disk jobs (in microseconds) + average_accumulator m_queue_time; + + // average read time for cache misses (in microseconds) + average_accumulator m_read_time; + + // average write time (in microseconds) + average_accumulator m_write_time; + + // average hash time (in microseconds) + average_accumulator m_hash_time; + + // average time to serve a job (any job) in microseconds + average_accumulator m_job_time; + + // average time to ask for physical offset on disk + // and insert into queue + average_accumulator m_sort_time; + + // the last time we reset the average time and store the + // latest value in m_cache_stats + ptime m_last_stats_flip; + + typedef std::multimap read_jobs_t; + read_jobs_t m_sorted_read_jobs; + +#ifdef TORRENT_DISK_STATS + std::ofstream m_log; +#endif + + // the amount of physical ram in the machine + boost::uint64_t m_physical_ram; + + // if we exceeded the max queue disk write size + // this is set to true. It remains true until the + // queue is smaller than the low watermark + bool m_exceeded_write_queue; + + io_service& m_ios; + + boost::function m_queue_callback; + + // this keeps the io_service::run() call blocked from + // returning. When shutting down, it's possible that + // the event queue is drained before the disk_io_thread + // has posted its last callback. When this happens, the + // io_service will have a pending callback from the + // disk_io_thread, but the event loop is not running. + // this means that the event is destructed after the + // disk_io_thread. If the event refers to a disk buffer + // it will try to free it, but the buffer pool won't + // exist anymore, and crash. This prevents that. + boost::optional m_work; + + // reference to the file_pool which is a member of + // the session_impl object + file_pool& m_file_pool; + + // when completion notifications are queued, they're stuck + // in this list + std::list > m_queued_completions; + +#if TORRENT_USE_ASSERTS + int m_magic; +#endif + + // thread for performing blocking disk io operations + thread m_disk_io_thread; + }; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/ed25519.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/ed25519.hpp new file mode 100644 index 0000000000..9701680774 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/ed25519.hpp @@ -0,0 +1,32 @@ +#ifndef ED25519_HPP +#define ED25519_HPP + +#include "libtorrent/export.hpp" // for TORRENT_EXPORT +#include // for size_t + +enum +{ + ed25519_seed_size = 32, + ed25519_private_key_size = 64, + ed25519_public_key_size = 32, + ed25519_signature_size = 64, + ed25519_scalar_size = 32, + ed25519_shared_secret_size = 32 +}; + +extern "C" { + +#ifndef ED25519_NO_SEED +int TORRENT_EXPORT ed25519_create_seed(unsigned char *seed); +#endif + +void TORRENT_EXPORT ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed); +void TORRENT_EXPORT ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key); +int TORRENT_EXPORT ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *private_key); +void TORRENT_EXPORT ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar); +void TORRENT_EXPORT ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key); + +} + +#endif // ED25519_HPP + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/entry.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/entry.hpp new file mode 100644 index 0000000000..1b3a17d230 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/entry.hpp @@ -0,0 +1,310 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ENTRY_HPP_INCLUDED +#define TORRENT_ENTRY_HPP_INCLUDED + +/* + * + * This file declares the entry class. It is a + * variant-type that can be an integer, list, + * dictionary (map) or a string. This type is + * used to hold bdecoded data (which is the + * encoding BitTorrent messages uses). + * + * it has 4 accessors to access the actual + * type of the object. They are: + * integer() + * string() + * list() + * dict() + * The actual type has to match the type you + * are asking for, otherwise you will get an + * assertion failure. + * When you default construct an entry, it is + * uninitialized. You can initialize it through the + * assignment operator, copy-constructor or + * the constructor that takes a data_type enum. + * + * + */ + + +#include +#include +#include +#include + +#include "libtorrent/size_type.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/max.hpp" + +#if TORRENT_USE_IOSTREAM +#include +#endif + +namespace libtorrent +{ + struct lazy_entry; + + // thrown by any accessor function of entry if the accessor + // function requires a type different than the actual type + // of the entry object. + struct TORRENT_EXPORT type_error: std::runtime_error + { + // internal + type_error(const char* error): std::runtime_error(error) {} + }; + + // The ``entry`` class represents one node in a bencoded hierarchy. It works as a + // variant type, it can be either a list, a dictionary (``std::map``), an integer + // or a string. + class TORRENT_EXPORT entry + { + public: + + // the key is always a string. If a generic entry would be allowed + // as a key, sorting would become a problem (e.g. to compare a string + // to a list). The definition doesn't mention such a limit though. + typedef std::map dictionary_type; + typedef std::string string_type; + typedef std::list list_type; + typedef size_type integer_type; + + // the types an entry can have + enum data_type + { + int_t, + string_t, + list_t, + dictionary_t, + undefined_t + }; + + // returns the concrete type of the entry + data_type type() const; + + // constructors directly from a specific type. + // The content of the argument is copied into the + // newly constructed entry + entry(dictionary_type const&); + entry(string_type const&); + entry(list_type const&); + entry(integer_type const&); + + // construct an empty entry of the specified type. + // see data_type enum. + entry(data_type t); + + // hidden + entry(entry const& e); + + // hidden + entry(); + + // hidden + ~entry(); + + // hidden + bool operator==(entry const& e) const; + bool operator!=(entry const& e) const { return !(*this == e); } + + // copies the structure of the right hand side into this + // entry. + void operator=(lazy_entry const&); + void operator=(entry const&); + void operator=(dictionary_type const&); + void operator=(string_type const&); + void operator=(list_type const&); + void operator=(integer_type const&); + + // The ``integer()``, ``string()``, ``list()`` and ``dict()`` functions + // are accessors that return the respective type. If the ``entry`` object + // isn't of the type you request, the accessor will throw + // libtorrent_exception (which derives from ``std::runtime_error``). You + // can ask an ``entry`` for its type through the ``type()`` function. + // + // If you want to create an ``entry`` you give it the type you want it to + // have in its constructor, and then use one of the non-const accessors + // to get a reference which you then can assign the value you want it to + // have. + // + // The typical code to get info from a torrent file will then look like + // this: + // + // .. code:: c++ + // + // entry torrent_file; + // // ... + // + // // throws if this is not a dictionary + // entry::dictionary_type const& dict = torrent_file.dict(); + // entry::dictionary_type::const_iterator i; + // i = dict.find("announce"); + // if (i != dict.end()) + // { + // std::string tracker_url = i->second.string(); + // std::cout << tracker_url << "\n"; + // } + // + // + // The following code is equivalent, but a little bit shorter: + // + // .. code:: c++ + // + // entry torrent_file; + // // ... + // + // // throws if this is not a dictionary + // if (entry* i = torrent_file.find_key("announce")) + // { + // std::string tracker_url = i->string(); + // std::cout << tracker_url << "\n"; + // } + // + // + // To make it easier to extract information from a torrent file, the + // class torrent_info exists. + integer_type& integer(); + const integer_type& integer() const; + string_type& string(); + const string_type& string() const; + list_type& list(); + const list_type& list() const; + dictionary_type& dict(); + const dictionary_type& dict() const; + + // swaps the content of *this* with ``e``. + void swap(entry& e); + + // All of these functions requires the entry to be a dictionary, if it + // isn't they will throw ``libtorrent::type_error``. + // + // The non-const versions of the ``operator[]`` will return a reference + // to either the existing element at the given key or, if there is no + // element with the given key, a reference to a newly inserted element at + // that key. + // + // The const version of ``operator[]`` will only return a reference to an + // existing element at the given key. If the key is not found, it will + // throw ``libtorrent::type_error``. + entry& operator[](char const* key); + entry& operator[](std::string const& key); +#ifndef BOOST_NO_EXCEPTIONS + const entry& operator[](char const* key) const; + const entry& operator[](std::string const& key) const; +#endif + + // These functions requires the entry to be a dictionary, if it isn't + // they will throw ``libtorrent::type_error``. + // + // They will look for an element at the given key in the dictionary, if + // the element cannot be found, they will return 0. If an element with + // the given key is found, the return a pointer to it. + entry* find_key(char const* key); + entry const* find_key(char const* key) const; + entry* find_key(std::string const& key); + entry const* find_key(std::string const& key) const; + + // returns a pretty-printed string representation + // of the bencoded structure, with JSON-style syntax + std::string to_string() const; + + protected: + + void construct(data_type t); + void copy(const entry& e); + void destruct(); + + private: + + void to_string_impl(std::string& out, int indent) const; + +#if (defined(_MSC_VER) && _MSC_VER < 1310) || TORRENT_COMPLETE_TYPES_REQUIRED + // workaround for msvc-bug. + // assumes sizeof(map) == sizeof(map) + // and sizeof(list) == sizeof(list) + enum { union_size + = max4) + , sizeof(std::map) + , sizeof(string_type) + , sizeof(integer_type)>::value + }; +#else + enum { union_size + = max4::value + }; +#endif + integer_type data[(union_size + sizeof(integer_type) - 1) + / sizeof(integer_type)]; + + // the bitfield is used so that the m_type_queried field still fits, so + // that the ABI is the same for debug builds and release builds. It + // appears to be very hard to match debug builds with debug versions of + // libtorrent + boost::uint8_t m_type:7; + + public: + // in debug mode this is set to false by bdecode to indicate that the + // program has not yet queried the type of this entry, and sould not + // assume that it has a certain type. This is asserted in the accessor + // functions. This does not apply if exceptions are used. + mutable boost::uint8_t m_type_queried:1; + }; + +#if TORRENT_USE_IOSTREAM + // prints the bencoded structure to the ostream as a JSON-style structure. + inline std::ostream& operator<<(std::ostream& os, const entry& e) + { + os << e.to_string(); + return os; + } +#endif + +#ifndef BOOST_NO_EXCEPTIONS + // internal + inline void throw_type_error() + { + throw libtorrent_exception(error_code(errors::invalid_entry_type + , get_libtorrent_category())); + } +#endif + +} + +#endif // TORRENT_ENTRY_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/enum_net.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/enum_net.hpp new file mode 100644 index 0000000000..7866211656 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/enum_net.hpp @@ -0,0 +1,83 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ENUM_NET_HPP_INCLUDED +#define TORRENT_ENUM_NET_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include +#include "libtorrent/io_service_fwd.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/error_code.hpp" + +namespace libtorrent +{ + + // the interface should not have a netmask + struct ip_interface + { + address interface_address; + address netmask; + char name[64]; + int mtu; + }; + + struct ip_route + { + address destination; + address netmask; + address gateway; + char name[64]; + int mtu; + }; + + // returns a list of the configured IP interfaces + // on the machine + TORRENT_EXTRA_EXPORT std::vector enum_net_interfaces(io_service& ios + , error_code& ec); + + TORRENT_EXTRA_EXPORT std::vector enum_routes(io_service& ios, error_code& ec); + + // return (a1 & mask) == (a2 & mask) + TORRENT_EXTRA_EXPORT bool match_addr_mask(address const& a1, address const& a2, address const& mask); + + // returns true if the specified address is on the same + // local network as us + TORRENT_EXTRA_EXPORT bool in_local_network(io_service& ios, address const& addr + , error_code& ec); + + TORRENT_EXTRA_EXPORT address get_default_gateway(io_service& ios + , error_code& ec); +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/error.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/error.hpp new file mode 100644 index 0000000000..8812e57a2e --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/error.hpp @@ -0,0 +1,62 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ERROR_HPP_INCLUDED +#define TORRENT_ERROR_HPP_INCLUDED + +#include +#include "libtorrent/config.hpp" + +#if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN +// asio assumes that the windows error codes are defined already +#include +#endif + +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif + +namespace libtorrent +{ + +#if BOOST_VERSION < 103500 + namespace error = asio::error; +#else + namespace error = boost::asio::error; +#endif + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/error_code.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/error_code.hpp new file mode 100644 index 0000000000..3a2fa64e31 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/error_code.hpp @@ -0,0 +1,562 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ERROR_CODE_HPP_INCLUDED +#define TORRENT_ERROR_CODE_HPP_INCLUDED + +#include +#include "libtorrent/config.hpp" + +#if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN +// asio assumes that the windows error codes are defined already +#include +#endif + +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif + +#include "libtorrent/string_util.hpp" // for allocate_string_copy +#include // free + +#ifndef BOOST_SYSTEM_NOEXCEPT +#define BOOST_SYSTEM_NOEXCEPT throw() +#endif + +namespace libtorrent +{ + + namespace errors + { + // libtorrent uses boost.system's ``error_code`` class to represent errors. libtorrent has + // its own error category get_libtorrent_category() whith the error codes defined by error_code_enum. + enum error_code_enum + { + // Not an error + no_error = 0, + // Two torrents has files which end up overwriting each other + file_collision, + // A piece did not match its piece hash + failed_hash_check, + // The .torrent file does not contain a bencoded dictionary at + // its top level + torrent_is_no_dict, + // The .torrent file does not have an ``info`` dictionary + torrent_missing_info, + // The .torrent file's ``info`` entry is not a dictionary + torrent_info_no_dict, + // The .torrent file does not have a ``piece length`` entry + torrent_missing_piece_length, + // The .torrent file does not have a ``name`` entry + torrent_missing_name, + // The .torrent file's name entry is invalid + torrent_invalid_name, + // The length of a file, or of the whole .torrent file is invalid. + // Either negative or not an integer + torrent_invalid_length, + // Failed to parse a file entry in the .torrent + torrent_file_parse_failed, + // The ``pieces`` field is missing or invalid in the .torrent file + torrent_missing_pieces, + // The ``pieces`` string has incorrect length + torrent_invalid_hashes, + // The .torrent file has more pieces than is supported by libtorrent + too_many_pieces_in_torrent, + // The metadata (.torrent file) that was received from the swarm + // matched the info-hash, but failed to be parsed + invalid_swarm_metadata, + // The file or buffer is not correctly bencoded + invalid_bencoding, + // The .torrent file does not contain any files + no_files_in_torrent, + // The string was not properly url-encoded as expected + invalid_escaped_string, + // Operation is not permitted since the session is shutting down + session_is_closing, + // There's already a torrent with that info-hash added to the + // session + duplicate_torrent, + // The supplied torrent_handle is not referring to a valid torrent + invalid_torrent_handle, + // The type requested from the entry did not match its type + invalid_entry_type, + // The specified URI does not contain a valid info-hash + missing_info_hash_in_uri, + // One of the files in the torrent was unexpectadly small. This + // might be caused by files being changed by an external process + file_too_short, + // The URL used an unknown protocol. Currently ``http`` and + // ``https`` (if built with openssl support) are recognized. For + // trackers ``udp`` is recognized as well. + unsupported_url_protocol, + // The URL did not conform to URL syntax and failed to be parsed + url_parse_error, + // The peer sent a 'piece' message of length 0 + peer_sent_empty_piece, + // A bencoded structure was currupt and failed to be parsed + parse_failed, + // The fast resume file was missing or had an invalid file version + // tag + invalid_file_tag, + // The fast resume file was missing or had an invalid info-hash + missing_info_hash, + // The info-hash did not match the torrent + mismatching_info_hash, + // The URL contained an invalid hostname + invalid_hostname, + // The URL had an invalid port + invalid_port, + // The port is blocked by the port-filter, and prevented the + // connection + port_blocked, + // The IPv6 address was expected to end with ']' + expected_close_bracket_in_address, + // The torrent is being destructed, preventing the operation to + // succeed + destructing_torrent, + // The connection timed out + timed_out, + // The peer is upload only, and we are upload only. There's no point + // in keeping the connection + upload_upload_connection, + // The peer is upload only, and we're not interested in it. There's + // no point in keeping the connection + uninteresting_upload_peer, + // The peer sent an unknown info-hash + invalid_info_hash, + // The torrent is paused, preventing the operation from succeeding + torrent_paused, + // The peer sent an invalid have message, either wrong size or + // referring to a piece that doesn't exist in the torrent + invalid_have, + // The bitfield message had the incorrect size + invalid_bitfield_size, + // The peer kept requesting pieces after it was choked, possible + // abuse attempt. + too_many_requests_when_choked, + // The peer sent a piece message that does not correspond to a + // piece request sent by the client + invalid_piece, + // memory allocation failed + no_memory, + // The torrent is aborted, preventing the operation to succeed + torrent_aborted, + // The peer is a connection to ourself, no point in keeping it + self_connection, + // The peer sent a piece message with invalid size, either negative + // or greater than one block + invalid_piece_size, + // The peer has not been interesting or interested in us for too + // long, no point in keeping it around + timed_out_no_interest, + // The peer has not said anything in a long time, possibly dead + timed_out_inactivity, + // The peer did not send a handshake within a reasonable amount of + // time, it might not be a bittorrent peer + timed_out_no_handshake, + // The peer has been unchoked for too long without requesting any + // data. It might be lying about its interest in us + timed_out_no_request, + // The peer sent an invalid choke message + invalid_choke, + // The peer send an invalid unchoke message + invalid_unchoke, + // The peer sent an invalid interested message + invalid_interested, + // The peer sent an invalid not-interested message + invalid_not_interested, + // The peer sent an invalid piece request message + invalid_request, + // The peer sent an invalid hash-list message (this is part of the + // merkle-torrent extension) + invalid_hash_list, + // The peer sent an invalid hash-piece message (this is part of the + // merkle-torrent extension) + invalid_hash_piece, + // The peer sent an invalid cancel message + invalid_cancel, + // The peer sent an invalid DHT port-message + invalid_dht_port, + // The peer sent an invalid suggest piece-message + invalid_suggest, + // The peer sent an invalid have all-message + invalid_have_all, + // The peer sent an invalid have none-message + invalid_have_none, + // The peer sent an invalid reject message + invalid_reject, + // The peer sent an invalid allow fast-message + invalid_allow_fast, + // The peer sent an invalid extesion message ID + invalid_extended, + // The peer sent an invalid message ID + invalid_message, + // The synchronization hash was not found in the encrypted handshake + sync_hash_not_found, + // The encryption constant in the handshake is invalid + invalid_encryption_constant, + // The peer does not support plaintext, which is the selected mode + no_plaintext_mode, + // The peer does not support rc4, which is the selected mode + no_rc4_mode, + // The peer does not support any of the encryption modes that the + // client supports + unsupported_encryption_mode, + // The peer selected an encryption mode that the client did not + // advertise and does not support + unsupported_encryption_mode_selected, + // The pad size used in the encryption handshake is of invalid size + invalid_pad_size, + // The encryption handshake is invalid + invalid_encrypt_handshake, + // The client is set to not support incoming encrypted connections + // and this is an encrypted connection + no_incoming_encrypted, + // The client is set to not support incoming regular bittorrent + // connections, and this is a regular connection + no_incoming_regular, + // The client is already connected to this peer-ID + duplicate_peer_id, + // Torrent was removed + torrent_removed, + // The packet size exceeded the upper sanity check-limit + packet_too_large, + + reserved, + + // The web server responded with an error + http_error, + // The web server response is missing a location header + missing_location, + // The web seed redirected to a path that no longer matches the + // .torrent directory structure + invalid_redirection, + // The connection was closed becaused it redirected to a different + // URL + redirecting, + // The HTTP range header is invalid + invalid_range, + // The HTTP response did not have a content length + no_content_length, + // The IP is blocked by the IP filter + banned_by_ip_filter, + // At the connection limit + too_many_connections, + // The peer is marked as banned + peer_banned, + // The torrent is stopping, causing the operation to fail + stopping_torrent, + // The peer has sent too many corrupt pieces and is banned + too_many_corrupt_pieces, + // The torrent is not ready to receive peers + torrent_not_ready, + // The peer is not completely constructed yet + peer_not_constructed, + // The session is closing, causing the operation to fail + session_closing, + // The peer was disconnected in order to leave room for a + // potentially better peer + optimistic_disconnect, + // The torrent is finished + torrent_finished, + // No UPnP router found + no_router, + // The metadata message says the metadata exceeds the limit + metadata_too_large, + // The peer sent an invalid metadata request message + invalid_metadata_request, + // The peer advertised an invalid metadata size + invalid_metadata_size, + // The peer sent a message with an invalid metadata offset + invalid_metadata_offset, + // The peer sent an invalid metadata message + invalid_metadata_message, + // The peer sent a peer exchange message that was too large + pex_message_too_large, + // The peer sent an invalid peer exchange message + invalid_pex_message, + // The peer sent an invalid tracker exchange message + invalid_lt_tracker_message, + // The peer sent an pex messages too often. This is a possible + // attempt of and attack + too_frequent_pex, + // The operation failed because it requires the torrent to have + // the metadata (.torrent file) and it doesn't have it yet. + // This happens for magnet links before they have downloaded the + // metadata, and also torrents added by URL. + no_metadata, + // The peer sent an invalid ``dont_have`` message. The dont have + // message is an extension to allow peers to advertise that the + // no longer has a piece they previously had. + invalid_dont_have, + // The peer tried to connect to an SSL torrent without connecting + // over SSL. + requires_ssl_connection, + // The peer tried to connect to a torrent with a certificate + // for a different torrent. + invalid_ssl_cert, + // the torrent is not an SSL torrent, and the operation requires + // an SSL torrent + not_an_ssl_torrent, + + + // The NAT-PMP router responded with an unsupported protocol version + unsupported_protocol_version = 120, + // You are not authorized to map ports on this NAT-PMP router + natpmp_not_authorized, + // The NAT-PMP router failed because of a network failure + network_failure, + // The NAT-PMP router failed because of lack of resources + no_resources, + // The NAT-PMP router failed because an unsupported opcode was sent + unsupported_opcode, + + + + // The resume data file is missing the 'file sizes' entry + missing_file_sizes = 130, + // The resume data file 'file sizes' entry is empty + no_files_in_resume_data, + // The resume data file is missing the 'pieces' and 'slots' entry + missing_pieces, + // The number of files in the resume data does not match the number + // of files in the torrent + mismatching_number_of_files, + // One of the files on disk has a different size than in the fast + // resume file + mismatching_file_size, + // One of the files on disk has a different timestamp than in the + // fast resume file + mismatching_file_timestamp, + // The resume data file is not a dictionary + not_a_dictionary, + // The 'blocks per piece' entry is invalid in the resume data file + invalid_blocks_per_piece, + // The resume file is missing the 'slots' entry, which is required + // for torrents with compact allocation + missing_slots, + // The resume file contains more slots than the torrent + too_many_slots, + // The 'slot' entry is invalid in the resume data + invalid_slot_list, + // One index in the 'slot' list is invalid + invalid_piece_index, + // The pieces on disk needs to be re-ordered for the specified + // allocation mode. This happens if you specify sparse allocation + // and the files on disk are using compact storage. The pieces needs + // to be moved to their right position + pieces_need_reorder, + + + + // The HTTP header was not correctly formatted + http_parse_error = 150, + // The HTTP response was in the 300-399 range but lacked a location + // header + http_missing_location, + // The HTTP response was encoded with gzip or deflate but + // decompressing it failed + http_failed_decompress, + + + + // The URL specified an i2p address, but no i2p router is configured + no_i2p_router = 160, + + + + // The tracker URL doesn't support transforming it into a scrape + // URL. i.e. it doesn't contain "announce. + scrape_not_available = 170, + // invalid tracker response + invalid_tracker_response, + // invalid peer dictionary entry. Not a dictionary + invalid_peer_dict, + // tracker sent a failure message + tracker_failure, + // missing or invalid 'files' entry + invalid_files_entry, + // missing or invalid 'hash' entry + invalid_hash_entry, + // missing or invalid 'peers' and 'peers6' entry + invalid_peers_entry, + // udp tracker response packet has invalid size + invalid_tracker_response_length, + // invalid transaction id in udp tracker response + invalid_tracker_transaction_id, + // invalid action field in udp tracker response + invalid_tracker_action, + +#ifndef TORRENT_NO_DEPRECATE + // expected string in bencoded string + expected_string = 190, + // expected colon in bencoded string + expected_colon, + // unexpected end of file in bencoded string + unexpected_eof, + // expected value (list, dict, int or string) in bencoded string + expected_value, + // bencoded recursion depth limit exceeded + depth_exceeded, + // bencoded item count limit exceeded + limit_exceeded, + // integer overflow + overflow, +#endif + + // the number of error codes + error_code_max + }; + + // HTTP errors are reported in the libtorrent::http_category, with error code enums in + // the ``libtorrent::errors`` namespace. + enum http_errors + { + cont = 100, + ok = 200, + created = 201, + accepted = 202, + no_content = 204, + multiple_choices = 300, + moved_permanently = 301, + moved_temporarily = 302, + not_modified = 304, + bad_request = 400, + unauthorized = 401, + forbidden = 403, + not_found = 404, + internal_server_error = 500, + not_implemented = 501, + bad_gateway = 502, + service_unavailable = 503 + }; + + // hidden + TORRENT_EXPORT boost::system::error_code make_error_code(error_code_enum e); + + } // namespace errors + +#if BOOST_VERSION < 103500 + typedef asio::error_code error_code; + // hidden + inline asio::error::error_category get_posix_category() + { return asio::error::system_category; } + // hidden + inline asio::error::error_category get_system_category() + { return asio::error::system_category; } + + // hidden + boost::system::error_category const& get_libtorrent_category() + { + static ::asio::error::error_category libtorrent_category(20); + return libtorrent_category; + } + + // hidden + boost::system::error_category const& get_http_category() + { + static ::asio::error::error_category http_category(21); + return http_category; + } + +#else + + // return the instance of the libtorrent_error_category which + // maps libtorrent error codes to human readable error messages. + TORRENT_EXPORT boost::system::error_category& get_libtorrent_category(); + + // returns the error_category for HTTP errors + TORRENT_EXPORT boost::system::error_category& get_http_category(); + + using boost::system::error_code; + + // hidden + inline boost::system::error_category const& get_system_category() +#if BOOST_VERSION < 104400 + { return boost::system::get_system_category(); } +#else + { return boost::system::system_category(); } +#endif + + // hidden + inline boost::system::error_category const& get_posix_category() +#if BOOST_VERSION < 103600 + { return boost::system::get_posix_category(); } +#elif BOOST_VERSION < 104400 + { return boost::system::get_generic_category(); } +#else + { return boost::system::generic_category(); } +#endif // BOOST_VERSION < 103600 +#endif // BOOST_VERSION < 103500 + + // internal + inline boost::system::error_category const& generic_category() + { return get_posix_category(); } + +#ifndef BOOST_NO_EXCEPTIONS + struct TORRENT_EXPORT libtorrent_exception: std::exception + { + libtorrent_exception(error_code const& s): m_error(s), m_msg(0) {} + virtual const char* what() const throw(); + virtual ~libtorrent_exception() throw(); + error_code error() const { return m_error; } + private: + error_code m_error; + mutable char* m_msg; + }; +#endif +} + +#if BOOST_VERSION >= 103500 + +namespace boost { namespace system { + + template<> struct is_error_code_enum + { static const bool value = true; }; + + template<> struct is_error_condition_enum + { static const bool value = true; }; + + template<> struct is_error_code_enum + { static const bool value = true; }; + + template<> struct is_error_condition_enum + { static const bool value = true; }; +} } + +#endif // BOOST_VERSION + + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/escape_string.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/escape_string.hpp new file mode 100644 index 0000000000..ffe59cd423 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/escape_string.hpp @@ -0,0 +1,111 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ESCAPE_STRING_HPP_INCLUDED +#define TORRENT_ESCAPE_STRING_HPP_INCLUDED + +#include +#include +#include +#include "libtorrent/config.hpp" +#include "libtorrent/size_type.hpp" +#include "libtorrent/error_code.hpp" + +namespace libtorrent +{ + TORRENT_EXTRA_EXPORT boost::array::digits10> to_string(size_type n); + + TORRENT_EXTRA_EXPORT std::string unescape_string(std::string const& s, error_code& ec); + // replaces all disallowed URL characters by their %-encoding + TORRENT_EXTRA_EXPORT std::string escape_string(const char* str, int len); + // same as escape_string but does not encode '/' + TORRENT_EXTRA_EXPORT std::string escape_path(const char* str, int len); + // if the url does not appear to be encoded, and it contains illegal url characters + // it will be encoded + TORRENT_EXTRA_EXPORT std::string maybe_url_encode(std::string const& url); + + // returns true if the given string (not null terminated) contains + // characters that would need to be escaped if used in a URL + TORRENT_EXTRA_EXPORT bool need_encoding(char const* str, int len); + + // encodes a string using the base64 scheme + TORRENT_EXTRA_EXPORT std::string base64encode(std::string const& s); + // encodes a string using the base32 scheme + TORRENT_EXTRA_EXPORT std::string base32encode(std::string const& s); + TORRENT_EXTRA_EXPORT std::string base32decode(std::string const& s); + + TORRENT_EXTRA_EXPORT std::string url_has_argument( + std::string const& url, std::string argument, std::string::size_type* out_pos = 0); + + // replaces \ with / + TORRENT_EXTRA_EXPORT void convert_path_to_posix(std::string& path); + + TORRENT_EXTRA_EXPORT std::string read_until(char const*& str, char delim, char const* end); + TORRENT_EXTRA_EXPORT int hex_to_int(char in); + + TORRENT_EXTRA_EXPORT bool is_hex(char const *in, int len); + + // converts (binary) the string ``s`` to hexadecimal representation and + // returns it. + TORRENT_EXPORT std::string to_hex(std::string const& s); + + // converts the binary buffer [``in``, ``in`` + len) to hexadecimal + // and prints it to the buffer ``out``. The caller is responsible for + // making sure the buffer pointed to by ``out`` is large enough, + // i.e. has at least len * 2 bytes of space. + TORRENT_EXPORT void to_hex(char const *in, int len, char* out); + + // converts the buffer [``in``, ``in`` + len) from hexadecimal to + // binary. The binary output is written to the buffer pointed to + // by ``out``. The caller is responsible for making sure the buffer + // at ``out`` has enough space for the result to be written to, i.e. + // (len + 1) / 2 bytes. + TORRENT_EXPORT bool from_hex(char const *in, int len, char* out); + +#if defined TORRENT_WINDOWS && TORRENT_USE_WSTRING + TORRENT_EXTRA_EXPORT std::wstring convert_to_wstring(std::string const& s); + TORRENT_EXTRA_EXPORT std::string convert_from_wstring(std::wstring const& s); +#endif + +#if TORRENT_USE_ICONV || TORRENT_USE_LOCALE || defined TORRENT_WINDOWS + TORRENT_EXTRA_EXPORT std::string convert_to_native(std::string const& s); + TORRENT_EXTRA_EXPORT std::string convert_from_native(std::string const& s); +#else + // internal + inline std::string const& convert_to_native(std::string const& s) { return s; } + // internal + inline std::string const& convert_from_native(std::string const& s) { return s; } +#endif +} + +#endif // TORRENT_ESCAPE_STRING_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/export.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/export.hpp new file mode 100644 index 0000000000..cef3bbef39 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/export.hpp @@ -0,0 +1,89 @@ +/* + +Copyright (c) 2005-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_EXPORT_HPP_INCLUDED +#define TORRENT_EXPORT_HPP_INCLUDED + +#if !defined(BOOST_COMPILER_CONFIG) && !defined(BOOST_NO_COMPILER_CONFIG) +# include +#endif +#ifdef BOOST_COMPILER_CONFIG +# include BOOST_COMPILER_CONFIG +#endif + +#if !defined(BOOST_PLATFORM_CONFIG) && !defined(BOOST_NO_PLATFORM_CONFIG) +# include +#endif +#ifdef BOOST_PLATFORM_CONFIG +# include BOOST_PLATFORM_CONFIG +#endif + +// backwards compatibility with older versions of boost +#if !defined BOOST_SYMBOL_EXPORT && !defined BOOST_SYMBOL_IMPORT +# if defined _MSC_VER || defined __MINGW32__ +# define BOOST_SYMBOL_EXPORT __declspec(dllexport) +# define BOOST_SYMBOL_IMPORT __declspec(dllimport) +# elif __GNU__ >= 4 +# define BOOST_SYMBOL_EXPORT __attribute__((visibility("default"))) +# define BOOST_SYMBOL_IMPORT __attribute__((visibility("default"))) +# else +# define BOOST_SYMBOL_EXPORT +# define BOOST_SYMBOL_IMPORT +# endif +#endif + +#if defined TORRENT_BUILDING_SHARED +# define TORRENT_EXPORT BOOST_SYMBOL_EXPORT +#elif defined TORRENT_LINKING_SHARED +# define TORRENT_EXPORT BOOST_SYMBOL_IMPORT +#endif + +// when this is specified, export a bunch of extra +// symbols, mostly for the unit tests to reach +#if TORRENT_EXPORT_EXTRA +# if defined TORRENT_BUILDING_SHARED +# define TORRENT_EXTRA_EXPORT BOOST_SYMBOL_EXPORT +# elif defined TORRENT_LINKING_SHARED +# define TORRENT_EXTRA_EXPORT BOOST_SYMBOL_IMPORT +# endif +#endif + +#ifndef TORRENT_EXPORT +# define TORRENT_EXPORT +#endif + +#ifndef TORRENT_EXTRA_EXPORT +# define TORRENT_EXTRA_EXPORT +#endif + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/extensions.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions.hpp new file mode 100644 index 0000000000..789cfb5882 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions.hpp @@ -0,0 +1,440 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_EXTENSIONS_HPP_INCLUDED +#define TORRENT_EXTENSIONS_HPP_INCLUDED + +// OVERVIEW +// +// libtorrent has a plugin interface for implementing extensions to the protocol. +// These can be general extensions for transferring metadata or peer exchange +// extensions, or it could be used to provide a way to customize the protocol +// to fit a particular (closed) network. +// +// In short, the plugin interface makes it possible to: +// +// * register extension messages (sent in the extension handshake), see +// extensions_. +// * add data and parse data from the extension handshake. +// * send extension messages and standard bittorrent messages. +// * override or block the handling of standard bittorrent messages. +// * save and restore state via the session state +// * see all alerts that are posted +// +// .. _extensions: extension_protocol.html +// +// a word of caution +// ----------------- +// +// Writing your own plugin is a very easy way to introduce serious bugs such as +// dead locks and race conditions. Since a plugin has access to internal +// structures it is also quite easy to sabotage libtorrent's operation. +// +// All the callbacks in this interface are called with the main libtorrent thread +// mutex locked. And they are always called from the libtorrent network thread. In +// case portions of your plugin are called from other threads, typically the main +// thread, you cannot use any of the member functions on the internal structures +// in libtorrent, since those require the mutex to be locked. Futhermore, you would +// also need to have a mutex on your own shared data within the plugin, to make +// sure it is not accessed at the same time from the libtorrent thread (through a +// callback). See `boost thread's mutex`_. If you need to send out a message from +// another thread, it is advised to use an internal queue, and do the actual +// sending in ``tick()``. +// +// Since the plugin interface gives you easy access to internal structures, it +// is not supported as a stable API. Plugins should be considered spcific to a +// specific version of libtorrent. Although, in practice the internals mostly +// don't change that dramatically. +// +// .. _`boost thread's mutex`: http://www.boost.org/doc/html/mutex.html +// +// +// plugin-interface +// ================ +// +// The plugin interface consists of three base classes that the plugin may +// implement. These are called plugin, torrent_plugin and peer_plugin. +// They are found in the ```` header. +// +// These plugins are instantiated for each session, torrent and possibly each peer, +// respectively. +// +// For plugins that only need per torrent state, it is enough to only implement +// ``torrent_plugin`` and pass a constructor function or function object to +// ``session::add_extension()`` or ``torrent_handle::add_extension()`` (if the +// torrent has already been started and you want to hook in the extension at +// run-time). +// +// The signature of the function is:: +// +// boost::shared_ptr (*)(torrent*, void*); +// +// The first argument is the internal torrent object, the second argument +// is the userdata passed to ``session::add_torrent()`` or +// ``torrent_handle::add_extension()``. +// +// The function should return a ``boost::shared_ptr`` which +// may or may not be 0. If it is a null pointer, the extension is simply ignored +// for this torrent. If it is a valid pointer (to a class inheriting +// ``torrent_plugin``), it will be associated with this torrent and callbacks +// will be made on torrent events. +// +// For more elaborate plugins which require session wide state, you would +// implement ``plugin``, construct an object (in a ``boost::shared_ptr``) and pass +// it in to ``session::add_extension()``. +// +// custom alerts +// ============= +// +// Since plugins are running within internal libtorrent threads, one convenient +// way to communicate with the client is to post custom alerts. +// +// The expected interface of any alert, apart from deriving from the alert +// base class, looks like this: +// +// .. parsed-literal:: +// +// const static int alert_type = **; +// virtual int type() const { return alert_type; } +// +// virtual std::string message() const; +// +// virtual std::auto_ptr clone() const +// { return std::auto_ptr(new name(\*this)); } +// +// const static int static_category = **; +// virtual int category() const { return static_category; } +// +// virtual char const* what() const { return **; } +// +// The ``alert_type`` is used for the type-checking in ``alert_cast``. It must +// not collide with any other alert. The built-in alerts in libtorrent will +// not use alert type IDs greater than ``user_alert_id``. When defining your +// own alert, make sure it's greater than this constant. +// +// ``type()`` is the run-time equivalence of the ``alert_type``. +// +// The ``message()`` virtual function is expected to construct a useful +// string representation of the alert and the event or data it represents. +// Something convenient to put in a log file for instance. +// +// ``clone()`` is used internally to copy alerts. The suggested implementation +// of simply allocating a new instance as a copy of ``*this`` is all that's +// expected. +// +// The static category is required for checking wether or not the category +// for a specific alert is enabled or not, without instantiating the alert. +// The ``category`` virtual function is the run-time equivalence. +// +// The ``what()`` virtual function may simply be a string literal of the class +// name of your alert. +// +// For more information, see the `alert section`_. +// +// .. _`alert section`: reference-Alerts.html + + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include "libtorrent/config.hpp" +#include "libtorrent/buffer.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/policy.hpp" // for policy::peer + +namespace libtorrent +{ + namespace aux { struct session_impl; } + + struct peer_plugin; + class bt_peer_connection; + struct peer_request; + class peer_connection; + class entry; + struct lazy_entry; + struct disk_buffer_holder; + struct bitfield; + class alert; + struct torrent_plugin; + class torrent; + struct torrent_peer; + + // this is the base class for a session plugin. One primary feature + // is that it is notified of all torrents that are added to the session, + // and can add its own torrent_plugins. + struct TORRENT_EXPORT plugin + { + // hidden + virtual ~plugin() {} + + // this is called by the session every time a new torrent is added. + // The ``torrent*`` points to the internal torrent object created + // for the new torrent. The ``void*`` is the userdata pointer as + // passed in via add_torrent_params. + // + // If the plugin returns a torrent_plugin instance, it will be added + // to the new torrent. Otherwise, return an empty shared_ptr to a + // torrent_plugin (the default). + virtual boost::shared_ptr new_torrent(torrent*, void*) + { return boost::shared_ptr(); } + + // called when plugin is added to a session + virtual void added(aux::session_impl*) {} + + // called when an alert is posted + // alerts that are filtered are not + // posted + virtual void on_alert(alert const*) {} + + // called once per second + virtual void on_tick() {} + + // called when choosing peers to optimisticly unchoke + // peer's will be unchoked in the order they appear in the given + // vector which is initiallity sorted by when they were last + // optimistically unchoked. + // if the plugin returns true then the ordering provided will be + // used and no other plugin will be allowed to change it. + virtual bool on_optimistic_unchoke(std::vector& /* peers */) + { return false; } + + // called when saving settings state + virtual void save_state(entry&) const {} + + // called when loading settings state + virtual void load_state(lazy_entry const&) {} + }; + + // Torrent plugins are associated with a single torrent and have a number + // of functions called at certain events. Many of its functions have the + // ability to change or override the default libtorrent behavior. + struct TORRENT_EXPORT torrent_plugin + { + // hidden + virtual ~torrent_plugin() {} + + // This function is called each time a new peer is connected to the torrent. You + // may choose to ignore this by just returning a default constructed + // ``shared_ptr`` (in which case you don't need to override this member + // function). + // + // If you need an extension to the peer connection (which most plugins do) you + // are supposed to return an instance of your peer_plugin class. Which in + // turn will have its hook functions called on event specific to that peer. + // + // The ``peer_connection`` will be valid as long as the ``shared_ptr`` is being + // held by the torrent object. So, it is generally a good idea to not keep a + // ``shared_ptr`` to your own peer_plugin. If you want to keep references to it, + // use ``weak_ptr``. + // + // If this function throws an exception, the connection will be closed. + virtual boost::shared_ptr new_connection(peer_connection*) + { return boost::shared_ptr(); } + + // These hooks are called when a piece passes the hash check or fails the hash + // check, respectively. The ``index`` is the piece index that was downloaded. + // It is possible to access the list of peers that participated in sending the + // piece through the ``torrent`` and the ``piece_picker``. + virtual void on_piece_pass(int /*index*/) {} + virtual void on_piece_failed(int /*index*/) {} + + // This hook is called approximately once per second. It is a way of making it + // easy for plugins to do timed events, for sending messages or whatever. + virtual void tick() {} + + // These hooks are called when the torrent is paused and unpaused respectively. + // The return value indicates if the event was handled. A return value of + // ``true`` indicates that it was handled, and no other plugin after this one + // will have this hook function called, and the standard handler will also not be + // invoked. So, returning true effectively overrides the standard behavior of + // pause or unpause. + // + // Note that if you call ``pause()`` or ``resume()`` on the torrent from your + // handler it will recurse back into your handler, so in order to invoke the + // standard handler, you have to keep your own state on whether you want standard + // behavior or overridden behavior. + virtual bool on_pause() { return false; } + virtual bool on_resume() { return false; } + + // This function is called when the initial files of the torrent have been + // checked. If there are no files to check, this function is called immediately. + // + // i.e. This function is always called when the torrent is in a state where it + // can start downloading. + virtual void on_files_checked() {} + + // called when the torrent changes state + // the state is one of torrent_status::state_t + // enum members + virtual void on_state(int /*s*/) {} + + enum flags_t { + // this is the first time we see this peer + first_time = 1, + // this peer was not added because it was + // filtered by the IP filter + filtered = 2 + }; + + // called every time a new peer is added to the peer list. + // This is before the peer is connected to. For ``flags``, see + // torrent_plugin::flags_t. The ``source`` argument refers to + // the source where we learned about this peer from. It's a + // bitmask, because many sources may have told us about the same + // peer. For peer source flags, see peer_info::peer_source_flags. + virtual void on_add_peer(tcp::endpoint const&, + int /*src*/, int /*flags*/) {} + }; + + // peer plugins are associated with a specific peer. A peer could be + // both a regular bittorrent peer (``bt_peer_connection``) or one of the + // web seed connections (``web_peer_connection`` or ``http_seed_connection``). + // In order to only attach to certain peers, make your + // torrent_plugin::new_connection only return a plugin for certain peer + // connection types + struct TORRENT_EXPORT peer_plugin + { + // hidden + virtual ~peer_plugin() {} + + // This function is expected to return the name of + // the plugin. + virtual char const* type() const { return ""; } + + // can add entries to the extension handshake + // this is not called for web seeds + virtual void add_handshake(entry&) {} + + // called when the peer is being disconnected. + virtual void on_disconnect(error_code const& /*ec*/) {} + + // called when the peer is successfully connected. Note that + // incoming connections will have been connected by the time + // the peer plugin is attached to it, and won't have this hook + // called. + virtual void on_connected() {} + + // throwing an exception from any of the handlers (except add_handshake) + // closes the connection + + // this is called when the initial BT handshake is received. Returning false + // means that the other end doesn't support this extension and will remove + // it from the list of plugins. + // this is not called for web seeds + virtual bool on_handshake(char const* /*reserved_bits*/) { return true; } + + // called when the extension handshake from the other end is received + // if this returns false, it means that this extension isn't + // supported by this peer. It will result in this peer_plugin + // being removed from the peer_connection and destructed. + // this is not called for web seeds + virtual bool on_extension_handshake(lazy_entry const&) { return true; } + + // returning true from any of the message handlers + // indicates that the plugin has handeled the message. + // it will break the plugin chain traversing and not let + // anyone else handle the message, including the default + // handler. + virtual bool on_choke() { return false; } + virtual bool on_unchoke() { return false; } + virtual bool on_interested() { return false; } + virtual bool on_not_interested() { return false; } + virtual bool on_have(int /*index*/) { return false; } + virtual bool on_dont_have(int /*index*/) { return false; } + virtual bool on_bitfield(bitfield const& /*bitfield*/) { return false; } + virtual bool on_have_all() { return false; } + virtual bool on_have_none() { return false; } + virtual bool on_allowed_fast(int /*index*/) { return false; } + virtual bool on_request(peer_request const&) { return false; } + virtual bool on_piece(peer_request const& /*piece*/ + , disk_buffer_holder& /*data*/) { return false; } + virtual bool on_cancel(peer_request const&) { return false; } + virtual bool on_reject(peer_request const&) { return false; } + virtual bool on_suggest(int /*index*/) { return false; } + + // called after a choke message has been sent to the peer + virtual void sent_unchoke() {} + + // called when libtorrent think this peer should be disconnected. + // if the plugin returns false, the peer will not be disconnected. + virtual bool can_disconnect(error_code const& /*ec*/) { return true; } + + // called when an extended message is received. If returning true, + // the message is not processed by any other plugin and if false + // is returned the next plugin in the chain will receive it to + // be able to handle it. This is not called for web seeds. + // thus function may be called more than once per incoming message, but + // only the last of the calls will the ``body`` size equal the ``length``. + // i.e. Every time another fragment of the message is received, this + // function will be called, until finally the whole message has been + // received. The purpose of this is to allow early disconnects for invalid + // messages and for reporting progress of receiving large messages. + virtual bool on_extended(int /*length*/, int /*msg*/, + buffer::const_interval /*body*/) + { return false; } + + // this is not called for web seeds + virtual bool on_unknown_message(int /*length*/, int /*msg*/, + buffer::const_interval /*body*/) + { return false; } + + // called when a piece that this peer participated in either + // fails or passes the hash_check + virtual void on_piece_pass(int /*index*/) {} + virtual void on_piece_failed(int /*index*/) {} + + // called aproximately once every second + virtual void tick() {} + + // called each time a request message is to be sent. If true + // is returned, the original request message won't be sent and + // no other plugin will have this function called. + virtual bool write_request(peer_request const&) { return false; } + }; + +} + +#endif + +#endif // TORRENT_EXTENSIONS_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/logger.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/logger.hpp new file mode 100644 index 0000000000..5e5e6cb3a2 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/logger.hpp @@ -0,0 +1,60 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_LOGGER_HPP_INCLUDED +#define TORRENT_LOGGER_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" + +#if TORRENT_USE_IOSTREAM + +namespace libtorrent +{ + struct torrent_plugin; + class torrent; + boost::shared_ptr create_logger_plugin(torrent*); +} + +#endif + +#endif // TORRENT_LOGGER_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/lt_trackers.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/lt_trackers.hpp new file mode 100644 index 0000000000..8587ee0576 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/lt_trackers.hpp @@ -0,0 +1,63 @@ +/* + +Copyright (c) 2008, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_LT_TRACKERS_HPP_INCLUDED +#define TORRENT_LT_TRACKERS_HPP_INCLUDED + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include "libtorrent/config.hpp" + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace libtorrent +{ + struct torrent_plugin; + class torrent; + + // constructor function for the trackers exchange extension. This can + // either be passed in the add_torrent_params::extensions field, or + // via torrent_handle::add_extension(). + boost::shared_ptr TORRENT_EXPORT create_lt_trackers_plugin(torrent*, void*); +} + +#endif // TORRENT_DISABLE_EXTENSIONS + +#endif // TORRENT_LT_TRACKERS_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/metadata_transfer.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/metadata_transfer.hpp new file mode 100644 index 0000000000..84f7aa7b7b --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/metadata_transfer.hpp @@ -0,0 +1,69 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_METADATA_TRANSFER_HPP_INCLUDED +#define TORRENT_METADATA_TRANSFER_HPP_INCLUDED + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include "libtorrent/config.hpp" + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace libtorrent +{ + struct torrent_plugin; + class torrent; + +#ifndef TORRENT_NO_DEPRECATE + // constructor function for the metadata transfer extension. This + // extension has been superceded by the ut_metadata extension and + // is deprecated. It can be either be passed in the + // add_torrent_params::extensions field, or + // via torrent_handle::add_extension(). + TORRENT_DEPRECATED_PREFIX + TORRENT_EXPORT boost::shared_ptr + create_metadata_plugin(torrent*, void*) TORRENT_DEPRECATED; +#endif +} + +#endif // TORRENT_DISABLE_EXTENSIONS + +#endif // TORRENT_METADATA_TRANSFER_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/smart_ban.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/smart_ban.hpp new file mode 100644 index 0000000000..ba0ccb2137 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/smart_ban.hpp @@ -0,0 +1,66 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SMART_BAN_HPP_INCLUDED +#define TORRENT_SMART_BAN_HPP_INCLUDED + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include "libtorrent/config.hpp" + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace libtorrent +{ + struct torrent_plugin; + class torrent; + + // constructor function for the smart ban extension. The extension keeps + // track of the data peers have sent us for failing pieces and once the + // piece completes and passes the hash check bans the peers that turned + // out to have sent corrupt data. + // This function can either be passed in the add_torrent_params::extensions + // field, or via torrent_handle::add_extension(). + TORRENT_EXPORT boost::shared_ptr create_smart_ban_plugin(torrent*, void*); +} + +#endif // TORRENT_DISABLE_EXTENSIONS + +#endif // TORRENT_SMART_BAN_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/ut_metadata.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/ut_metadata.hpp new file mode 100644 index 0000000000..4daab9b6ad --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/ut_metadata.hpp @@ -0,0 +1,68 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UT_METADATA_HPP_INCLUDED +#define TORRENT_UT_METADATA_HPP_INCLUDED + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include "libtorrent/config.hpp" + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace libtorrent +{ + struct torrent_plugin; + class torrent; + + // constructor function for the ut_metadata extension. The ut_metadata + // extension allows peers to request the .torrent file (or more + // specifically the 'info'-dictionary of the .torrent file) from each + // other. This is the main building block in making magnet links work. + // This extension is enabled by default unless explicitly disabled in + // the session constructor. + // + // This can either be passed in the add_torrent_params::extensions field, or + // via torrent_handle::add_extension(). + TORRENT_EXPORT boost::shared_ptr create_ut_metadata_plugin(torrent*, void*); +} + +#endif // TORRENT_DISABLE_EXTENSIONS +#endif // TORRENT_UT_METADATA_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/ut_pex.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/ut_pex.hpp new file mode 100644 index 0000000000..fc91ef7f22 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/ut_pex.hpp @@ -0,0 +1,67 @@ +/* + +Copyright (c) 2006, MassaRoddel +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UT_PEX_EXTENSION_HPP_INCLUDED +#define TORRENT_UT_PEX_EXTENSION_HPP_INCLUDED + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include "libtorrent/config.hpp" + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace libtorrent +{ + struct torrent_plugin; + class torrent; + + // constructor function for the ut_pex extension. The ut_pex + // extension allows peers to gossip about their connections, allowing + // the swarm stay well connected and peers aware of more peers in the + // swarm. This extension is enabled by default unless explicitly disabled in + // the session constructor. + // + // This can either be passed in the add_torrent_params::extensions field, or + // via torrent_handle::add_extension(). + TORRENT_EXPORT boost::shared_ptr create_ut_pex_plugin(torrent*, void*); +} + +#endif // TORRENT_DISABLE_EXTENSIONS + +#endif // TORRENT_UT_PEX_EXTENSION_HPP_INCLUDED diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/file.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/file.hpp new file mode 100644 index 0000000000..ca7b97e569 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/file.hpp @@ -0,0 +1,333 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_FILE_HPP_INCLUDED +#define TORRENT_FILE_HPP_INCLUDED + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/error_code.hpp" +#include "libtorrent/size_type.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/intrusive_ptr_base.hpp" + +#ifdef TORRENT_WINDOWS +// windows part +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#include +#else +// posix part +#define _FILE_OFFSET_BITS 64 + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 600 +#endif + +#include +#include +#include +#include +#include // for DIR + +#undef _FILE_OFFSET_BITS + +#endif + +namespace libtorrent +{ + struct file_status + { + size_type file_size; + boost::uint64_t atime; + boost::uint64_t mtime; + boost::uint64_t ctime; + enum { +#if defined TORRENT_WINDOWS + fifo = 0x1000, // named pipe (fifo) + character_special = 0x2000, // character special + directory = 0x4000, // directory + regular_file = 0x8000 // regular +#else + fifo = 0010000, // named pipe (fifo) + character_special = 0020000, // character special + directory = 0040000, // directory + block_special = 0060000, // block special + regular_file = 0100000, // regular + link = 0120000, // symbolic link + socket = 0140000 // socket +#endif + } modes_t; + int mode; + }; + + // internal flags for stat_file + enum { dont_follow_links = 1 }; + TORRENT_EXTRA_EXPORT void stat_file(std::string f, file_status* s + , error_code& ec, int flags = 0); + TORRENT_EXTRA_EXPORT void rename(std::string const& f + , std::string const& newf, error_code& ec); + TORRENT_EXTRA_EXPORT void create_directories(std::string const& f + , error_code& ec); + TORRENT_EXTRA_EXPORT void create_directory(std::string const& f + , error_code& ec); + TORRENT_EXTRA_EXPORT void remove_all(std::string const& f + , error_code& ec); + TORRENT_EXTRA_EXPORT void remove(std::string const& f, error_code& ec); + TORRENT_EXTRA_EXPORT bool exists(std::string const& f); + TORRENT_EXTRA_EXPORT size_type file_size(std::string const& f); + TORRENT_EXTRA_EXPORT bool is_directory(std::string const& f + , error_code& ec); + TORRENT_EXTRA_EXPORT void recursive_copy(std::string const& old_path + , std::string const& new_path, error_code& ec); + TORRENT_EXTRA_EXPORT void copy_file(std::string const& f + , std::string const& newf, error_code& ec); + + TORRENT_EXTRA_EXPORT std::string split_path(std::string const& f); + TORRENT_EXTRA_EXPORT char const* next_path_element(char const* p); + TORRENT_EXTRA_EXPORT std::string extension(std::string const& f); + TORRENT_EXTRA_EXPORT std::string remove_extension(std::string const& f); + TORRENT_EXTRA_EXPORT void replace_extension(std::string& f, std::string const& ext); + TORRENT_EXTRA_EXPORT bool is_root_path(std::string const& f); + + + // internal used by create_torrent.hpp + TORRENT_EXPORT std::string parent_path(std::string const& f); + TORRENT_EXTRA_EXPORT bool has_parent_path(std::string const& f); + // internal used by create_torrent.hpp + TORRENT_EXPORT std::string filename(std::string const& f); + TORRENT_EXTRA_EXPORT std::string combine_path(std::string const& lhs + , std::string const& rhs); + // internal used by create_torrent.hpp + TORRENT_EXPORT std::string complete(std::string const& f); + TORRENT_EXTRA_EXPORT bool is_complete(std::string const& f); + TORRENT_EXTRA_EXPORT std::string current_working_directory(); +#if TORRENT_USE_UNC_PATHS + TORRENT_EXTRA_EXPORT std::string canonicalize_path(std::string const& f); +#endif + + class TORRENT_EXTRA_EXPORT directory : public boost::noncopyable + { + public: + directory(std::string const& path, error_code& ec); + ~directory(); + void next(error_code& ec); + std::string file() const; + boost::uint64_t inode() const; + bool done() const { return m_done; } + private: +#ifdef TORRENT_WINDOWS + HANDLE m_handle; + int m_inode; +#if TORRENT_USE_WSTRING + WIN32_FIND_DATAW m_fd; +#else + WIN32_FIND_DATAA m_fd; +#endif +#else + DIR* m_handle; + // the dirent struct contains a zero-sized + // array at the end, it will end up referring + // to the m_name field + struct dirent m_dirent; + char m_name[TORRENT_MAX_PATH + 1]; // +1 to make room for null +#endif + bool m_done; + }; + + struct TORRENT_EXTRA_EXPORT file: boost::noncopyable, intrusive_ptr_base + { + // the open mode for files. Used for the file constructor or + // file::open(). + enum open_mode_t + { + // open the file for reading only + read_only = 0, + + // open the file for writing only + write_only = 1, + + // open the file for reading and writing + read_write = 2, + + // the mask for the bits determining read or write mode + rw_mask = read_only | write_only | read_write, + + // indicate that the file should be opened in + // *direct io* mode, i.e. bypassing the operating + // system's disk cache, or as much as possible of it + // depending on the system. + // when a file is opened with no_buffer, + // file offsets have to be aligned to + // pos_alignment() and buffer addresses + // to buf_alignment() and read/write sizes + // to size_alignment() + no_buffer = 4, + + // open the file in sparse mode (if supported by the + // filesystem). + sparse = 8, + + // don't update the access timestamps on the file (if + // supported by the operating system and filesystem). + // this generally improves disk performance. + no_atime = 16, + + // open the file for random acces. This disables read-ahead + // logic + random_access = 32, + + // prevent the file from being opened by another process + // while it's still being held open by this handle + lock_file = 64, + + // when creating a file, set the hidden attribute (windows only) + attribute_hidden = 0x1000, + + // when creating a file, set the executable attribute + attribute_executable = 0x2000, + + // the mask of all attribute bits + attribute_mask = attribute_hidden | attribute_executable + }; + +#ifdef TORRENT_WINDOWS + struct iovec_t + { + void* iov_base; + size_t iov_len; + }; +#else + typedef iovec iovec_t; +#endif + + // use a typedef for the type of iovec_t::iov_base + // since it may differ +#ifdef TORRENT_SOLARIS + typedef char* iovec_base_t; +#else + typedef void* iovec_base_t; +#endif + + file(); + file(std::string const& p, int m, error_code& ec); + ~file(); + + bool open(std::string const& p, int m, error_code& ec); + bool is_open() const; + void close(); + bool set_size(size_type size, error_code& ec); + + int open_mode() const { return m_open_mode; } + + // when opened in unbuffered mode, this is the + // required alignment of file_offsets. i.e. + // any (file_offset & (pos_alignment()-1)) == 0 + // is a precondition to read and write operations + int pos_alignment() const; + + // when opened in unbuffered mode, this is the + // required alignment of buffer addresses + int buf_alignment() const; + + // read/write buffer sizes needs to be aligned to + // this when in unbuffered mode + int size_alignment() const; + + size_type writev(size_type file_offset, iovec_t const* bufs, int num_bufs, error_code& ec); + size_type readv(size_type file_offset, iovec_t const* bufs, int num_bufs, error_code& ec); + void hint_read(size_type file_offset, int len); + + size_type get_size(error_code& ec) const; + + // return the offset of the first byte that + // belongs to a data-region + size_type sparse_end(size_type start) const; + + size_type phys_offset(size_type offset); + +#ifdef TORRENT_WINDOWS + HANDLE native_handle() const { return m_file_handle; } +#else + int native_handle() const { return m_fd; } +#endif + + private: + +#ifdef TORRENT_WINDOWS + HANDLE m_file_handle; +#if TORRENT_USE_WSTRING + std::wstring m_path; +#else + std::string m_path; +#endif // TORRENT_USE_WSTRING +#else // TORRENT_WINDOWS + int m_fd; +#endif // TORRENT_WINDOWS + +#if defined TORRENT_WINDOWS || defined TORRENT_LINUX || defined TORRENT_DEBUG + static void init_file(); + static int m_page_size; +#endif + int m_open_mode; +#if defined TORRENT_WINDOWS || defined TORRENT_LINUX + mutable int m_sector_size; +#endif +#if defined TORRENT_WINDOWS + mutable int m_cluster_size; + + static bool has_manage_volume_privs; +#endif + }; + +} + +#endif // TORRENT_FILE_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/file_pool.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/file_pool.hpp new file mode 100644 index 0000000000..4062a1b6a0 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/file_pool.hpp @@ -0,0 +1,123 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_FILE_POOL_HPP +#define TORRENT_FILE_POOL_HPP + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include "libtorrent/file.hpp" +#include "libtorrent/ptime.hpp" +#include "libtorrent/thread.hpp" +#include "libtorrent/file_storage.hpp" + +namespace libtorrent +{ + // this is an internal cache of open file handles. It's primarily used by + // storage_interface implementations. It provides semi weak guarantees of + // not opening more file handles than specified. Given multiple threads, + // each with the ability to lock a file handle (via smart pointer), there + // may be windows where more file handles are open. + struct TORRENT_EXPORT file_pool : boost::noncopyable + { + // ``size`` specifies the number of allowed files handles + // to hold open at any given time. + file_pool(int size = 40); + ~file_pool(); + + // return an open file handle to file at ``file_index`` in the + // file_storage ``fs`` opened at save path ``p``. ``m`` is the + // file open mode (see file::open_mode_t). + boost::intrusive_ptr open_file(void* st, std::string const& p + , int file_index, file_storage const& fs, int m, error_code& ec); + + // release all files belonging to the specified storage_interface (``st``) + // the overload that takes ``file_index`` releases only the file with + // that index in storage ``st``. + void release(void* st); + void release(void* st, int file_index); + + // update the allowed number of open file handles to ``size``. + void resize(int size); + + // returns the current limit of number of allowed open file handles held + // by the file_pool. + int size_limit() const { return m_size; } + + // internal + void set_low_prio_io(bool b) { m_low_prio_io = b; } + + private: + + void remove_oldest(); + + int m_size; + bool m_low_prio_io; + + struct lru_file_entry + { + lru_file_entry(): key(0), last_use(time_now()), mode(0) {} + mutable boost::intrusive_ptr file_ptr; + void* key; + ptime last_use; + int mode; + }; + + // maps storage pointer, file index pairs to the + // lru entry for the file + typedef std::map, lru_file_entry> file_set; + + file_set m_files; + mutex m_mutex; + +#if TORRENT_CLOSE_MAY_BLOCK + void closer_thread_fun(); + mutex m_closer_mutex; + std::vector > m_queued_for_close; + bool m_stop_thread; + + // used to close files + thread m_closer_thread; +#endif + }; +} + +#endif diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/file_storage.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/file_storage.hpp new file mode 100644 index 0000000000..0c26220e76 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/file_storage.hpp @@ -0,0 +1,586 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_FILE_STORAGE_HPP_INCLUDED +#define TORRENT_FILE_STORAGE_HPP_INCLUDED + +#include +#include +#include + +#include "libtorrent/size_type.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/peer_id.hpp" + +namespace libtorrent +{ + struct file; + + // information about a file in a file_storage + struct TORRENT_EXPORT file_entry + { + // hidden + file_entry(); + // hidden + ~file_entry(); + + // the full path of this file. The paths are unicode strings + // encoded in UTF-8. + std::string path; + + // the path which this is a symlink to, or empty if this is + // not a symlink. This field is only used if the ``symlink_attribute`` is set. + std::string symlink_path; + + // the offset of this file inside the torrent + size_type offset; + + // the size of the file (in bytes) and ``offset`` is the byte offset + // of the file within the torrent. i.e. the sum of all the sizes of the files + // before it in the list. + size_type size; + + // the offset in the file where the storage should start. The normal + // case is to have this set to 0, so that the storage starts saving data at the start + // if the file. In cases where multiple files are mapped into the same file though, + // the ``file_base`` should be set to an offset so that the different regions do + // not overlap. This is used when mapping "unselected" files into a so-called part + // file. + size_type file_base; + + // the modification time of this file specified in posix time. + std::time_t mtime; + + // a sha-1 hash of the content of the file, or zeroes, if no + // file hash was present in the torrent file. It can be used to potentially + // find alternative sources for the file. + sha1_hash filehash; + + // set to true for files that are not part of the data of the torrent. + // They are just there to make sure the next file is aligned to a particular byte offset + // or piece boundry. These files should typically be hidden from an end user. They are + // not written to disk. + bool pad_file:1; + + // true if the file was marked as hidden (on windows). + bool hidden_attribute:1; + + // true if the file was marked as executable (posix) + bool executable_attribute:1; + + // true if the file was a symlink. If this is the case + // the ``symlink_index`` refers to a string which specifies the original location + // where the data for this file was found. + bool symlink_attribute:1; + }; + + // only export this type if deprecated functions are enabled +#ifdef TORRENT_NO_DEPRECATE +#define TORRENT_DEPRECATED_EXPORT +#else +#define TORRENT_DEPRECATED_EXPORT TORRENT_EXPORT +#endif + + // internal + struct TORRENT_DEPRECATED_EXPORT internal_file_entry + { + friend class file_storage; +#ifdef TORRENT_DEBUG + // for torrent_info::invariant_check + friend class torrent_info; +#endif + + internal_file_entry() + : offset(0) + , symlink_index(not_a_symlink) + , no_root_dir(false) + , size(0) + , name_len(name_is_owned) + , pad_file(false) + , hidden_attribute(false) + , executable_attribute(false) + , symlink_attribute(false) + , name(NULL) + , path_index(-1) + {} + + internal_file_entry(file_entry const& e) + : offset(e.offset) + , symlink_index(not_a_symlink) + , no_root_dir(false) + , size(e.size) + , name_len(name_is_owned) + , pad_file(e.pad_file) + , hidden_attribute(e.hidden_attribute) + , executable_attribute(e.executable_attribute) + , symlink_attribute(e.symlink_attribute) + , name(NULL) + , path_index(-1) + { + set_name(e.path.c_str()); + } + + internal_file_entry(internal_file_entry const& fe); + internal_file_entry& operator=(internal_file_entry const& fe); + + ~internal_file_entry(); + + void set_name(char const* n, bool borrow_string = false, int string_len = 0); + std::string filename() const; + + enum { + name_is_owned = (1<<12)-1, + not_a_symlink = (1<<15)-1 + }; + + // the offset of this file inside the torrent + boost::uint64_t offset:48; + + // index into file_storage::m_symlinks or not_a_symlink + // if this is not a symlink + boost::uint64_t symlink_index:15; + + // if this is true, don't include m_name as part of the + // path to this file + boost::uint64_t no_root_dir:1; + + // the size of this file + boost::uint64_t size:48; + + // the number of characters in the name. If this is + // name_is_owned, name is null terminated and owned by this object + // (i.e. it should be freed in the destructor). If + // the len is not name_is_owned, the name pointer doesn not belong + // to this object, and it's not null terminated + boost::uint64_t name_len:12; + boost::uint64_t pad_file:1; + boost::uint64_t hidden_attribute:1; + boost::uint64_t executable_attribute:1; + boost::uint64_t symlink_attribute:1; + + // make it available for logging + private: + // This string is not necessarily null terminated! + // that's why it's private, to keep people away from it + char const* name; + public: + + // the index into file_storage::m_paths. To get + // the full path to this file, concatenate the path + // from that array with the 'name' field in + // this struct + // values for path_index include: + // -1 means no path (i.e. single file torrent) + // -2, it means the filename + // in this field contains the full, absolute path + // to the file + int path_index; + }; + + // represents a window of a file in a torrent. + // + // The ``file_index`` refers to the index of the file (in the torrent_info). + // To get the path and filename, use ``file_at()`` and give the ``file_index`` + // as argument. The ``offset`` is the byte offset in the file where the range + // starts, and ``size`` is the number of bytes this range is. The size + offset + // will never be greater than the file size. + struct TORRENT_EXPORT file_slice + { + // the index of the file + int file_index; + + // the offset from the start of the file, in bytes + size_type offset; + + // the size of the window, in bytes + size_type size; + }; + + // The ``file_storage`` class represents a file list and the piece + // size. Everything necessary to interpret a regular bittorrent storage + // file structure. + class TORRENT_EXPORT file_storage + { + friend class torrent_info; + public: + // hidden + file_storage(); + // hidden + ~file_storage(); + file_storage(file_storage const& f); + file_storage& operator=(file_storage const&); + + // returns true if the piece length has been initialized + // on the file_storage. This is typically taken as a proxy + // of whether the file_storage as a whole is initialized or + // not. + bool is_valid() const { return m_piece_length > 0; } + + // file attribute flags + enum flags_t + { + // the file is a pad file. It's required to contain zeroes + // at it will not be saved to disk. Its purpose is to make + // the following file start on a piece boundary. + pad_file = 1, + + // this file has the hidden attribute set. This is primarily + // a windows attribute + attribute_hidden = 2, + + // this file has the executable attribute set. + attribute_executable = 4, + + // this file is a symbilic link. It should have a link + // target string associated with it. + attribute_symlink = 8 + }; + + // allocates space for ``num_files`` in the internal file list. This can + // be used to avoid reallocating the internal file list when the number + // of files to be added is known up-front. + void reserve(int num_files); + + // Adds a file to the file storage. The ``flags`` argument sets + // attributes on the file. The file attributes is an extension and may + // not work in all bittorrent clients. + // + // For possible file attributes, see file_storage::flags_t. + // + // If more files than one are added, certain restrictions to their paths + // apply. In a multi-file file storage (torrent), all files must share + // the same root directory. + // + // That is, the first path element of all files must be the same. + // This shared path element is also set to the name of the torrent. It + // can be changed by calling ``set_name``. + // + // The ``filehash`` argument is an optional pointer to a sha-1 hash (20 + // bytes) of the file. The hash is not copied into the file_storage + // object, but the pointer is expected to point to memory that stays + // valid throughout the life time of the file_storage. + // + // Currently, the ``filehash`` from ``file_entry`` is not used. + void add_file(file_entry const& e, char const* filehash = 0); + void add_file(std::string const& p, size_type size, int flags = 0 + , std::time_t mtime = 0, std::string const& s_p = ""); + + // renames the file at ``index`` to ``new_filename``. Keep in mind + // that filenames are expected to be UTF-8 encoded. + void rename_file(int index, std::string const& new_filename); + + // this is a low-level function that sets the name of a file + // by making it reference a buffer that is not owned by the file_storage. + // it's an optimization used when loading .torrent files, to not + // duplicate names in memory. + void rename_file_borrow(int index, char const* new_filename, int len); + +#if TORRENT_USE_WSTRING + // all wstring APIs are deprecated since 0.16.11 + // instead, use the wchar -> utf8 conversion functions + // and pass in utf8 strings +#ifndef TORRENT_NO_DEPRECATE + TORRENT_DEPRECATED_PREFIX + void add_file(std::wstring const& p, size_type size, int flags = 0 + , std::time_t mtime = 0, std::string const& s_p = "") TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void rename_file(int index, std::wstring const& new_filename) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_name(std::wstring const& n) TORRENT_DEPRECATED; + + void rename_file_deprecated(int index, std::wstring const& new_filename); +#endif // TORRENT_NO_DEPRECATE +#endif // TORRENT_USE_WSTRING + + // returns a list of file_slice objects representing the portions of + // files the specified piece index, byte offset and size range overlaps. + // this is the inverse mapping of map_file(). + std::vector map_block(int piece, size_type offset + , int size) const; + + // returns a peer_request representing the piece index, byte offset + // and size the specified file range overlaps. This is the inverse + // mapping ove map_block(). Note that the ``peer_request`` return type + // is meant to hold bittorrent block requests, which may not be larger + // than 16 kiB. Mapping a range larger than that may return an overflown + // integer. + peer_request map_file(int file, size_type offset, int size) const; + +#ifndef TORRENT_NO_DEPRECATE + // all functions depending on internal_file_entry + // were deprecated in 1.0. Use the variants that take an + // index instead + typedef std::vector::const_iterator iterator; + typedef std::vector::const_reverse_iterator reverse_iterator; + + TORRENT_DEPRECATED_PREFIX + iterator file_at_offset(size_type offset) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + iterator begin() const TORRENT_DEPRECATED { return m_files.begin(); } + TORRENT_DEPRECATED_PREFIX + iterator end() const TORRENT_DEPRECATED { return m_files.end(); } + TORRENT_DEPRECATED_PREFIX + reverse_iterator rbegin() const TORRENT_DEPRECATED { return m_files.rbegin(); } + TORRENT_DEPRECATED_PREFIX + reverse_iterator rend() const TORRENT_DEPRECATED { return m_files.rend(); } + TORRENT_DEPRECATED_PREFIX + internal_file_entry const& internal_at(int index) const TORRENT_DEPRECATED + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < int(m_files.size())); + return m_files[index]; + } + TORRENT_DEPRECATED_PREFIX + file_entry at(iterator i) const TORRENT_DEPRECATED; + + iterator begin_deprecated() const { return m_files.begin(); } + iterator end_deprecated() const { return m_files.end(); } + reverse_iterator rbegin_deprecated() const { return m_files.rbegin(); } + reverse_iterator rend_deprecated() const { return m_files.rend(); } + iterator file_at_offset_deprecated(size_type offset) const; +#endif // TORRENT_NO_DEPRECATE + + // returns the number of files in the file_storage + int num_files() const + { return int(m_files.size()); } + + // returns a file_entry with information about the file + // at ``index``. Index must be in the range [0, ``num_files()`` ). + file_entry at(int index) const; + + // returns the total number of bytes all the files in this torrent spans + size_type total_size() const { return m_total_size; } + + // set and get the number of pieces in the torrent + void set_num_pieces(int n) { m_num_pieces = n; } + int num_pieces() const { TORRENT_ASSERT(m_piece_length > 0); return m_num_pieces; } + + // set and get the size of each piece in this torrent. This size is typically an even power + // of 2. It doesn't have to be though. It should be divisible by 16kiB however. + void set_piece_length(int l) { m_piece_length = l; } + int piece_length() const { TORRENT_ASSERT(m_piece_length > 0); return m_piece_length; } + + // returns the piece size of ``index``. This will be the same as piece_length(), except + // for the last piece, which may be shorter. + int piece_size(int index) const; + + // set and get the name of this torrent. For multi-file torrents, this is also + // the name of the root directory all the files are stored in. + void set_name(std::string const& n) { m_name = n; } + const std::string& name() const { return m_name; } + + // swap all content of *this* with *ti*. + void swap(file_storage& ti) + { + using std::swap; + swap(ti.m_files, m_files); + swap(ti.m_file_hashes, m_file_hashes); + swap(ti.m_symlinks, m_symlinks); + swap(ti.m_mtime, m_mtime); + swap(ti.m_file_base, m_file_base); + swap(ti.m_paths, m_paths); + swap(ti.m_name, m_name); + swap(ti.m_total_size, m_total_size); + swap(ti.m_num_pieces, m_num_pieces); + swap(ti.m_piece_length, m_piece_length); + } + + // if pad_file_limit >= 0, files larger than that limit will be padded, + // default is to not add any padding (-1). The alignment specifies the + // alignment files should be padded to. This defaults to the piece size + // (-1) but it may also make sense to set it to 16 kiB, or something + // divisible by 16 kiB. + // If pad_file_limit is 0, every file will be padded (except empty ones). + void optimize(int pad_file_limit = -1, int alignment = -1); + + // These functions are used to query attributes of files at + // a given index. + // + // The ``hash()`` is a sha-1 hash of the file, or 0 if none was + // provided in the torrent file. This can potentially be used to + // join a bittorrent network with other file sharing networks. + // + // The ``mtime()`` is the modification time is the posix + // time when a file was last modified when the torrent + // was created, or 0 if it was not included in the torrent file. + // + // ``file_path()`` returns the full path to a file. + // + // ``file_size()`` returns the size of a file. + // + // ``pad_file_at()`` returns true if the file at the given + // index is a pad-file. + // + // ``file_name()`` returns *just* the name of the file, whereas + // ``file_path()`` returns the path (inside the torrent file) with + // the filename appended. + // + // ``file_offset()`` returns the byte offset within the torrent file + // where this file starts. It can be used to map the file to a piece + // index (given the piece size). + sha1_hash hash(int index) const; + std::string const& symlink(int index) const; + time_t mtime(int index) const; + std::string file_path(int index, std::string const& save_path = "") const; + std::string file_name(int index) const; + size_type file_size(int index) const; + bool pad_file_at(int index) const; + size_type file_offset(int index) const; + + // flags indicating various attributes for files in + // a file_storage. + enum file_flags_t + { + // this file is a pad file. The creator of the + // torrent promises the file is entirely filled with + // zeroes and does not need to be downloaded. The + // purpose is just to align the next file to either + // a block or piece boundary. + flag_pad_file = 1, + + // this file is hiddent (sets the hidden attribute + // on windows) + flag_hidden = 2, + + // this file is executable (sets the executable bit + // on posix like systems) + flag_executable = 4, + + // this file is a symlink. The symlink target is + // specified in a separate field + flag_symlink = 8 + }; + + // returns a bitmask of flags from file_flags_t that apply + // to file at ``index``. + int file_flags(int index) const; + + // The file base of a file is the offset within the file on the filsystem + // where it starts to write. For the most part, this is always 0. It's + // possible to map several files (in the torrent) into a single file on + // the filesystem by making them all point to the same filename, but with + // different file bases, so that they don't overlap. + // torrent_info::remap_files() can be used to use a new file layout. + size_type file_base(int index) const; + void set_file_base(int index, size_type off); + + // returns the index of the file at the given offset in the torrent + int file_index_at_offset(size_type offset) const; + + // low-level function. returns a pointer to the internal storage for + // the filename. This string may not be null terinated! + // the ``file_name_len()`` function returns the length of the filename. + char const* file_name_ptr(int index) const; + int file_name_len(int index) const; + +#ifndef TORRENT_NO_DEPRECATE + // these were deprecated in 1.0. Use the versions that take an index instead + TORRENT_DEPRECATED_PREFIX + sha1_hash hash(internal_file_entry const& fe) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + std::string const& symlink(internal_file_entry const& fe) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + time_t mtime(internal_file_entry const& fe) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + int file_index(internal_file_entry const& fe) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + size_type file_base(internal_file_entry const& fe) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_file_base(internal_file_entry const& fe, size_type off) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + std::string file_path(internal_file_entry const& fe, std::string const& save_path = "") const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + std::string file_name(internal_file_entry const& fe) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + size_type file_size(internal_file_entry const& fe) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + bool pad_file_at(internal_file_entry const& fe) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + size_type file_offset(internal_file_entry const& fe) const TORRENT_DEPRECATED; +#endif + + private: + + void update_path_index(internal_file_entry& e); + void reorder_file(int index, int dst); + + // the list of files that this torrent consists of + std::vector m_files; + + // if there are sha1 hashes for each individual file + // there are as many entries in this array as the + // m_files array. Each entry in m_files has a corresponding + // hash pointer in this array. The reason to split it up + // in separate arrays is to save memory in case the torrent + // doesn't have file hashes + std::vector m_file_hashes; + + // for files that are symlinks, the symlink + // path_index in the internal_file_entry indexes + // this vector of strings + std::vector m_symlinks; + + // the modification times of each file. This vector + // is empty if no file have a modification time. + // each element corresponds to the file with the same + // index in m_files + std::vector m_mtime; + + // if any file has a non-zero file base (i.e. multiple + // files residing in the same physical file at different + // offsets) + std::vector m_file_base; + + // all unique paths files have. The internal_file_entry::path_index + // points into this array. The paths don't include the root directory + // name for multi-file torrents. The m_name field need to be + // prepended to these paths, and the filename of a specific file + // entry appended, to form full file paths + std::vector m_paths; + + // name of torrent. For multi-file torrents + // this is always the root directory + std::string m_name; + + // the sum of all filesizes + size_type m_total_size; + + // the number of pieces in the torrent + int m_num_pieces; + + int m_piece_length; + }; +} + +#endif // TORRENT_FILE_STORAGE_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/fingerprint.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/fingerprint.hpp new file mode 100644 index 0000000000..2527d5be8c --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/fingerprint.hpp @@ -0,0 +1,129 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_FINGERPRINT_HPP_INCLUDED +#define TORRENT_FINGERPRINT_HPP_INCLUDED + +#include +#include + +#include "libtorrent/config.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/assert.hpp" + +namespace libtorrent +{ + + // The fingerprint class represents information about a client and its version. It is used + // to encode this information into the client's peer id. + struct fingerprint + { + + // The constructor takes a ``char const*`` that should point to a string constant containing + // exactly two characters. These are the characters that should be unique for your client. Make + // sure not to clash with anybody else. Here are some taken id's: + // + // +----------+-----------------------+ + // | id chars | client | + // +==========+=======================+ + // | 'AZ' | Azureus | + // +----------+-----------------------+ + // | 'LT' | libtorrent (default) | + // +----------+-----------------------+ + // | 'BX' | BittorrentX | + // +----------+-----------------------+ + // | 'MT' | Moonlight Torrent | + // +----------+-----------------------+ + // | 'TS' | Torrent Storm | + // +----------+-----------------------+ + // | 'SS' | Swarm Scope | + // +----------+-----------------------+ + // | 'XT' | Xan Torrent | + // +----------+-----------------------+ + // + // There's an informal directory of client id's here_. + // + // .. _here: http://wiki.theory.org/BitTorrentSpecification#peer_id + // + // The ``major``, ``minor``, ``revision`` and ``tag`` parameters are used to identify the + // version of your client. + fingerprint(const char* id_string, int major, int minor, int revision, int tag) + : major_version(major) + , minor_version(minor) + , revision_version(revision) + , tag_version(tag) + { + TORRENT_ASSERT(id_string); + TORRENT_ASSERT(major >= 0); + TORRENT_ASSERT(minor >= 0); + TORRENT_ASSERT(revision >= 0); + TORRENT_ASSERT(tag >= 0); + TORRENT_ASSERT(std::strlen(id_string) == 2); + name[0] = id_string[0]; + name[1] = id_string[1]; + } + + // generates the actual string put in the peer-id, and return it. + std::string to_string() const + { + char s[100]; + snprintf(s, 100, "-%c%c%c%c%c%c-" + , name[0], name[1] + , version_to_char(major_version) + , version_to_char(minor_version) + , version_to_char(revision_version) + , version_to_char(tag_version)); + return s; + } + + char name[2]; + int major_version; + int minor_version; + int revision_version; + int tag_version; + + private: + + char version_to_char(int v) const + { + if (v >= 0 && v < 10) return '0' + v; + else if (v >= 10) return 'A' + (v - 10); + TORRENT_ASSERT(false); + return '0'; + } + + }; + +} + +#endif // TORRENT_FINGERPRINT_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/gzip.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/gzip.hpp new file mode 100644 index 0000000000..75678f0cf7 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/gzip.hpp @@ -0,0 +1,133 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_GZIP_HPP_INCLUDED +#define TORRENT_GZIP_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include "libtorrent/error_code.hpp" + +#include + +namespace libtorrent +{ + + TORRENT_EXTRA_EXPORT void inflate_gzip( + char const* in, int size + , std::vector& buffer + , int maximum_size + , error_code& error); + + // get the ``error_category`` for zip errors + TORRENT_EXPORT boost::system::error_category& get_gzip_category(); + + namespace gzip_errors + { + // libtorrent uses boost.system's ``error_code`` class to represent errors. libtorrent has + // its own error category get_gzip_category() whith the error codes defined by error_code_enum. + enum error_code_enum + { + // Not an error + no_error = 0, + + // the supplied gzip buffer has invalid header + invalid_gzip_header, + + // the gzip buffer would inflate to more bytes than the specified + // maximum size, and was rejected. + inflated_data_too_large, + + // available inflate data did not terminate + data_did_not_terminate, + + // output space exhausted before completing inflate + space_exhausted, + + // invalid block type (type == 3) + invalid_block_type, + + // stored block length did not match one's complement + invalid_stored_block_length, + + // dynamic block code description: too many length or distance codes + too_many_length_or_distance_codes, + + // dynamic block code description: code lengths codes incomplete + code_lengths_codes_incomplete, + + // dynamic block code description: repeat lengths with no first length + repeat_lengths_with_no_first_length, + + // dynamic block code description: repeat more than specified lengths + repeat_more_than_specified_lengths, + + // dynamic block code description: invalid literal/length code lengths + invalid_literal_length_code_lengths, + + // dynamic block code description: invalid distance code lengths + invalid_distance_code_lengths, + + // invalid literal/length or distance code in fixed or dynamic block + invalid_literal_code_in_block, + + // distance is too far back in fixed or dynamic block + distance_too_far_back_in_block, + + // an unknown error occurred during gzip inflation + unknown_gzip_error, + + // the number of error codes + error_code_max + }; + + // hidden + TORRENT_EXPORT boost::system::error_code make_error_code(error_code_enum e); + } + +} + +#if BOOST_VERSION >= 103500 +namespace boost { namespace system { + +template<> +struct is_error_code_enum +{ static const bool value = true; }; + +template<> +struct is_error_condition_enum +{ static const bool value = true; }; + +} } +#endif // BOOST_VERSION + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/hasher.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/hasher.hpp new file mode 100644 index 0000000000..dc4b0d07c4 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/hasher.hpp @@ -0,0 +1,139 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_HASHER_HPP_INCLUDED +#define TORRENT_HASHER_HPP_INCLUDED + +#include + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" + +#ifdef TORRENT_USE_GCRYPT +#include + +#elif TORRENT_USE_COMMONCRYPTO + +#include + +#elif defined TORRENT_USE_OPENSSL + +extern "C" +{ +#include +} + +#else +// from sha1.cpp +namespace libtorrent +{ + + struct TORRENT_EXTRA_EXPORT sha_ctx + { + boost::uint32_t state[5]; + boost::uint32_t count[2]; + boost::uint8_t buffer[64]; + }; + + TORRENT_EXTRA_EXPORT void SHA1_init(sha_ctx* context); + TORRENT_EXTRA_EXPORT void SHA1_update(sha_ctx* context, boost::uint8_t const* data, boost::uint32_t len); + TORRENT_EXTRA_EXPORT void SHA1_final(boost::uint8_t* digest, sha_ctx* context); +} // namespace libtorrent + +#endif + +namespace libtorrent +{ + // this is a SHA-1 hash class. + // + // You use it by first instantiating it, then call ``update()`` to feed it + // with data. i.e. you don't have to keep the entire buffer of which you want to + // create the hash in memory. You can feed the hasher parts of it at a time. When + // You have fed the hasher with all the data, you call ``final()`` and it + // will return the sha1-hash of the data. + // + // The constructor that takes a ``char const*`` and an integer will construct the + // sha1 context and feed it the data passed in. + // + // If you want to reuse the hasher object once you have created a hash, you have to + // call ``reset()`` to reinitialize it. + // + // The sha1-algorithm used was implemented by Steve Reid and released as public domain. + // For more info, see ``src/sha1.cpp``. + class TORRENT_EXPORT hasher + { + public: + + hasher(); + + // this is the same as default constructing followed by a call to + // ``update(data, len)``. + hasher(const char* data, int len); + +#ifdef TORRENT_USE_GCRYPT + hasher(hasher const& h); + hasher& operator=(hasher const& h); +#endif + + // append the following bytes to what is being hashed + hasher& update(std::string const& data) { update(data.c_str(), int(data.size())); return *this; } + hasher& update(const char* data, int len); + + // returns the SHA-1 digest of the buffers previously passed to + // update() and the hasher constructor. + sha1_hash final(); + + // restore the hasher state to be as if the hasher has just been + // default constructed. + void reset(); + +#ifdef TORRENT_USE_GCRYPT + ~hasher(); +#endif + + private: + +#ifdef TORRENT_USE_GCRYPT + gcry_md_hd_t m_context; +#elif TORRENT_USE_COMMONCRYPTO + CC_SHA1_CTX m_context; +#elif defined TORRENT_USE_OPENSSL + SHA_CTX m_context; +#else + sha_ctx m_context; +#endif + }; +} + +#endif // TORRENT_HASHER_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/http_connection.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/http_connection.hpp new file mode 100644 index 0000000000..16f0547e5e --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/http_connection.hpp @@ -0,0 +1,225 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_HTTP_CONNECTION +#define TORRENT_HTTP_CONNECTION + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libtorrent/socket.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/http_parser.hpp" +#include "libtorrent/deadline_timer.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/socket_type.hpp" +#include "libtorrent/session_settings.hpp" + +#include "libtorrent/i2p_stream.hpp" + +#ifdef TORRENT_USE_OPENSSL +#include +#endif + +namespace libtorrent +{ + +struct http_connection; +class connection_queue; + +const int default_max_bottled_buffer_size = 2*1024*1024; + +typedef boost::function http_handler; + +typedef boost::function http_connect_handler; + +typedef boost::function&)> http_filter_handler; + +// when bottled, the last two arguments to the handler +// will always be 0 +struct TORRENT_EXTRA_EXPORT http_connection + : boost::enable_shared_from_this + , boost::noncopyable +{ + http_connection(io_service& ios, connection_queue& cc + , http_handler const& handler, bool bottled = true + , int max_bottled_buffer_size = default_max_bottled_buffer_size + , http_connect_handler const& ch = http_connect_handler() + , http_filter_handler const& fh = http_filter_handler() +#ifdef TORRENT_USE_OPENSSL + , boost::asio::ssl::context* ssl_ctx = 0 +#endif + ); + + ~http_connection(); + + void rate_limit(int limit); + + int rate_limit() const + { return m_rate_limit; } + + std::string sendbuffer; + + void get(std::string const& url, time_duration timeout = seconds(30) + , int prio = 0, proxy_settings const* ps = 0, int handle_redirects = 5 + , std::string const& user_agent = "", address const& bind_addr = address_v4::any() +#if TORRENT_USE_I2P + , i2p_connection* i2p_conn = 0 +#endif + ); + + void start(std::string const& hostname, std::string const& port + , time_duration timeout, int prio = 0, proxy_settings const* ps = 0 + , bool ssl = false, int handle_redirect = 5 + , address const& bind_addr = address_v4::any() +#if TORRENT_USE_I2P + , i2p_connection* i2p_conn = 0 +#endif + ); + + void close(bool force = false); + + socket_type const& socket() const { return m_sock; } + + std::list const& endpoints() const { return m_endpoints; } + +private: + +#if TORRENT_USE_I2P + void on_i2p_resolve(error_code const& e + , char const* destination); +#endif + void on_resolve(error_code const& e + , tcp::resolver::iterator i); + void queue_connect(); + void connect(int ticket, tcp::endpoint target_address); + void on_connect_timeout(); + void on_connect(error_code const& e); + void on_write(error_code const& e); + void on_read(error_code const& e, std::size_t bytes_transferred); + static void on_timeout(boost::weak_ptr p + , error_code const& e); + void on_assign_bandwidth(error_code const& e); + + void callback(error_code e, char* data = 0, int size = 0); + + std::vector m_recvbuffer; + socket_type m_sock; +#if TORRENT_USE_I2P + i2p_connection* m_i2p_conn; +#endif + int m_read_pos; + tcp::resolver m_resolver; + http_parser m_parser; + http_handler m_handler; + http_connect_handler m_connect_handler; + http_filter_handler m_filter_handler; + deadline_timer m_timer; + time_duration m_read_timeout; + time_duration m_completion_timeout; + ptime m_last_receive; + ptime m_start_time; + + // bottled means that the handler is called once, when + // everything is received (and buffered in memory). + // non bottled means that once the headers have been + // received, data is streamed to the handler + bool m_bottled; + + // maximum size of bottled buffer + int m_max_bottled_buffer_size; + + // set to true the first time the handler is called + bool m_called; + std::string m_hostname; + std::string m_port; + std::string m_url; + std::string m_user_agent; + + std::list m_endpoints; +#ifdef TORRENT_USE_OPENSSL + asio::ssl::context* m_ssl_ctx; + bool m_own_ssl_context; +#endif + + // the current download limit, in bytes per second + // 0 is unlimited. + int m_rate_limit; + + // the number of bytes we are allowed to receive + int m_download_quota; + + // only hand out new quota 4 times a second if the + // quota is 0. If it isn't 0 wait for it to reach + // 0 and continue to hand out quota at that time. + bool m_limiter_timer_active; + + // the timer fires every 250 millisecond as long + // as all the quota was used. + deadline_timer m_limiter_timer; + + // the number of redirects to follow (in sequence) + int m_redirects; + + int m_connection_ticket; + connection_queue& m_cc; + + // specifies whether or not the connection is + // configured to use a proxy + proxy_settings m_proxy; + + // true if the connection is using ssl + bool m_ssl; + + // the address to bind to. address_v4::any() + // means do not bind + address m_bind_addr; + + // the priority we have in the connection queue. + // 0 is normal, 1 is high + int m_priority; + + bool m_abort; +}; + +} + +#endif diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/http_parser.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/http_parser.hpp new file mode 100644 index 0000000000..9682f50145 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/http_parser.hpp @@ -0,0 +1,180 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_HTTP_PARSER_HPP_INCLUDED +#define TORRENT_HTTP_PARSER_HPP_INCLUDED + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/buffer.hpp" +#include "libtorrent/size_type.hpp" + +namespace libtorrent +{ + + // return true if the status code is 200, 206, or in the 300-400 range + bool is_ok_status(int http_status); + + // return true if the status code is a redirect + bool is_redirect(int http_status); + + TORRENT_EXTRA_EXPORT std::string resolve_redirect_location(std::string referrer + , std::string location); + + class TORRENT_EXTRA_EXPORT http_parser + { + public: + enum flags_t { dont_parse_chunks = 1 }; + http_parser(int flags = 0); + ~http_parser(); + std::string const& header(char const* key) const + { + static std::string empty; + std::multimap::const_iterator i + = m_header.find(key); + if (i == m_header.end()) return empty; + return i->second; + } + + std::string const& protocol() const { return m_protocol; } + int status_code() const { return m_status_code; } + std::string const& method() const { return m_method; } + std::string const& path() const { return m_path; } + std::string const& message() const { return m_server_message; } + buffer::const_interval get_body() const; + bool header_finished() const { return m_state == read_body; } + bool finished() const { return m_finished; } + boost::tuple incoming(buffer::const_interval recv_buffer + , bool& error); + int body_start() const { return m_body_start_pos; } + size_type content_length() const { return m_content_length; } + std::pair content_range() const + { return std::make_pair(m_range_start, m_range_end); } + + // returns true if this response is using chunked encoding. + // in this case the body is split up into chunks. You need + // to call parse_chunk_header() for each chunk, starting with + // the start of the body. + bool chunked_encoding() const { return m_chunked_encoding; } + + // removes the chunk headers from the supplied buffer. The buffer + // must be the stream received from the http server this parser + // instanced parsed. It will use the internal chunk list to determine + // where the chunks are in the buffer. It returns the new length of + // the buffer + int collapse_chunk_headers(char* buffer, int size) const; + + // returns false if the buffer doesn't contain a complete + // chunk header. In this case, call the function again with + // a bigger buffer once more bytes have been received. + // chunk_size is filled in with the number of bytes in the + // chunk that follows. 0 means the response terminated. In + // this case there might be additional headers in the parser + // object. + // header_size is filled in with the number of bytes the header + // itself was. Skip this number of bytes to get to the actual + // chunk data. + // if the function returns false, the chunk size and header + // size may still have been modified, but their values are + // undefined + bool parse_chunk_header(buffer::const_interval buf + , size_type* chunk_size, int* header_size); + + // reset the whole state and start over + void reset(); + + bool connection_close() const { return m_connection_close; } + + std::multimap const& headers() const { return m_header; } + std::vector > const& chunks() const { return m_chunked_ranges; } + + private: + size_type m_recv_pos; + int m_status_code; + std::string m_method; + std::string m_path; + std::string m_protocol; + std::string m_server_message; + + size_type m_content_length; + size_type m_range_start; + size_type m_range_end; + + enum { read_status, read_header, read_body, error_state } m_state; + + std::multimap m_header; + buffer::const_interval m_recv_buffer; + int m_body_start_pos; + + // this is true if the server is HTTP/1.0 or + // if it sent "connection: close" + bool m_connection_close; + bool m_chunked_encoding; + bool m_finished; + + // contains offsets of the first and one-past-end of + // each chunked range in the response + std::vector > m_chunked_ranges; + + // while reading a chunk, this is the offset where the + // current chunk will end (it refers to the first character + // in the chunk tail header or the next chunk header) + size_type m_cur_chunk_end; + + // the sum of all chunk headers read so far + int m_chunk_header_size; + + int m_partial_chunk_header; + + // controls some behaviors of the parser + int m_flags; + }; + +} + +#endif // TORRENT_HTTP_PARSER_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/http_seed_connection.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/http_seed_connection.hpp new file mode 100644 index 0000000000..1813091746 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/http_seed_connection.hpp @@ -0,0 +1,137 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_HTTP_SEED_CONNECTION_HPP_INCLUDED +#define TORRENT_HTTP_SEED_CONNECTION_HPP_INCLUDED + +#include +#include +#include +#include + +#include "libtorrent/debug.hpp" + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/web_connection_base.hpp" +#include "libtorrent/disk_buffer_holder.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/http_parser.hpp" + +namespace libtorrent +{ + class torrent; + struct peer_request; + + namespace detail + { + struct session_impl; + } + + class TORRENT_EXTRA_EXPORT http_seed_connection + : public web_connection_base + { + friend class invariant_access; + public: + + // this is the constructor where the we are the active part. + // The peer_conenction should handshake and verify that the + // other end has the correct id + http_seed_connection( + aux::session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote + , web_seed_entry& web); + + virtual int type() const { return peer_connection::http_seed_connection; } + + // called from the main loop when this connection has any + // work to do. + void on_receive(error_code const& error + , std::size_t bytes_transferred); + + std::string const& url() const { return m_url; } + + virtual void get_specific_peer_info(peer_info& p) const; + virtual void disconnect(error_code const& ec, int error = 0); + + void write_request(peer_request const& r); + + private: + + // returns the block currently being + // downloaded. And the progress of that + // block. If the peer isn't downloading + // a piece for the moment, the boost::optional + // will be invalid. + boost::optional downloading_piece_progress() const; + + // this is const since it's used as a key in the web seed list in the torrent + // if it's changed referencing back into that list will fail + const std::string m_url; + + // the number of bytes left to receive of the response we're + // currently parsing + size_type m_response_left; + + // this is the offset inside the current receive + // buffer where the next chunk header will be. + // this is updated for each chunk header that's + // parsed. It does not necessarily point to a valid + // offset in the receive buffer, if we haven't received + // it yet. This offset never includes the HTTP header + size_type m_chunk_pos; + + // this is the number of bytes we've already received + // from the next chunk header we're waiting for + int m_partial_chunk_header; + }; +} + +#endif // TORRENT_WEB_PEER_CONNECTION_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/http_stream.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/http_stream.hpp new file mode 100644 index 0000000000..8165102218 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/http_stream.hpp @@ -0,0 +1,124 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_HTTP_STREAM_HPP_INCLUDED +#define TORRENT_HTTP_STREAM_HPP_INCLUDED + +#include +#include "libtorrent/proxy_base.hpp" +#include +#include + +namespace libtorrent { + +class http_stream : public proxy_base +{ +public: + + explicit http_stream(io_service& io_service) + : proxy_base(io_service) + , m_no_connect(false) + {} + + void set_no_connect(bool c) { m_no_connect = c; } + + void set_username(std::string const& user + , std::string const& password) + { + m_user = user; + m_password = password; + } + + void set_dst_name(std::string const& host) + { + m_dst_name = host; + } + + void close(error_code& ec) + { + m_dst_name.clear(); + proxy_base::close(ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + void close() + { + m_dst_name.clear(); + proxy_base::close(); + } +#endif + + typedef boost::function handler_type; + + template + void async_connect(endpoint_type const& endpoint, Handler const& handler) + { + m_remote_endpoint = endpoint; + + // the connect is split up in the following steps: + // 1. resolve name of proxy server + // 2. connect to proxy server + // 3. send HTTP CONNECT method and possibly username+password + // 4. read CONNECT response + + // to avoid unnecessary copying of the handler, + // store it in a shared_ptr + boost::shared_ptr h(new handler_type(handler)); + + tcp::resolver::query q(m_hostname, to_string(m_port).elems); + m_resolver.async_resolve(q, boost::bind( + &http_stream::name_lookup, this, _1, _2, h)); + } + +private: + + void name_lookup(error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h); + void connected(error_code const& e, boost::shared_ptr h); + void handshake1(error_code const& e, boost::shared_ptr h); + void handshake2(error_code const& e, boost::shared_ptr h); + + // send and receive buffer + std::vector m_buffer; + // proxy authentication + std::string m_user; + std::string m_password; + std::string m_dst_name; + + // this is true if the connection is HTTP based and + // want to talk directly to the proxy + bool m_no_connect; +}; + +} + +#endif diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/http_tracker_connection.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/http_tracker_connection.hpp new file mode 100644 index 0000000000..fb672871f8 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/http_tracker_connection.hpp @@ -0,0 +1,117 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED +#define TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED + +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/lazy_entry.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/i2p_stream.hpp" + +namespace libtorrent +{ + + struct http_connection; + class entry; + class http_parser; + class connection_queue; + struct session_settings; + namespace aux { struct session_impl; } + + class TORRENT_EXTRA_EXPORT http_tracker_connection + : public tracker_connection + { + friend class tracker_manager; + public: + + http_tracker_connection( + io_service& ios + , connection_queue& cc + , tracker_manager& man + , tracker_request const& req + , boost::weak_ptr c + , aux::session_impl const& ses + , proxy_settings const& ps + , std::string const& password = "" +#if TORRENT_USE_I2P + , i2p_connection* i2p_conn = 0 +#endif + ); + + void start(); + void close(); + + private: + + boost::intrusive_ptr self() + { return boost::intrusive_ptr(this); } + + void on_filter(http_connection& c, std::list& endpoints); + void on_connect(http_connection& c); + void on_response(error_code const& ec, http_parser const& parser + , char const* data, int size); + + virtual void on_timeout(error_code const& ec) {} + + void parse(int status_code, lazy_entry const& e); + bool extract_peer_info(lazy_entry const& e, peer_entry& ret); + + tracker_manager& m_man; + boost::shared_ptr m_tracker_connection; + aux::session_impl const& m_ses; + address m_tracker_ip; + proxy_settings const& m_ps; + connection_queue& m_cc; + io_service& m_ios; +#if TORRENT_USE_I2P + i2p_connection* m_i2p_conn; +#endif + }; + +} + +#endif // TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/i2p_stream.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/i2p_stream.hpp new file mode 100644 index 0000000000..b82adf7dbc --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/i2p_stream.hpp @@ -0,0 +1,238 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_I2P_STREAM_HPP_INCLUDED +#define TORRENT_I2P_STREAM_HPP_INCLUDED + +#include "libtorrent/config.hpp" + +#if TORRENT_USE_I2P + +#include +#include +#include +#include +#include +#include +#include +#include "libtorrent/proxy_base.hpp" +#include "libtorrent/session_settings.hpp" + +namespace libtorrent { + + namespace i2p_error { + + // error values for the i2p_category error_category. + enum i2p_error_code + { + no_error = 0, + parse_failed, + cant_reach_peer, + i2p_error, + invalid_key, + invalid_id, + timeout, + key_not_found, + duplicated_id, + num_errors + }; + + // hidden + TORRENT_EXPORT boost::system::error_code make_error_code(i2p_error_code e); + } + + // returns the error category for I2P errors + TORRENT_EXPORT boost::system::error_category& get_i2p_category(); + +class i2p_stream : public proxy_base +{ +public: + + explicit i2p_stream(io_service& io_service); + ~i2p_stream(); + + enum command_t + { + cmd_none, + cmd_create_session, + cmd_connect, + cmd_accept, + cmd_name_lookup, + cmd_incoming + }; + + void set_command(command_t c) { m_command = c; } + + void set_session_id(char const* id) { m_id = id; } + + void set_destination(std::string const& d) { m_dest = d; } + std::string const& destination() { return m_dest; } + + typedef boost::function handler_type; + + template + void async_connect(endpoint_type const& endpoint, Handler const& handler) + { + // since we don't support regular endpoints, just ignore the one + // provided and use m_dest. + + // the connect is split up in the following steps: + // 1. resolve name of proxy server + // 2. connect to SAM bridge + // 4 send command message (CONNECT/ACCEPT) + + // to avoid unnecessary copying of the handler, + // store it in a shaed_ptr + boost::shared_ptr h(new handler_type(handler)); + + tcp::resolver::query q(m_hostname, to_string(m_port).elems); + m_resolver.async_resolve(q, boost::bind( + &i2p_stream::do_connect, this, _1, _2, h)); + } + + std::string name_lookup() const { return m_name_lookup; } + void set_name_lookup(char const* name) { m_name_lookup = name; } + + void send_name_lookup(boost::shared_ptr h); + +private: + + bool handle_error(error_code const& e, boost::shared_ptr const& h); + void do_connect(error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h); + void connected(error_code const& e, boost::shared_ptr h); + void start_read_line(error_code const& e, boost::shared_ptr h); + void read_line(error_code const& e, boost::shared_ptr h); + void send_connect(boost::shared_ptr h); + void send_accept(boost::shared_ptr h); + void send_session_create(boost::shared_ptr h); + + // send and receive buffer + std::vector m_buffer; + char const* m_id; + int m_command; // 0 = connect, 1 = accept + std::string m_dest; + std::string m_name_lookup; + + enum state_t + { + read_hello_response, + read_connect_response, + read_accept_response, + read_session_create_response, + read_name_lookup_response + }; + + int m_state; +#if TORRENT_USE_ASSERTS + int m_magic; +#endif +}; + +class i2p_connection +{ +public: + i2p_connection(io_service& ios); + ~i2p_connection(); + + proxy_settings const& proxy() const { return m_sam_router; } + + bool is_open() const + { + return m_sam_socket + && m_sam_socket->is_open() + && m_state != sam_connecting; + } + void open(proxy_settings const& s, i2p_stream::handler_type const& h); + void close(error_code&); + + char const* session_id() const { return m_session_id.c_str(); } + std::string const& local_endpoint() const { return m_i2p_local_endpoint; } + + typedef boost::function name_lookup_handler; + void async_name_lookup(char const* name, name_lookup_handler handler); + +private: + + void on_sam_connect(error_code const& ec, i2p_stream::handler_type const& h + , boost::shared_ptr); + void do_name_lookup(std::string const& name + , name_lookup_handler const& h); + void on_name_lookup(error_code const& ec + , name_lookup_handler handler + , boost::shared_ptr); + + void set_local_endpoint(error_code const& ec, char const* dest + , i2p_stream::handler_type const& h); + + // to talk to i2p SAM bridge + boost::shared_ptr m_sam_socket; + proxy_settings m_sam_router; + + // our i2p endpoint key + std::string m_i2p_local_endpoint; + std::string m_session_id; + + std::list > m_name_lookup; + + enum state_t + { + sam_connecting, + sam_name_lookup, + sam_idle + }; + + state_t m_state; + + io_service& m_io_service; +}; + +} + +#if BOOST_VERSION >= 103500 +namespace boost { namespace system { + +template<> +struct is_error_code_enum +{ static const bool value = true; }; + +template<> +struct is_error_condition_enum +{ static const bool value = true; }; + +} } +#endif // BOOST_VERSION + +#endif // TORRENT_USE_I2P + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/identify_client.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/identify_client.hpp new file mode 100644 index 0000000000..ed8492b50d --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/identify_client.hpp @@ -0,0 +1,67 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_IDENTIFY_CLIENT_HPP_INCLUDED +#define TORRENT_IDENTIFY_CLIENT_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/fingerprint.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + // This function can can be used to extract a string describing a client + // version from its peer-id. It will recognize most clients that have this + // kind of identification in the peer-id. + TORRENT_EXPORT std::string identify_client(const peer_id& p); + + // Returns an optional fingerprint if any can be identified from the peer + // id. This can be used to automate the identification of clients. It will + // not be able to identify peers with non- standard encodings. Only Azureus + // style, Shadow's style and Mainline style. + TORRENT_EXPORT boost::optional client_fingerprint(peer_id const& p); + +} + +#endif // TORRENT_IDENTIFY_CLIENT_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/instantiate_connection.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/instantiate_connection.hpp new file mode 100644 index 0000000000..c876634d78 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/instantiate_connection.hpp @@ -0,0 +1,54 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_INSTANTIATE_CONNECTION +#define TORRENT_INSTANTIATE_CONNECTION + +#include "libtorrent/socket_type.hpp" +#include + +namespace libtorrent +{ + struct proxy_settings; + struct utp_socket_manager; + struct socket_type; + + // instantiate a socket_type (s) according to the specified criteria + TORRENT_EXTRA_EXPORT bool instantiate_connection(io_service& ios + , proxy_settings const& ps, socket_type& s + , void* ssl_context = 0 + , utp_socket_manager* sm = 0 + , bool peer_connection = false); +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/intrusive_ptr_base.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/intrusive_ptr_base.hpp new file mode 100644 index 0000000000..7ebe986154 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/intrusive_ptr_base.hpp @@ -0,0 +1,84 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_INTRUSIVE_PTR_BASE +#define TORRENT_INTRUSIVE_PTR_BASE + +#include +#include +#include +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" + +namespace libtorrent +{ + template + struct intrusive_ptr_base + { + intrusive_ptr_base(intrusive_ptr_base const&) + : m_refs(0) {} + + friend void intrusive_ptr_add_ref(intrusive_ptr_base const* s) + { + TORRENT_ASSERT(s != 0); + TORRENT_ASSERT(s->m_refs >= 0); + ++s->m_refs; + } + + friend void intrusive_ptr_release(intrusive_ptr_base const* s) + { + TORRENT_ASSERT(s != 0); + TORRENT_ASSERT(s->m_refs > 0); + if (--s->m_refs == 0) + boost::checked_delete(static_cast(s)); + } + + boost::intrusive_ptr self() + { return boost::intrusive_ptr((T*)this); } + + boost::intrusive_ptr self() const + { return boost::intrusive_ptr((T const*)this); } + + int refcount() const { return m_refs; } + + intrusive_ptr_base(): m_refs(0) {} + + private: + + // reference counter for intrusive_ptr + mutable boost::detail::atomic_count m_refs; + }; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/invariant_check.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/invariant_check.hpp new file mode 100644 index 0000000000..388552954a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/invariant_check.hpp @@ -0,0 +1,81 @@ +// Copyright Daniel Wallin 2004. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef TORRENT_INVARIANT_ACCESS_HPP_INCLUDED +#define TORRENT_INVARIANT_ACCESS_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/config.hpp" + +#if TORRENT_USE_INVARIANT_CHECKS + +namespace libtorrent +{ + + class invariant_access + { + public: + template + static void check_invariant(T const& self) + { + self.check_invariant(); + } + }; + + template + void check_invariant(T const& x) + { + invariant_access::check_invariant(x); + } + + struct invariant_checker {}; + + template + struct invariant_checker_impl : invariant_checker + { + invariant_checker_impl(T const& self_) + : self(self_) + { + TORRENT_TRY + { + check_invariant(self); + } + TORRENT_CATCH_ALL + { + TORRENT_ASSERT(false); + } + } + + ~invariant_checker_impl() + { + TORRENT_TRY + { + check_invariant(self); + } + TORRENT_CATCH_ALL + { + TORRENT_ASSERT(false); + } + } + + T const& self; + }; + + template + invariant_checker_impl make_invariant_checker(T const& x) + { + return invariant_checker_impl(x); + } +} + +#define INVARIANT_CHECK \ + invariant_checker const& _invariant_check = make_invariant_checker(*this); \ + (void)_invariant_check; \ + do {} while (false) +#else +#define INVARIANT_CHECK do {} while (false) +#endif + +#endif // TORRENT_INVARIANT_ACCESS_HPP_INCLUDED diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/io.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/io.hpp new file mode 100644 index 0000000000..6e0c054fb2 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/io.hpp @@ -0,0 +1,170 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_IO_HPP_INCLUDED +#define TORRENT_IO_HPP_INCLUDED + +#include +#include +#include // for copy +#include // for memcpy + +namespace libtorrent +{ + namespace detail + { + template struct type {}; + + // reads an integer from a byte stream + // in big endian byte order and converts + // it to native endianess + template + inline T read_impl(InIt& start, type) + { + T ret = 0; + for (int i = 0; i < (int)sizeof(T); ++i) + { + ret <<= 8; + ret |= static_cast(*start); + ++start; + } + return ret; + } + + template + boost::uint8_t read_impl(InIt& start, type) + { + return static_cast(*start++); + } + + template + boost::int8_t read_impl(InIt& start, type) + { + return static_cast(*start++); + } + + template + inline void write_impl(T val, OutIt& start) + { + for (int i = (int)sizeof(T)-1; i >= 0; --i) + { + *start = static_cast((val >> (i * 8)) & 0xff); + ++start; + } + } + + // -- adaptors + + template + boost::int64_t read_int64(InIt& start) + { return read_impl(start, type()); } + + template + boost::uint64_t read_uint64(InIt& start) + { return read_impl(start, type()); } + + template + boost::uint32_t read_uint32(InIt& start) + { return read_impl(start, type()); } + + template + boost::int32_t read_int32(InIt& start) + { return read_impl(start, type()); } + + template + boost::int16_t read_int16(InIt& start) + { return read_impl(start, type()); } + + template + boost::uint16_t read_uint16(InIt& start) + { return read_impl(start, type()); } + + template + boost::int8_t read_int8(InIt& start) + { return read_impl(start, type()); } + + template + boost::uint8_t read_uint8(InIt& start) + { return read_impl(start, type()); } + + + template + void write_uint64(boost::uint64_t val, OutIt& start) + { write_impl(val, start); } + + template + void write_int64(boost::int64_t val, OutIt& start) + { write_impl(val, start); } + + template + void write_uint32(boost::uint32_t val, OutIt& start) + { write_impl(val, start); } + + template + void write_int32(boost::int32_t val, OutIt& start) + { write_impl(val, start); } + + template + void write_uint16(boost::uint16_t val, OutIt& start) + { write_impl(val, start); } + + template + void write_int16(boost::int16_t val, OutIt& start) + { write_impl(val, start); } + + template + void write_uint8(boost::uint8_t val, OutIt& start) + { write_impl(val, start); } + + template + void write_int8(boost::int8_t val, OutIt& start) + { write_impl(val, start); } + + inline int write_string(std::string const& str, char*& start) + { + std::memcpy((void*)start, str.c_str(), str.size()); + start += str.size(); + return str.size(); + } + + template + int write_string(std::string const& val, OutIt& out) + { + for (std::string::const_iterator i = val.begin() + , end(val.end()); i != end; ++i) + *out++ = *i; + return int(val.length()); + } + } +} + +#endif // TORRENT_IO_HPP_INCLUDED diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/io_service.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/io_service.hpp new file mode 100644 index 0000000000..91ebdd5c05 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/io_service.hpp @@ -0,0 +1,77 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_IO_SERVICE_HPP_INCLUDED +#define TORRENT_IO_SERVICE_HPP_INCLUDED + +#ifdef __OBJC__ +#define Protocol Protocol_ +#endif + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN +// asio assumes that the windows error codes are defined already +#include +#endif + +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#ifdef __OBJC__ +#undef Protocol +#endif + +namespace libtorrent +{ + +#if BOOST_VERSION < 103500 + typedef ::asio::io_service io_service; +#else + typedef boost::asio::io_service io_service; +#endif +} + +#endif + + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/io_service_fwd.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/io_service_fwd.hpp new file mode 100644 index 0000000000..e083ef6636 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/io_service_fwd.hpp @@ -0,0 +1,77 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_IO_SERVICE_FWD_HPP_INCLUDED +#define TORRENT_IO_SERVICE_FWD_HPP_INCLUDED + +#ifdef __OBJC__ +#define Protocol Protocol_ +#endif + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#ifdef __OBJC__ +#undef Protocol +#endif + +#if BOOST_VERSION >= 103500 +namespace boost { +#endif +namespace asio { + +class io_service; + +} +#if BOOST_VERSION >= 103500 +} +#endif + +namespace libtorrent +{ + +#if BOOST_VERSION < 103500 + typedef ::asio::io_service io_service; +#else + typedef boost::asio::io_service io_service; +#endif +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/ip_filter.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/ip_filter.hpp new file mode 100644 index 0000000000..d868ea7cc9 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/ip_filter.hpp @@ -0,0 +1,362 @@ +/* + +Copyright (c) 2005-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_IP_FILTER_HPP +#define TORRENT_IP_FILTER_HPP + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + +#include "libtorrent/config.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/assert.hpp" + +namespace libtorrent +{ + +// hidden +inline bool operator<=(address const& lhs + , address const& rhs) +{ + return lhs < rhs || lhs == rhs; +} + +template +struct ip_range +{ + Addr first; + Addr last; + int flags; +}; + +namespace detail +{ + + template + Addr zero() + { + Addr zero; + std::fill(zero.begin(), zero.end(), 0); + return zero; + } + + template<> + inline boost::uint16_t zero() { return 0; } + + template + Addr plus_one(Addr const& a) + { + Addr tmp(a); + for (int i = int(tmp.size()) - 1; i >= 0; --i) + { + if (tmp[i] < (std::numeric_limits::max)()) + { + tmp[i] += 1; + break; + } + tmp[i] = 0; + } + return tmp; + } + + inline boost::uint16_t plus_one(boost::uint16_t val) { return val + 1; } + + template + Addr minus_one(Addr const& a) + { + Addr tmp(a); + for (int i = int(tmp.size()) - 1; i >= 0; --i) + { + if (tmp[i] > 0) + { + tmp[i] -= 1; + break; + } + tmp[i] = (std::numeric_limits::max)(); + } + return tmp; + } + + inline boost::uint16_t minus_one(boost::uint16_t val) { return val - 1; } + + template + Addr max_addr() + { + Addr tmp; + std::fill(tmp.begin(), tmp.end() + , (std::numeric_limits::max)()); + return Addr(tmp); + } + + template<> + inline boost::uint16_t max_addr() + { return (std::numeric_limits::max)(); } + + // this is the generic implementation of + // a filter for a specific address type. + // it works with IPv4 and IPv6 + template + class filter_impl + { + public: + + filter_impl() + { + // make the entire ip-range non-blocked + m_access_list.insert(range(zero(), 0)); + } + + void add_rule(Addr first, Addr last, int flags) + { + TORRENT_ASSERT(!m_access_list.empty()); + TORRENT_ASSERT(first < last || first == last); + + typename range_t::iterator i = m_access_list.upper_bound(first); + typename range_t::iterator j = m_access_list.upper_bound(last); + + if (i != m_access_list.begin()) --i; + + TORRENT_ASSERT(j != m_access_list.begin()); + TORRENT_ASSERT(j != i); + + int first_access = i->access; + int last_access = boost::prior(j)->access; + + if (i->start != first && first_access != flags) + { + i = m_access_list.insert(i, range(first, flags)); + } + else if (i != m_access_list.begin() && boost::prior(i)->access == flags) + { + --i; + first_access = i->access; + } + TORRENT_ASSERT(!m_access_list.empty()); + TORRENT_ASSERT(i != m_access_list.end()); + + if (i != j) m_access_list.erase(boost::next(i), j); + if (i->start == first) + { + // we can do this const-cast because we know that the new + // start address will keep the set correctly ordered + const_cast(i->start) = first; + const_cast(i->access) = flags; + } + else if (first_access != flags) + { + m_access_list.insert(i, range(first, flags)); + } + + if ((j != m_access_list.end() + && minus_one(j->start) != last) + || (j == m_access_list.end() + && last != max_addr())) + { + TORRENT_ASSERT(j == m_access_list.end() || last < minus_one(j->start)); + if (last_access != flags) + j = m_access_list.insert(j, range(plus_one(last), last_access)); + } + + if (j != m_access_list.end() && j->access == flags) m_access_list.erase(j); + TORRENT_ASSERT(!m_access_list.empty()); + } + + int access(Addr const& addr) const + { + TORRENT_ASSERT(!m_access_list.empty()); + typename range_t::const_iterator i = m_access_list.upper_bound(addr); + if (i != m_access_list.begin()) --i; + TORRENT_ASSERT(i != m_access_list.end()); + TORRENT_ASSERT(i->start <= addr && (boost::next(i) == m_access_list.end() + || addr < boost::next(i)->start)); + return i->access; + } + + template + std::vector > export_filter() const + { + std::vector > ret; + ret.reserve(m_access_list.size()); + + for (typename range_t::const_iterator i = m_access_list.begin() + , end(m_access_list.end()); i != end;) + { + ip_range r; + r.first = ExternalAddressType(i->start); + r.flags = i->access; + + ++i; + if (i == end) + r.last = ExternalAddressType(max_addr()); + else + r.last = ExternalAddressType(minus_one(i->start)); + + ret.push_back(r); + } + return ret; + } + + private: + + struct range + { + range(Addr addr, int a = 0): start(addr), access(a) {} + bool operator<(range const& r) const + { return start < r.start; } + bool operator<(Addr const& a) const + { return start < a; } + Addr start; + // the end of the range is implicit + // and given by the next entry in the set + int access; + }; + + typedef std::set range_t; + range_t m_access_list; + + }; + +} + +// The ``ip_filter`` class is a set of rules that uniquely categorizes all +// ip addresses as allowed or disallowed. The default constructor creates +// a single rule that allows all addresses (0.0.0.0 - 255.255.255.255 for +// the IPv4 range, and the equivalent range covering all addresses for the +// IPv6 range). +// +// A default constructed ip_filter does not filter any address. +struct TORRENT_EXPORT ip_filter +{ + // the flags defined for an IP range + enum access_flags + { + // indicates that IPs in this range should not be connected + // to nor accepted as incoming connections + blocked = 1 + }; + + // Adds a rule to the filter. ``first`` and ``last`` defines a range of + // ip addresses that will be marked with the given flags. The ``flags`` + // can currently be 0, which means allowed, or ``ip_filter::blocked``, which + // means disallowed. + // + // precondition: + // ``first.is_v4() == last.is_v4() && first.is_v6() == last.is_v6()`` + // + // postcondition: + // ``access(x) == flags`` for every ``x`` in the range [``first``, ``last``] + // + // This means that in a case of overlapping ranges, the last one applied takes + // precedence. + void add_rule(address first, address last, int flags); + + // Returns the access permissions for the given address (``addr``). The permission + // can currently be 0 or ``ip_filter::blocked``. The complexity of this operation + // is O(``log`` n), where n is the minimum number of non-overlapping ranges to describe + // the current filter. + int access(address const& addr) const; + +#if TORRENT_USE_IPV6 + typedef boost::tuple > + , std::vector > > filter_tuple_t; +#else + typedef std::vector > filter_tuple_t; +#endif + + // This function will return the current state of the filter in the minimum number of + // ranges possible. They are sorted from ranges in low addresses to high addresses. Each + // entry in the returned vector is a range with the access control specified in its + // ``flags`` field. + // + // The return value is a tuple containing two range-lists. One for IPv4 addresses + // and one for IPv6 addresses. + filter_tuple_t export_filter() const; + +// void print() const; + +private: + + detail::filter_impl m_filter4; +#if TORRENT_USE_IPV6 + detail::filter_impl m_filter6; +#endif +}; + +// the port filter maps non-overlapping port ranges to flags. This +// is primarily used to indicate whether a range of ports should +// be connected to or not. The default is to have the full port +// range (0-65535) set to flag 0. +class TORRENT_EXPORT port_filter +{ +public: + + // the defined flags for a port range + enum access_flags + { + // this flag indicates that destination ports in the + // range should not be connected to + blocked = 1 + }; + + // set the flags for the specified port range (``first``, ``last``) to + // ``flags`` overwriting any existing rule for those ports. The range + // is inclusive, i.e. the port ``last`` also has the flag set on it. + void add_rule(boost::uint16_t first, boost::uint16_t last, int flags); + + // test the specified port (``port``) for whether it is blocked + // or not. The returned value is the flags set for this port. + // see acces_flags. + int access(boost::uint16_t port) const; + +private: + + detail::filter_impl m_filter; + +}; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/ip_voter.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/ip_voter.hpp new file mode 100644 index 0000000000..9d63942572 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/ip_voter.hpp @@ -0,0 +1,131 @@ +/* + +Copyright (c) 2013-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_IP_VOTER_HPP_INCLUDED +#define TORRENT_IP_VOTER_HPP_INCLUDED + +#include +#include "libtorrent/address.hpp" +#include "libtorrent/bloom_filter.hpp" +#include "libtorrent/time.hpp" // for ptime + +namespace libtorrent +{ + // this is an object that keeps the state for a single external IP + // based on peoples votes + struct TORRENT_EXTRA_EXPORT ip_voter + { + ip_voter(); + + // returns true if a different IP is the top vote now + // i.e. we changed our idea of what our external IP is + bool cast_vote(address const& ip, int source_type, address const& sorce); + + address external_address() const { return m_external_address; } + + private: + + bool maybe_rotate(); + + struct external_ip_t + { + external_ip_t(): sources(0), num_votes(0) {} + + bool add_vote(sha1_hash const& k, int type); + + // we want to sort decending + bool operator<(external_ip_t const& rhs) const + { + if (num_votes > rhs.num_votes) return true; + if (num_votes < rhs.num_votes) return false; + return sources > rhs.sources; + } + + // this is a bloom filter of the IPs that have + // reported this address + bloom_filter<16> voters; + // this is the actual external address + address addr; + // a bitmask of sources the reporters have come from + boost::uint16_t sources; + // the total number of votes for this IP + boost::uint16_t num_votes; + }; + + // this is a bloom filter of all the IPs that have + // been the first to report an external address. Each + // IP only gets to add a new item once. + bloom_filter<32> m_external_address_voters; + + std::vector m_external_addresses; + address m_external_address; + + // the total number of unique IPs that have voted + int m_total_votes; + + // this is true from the first time we rotate. Before + // we rotate for the first time, we keep updating the + // external address as we go, since we don't have any + // stable setting to fall back on. Once this is true, + // we stop updating it on the fly, and just use the + // address from when we rotated. + bool m_valid_external; + + // the last time we rotated this ip_voter. i.e. threw + // away all the votes and started from scratch, in case + // our IP has changed + ptime m_last_rotate; + }; + + // this keeps track of multiple external IPs (for now, just IPv6 and IPv4, but + // it could be extended to deal with loopback and local network addresses as well) + struct TORRENT_EXTRA_EXPORT external_ip + { + // returns true if a different IP is the top vote now + // i.e. we changed our idea of what our external IP is + bool cast_vote(address const& ip, int source_type, address const& source); + + // the external IP as it would be observed from `ip` + address external_address(address const& ip) const; + + private: + + // for now, assume one external IPv4 and one external IPv6 address + // 0 = IPv4 1 = IPv6 + // TODO: 1 instead, have one instance per possible subnet, global IPv4, global IPv6, loopback, 192.168.x.x, 10.x.x.x, etc. + ip_voter m_vote_group[2]; + }; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/dht_observer.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/dht_observer.hpp new file mode 100644 index 0000000000..02fb347cec --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/dht_observer.hpp @@ -0,0 +1,48 @@ +/* + +Copyright (c) 2012-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef DHT_OBSERVER_HPP +#define DHT_OBSERVER_HPP + +#include "libtorrent/address.hpp" + +namespace libtorrent { namespace dht +{ + struct dht_observer + { + virtual void set_external_address(address const& addr + , address const& source) = 0; + }; +}} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/dht_tracker.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/dht_tracker.hpp new file mode 100644 index 0000000000..691169b51f --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/dht_tracker.hpp @@ -0,0 +1,191 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISABLE_DHT + +#ifndef TORRENT_DHT_TRACKER +#define TORRENT_DHT_TRACKER + +#include +#include +#include +#include +#include +#include +#include + +#include "libtorrent/kademlia/node.hpp" +#include "libtorrent/kademlia/node_id.hpp" +#include "libtorrent/kademlia/traversal_algorithm.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/session_status.hpp" +#include "libtorrent/udp_socket.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/thread.hpp" +#include "libtorrent/deadline_timer.hpp" + +namespace libtorrent +{ + namespace aux { struct session_impl; } + struct lazy_entry; +} + +namespace libtorrent { namespace dht +{ + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_DECLARE_LOG(dht_tracker); +#endif + + struct dht_tracker; + + TORRENT_EXTRA_EXPORT void intrusive_ptr_add_ref(dht_tracker const*); + TORRENT_EXTRA_EXPORT void intrusive_ptr_release(dht_tracker const*); + + struct dht_tracker : udp_socket_interface, udp_socket_observer + { + friend void intrusive_ptr_add_ref(dht_tracker const*); + friend void intrusive_ptr_release(dht_tracker const*); + + dht_tracker(libtorrent::aux::session_impl& ses, rate_limited_udp_socket& sock + , dht_settings const& settings, entry const* state = 0); + virtual ~dht_tracker(); + + void start(entry const& bootstrap + , find_data::nodes_callback const& f); + void stop(); + + void add_node(udp::endpoint node); + void add_node(std::pair const& node); + void add_router_node(udp::endpoint const& node); + + entry state() const; + + enum flags_t { flag_seed = 1, flag_implied_port = 2 }; + void announce(sha1_hash const& ih, int listen_port, int flags + , boost::function const&)> f); + + void get_item(sha1_hash const& target + , boost::function cb); + + // key is a 32-byte binary string, the public key to look up. + // the salt is optional + void get_item(char const* key + , boost::function cb + , std::string salt = std::string()); + + void put_item(entry data + , boost::function cb); + + void put_item(char const* key + , boost::function cb, std::string salt = std::string()); + + void dht_status(session_status& s); + void network_stats(int& sent, int& received); + + // translate bittorrent kademlia message into the generic kademlia message + // used by the library + virtual bool incoming_packet(error_code const& ec + , udp::endpoint const&, char const* buf, int size); + + private: + + boost::intrusive_ptr self() + { return boost::intrusive_ptr(this); } + + void on_name_lookup(error_code const& e + , udp::resolver::iterator host); + void on_router_name_lookup(error_code const& e + , udp::resolver::iterator host); + void connection_timeout(error_code const& e); + void refresh_timeout(error_code const& e); + void tick(error_code const& e); + + // implements udp_socket_interface + virtual bool send_packet(libtorrent::entry& e, udp::endpoint const& addr + , int send_flags); + + node_impl m_dht; + rate_limited_udp_socket& m_sock; + + std::vector m_send_buf; + + ptime m_last_new_key; + deadline_timer m_timer; + deadline_timer m_connection_timer; + deadline_timer m_refresh_timer; + dht_settings const& m_settings; + int m_refresh_bucket; + + bool m_abort; + + // used to resolve hostnames for nodes + udp::resolver m_host_resolver; + + // sent and received bytes since queried last time + int m_sent_bytes; + int m_received_bytes; + + // used to ignore abusive dht nodes + struct node_ban_entry + { + node_ban_entry(): count(0) {} + address src; + ptime limit; + int count; + }; + + enum { num_ban_nodes = 20 }; + + node_ban_entry m_ban_nodes[num_ban_nodes]; + + // reference counter for intrusive_ptr + mutable boost::detail::atomic_count m_refs; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + int m_replies_sent[5]; + int m_queries_received[5]; + int m_replies_bytes_sent[5]; + int m_queries_bytes_received[5]; + int m_counter; + + int m_total_message_input; + int m_total_in_bytes; + int m_total_out_bytes; + + int m_queries_out_bytes; +#endif + }; +}} + +#endif +#endif diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/find_data.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/find_data.hpp new file mode 100644 index 0000000000..45a7ad3079 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/find_data.hpp @@ -0,0 +1,100 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef FIND_DATA_050323_HPP +#define FIND_DATA_050323_HPP + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace libtorrent { namespace dht +{ + +typedef std::vector packet_t; + +class rpc_manager; +class node_impl; + +// -------- find data ----------- + +struct find_data : traversal_algorithm +{ + typedef boost::function > const&)> nodes_callback; + + find_data(node_impl& node, node_id target + , nodes_callback const& ncallback); + + void got_write_token(node_id const& n, std::string const& write_token); + + virtual void start(); + + virtual char const* name() const; + + node_id const target() const { return m_target; } + +protected: + + virtual void done(); + virtual observer_ptr new_observer(void* ptr, udp::endpoint const& ep + , node_id const& id); + + nodes_callback m_nodes_callback; + std::map m_write_tokens; + bool m_done; +}; + +struct find_data_observer : traversal_observer +{ + find_data_observer( + boost::intrusive_ptr const& algorithm + , udp::endpoint const& ep, node_id const& id) + : traversal_observer(algorithm, ep, id) + {} + + virtual void reply(msg const&); +}; + +} } // namespace libtorrent::dht + +#endif // FIND_DATA_050323_HPP + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/get_item.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/get_item.hpp new file mode 100644 index 0000000000..ee547a2c72 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/get_item.hpp @@ -0,0 +1,92 @@ +/* + +Copyright (c) 2013, Steven Siloti +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef LIBTORRENT_GET_ITEM_HPP +#define LIBTORRENT_GET_ITEM_HPP + +#include +#include +#include + +namespace libtorrent { namespace dht +{ + +class get_item : public find_data +{ +public: + typedef boost::function data_callback; + + void got_data(lazy_entry const* v, + char const* pk, + boost::uint64_t seq, + char const* sig); + + // for immutable itms + get_item(node_impl& node + , node_id target + , data_callback const& dcallback); + + // for mutable items + get_item(node_impl& node + , char const* pk + , std::string const& salt + , data_callback const& dcallback); + + virtual char const* name() const; + +protected: + virtual observer_ptr new_observer(void* ptr, udp::endpoint const& ep, node_id const& id); + virtual bool invoke(observer_ptr o); + virtual void done(); + + void put(std::vector > const& v); + + data_callback m_data_callback; + item m_data; + std::string m_salt; +}; + +class get_item_observer : public find_data_observer +{ +public: + get_item_observer( + boost::intrusive_ptr const& algorithm + , udp::endpoint const& ep, node_id const& id) + : find_data_observer(algorithm, ep, id) + {} + + virtual void reply(msg const&); +}; + +} } // namespace libtorrent::dht + +#endif // LIBTORRENT_GET_ITEM_HPP diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/get_peers.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/get_peers.hpp new file mode 100644 index 0000000000..a80f6d6e9f --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/get_peers.hpp @@ -0,0 +1,108 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef LIBTORRENT_GET_PEERS_HPP +#define LIBTORRENT_GET_PEERS_HPP + +#include + +namespace libtorrent { namespace dht +{ + +struct get_peers : find_data +{ + typedef boost::function const&)> data_callback; + + void got_peers(std::vector const& peers); + + get_peers(node_impl& node, node_id target + , data_callback const& dcallback + , nodes_callback const& ncallback + , bool noseeds); + + virtual char const* name() const; + +protected: + virtual bool invoke(observer_ptr o); + virtual observer_ptr new_observer(void* ptr, udp::endpoint const& ep, node_id const& id); + + data_callback m_data_callback; + bool m_noseeds; +}; + +struct obfuscated_get_peers : get_peers +{ + typedef get_peers::nodes_callback done_callback; + + obfuscated_get_peers(node_impl& node, node_id target + , data_callback const& dcallback + , nodes_callback const& ncallback + , bool noseeds); + + virtual char const* name() const; + +protected: + + virtual observer_ptr new_observer(void* ptr, udp::endpoint const& ep, + node_id const& id); + virtual bool invoke(observer_ptr o); + virtual void done(); +private: + // when set to false, we no longer obfuscate + // the target hash, and send regular get_peers + bool m_obfuscated; +}; + +struct get_peers_observer : find_data_observer +{ + get_peers_observer( + boost::intrusive_ptr const& algorithm + , udp::endpoint const& ep, node_id const& id) + : find_data_observer(algorithm, ep, id) + {} + + virtual void reply(msg const&); +}; + +struct obfuscated_get_peers_observer : traversal_observer +{ + obfuscated_get_peers_observer( + boost::intrusive_ptr const& algorithm + , udp::endpoint const& ep, node_id const& id) + : traversal_observer(algorithm, ep, id) + {} + virtual void reply(msg const&); +}; + +} } // namespace libtorrent::dht + +#endif // LIBTORRENT_GET_PEERS_HPP diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/item.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/item.hpp new file mode 100644 index 0000000000..350c7e4660 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/item.hpp @@ -0,0 +1,137 @@ +/* + +Copyright (c) 2013, Steven Siloti +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef LIBTORRENT_ITEM_HPP +#define LIBTORRENT_ITEM_HPP + +#include +#include +#include +#include +#include +#include + +namespace libtorrent { namespace dht +{ + +// calculate the target hash for an immutable item. +sha1_hash TORRENT_EXTRA_EXPORT item_target_id( + std::pair v); + +// calculate the target hash for a mutable item. +sha1_hash TORRENT_EXTRA_EXPORT item_target_id(std::pair salt + , char const* pk); + +bool TORRENT_EXTRA_EXPORT verify_mutable_item( + std::pair v + , std::pair salt + , boost::uint64_t seq + , char const* pk + , char const* sig); + +// TODO: since this is a public function, it should probably be moved +// out of this header and into one with other public functions. + +// given a byte range ``v`` and an optional byte range ``salt``, a +// sequence number, public key ``pk`` (must be 32 bytes) and a secret key +// ``sk`` (must be 64 bytes), this function produces a signature which +// is written into a 64 byte buffer pointed to by ``sig``. The caller +// is responsible for allocating the destination buffer that's passed in +// as the ``sig`` argument. Typically it would be allocated on the stack. +void TORRENT_EXPORT sign_mutable_item( + std::pair v + , std::pair salt + , boost::uint64_t seq + , char const* pk + , char const* sk + , char* sig); + +enum +{ + item_pk_len = 32, + item_sk_len = 64, + item_sig_len = 64 +}; + +class TORRENT_EXTRA_EXPORT item +{ +public: + item() : m_seq(0), m_mutable(false) {} + item(char const* pk, std::string const& salt); + item(entry const& v) { assign(v); } + item(entry const& v + , std::pair salt + , boost::uint64_t seq, char const* pk, char const* sk); + item(lazy_entry const* v) { assign(v); } + + void assign(entry const& v) + { + assign(v, std::pair(static_cast(NULL) + , 0), 0, NULL, NULL); + } + void assign(entry const& v, std::pair salt + , boost::uint64_t seq, char const* pk, char const* sk); + void assign(lazy_entry const* v) + { + assign(v, std::pair(static_cast(NULL) + , 0), 0, NULL, NULL); + } + bool assign(lazy_entry const* v, std::pair salt + , boost::uint64_t seq, char const* pk, char const* sig); + void assign(entry const& v, std::string salt, boost::uint64_t seq + , char const* pk, char const* sig); + + void clear() { m_value = entry(); } + bool empty() const { return m_value.type() == entry::undefined_t; } + + bool is_mutable() const { return m_mutable; } + + entry const& value() const { return m_value; } + boost::array const& pk() const + { return m_pk; } + boost::array const& sig() const + { return m_sig; } + boost::uint64_t seq() const { return m_seq; } + std::string const& salt() const { return m_salt; } + +private: + entry m_value; + std::string m_salt; + boost::array m_pk; + boost::array m_sig; + boost::uint64_t m_seq; + bool m_mutable; +}; + +} } // namespace libtorrent::dht + +#endif // LIBTORRENT_ITEM_HPP diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/logging.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/logging.hpp new file mode 100644 index 0000000000..f10529a21e --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/logging.hpp @@ -0,0 +1,140 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_LOGGING_HPP +#define TORRENT_LOGGING_HPP + +#include "libtorrent/config.hpp" + +#if TORRENT_USE_IOSTREAM + +#include +#include +#include "libtorrent/ptime.hpp" + +namespace libtorrent { namespace dht +{ + +class log +{ +public: + log(char const* id, std::ostream& stream) + : m_id(id) + , m_enabled(true) + , m_stream(stream) + { + } + + char const* id() const + { + return m_id; + } + + bool enabled() const + { + return m_enabled; + } + + void enable(bool e) + { + m_enabled = e; + } + + void flush() { m_stream.flush(); } + + template + log& operator<<(T const& x) + { + m_stream << x; + return *this; + } + +private: + char const* m_id; + bool m_enabled; + std::ostream& m_stream; +}; + +class log_event +{ +public: + log_event(log& log); + ~log_event(); + + template + log_event& operator<<(T const& x) + { + log_ << x; + return *this; + } + + operator bool() const + { + return log_.enabled(); + } + +private: + log& log_; +}; + +class inverted_log_event : public log_event +{ +public: + inverted_log_event(log& log) : log_event(log) {} + + operator bool() const + { + return !log_event::operator bool(); + } +}; + +} } // namespace libtorrent::dht + +#define TORRENT_DECLARE_LOG(name) \ + libtorrent::dht::log& name ## _log() + +#define TORRENT_DEFINE_LOG(name) \ + libtorrent::dht::log& name ## _log() \ + { \ + static std::ofstream log_file("dht.log", std::ios::app); \ + static libtorrent::dht::log instance(#name, log_file); \ + return instance; \ + } + +#define TORRENT_LOG(name) \ + if (libtorrent::dht::inverted_log_event event_object__ = name ## _log()); \ + else static_cast(event_object__) + +#endif // TORRENT_USE_IOSTREAM + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/msg.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/msg.hpp new file mode 100644 index 0000000000..e9dc6e1c16 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/msg.hpp @@ -0,0 +1,65 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef MSG_HPP +#define MSG_HPP + +#include +#include +#include "libtorrent/lazy_entry.hpp" +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif + +namespace libtorrent { +namespace dht { + +typedef std::vector packet_t; +typedef std::vector nodes_t; +typedef std::vector peers_t; + +struct msg +{ + msg(lazy_entry const& m, udp::endpoint const& ep): message(m), addr(ep) {} + // the message + lazy_entry const& message; + + // the address of the process sending or receiving + // the message. + udp::endpoint addr; +}; + +} } + +#endif diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/node.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/node.hpp new file mode 100644 index 0000000000..52703032f4 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/node.hpp @@ -0,0 +1,330 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef NODE_HPP +#define NODE_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "libtorrent/socket.hpp" + +namespace libtorrent { + class alert_manager; + struct alert_dispatcher; +} + +namespace libtorrent { namespace dht +{ + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DECLARE_LOG(node); +#endif + +struct traversal_algorithm; +struct dht_observer; + +struct key_desc_t +{ + char const* name; + int type; + int size; + int flags; + + enum { + // this argument is optional, parsing will not + // fail if it's not present + optional = 1, + // for dictionaries, the following entries refer + // to child nodes to this node, up until and including + // the next item that has the last_child flag set. + // these flags are nestable + parse_children = 2, + // this is the last item in a child dictionary + last_child = 4, + // the size argument refers to that the size + // has to be divisible by the number, instead + // of having that exact size + size_divisible = 8 + }; +}; + +bool TORRENT_EXTRA_EXPORT verify_message(lazy_entry const* msg, key_desc_t const desc[] + , lazy_entry const* ret[], int size , char* error, int error_size); + +// this is the entry for every peer +// the timestamp is there to make it possible +// to remove stale peers +struct peer_entry +{ + ptime added; + tcp::endpoint addr; + bool seed; +}; + +// this is a group. It contains a set of group members +struct torrent_entry +{ + std::string name; + std::set peers; +}; + +struct dht_immutable_item +{ + dht_immutable_item() : value(0), num_announcers(0), size(0) {} + // malloced space for the actual value + char* value; + // this counts the number of IPs we have seen + // announcing this item, this is used to determine + // popularity if we reach the limit of items to store + bloom_filter<128> ips; + // the last time we heard about this + ptime last_seen; + // number of IPs in the bloom filter + int num_announcers; + // size of malloced space pointed to by value + int size; +}; + +struct ed25519_public_key { char bytes[item_pk_len]; }; + +struct dht_mutable_item : dht_immutable_item +{ + char sig[item_sig_len]; + boost::uint64_t seq; + ed25519_public_key key; + char* salt; + int salt_size; +}; + +// internal +inline bool operator<(ed25519_public_key const& lhs, ed25519_public_key const& rhs) +{ + return memcmp(lhs.bytes, rhs.bytes, sizeof(lhs.bytes)) < 0; +} + +// internal +inline bool operator<(peer_entry const& lhs, peer_entry const& rhs) +{ + return lhs.addr.address() == rhs.addr.address() + ? lhs.addr.port() < rhs.addr.port() + : lhs.addr.address() < rhs.addr.address(); +} + +struct null_type {}; + +class announce_observer : public observer +{ +public: + announce_observer(boost::intrusive_ptr const& algo + , udp::endpoint const& ep, node_id const& id) + : observer(algo, ep, id) + {} + + void reply(msg const&) { flags |= flag_done; } +}; + +struct count_peers +{ + int& count; + count_peers(int& c): count(c) {} + void operator()(std::pair const& t) + { + count += t.second.peers.size(); + } +}; + +struct udp_socket_interface +{ + virtual bool send_packet(entry& e, udp::endpoint const& addr, int flags) = 0; +}; + +class TORRENT_EXTRA_EXPORT node_impl : boost::noncopyable +{ +typedef std::map table_t; +typedef std::map dht_immutable_table_t; +typedef std::map dht_mutable_table_t; + +public: + node_impl(alert_dispatcher* alert_disp, udp_socket_interface* sock + , libtorrent::dht_settings const& settings, node_id nid, address const& external_address + , dht_observer* observer); + + virtual ~node_impl() {} + + void tick(); + void bootstrap(std::vector const& nodes + , find_data::nodes_callback const& f); + void add_router_node(udp::endpoint router); + + void unreachable(udp::endpoint const& ep); + void incoming(msg const& m); + + int num_torrents() const { return m_map.size(); } + int num_peers() const + { + int ret = 0; + std::for_each(m_map.begin(), m_map.end(), count_peers(ret)); + return ret; + } + + int bucket_size(int bucket); + + node_id const& nid() const { return m_id; } + + boost::tuple size() const { return m_table.size(); } + size_type num_global_nodes() const + { return m_table.num_global_nodes(); } + + int data_size() const { return int(m_map.size()); } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + void print_state(std::ostream& os) const + { m_table.print_state(os); } +#endif + + enum flags_t { flag_seed = 1, flag_implied_port = 2 }; + void announce(sha1_hash const& info_hash, int listen_port, int flags + , boost::function const&)> f); + + void get_item(sha1_hash const& target, boost::function f); + void get_item(char const* pk, std::string const& salt, boost::function f); + + bool verify_token(std::string const& token, char const* info_hash + , udp::endpoint const& addr); + + std::string generate_token(udp::endpoint const& addr, char const* info_hash); + + // the returned time is the delay until connection_timeout() + // should be called again the next time + time_duration connection_timeout(); + + // generates a new secret number used to generate write tokens + void new_write_key(); + + // pings the given node, and adds it to + // the routing table if it respons and if the + // bucket is not full. + void add_node(udp::endpoint node); + + void replacement_cache(bucket_t& nodes) const + { m_table.replacement_cache(nodes); } + + int branch_factor() const { return m_settings.search_branching; } + + void add_traversal_algorithm(traversal_algorithm* a) + { + mutex_t::scoped_lock l(m_mutex); + m_running_requests.insert(a); + } + + void remove_traversal_algorithm(traversal_algorithm* a) + { + mutex_t::scoped_lock l(m_mutex); + m_running_requests.erase(a); + } + + void status(libtorrent::session_status& s); + + libtorrent::dht_settings const& settings() const { return m_settings; } + +protected: + + void send_single_refresh(udp::endpoint const& ep, int bucket + , node_id const& id = node_id()); + void lookup_peers(sha1_hash const& info_hash, entry& reply + , bool noseed, bool scrape) const; + bool lookup_torrents(sha1_hash const& target, entry& reply + , char* tags) const; + + libtorrent::dht_settings const& m_settings; + +private: + typedef libtorrent::mutex mutex_t; + mutex_t m_mutex; + + // this list must be destructed after the rpc manager + // since it might have references to it + std::set m_running_requests; + + void incoming_request(msg const& h, entry& e); + + node_id m_id; + +public: + routing_table m_table; + rpc_manager m_rpc; + +private: + dht_observer* m_observer; + + table_t m_map; + dht_immutable_table_t m_immutable_table; + dht_mutable_table_t m_mutable_table; + + ptime m_last_tracker_tick; + + // the last time we issued a bootstrap or a refresh on our own ID, to expand + // the routing table buckets close to us. + ptime m_last_self_refresh; + + // secret random numbers used to create write tokens + int m_secret[2]; + + alert_dispatcher* m_post_alert; + udp_socket_interface* m_sock; +}; + + +} } // namespace libtorrent::dht + +#endif // NODE_HPP + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/node_entry.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/node_entry.hpp new file mode 100644 index 0000000000..6bc5acada3 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/node_entry.hpp @@ -0,0 +1,131 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef KADEMLIA_NODE_ENTRY_HPP +#define KADEMLIA_NODE_ENTRY_HPP + +#include "libtorrent/kademlia/node_id.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/union_endpoint.hpp" +#include "libtorrent/time.hpp" // for time_now() + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +#include "libtorrent/time.hpp" +#endif + +namespace libtorrent { namespace dht +{ + +struct node_entry +{ + node_entry(node_id const& id_, udp::endpoint ep, int roundtriptime = 0xffff + , bool pinged = false) + : last_queried(pinged ? time_now() : min_time()) + , id(id_) + , a(ep.address().to_v4().to_bytes()) + , p(ep.port()) + , rtt(roundtriptime & 0xffff) + , timeout_count(pinged ? 0 : 0xff) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + first_seen = time_now(); +#endif + } + + node_entry(udp::endpoint ep) + : last_queried(min_time()) + , id(0) + , a(ep.address().to_v4().to_bytes()) + , p(ep.port()) + , rtt(0xffff) + , timeout_count(0xff) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + first_seen = time_now(); +#endif + } + + node_entry() + : last_queried(min_time()) + , id(0) + , p(0) + , rtt(0xffff) + , timeout_count(0xff) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + first_seen = time_now(); +#endif + } + + bool pinged() const { return timeout_count != 0xff; } + void set_pinged() { if (timeout_count == 0xff) timeout_count = 0; } + void timed_out() { if (pinged() && timeout_count < 0xfe) ++timeout_count; } + int fail_count() const { return pinged() ? timeout_count : 0; } + void reset_fail_count() { if (pinged()) timeout_count = 0; } + udp::endpoint ep() const { return udp::endpoint(address_v4(a), p); } + bool confirmed() const { return timeout_count == 0; } + void update_rtt(int new_rtt) + { + TORRENT_ASSERT(new_rtt <= 0xffff); + TORRENT_ASSERT(new_rtt >= 0); + if (new_rtt == 0xffff) return; + if (rtt == 0xffff) rtt = new_rtt; + else rtt = int(rtt) * 2 / 3 + int(new_rtt) / 3; + } + address addr() const { return address_v4(a); } + int port() const { return p; } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + ptime first_seen; +#endif + + // the time we last received a response for a request to this peer + ptime last_queried; + + node_id id; + + address_v4::bytes_type a; + boost::uint16_t p; + + // the average RTT of this node + boost::uint16_t rtt; + + // the number of times this node has failed to + // respond in a row + boost::uint8_t timeout_count; +}; + +} } // namespace libtorrent::dht + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/node_id.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/node_id.hpp new file mode 100644 index 0000000000..d4dd84a57c --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/node_id.hpp @@ -0,0 +1,78 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ +#ifndef NODE_ID_HPP +#define NODE_ID_HPP + +#include + +#include +#include "libtorrent/config.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/address.hpp" + +namespace libtorrent { namespace dht +{ + +struct node_entry; + +typedef libtorrent::sha1_hash node_id; + +// returns the distance between the two nodes +// using the kademlia XOR-metric +node_id TORRENT_EXTRA_EXPORT distance(node_id const& n1, node_id const& n2); + +// returns true if: distance(n1, ref) < distance(n2, ref) +bool TORRENT_EXTRA_EXPORT compare_ref(node_id const& n1, node_id const& n2, node_id const& ref); + +// returns n in: 2^n <= distance(n1, n2) < 2^(n+1) +// useful for finding out which bucket a node belongs to +// the value that's returned is the number of trailing bits +// after the shared bit prefix of ``n1`` and ``n2``. +// if the first bits are different, that's 160. +int TORRENT_EXTRA_EXPORT distance_exp(node_id const& n1, node_id const& n2); + +node_id TORRENT_EXTRA_EXPORT generate_id(address const& external_ip); +node_id TORRENT_EXTRA_EXPORT generate_random_id(); +void TORRENT_EXTRA_EXPORT make_id_secret(node_id& in); +node_id TORRENT_EXTRA_EXPORT generate_secret_id(); +bool TORRENT_EXTRA_EXPORT verify_secret_id(node_id const& nid); +node_id TORRENT_EXTRA_EXPORT generate_id_impl(address const& ip_, boost::uint32_t r); + +bool TORRENT_EXTRA_EXPORT verify_id(node_id const& nid, address const& source_ip); +bool TORRENT_EXTRA_EXPORT matching_prefix(node_entry const& n, int mask, int prefix, int bucket_index); +node_id TORRENT_EXTRA_EXPORT generate_prefix_mask(int bits); + +} } // namespace libtorrent::dht + +#endif // NODE_ID_HPP + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/observer.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/observer.hpp new file mode 100644 index 0000000000..61c6d714ed --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/observer.hpp @@ -0,0 +1,181 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef OBSERVER_HPP +#define OBSERVER_HPP + +#include +#include +#include +#include +#include +#include + +namespace libtorrent { +namespace dht { + +struct observer; +struct msg; +struct traversal_algorithm; + +// defined in rpc_manager.cpp +TORRENT_EXTRA_EXPORT void intrusive_ptr_add_ref(observer const*); +TORRENT_EXTRA_EXPORT void intrusive_ptr_release(observer const*); + +// intended struct layout (on 32 bit architectures) +// offset size alignment field +// 0 8 8 sent +// 8 8 4 m_refs +// 16 4 4 pool_allocator +// 20 16 4 m_addr +// 36 2 2 m_port +// 38 1 1 flags +// 39 1 1 +// 40 + +struct observer : boost::noncopyable +{ + friend TORRENT_EXTRA_EXPORT void intrusive_ptr_add_ref(observer const*); + friend TORRENT_EXTRA_EXPORT void intrusive_ptr_release(observer const*); + + observer(boost::intrusive_ptr const& a + , udp::endpoint const& ep, node_id const& id) + : m_sent() + , m_refs(0) + , m_algorithm(a) + , m_id(id) + , m_port(0) + , m_transaction_id() + , flags(0) + { + TORRENT_ASSERT(a); +#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS + m_in_constructor = true; + m_was_sent = false; + m_was_abandoned = false; + m_in_use = true; +#endif + set_target(ep); + } + + // defined in rpc_manager.cpp + virtual ~observer(); + + // this is called when a reply is received + virtual void reply(msg const& m) = 0; + + // this is called if no response has been received after + // a few seconds, before the request has timed out + void short_timeout(); + + bool has_short_timeout() const { return (flags & flag_short_timeout) != 0; } + + // this is called when no reply has been received within + // some timeout + void timeout(); + + // if this is called the destructor should + // not invoke any new messages, and should + // only clean up. It means the rpc-manager + // is being destructed + void abort(); + + ptime sent() const { return m_sent; } + + void set_target(udp::endpoint const& ep); + address target_addr() const; + udp::endpoint target_ep() const; + + void set_id(node_id const& id); + node_id const& id() const { return m_id; } + + void set_transaction_id(boost::uint16_t tid) + { m_transaction_id = tid; } + + boost::uint16_t transaction_id() const + { return m_transaction_id; } + + enum { + flag_queried = 1, + flag_initial = 2, + flag_no_id = 4, + flag_short_timeout = 8, + flag_failed = 16, + flag_ipv6_address = 32, + flag_alive = 64, + flag_done = 128 + }; + +#ifndef TORRENT_DHT_VERBOSE_LOGGING +protected: +#endif + + void done(); + + ptime m_sent; + + // reference counter for intrusive_ptr + mutable boost::detail::atomic_count m_refs; + + const boost::intrusive_ptr m_algorithm; + + node_id m_id; + + TORRENT_UNION addr_t + { +#if TORRENT_USE_IPV6 + address_v6::bytes_type v6; +#endif + address_v4::bytes_type v4; + } m_addr; + + boost::uint16_t m_port; + + // the transaction ID for this call + boost::uint16_t m_transaction_id; +public: + unsigned char flags; + +#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS + bool m_in_constructor:1; + bool m_was_sent:1; + bool m_was_abandoned:1; + bool m_in_use:1; +#endif +}; + +typedef boost::intrusive_ptr observer_ptr; + +} } + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/refresh.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/refresh.hpp new file mode 100644 index 0000000000..3285997971 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/refresh.hpp @@ -0,0 +1,71 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef REFRESH_050324_HPP +#define REFRESH_050324_HPP + +#include +#include +#include + +namespace libtorrent { namespace dht +{ + +class routing_table; +class rpc_manager; + +class bootstrap : public get_peers +{ +public: + typedef get_peers::nodes_callback done_callback; + + bootstrap(node_impl& node, node_id target + , done_callback const& callback); + virtual char const* name() const; + + observer_ptr new_observer(void* ptr, udp::endpoint const& ep + , node_id const& id); + + void trim_seed_nodes(); + +protected: + + virtual bool invoke(observer_ptr o); + + virtual void done(); + +}; + +} } // namespace libtorrent::dht + +#endif // REFRESH_050324_HPP + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/routing_table.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/routing_table.hpp new file mode 100644 index 0000000000..b40fb76989 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/routing_table.hpp @@ -0,0 +1,235 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef ROUTING_TABLE_HPP +#define ROUTING_TABLE_HPP + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace libtorrent +{ + struct session_status; +} + +namespace libtorrent { namespace dht +{ + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DECLARE_LOG(table); +#endif + + +typedef std::vector bucket_t; + +struct routing_table_node +{ + bucket_t replacements; + bucket_t live_nodes; +}; + +// differences in the implementation from the description in +// the paper: +// +// * Nodes are not marked as being stale, they keep a counter +// that tells how many times in a row they have failed. When +// a new node is to be inserted, the node that has failed +// the most times is replaced. If none of the nodes in the +// bucket has failed, then it is put in the replacement +// cache (just like in the paper). + +class TORRENT_EXTRA_EXPORT routing_table : boost::noncopyable +{ +public: + typedef std::vector table_t; + + routing_table(node_id const& id, int bucket_size + , dht_settings const& settings); + + void status(session_status& s) const; + + void node_failed(node_id const& id, udp::endpoint const& ep); + + // adds an endpoint that will never be added to + // the routing table + void add_router_node(udp::endpoint router); + + // iterates over the router nodes added + typedef std::set::const_iterator router_iterator; + router_iterator router_begin() const { return m_router_nodes.begin(); } + router_iterator router_end() const { return m_router_nodes.end(); } + + enum add_node_status_t { + failed_to_add = 0, + node_added, + need_bucket_split + }; + add_node_status_t add_node_impl(node_entry e); + + bool add_node(node_entry e); + + // this function is called every time the node sees + // a sign of a node being alive. This node will either + // be inserted in the k-buckets or be moved to the top + // of its bucket. + bool node_seen(node_id const& id, udp::endpoint ep, int rtt); + + // this may add a node to the routing table and mark it as + // not pinged. If the bucket the node falls into is full, + // the node will be ignored. + void heard_about(node_id const& id, udp::endpoint const& ep); + + node_entry const* next_refresh(); + + enum + { + // nodes that have not been pinged are considered failed by this flag + include_failed = 1 + }; + + // fills the vector with the count nodes from our buckets that + // are nearest to the given id. + void find_node(node_id const& id, std::vector& l + , int options, int count = 0); + void remove_node(node_entry* n + , table_t::iterator bucket) ; + + int bucket_size(int bucket) const + { + int num_buckets = m_buckets.size(); + if (num_buckets == 0) return 0; + if (bucket < num_buckets) bucket = num_buckets - 1; + table_t::const_iterator i = m_buckets.begin(); + std::advance(i, bucket); + return (int)i->live_nodes.size(); + } + + void for_each_node(void (*)(void*, node_entry const&) + , void (*)(void*, node_entry const&), void* userdata) const; + + int bucket_size() const { return m_bucket_size; } + + // returns the number of nodes in the main buckets, number of nodes in the + // replacement buckets and the number of nodes in the main buckets that have + // been pinged and confirmed up + boost::tuple size() const; + + size_type num_global_nodes() const; + + // the number of bits down we have full buckets + // i.e. essentially the number of full buckets + // we have + int depth() const; + + int num_active_buckets() const { return m_buckets.size(); } + + void replacement_cache(bucket_t& nodes) const; + +#if defined TORRENT_DHT_VERBOSE_LOGGING || defined TORRENT_DEBUG + // used for debug and monitoring purposes. This will print out + // the state of the routing table to the given stream + void print_state(std::ostream& os) const; +#endif + + int bucket_limit(int bucket) const; + +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + +private: + + table_t::iterator find_bucket(node_id const& id); + + void split_bucket(); + + // return a pointer the node_entry with the given endpoint + // or 0 if we don't have such a node. Both the address and the + // port has to match + node_entry* find_node(udp::endpoint const& ep + , routing_table::table_t::iterator* bucket); + + dht_settings const& m_settings; + + // constant called k in paper + int m_bucket_size; + + // (k-bucket, replacement cache) pairs + // the first entry is the bucket the furthest + // away from our own ID. Each time the bucket + // closest to us (m_buckets.back()) has more than + // bucket size nodes in it, another bucket is + // added to the end and it's split up between them + table_t m_buckets; + + node_id m_id; // our own node id + + // the last seen depth (i.e. levels in the routing table) + // it's mutable because it's updated by depth(), which is const + mutable int m_depth; + + // the last time we refreshed our own bucket + // refreshed every 15 minutes + mutable ptime m_last_self_refresh; + + // this is a set of all the endpoints that have + // been identified as router nodes. They will + // be used in searches, but they will never + // be added to the routing table. + std::set m_router_nodes; + + // these are all the IPs that are in the routing + // table. It's used to only allow a single entry + // per IP in the whole table. Currently only for + // IPv4 + std::multiset m_ips; +}; + +} } // namespace libtorrent::dht + +#endif // ROUTING_TABLE_HPP + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/rpc_manager.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/rpc_manager.hpp new file mode 100644 index 0000000000..4e277079c0 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/rpc_manager.hpp @@ -0,0 +1,126 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef RPC_MANAGER_HPP +#define RPC_MANAGER_HPP + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "libtorrent/ptime.hpp" + +namespace libtorrent { namespace aux { struct session_impl; } } + +namespace libtorrent { struct dht_settings; } + +namespace libtorrent { namespace dht +{ + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DECLARE_LOG(rpc); +#endif + +struct udp_socket_interface; + +struct null_observer : public observer +{ + null_observer(boost::intrusive_ptr const& a + , udp::endpoint const& ep, node_id const& id): observer(a, ep, id) {} + virtual void reply(msg const&) { flags |= flag_done; } +}; + +class routing_table; + +class TORRENT_EXTRA_EXPORT rpc_manager +{ +public: + + rpc_manager(node_id const& our_id + , routing_table& table, udp_socket_interface* sock); + ~rpc_manager(); + + void unreachable(udp::endpoint const& ep); + + // returns true if the node needs a refresh + // if so, id is assigned the node id to refresh + bool incoming(msg const&, node_id* id, libtorrent::dht_settings const& settings); + time_duration tick(); + + bool invoke(entry& e, udp::endpoint target + , observer_ptr o); + + void add_our_id(entry& e); + +#if TORRENT_USE_ASSERTS + size_t allocation_size() const; +#endif +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + + void* allocate_observer(); + void free_observer(void* ptr); + + int num_allocated_observers() const { return m_allocated_observers; } + +private: + + boost::uint32_t calc_connection_id(udp::endpoint addr); + + mutable boost::pool<> m_pool_allocator; + + typedef std::deque transactions_t; + transactions_t m_transactions; + + udp_socket_interface* m_sock; + routing_table& m_table; + ptime m_timer; + node_id m_our_id; + int m_allocated_observers; + bool m_destructing; +}; + +} } // namespace libtorrent::dht + +#endif + + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/traversal_algorithm.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/traversal_algorithm.hpp new file mode 100644 index 0000000000..def31d8adc --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/traversal_algorithm.hpp @@ -0,0 +1,143 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TRAVERSAL_ALGORITHM_050324_HPP +#define TRAVERSAL_ALGORITHM_050324_HPP + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace libtorrent { struct dht_lookup; } +namespace libtorrent { namespace dht +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DECLARE_LOG(traversal); +#endif + +class rpc_manager; +class node_impl; + +// this class may not be instantiated as a stack object +struct traversal_algorithm : boost::noncopyable +{ + void traverse(node_id const& id, udp::endpoint addr); + void finished(observer_ptr o); + + enum flags_t { prevent_request = 1, short_timeout = 2 }; + void failed(observer_ptr o, int flags = 0); + virtual ~traversal_algorithm(); + void status(dht_lookup& l); + void abort(); + + void* allocate_observer(); + void free_observer(void* ptr); + + virtual char const* name() const; + virtual void start(); + + node_id const& target() const { return m_target; } + + void resort_results(); + void add_entry(node_id const& id, udp::endpoint addr, unsigned char flags); + + traversal_algorithm(node_impl& node, node_id target); + int invoke_count() const { return m_invoke_count; } + int branch_factor() const { return m_branch_factor; } + + node_impl& node() const { return m_node; } + +protected: + + // returns true if we're done + bool add_requests(); + + void add_router_entries(); + void init(); + + virtual void done(); + // should construct an algorithm dependent + // observer in ptr. + virtual observer_ptr new_observer(void* ptr + , udp::endpoint const& ep, node_id const& id); + + virtual bool invoke(observer_ptr o) { return false; } + + friend void intrusive_ptr_add_ref(traversal_algorithm* p) + { + p->m_ref_count++; + } + + friend void intrusive_ptr_release(traversal_algorithm* p) + { + if (--p->m_ref_count == 0) + delete p; + } + + int m_ref_count; + + node_impl& m_node; + node_id const m_target; + std::vector m_results; + int m_invoke_count; + int m_branch_factor; + int m_responses; + int m_timeouts; + int m_num_target_nodes; +}; + +struct traversal_observer : observer +{ + traversal_observer( + boost::intrusive_ptr const& algorithm + , udp::endpoint const& ep, node_id const& id) + : observer(algorithm, ep, id) + {} + + // parses out "nodes" and keeps traversing + virtual void reply(msg const&); +}; + +} } // namespace libtorrent::dht + +#endif // TRAVERSAL_ALGORITHM_050324_HPP + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/lazy_entry.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/lazy_entry.hpp new file mode 100644 index 0000000000..8c029c6714 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/lazy_entry.hpp @@ -0,0 +1,453 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_LAZY_ENTRY_HPP_INCLUDED +#define TORRENT_LAZY_ENTRY_HPP_INCLUDED + +#include +#include +#include +#include +#include +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/error_code.hpp" + +namespace libtorrent +{ + struct lazy_entry; + + // This function decodes bencoded_ data. + // + // .. _bencoded: http://wiki.theory.org/index.php/BitTorrentSpecification + // + // Whenever possible, ``lazy_bdecode()`` should be preferred over ``bdecode()``. + // It is more efficient and more secure. It supports having constraints on the + // amount of memory is consumed by the parser. + // + // *lazy* refers to the fact that it doesn't copy any actual data out of the + // bencoded buffer. It builds a tree of ``lazy_entry`` which has pointers into + // the bencoded buffer. This makes it very fast and efficient. On top of that, + // it is not recursive, which saves a lot of stack space when parsing deeply + // nested trees. However, in order to protect against potential attacks, the + // ``depth_limit`` and ``item_limit`` control how many levels deep the tree is + // allowed to get. With recursive parser, a few thousand levels would be enough + // to exhaust the threads stack and terminate the process. The ``item_limit`` + // protects against very large structures, not necessarily deep. Each bencoded + // item in the structure causes the parser to allocate some amount of memory, + // this memory is constant regardless of how much data actually is stored in + // the item. One potential attack is to create a bencoded list of hundreds of + // thousands empty strings, which would cause the parser to allocate a significant + // amount of memory, perhaps more than is available on the machine, and effectively + // provide a denial of service. The default item limit is set as a reasonable + // upper limit for desktop computers. Very few torrents have more items in them. + // The limit corresponds to about 25 MB, which might be a bit much for embedded + // systems. + // + // ``start`` and ``end`` defines the bencoded buffer to be decoded. ``ret`` is + // the ``lazy_entry`` which is filled in with the whole decoded tree. ``ec`` + // is a reference to an ``error_code`` which is set to describe the error encountered + // in case the function fails. ``error_pos`` is an optional pointer to an int, + // which will be set to the byte offset into the buffer where an error occurred, + // in case the function fails. + TORRENT_EXPORT int lazy_bdecode(char const* start, char const* end + , lazy_entry& ret, error_code& ec, int* error_pos = 0 + , int depth_limit = 1000, int item_limit = 1000000); + +#ifndef TORRENT_NO_DEPRECATE + // for backwards compatibility, does not report error code + // deprecated in 0.16 + TORRENT_DEPRECATED_PREFIX + TORRENT_EXPORT int lazy_bdecode(char const* start, char const* end + , lazy_entry& ret, int depth_limit = 1000, int item_limit = 1000000) TORRENT_DEPRECATED; +#endif + + // this is a string that is not NULL-terminated. Instead it + // comes with a length, specified in bytes. This is particularly + // useful when parsing bencoded structures, because strings are + // not NULL-terminated internally, and requiring NULL termination + // would require copying the string. + // + // see lazy_entry::string_pstr(). + struct TORRENT_EXPORT pascal_string + { + // construct a string pointing to the characters at ``p`` + // of length ``l`` characters. No NULL termination is required. + pascal_string(char const* p, int l): len(l), ptr(p) {} + + // the number of characters in the string. + int len; + + // the pointer to the first character in the string. This is + // not NULL terminated, but instead consult the ``len`` field + // to know how many characters follow. + char const* ptr; + + // lexicographical comparison of strings. Order is consisten + // with memcmp. + bool operator<(pascal_string const& rhs) const + { + return std::memcmp(ptr, rhs.ptr, (std::min)(len, rhs.len)) < 0 + || len < rhs.len; + } + }; + + struct lazy_dict_entry; + + // this object represent a node in a bencoded structure. It is a variant + // type whose concrete type is one of: + // + // 1. dictionary (maps strings -> lazy_entry) + // 2. list (sequence of lazy_entry, i.e. heterogenous) + // 3. integer + // 4. string + // + // There is also a ``none`` type, which is used for uninitialized + // lazy_entries. + struct TORRENT_EXPORT lazy_entry + { + // The different types a lazy_entry can have + enum entry_type_t + { + none_t, dict_t, list_t, string_t, int_t + }; + + // internal + lazy_entry() : m_begin(0), m_len(0), m_size(0), m_capacity(0), m_type(none_t) + { m_data.start = 0; } + + // tells you which specific type this lazy entry has. + // See entry_type_t. The type determines which subset of + // member functions are valid to use. + entry_type_t type() const { return (entry_type_t)m_type; } + + // start points to the first decimal digit + // length is the number of digits + void construct_int(char const* start, int length) + { + TORRENT_ASSERT(m_type == none_t); + m_type = int_t; + m_data.start = start; + m_size = length; + m_begin = start - 1; // include 'i' + m_len = length + 2; // include 'e' + } + + // requires the type to be an integer. return the integer value + boost::int64_t int_value() const; + + // internal + void construct_string(char const* start, int length); + + // the string is not null-terminated! + // use string_length() to determine how many bytes + // are part of the string. + char const* string_ptr() const + { + TORRENT_ASSERT(m_type == string_t); + return m_data.start; + } + + // this will return a null terminated string + // it will write to the source buffer! + char const* string_cstr() const + { + TORRENT_ASSERT(m_type == string_t); + const_cast(m_data.start)[m_size] = 0; + return m_data.start; + } + + // if this is a string, returns a pascal_string + // representing the string value. + pascal_string string_pstr() const + { + TORRENT_ASSERT(m_type == string_t); + return pascal_string(m_data.start, m_size); + } + + // if this is a string, returns the string as a std::string. + // (which requires a copy) + std::string string_value() const + { + TORRENT_ASSERT(m_type == string_t); + return std::string(m_data.start, m_size); + } + + // if the lazy_entry is a string, returns the + // length of the string, in bytes. + int string_length() const + { return m_size; } + + // internal + void construct_dict(char const* begin) + { + TORRENT_ASSERT(m_type == none_t); + m_type = dict_t; + m_size = 0; + m_capacity = 0; + m_begin = begin; + } + + // internal + lazy_entry* dict_append(char const* name); + // internal + void pop(); + + // if this is a dictionary, look for a key ``name``, and return + // a pointer to its value, or NULL if there is none. + lazy_entry* dict_find(char const* name); + lazy_entry const* dict_find(char const* name) const + { return const_cast(this)->dict_find(name); } + lazy_entry* dict_find(std::string const& name); + lazy_entry const* dict_find(std::string const& name) const + { return const_cast(this)->dict_find(name); } + lazy_entry const* dict_find_string(char const* name) const; + + // if this is a dictionary, look for a key ``name`` whose value + // is a string. If such key exist, return a pointer to + // its value, otherwise NULL. + std::string dict_find_string_value(char const* name) const; + pascal_string dict_find_pstr(char const* name) const; + + // if this is a dictionary, look for a key ``name`` whose value + // is an int. If such key exist, return a pointer to its value, + // otherwise NULL. + boost::int64_t dict_find_int_value(char const* name, boost::int64_t default_val = 0) const; + lazy_entry const* dict_find_int(char const* name) const; + + // these functions require that ``this`` is a dictionary. + // (this->type() == dict_t). They look for an element with the + // specified name in the dictionary. ``dict_find_dict`` only + // finds dictionaries and ``dict_find_list`` only finds lists. + // if no key with the corresponding value of the right type is + // found, NULL is returned. + lazy_entry const* dict_find_dict(char const* name) const; + lazy_entry const* dict_find_dict(std::string const& name) const; + lazy_entry const* dict_find_list(char const* name) const; + + // if this is a dictionary, return the key value pair at + // position ``i`` from the dictionary. + std::pair dict_at(int i) const; + + // requires that ``this`` is a dictionary. return the + // number of items in it + int dict_size() const + { + TORRENT_ASSERT(m_type == dict_t); + return m_size; + } + + // internal + void construct_list(char const* begin) + { + TORRENT_ASSERT(m_type == none_t); + m_type = list_t; + m_size = 0; + m_capacity = 0; + m_begin = begin; + } + + // internal + lazy_entry* list_append(); + + // requires that ``this`` is a list. return + // the item at index ``i``. + lazy_entry* list_at(int i) + { + TORRENT_ASSERT(m_type == list_t); + TORRENT_ASSERT(i < int(m_size)); + return &m_data.list[i]; + } + lazy_entry const* list_at(int i) const + { return const_cast(this)->list_at(i); } + + // these functions require ``this`` to have the type list. + // (this->type() == list_t). ``list_string_value_at`` returns + // the string at index ``i``. ``list_pstr_at`` + // returns a pascal_string of the string value at index ``i``. + // if the element at ``i`` is not a string, an empty string + // is returned. + std::string list_string_value_at(int i) const; + pascal_string list_pstr_at(int i) const; + + // this function require ``this`` to have the type list. + // (this->type() == list_t). returns the integer value at + // index ``i``. If the element at ``i`` is not an integer + // ``default_val`` is returned, which defaults to 0. + boost::int64_t list_int_value_at(int i, boost::int64_t default_val = 0) const; + + // if this is a list, return the number of items in it. + int list_size() const + { + TORRENT_ASSERT(m_type == list_t); + return int(m_size); + } + + // internal: end points one byte passed last byte in the source + // buffer backing the bencoded structure. + void set_end(char const* end) + { + TORRENT_ASSERT(end > m_begin); + TORRENT_ASSERT(end - m_begin < INT_MAX); + m_len = int(end - m_begin); + } + + // internal + void clear(); + + // internal: releases ownership of any memory allocated + void release() + { + m_data.start = 0; + m_size = 0; + m_capacity = 0; + m_type = none_t; + } + + // internal + ~lazy_entry() + { clear(); } + + // returns pointers into the source buffer where + // this entry has its bencoded data + std::pair data_section() const; + + // swap values of ``this`` and ``e``. + void swap(lazy_entry& e) + { + using std::swap; + boost::uint32_t tmp = e.m_type; + e.m_type = m_type; + m_type = tmp; + tmp = e.m_capacity; + e.m_capacity = m_capacity; + m_capacity = tmp; + swap(m_data.start, e.m_data.start); + swap(m_size, e.m_size); + swap(m_begin, e.m_begin); + swap(m_len, e.m_len); + } + + private: + + union data_t + { + lazy_dict_entry* dict; + lazy_entry* list; + char const* start; + } m_data; + + // used for dictionaries and lists to record the range + // in the original buffer they are based on + char const* m_begin; + // the number of bytes this entry extends in the + // bencoded byffer + boost::uint32_t m_len; + + // if list or dictionary, the number of items + boost::uint32_t m_size; + // if list or dictionary, allocated number of items + boost::uint32_t m_capacity:29; + // element type (dict, list, int, string) + boost::uint32_t m_type:3; + + // non-copyable + lazy_entry(lazy_entry const&); + lazy_entry const& operator=(lazy_entry const&); + }; + + struct lazy_dict_entry + { + char const* name; + lazy_entry val; + }; + + // print the bencoded structure in a human-readable format to a stting + // that's returned. + TORRENT_EXPORT std::string print_entry(lazy_entry const& e + , bool single_line = false, int indent = 0); + + // get the ``error_category`` for bdecode errors + TORRENT_EXPORT boost::system::error_category& get_bdecode_category(); + + namespace bdecode_errors + { + // libtorrent uses boost.system's ``error_code`` class to represent errors. libtorrent has + // its own error category get_bdecode_category() whith the error codes defined by error_code_enum. + enum error_code_enum + { + // Not an error + no_error = 0, + // expected string in bencoded string + expected_string, + // expected colon in bencoded string + expected_colon, + // unexpected end of file in bencoded string + unexpected_eof, + // expected value (list, dict, int or string) in bencoded string + expected_value, + // bencoded recursion depth limit exceeded + depth_exceeded, + // bencoded item count limit exceeded + limit_exceeded, + // integer overflow + overflow, + + // the number of error codes + error_code_max + }; + + // hidden + TORRENT_EXPORT boost::system::error_code make_error_code(error_code_enum e); + } + + TORRENT_EXTRA_EXPORT char const* parse_int(char const* start + , char const* end, char delimiter, boost::int64_t& val + , bdecode_errors::error_code_enum& ec); + +} + +#if BOOST_VERSION >= 103500 + +namespace boost { namespace system { + + template<> struct is_error_code_enum + { static const bool value = true; }; + + template<> struct is_error_condition_enum + { static const bool value = true; }; +} } + +#endif + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/lsd.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/lsd.hpp new file mode 100644 index 0000000000..37a7aec2b9 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/lsd.hpp @@ -0,0 +1,109 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_LSD_HPP +#define TORRENT_LSD_HPP + +#include "libtorrent/socket.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/broadcast_socket.hpp" +#include "libtorrent/intrusive_ptr_base.hpp" +#include "libtorrent/deadline_timer.hpp" + +#include +#include +#include + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) +#include +#endif + +namespace libtorrent +{ + +typedef boost::function peer_callback_t; + +class lsd : public intrusive_ptr_base +{ +public: + lsd(io_service& ios, address const& listen_interface + , peer_callback_t const& cb); + ~lsd(); + +// void rebind(address const& listen_interface); + + void announce(sha1_hash const& ih, int listen_port, bool broadcast = false); + void close(); + +private: + + void announce_impl(sha1_hash const& ih, int listen_port + , bool broadcast, int retry_count); + void resend_announce(error_code const& e, sha1_hash const& ih + , int listen_port, int retry_count); + void on_announce(udp::endpoint const& from, char* buffer + , std::size_t bytes_transferred); + + peer_callback_t m_callback; + + // the udp socket used to send and receive + // multicast messages on + broadcast_socket m_socket; +#if TORRENT_USE_IPV6 + broadcast_socket m_socket6; +#endif + + // used to resend udp packets in case + // they time out + deadline_timer m_broadcast_timer; + + // this is a random (presumably unique) + // ID for this LSD node. It is used to + // ignore our own broadcast messages. + // There's no point in adding ourselves + // as a peer + int m_cookie; + + bool m_disabled; +#if TORRENT_USE_IPV6 + bool m_disabled6; +#endif +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + FILE* m_log; +#endif +}; + +} + + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/magnet_uri.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/magnet_uri.hpp new file mode 100644 index 0000000000..74024cf937 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/magnet_uri.hpp @@ -0,0 +1,84 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_MAGNET_URI_HPP_INCLUDED +#define TORRENT_MAGNET_URI_HPP_INCLUDED + +#include +#include "libtorrent/config.hpp" +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/add_torrent_params.hpp" + +namespace libtorrent +{ + struct torrent_handle; + class session; + + // Generates a magnet URI from the specified torrent. If the torrent + // handle is invalid, an empty string is returned. + // + // For more information about magnet links, see magnet-links_. + // + std::string TORRENT_EXPORT make_magnet_uri(torrent_handle const& handle); + std::string TORRENT_EXPORT make_magnet_uri(torrent_info const& info); + +#ifndef TORRENT_NO_DEPRECATE +#ifndef BOOST_NO_EXCEPTIONS + // deprecated in 0.14 + TORRENT_DEPRECATED_PREFIX + torrent_handle TORRENT_EXPORT add_magnet_uri(session& ses, std::string const& uri + , std::string const& save_path + , storage_mode_t storage_mode = storage_mode_sparse + , bool paused = false + , storage_constructor_type sc = default_storage_constructor + , void* userdata = 0) TORRENT_DEPRECATED; + + // deprecated in 0.16. Instead, pass in the magnet link as add_torrent_params::url + TORRENT_DEPRECATED_PREFIX + torrent_handle TORRENT_EXPORT add_magnet_uri(session& ses, std::string const& uri + , add_torrent_params p) TORRENT_DEPRECATED; +#endif + + // deprecated in 0.16. Instead, pass in the magnet link as add_torrent_params::url + TORRENT_DEPRECATED_PREFIX + torrent_handle TORRENT_EXPORT add_magnet_uri(session& ses, std::string const& uri + , add_torrent_params p, error_code& ec) TORRENT_DEPRECATED; + +#endif + + // This function parses out information from the magnet link and populates the + // add_torrent_params object. + TORRENT_EXPORT void parse_magnet_uri(std::string const& uri, add_torrent_params& p, error_code& ec); +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/max.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/max.hpp new file mode 100644 index 0000000000..f6c88002cd --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/max.hpp @@ -0,0 +1,123 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_MAX_TYPE +#define TORRENT_MAX_TYPE + +namespace libtorrent +{ + + template + struct max { enum { value = v1>v2?v1:v2 }; }; + + template + struct max3 + { + enum + { + temp = max::value, + value = max::value + }; + }; + + template + struct max4 + { + enum + { + temp1 = max::value, + temp2 = max::value, + value = max::value + }; + }; + + template + struct max5 + { + enum + { + temp = max4::value, + value = max::value + }; + }; + + template + struct max6 + { + enum + { + temp1 = max::value, + temp2 = max::value, + temp3 = max::value, + value = max3::value + }; + }; + + template + struct max7 + { + enum + { + temp1 = max::value, + temp2 = max::value, + temp3 = max3::value, + value = max3::value + }; + }; + + template + struct max8 + { + enum + { + temp1 = max::value, + temp2 = max3::value, + temp3 = max3::value, + value = max3::value + }; + }; + + template + struct max9 + { + enum + { + temp1 = max3::value, + temp2 = max3::value, + temp3 = max3::value, + value = max3::value + }; + }; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/natpmp.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/natpmp.hpp new file mode 100644 index 0000000000..81c9198242 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/natpmp.hpp @@ -0,0 +1,177 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_NATPMP_HPP +#define TORRENT_NATPMP_HPP + +#include "libtorrent/io_service_fwd.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/thread.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/intrusive_ptr_base.hpp" +#include "libtorrent/deadline_timer.hpp" + +#include +#include + +namespace libtorrent +{ + +// int: port mapping index +// int: external port +// std::string: error message +typedef boost::function portmap_callback_t; +typedef boost::function log_callback_t; + +class natpmp : public intrusive_ptr_base +{ +public: + natpmp(io_service& ios, address const& listen_interface + , portmap_callback_t const& cb + , log_callback_t const& lcb); + + void rebind(address const& listen_interface); + + // maps the ports, if a port is set to 0 + // it will not be mapped + enum protocol_type { none = 0, udp = 1, tcp = 2 }; + int add_mapping(protocol_type p, int external_port, int local_port); + void delete_mapping(int mapping_index); + bool get_mapping(int mapping_index, int& local_port, int& external_port, int& protocol) const; + + void close(); + +private: + + void update_mapping(int i, mutex::scoped_lock& l); + void send_map_request(int i, mutex::scoped_lock& l); + void send_get_ip_address_request(mutex::scoped_lock& l); + void resend_request(int i, error_code const& e); + void on_reply(error_code const& e + , std::size_t bytes_transferred); + void try_next_mapping(int i, mutex::scoped_lock& l); + void update_expiration_timer(mutex::scoped_lock& l); + void mapping_expired(error_code const& e, int i); + void close_impl(mutex::scoped_lock& l); + + void log(char const* msg, mutex::scoped_lock& l); + void disable(error_code const& ec, mutex::scoped_lock& l); + + struct mapping_t + { + enum action_t { action_none, action_add, action_delete }; + mapping_t() + : action(action_none) + , local_port(0) + , external_port(0) + , protocol(none) + , map_sent(false) + , outstanding_request(false) + {} + + // indicates that the mapping has changed + // and needs an update + int action; + + // the time the port mapping will expire + ptime expires; + + // the local port for this mapping. If this is set + // to 0, the mapping is not in use + int local_port; + + // the external (on the NAT router) port + // for the mapping. This is the port we + // should announce to others + int external_port; + + int protocol; + + // set to true when the first map request is sent + bool map_sent; + + // set to true while we're waiting for a response + bool outstanding_request; + }; + + portmap_callback_t m_callback; + log_callback_t m_log_callback; + + std::vector m_mappings; + + // the endpoint to the nat router + udp::endpoint m_nat_endpoint; + + // this is the mapping that is currently + // being updated. It is -1 in case no + // mapping is being updated at the moment + int m_currently_mapping; + + // current retry count + int m_retry_count; + + // used to receive responses in + char m_response_buffer[16]; + + // router external IP address + address m_external_ip; + + // the endpoint we received the message from + udp::endpoint m_remote; + + // the udp socket used to communicate + // with the NAT router + datagram_socket m_socket; + + // used to resend udp packets in case + // they time out + deadline_timer m_send_timer; + + // timer used to refresh mappings + deadline_timer m_refresh_timer; + + // the mapping index that will expire next + int m_next_refresh; + + bool m_disabled; + + bool m_abort; + + mutable mutex m_mutex; +}; + +} + + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/packet_buffer.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/packet_buffer.hpp new file mode 100644 index 0000000000..10b909958d --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/packet_buffer.hpp @@ -0,0 +1,117 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg, Daniel Wallin. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PACKET_BUFFER_HPP_INCLUDED +#define TORRENT_PACKET_BUFFER_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include "boost/cstdint.hpp" +#include + +namespace libtorrent +{ + // this is a circular buffer that automatically resizes + // itself as elements are inserted. Elements are indexed + // by integers and are assumed to be sequential. Unless the + // old elements are removed when new elements are inserted, + // the buffer will be resized. + + // m_capacity is the number of elements in m_array + // and must be an even 2^x. + // m_first is the lowest index that has an element + // it also determines which indices the other slots + // refers to. Since it's a circular buffer, it wraps + // around. For example + + // m_first = 9 + // | refers to index 14 + // | | + // V V + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | | | | | | | | | | | | | | | | mask = (m_capacity-1) + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ^ + // | + // refers to index 15 + + // whenever the element at the cursor is removed, the + // cursor is bumped to the next occupied element + + class TORRENT_EXTRA_EXPORT packet_buffer + { + public: + typedef boost::uint32_t index_type; + + packet_buffer(); + ~packet_buffer(); + + void* insert(index_type idx, void* value); + + std::size_t size() const + { return m_size; } + + std::size_t capacity() const + { return m_capacity; } + + void* at(index_type idx) const; + + void* remove(index_type idx); + + void reserve(std::size_t size); + + index_type cursor() const + { return m_first; } + + index_type span() const + { return (m_last - m_first) & 0xffff; } + +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + + private: + void** m_storage; + std::size_t m_capacity; + + // this is the total number of elements that are occupied + // in the array + std::size_t m_size; + + // This defines the first index that is part of the m_storage. + // last is one passed the last used slot + index_type m_first; + index_type m_last; + }; +} + +#endif // TORRENT_PACKET_BUFFER_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/parse_url.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/parse_url.hpp new file mode 100644 index 0000000000..f4a6ac8b72 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/parse_url.hpp @@ -0,0 +1,61 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PARSE_URL_HPP_INCLUDED +#define TORRENT_PARSE_URL_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include "libtorrent/config.hpp" +#include "libtorrent/error_code.hpp" + +namespace libtorrent +{ + + // returns protocol, auth, hostname, port, path + TORRENT_EXTRA_EXPORT boost::tuple + parse_url_components(std::string url, error_code& ec); + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/pe_crypto.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/pe_crypto.hpp new file mode 100644 index 0000000000..fbd2b2a28f --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/pe_crypto.hpp @@ -0,0 +1,205 @@ +/* + +Copyright (c) 2007-2014, Un Shyam & Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISABLE_ENCRYPTION + +#ifndef TORRENT_PE_CRYPTO_HPP_INCLUDED +#define TORRENT_PE_CRYPTO_HPP_INCLUDED + +#include "libtorrent/config.hpp" + +#ifdef TORRENT_USE_GCRYPT +#include +#elif defined TORRENT_USE_OPENSSL +#include +#else +// RC4 state from libtomcrypt +struct rc4 { + int x, y; + unsigned char buf[256]; +}; + +void TORRENT_EXTRA_EXPORT rc4_init(const unsigned char* in, unsigned long len, rc4 *state); +unsigned long TORRENT_EXTRA_EXPORT rc4_encrypt(unsigned char *out, unsigned long outlen, rc4 *state); +#endif + +#include "libtorrent/peer_id.hpp" // For sha1_hash +#include "libtorrent/assert.hpp" + +namespace libtorrent +{ + class TORRENT_EXTRA_EXPORT dh_key_exchange + { + public: + dh_key_exchange(); + bool good() const { return true; } + + // Get local public key, always 96 bytes + char const* get_local_key() const; + + // read remote_pubkey, generate and store shared secret in + // m_dh_shared_secret. + int compute_secret(const char* remote_pubkey); + + char const* get_secret() const { return m_dh_shared_secret; } + + sha1_hash const& get_hash_xor_mask() const { return m_xor_mask; } + + private: + + int get_local_key_size() const + { return sizeof(m_dh_local_key); } + + char m_dh_local_key[96]; + char m_dh_local_secret[96]; + char m_dh_shared_secret[96]; + sha1_hash m_xor_mask; + }; + + struct encryption_handler + { + virtual void set_incoming_key(unsigned char const* key, int len) = 0; + virtual void set_outgoing_key(unsigned char const* key, int len) = 0; + virtual void encrypt(char* pos, int len) = 0; + virtual void decrypt(char* pos, int len) = 0; + virtual ~encryption_handler() {} + }; + + struct rc4_handler : encryption_handler + { + public: + // Input longkeys must be 20 bytes + rc4_handler() + : m_encrypt(false) + , m_decrypt(false) + { +#ifdef TORRENT_USE_GCRYPT + gcry_cipher_open(&m_rc4_incoming, GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM, 0); + gcry_cipher_open(&m_rc4_outgoing, GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM, 0); +#endif + }; + + void set_incoming_key(unsigned char const* key, int len) + { + m_decrypt = true; +#ifdef TORRENT_USE_GCRYPT + gcry_cipher_close(m_rc4_incoming); + gcry_cipher_open(&m_rc4_incoming, GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM, 0); + gcry_cipher_setkey(m_rc4_incoming, key, len); +#elif defined TORRENT_USE_OPENSSL + RC4_set_key(&m_remote_key, len, key); +#else + rc4_init(key, len, &m_rc4_incoming); +#endif + // Discard first 1024 bytes + char buf[1024]; + decrypt(buf, 1024); + } + + void set_outgoing_key(unsigned char const* key, int len) + { + m_encrypt = true; +#ifdef TORRENT_USE_GCRYPT + gcry_cipher_close(m_rc4_outgoing); + gcry_cipher_open(&m_rc4_outgoing, GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM, 0); + gcry_cipher_setkey(m_rc4_outgoing, key, len); +#elif defined TORRENT_USE_OPENSSL + RC4_set_key(&m_local_key, len, key); +#else + rc4_init(key, len, &m_rc4_outgoing); +#endif + // Discard first 1024 bytes + char buf[1024]; + encrypt(buf, 1024); + } + + ~rc4_handler() + { +#ifdef TORRENT_USE_GCRYPT + gcry_cipher_close(m_rc4_incoming); + gcry_cipher_close(m_rc4_outgoing); +#endif + }; + + void encrypt(char* pos, int len) + { + if (!m_encrypt) return; + + TORRENT_ASSERT(len >= 0); + TORRENT_ASSERT(pos); + +#ifdef TORRENT_USE_GCRYPT + gcry_cipher_encrypt(m_rc4_outgoing, pos, len, 0, 0); +#elif defined TORRENT_USE_OPENSSL + RC4(&m_local_key, len, (const unsigned char*)pos, (unsigned char*)pos); +#else + rc4_encrypt((unsigned char*)pos, len, &m_rc4_outgoing); +#endif + } + + void decrypt(char* pos, int len) + { + if (!m_decrypt) return; + + TORRENT_ASSERT(len >= 0); + TORRENT_ASSERT(pos); + +#ifdef TORRENT_USE_GCRYPT + gcry_cipher_decrypt(m_rc4_incoming, pos, len, 0, 0); +#elif defined TORRENT_USE_OPENSSL + RC4(&m_remote_key, len, (const unsigned char*)pos, (unsigned char*)pos); +#else + rc4_encrypt((unsigned char*)pos, len, &m_rc4_incoming); +#endif + } + + private: +#ifdef TORRENT_USE_GCRYPT + gcry_cipher_hd_t m_rc4_incoming; + gcry_cipher_hd_t m_rc4_outgoing; +#elif defined TORRENT_USE_OPENSSL + RC4_KEY m_local_key; // Key to encrypt outgoing data + RC4_KEY m_remote_key; // Key to decrypt incoming data +#else + rc4 m_rc4_incoming; + rc4 m_rc4_outgoing; +#endif + // determines whether or not encryption and decryption is enabled + bool m_encrypt; + bool m_decrypt; + }; + +} // namespace libtorrent + +#endif // TORRENT_PE_CRYPTO_HPP_INCLUDED +#endif // TORRENT_DISABLE_ENCRYPTION + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/peer.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/peer.hpp new file mode 100644 index 0000000000..33baf52d34 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/peer.hpp @@ -0,0 +1,63 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PEER_HPP_INCLUDED +#define TORRENT_PEER_HPP_INCLUDED + +#include + +#include "libtorrent/peer_id.hpp" + +namespace libtorrent +{ + + struct TORRENT_EXTRA_EXPORT peer_entry + { + std::string ip; + int port; + peer_id pid; + + bool operator==(const peer_entry& p) const + { + return pid == p.pid; + } + + bool operator<(const peer_entry& p) const + { + return pid < p.pid; + } + }; + +} + +#endif // TORRENT_PEER_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/peer_connection.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/peer_connection.hpp new file mode 100644 index 0000000000..2321b48782 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/peer_connection.hpp @@ -0,0 +1,1316 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PEER_CONNECTION_HPP_INCLUDED +#define TORRENT_PEER_CONNECTION_HPP_INCLUDED + +#include +#include +#include +#include + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING +#include "libtorrent/debug.hpp" +#endif + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/buffer.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/stat.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/bandwidth_limit.hpp" +#include "libtorrent/policy.hpp" +#include "libtorrent/socket_type_fwd.hpp" +#include "libtorrent/intrusive_ptr_base.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/chained_buffer.hpp" +#include "libtorrent/disk_buffer_holder.hpp" +#include "libtorrent/bitfield.hpp" +#include "libtorrent/bandwidth_socket.hpp" +#include "libtorrent/socket_type_fwd.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/sliding_average.hpp" +#include "libtorrent/io_service_fwd.hpp" + +#ifdef TORRENT_STATS +#include "libtorrent/aux_/session_impl.hpp" +#endif + +namespace libtorrent +{ + class torrent; + struct peer_info; + struct disk_io_job; +#ifndef TORRENT_DISABLE_EXTENSIONS + struct peer_plugin; +#endif + + namespace detail + { + struct session_impl; + } + + struct pending_block + { + pending_block(piece_block const& b) + : block(b), skipped(0), not_wanted(false) + , timed_out(false), busy(false) {} + + piece_block block; + + // the number of times the request + // has been skipped by out of order blocks + boost::uint16_t skipped:13; + + // if any of these are set to true, this block + // is not allocated + // in the piece picker anymore, and open for + // other peers to pick. This may be caused by + // it either timing out or being received + // unexpectedly from the peer + bool not_wanted:1; + bool timed_out:1; + + // the busy flag is set if the block was + // requested from another peer when this + // request was queued. We only allow a single + // busy request at a time in each peer's queue + bool busy:1; + + bool operator==(pending_block const& b) + { + return b.skipped == skipped && b.block == block + && b.not_wanted == not_wanted && b.timed_out == timed_out; + } + }; + + struct has_block + { + has_block(piece_block const& b): block(b) {} + piece_block const& block; + bool operator()(pending_block const& pb) const + { return pb.block == block; } + }; + + class TORRENT_EXTRA_EXPORT peer_connection + : public bandwidth_socket + , public boost::noncopyable + { + friend class invariant_access; + public: + + enum connection_type + { + bittorrent_connection = 0, + url_seed_connection = 1, + http_seed_connection = 2 + }; + + virtual int type() const = 0; + + enum channels + { + upload_channel, + download_channel, + num_channels + }; + + // this is the constructor where the we are the active part. + // The peer_conenction should handshake and verify that the + // other end has the correct id + peer_connection( + aux::session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote + , policy::peer* peerinfo + , bool outgoing = true); + + // this function is called after it has been constructed and properly + // reference counted. It is safe to call self() in this function + // and schedule events with references to itself (that is not safe to + // do in the constructor). + virtual void start(); + + virtual ~peer_connection(); + + void set_peer_info(policy::peer* pi) + { + TORRENT_ASSERT(m_peer_info == 0 || pi == 0 ); + m_peer_info = pi; + } + + // this is called when the peer object is created, in case + // it was let in by the connections limit slack. This means + // the peer needs to, as soon as the handshake is done, either + // disconnect itself or another peer. + void peer_exceeds_limit() + { m_exceeded_limit = true; } + + // this is called if this peer causes another peer + // to be disconnected, in which case it has fulfilled + // its requirement. + void peer_disconnected_other() + { m_exceeded_limit = false; } + + policy::peer* peer_info_struct() const + { return m_peer_info; } + + enum peer_speed_t { slow = 1, medium, fast }; + peer_speed_t peer_speed(); + + void send_allowed_set(); + +#ifndef TORRENT_DISABLE_EXTENSIONS + void add_extension(boost::shared_ptr); + peer_plugin const* find_plugin(char const* type); +#endif + + // this function is called once the torrent associated + // with this peer connection has retrieved the meta- + // data. If the torrent was spawned with metadata + // this is called from the constructor. + void init(); + + // this is called when the metadata is retrieved + // and the files has been checked + virtual void on_metadata() {}; + + void on_metadata_impl(); + + int get_upload_limit() const; + int get_download_limit() const; + void set_upload_limit(int limit); + void set_download_limit(int limit); + + int upload_limit() const { return m_upload_limit; } + int download_limit() const { return m_download_limit; } + + int prefer_whole_pieces() const + { + if (on_parole()) return 1; + return m_prefer_whole_pieces; + } + + bool on_parole() const + { return peer_info_struct() && peer_info_struct()->on_parole; } + + int picker_options() const; + + void prefer_whole_pieces(int num) + { m_prefer_whole_pieces = num; } + + bool request_large_blocks() const + { return m_request_large_blocks; } + + void request_large_blocks(bool b) + { m_request_large_blocks = b; } + + void set_endgame(bool b) { m_endgame_mode = b; } + bool endgame() const { return m_endgame_mode; } + + bool no_download() const { return m_no_download; } + void no_download(bool b) { m_no_download = b; } + + bool ignore_stats() const { return m_ignore_stats; } + void ignore_stats(bool b) { m_ignore_stats = b; } + + void set_priority(int p) + { + TORRENT_ASSERT(p > 0); + TORRENT_ASSERT(m_priority <= 255); + if (p > 255) p = 255; + m_priority = p; + } + + boost::uint32_t peer_rank() const; + + void fast_reconnect(bool r); + bool fast_reconnect() const { return m_fast_reconnect; } + + // this adds an announcement in the announcement queue + // it will let the peer know that we have the given piece + void announce_piece(int index); + + // this will tell the peer to announce the given piece + // and only allow it to request that piece + void superseed_piece(int replace_piece, int new_piece); + bool super_seeded_piece(int index) const + { + return m_superseed_piece[0] == index + || m_superseed_piece[1] == index; + } + + // tells if this connection has data it want to send + // and has enough upload bandwidth quota left to send it. + bool can_write() const; + bool can_read(boost::uint8_t* state = 0) const; + + bool is_seed() const; + int num_have_pieces() const { return m_num_pieces; } + + void set_share_mode(bool m); + bool share_mode() const { return m_share_mode; } + + void set_upload_only(bool u); + bool upload_only() const { return m_upload_only; } + + void set_holepunch_mode() + { + m_holepunch_mode = true; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** HOLEPUNCH MODE ***"); +#endif + } + + // will send a keep-alive message to the peer + void keep_alive(); + + peer_id const& pid() const { return m_peer_id; } + void set_pid(const peer_id& peer_id) { m_peer_id = peer_id; } + bool has_piece(int i) const; + + std::vector const& download_queue() const; + std::vector const& request_queue() const; + std::vector const& upload_queue() const; + + void clear_request_queue(); + + // estimate of how long it will take until we have + // received all piece requests that we have sent + // if extra_bytes is specified, it will include those + // bytes as if they've been requested + time_duration download_queue_time(int extra_bytes = 0) const; + + bool is_interesting() const { return m_interesting; } + bool is_choked() const { return m_choked; } + + bool is_peer_interested() const { return m_peer_interested; } + bool has_peer_choked() const { return m_peer_choked; } + + void update_interest(); + + virtual void get_peer_info(peer_info& p) const; + + // returns the torrent this connection is a part of + // may be zero if the connection is an incoming connection + // and it hasn't received enough information to determine + // which torrent it should be associated with + boost::weak_ptr associated_torrent() const + { return m_torrent; } + + const stat& statistics() const { return m_statistics; } + void add_stat(size_type downloaded, size_type uploaded); + + // is called once every second by the main loop + void second_tick(int tick_interval_ms); + + void timeout_requests(); + + boost::shared_ptr get_socket() const { return m_socket; } + tcp::endpoint const& remote() const { return m_remote; } + + bitfield const& get_bitfield() const; + std::vector const& allowed_fast(); + std::vector const& suggested_pieces() const { return m_suggested_pieces; } + + ptime connected_time() const { return m_connect; } + ptime last_received() const { return m_last_receive; } + + void on_timeout(); + // this will cause this peer_connection to be disconnected. + virtual void disconnect(error_code const& ec, int error = 0); + // called when a connect attempt fails (not when an + // established connection fails) + void connect_failed(error_code const& e); + bool is_disconnecting() const { return m_disconnecting; } + + // this is called when the connection attempt has succeeded + // and the peer_connection is supposed to set m_connecting + // to false, and stop monitor writability + void on_connection_complete(error_code const& e); + + // returns true if this connection is still waiting to + // finish the connection attempt + bool is_connecting() const { return m_connecting; } + + // returns true if the socket of this peer hasn't been + // attempted to connect yet (i.e. it's queued for + // connection attempt). + bool is_queued() const { return m_queued; } + + // called when it's time for this peer_conncetion to actually + // initiate the tcp connection. This may be postponed until + // the library isn't using up the limitation of half-open + // tcp connections. + void on_connect(int ticket); + + // This is called for every peer right after the upload + // bandwidth has been distributed among them + // It will reset the used bandwidth to 0. + void reset_upload_quota(); + + // trust management. + virtual void received_valid_data(int index); + // returns false if the peer should not be + // disconnected + virtual bool received_invalid_data(int index, bool single_peer); + + // a connection is local if it was initiated by us. + // if it was an incoming connection, it is remote + bool is_outgoing() const { return m_outgoing; } + + bool received_listen_port() const { return m_received_listen_port; } + void received_listen_port() + { m_received_listen_port = true; } + + bool on_local_network() const; + bool ignore_bandwidth_limits() const + { return m_ignore_bandwidth_limits; } + void ignore_bandwidth_limits(bool i) + { m_ignore_bandwidth_limits = i; } + + bool ignore_unchoke_slots() const; + void ignore_unchoke_slots(bool i) + { m_ignore_unchoke_slots = i; } + + bool failed() const { return m_failed; } + + int desired_queue_size() const + { + // this peer is in end-game mode we only want + // one outstanding request + return (m_endgame_mode || m_snubbed) ? 1 : m_desired_queue_size; + } + + bool bittyrant_unchoke_compare( + boost::intrusive_ptr const& p) const; + // compares this connection against the given connection + // for which one is more eligible for an unchoke. + // returns true if this is more eligible + bool unchoke_compare(boost::intrusive_ptr const& p) const; + bool upload_rate_compare(peer_connection const* p) const; + + // resets the byte counters that are used to measure + // the number of bytes transferred within unchoke cycles + void reset_choke_counters(); + + // if this peer connection is useless (neither party is + // interested in the other), disconnect it + void disconnect_if_redundant(); + + void increase_est_reciprocation_rate(); + void decrease_est_reciprocation_rate(); + int est_reciprocation_rate() const { return m_est_reciprocation_rate; } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + void peer_log(char const* fmt, ...) const; + boost::shared_ptr m_logger; +#endif + + // the message handlers are called + // each time a recv() returns some new + // data, the last time it will be called + // is when the entire packet has been + // received, then it will no longer + // be called. i.e. most handlers need + // to check how much of the packet they + // have received before any processing + void incoming_keepalive(); + void incoming_choke(); + void incoming_unchoke(); + void incoming_interested(); + void incoming_not_interested(); + void incoming_have(int piece_index); + void incoming_dont_have(int piece_index); + void incoming_bitfield(bitfield const& bits); + void incoming_request(peer_request const& r); + void incoming_piece(peer_request const& p, disk_buffer_holder& data); + void incoming_piece(peer_request const& p, char const* data); + void incoming_piece_fragment(int bytes); + void start_receive_piece(peer_request const& r); + void incoming_cancel(peer_request const& r); + + bool can_disconnect(error_code const& ec) const; + void incoming_dht_port(int listen_port); + + void incoming_reject_request(peer_request const& r); + void incoming_have_all(); + void incoming_have_none(); + void incoming_allowed_fast(int index); + void incoming_suggest(int index); + + void set_has_metadata(bool m) { m_has_metadata = m; } + bool has_metadata() const { return m_has_metadata; } + + // the following functions appends messages + // to the send buffer + bool send_choke(); + bool send_unchoke(); + void send_interested(); + void send_not_interested(); + void send_suggest(int piece); + + void snub_peer(); + + bool can_request_time_critical() const; + + // returns true if the specified block was actually made time-critical. + // if the block was already time-critical, it returns false. + bool make_time_critical(piece_block const& block); + + // adds a block to the request queue + // returns true if successful, false otherwise + enum flags_t { req_time_critical = 1, req_busy = 2 }; + bool add_request(piece_block const& b, int flags = 0); + + // clears the request queue and sends cancels for all messages + // in the download queue + void cancel_all_requests(); + + // removes a block from the request queue or download queue + // sends a cancel message if appropriate + // refills the request queue, and possibly ignoring pieces requested + // by peers in the ignore list (to avoid recursion) + // if force is true, the blocks is also freed from the piece + // picker, allowing another peer to request it immediately + void cancel_request(piece_block const& b, bool force = false); + void send_block_requests(); + + int bandwidth_throttle(int channel) const + { return m_bandwidth_channel[channel].throttle(); } + + void assign_bandwidth(int channel, int amount); + +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + + // is true until we can be sure that the other end + // speaks our protocol (be it bittorrent or http). + virtual bool in_handshake() const = 0; + + // returns the block currently being + // downloaded. And the progress of that + // block. If the peer isn't downloading + // a piece for the moment, the boost::optional + // will be invalid. + virtual boost::optional + downloading_piece_progress() const + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** downloading_piece_progress() dispatched to the base class!"); +#endif + return boost::optional(); + } + + // these functions are virtual to let bt_peer_connection hook into them + // and encrypt the content + enum message_type_flags { message_type_request = 1 }; + virtual void send_buffer(char const* begin, int size, int flags = 0 + , void (*fun)(char*, int, void*) = 0, void* userdata = 0); + virtual void setup_send(); + + void cork_socket() { TORRENT_ASSERT(!m_corked); m_corked = true; } + void uncork_socket(); + +#ifdef TORRENT_DISK_STATS + void log_buffer_usage(char* buffer, int size, char const* label); +#endif + + template + void append_send_buffer(char* buffer, int size, Destructor const& destructor + , bool encrypted) + { +#if defined TORRENT_DISK_STATS + log_buffer_usage(buffer, size, "queued send buffer"); +#endif + // bittorrent connections should never use this function, since + // they might be encrypted and this would circumvent the actual + // encryption. bt_peer_connection overrides this function with + // its own version. + TORRENT_ASSERT(encrypted || type() != bittorrent_connection); + m_send_buffer.append_buffer(buffer, size, size, destructor); + } + + virtual void append_const_send_buffer(char const* buffer, int size); + +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + void set_country(char const* c) + { + TORRENT_ASSERT(strlen(c) == 2); + m_country[0] = c[0]; + m_country[1] = c[1]; + } + bool has_country() const { return m_country[0] != 0; } +#endif + + int outstanding_bytes() const { return m_outstanding_bytes; } + + int send_buffer_size() const + { return m_send_buffer.size(); } + + int send_buffer_capacity() const + { return m_send_buffer.capacity(); } + + int packet_size() const { return m_packet_size; } + + bool packet_finished() const + { return m_packet_size <= m_recv_pos; } + + int receive_pos() const { return m_recv_pos; } + + void max_out_request_queue(int s); + int max_out_request_queue() const; + +#ifdef TORRENT_DEBUG + bool piece_failed; +#endif + + time_t last_seen_complete() const { return m_last_seen_complete; } + void set_last_seen_complete(int ago) { m_last_seen_complete = time(0) - ago; } + + size_type uploaded_in_last_round() const + { return m_statistics.total_payload_upload() - m_uploaded_at_last_round; } + + size_type downloaded_in_last_round() const + { return m_statistics.total_payload_download() - m_downloaded_at_last_round; } + + size_type uploaded_since_unchoked() const + { return m_statistics.total_payload_upload() - m_uploaded_at_last_unchoke; } + + // called when the disk write buffer is drained again, and we can + // start downloading payload again + void on_disk(); + + int num_reading_bytes() const { return m_reading_bytes; } + + enum sync_t { read_async, read_sync }; + void setup_receive(sync_t sync = read_sync); + + protected: + + size_t try_read(sync_t s, error_code& ec); + + virtual void get_specific_peer_info(peer_info& p) const = 0; + + virtual void write_choke() = 0; + virtual void write_unchoke() = 0; + virtual void write_interested() = 0; + virtual void write_not_interested() = 0; + virtual void write_request(peer_request const& r) = 0; + virtual void write_cancel(peer_request const& r) = 0; + virtual void write_have(int index) = 0; + virtual void write_keepalive() = 0; + virtual void write_piece(peer_request const& r, disk_buffer_holder& buffer) = 0; + virtual void write_suggest(int piece) = 0; + + virtual void write_reject_request(peer_request const& r) = 0; + virtual void write_allow_fast(int piece) = 0; + + virtual void on_connected() = 0; + virtual void on_tick() {} + + virtual void on_receive(error_code const& error + , std::size_t bytes_transferred) = 0; + virtual void on_sent(error_code const& error + , std::size_t bytes_transferred) = 0; + +#ifndef TORRENT_DISABLE_ENCRYPTION + buffer::interval wr_recv_buffer() + { + if (m_recv_buffer.empty()) + { + TORRENT_ASSERT(m_recv_pos == 0); + return buffer::interval(0,0); + } + TORRENT_ASSERT(!m_disk_recv_buffer); + TORRENT_ASSERT(m_disk_recv_buffer_size == 0); + int rcv_pos = (std::min)(m_recv_pos, int(m_recv_buffer.size())); + return buffer::interval(&m_recv_buffer[0] + , &m_recv_buffer[0] + rcv_pos); + } + + std::pair wr_recv_buffers(int bytes); +#endif + + buffer::const_interval receive_buffer() const + { + if (m_recv_buffer.empty()) + { + TORRENT_ASSERT(m_recv_pos == 0); + return buffer::interval(0,0); + } + int rcv_pos = (std::min)(m_recv_pos, int(m_recv_buffer.size())); + return buffer::const_interval(&m_recv_buffer[0] + , &m_recv_buffer[0] + rcv_pos); + } + + bool allocate_disk_receive_buffer(int disk_buffer_size); + char* release_disk_receive_buffer(); + bool has_disk_receive_buffer() const { return m_disk_recv_buffer; } + void cut_receive_buffer(int size, int packet_size, int offset = 0); + void reset_recv_buffer(int packet_size); + void set_soft_packet_size(int size) { m_soft_packet_size = size; } + + // if allow_encrypted is false, and the torrent 'ih' turns out + // to be an encrypted torrent (AES-256 encrypted) the peer will + // be disconnected. This is to prevent non-encrypted peers to + // attach to an encrypted torrent + void attach_to_torrent(sha1_hash const& ih, bool allow_encrypted); + + bool verify_piece(peer_request const& p) const; + + void update_desired_queue_size(); + + void set_timeout(int s) { m_timeout = s; } + + boost::intrusive_ptr self() + { + TORRENT_ASSERT(!m_in_constructor); + return boost::intrusive_ptr(this); + } + + // TODO: make this private + public: + + // upload and download channel state + // enum from peer_info::bw_state + boost::uint8_t m_channel_state[2]; + + private: + + // is true if we learn the incoming connections listening + // during the extended handshake + bool m_received_listen_port:1; + + // this is set to true when a have_all + // message is received. This information + // is used to fill the bitmask in init() + bool m_have_all:1; + + // other side says that it's interested in downloading + // from us. + bool m_peer_interested:1; + + // the other side has told us that it won't send anymore + // data to us for a while + bool m_peer_choked:1; + + // the peer has pieces we are interested in + bool m_interesting:1; + + // we have choked the upload to the peer + bool m_choked:1; + + // this is set to true if the connection timed + // out or closed the connection. In that + // case we will not try to reconnect to + // this peer + bool m_failed:1; + + // this is true if this connection has been added + // to the list of connections that will be closed. + bool m_disconnecting:1; + + // this is set to true once the bitfield is received + bool m_bitfield_received:1; + + // this is set to true if the last time we tried to + // pick a piece to download, we could only find + // blocks that were already requested from other + // peers. In this case, we should not try to pick + // another piece until the last one we requested is done + bool m_endgame_mode:1; + + // set to true when we've sent the first round of suggests + bool m_sent_suggests:1; + + // set to true while we're trying to holepunch + bool m_holepunch_mode:1; + + // when this is set, the transfer stats for this connection + // is not included in the torrent or session stats + bool m_ignore_stats:1; + + // when this is set, the peer_connection socket is + // corked, similar to the linux TCP feature TCP_CORK. + // we won't send anything to the actual socket, just + // buffer messages up in the application layer send + // buffer, and send it once we're uncorked. + bool m_corked:1; + + // set to true if this peer has metadata, and false + // otherwise. + bool m_has_metadata:1; + + // this is set to true if this peer was accepted exceeding + // the connection limit. It means it has to disconnect + // itself, or some other peer, as soon as it's completed + // the handshake. We need to wait for the handshake in + // order to know which torrent it belongs to, to know which + // other peers to compare it to. + bool m_exceeded_limit:1; + + // TODO: make these private as well + protected: + + // number of bytes this peer can send and receive + int m_quota[2]; + + // statistics about upload and download speeds + // and total amount of uploads and downloads for + // this peer + stat m_statistics; + + // a back reference to the session + // the peer belongs to. + aux::session_impl& m_ses; + +#ifndef TORRENT_DISABLE_EXTENSIONS + typedef std::list > extension_list_t; + extension_list_t m_extensions; +#endif + + // called from the main loop when this connection has any + // work to do. + void on_send_data(error_code const& error + , std::size_t bytes_transferred); + void on_receive_data(error_code const& error + , std::size_t bytes_transferred); + + // the average rate of receiving complete piece messages + sliding_average<20> m_piece_rate; + sliding_average<20> m_send_rate; + + private: + + std::pair preferred_caching() const; + void fill_send_buffer(); + void on_disk_read_complete(int ret, disk_io_job const& j, peer_request r); + void on_disk_write_complete(int ret, disk_io_job const& j + , peer_request r, boost::shared_ptr t); + int request_upload_bandwidth( + bandwidth_channel* bwc1 + , bandwidth_channel* bwc2 = 0 + , bandwidth_channel* bwc3 = 0 + , bandwidth_channel* bwc4 = 0); + int request_download_bandwidth( + bandwidth_channel* bwc1 + , bandwidth_channel* bwc2 = 0 + , bandwidth_channel* bwc3 = 0 + , bandwidth_channel* bwc4 = 0); + + // keep the io_service running as long as we + // have peer connections + io_service::work m_work; + + // the time when we last got a part of a + // piece packet from this peer + ptime m_last_piece; + + // the time we sent a request to + // this peer the last time + ptime m_last_request; + // the time we received the last + // piece request from the peer + ptime m_last_incoming_request; + // the time when we unchoked this peer + ptime m_last_unchoke; + + // if we're unchoked by this peer, this + // was the time + ptime m_last_unchoked; + + // the time we last choked this peer. min_time() in + // case we never unchoked it + ptime m_last_choke; + + // timeouts + ptime m_last_receive; + ptime m_last_sent; + + // the time when the first entry in the + // request queue was requested, increased + // for each entry that is popped from the + // download queue. Used for request timeout + ptime m_requested; + + // a timestamp when the remote download rate + // was last updated + ptime m_remote_dl_update; + + // the time when async_connect was called + // or when the incoming connection was established + ptime m_connect; + + // the time when this peer sent us a not_interested message + // the last time. + ptime m_became_uninterested; + + // the time when we sent a not_interested message to + // this peer the last time. + ptime m_became_uninteresting; + + // the total payload download bytes + // at the last unchoke round. This is used to + // measure the number of bytes transferred during + // an unchoke cycle, to unchoke peers the more bytes + // they sent us + size_type m_downloaded_at_last_round; + size_type m_uploaded_at_last_round; + + // this is the number of bytes we had uploaded the + // last time this peer was unchoked. This does not + // reset each unchoke interval/round. This is used to + // track upload across rounds, for the full duration of + // the peer being unchoked. Specifically, it's used + // for the round-robin unchoke algorithm. + size_type m_uploaded_at_last_unchoke; + + template + struct handler_storage + { +#ifdef TORRENT_DEBUG + handler_storage() + : used(false) + {} + + bool used; +#else + handler_storage() {} +#endif + boost::aligned_storage bytes; + }; + + handler_storage m_read_handler_storage; + handler_storage m_write_handler_storage; + +#ifndef TORRENT_DISABLE_GEO_IP + std::string m_inet_as_name; +#endif + + buffer m_recv_buffer; + + // if this peer is receiving a piece, this + // points to a disk buffer that the data is + // read into. This eliminates a memcopy from + // the receive buffer into the disk buffer + disk_buffer_holder m_disk_recv_buffer; + + chained_buffer m_send_buffer; + + boost::shared_ptr m_socket; + + // this is the torrent this connection is + // associated with. If the connection is an + // incoming connection, this is set to zero + // until the info_hash is received. Then it's + // set to the torrent it belongs to. + boost::weak_ptr m_torrent; + + // the pieces the other end have + bitfield m_have_piece; + + // the queue of requests we have got + // from this peer that haven't been issued + // to the disk thread yet + std::vector m_requests; + + // the blocks we have reserved in the piece + // picker and will request from this peer. + std::vector m_request_queue; + + // the queue of blocks we have requested + // from this peer + std::vector m_download_queue; + + // the pieces we will send to the peer + // if requested (regardless of choke state) + std::vector m_accept_fast; + + // a sent-piece counter for the allowed fast set + // to avoid exploitation. Each slot is a counter + // for one of the pieces from the allowed-fast set + std::vector m_accept_fast_piece_cnt; + + // the pieces the peer will send us if + // requested (regardless of choke state) + std::vector m_allowed_fast; + + // pieces that has been suggested to be + // downloaded from this peer + std::vector m_suggested_pieces; + + // a list of byte offsets inside the send buffer + // the piece requests + std::vector m_requests_in_buffer; + + // this peer's peer info struct. This may + // be 0, in case the connection is incoming + // and hasn't been added to a torrent yet. + policy::peer* m_peer_info; + + // the time when this peer last saw a complete copy + // of this torrent + time_t m_last_seen_complete; + + // the block we're currently receiving. Or + // (-1, -1) if we're not receiving one + piece_block m_receiving_block; + + // this is the peer we're actually talking to + // it may not necessarily be the peer we're + // connected to, in case we use a proxy + tcp::endpoint m_remote; + + // the bandwidth channels, upload and download + // keeps track of the current quotas + bandwidth_channel m_bandwidth_channel[num_channels]; + + // remote peer's id + peer_id m_peer_id; + + // if the timeout is extended for the outstanding + // requests, this is the number of seconds it was + // extended. + int m_timeout_extend; + + // the number of bytes that the other + // end has to send us in order to respond + // to all outstanding piece requests we + // have sent to it + int m_outstanding_bytes; + + // the number of outstanding bytes expected + // to be received by extensions + int m_extension_outstanding_bytes; + + // the number of time critical requests + // queued up in the m_request_queue that + // soon will be committed to the download + // queue. This is included in download_queue_time() + // so that it can be used while adding more + // requests and take the previous requests + // into account without submitting it all + // immediately + int m_queued_time_critical; + + // the number of pieces this peer + // has. Must be the same as + // std::count(m_have_piece.begin(), + // m_have_piece.end(), true) + int m_num_pieces; + + // the timeout in seconds + int m_timeout; + + // the size (in bytes) of the bittorrent message + // we're currently receiving + int m_packet_size; + + // some messages needs to be read from the socket + // buffer in multiple stages. This soft packet + // size limits the read size between message handler + // dispatch. Ignored when set to 0 + int m_soft_packet_size; + + // the number of bytes of the bittorrent payload + // we've received so far + int m_recv_pos; + + int m_disk_recv_buffer_size; + + // the number of bytes we are currently reading + // from disk, that will be added to the send + // buffer as soon as they complete + int m_reading_bytes; + + // the number of invalid piece-requests + // we have got from this peer. If the request + // queue gets empty, and there have been + // invalid requests, we can assume the + // peer is waiting for those pieces. + // we can then clear its download queue + // by sending choke, unchoke. + int m_num_invalid_requests; + + // this is the priority with which this peer gets + // download bandwidth quota assigned to it. + int m_priority; + + int m_upload_limit; + int m_download_limit; + + // this is a measurement of how fast the peer + // it allows some variance without changing + // back and forth between states + peer_speed_t m_speed; + + // the ticket id from the connection queue. + // This is used to identify the connection + // so that it can be removed from the queue + // once the connection completes + int m_connection_ticket; + + // if [0] is -1, superseeding is not active. If it is >= 0 + // this is the piece that is available to this peer. Only + // these two pieces can be downloaded from us by this peer. + // This will remain the current piece for this peer until + // another peer sends us a have message for this piece + int m_superseed_piece[2]; + + // pieces downloaded since last second + // timer timeout; used for determining + // approx download rate + int m_remote_pieces_dled; + + // approximate peer download rate + int m_remote_dl_rate; + + // the number of bytes send to the disk-io + // thread that hasn't yet been completely written. + int m_outstanding_writing_bytes; + + // max transfer rates seen on this peer + int m_download_rate_peak; + int m_upload_rate_peak; + + // this is the limit on the number of outstanding requests + // we have to this peer. This is initialized to the settings + // in the session_settings structure. But it may be lowered + // if the peer is known to require a smaller limit (like BitComet). + // or if the extended handshake sets a limit. + // web seeds also has a limit on the queue size. + int m_max_out_request_queue; + + // when using the BitTyrant choker, this is our + // estimated reciprocation rate. i.e. the rate + // we need to send to this peer for it to unchoke + // us + int m_est_reciprocation_rate; + + // estimated round trip time to this peer + // based on the time from when async_connect + // was called to when on_connection_complete + // was called. The rtt is specified in milliseconds + boost::uint16_t m_rtt; + + // the number of request we should queue up + // at the remote end. + boost::uint16_t m_desired_queue_size; + +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + // in case the session settings is set + // to resolve countries, this is set to + // the two character country code this + // peer resides in. + char m_country[2]; +#endif + + // if set to non-zero, this peer will always prefer + // to request entire n pieces, rather than blocks. + // where n is the value of this variable. + // if it is 0, the download rate limit setting + // will be used to determine if whole pieces + // are preferred. + boost::uint8_t m_prefer_whole_pieces; + + // if this is true, the disconnection + // timestamp is not updated when the connection + // is closed. This means the time until we can + // reconnect to this peer is shorter, and likely + // immediate. + bool m_fast_reconnect:1; + + // is true if it was we that connected to the peer + // and false if we got an incoming connection + // could be considered: true = local, false = remote + bool m_outgoing:1; + + // if this is set to true, the peer will not + // request bandwidth from the limiter, but instead + // just send and receive as much as possible. + bool m_ignore_bandwidth_limits:1; + + // set to true if this peer controls its unchoke + // state individually, regardless of the global + // unchoker + bool m_ignore_unchoke_slots:1; + + // this is true until this socket has become + // writable for the first time (i.e. the + // connection completed). While connecting + // the timeout will not be triggered. This is + // because windows XP SP2 may delay connection + // attempts, which means that the connection + // may not even have been attempted when the + // time out is reached. + bool m_connecting:1; + + // This is true until connect is called on the + // peer_connection's socket. It is false on incoming + // connections. + bool m_queued:1; + + // if this is true, the blocks picked by the piece + // picker will be merged before passed to the + // request function. i.e. subsequent blocks are + // merged into larger blocks. This is used by + // the http-downloader, to request whole pieces + // at a time. + bool m_request_large_blocks:1; + + // set to true if this peer is in share mode + bool m_share_mode:1; + + // set to true when this peer is only uploading + bool m_upload_only:1; + + // set to true when a piece request times out. The + // result is that the desired pending queue size + // is set to 1 + bool m_snubbed:1; + + // if this is set to true, the client will not + // pick any pieces from this peer + bool m_no_download:1; + + template + struct allocating_handler + { + allocating_handler( + Handler const& h, handler_storage& s + ) + : handler(h) + , storage(s) + {} + + template + void operator()(A0 const& a0) const + { + handler(a0); + } + + template + void operator()(A0 const& a0, A1 const& a1) const + { + handler(a0, a1); + } + + template + void operator()(A0 const& a0, A1 const& a1, A2 const& a2) const + { + handler(a0, a1, a2); + } + + friend void* asio_handler_allocate( + std::size_t size, allocating_handler* ctx) + { + TORRENT_ASSERT(size <= Size); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(!ctx->storage.used); + ctx->storage.used = true; +#endif + return &ctx->storage.bytes; + } + + friend void asio_handler_deallocate( + void*, std::size_t, allocating_handler* ctx) + { +#ifdef TORRENT_DEBUG + ctx->storage.used = false; +#endif + } + + Handler handler; + handler_storage& storage; + }; + + template + allocating_handler + make_read_handler(Handler const& handler) + { + return allocating_handler( + handler, m_read_handler_storage + ); + } + + template + allocating_handler + make_write_handler(Handler const& handler) + { + return allocating_handler( + handler, m_write_handler_storage + ); + } + +#if TORRENT_USE_ASSERTS + public: + bool m_in_constructor:1; + bool m_disconnect_started:1; + bool m_initialized:1; + int m_in_use; + int m_received_in_piece; +#endif + }; + + struct cork + { + cork(peer_connection& p): m_pc(p) { m_pc.cork_socket(); } + ~cork() { m_pc.uncork_socket(); } + peer_connection& m_pc; + }; + +} + +#endif // TORRENT_PEER_CONNECTION_HPP_INCLUDED diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/peer_id.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/peer_id.hpp new file mode 100644 index 0000000000..089f43438c --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/peer_id.hpp @@ -0,0 +1,48 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PEER_ID_HPP_INCLUDED +#define TORRENT_PEER_ID_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include "libtorrent/sha1_hash.hpp" + +namespace libtorrent +{ + typedef sha1_hash peer_id; +#ifndef TORRENT_NO_DEPRECATE + typedef sha1_hash big_number; +#endif +} + +#endif // TORRENT_PEER_ID_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/peer_info.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/peer_info.hpp new file mode 100644 index 0000000000..508c600dc5 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/peer_info.hpp @@ -0,0 +1,444 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PEER_INFO_HPP_INCLUDED +#define TORRENT_PEER_INFO_HPP_INCLUDED + +#include "libtorrent/socket.hpp" +#include "libtorrent/deadline_timer.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/size_type.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/bitfield.hpp" + +namespace libtorrent +{ + // holds information and statistics about one peer + // that libtorrent is connected to + struct TORRENT_EXPORT peer_info + { + // flags for the peer_info::flags field. Indicates various states + // the peer may be in. These flags are not mutually exclusive, but + // not every combination of them makes sense either. + enum peer_flags_t + { + // **we** are interested in pieces from this peer. + interesting = 0x1, + + // **we** have choked this peer. + choked = 0x2, + + // the peer is interested in **us** + remote_interested = 0x4, + + // the peer has choked **us**. + remote_choked = 0x8, + + // means that this peer supports the + // `extension protocol`__. + // + // __ extension_protocol.html + supports_extensions = 0x10, + + // The connection was initiated by us, the peer has a + // listen port open, and that port is the same as in the + // address of this peer. If this flag is not set, this + // peer connection was opened by this peer connecting to + // us. + local_connection = 0x20, + + // The connection is opened, and waiting for the + // handshake. Until the handshake is done, the peer + // cannot be identified. + handshake = 0x40, + + // The connection is in a half-open state (i.e. it is + // being connected). + connecting = 0x80, + + // The connection is currently queued for a connection + // attempt. This may happen if there is a limit set on + // the number of half-open TCP connections. + queued = 0x100, + + // The peer has participated in a piece that failed the + // hash check, and is now "on parole", which means we're + // only requesting whole pieces from this peer until + // it either fails that piece or proves that it doesn't + // send bad data. + on_parole = 0x200, + + // This peer is a seed (it has all the pieces). + seed = 0x400, + + // This peer is subject to an optimistic unchoke. It has + // been unchoked for a while to see if it might unchoke + // us in return an earn an upload/unchoke slot. If it + // doesn't within some period of time, it will be choked + // and another peer will be optimistically unchoked. + optimistic_unchoke = 0x800, + + // This peer has recently failed to send a block within + // the request timeout from when the request was sent. + // We're currently picking one block at a time from this + // peer. + snubbed = 0x1000, + + // This peer has either explicitly (with an extension) + // or implicitly (by becoming a seed) told us that it + // will not downloading anything more, regardless of + // which pieces we have. + upload_only = 0x2000, + + // This means the last time this peer picket a piece, + // it could not pick as many as it wanted because there + // were not enough free ones. i.e. all pieces this peer + // has were already requested from other peers. + endgame_mode = 0x4000, + + // This flag is set if the peer was in holepunch mode + // when the connection succeeded. This typically only + // happens if both peers are behind a NAT and the peers + // connect via the NAT holepunch mechanism. + holepunched = 0x8000, + + // indicates that this socket is runnin on top of the + // I2P transport. + i2p_socket = 0x10000, + + // indicates that this socket is a uTP socket + utp_socket = 0x20000, + + // indicates that this socket is running on top of an SSL + // (TLS) channel + ssl_socket = 0x40000, + + // this connection is obfuscated with RC4 + rc4_encrypted = 0x100000, + + // the handshake of this connection was obfuscated + // with a diffie-hellman exchange + plaintext_encrypted = 0x200000 + }; + + // tells you in which state the peer is in. It is set to + // any combination of the peer_flags_t enum. + boost::uint32_t flags; + + // the flags indicating which sources a peer can + // have come from. A peer may have been seen from + // multiple sources + enum peer_source_flags + { + // The peer was received from the tracker. + tracker = 0x1, + + // The peer was received from the kademlia DHT. + dht = 0x2, + + // The peer was received from the peer exchange + // extension. + pex = 0x4, + + // The peer was received from the local service + // discovery (The peer is on the local network). + lsd = 0x8, + + // The peer was added from the fast resume data. + resume_data = 0x10, + + // we received an incoming connection from this peer + incoming = 0x20 + }; + + // a combination of flags describing from which sources this peer + // was received. + int source; + + // bits for the read_state and write_state + enum bw_state + { + // The peer is not waiting for any external events to + // send or receive data. + bw_idle = 0, + + // The peer is waiting for the rate limiter. + bw_limit = 1, + + // The peer has quota and is currently waiting for a + // network read or write operation to complete. This is + // the state all peers are in if there are no bandwidth + // limits. + bw_network = 2, + + // The peer is waiting for the disk I/O thread to catch + // up writing buffers to disk before downloading more. + bw_disk = 4 + }; +#ifndef TORRENT_NO_DEPRECATE + enum bw_state_deprecated { bw_torrent = bw_limit, bw_global = bw_limit }; +#endif + + // bitmasks indicating what state this peer is in with regards to sending + // and receiving data. The states are declared in the bw_state enum. + char read_state; + char write_state; + + // the IP-address to this peer. The type is an asio endpoint. For + // more info, see the asio_ documentation. + // + // .. _asio: http://asio.sourceforge.net/asio-0.3.8/doc/asio/reference.html + tcp::endpoint ip; + + // the current upload and download speed we have to and from this peer + // (including any protocol messages). updated about once per second + int up_speed; + int down_speed; + + // The transfer rates of payload data only updated about once per second + int payload_up_speed; + int payload_down_speed; + + // the total number of bytes downloaded from and uploaded to this peer. + // These numbers do not include the protocol chatter, but only the + // payload data. + size_type total_download; + size_type total_upload; + + // the peer's id as used in the bit torrent protocol. This id can be used + // to extract 'fingerprints' from the peer. Sometimes it can tell you + // which client the peer is using. See identify_client()_ + peer_id pid; + + // a bitfield, with one bit per piece in the torrent. + // Each bit tells you if the peer has that piece (if it's set to 1) + // or if the peer miss that piece (set to 0). + bitfield pieces; + + // the number of bytes per second we are allowed to send to or receive + // from this peer. It may be -1 if there's no local limit on the peer. + // The global limit and the torrent limit may also be enforced. + int upload_limit; + int download_limit; + + // the time since we last sent a request + // to this peer and since any transfer occurred with this peer + time_duration last_request; + time_duration last_active; + + // the time until all blocks in the request + // queue will be d + time_duration download_queue_time; + int queue_bytes; + + // the number of seconds until the current front piece request will time + // out. This timeout can be adjusted through + // ``session_settings::request_timeout``. + // -1 means that there is not outstanding request. + int request_timeout; + + // the number of bytes allocated + // and used for the peer's send buffer, respectively. + int send_buffer_size; + int used_send_buffer; + + // the number of bytes + // allocated and used as receive buffer, respectively. + int receive_buffer_size; + int used_receive_buffer; + + // the number of pieces this peer has participated in + // sending us that turned out to fail the hash check. + int num_hashfails; + + // the two letter `ISO 3166 country code`__ for the country the peer is + // connected from. If the country hasn't been resolved yet, both chars + // are set to 0. If the resolution failed for some reason, the field is + // set to "--". If the resolution service returns an invalid country + // code, it is set to "!!". The ``countries.nerd.dk`` service is used to + // look up countries. This field will remain set to 0 unless the torrent + // is set to resolve countries, see `resolve_countries()`_. + // + // __ http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html + char country[2]; + + // the name of the AS this peer is located in. This might be + // an empty string if there is no name in the geo ip database. + std::string inet_as_name; + + // the AS number the peer is located in. + int inet_as; + + // this is the number of requests + // we have sent to this peer + // that we haven't got a response + // for yet + int download_queue_length; + + // the number of block requests that have + // timed out, and are still in the download + // queue + int timed_out_requests; + + // the number of busy requests in the download + // queue. A budy request is a request for a block + // we've also requested from a different peer + int busy_requests; + + // the number of requests messages that are currently in the + // send buffer waiting to be sent. + int requests_in_buffer; + + // the number of requests that is + // tried to be maintained (this is + // typically a function of download speed) + int target_dl_queue_length; + + // the number of piece-requests we have received from this peer + // that we haven't answered with a piece yet. + int upload_queue_length; + + // the number of times this peer has "failed". i.e. failed to connect or + // disconnected us. The failcount is decremented when we see this peer in + // a tracker response or peer exchange message. + int failcount; + + // You can know which piece, and which part of that piece, that is + // currently being downloaded from a specific peer by looking at these + // four members. ``downloading_piece_index`` is the index of the piece + // that is currently being downloaded. This may be set to -1 if there's + // currently no piece downloading from this peer. If it is >= 0, the + // other three members are valid. ``downloading_block_index`` is the + // index of the block (or sub-piece) that is being downloaded. + // ``downloading_progress`` is the number of bytes of this block we have + // received from the peer, and ``downloading_total`` is the total number + // of bytes in this block. + int downloading_piece_index; + int downloading_block_index; + int downloading_progress; + int downloading_total; + + // a string describing the software at the other end of the connection. + // In some cases this information is not available, then it will contain + // a string that may give away something about which software is running + // in the other end. In the case of a web seed, the server type and + // version will be a part of this string. + std::string client; + + // the kind of connection this is. Used for the connection_type field. + enum connection_type_t + { + // Regular bittorrent connection over TCP + standard_bittorrent = 0, + + // HTTP connection using the `BEP 19`_ protocol + web_seed = 1, + + // HTTP connection using the `BEP 17`_ protocol + http_seed = 2 + }; + + // the kind of connection this peer uses. See connection_type_t. + int connection_type; + + // an estimate of the rate this peer is downloading at, in + // bytes per second. + int remote_dl_rate; + + // the number of bytes this peer has pending in the disk-io thread. + // Downloaded and waiting to be written to disk. This is what is capped + // by ``session_settings::max_queued_disk_bytes``. + int pending_disk_bytes; + + // the number of bytes this peer has been assigned to be allowed to send + // and receive until it has to request more quota from the bandwidth + // manager. + int send_quota; + int receive_quota; + + // an estimated round trip time to this peer, in milliseconds. It is + // estimated by timing the the tcp ``connect()``. It may be 0 for + // incoming connections. + int rtt; + + // the number of pieces this peer has. + int num_pieces; + + // the highest download and upload rates seen on this connection. They + // are given in bytes per second. This number is reset to 0 on reconnect. + int download_rate_peak; + int upload_rate_peak; + + // the progress of the peer in the range [0, 1]. This is always 0 when + // floating point operations are diabled, instead use ``progress_ppm``. + float progress; // [0, 1] + + // indicates the download progress of the peer in the range [0, 1000000] + // (parts per million). + int progress_ppm; + + // this is an estimation of the upload rate, to this peer, where it will + // unchoke us. This is a coarse estimation based on the rate at which + // we sent right before we were choked. This is primarily used for the + // bittyrant choking algorithm. + int estimated_reciprocation_rate; + + // the IP and port pair the socket is bound to locally. i.e. the IP + // address of the interface it's going out over. This may be useful for + // multi-homed clients with multiple interfaces to the internet. + tcp::endpoint local_endpoint; + }; + + // internal + struct TORRENT_EXPORT peer_list_entry + { + // internal + enum flags_t + { + banned = 1 + }; + + // internal + tcp::endpoint ip; + // internal + int flags; + // internal + boost::uint8_t failcount; + // internal + boost::uint8_t source; + }; + + // defined in policy.cpp + int source_rank(int source_bitmask); +} + +#endif // TORRENT_PEER_INFO_HPP_INCLUDED diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/peer_request.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/peer_request.hpp new file mode 100644 index 0000000000..c8a19727e2 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/peer_request.hpp @@ -0,0 +1,58 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PEER_REQUEST_HPP_INCLUDED +#define TORRENT_PEER_REQUEST_HPP_INCLUDED + +namespace libtorrent +{ + + // represents a byte range within a piece. Internally this is + // is used for incoming piece requests. + struct TORRENT_EXPORT peer_request + { + // the index of the piece in which the range starts. + int piece; + // the offset within that piece where the range starts. + int start; + // the size of the range, in bytes. + int length; + + // returns true if the right hand side peer_request refers to the same + // range as this does. + bool operator==(peer_request const& r) const + { return piece == r.piece && start == r.start && length == r.length; } + }; +} + +#endif // TORRENT_PEER_REQUEST_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/piece_block_progress.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/piece_block_progress.hpp new file mode 100644 index 0000000000..b2f791a57d --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/piece_block_progress.hpp @@ -0,0 +1,57 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PIECE_BLOCK_PROGRESS_HPP_INCLUDED +#define TORRENT_PIECE_BLOCK_PROGRESS_HPP_INCLUDED + +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + struct piece_block_progress + { + // the piece and block index + // determines exactly which + // part of the torrent that + // is currently being downloaded + int piece_index; + int block_index; + // the number of bytes we have received + // of this block + int bytes_downloaded; + // the number of bytes in the block + int full_block_bytes; + }; +} + +#endif // TORRENT_PIECE_BLOCK_PROGRESS_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/piece_picker.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/piece_picker.hpp new file mode 100644 index 0000000000..9980f840d9 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/piece_picker.hpp @@ -0,0 +1,654 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ +#ifndef TORRENT_PIECE_PICKER_HPP_INCLUDED +#define TORRENT_PIECE_PICKER_HPP_INCLUDED + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/time.hpp" + +// #define TORRENT_DEBUG_REFCOUNTS + +#ifdef TORRENT_DEBUG_REFCOUNTS +#include +#endif + +namespace libtorrent +{ + + class torrent; + class peer_connection; + struct bitfield; + + struct TORRENT_EXTRA_EXPORT piece_block + { + const static piece_block invalid; + + piece_block() {} + piece_block(boost::uint32_t p_index, boost::uint16_t b_index) + : piece_index(p_index) + , block_index(b_index) + { + TORRENT_ASSERT(p_index < (1 << 19)); + TORRENT_ASSERT(b_index < (1 << 13)); + } + boost::uint32_t piece_index:19; + boost::uint32_t block_index:13; + + bool operator<(piece_block const& b) const + { + if (piece_index < b.piece_index) return true; + if (piece_index == b.piece_index) return block_index < b.block_index; + return false; + } + + bool operator==(piece_block const& b) const + { return piece_index == b.piece_index && block_index == b.block_index; } + + bool operator!=(piece_block const& b) const + { return piece_index != b.piece_index || block_index != b.block_index; } + }; + + class TORRENT_EXTRA_EXPORT piece_picker + { + public: + + struct piece_pos; + + enum + { + // the number of priority levels + priority_levels = 8, + // priority factor + prio_factor = priority_levels - 4 + }; + + struct block_info + { + block_info(): peer(0), num_peers(0), state(state_none) {} + // the peer this block was requested or + // downloaded from. This is a pointer to + // a policy::peer object + void* peer; + // the number of peers that has this block in their + // download or request queues + unsigned num_peers:14; + // the state of this block + enum { state_none, state_requested, state_writing, state_finished }; + unsigned state:2; +#if TORRENT_USE_ASSERTS + // to allow verifying the invariant of blocks belonging to the right piece + int piece_index; +#endif + }; + + // the peers that are downloading this piece + // are considered fast peers or slow peers. + // none is set if the blocks were downloaded + // in a previous session + enum piece_state_t + { none, slow, medium, fast }; + + enum options_t + { + // pick rarest first + rarest_first = 1, + // pick the most common first, or the last pieces if sequential + reverse = 2, + // only pick pieces exclusively requested from this peer + on_parole = 4, + // always pick partial pieces before any other piece + prioritize_partials = 8, + // pick pieces in sequential order + sequential = 16, + // have affinity to pieces with the same speed category + speed_affinity = 32, + // ignore the prefer_whole_pieces parameter + ignore_whole_pieces = 64, + // treat pieces with priority 6 and below as filtered + // to trigger end-game mode until all prio 7 pieces are + // completed + time_critical_mode = 128 + }; + + struct downloading_piece + { + downloading_piece(): info(NULL), index(-1) + , finished(0), writing(0), requested(0) + , state(none) {} + + bool operator<(downloading_piece const& rhs) const { return index < rhs.index; } + + // info about each block + // this is a pointer into the m_block_info + // vector owned by the piece_picker + block_info* info; + // the index of the piece + int index; + // the number of blocks in the finished state + boost::int16_t finished; + // the number of blocks in the writing state + boost::int16_t writing; + // the number of blocks in the requested state + boost::int16_t requested; + + // one of piece_state_t values + // really just needs 2 bits + boost::uint16_t state; + }; + + piece_picker(); + + void get_availability(std::vector& avail) const; + + // increases the peer count for the given piece + // (is used when a HAVE message is received) + void inc_refcount(int index, const void* peer); + void dec_refcount(int index, const void* peer); + + // increases the peer count for the given piece + // (is used when a BITFIELD message is received) + void inc_refcount(bitfield const& bitmask, const void* peer); + // decreases the peer count for the given piece + // (used when a peer disconnects) + void dec_refcount(bitfield const& bitmask, const void* peer); + + // these will increase and decrease the peer count + // of all pieces. They are used when seeds join + // or leave the swarm. + void inc_refcount_all(const void* peer); + void dec_refcount_all(const void* peer); + + // This indicates that we just received this piece + // it means that the refcounter will indicate that + // we are not interested in this piece anymore + // (i.e. we don't have to maintain a refcount) + void we_have(int index); + void we_dont_have(int index); + + int cursor() const { return m_cursor; } + int reverse_cursor() const { return m_reverse_cursor; } + int sparse_regions() const { return m_sparse_regions; } + + // sets all pieces to dont-have + void init(int blocks_per_piece, int blocks_in_last_piece, int total_num_pieces); + int num_pieces() const { return int(m_piece_map.size()); } + + bool have_piece(int index) const + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < int(m_piece_map.size())); + return m_piece_map[index].index == piece_pos::we_have_index; + } + + bool is_downloading(int index) const + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < int(m_piece_map.size())); + + piece_pos const& p = m_piece_map[index]; + return p.downloading; + } + + // sets the priority of a piece. + // returns true if the priority was changed from 0 to non-0 + // or vice versa + bool set_piece_priority(int index, int prio); + + // returns the priority for the piece at 'index' + int piece_priority(int index) const; + + // returns the current piece priorities for all pieces + void piece_priorities(std::vector& pieces) const; + + // ========== start deprecation ============== + + // fills the bitmask with 1's for pieces that are filtered + void filtered_pieces(std::vector& mask) const; + + // ========== end deprecation ============== + + // pieces should be the vector that represents the pieces a + // client has. It returns a list of all pieces that this client + // has and that are interesting to download. It returns them in + // priority order. It doesn't care about the download flag. + // The user of this function must lookup if any piece is + // marked as being downloaded. If the user of this function + // decides to download a piece, it must mark it as being downloaded + // itself, by using the mark_as_downloading() member function. + // THIS IS DONE BY THE peer_connection::send_request() MEMBER FUNCTION! + // The last argument is the policy::peer pointer for the peer that + // we'll download from. + void pick_pieces(bitfield const& pieces + , std::vector& interesting_blocks, int num_blocks + , int prefer_whole_pieces, void* peer, piece_state_t speed + , int options, std::vector const& suggested_pieces + , int num_peers) const; + + // picks blocks from each of the pieces in the piece_list + // vector that is also in the piece bitmask. The blocks + // are added to interesting_blocks, and busy blocks are + // added to backup_blocks. num blocks is the number of + // blocks to be picked. Blocks are not picked from pieces + // that are being downloaded + int add_blocks(int piece, bitfield const& pieces + , std::vector& interesting_blocks + , std::vector& backup_blocks + , std::vector& backup_blocks2 + , int num_blocks, int prefer_whole_pieces + , void* peer, std::vector const& ignore + , piece_state_t speed, int options) const; + + // picks blocks only from downloading pieces + int add_blocks_downloading(downloading_piece const& dp + , bitfield const& pieces + , std::vector& interesting_blocks + , std::vector& backup_blocks + , std::vector& backup_blocks2 + , int num_blocks, int prefer_whole_pieces + , void* peer, piece_state_t speed + , int options) const; + + // clears the peer pointer in all downloading pieces with this + // peer pointer + void clear_peer(void* peer); + + // returns true if any client is currently downloading this + // piece-block, or if it's queued for downloading by some client + // or if it already has been successfully downloaded + bool is_requested(piece_block block) const; + // returns true if the block has been downloaded + bool is_downloaded(piece_block block) const; + // returns true if the block has been downloaded and written to disk + bool is_finished(piece_block block) const; + + // marks this piece-block as queued for downloading + bool mark_as_downloading(piece_block block, void* peer + , piece_state_t s); + // returns true if the block was marked as writing, + // and false if the block is already finished or writing + bool mark_as_writing(piece_block block, void* peer); + + void mark_as_finished(piece_block block, void* peer); + void write_failed(piece_block block); + int num_peers(piece_block block) const; + + // returns information about the given piece + void piece_info(int index, piece_picker::downloading_piece& st) const; + + piece_pos const& piece_stats(int index) const + { + TORRENT_ASSERT(index >= 0 && index < int(m_piece_map.size())); + return m_piece_map[index]; + } + + // if a piece had a hash-failure, it must be restored and + // made available for redownloading + void restore_piece(int index); + + // clears the given piece's download flag + // this means that this piece-block can be picked again + void abort_download(piece_block block, void* peer = 0); + + bool is_piece_finished(int index) const; + + // returns the number of blocks there is in the given piece + int blocks_in_piece(int index) const; + + // the number of downloaded blocks that hasn't passed + // the hash-check yet + int unverified_blocks() const; + + void get_downloaders(std::vector& d, int index) const; + + std::vector const& get_download_queue() const + { return m_downloads; } + + int num_downloading_pieces() const { return int(m_downloads.size()); } + + void* get_downloader(piece_block block) const; + + // the number of filtered pieces we don't have + int num_filtered() const { return m_num_filtered; } + + // the number of filtered pieces we already have + int num_have_filtered() const { return m_num_have_filtered; } + + int num_have() const { return m_num_have; } + + // the number of pieces we want and don't have + int num_want_left() const { return num_pieces() - m_num_have - m_num_filtered; } + +#if TORRENT_USE_INVARIANT_CHECKS + // used in debug mode + void verify_priority(int start, int end, int prio) const; + void verify_pick(std::vector const& picked + , bitfield const& bits) const; + void check_invariant(const torrent* t = 0) const; +#endif +#if defined TORRENT_PICKER_LOG + void print_pieces() const; +#endif + + // functor that compares indices on downloading_pieces + struct has_index + { + has_index(int i): index(i) { TORRENT_ASSERT(i >= 0); } + bool operator()(const downloading_piece& p) const + { return p.index == index; } + int index; + }; + + int blocks_in_last_piece() const + { return m_blocks_in_last_piece; } + + std::pair distributed_copies() const; + + private: + + friend struct piece_pos; + + bool can_pick(int piece, bitfield const& bitmask) const; + bool is_piece_free(int piece, bitfield const& bitmask) const; + std::pair expand_piece(int piece, int whole_pieces + , bitfield const& have) const; + + public: + + struct piece_pos + { + piece_pos() {} + piece_pos(int peer_count_, int index_) + : peer_count(peer_count_) + , downloading(0) + , full(0) + , piece_priority(1) + , index(index_) + { + TORRENT_ASSERT(peer_count_ >= 0); + TORRENT_ASSERT(index_ >= 0); + } + + // the number of peers that has this piece + // (availability) +#if TORRENT_COMPACT_PICKER + boost::uint32_t peer_count : 9; +#else + boost::uint32_t peer_count : 16; +#endif + // is 1 if the piece is marked as being downloaded + boost::uint32_t downloading : 1; + // set when downloading, but no free blocks to request left + boost::uint32_t full : 1; + // is 0 if the piece is filtered (not to be downloaded) + // 1 is normal priority (default) + // 2 is higher priority than pieces at the same availability level + // 3 is same priority as partial pieces + // 4 is higher priority than partial pieces + // 5 and 6 same priority as availability 1 (ignores availability) + // 7 is maximum priority (ignores availability) + boost::uint32_t piece_priority : 3; + // index in to the piece_info vector +#if TORRENT_COMPACT_PICKER + boost::uint32_t index : 18; +#else + boost::uint32_t index; +#endif + +#ifdef TORRENT_DEBUG_REFCOUNTS + // all the peers that have this piece + std::set have_peers; +#endif + enum + { + // index is set to this to indicate that we have the + // piece. There is no entry for the piece in the + // buckets if this is the case. +#if TORRENT_COMPACT_PICKER + we_have_index = 0x3ffff, +#else + we_have_index = 0xffffffff, +#endif + // the priority value that means the piece is filtered + filter_priority = 0, + // the max number the peer count can hold +#if TORRENT_COMPACT_PICKER + max_peer_count = 0x1ff +#else + max_peer_count = 0xffff +#endif + }; + + bool have() const { return index == we_have_index; } + void set_have() { index = we_have_index; TORRENT_ASSERT(have()); } + void set_not_have() { index = 0; TORRENT_ASSERT(!have()); } + + bool filtered() const { return piece_priority == filter_priority; } + + // prio 7 is always top priority + // prio 0 is always -1 (don't pick) + // downloading pieces are always on an even prio_factor priority + // + // availability x, downloading + // | availability x, prio 3; availability 2x, prio 6 + // | | availability x, prio 2; availability 2x, prio 5 + // | | | availability x, prio 1; availability 2x, prio 4 + // | | | | + // +---+---+---+---+ + // | 0 | 1 | 2 | 3 | + // +---+---+---+---+ + + int priority(piece_picker const* picker) const + { + // filtered pieces (prio = 0), pieces we have or pieces with + // availability = 0 should not be present in the piece list + // returning -1 indicates that they shouldn't. + if (filtered() || have() || peer_count + picker->m_seeds == 0) + return -1; + + // prio 7 disregards availability + if (piece_priority == priority_levels - 1) return 1 - downloading; + + // prio 4,5,6 halves the availability of a piece + int availability = peer_count; + int p = piece_priority; + if (piece_priority >= priority_levels / 2) + { + availability /= 2; + p -= (priority_levels - 2) / 2; + } + + if (downloading) return availability * prio_factor; + return availability * prio_factor + (priority_levels / 2) - p; + } + + bool operator!=(piece_pos p) const + { return index != p.index || peer_count != p.peer_count; } + + bool operator==(piece_pos p) const + { return index == p.index && peer_count == p.peer_count; } + }; + + void set_num_pad_files(int n) { m_num_pad_files = n; } + + private: + +#ifndef TORRENT_DEBUG_REFCOUNTS +#if TORRENT_COMPACT_PICKER + BOOST_STATIC_ASSERT(sizeof(piece_pos) == sizeof(char) * 4); +#else + BOOST_STATIC_ASSERT(sizeof(piece_pos) == sizeof(char) * 8); +#endif +#endif + + void break_one_seed(); + + void update_pieces() const; + + // fills in the range [start, end) of pieces in + // m_pieces that have priority 'prio' + void priority_range(int prio, int* start, int* end); + + // adds the piece 'index' to m_pieces + void add(int index); + // removes the piece with the given priority and the + // elem_index in the m_pieces vector + void remove(int priority, int elem_index); + // updates the position of the piece with the given + // priority and the elem_index in the m_pieces vector + void update(int priority, int elem_index); + // shuffles the given piece inside it's priority range + void shuffle(int priority, int elem_index); + +// void sort_piece(std::vector::iterator dp); + + downloading_piece& add_download_piece(int index); + void erase_download_piece(std::vector::iterator i); + + std::vector::const_iterator find_dl_piece(int index) const; + std::vector::iterator find_dl_piece(int index); + + void update_full(downloading_piece& dp); + + // some compilers (e.g. gcc 2.95, does not inherit access + // privileges to nested classes) + public: + // the number of seeds. These are not added to + // the availability counters of the pieces + int m_seeds; + private: + + // the following vectors are mutable because they sometimes may + // be updated lazily, triggered by const functions + + // this vector contains all piece indices that are pickable + // sorted by priority. Pieces are in random random order + // among pieces with the same priority + mutable std::vector m_pieces; + + // these are indices to the priority boundries inside + // the m_pieces vector. priority 0 always start at + // 0, priority 1 starts at m_priority_boundries[0] etc. + mutable std::vector m_priority_boundries; + + // this maps indices to number of peers that has this piece and + // index into the m_piece_info vectors. + // piece_pos::we_have_index means that we have the piece, so it + // doesn't exist in the piece_info buckets + // pieces with the filtered flag set doesn't have entries in + // the m_piece_info buckets either + mutable std::vector m_piece_map; + + // each piece that's currently being downloaded + // has an entry in this list with block allocations. + // i.e. it says wich parts of the piece that + // is being downloaded. This list is ordered + // by piece index to make lookups efficient + std::vector m_downloads; + + // this holds the information of the + // blocks in partially downloaded pieces. + // the first m_blocks_per_piece entries + // in the vector belongs to the first + // entry in m_downloads, the second + // m_blocks_per_piece entries to the + // second entry in m_downloads and so on. + std::vector m_block_info; + + boost::uint16_t m_blocks_per_piece; + boost::uint16_t m_blocks_in_last_piece; + + // the number of filtered pieces that we don't already + // have. total_number_of_pieces - number_of_pieces_we_have + // - num_filtered is supposed to the number of pieces + // we still want to download + int m_num_filtered; + + // the number of pieces we have that also are filtered + int m_num_have_filtered; + + // the number of pieces we have + int m_num_have; + + // we have all pieces in the range [0, m_cursor) + // m_cursor is the first piece we don't have + int m_cursor; + + // we have all pieces in the range [m_reverse_cursor, end) + // m_reverse_cursor is the first piece where we also have + // all the subsequent pieces + int m_reverse_cursor; + + // the number of regions of pieces we don't have. + int m_sparse_regions; + + // this is the number of partial download pieces + // that may be caused by pad files. We raise the limit + // of number of partial pieces by this amount, to not + // prioritize pieces that intersect pad files for no + // apparent reason + int m_num_pad_files; + + // if this is set to true, it means update_pieces() + // has to be called before accessing m_pieces. + mutable bool m_dirty; + public: + +#if TORRENT_COMPACT_PICKER + enum { max_pieces = piece_pos::we_have_index - 1 }; +#else + // still limited by piece_block + enum { max_pieces = (1 << 19) - 2 }; +#endif + + }; +} + +#endif // TORRENT_PIECE_PICKER_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/policy.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/policy.hpp new file mode 100644 index 0000000000..2cd265caec --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/policy.hpp @@ -0,0 +1,549 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_POLICY_HPP_INCLUDED +#define TORRENT_POLICY_HPP_INCLUDED + +#include +#include +#include "libtorrent/string_util.hpp" // for allocate_string_copy + +#include "libtorrent/peer.hpp" +#include "libtorrent/piece_picker.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/size_type.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + class torrent; + class peer_connection; + struct external_ip; + + // this is compressed as an unsigned floating point value + // the top 13 bits are the mantissa and the low + // 3 bits is the unsigned exponent. The exponent + // has an implicit + 4 as well. + // This means that the resolution is no less than 16 + // The actual rate is: (upload_rate >> 4) << ((upload_rate & 0xf) + 4) + // the resolution gets worse the higher the value is + // min value is 0, max value is 16775168 + struct ufloat16 + { + ufloat16():m_val(0) {} + ufloat16(int v) + { *this = v; } + operator int() + { + return (m_val >> 3) << ((m_val & 7) + 4); + } + + ufloat16& operator=(int v) + { + if (v > 0x1fff << (7 + 4)) m_val = 0xffff; + else if (v <= 0) m_val = 0; + else + { + int exp = 4; + v >>= 4; + while (v > 0x1fff) + { + v >>= 1; + ++exp; + } + TORRENT_ASSERT(exp <= 7); + m_val = (v << 3) || (exp & 7); + } + return *this; + } + private: + boost::uint16_t m_val; + }; + + enum + { + // the limits of the download queue size + min_request_queue = 2 + }; + + // calculate the priority of a peer based on its address. One of the + // endpoint should be our own. The priority is symmetric, so it doesn't + // matter which is which + TORRENT_EXTRA_EXPORT boost::uint32_t peer_priority( + tcp::endpoint e1, tcp::endpoint e2); + + void request_a_block(torrent& t, peer_connection& c); + + class TORRENT_EXTRA_EXPORT policy + { + public: + + policy(torrent* t); + + struct peer; + +#if TORRENT_USE_I2P + policy::peer* add_i2p_peer(char const* destination, int source, char flags); +#endif + + // this is called once for every peer we get from + // the tracker, pex, lsd or dht. + policy::peer* add_peer(const tcp::endpoint& remote, const peer_id& pid + , int source, char flags); + + // false means duplicate connection + bool update_peer_port(int port, policy::peer* p, int src); + + // called when an incoming connection is accepted + // false means the connection was refused or failed + bool new_connection(peer_connection& c, int session_time); + + // the given connection was just closed + void connection_closed(const peer_connection& c, int session_time); + + void ban_peer(policy::peer* p); + void set_connection(policy::peer* p, peer_connection* c); + void set_failcount(policy::peer* p, int f); + + // the peer has got at least one interesting piece + void peer_is_interesting(peer_connection& c); + + void ip_filter_updated(); + + void set_seed(policy::peer* p, bool s); + + // this clears all cached peer priorities. It's called when + // our external IP changes + void clear_peer_prio(); + +#if TORRENT_USE_ASSERTS + bool has_connection(const peer_connection* p); +#endif +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + + struct TORRENT_EXTRA_EXPORT peer + { + peer(boost::uint16_t port, bool connectable, int src); + + size_type total_download() const; + size_type total_upload() const; + + boost::uint32_t rank(external_ip const& external, int external_port) const; + + libtorrent::address address() const; + char const* dest() const; + + tcp::endpoint ip() const { return tcp::endpoint(address(), port); } + + // this is the accumulated amount of + // uploaded and downloaded data to this + // peer. It only accounts for what was + // shared during the last connection to + // this peer. i.e. These are only updated + // when the connection is closed. For the + // total amount of upload and download + // we'll have to add thes figures with the + // statistics from the peer_connection. + // since these values don't need to be stored + // with byte-precision, they specify the number + // of kiB. i.e. shift left 10 bits to compare to + // byte counters. + boost::uint32_t prev_amount_upload; + boost::uint32_t prev_amount_download; + + // if the peer is connected now, this + // will refer to a valid peer_connection + peer_connection* connection; + +#ifndef TORRENT_DISABLE_GEO_IP +#ifdef TORRENT_DEBUG + // only used in debug mode to assert that + // the first entry in the AS pair keeps the same + boost::uint16_t inet_as_num; +#endif + // The AS this peer belongs to + std::pair* inet_as; +#endif + + // as computed by hashing our IP with the remote + // IP of this peer + // calculated lazily + mutable boost::uint32_t peer_rank; + + // the time when this peer was optimistically unchoked + // the last time. in seconds since session was created + // 16 bits is enough to last for 18.2 hours + // when the session time reaches 18 hours, it jumps back by + // 9 hours, and all peers' times are updated to be + // relative to that new time offset + boost::uint16_t last_optimistically_unchoked; + + // the time when the peer connected to us + // or disconnected if it isn't connected right now + // in number of seconds since session was created + boost::uint16_t last_connected; + + // the port this peer is or was connected on + boost::uint16_t port; + + // the upload and download rate limits set for this peer + ufloat16 upload_rate_limit; + ufloat16 download_rate_limit; + + // the number of times this peer has been + // part of a piece that failed the hash check + boost::uint8_t hashfails; + + // the number of failed connection attempts + // this peer has + unsigned failcount:5; // [0, 31] + + // incoming peers (that don't advertize their listen port) + // will not be considered connectable. Peers that + // we have a listen port for will be assumed to be. + bool connectable:1; + + // true if this peer currently is unchoked + // because of an optimistic unchoke. + // when the optimistic unchoke is moved to + // another peer, this peer will be choked + // if this is true + bool optimistically_unchoked:1; + + // this is true if the peer is a seed + bool seed:1; + + // the number of times we have allowed a fast + // reconnect for this peer. + unsigned fast_reconnects:4; + + // for every valid piece we receive where this + // peer was one of the participants, we increase + // this value. For every invalid piece we receive + // where this peer was a participant, we decrease + // this value. If it sinks below a threshold, its + // considered a bad peer and will be banned. + signed trust_points:4; // [-7, 8] + + // a bitmap combining the peer_source flags + // from peer_info. + unsigned source:6; + +#ifndef TORRENT_DISABLE_ENCRYPTION + // Hints encryption support of peer. Only effective + // for and when the outgoing encryption policy + // allows both encrypted and non encrypted + // connections (pe_settings::out_enc_policy + // == enabled). The initial state of this flag + // determines the initial connection attempt + // type (true = encrypted, false = standard). + // This will be toggled everytime either an + // encrypted or non-encrypted handshake fails. + bool pe_support:1; +#endif + +#if TORRENT_USE_IPV6 + // this is true if the v6 union member in addr is + // the one to use, false if it's the v4 one + bool is_v6_addr:1; +#endif +#if TORRENT_USE_I2P + // set if the i2p_destination is in use in the addr union + bool is_i2p_addr:1; +#endif + + // if this is true, the peer has previously + // participated in a piece that failed the piece + // hash check. This will put the peer on parole + // and only request entire pieces. If a piece pass + // that was partially requested from this peer it + // will leave parole mode and continue download + // pieces as normal peers. + bool on_parole:1; + + // is set to true if this peer has been banned + bool banned:1; + +#ifndef TORRENT_DISABLE_DHT + // this is set to true when this peer as been + // pinged by the DHT + bool added_to_dht:1; +#endif + // we think this peer supports uTP + bool supports_utp:1; + // we have been connected via uTP at least once + bool confirmed_supports_utp:1; + bool supports_holepunch:1; + // this is set to one for web seeds. Web seeds + // are not stored in the policy m_peers list, + // and are excempt from connect candidate bookkeeping + // so, any peer with the web_seed bit set, is + // never considered a connect candidate + bool web_seed:1; +#if TORRENT_USE_ASSERTS + bool in_use:1; +#endif + }; + + struct TORRENT_EXTRA_EXPORT ipv4_peer : peer + { + ipv4_peer(tcp::endpoint const& ip, bool connectable, int src); + + address_v4 addr; + }; + +#if TORRENT_USE_I2P + struct TORRENT_EXTRA_EXPORT i2p_peer : peer + { + i2p_peer(char const* destination, bool connectable, int src); + ~i2p_peer(); + + char* destination; + }; +#endif + +#if TORRENT_USE_IPV6 + struct TORRENT_EXTRA_EXPORT ipv6_peer : peer + { + ipv6_peer(tcp::endpoint const& ip, bool connectable, int src); + + const address_v6::bytes_type addr; + }; +#endif + + int num_peers() const { return int(m_peers.size()); } + + struct peer_address_compare + { + bool operator()( + peer const* lhs, libtorrent::address const& rhs) const + { + return lhs->address() < rhs; + } + + bool operator()( + libtorrent::address const& lhs, peer const* rhs) const + { + return lhs < rhs->address(); + } + +#if TORRENT_USE_I2P + bool operator()( + peer const* lhs, char const* rhs) const + { + return strcmp(lhs->dest(), rhs) < 0; + } + + bool operator()( + char const* lhs, peer const* rhs) const + { + return strcmp(lhs, rhs->dest()) < 0; + } +#endif + + bool operator()( + peer const* lhs, peer const* rhs) const + { +#if TORRENT_USE_I2P + if (rhs->is_i2p_addr == lhs->is_i2p_addr) + return strcmp(lhs->dest(), rhs->dest()) < 0; +#endif + return lhs->address() < rhs->address(); + } + }; + + typedef std::deque peers_t; + + typedef peers_t::iterator iterator; + typedef peers_t::const_iterator const_iterator; + iterator begin_peer() { return m_peers.begin(); } + iterator end_peer() { return m_peers.end(); } + const_iterator begin_peer() const { return m_peers.begin(); } + const_iterator end_peer() const { return m_peers.end(); } + + std::pair find_peers(address const& a) + { + return std::equal_range( + m_peers.begin(), m_peers.end(), a, peer_address_compare()); + } + + std::pair find_peers(address const& a) const + { + return std::equal_range( + m_peers.begin(), m_peers.end(), a, peer_address_compare()); + } + + bool connect_one_peer(int session_time); + + bool has_peer(policy::peer const* p) const; + + int num_seeds() const { return m_num_seeds; } + int num_connect_candidates() const { return m_num_connect_candidates; } + void recalculate_connect_candidates(); + + void erase_peer(policy::peer* p); + void erase_peer(iterator i); + + private: + + void update_peer(policy::peer* p, int src, int flags + , tcp::endpoint const& remote, char const* destination); + bool insert_peer(policy::peer* p, iterator iter, int flags); + + bool compare_peer_erase(policy::peer const& lhs, policy::peer const& rhs) const; + bool compare_peer(policy::peer const& lhs, policy::peer const& rhs + , external_ip const& external, int source_port) const; + + iterator find_connect_candidate(int session_time); + + bool is_connect_candidate(peer const& p, bool finished) const; + bool is_erase_candidate(peer const& p, bool finished) const; + bool is_force_erase_candidate(peer const& pe) const; + bool should_erase_immediately(peer const& p) const; + + enum flags_t { force_erase = 1 }; + void erase_peers(int flags = 0); + + peers_t m_peers; + + torrent* m_torrent; + + // this shouldbe NULL for the most part. It's set + // to point to a valid torrent_peer object if that + // object needs to be kept alive. If we ever feel + // like removing a torrent_peer from m_peers, we + // first check if the peer matches this one, and + // if so, don't delete it. + peer* m_locked_peer; + + // since the peer list can grow too large + // to scan all of it, start at this iterator + int m_round_robin; + + // The number of peers in our peer list + // that are connect candidates. i.e. they're + // not already connected and they have not + // yet reached their max try count and they + // have the connectable state (we have a listen + // port for them). + int m_num_connect_candidates; + + // the number of seeds in the peer list + int m_num_seeds; + + // this was the state of the torrent the + // last time we recalculated the number of + // connect candidates. Since seeds (or upload + // only) peers are not connect candidates + // when we're finished, the set depends on + // this state. Every time m_torrent->is_finished() + // is different from this state, we need to + // recalculate the connect candidates. + bool m_finished:1; + }; + + inline policy::ipv4_peer::ipv4_peer( + tcp::endpoint const& ep, bool c, int src + ) + : peer(ep.port(), c, src) + , addr(ep.address().to_v4()) + { +#if TORRENT_USE_IPV6 + is_v6_addr = false; +#endif +#if TORRENT_USE_I2P + is_i2p_addr = false; +#endif + } + +#if TORRENT_USE_I2P + inline policy::i2p_peer::i2p_peer(char const* dest, bool connectable, int src) + : peer(0, connectable, src), destination(allocate_string_copy(dest)) + { +#if TORRENT_USE_IPV6 + is_v6_addr = false; +#endif + is_i2p_addr = true; + } + + inline policy::i2p_peer::~i2p_peer() + { free(destination); } +#endif // TORRENT_USE_I2P + +#if TORRENT_USE_IPV6 + inline policy::ipv6_peer::ipv6_peer( + tcp::endpoint const& ep, bool c, int src + ) + : peer(ep.port(), c, src) + , addr(ep.address().to_v6().to_bytes()) + { + is_v6_addr = true; +#if TORRENT_USE_I2P + is_i2p_addr = false; +#endif + } + +#endif // TORRENT_USE_IPV6 + +#if TORRENT_USE_I2P + inline char const* policy::peer::dest() const + { + if (is_i2p_addr) + return static_cast(this)->destination; + return ""; + } +#endif + + inline libtorrent::address policy::peer::address() const + { +#if TORRENT_USE_IPV6 + if (is_v6_addr) + return libtorrent::address_v6( + static_cast(this)->addr); + else +#endif +#if TORRENT_USE_I2P + if (is_i2p_addr) return libtorrent::address(); + else +#endif + return static_cast(this)->addr; + } + +} + +#endif // TORRENT_POLICY_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/proxy_base.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/proxy_base.hpp new file mode 100644 index 0000000000..a9903cc229 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/proxy_base.hpp @@ -0,0 +1,256 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PROXY_BASE_HPP_INCLUDED +#define TORRENT_PROXY_BASE_HPP_INCLUDED + +#include "libtorrent/io.hpp" +#include "libtorrent/io_service_fwd.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/error_code.hpp" + +namespace libtorrent { + +class proxy_base : boost::noncopyable +{ +public: + + typedef stream_socket next_layer_type; + typedef stream_socket::lowest_layer_type lowest_layer_type; + typedef stream_socket::endpoint_type endpoint_type; + typedef stream_socket::protocol_type protocol_type; + + explicit proxy_base(io_service& io_service) + : m_sock(io_service) + , m_port(0) + , m_resolver(io_service) + {} + + void set_proxy(std::string hostname, int port) + { + m_hostname = hostname; + m_port = port; + } + + template + void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) + { + m_sock.async_read_some(buffers, handler); + } + + template + std::size_t read_some(Mutable_Buffers const& buffers, error_code& ec) + { + return m_sock.read_some(buffers, ec); + } + + template + std::size_t write_some(Const_Buffers const& buffers, error_code& ec) + { + return m_sock.write_some(buffers, ec); + } + + std::size_t available(error_code& ec) const + { return m_sock.available(ec); } + +#ifndef BOOST_NO_EXCEPTIONS + std::size_t available() const + { return m_sock.available(); } + + template + std::size_t read_some(Mutable_Buffers const& buffers) + { + return m_sock.read_some(buffers); + } + + template + std::size_t write_some(Const_Buffers const& buffers) + { + return m_sock.write_some(buffers); + } + + template + void io_control(IO_Control_Command& ioc) + { + m_sock.io_control(ioc); + } +#endif + + template + void io_control(IO_Control_Command& ioc, error_code& ec) + { + m_sock.io_control(ioc, ec); + } + + template + void async_write_some(Const_Buffers const& buffers, Handler const& handler) + { + m_sock.async_write_some(buffers, handler); + } + +#ifndef BOOST_NO_EXCEPTIONS + template + void set_option(SettableSocketOption const& opt) + { + m_sock.set_option(opt); + } +#endif + + template + error_code set_option(SettableSocketOption const& opt, error_code& ec) + { + return m_sock.set_option(opt, ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + template + void get_option(GettableSocketOption& opt) + { + m_sock.get_option(opt); + } +#endif + + template + error_code get_option(GettableSocketOption& opt, error_code& ec) + { + return m_sock.get_option(opt, ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + void bind(endpoint_type const& endpoint) + { +// m_sock.bind(endpoint); + } +#endif + + void bind(endpoint_type const& endpoint, error_code& ec) + { + // the reason why we ignore binds here is because we don't + // (necessarily) yet know what address family the proxy + // will resolve to, and binding to the wrong one would + // break our connection attempt later. The caller here + // doesn't necessarily know that we're proxying, so this + // bind address is based on the final endpoint, not the + // proxy. + // TODO: it would be nice to remember the bind port and bind once we know where the proxy is +// m_sock.bind(endpoint, ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + void open(protocol_type const& p) + { +// m_sock.open(p); + } +#endif + + void open(protocol_type const& p, error_code& ec) + { + // we need to ignore this for the same reason as stated + // for ignoring bind() +// m_sock.open(p, ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + void close() + { + m_remote_endpoint = endpoint_type(); + m_sock.close(); + m_resolver.cancel(); + } +#endif + + void close(error_code& ec) + { + m_remote_endpoint = endpoint_type(); + m_sock.close(ec); + m_resolver.cancel(); + } + +#ifndef BOOST_NO_EXCEPTIONS + endpoint_type remote_endpoint() const + { + return m_remote_endpoint; + } +#endif + + endpoint_type remote_endpoint(error_code& ec) const + { + if (!m_sock.is_open()) ec = asio::error::not_connected; + return m_remote_endpoint; + } + +#ifndef BOOST_NO_EXCEPTIONS + endpoint_type local_endpoint() const + { + return m_sock.local_endpoint(); + } +#endif + + endpoint_type local_endpoint(error_code& ec) const + { + return m_sock.local_endpoint(ec); + } + + io_service& get_io_service() + { + return m_sock.get_io_service(); + } + + lowest_layer_type& lowest_layer() + { + return m_sock.lowest_layer(); + } + + next_layer_type& next_layer() + { + return m_sock; + } + + bool is_open() const { return m_sock.is_open(); } + +protected: + + stream_socket m_sock; + std::string m_hostname; + int m_port; + + endpoint_type m_remote_endpoint; + + tcp::resolver m_resolver; +}; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/ptime.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/ptime.hpp new file mode 100644 index 0000000000..444bbb6789 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/ptime.hpp @@ -0,0 +1,139 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PTIME_HPP_INCLUDED +#define TORRENT_PTIME_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include + +#if defined TORRENT_USE_BOOST_DATE_TIME + +#include +#include +#include + +namespace libtorrent +{ + typedef boost::posix_time::ptime ptime; + typedef boost::posix_time::time_duration time_duration; +} + +#else // TORRENT_USE_BOOST_DATE_TIME + +#include + +namespace libtorrent +{ + // libtorrent time_duration type + struct TORRENT_EXPORT time_duration + { + // hidden + time_duration() {} + + // all operators have the same semantics as a 64 bit signed integer + time_duration operator/(int rhs) const { return time_duration(diff / rhs); } + explicit time_duration(boost::int64_t d) : diff(d) {} + time_duration& operator-=(time_duration const& c) + { diff -= c.diff; return *this; } + time_duration& operator+=(time_duration const& c) + { diff += c.diff; return *this; } + time_duration& operator*=(int v) { diff *= v; return *this; } + time_duration operator+(time_duration const& c) + { return time_duration(diff + c.diff); } + time_duration operator-(time_duration const& c) + { return time_duration(diff - c.diff); } + + // internal + boost::int64_t diff; + }; + + // This type represents a point in time. + struct TORRENT_EXPORT ptime + { + // hidden + ptime() {} + explicit ptime(boost::uint64_t t): time(t) {} + + // these operators have the same semantics as signed 64 bit integers + ptime& operator+=(time_duration rhs) { time += rhs.diff; return *this; } + ptime& operator-=(time_duration rhs) { time -= rhs.diff; return *this; } + + // internal + boost::uint64_t time; + }; + + // returns true of the time duration is less than 0 + inline bool is_negative(time_duration dt) { return dt.diff < 0; } + + // all operators have the same semantics as signed 64 bit integers + inline bool operator>(ptime lhs, ptime rhs) + { return lhs.time > rhs.time; } + inline bool operator>=(ptime lhs, ptime rhs) + { return lhs.time >= rhs.time; } + inline bool operator<=(ptime lhs, ptime rhs) + { return lhs.time <= rhs.time; } + inline bool operator<(ptime lhs, ptime rhs) + { return lhs.time < rhs.time; } + inline bool operator!=(ptime lhs, ptime rhs) + { return lhs.time != rhs.time;} + inline bool operator==(ptime lhs, ptime rhs) + { return lhs.time == rhs.time;} + inline bool operator==(time_duration lhs, time_duration rhs) + { return lhs.diff == rhs.diff; } + inline bool operator<(time_duration lhs, time_duration rhs) + { return lhs.diff < rhs.diff; } + inline bool operator<=(time_duration lhs, time_duration rhs) + { return lhs.diff <= rhs.diff; } + inline bool operator>(time_duration lhs, time_duration rhs) + { return lhs.diff > rhs.diff; } + inline bool operator>=(time_duration lhs, time_duration rhs) + { return lhs.diff >= rhs.diff; } + inline time_duration operator*(time_duration lhs, int rhs) + { return time_duration(boost::int64_t(lhs.diff * rhs)); } + inline time_duration operator*(int lhs, time_duration rhs) + { return time_duration(boost::int64_t(lhs * rhs.diff)); } + inline time_duration operator-(ptime lhs, ptime rhs) + { return time_duration(lhs.time - rhs.time); } + inline ptime operator+(ptime lhs, time_duration rhs) + { return ptime(lhs.time + rhs.diff); } + inline ptime operator+(time_duration lhs, ptime rhs) + { return ptime(rhs.time + lhs.diff); } + inline ptime operator-(ptime lhs, time_duration rhs) + { return ptime(lhs.time - rhs.diff); } + +} + +#endif // TORRENT_USE_BOOST_DATE_TIME + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/puff.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/puff.hpp new file mode 100644 index 0000000000..5af5abb0c5 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/puff.hpp @@ -0,0 +1,33 @@ +/* puff.h + Copyright (C) 2002, 2003 Mark Adler, all rights reserved + version 1.7, 3 Mar 2002 + + This software 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 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. + + Mark Adler madler@alumni.caltech.edu + */ + + +/* + * See puff.c for purpose and usage. + */ +#include + +int puff(unsigned char *dest, /* pointer to destination pointer */ + boost::uint32_t *destlen, /* amount of output space */ + unsigned char *source, /* pointer to source data pointer */ + boost::uint32_t *sourcelen); /* amount of input available */ diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/random.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/random.hpp new file mode 100644 index 0000000000..07589a8043 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/random.hpp @@ -0,0 +1,40 @@ +/* + +Copyright (c) 2011-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" +#include + +namespace libtorrent +{ + void TORRENT_EXTRA_EXPORT random_seed(boost::uint32_t v); + boost::uint32_t TORRENT_EXTRA_EXPORT random(); +} diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/rss.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/rss.hpp new file mode 100644 index 0000000000..5a32b352b1 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/rss.hpp @@ -0,0 +1,277 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_RSS_HPP_INCLUDED +#define TORRENT_RSS_HPP_INCLUDED + +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/add_torrent_params.hpp" +#include "libtorrent/size_type.hpp" + +#include +#include + +namespace libtorrent +{ + namespace aux + { struct session_impl; } + + // represents one item from an RSS feed. Specifically + // a feed of torrents. + // + struct TORRENT_EXPORT feed_item + { + feed_item(); + ~feed_item(); + + // these are self explanatory and may be empty if the feed does not specify + // those fields. + std::string url; + std::string uuid; + std::string title; + std::string description; + std::string comment; + std::string category; + + // the total size of the content the torrent refers to, or -1 + // if no size was specified by the feed. + size_type size; + + // the handle to the torrent, if the session is already downloading + // this torrent. + torrent_handle handle; + + // the info-hash of the torrent, or cleared (i.e. all zeroes) if + // the feed does not specify the info-hash. + sha1_hash info_hash; + }; + + // given a feed_item ``f``, add the torrent it refers to to session ``s``. +#ifndef BOOST_NO_EXCEPTIONS + torrent_handle TORRENT_EXPORT add_feed_item(session& s, feed_item const& fi + , add_torrent_params const& p); +#endif + torrent_handle TORRENT_EXPORT add_feed_item(session& s, feed_item const& fi + , add_torrent_params const& p, error_code& ec); + + // the feed_settings object is all the information + // and configuration for a specific feed. All of + // these settings can be changed by the user + // after adding the feed + struct TORRENT_EXPORT feed_settings + { + feed_settings() + : auto_download(true) + , auto_map_handles(true) + , default_ttl(30) + {} + + std::string url; + + // By default ``auto_download`` is true, which means all torrents in + // the feed will be downloaded. Set this to false in order to manually + // add torrents to the session. You may react to the rss_alert when + // a feed has been updated to poll it for the new items in the feed + // when adding torrents manually. When torrents are added automatically, + // an add_torrent_alert is posted which includes the torrent handle + // as well as the error code if it failed to be added. You may also call + // ``session::get_torrents()`` to get the handles to the new torrents. + bool auto_download; + + // ``auto_map_handles`` defaults to true and determines whether or + // not to set the ``handle`` field in the feed_item, returned + // as the feed status. If auto-download is enabled, this setting + // is ignored. If auto-download is not set, setting this to false + // will save one pass through all the feed items trying to find + // corresponding torrents in the session. + bool auto_map_handles; + + // The ``default_ttl`` is the default interval for refreshing a feed. + // This may be overridden by the feed itself (by specifying the ```` + // tag) and defaults to 30 minutes. The field specifies the number of + // minutes between refreshes. + int default_ttl; + + // If torrents are added automatically, you may want to set the + // ``add_args`` to appropriate values for download directory etc. + // This object is used as a template for adding torrents from feeds, + // but some torrent specific fields will be overridden by the + // individual torrent being added. For more information on the + // add_torrent_params, see async_add_torrent() and add_torrent(). + add_torrent_params add_args; + }; + + // holds information about the status of an RSS feed. Retrieved by + // calling get_feed_status() on feed_handle. + struct TORRENT_EXPORT feed_status + { + feed_status(): last_update(0), next_update(0) + , updating(false), ttl(0) {} + + // the URL of the feed. + std::string url; + + // the name of the feed (as specified by the feed itself). This + // may be empty if we have not recevied a response from the RSS server yet, + // or if the feed does not specify a title. + std::string title; + + // the feed description (as specified by the feed itself). + // This may be empty if we have not received a response from the RSS server + // yet, or if the feed does not specify a description. + std::string description; + + // the posix time of the last successful response from the feed. + time_t last_update; + + // the number of seconds, from now, when the feed will be + // updated again. + int next_update; + + // true if the feed is currently being updated (i.e. waiting for + // DNS resolution, connecting to the server or waiting for the response to the + // HTTP request, or receiving the response). + bool updating; + + // a vector of all items that we have received from the feed. See + // feed_item for more information. + std::vector items; + + // set to the appropriate error code if the feed encountered an + // error. See error_code for more info. + error_code error; + + // the current refresh time (in minutes). It's either the configured + // default ttl, or the ttl specified by the feed. + int ttl; + }; + + struct feed; + + // The ``feed_handle`` refers to a specific RSS feed that is watched by the session. + struct TORRENT_EXPORT feed_handle + { + feed_handle() {} + + // Forces an update/refresh of the feed. Regular updates of the feed is managed + // by libtorrent, be careful to not call this too frequently since it may + // overload the RSS server. + void update_feed(); + + // Queries the RSS feed for information, including all the items in the feed. + // see feed_status. + feed_status get_feed_status() const; + + // Sets and gets settings for this feed. For more information on the + // available settings, see add_feed(). + void set_settings(feed_settings const& s); + feed_settings settings() const; + private: + friend struct aux::session_impl; + friend struct feed; + feed_handle(boost::weak_ptr const& p); + boost::weak_ptr m_feed_ptr; + }; + + struct feed_state; + class http_parser; + + boost::shared_ptr TORRENT_EXPORT new_feed(aux::session_impl& ses, feed_settings const& sett); + + // this is the internal object holding all state about an + // RSS feed. All user interaction with this object + // goes through the feed_handle, which makes sure all calls + // are posted to the network thread + struct TORRENT_EXTRA_EXPORT feed : boost::enable_shared_from_this + { + friend void parse_feed(feed_state& f, int token, char const* name, char const* val); + + feed(aux::session_impl& ses, feed_settings const& feed); + + void on_feed(error_code const& ec, http_parser const& parser + , char const* data, int size); + + int update_feed(); + + aux::session_impl& session() const { return m_ses; } + + void set_settings(feed_settings const& s); + void get_settings(feed_settings* s) const; + void get_feed_status(feed_status* ret) const; + + int next_update(time_t now) const; + + void load_state(lazy_entry const& rd); + void save_state(entry& rd) const; + +// private: + + void add_item(feed_item const& item); + + feed_handle my_handle(); + + error_code m_error; + std::vector m_items; + + // these are all the URLs we've seen in the items list. + // it's used to avoid adding duplicate entries to the actual + // item vector + std::set m_urls; + + // these are URLs that have been added to the session + // once. If we see them again, and they're not in the + // session, don't add them again, since it means they + // were removed from the session. It maps URLs to the + // posix time when they were added. The timestamp is + // used to prune this list by removing the oldest ones + // when the size gets too big + std::map m_added; + + std::string m_title; + std::string m_description; + time_t m_last_attempt; + time_t m_last_update; + // refresh rate of this feed in minutes + int m_ttl; + // the number of update failures in a row + int m_failures; + // true while waiting for the server to respond + bool m_updating; + feed_settings m_settings; + + aux::session_impl& m_ses; + }; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/session.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/session.hpp new file mode 100644 index 0000000000..40846044aa --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/session.hpp @@ -0,0 +1,1064 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SESSION_HPP_INCLUDED +#define TORRENT_SESSION_HPP_INCLUDED + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/session_status.hpp" +#include "libtorrent/version.hpp" +#include "libtorrent/fingerprint.hpp" +#include "libtorrent/disk_io_thread.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/alert.hpp" // alert::error_notification +#include "libtorrent/add_torrent_params.hpp" +#include "libtorrent/rss.hpp" +#include "libtorrent/build_config.hpp" + +#include "libtorrent/storage.hpp" + +#ifdef _MSC_VER +# include +#endif + +#ifdef TORRENT_USE_OPENSSL +// this is a nasty openssl macro +#ifdef set_key +#undef set_key +#endif +#endif + +namespace libtorrent +{ + struct plugin; + struct torrent_plugin; + class torrent; + struct ip_filter; + class port_filter; + class connection_queue; + class alert; + + // The default values of the session settings are set for a regular + // bittorrent client running on a desktop system. There are functions that + // can set the session settings to pre set settings for other environments. + // These can be used for the basis, and should be tweaked to fit your needs + // better. + // + // ``min_memory_usage`` returns settings that will use the minimal amount of + // RAM, at the potential expense of upload and download performance. It + // adjusts the socket buffer sizes, disables the disk cache, lowers the send + // buffer watermarks so that each connection only has at most one block in + // use at any one time. It lowers the outstanding blocks send to the disk + // I/O thread so that connections only have one block waiting to be flushed + // to disk at any given time. It lowers the max number of peers in the peer + // list for torrents. It performs multiple smaller reads when it hashes + // pieces, instead of reading it all into memory before hashing. + // + // This configuration is inteded to be the starting point for embedded + // devices. It will significantly reduce memory usage. + // + // ``high_performance_seed`` returns settings optimized for a seed box, + // serving many peers and that doesn't do any downloading. It has a 128 MB + // disk cache and has a limit of 400 files in its file pool. It support fast + // upload rates by allowing large send buffers. + TORRENT_EXPORT session_settings min_memory_usage(); + TORRENT_EXPORT session_settings high_performance_seed(); + +#ifndef TORRENT_CFG +#error TORRENT_CFG is not defined! +#endif + + void TORRENT_EXPORT TORRENT_CFG(); + + namespace aux + { + struct session_impl; + } + + // this is a holder for the internal session implementation object. Once the + // session destruction is explicitly initiated, this holder is used to + // synchronize the completion of the shutdown. The lifetime of this object + // may outlive session, causing the session destructor to not block. The + // session_proxy destructor will block however, until the underlying session + // is done shutting down. + class TORRENT_EXPORT session_proxy + { + friend class session; + public: + // default constructor, does not refer to any session + // implementation object. + session_proxy() {} + private: + session_proxy(boost::shared_ptr impl) + : m_impl(impl) {} + boost::shared_ptr m_impl; + }; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING +#define TORRENT_LOGPATH_ARG_DEFAULT , std::string logpath = "." +#else +#define TORRENT_LOGPATH_ARG_DEFAULT +#endif + + // The session holds all state that spans multiple torrents. Among other + // things it runs the network loop and manages all torrents. Once it's + // created, the session object will spawn the main thread that will do all + // the work. The main thread will be idle as long it doesn't have any + // torrents to participate in. + class TORRENT_EXPORT session: public boost::noncopyable + { + public: + + // If the fingerprint in the first overload is omited, the client will + // get a default fingerprint stating the version of libtorrent. The + // fingerprint is a short string that will be used in the peer-id to + // identify the client and the client's version. For more details see the + // fingerprint class. The constructor that only takes a fingerprint will + // not open a listen port for the session, to get it running you'll have + // to call ``session::listen_on()``. The other constructor, that takes a + // port range and an interface as well as the fingerprint will + // automatically try to listen on a port on the given interface. For more + // information about the parameters, see ``listen_on()`` function. + // + // The flags paramater can be used to start default features (upnp & + // nat-pmp) and default plugins (ut_metadata, ut_pex and smart_ban). The + // default is to start those things. If you do not want them to start, + // pass 0 as the flags parameter. + // + // The ``alert_mask`` is the same mask that you would send to + // set_alert_mask(). + session(fingerprint const& print = fingerprint("LT" + , LIBTORRENT_VERSION_MAJOR, LIBTORRENT_VERSION_MINOR, 0, 0) + , int flags = start_default_features | add_default_plugins + , boost::uint32_t alert_mask = alert::error_notification + TORRENT_LOGPATH_ARG_DEFAULT) + { + TORRENT_CFG(); + init(std::make_pair(0, 0), "0.0.0.0", print, alert_mask); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + set_log_path(logpath); +#endif + start(flags); + } + session(fingerprint const& print + , std::pair listen_port_range + , char const* listen_interface = "0.0.0.0" + , int flags = start_default_features | add_default_plugins + , int alert_mask = alert::error_notification + TORRENT_LOGPATH_ARG_DEFAULT) + { + TORRENT_CFG(); + TORRENT_ASSERT(listen_port_range.first > 0); + TORRENT_ASSERT(listen_port_range.first < listen_port_range.second); + init(listen_port_range, listen_interface, print, alert_mask); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + set_log_path(logpath); +#endif + start(flags); + } + + // The destructor of session will notify all trackers that our torrents + // have been shut down. If some trackers are down, they will time out. + // All this before the destructor of session returns. So, it's advised + // that any kind of interface (such as windows) are closed before + // destructing the session object. Because it can take a few second for + // it to finish. The timeout can be set with ``set_settings()``. + ~session(); + + // TODO: 2 the ip filter should probably be saved here too + + // flags that determines which aspects of the session should be + // saved when calling save_state(). + enum save_state_flags_t + { + // saves settings (i.e. the session_settings) + save_settings = 0x001, + + // saves dht_settings + save_dht_settings = 0x002, + + // saves dht state such as nodes and node-id, possibly accelerating + // joining the DHT if provided at next session startup. + save_dht_state = 0x004, + + // save proxy_settings + save_proxy = 0x008, + + // save i2p_proxy settings + save_i2p_proxy = 0x010, + + // save pe_settings + save_encryption_settings = 0x020, + + // internal + save_as_map = 0x040, + + // saves RSS feeds + save_feeds = 0x080 + +#ifndef TORRENT_NO_DEPRECATE + , + save_dht_proxy = save_proxy, + save_peer_proxy = save_proxy, + save_web_proxy = save_proxy, + save_tracker_proxy = save_proxy +#endif + }; + + // loads and saves all session settings, including dht_settings, + // encryption settings and proxy settings. ``save_state`` writes all keys + // to the ``entry`` that's passed in, which needs to either not be + // initialized, or initialized as a dictionary. + // + // ``load_state`` expects a lazy_entry which can be built from a bencoded + // buffer with lazy_bdecode(). + // + // The ``flags`` arguments passed in to ``save_state`` can be used to + // filter which parts of the session state to save. By default, all state + // is saved (except for the individual torrents). see save_state_flags_t + void save_state(entry& e, boost::uint32_t flags = 0xffffffff) const; + void load_state(lazy_entry const& e); + + // .. note:: + // these calls are potentially expensive and won't scale well with + // lots of torrents. If you're concerned about performance, consider + // using ``post_torrent_updates()`` instead. + // + // ``get_torrent_status`` returns a vector of the torrent_status for + // every torrent which satisfies ``pred``, which is a predicate function + // which determines if a torrent should be included in the returned set + // or not. Returning true means it should be included and false means + // excluded. The ``flags`` argument is the same as to + // ``torrent_handle::status()``. Since ``pred`` is guaranteed to be + // called for every torrent, it may be used to count the number of + // torrents of different categories as well. + // + // ``refresh_torrent_status`` takes a vector of torrent_status structs + // (for instance the same vector that was returned by + // get_torrent_status() ) and refreshes the status based on the + // ``handle`` member. It is possible to use this function by first + // setting up a vector of default constructed ``torrent_status`` objects, + // only initializing the ``handle`` member, in order to request the + // torrent status for multiple torrents in a single call. This can save a + // significant amount of time if you have a lot of torrents. + // + // Any torrent_status object whose ``handle`` member is not referring to + // a valid torrent are ignored. + void get_torrent_status(std::vector* ret + , boost::function const& pred + , boost::uint32_t flags = 0) const; + void refresh_torrent_status(std::vector* ret + , boost::uint32_t flags = 0) const; + + // This functions instructs the session to post the state_update_alert, + // containing the status of all torrents whose state changed since the + // last time this function was called. + // + // Only torrents who has the state subscription flag set will be + // included. This flag is on by default. See add_torrent_params. + void post_torrent_updates(); + + // internal + io_service& get_io_service(); + + // ``find_torrent()`` looks for a torrent with the given info-hash. In + // case there is such a torrent in the session, a torrent_handle to that + // torrent is returned. In case the torrent cannot be found, an invalid + // torrent_handle is returned. + // + // See ``torrent_handle::is_valid()`` to know if the torrent was found or + // not. + // + // ``get_torrents()`` returns a vector of torrent_handles to all the + // torrents currently in the session. + torrent_handle find_torrent(sha1_hash const& info_hash) const; + std::vector get_torrents() const; + + // You add torrents through the add_torrent() function where you give an + // object with all the parameters. The add_torrent() overloads will block + // until the torrent has been added (or failed to be added) and returns + // an error code and a torrent_handle. In order to add torrents more + // efficiently, consider using async_add_torrent() which returns + // immediately, without waiting for the torrent to add. Notification of + // the torrent being added is sent as add_torrent_alert. + // + // The overload that does not take an error_code throws an exception on + // error and is not available when building without exception support. + // The torrent_handle returned by add_torrent() can be used to retrieve + // information about the torrent's progress, its peers etc. It is also + // used to abort a torrent. + // + // If the torrent you are trying to add already exists in the session (is + // either queued for checking, being checked or downloading) + // ``add_torrent()`` will throw libtorrent_exception which derives from + // ``std::exception`` unless duplicate_is_error is set to false. In that + // case, add_torrent() will return the handle to the existing torrent. + // + // all torrent_handles must be destructed before the session is destructed! +#ifndef BOOST_NO_EXCEPTIONS + torrent_handle add_torrent(add_torrent_params const& params); +#endif + torrent_handle add_torrent(add_torrent_params const& params, error_code& ec); + void async_add_torrent(add_torrent_params const& params); + +#ifndef BOOST_NO_EXCEPTIONS +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 0.14 + TORRENT_DEPRECATED_PREFIX + torrent_handle add_torrent( + torrent_info const& ti + , std::string const& save_path + , entry const& resume_data = entry() + , storage_mode_t storage_mode = storage_mode_sparse + , bool paused = false + , storage_constructor_type sc = default_storage_constructor) TORRENT_DEPRECATED; + + // deprecated in 0.14 + TORRENT_DEPRECATED_PREFIX + torrent_handle add_torrent( + boost::intrusive_ptr ti + , std::string const& save_path + , entry const& resume_data = entry() + , storage_mode_t storage_mode = storage_mode_sparse + , bool paused = false + , storage_constructor_type sc = default_storage_constructor + , void* userdata = 0) TORRENT_DEPRECATED; + + // deprecated in 0.14 + TORRENT_DEPRECATED_PREFIX + torrent_handle add_torrent( + char const* tracker_url + , sha1_hash const& info_hash + , char const* name + , std::string const& save_path + , entry const& resume_data = entry() + , storage_mode_t storage_mode = storage_mode_sparse + , bool paused = false + , storage_constructor_type sc = default_storage_constructor + , void* userdata = 0) TORRENT_DEPRECATED; +#endif +#endif + + // In case you want to destruct the session asynchrounously, you can + // request a session destruction proxy. If you don't do this, the + // destructor of the session object will block while the trackers are + // contacted. If you keep one ``session_proxy`` to the session when + // destructing it, the destructor will not block, but start to close down + // the session, the destructor of the proxy will then synchronize the + // threads. So, the destruction of the session is performed from the + // ``session`` destructor call until the ``session_proxy`` destructor + // call. The ``session_proxy`` does not have any operations on it (since + // the session is being closed down, no operations are allowed on it). + // The only valid operation is calling the destructor:: + // + // class session_proxy + // { + // public: + // session_proxy(); + // ~session_proxy() + // }; + session_proxy abort() { return session_proxy(m_impl); } + + // Pausing the session has the same effect as pausing every torrent in + // it, except that torrents will not be resumed by the auto-manage + // mechanism. Resuming will restore the torrents to their previous paused + // state. i.e. the session pause state is separate from the torrent pause + // state. A torrent is inactive if it is paused or if the session is + // paused. + void pause(); + void resume(); + bool is_paused() const; + + // returns session wide-statistics and status. For more information, see + // the ``session_status`` struct. + session_status status() const; + + // Returns status of the disk cache for this session. For more + // information, see the cache_status type. + cache_status get_cache_status() const; + + // fills out the supplied vector with information for + // each piece that is currently in the disk cache for the torrent with the + // specified info-hash (``ih``). + void get_cache_info(sha1_hash const& ih + , std::vector& ret) const; + + // This adds an RSS feed to the session. The feed will be refreshed + // regularly and optionally add all torrents from the feed, as they + // appear. + // + // Before adding the feed, you must set the ``url`` field to the feed's + // url. It may point to an RSS or an atom feed. The returned feed_handle + // is a handle which is used to interact with the feed, things like + // forcing a refresh or querying for information about the items in the + // feed. For more information, see feed_handle. + feed_handle add_feed(feed_settings const& feed); + + // Removes a feed from being watched by the session. When this + // call returns, the feed handle is invalid and won't refer + // to any feed. + void remove_feed(feed_handle h); + + // Returns a list of all RSS feeds that are being watched by the session. + void get_feeds(std::vector& f) const; + + // starts/stops UPnP, NATPMP or LSD port mappers they are stopped by + // default These functions are not available in case + // ``TORRENT_DISABLE_DHT`` is defined. ``start_dht`` starts the dht node + // and makes the trackerless service available to torrents. The startup + // state is optional and can contain nodes and the node id from the + // previous session. The dht node state is a bencoded dictionary with the + // following entries: + // + // nodes + // A list of strings, where each string is a node endpoint encoded in + // binary. If the string is 6 bytes long, it is an IPv4 address of 4 + // bytes, encoded in network byte order (big endian), followed by a 2 + // byte port number (also network byte order). If the string is 18 + // bytes long, it is 16 bytes of IPv6 address followed by a 2 bytes + // port number (also network byte order). + // + // node-id + // The node id written as a readable string as a hexadecimal number. + // + // ``dht_state`` will return the current state of the dht node, this can + // be used to start up the node again, passing this entry to + // ``start_dht``. It is a good idea to save this to disk when the session + // is closed, and read it up again when starting. + // + // If the port the DHT is supposed to listen on is already in use, and + // exception is thrown, ``asio::error``. + // + // ``stop_dht`` stops the dht node. + // + // ``add_dht_node`` adds a node to the routing table. This can be used if + // your client has its own source of bootstrapping nodes. + // + // ``set_dht_settings`` sets some parameters availavle to the dht node. + // See dht_settings for more information. + // + // ``is_dht_running()`` returns true if the DHT support has been started + // and false + // otherwise. + void start_dht(); + void stop_dht(); + void set_dht_settings(dht_settings const& settings); + bool is_dht_running() const; + + // ``add_dht_node`` takes a host name and port pair. That endpoint will be + // pinged, and if a valid DHT reply is received, the node will be added to + // the routing table. + // + // ``add_dht_router`` adds the given endpoint to a list of DHT router nodes. + // If a search is ever made while the routing table is empty, those nodes will + // be used as backups. Nodes in the router node list will also never be added + // to the regular routing table, which effectively means they are only used + // for bootstrapping, to keep the load off them. + // + // An example routing node that you could typically add is + // ``router.bittorrent.com``. + void add_dht_node(std::pair const& node); + void add_dht_router(std::pair const& node); + + // query the DHT for an immutable item at the ``target`` hash. + // the result is posted as a dht_immutable_item_alert. + void dht_get_item(sha1_hash const& target); + + // query the DHT for a mutable item under the public key ``key``. + // this is an ed25519 key. ``salt`` is optional and may be left + // as an empty string if no salt is to be used. + // if the item is found in the DHT, a dht_mutable_item_alert is + // posted. + void dht_get_item(boost::array key + , std::string salt = std::string()); + + // store the given bencoded data as an immutable item in the DHT. + // the returned hash is the key that is to be used to look the item + // up agan. It's just the sha-1 hash of the bencoded form of the + // structure. + sha1_hash dht_put_item(entry data); + + // store a mutable item. The ``key`` is the public key the blob is + // to be stored under. The optional ``salt`` argument is a string that + // is to be mixed in with the key when determining where in the DHT + // the value is to be stored. The callback function is called from within + // the libtorrent network thread once we've found where to store the blob, + // possibly with the current value stored under the key. + // The values passed to the callback functions are: + // + // entry& value + // the current value stored under the key (may be empty). Also expected + // to be set to the value to be stored by the function. + // + // boost::array& signature + // the signature authenticating the current value. This may be zeroes + // if there is currently no value stored. The functon is expected to + // fill in this buffer with the signature of the new value to store. + // To generate the signature, you may want to use the + // ``sign_mutable_item`` function. + // + // boost::uint64_t& seq + // current sequence number. May be zero if there is no current value. + // The function is expected to set this to the new sequence number of + // the value that is to be stored. Sequence numbers must be monotonically + // increasing. Attempting to overwrite a value with a lower or equal + // sequence number will fail, even if the signature is correct. + // + // std::string const& salt + // this is the salt that was used for this put call. + // + // Since the callback function ``cb`` is called from within libtorrent, + // it is critical to not perform any blocking operations. Ideally not + // even locking a mutex. Pass any data required for this function along + // with the function object's context and make the function entirely + // self-contained. The only reason data blobs' values are computed + // via a function instead of just passing in the new value is to avoid + // race conditions. If you want to *update* the value in the DHT, you + // must first retrieve it, then modify it, then write it back. The way + // the DHT works, it is natural to always do a lookup before storing and + // calling the callback in between is convenient. + void dht_put_item(boost::array key + , boost::function& + , boost::uint64_t&, std::string const&)> cb + , std::string salt = std::string()); + +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 0.15 + // use save_state and load_state instead + TORRENT_DEPRECATED_PREFIX + entry dht_state() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void start_dht(entry const& startup_state) TORRENT_DEPRECATED; +#endif + + // This function adds an extension to this session. The argument is a + // function object that is called with a ``torrent*`` and which should + // return a ``boost::shared_ptr``. To write custom + // plugins, see `libtorrent plugins`_. For the typical bittorrent client + // all of these extensions should be added. The main plugins implemented + // in libtorrent are: + // + // metadata extension + // Allows peers to download the metadata (.torren files) from the swarm + // directly. Makes it possible to join a swarm with just a tracker and + // info-hash. + // + // :: + // + // #include + // ses.add_extension(&libtorrent::create_metadata_plugin); + // + // uTorrent metadata + // Same as ``metadata extension`` but compatible with uTorrent. + // + // :: + // + // #include + // ses.add_extension(&libtorrent::create_ut_metadata_plugin); + // + // uTorrent peer exchange + // Exchanges peers between clients. + // + // :: + // + // #include + // ses.add_extension(&libtorrent::create_ut_pex_plugin); + // + // smart ban plugin + // A plugin that, with a small overhead, can ban peers + // that sends bad data with very high accuracy. Should + // eliminate most problems on poisoned torrents. + // + // :: + // + // #include + // ses.add_extension(&libtorrent::create_smart_ban_plugin); + // + // + // .. _`libtorrent plugins`: libtorrent_plugins.html + void add_extension(boost::function( + torrent*, void*)> ext); + void add_extension(boost::shared_ptr ext); + + // These functions are not available if ``TORRENT_DISABLE_GEO_IP`` is + // defined. They expects a path to the `MaxMind ASN database`_ and + // `MaxMind GeoIP database`_ respectively. This will be used to look up + // which AS and country peers belong to. + // + // ``as_for_ip`` returns the AS number for the IP address specified. If + // the IP is not in the database or the ASN database is not loaded, 0 is + // returned. + // + // .. _`MaxMind ASN database`: http://www.maxmind.com/app/asnum + // .. _`MaxMind GeoIP database`: http://www.maxmind.com/app/geolitecountry + void load_asnum_db(char const* file); + void load_country_db(char const* file); + int as_for_ip(address const& addr); +#ifndef TORRENT_NO_DEPRECATE +#if TORRENT_USE_WSTRING + // all wstring APIs are deprecated since 0.16.11 + // instead, use the wchar -> utf8 conversion functions + // and pass in utf8 strings + TORRENT_DEPRECATED_PREFIX + void load_country_db(wchar_t const* file) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void load_asnum_db(wchar_t const* file) TORRENT_DEPRECATED; +#endif // TORRENT_USE_WSTRING +#endif // TORRENT_NO_DEPRECATE + +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 0.15 + // use load_state and save_state instead + TORRENT_DEPRECATED_PREFIX + void load_state(entry const& ses_state) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + entry state() const TORRENT_DEPRECATED; +#endif + + // Sets a filter that will be used to reject and accept incoming as well + // as outgoing connections based on their originating ip address. The + // default filter will allow connections to any ip address. To build a + // set of rules for which addresses are accepted and not, see ip_filter. + // + // Each time a peer is blocked because of the IP filter, a + // peer_blocked_alert is generated. ``get_ip_filter()`` Returns the + // ip_filter currently in the session. See ip_filter. + void set_ip_filter(ip_filter const& f); + ip_filter get_ip_filter() const; + + // apply port_filter ``f`` to incoming and outgoing peers. a port filter + // will reject making outgoing peer connections to certain remote ports. + // The main intention is to be able to avoid triggering certain + // anti-virus software by connecting to SMTP, FTP ports. + void set_port_filter(port_filter const& f); + + // sets and gets the raw peer ID used by libtorrent. When anonymous + // mode is set the peer ID is randomized per peer anyway. + void set_peer_id(peer_id const& pid); + peer_id id() const; + + // sets the key sent to trackers. If it's not set, it is initialized + // by libtorrent. The key may be used by the tracker to identify the + // peer potentially across you changing your IP. + void set_key(int key); + + + // ``is_listening()`` will tell you whether or not the session has + // successfully opened a listening port. If it hasn't, this function will + // return false, and then you can use ``listen_on()`` to make another + // attempt. + // + // ``listen_port()`` returns the port we ended up listening on. Since you + // just pass a port-range to the constructor and to ``listen_on()``, to + // know which port it ended up using, you have to ask the session using + // this function. + // + // ``listen_on()`` will change the listen port and/or the listen + // interface. If the session is already listening on a port, this socket + // will be closed and a new socket will be opened with these new + // settings. The port range is the ports it will try to listen on, if the + // first port fails, it will continue trying the next port within the + // range and so on. The interface parameter can be left as 0, in that + // case the os will decide which interface to listen on, otherwise it + // should be the ip-address of the interface you want the listener socket + // bound to. ``listen_on()`` returns the error code of the operation in + // ``ec``. If this indicates success, the session is listening on a port + // within the specified range. If it fails, it will also generate an + // appropriate alert (listen_failed_alert). + // + // If all ports in the specified range fails to be opened for listening, + // libtorrent will try to use port 0 (which tells the operating system to + // pick a port that's free). If that still fails you may see a + // listen_failed_alert with port 0 even if you didn't ask to listen on + // it. + // + // It is possible to prevent libtorrent from binding to port 0 by passing + // in the flag ``session::no_system_port`` in the ``flags`` argument. + // + // The interface parameter can also be a hostname that will resolve to + // the device you want to listen on. If you don't specify an interface, + // libtorrent may attempt to listen on multiple interfaces (typically + // 0.0.0.0 and ::). This means that if your IPv6 interface doesn't work, + // you may still see a listen_failed_alert, even though the IPv4 port + // succeeded. + // + // The ``flags`` parameter can either be 0 or + // ``session::listen_reuse_address``, which will set the reuse address + // socket option on the listen socket(s). By default, the listen socket + // does not use reuse address. If you're running a service that needs to + // run on a specific port no matter if it's in use, set this flag. + // + // If you're also starting the DHT, it is a good idea to do that after + // you've called ``listen_on()``, since the default listen port for the + // DHT is the same as the tcp listen socket. If you start the DHT first, + // it will assume the tcp port is free and open the udp socket on that + // port, then later, when ``listen_on()`` is called, it may turn out that + // the tcp port is in use. That results in the DHT and the bittorrent + // socket listening on different ports. If the DHT is active when + // ``listen_on`` is called, the udp port will be rebound to the new port, + // if it was configured to use the same port as the tcp socket, and if + // the listen_on call failed to bind to the same port that the udp uses. + // + // If you want the OS to pick a port for you, pass in 0 as both first and + // second. + // + // The reason why it's a good idea to run the DHT and the bittorrent + // socket on the same port is because that is an assumption that may be + // used to increase performance. One way to accelerate the connecting of + // peers on windows may be to first ping all peers with a DHT ping + // packet, and connect to those that responds first. On windows one can + // only connect to a few peers at a time because of a built in limitation + // (in XP Service pack 2). + void listen_on( + std::pair const& port_range + , error_code& ec + , const char* net_interface = 0 + , int flags = 0); + unsigned short listen_port() const; + unsigned short ssl_listen_port() const; + bool is_listening() const; + + // if the listen port failed in some way you can retry to listen on + // another port- range with this function. If the listener succeeded and + // is currently listening, a call to this function will shut down the + // listen port and reopen it using these new properties (the given + // interface and port range). As usual, if the interface is left as 0 + // this function will return false on failure. If it fails, it will also + // generate alerts describing the error. It will return true on success. + enum listen_on_flags_t + { +#ifndef TORRENT_NO_DEPRECATE + // this is always on starting with 0.16.2 + listen_reuse_address = 0x01, +#endif + listen_no_system_port = 0x02 + }; + +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 0.16 + TORRENT_DEPRECATED_PREFIX + bool listen_on( + std::pair const& port_range + , const char* net_interface = 0 + , int flags = 0) TORRENT_DEPRECATED; +#endif + + // flags to be passed in to remove_torrent(). + enum options_t + { + // delete the files belonging to the torrent from disk. + delete_files = 1 + }; + + // flags to be passed in to the session constructor + enum session_flags_t + { + // this will add common extensions like ut_pex, ut_metadata, lt_tex + // smart_ban and possibly others. + add_default_plugins = 1, + + // this will start features like DHT, local service discovery, UPnP + // and NAT-PMP. + start_default_features = 2 + }; + + // ``remove_torrent()`` will close all peer connections associated with + // the torrent and tell the tracker that we've stopped participating in + // the swarm. This operation cannot fail. When it completes, you will + // receive a torrent_removed_alert. + // + // The optional second argument ``options`` can be used to delete all the + // files downloaded by this torrent. To do so, pass in the value + // ``session::delete_files``. The removal of the torrent is asyncronous, + // there is no guarantee that adding the same torrent immediately after + // it was removed will not throw a libtorrent_exception exception. Once + // the torrent is deleted, a torrent_deleted_alert is posted. + void remove_torrent(const torrent_handle& h, int options = 0); + + // Sets the session settings and the packet encryption settings + // respectively. See session_settings and pe_settings for more + // information on available options. + void set_settings(session_settings const& s); + session_settings settings() const; + void set_pe_settings(pe_settings const& settings); + pe_settings get_pe_settings() const; + + // These functions sets and queries the proxy settings to be used for the + // session. + // + // For more information on what settings are available for proxies, see + // proxy_settings. If the session is not in anonymous mode, proxies that + // aren't working or fail, will automatically be disabled and packets + // will flow without using any proxy. If you want to enforce using a + // proxy, even when the proxy doesn't work, enable anonymous_mode in + // session_settings. + void set_proxy(proxy_settings const& s); + proxy_settings proxy() const; + +#ifdef TORRENT_STATS + // internal + void enable_stats_logging(bool s); +#endif + + // ``set_i2p_proxy`` sets the i2p_ proxy, and tries to open a persistant + // connection to it. The only used fields in the proxy settings structs + // are ``hostname`` and ``port``. + // + // ``i2p_proxy`` returns the current i2p proxy in use. + // + // .. _i2p: http://www.i2p2.de + void set_i2p_proxy(proxy_settings const& s); + proxy_settings i2p_proxy() const; + +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 0.16 + // Get the number of uploads. + TORRENT_DEPRECATED_PREFIX + int num_uploads() const TORRENT_DEPRECATED; + + // Get the number of connections. This number also contains the + // number of half open connections. + TORRENT_DEPRECATED_PREFIX + int num_connections() const TORRENT_DEPRECATED; + + // deprecated in 0.15. + TORRENT_DEPRECATED_PREFIX + void set_peer_proxy(proxy_settings const& s) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_web_seed_proxy(proxy_settings const& s) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_tracker_proxy(proxy_settings const& s) TORRENT_DEPRECATED; + + TORRENT_DEPRECATED_PREFIX + proxy_settings peer_proxy() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + proxy_settings web_seed_proxy() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + proxy_settings tracker_proxy() const TORRENT_DEPRECATED; + + TORRENT_DEPRECATED_PREFIX + void set_dht_proxy(proxy_settings const& s) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + proxy_settings dht_proxy() const TORRENT_DEPRECATED; + + // deprecated in 0.16 + TORRENT_DEPRECATED_PREFIX + int upload_rate_limit() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + int download_rate_limit() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + int local_upload_rate_limit() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + int local_download_rate_limit() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + int max_half_open_connections() const TORRENT_DEPRECATED; + + TORRENT_DEPRECATED_PREFIX + void set_local_upload_rate_limit(int bytes_per_second) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_local_download_rate_limit(int bytes_per_second) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_upload_rate_limit(int bytes_per_second) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_download_rate_limit(int bytes_per_second) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_max_uploads(int limit) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_max_connections(int limit) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_max_half_open_connections(int limit) TORRENT_DEPRECATED; + + TORRENT_DEPRECATED_PREFIX + int max_connections() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + int max_uploads() const TORRENT_DEPRECATED; +#endif + + // ``pop_alert()`` is used to ask the session if any errors or events has + // occurred. With set_alert_mask() you can filter which alerts to receive + // through ``pop_alert()``. For information about the alert categories, + // see alerts_. + // + // ``pop_alerts()`` pops all pending alerts in a single call. In high + // performance environments with a very high alert churn rate, this can + // save significant amount of time compared to popping alerts one at a + // time. Each call requires one round-trip to the network thread. If + // alerts are produced in a higher rate than they can be popped (when + // popped one at a time) it's easy to get stuck in an infinite loop, + // trying to drain the alert queue. Popping the entire queue at once + // avoids this problem. + // + // However, the ``pop_alerts`` function comes with significantly more + // responsibility. You pass in an *empty* ``std::dequeue`` to it. + // If it's not empty, all elements in it will be deleted and then + // cleared. All currently pending alerts are returned by being swapped + // into the passed in container. The responsibility of deleting the + // alerts is transferred to the caller. This means you need to call + // delete for each item in the returned dequeue. It's probably a good + // idea to delete the alerts as you handle them, to save one extra pass + // over the dequeue. + // + // Alternatively, you can pass in the same container the next time you + // call ``pop_alerts``. + // + // ``wait_for_alert`` blocks until an alert is available, or for no more + // than ``max_wait`` time. If ``wait_for_alert`` returns because of the + // time-out, and no alerts are available, it returns 0. If at least one + // alert was generated, a pointer to that alert is returned. The alert is + // not popped, any subsequent calls to ``wait_for_alert`` will return the + // same pointer until the alert is popped by calling ``pop_alert``. This + // is useful for leaving any alert dispatching mechanism independent of + // this blocking call, the dispatcher can be called and it can pop the + // alert independently. + // + // .. note:: + // Although these functions are all thread-safe, popping alerts from + // multiple separate threads may introduce race conditions in that + // the thread issuing an asynchronous operation may not be the one + // receiving the alert with the result. + // + // In the python binding, ``wait_for_alert`` takes the number of + // milliseconds to wait as an integer. + // + // To control the max number of alerts that's queued by the session, see + // ``session_settings::alert_queue_size``. + // + // save_resume_data_alert and save_resume_data_failed_alert are always + // posted, regardelss of the alert mask. + std::auto_ptr pop_alert(); + void pop_alerts(std::deque* alerts); + alert const* wait_for_alert(time_duration max_wait); + +#ifndef TORRENT_NO_DEPRECATE + TORRENT_DEPRECATED_PREFIX + void set_severity_level(alert::severity_t s) TORRENT_DEPRECATED; + + TORRENT_DEPRECATED_PREFIX + size_t set_alert_queue_size_limit(size_t queue_size_limit_) TORRENT_DEPRECATED; +#endif + + // Changes the mask of which alerts to receive. By default only errors + // are reported. ``m`` is a bitmask where each bit represents a category + // of alerts. + // + // See category_t enum for options. + void set_alert_mask(boost::uint32_t m); + + // This sets a function to be called (from within libtorrent's netowrk + // thread) every time an alert is posted. Since the function (``fun``) is + // run in libtorrent's internal thread, it may not call any of + // libtorrent's external API functions. Doing so results in a dead lock. + // + // The main intention with this function is to support integration with + // platform-dependent message queues or signalling systems. For instance, + // on windows, one could post a message to an HNWD or on linux, write to + // a pipe or an eventfd. + void set_alert_dispatch(boost::function)> const& fun); + + // internal + connection_queue& get_connection_queue(); + + // Starts and stops Local Service Discovery. This service will broadcast + // the infohashes of all the non-private torrents on the local network to + // look for peers on the same swarm within multicast reach. + // + // It is turned off by default. + void start_lsd(); + void stop_lsd(); + + // Starts and stops the UPnP service. When started, the listen port and + // the DHT port are attempted to be forwarded on local UPnP router + // devices. + // + // The upnp object returned by ``start_upnp()`` can be used to add and + // remove arbitrary port mappings. Mapping status is returned through the + // portmap_alert and the portmap_error_alert. The object will be valid + // until ``stop_upnp()`` is called. See upnp-and-nat-pmp_. + // + // It is off by default. + void start_upnp(); + void stop_upnp(); + + // protocols used by add_port_mapping() + enum protocol_type { udp = 1, tcp = 2 }; + + // add_port_mapping adds a port forwarding on UPnP and/or NAT-PMP, + // whichever is enabled. The return value is a handle referring to the + // port mapping that was just created. Pass it to delete_port_mapping() + // to remove it. + int add_port_mapping(protocol_type t, int external_port, int local_port); + void delete_port_mapping(int handle); + + // Starts and stops the NAT-PMP service. When started, the listen port + // and the DHT port are attempted to be forwarded on the router through + // NAT-PMP. + // + // The natpmp object returned by ``start_natpmp()`` can be used to add + // and remove arbitrary port mappings. Mapping status is returned through + // the portmap_alert and the portmap_error_alert. The object will be + // valid until ``stop_natpmp()`` is called. See upnp-and-nat-pmp_. + // + // It is off by default. + void start_natpmp(); + void stop_natpmp(); + + private: + + void init(std::pair listen_range, char const* listen_interface + , fingerprint const& id, boost::uint32_t alert_mask); + void set_log_path(std::string const& p); + void start(int flags); + + // data shared between the main thread + // and the working thread + boost::shared_ptr m_impl; + }; + +} + +#endif // TORRENT_SESSION_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/session_settings.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/session_settings.hpp new file mode 100644 index 0000000000..e63c2b1e6c --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/session_settings.hpp @@ -0,0 +1,1569 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SESSION_SETTINGS_HPP_INCLUDED +#define TORRENT_SESSION_SETTINGS_HPP_INCLUDED + +#include "libtorrent/version.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/version.hpp" + +#include +#include + +namespace libtorrent +{ + + // The ``proxy_settings`` structs contains the information needed to + // direct certain traffic to a proxy. + struct TORRENT_EXPORT proxy_settings + { + // defaults constructs proxy settings, initializing it to the default + // settings. + proxy_settings() : type(none) + , port(0), proxy_hostnames(true) + , proxy_peer_connections(true) + {} + + // the name or IP of the proxy server. ``port`` is the port number the + // proxy listens to. If required, ``username`` and ``password`` can be + // set to authenticate with the proxy. + std::string hostname; + + // when using a proy type that requires authentication, the username + // and password fields must be set to the credentials for the proxy. + std::string username; + std::string password; + + // the type of proxy to use. Assign one of these to the + // proxy_settings::type field. + enum proxy_type + { + // This is the default, no proxy server is used, all other fields are + // ignored. + none, + + // The server is assumed to be a `SOCKS4 server`_ that requires a + // username. + // + // .. _`SOCKS4 server`: http://www.ufasoft.com/doc/socks4_protocol.htm + socks4, + + // The server is assumed to be a SOCKS5 server (`RFC 1928`_) that does + // not require any authentication. The username and password are + // ignored. + // + // .. _`RFC 1928`: http://www.faqs.org/rfcs/rfc1928.html + socks5, + + // The server is assumed to be a SOCKS5 server that supports plain + // text username and password authentication (`RFC 1929`_). The + // username and password specified may be sent to the proxy if it + // requires. + // + // .. _`RFC 1929`: http://www.faqs.org/rfcs/rfc1929.html + socks5_pw, + + // The server is assumed to be an HTTP proxy. If the transport used + // for the connection is non-HTTP, the server is assumed to support + // the CONNECT_ method. i.e. for web seeds and HTTP trackers, a plain + // proxy will suffice. The proxy is assumed to not require + // authorization. The username and password will not be used. + // + // .. _CONNECT: http://tools.ietf.org/html/draft-luotonen-web-proxy-tunneling-01 + http, + + // The server is assumed to be an HTTP proxy that requires user + // authorization. The username and password will be sent to the proxy. + http_pw, + + // route through a i2p SAM proxy + i2p_proxy + }; + + // tells libtorrent what kind of proxy server it is. See proxy_type + // enum for options + boost::uint8_t type; + + // the port the proxy server is running on + boost::uint16_t port; + + // defaults to true. It means that hostnames should be attempted to be + // resolved through the proxy instead of using the local DNS service. + // This is only supported by SOCKS5 and HTTP. + bool proxy_hostnames; + + // determines whether or not to excempt peer and web seed connections + // from using the proxy. This defaults to true, i.e. peer connections are + // proxied by default. + bool proxy_peer_connections; + }; + + // This holds most of the session-wide settings in libtorrent. Pass this + // to session::set_settings() to change the settings, initialize it from + // session::get_settings() to get the current settings. + struct TORRENT_EXPORT session_settings + { + // initializes the session_settings to the default settings. + session_settings(std::string const& user_agent = "libtorrent/" + LIBTORRENT_VERSION); + ~session_settings(); + + // automatically set to the libtorrent version you're using in order to + // be forward binary compatible. This field should not be changed. + int version; + + // the client identification to the tracker. The recommended format of + // this string is: "ClientName/ClientVersion + // libtorrent/libtorrentVersion". This name will not only be used when + // making HTTP requests, but also when sending extended headers to peers + // that support that extension. + std::string user_agent; + + // the number of seconds the tracker connection will wait from when it + // sent the request until it considers the tracker to have timed-out. + // Default value is 60 seconds. + int tracker_completion_timeout; + + // the number of seconds to wait to receive any data from the tracker. If + // no data is received for this number of seconds, the tracker will be + // considered as having timed out. If a tracker is down, this is the kind + // of timeout that will occur. The default value is 20 seconds. + int tracker_receive_timeout; + + // the time to wait when sending a stopped message before considering a + // tracker to have timed out. this is usually shorter, to make the client + // quit faster + // + // This is given in seconds. Default is 10 seconds. + int stop_tracker_timeout; + + // the maximum number of bytes in a tracker response. If a response size + // passes this number it will be rejected and the connection will be + // closed. On gzipped responses this size is measured on the uncompressed + // data. So, if you get 20 bytes of gzip response that'll expand to 2 + // megs, it will be interrupted before the entire response has been + // uncompressed (given your limit is lower than 2 megs). Default limit is + // 1 megabyte. + int tracker_maximum_response_length; + + // controls the number of seconds from a request is sent until it times + // out if no piece response is returned. + int piece_timeout; + + // the number of seconds one block (16kB) is expected to be received + // within. If it's not, the block is requested from a different peer + int request_timeout; + + // the length of the request queue given in the number of seconds it + // should take for the other end to send all the pieces. i.e. the actual + // number of requests depends on the download rate and this number. + int request_queue_time; + + // the number of outstanding block requests a peer is allowed to queue up + // in the client. If a peer sends more requests than this (before the + // first one has been sent) the last request will be dropped. the higher + // this is, the faster upload speeds the client can get to a single peer. + int max_allowed_in_request_queue; + + // the maximum number of outstanding requests to send to a peer. This + // limit takes precedence over request_queue_time. i.e. no matter the + // download speed, the number of outstanding requests will never exceed + // this limit. + int max_out_request_queue; + + // if a whole piece can be downloaded in this number of seconds, or less, + // the peer_connection will prefer to request whole pieces at a time from + // this peer. The benefit of this is to better utilize disk caches by + // doing localized accesses and also to make it easier to identify bad + // peers if a piece fails the hash check. + int whole_pieces_threshold; + + // the number of seconds to wait for any activity on the peer wire before + // closing the connectiong due to time out. This defaults to 120 seconds, + // since that's what's specified in the protocol specification. After + // half the time out, a keep alive message is sent. + int peer_timeout; + + // same as peer_timeout, but only applies to url-seeds. this is usually + // set lower, because web servers are expected to be more reliable. This + // value defaults to 20 seconds. + int urlseed_timeout; + + // controls the pipelining with the web server. When using persistent + // connections to HTTP 1.1 servers, the client is allowed to send more + // requests before the first response is received. This number controls + // the number of outstanding requests to use with url-seeds. Default is + // 5. + int urlseed_pipeline_size; + + // time to wait until a new retry takes place + int urlseed_wait_retry; + + // sets the upper limit on the total number of files this session will + // keep open. The reason why files are left open at all is that some anti + // virus software hooks on every file close, and scans the file for + // viruses. deferring the closing of the files will be the difference + // between a usable system and a completely hogged down system. Most + // operating systems also has a limit on the total number of file + // descriptors a process may have open. It is usually a good idea to find + // this limit and set the number of connections and the number of files + // limits so their sum is slightly below it. + int file_pool_size; + + // determines if connections from the same IP address as existing + // connections should be rejected or not. Multiple connections from the + // same IP address is not allowed by default, to prevent abusive behavior + // by peers. It may be useful to allow such connections in cases where + // simulations are run on the same machie, and all peers in a swarm has + // the same IP address. + bool allow_multiple_connections_per_ip; + + // the maximum times we try to connect to a peer before stop connecting + // again. If a peer succeeds, its failcounter is reset. If a peer is + // retrieved from a peer source (other than DHT) the failcount is + // decremented by one, allowing another try. + int max_failcount; + + // the number of seconds to wait to reconnect to a peer. this time is + // multiplied with the failcount. + int min_reconnect_time; + + // the number of seconds to wait after a connection attempt is initiated + // to a peer until it is considered as having timed out. The default is + // 10 seconds. This setting is especially important in case the number of + // half-open connections are limited, since stale half-open connection + // may delay the connection of other peers considerably. + int peer_connect_timeout; + + // if set to true, upload, download and unchoke limits are ignored for + // peers on the local network. + bool ignore_limits_on_local_network; + + // the number of connection attempts that are made per second. If a + // number < 0 is specified, it will default to 200 connections per + // second. If 0 is specified, it means don't make outgoing connections at + // all. + int connection_speed; + + // if this is set to true, have messages will be sent to peers that + // already have the piece. This is typically not necessary, but it might + // be necessary for collecting statistics in some cases. Default is + // false. + bool send_redundant_have; + + // prevents outgoing bitfields from being full. If the client is seed, a + // few bits will be set to 0, and later filled in with have-messages. + // This is an old attempt to prevent certain ISPs from stopping people + // from seeding. + bool lazy_bitfields; + + // if a peer is uninteresting and uninterested for longer than this + // number of seconds, it will be disconnected. default is 10 minutes + int inactivity_timeout; + + // the number of seconds between chokes/unchokes. On this interval, peers + // are re-evaluated for being choked/unchoked. This is defined as 30 + // seconds in the protocol, and it should be significantly longer than + // what it takes for TCP to ramp up to it's max rate. + int unchoke_interval; + + // the number of seconds between each *optimistic* unchoke. On this + // timer, the currently optimistically unchoked peer will change. + int optimistic_unchoke_interval; + + // the ip address passed along to trackers as the ``&ip=`` parameter. If + // left as the default (an empty string), that parameter is omitted. Most + // trackers ignore this argument. This is here for completeness for + // edge-cases where it may be useful. + std::string announce_ip; + + // the number of peers we want from each tracker request. It defines what + // is sent as the ``&num_want=`` parameter to the tracker. Stopped + // messages always send num_want=0. This setting control what to say in + // the case where we actually want peers. + int num_want; + + // specifies the number of pieces we need before we switch to rarest + // first picking. This defaults to 4, which means the 4 first pieces in + // any torrent are picked at random, the following pieces are picked in + // rarest first order. + int initial_picker_threshold; + + // the number of allowed pieces to send to choked peers that supports the + // fast extensions + int allowed_fast_set_size; + + // options for session_settings::suggest_mode. + enum suggest_mode_t + { + // the default. will not send out suggest messages. + no_piece_suggestions = 0, + + // send out suggest messages for the most recent pieces that are in + // the read cache. + suggest_read_cache = 1 + }; + + // this determines which pieces will be suggested to peers suggest read + // cache will make libtorrent suggest pieces that are fresh in the disk + // read cache, to potentially lower disk access and increase the cache + // hit ratio + // + // for options, see suggest_mode_t. + int suggest_mode; + + // the maximum number of bytes a connection may have pending in the disk + // write queue before its download rate is being throttled. This prevents + // fast downloads to slow medias to allocate more memory indefinitely. + // This should be set to at least 16 kB to not completely disrupt normal + // downloads. If it's set to 0, you will be starving the disk thread and + // nothing will be written to disk. this is a per session setting. + // + // When this limit is reached, the peer connections will stop reading + // data from their sockets, until the disk thread catches up. Setting + // this too low will severly limit your download rate. + int max_queued_disk_bytes; + + // this is the low watermark for the disk buffer queue. whenever the + // number of queued bytes exceed the max_queued_disk_bytes, libtorrent + // will wait for it to drop below this value before issuing more reads + // from the sockets. If set to 0, the low watermark will be half of the + // max queued disk bytes + int max_queued_disk_bytes_low_watermark; + + // the number of seconds to wait for a handshake response from a peer. If + // no response is received within this time, the peer is disconnected. + int handshake_timeout; + + // determines how the DHT is used. If this is true, the DHT will only be + // used for torrents where all trackers in its tracker list has failed. + // Either by an explicit error message or a time out. This is false by + // default, which means the DHT is used by default regardless of if the + // trackers fail or not. + bool use_dht_as_fallback; + + // determines whether or not the torrent's piece hashes are kept in + // memory after the torrent becomes a seed or not. If it is set to + // ``true`` the hashes are freed once the torrent is a seed (they're not + // needed anymore since the torrent won't download anything more). If + // it's set to false they are not freed. If they are freed, the + // torrent_info returned by get_torrent_info() will return an object that + // may be incomplete, that cannot be passed back to async_add_torrent() + // and add_torrent() for instance. + bool free_torrent_hashes; + + // indicates whether or not the UPnP implementation should ignore any + // broadcast response from a device whose address is not the configured + // router for this machine. i.e. it's a way to not talk to other people's + // routers by mistake. + bool upnp_ignore_nonrouters; + + // This is the minimum send buffer target size (send buffer includes + // bytes pending being read from disk). For good and snappy seeding + // performance, set this fairly high, to at least fit a few blocks. This + // is essentially the initial window size which will determine how fast + // we can ramp up the send rate + int send_buffer_low_watermark; + + // the upper limit of the send buffer low-watermark. + // + // if the send buffer has fewer bytes than this, we'll read another 16kB + // block onto it. If set too small, upload rate capacity will suffer. If + // set too high, memory will be wasted. The actual watermark may be lower + // than this in case the upload rate is low, this is the upper limit. + int send_buffer_watermark; + + // the current upload rate to a peer is multiplied by this factor to get + // the send buffer watermark. The factor is specified as a percentage. + // i.e. 50 indicates a factor of 0.5. + // + // This product is clamped to the send_buffer_watermark setting to not + // exceed the max. For high speed upload, this should be set to a greater + // value than 100. The default is 50. + // + // For high capacity connections, setting this higher can improve upload + // performance and disk throughput. Setting it too high may waste RAM and + // create a bias towards read jobs over write jobs. + int send_buffer_watermark_factor; + +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 0.16 defaults to true. When true, if there is a global + // upload limit set and the current upload rate is less than 90% of that, + // another upload slot is opened. If the upload rate has been saturated + // for an extended period of time, on upload slot is closed. The number + // of upload slots will never be less than what has been set by + // ``session::set_max_uploads()``. To query the current number of upload + // slots, see ``session_status::allowed_upload_slots``. + bool auto_upload_slots; + + // When set, and ``auto_upload_slots`` is set, the max upload slots + // setting is used as a minimum number of unchoked slots. This algorithm + // is designed to prevent the peer from spreading its upload capacity too + // thin, but still open more slots in order to utilize the full capacity. + bool auto_upload_slots_rate_based; +#endif + + // the different choking algorithms available. Set + // session_settings::choking_algorithm to one of these + enum choking_algorithm_t + { + // the traditional choker with a fixed number of unchoke slots, as + // specified by session::set_max_uploads().. + fixed_slots_choker, + + // opens at least the number of slots as specified by + // session::set_max_uploads() but opens up more slots if the upload + // capacity is not saturated. This unchoker will work just like the + // ``fixed_slot_choker`` if there's no global upload rate limit set. + auto_expand_choker, + + // opens up unchoke slots based on the upload rate achieved to peers. + // The more slots that are opened, the marginal upload rate required + // to open up another slot increases. + rate_based_choker, + // attempts to optimize download rate by finding the reciprocation + // rate of each peer individually and prefers peers that gives the + // highest *return on investment*. It still allocates all upload + // capacity, but shuffles it around to the best peers first. For this + // choker to be efficient, you need to set a global upload rate limit + // session_settings::upload_rate_limit. For more information about + // this choker, see the paper_. + // + // .. _paper: http://bittyrant.cs.washington.edu/#papers + bittyrant_choker + }; + + // specifies which algorithm to use to determine which peers to unchoke. + // This setting replaces the deprecated settings ``auto_upload_slots`` + // and ``auto_upload_slots_rate_based``. For options, see + // choking_algorithm_t. + int choking_algorithm; + + // the different choking algorithms available when seeding. Set + // session_settings::seed_choking_algorithm to one of these + enum seed_choking_algorithm_t + { + // round-robins the peers that are unchoked when seeding. This + // distributes the upload bandwidht uniformly and fairly. It minimizes + // the ability for a peer to download everything without + // redistributing it. + round_robin, + + // unchokes the peers we can send to the fastest. This might be a bit + // more reliable in utilizing all available capacity. + fastest_upload, + + // prioritizes peers who have just started or are just about to finish + // the download. The intention is to force peers in the middle of the + // download to trade with each other. + anti_leech + }; + + // controls the seeding unchoke behavior. For options, see + // seed_choking_algorithm_t. + int seed_choking_algorithm; + + // specifies if parole mode should be used. Parole mode means that peers + // that participate in pieces that fail the hash check are put in a mode + // where they are only allowed to download whole pieces. If the whole + // piece a peer in parole mode fails the hash check, it is banned. If a + // peer participates in a piece that passes the hash check, it is taken + // out of parole mode. + bool use_parole_mode; + + // the disk write and read cache. It is specified in units of 16 KiB + // blocks. Buffers that are part of a peer's send or receive buffer also + // count against this limit. Send and receive buffers will never be + // denied to be allocated, but they will cause the actual cached blocks + // to be flushed or evicted. If this is set to -1, the cache size is + // automatically set to the amount of physical RAM available in the + // machine divided by 8. If the amount of physical RAM cannot be + // determined, it's set to 1024 (= 16 MiB). + // + // Disk buffers are allocated using a pool allocator, the number of + // blocks that are allocated at a time when the pool needs to grow can be + // specified in ``cache_buffer_chunk_size``. This defaults to 16 blocks. + // Lower numbers saves memory at the expense of more heap allocations. It + // must be at least 1. + int cache_size; + + // this is the number of disk buffer blocks (16 kiB) that should be + // allocated at a time. It must be at least 1. Lower number saves memory + // at the expense of more heap allocations + int cache_buffer_chunk_size; + + // the number of seconds a write cache entry sits idle in the cache + // before it's forcefully flushed to disk. + int cache_expiry; + + // when set to true (default), the disk cache is also used to cache + // pieces read from disk. Blocks for writing pieces takes presedence. + bool use_read_cache; + + // defaults to 0. If set to something greater than 0, the disk read cache + // will not be evicted by cache misses and will explicitly be controlled + // based on the rarity of pieces. Rare pieces are more likely to be + // cached. This would typically be used together with ``suggest_mode`` + // set to ``suggest_read_cache``. The value is the number of pieces to + // keep in the read cache. If the actual read cache can't fit as many, it + // will essentially be clamped. + bool explicit_read_cache; + + // the number of seconds in between each refresh of a part of the + // explicit read cache. Torrents take turns in refreshing and this is the + // time in between each torrent refresh. Refreshing a torrent's explicit + // read cache means scanning all pieces and picking a random set of the + // rarest ones. There is an affinity to pick pieces that are already in + // the cache, so that subsequent refreshes only swaps in pieces that are + // rarer than whatever is in the cache at the time. + int explicit_cache_interval; + + // the buffer modes to use for reading and writing. Set + // session_settings::disk_io_read_mode and disk_io_write_mode to one of + // these. + enum io_buffer_mode_t + { + // This is the default and files are opened normally, with the OS + // caching reads and writes. + enable_os_cache = 0, + // This will open files in unbuffered mode for files where every read + // and write would be sector aligned. Using aligned disk offsets is a + // requirement on some operating systems. + disable_os_cache_for_aligned_files = 1, + // This opens all files in unbuffered mode (if allowed by the + // operating system). Linux and Windows, for instance, require disk + // offsets to be sector aligned, and in those cases, this option is + // the same as ``disable_os_caches_for_aligned_files``. + disable_os_cache = 2 + }; + + // determines how files are opened when they're in read only mode versus + // read and write mode. For options, see io_buffer_mode_t. + // + // One reason to disable caching is that it may help the operating system + // from growing its file cache indefinitely. Since some OSes only allow + // aligned files to be opened in unbuffered mode, It is recommended to + // make the largest file in a torrent the first file (with offset 0) or + // use pad files to align all files to piece boundries. + int disk_io_write_mode; + int disk_io_read_mode; + + // when set to true, instead of issuing multiple adjacent reads or writes + // to the disk, allocate a larger buffer, copy all writes into it and + // issue a single write. For reads, read into a larger buffer and copy + // the buffer into the smaller individual read buffers afterwards. This + // may save system calls, but will cost in additional memory allocation + // and copying. + bool coalesce_reads; + bool coalesce_writes; + + // if set to something other than (0, 0) is a range of ports used to bind + // outgoing sockets to. This may be useful for users whose router allows + // them to assign QoS classes to traffic based on its local port. It is a + // range instead of a single port because of the problems with failing to + // reconnect to peers if a previous socket to that peer and port is in + // ``TIME_WAIT`` state. + // + //.. warning:: + // setting outgoing ports will limit the ability to keep multiple + // connections to the same client, even for different torrents. It is not + // recommended to change this setting. Its main purpose is to use as an + // escape hatch for cheap routers with QoS capability but can only + // classify flows based on port numbers. + std::pair outgoing_ports; + + // determines the TOS byte set in the IP header of every packet sent to + // peers (including web seeds). The default value for this is ``0x0`` (no + // marking). One potentially useful TOS mark is ``0x20``, this represents + // the *QBone scavenger service*. For more details, see QBSS_. + // + // .. _`QBSS`: http://qbone.internet2.edu/qbss/ + char peer_tos; + + // for auto managed torrents, these are the limits they are subject to. + // If there are too many torrents some of the auto managed ones will be + // paused until some slots free up. + // + // ``active_dht_limit`` and ``active_tracker_limit`` limits the number of + // torrents that will be active on the DHT and their tracker. If the + // active limit is set higher than these numbers, some torrents will be + // "active" in the sense that they will accept incoming connections, but + // not announce on the DHT or their trackers. + // + // ``active_lsd_limit`` is the max number of torrents to announce to the + // local network over the local service discovery protocol. By default + // this is 80, which is no more than one announce every 5 seconds + // (assuming the default announce interval of 5 minutes). + // + // ``active_limit`` is a hard limit on the number of active torrents. + // This applies even to slow torrents. + // + // You can have more torrents *active*, even though they are not + // announced to the DHT, lsd or their tracker. If some peer knows about + // you for any reason and tries to connect, it will still be accepted, + // unless the torrent is paused, which means it won't accept any + // connections. + // + // ``active_downloads`` and ``active_seeds`` controls how many active + // seeding and downloading torrents the queuing mechanism allows. The + // target number of active torrents is ``min(active_downloads + + // active_seeds, active_limit)``. ``active_downloads`` and + // ``active_seeds`` are upper limits on the number of downloading + // torrents and seeding torrents respectively. Setting the value to -1 + // means unlimited. + // + // For example if there are 10 seeding torrents and 10 downloading + // torrents, and ``active_downloads`` is 4 and ``active_seeds`` is 4, + // there will be 4 seeds active and 4 downloading torrents. If the + // settings are ``active_downloads`` = 2 and ``active_seeds`` = 4, then + // there will be 2 downloading torrents and 4 seeding torrents active. + // Torrents that are not auto managed are also counted against these + // limits. If there are non-auto managed torrents that use up all the + // slots, no auto managed torrent will be activated. + int active_downloads; + int active_seeds; + int active_dht_limit; + int active_tracker_limit; + int active_lsd_limit; + int active_limit; + + // prefer seeding torrents when determining which torrents to give active + // slots to, the default is false which gives preference to downloading + // torrents + bool auto_manage_prefer_seeds; + + // if true, torrents without any payload transfers are not subject to the + // ``active_seeds`` and ``active_downloads`` limits. This is intended to + // make it more likely to utilize all available bandwidth, and avoid + // having torrents that don't transfer anything block the active slots. + bool dont_count_slow_torrents; + + // the number of seconds in between recalculating which torrents to + // activate and which ones to queue + int auto_manage_interval; + + // when a seeding torrent reaches either the share ratio (bytes up / + // bytes down) or the seed time ratio (seconds as seed / seconds as + // downloader) or the seed time limit (seconds as seed) it is considered + // done, and it will leave room for other torrents the default value for + // share ratio is 2 the default seed time ratio is 7, because that's a + // common asymmetry ratio on connections + // + //.. note:: + // This is an out-dated option that doesn't make much sense. It will be + // removed in future versions of libtorrent + float share_ratio_limit; + + // the seeding time / downloading time ratio limit for considering a + // seeding torrent to have met the seed limit criteria. See queuing_. + float seed_time_ratio_limit; + + // the limit on the time a torrent has been an active seed (specified in + // seconds) before it is considered having met the seed limit criteria. + // See queuing_. + int seed_time_limit; + + // controls a feature where libtorrent periodically can disconnect the + // least useful peers in the hope of connecting to better ones. + // ``peer_turnover_interval`` controls the interval of this optimistic + // disconnect. It defaults to every 5 minutes, and is specified in + // seconds. + // + // ``peer_turnover`` Is the fraction of the peers that are disconnected. + // This is a float where 1.f represents all peers an 0 represents no + // peers. It defaults to 4% (i.e. 0.04f) + // + // ``peer_turnover_cutoff`` is the cut off trigger for optimistic + // unchokes. If a torrent has more than this fraction of its connection + // limit, the optimistic unchoke is triggered. This defaults to 90% (i.e. + // 0.9f). + int peer_turnover_interval; + float peer_turnover; + float peer_turnover_cutoff; + + // specifies whether libtorrent should close connections where both ends + // have no utility in keeping the connection open. For instance if both + // ends have completed their downloads, there's no point in keeping it + // open. This defaults to ``true``. + bool close_redundant_connections; + + // the number of seconds between scrapes of queued torrents (auto managed + // and paused torrents). Auto managed torrents that are paused, are + // scraped regularly in order to keep track of their downloader/seed + // ratio. This ratio is used to determine which torrents to seed and + // which to pause. + int auto_scrape_interval; + + // the minimum number of seconds between any automatic scrape (regardless + // of torrent). In case there are a large number of paused auto managed + // torrents, this puts a limit on how often a scrape request is sent. + int auto_scrape_min_interval; + + // the maximum number of peers in the list of known peers. These peers + // are not necessarily connected, so this number should be much greater + // than the maximum number of connected peers. Peers are evicted from the + // cache when the list grows passed 90% of this limit, and once the size + // hits the limit, peers are no longer added to the list. If this limit + // is set to 0, there is no limit on how many peers we'll keep in the + // peer list. + int max_peerlist_size; + + // the max peer list size used for torrents that are paused. This default + // to the same as ``max_peerlist_size``, but can be used to save memory + // for paused torrents, since it's not as important for them to keep a + // large peer list. + int max_paused_peerlist_size; + + // the minimum allowed announce interval for a tracker. This is specified + // in seconds, defaults to 5 minutes and is used as a sanity check on + // what is returned from a tracker. It mitigates hammering misconfigured + // trackers. + int min_announce_interval; + + // If true, partial pieces are picked before pieces that are more rare. + // If false, rare pieces are always prioritized, unless the number of + // partial pieces is growing out of proportion. + bool prioritize_partial_pieces; + + // the number of seconds a torrent is considered active after it was + // started, regardless of upload and download speed. This is so that + // newly started torrents are not considered inactive until they have a + // fair chance to start downloading. + int auto_manage_startup; + + // if set to true, the estimated TCP/IP overhead is drained from the rate + // limiters, to avoid exceeding the limits with the total traffic + bool rate_limit_ip_overhead; + + // controls how multi tracker torrents are treated. If this is set to + // true, all trackers in the same tier are announced to in parallel. If + // all trackers in tier 0 fails, all trackers in tier 1 are announced as + // well. If it's set to false, the behavior is as defined by the multi + // tracker specification. It defaults to false, which is the same + // behavior previous versions of libtorrent has had as well. + bool announce_to_all_trackers; + + // controls how multi tracker torrents are treated. When this is set to + // true, one tracker from each tier is announced to. This is the uTorrent + // behavior. This is false by default in order to comply with the + // multi-tracker specification. + bool announce_to_all_tiers; + + // true by default. It means that trackers may be rearranged in a way + // that udp trackers are always tried before http trackers for the same + // hostname. Setting this to fails means that the trackers' tier is + // respected and there's no preference of one protocol over another. + bool prefer_udp_trackers; + + // when this is set to true, a piece has to have been forwarded to a + // third peer before another one is handed out. This is the traditional + // definition of super seeding. + bool strict_super_seeding; + + // the number of pieces to send to a peer, when seeding, before rotating + // in another peer to the unchoke set. It defaults to 3 pieces, which + // means that when seeding, any peer we've sent more than this number of + // pieces to will be unchoked in favour of a choked peer. + int seeding_piece_quota; + + // is a limit of the number of *sparse regions* in a torrent. A sparse + // region is defined as a hole of pieces we have not yet downloaded, in + // between pieces that have been downloaded. This is used as a hack for + // windows vista which has a bug where you cannot write files with more + // than a certain number of sparse regions. This limit is not hard, it + // will be exceeded. Once it's exceeded, pieces that will maintain or + // decrease the number of sparse regions are prioritized. To disable this + // functionality, set this to 0. It defaults to 0 on all platforms except + // windows. + int max_sparse_regions; + + // if lock disk cache is set to true the disk cache that's in use, will + // be locked in physical memory, preventing it from being swapped out. + bool lock_disk_cache; + + // the number of piece requests we will reject in a row while a peer is + // choked before the peer is considered abusive and is disconnected. + int max_rejects; + + // specifies the buffer sizes set on peer sockets. 0 (which is the + // default) means the OS default (i.e. don't change the buffer sizes). + // The socket buffer sizes are changed using setsockopt() with + // SOL_SOCKET/SO_RCVBUF and SO_SNDBUFFER. + int recv_socket_buffer_size; + int send_socket_buffer_size; + + // chooses between two ways of reading back piece data from disk when its + // complete and needs to be verified against the piece hash. This happens + // if some blocks were flushed to the disk out of order. Everything that + // is flushed in order is hashed as it goes along. Optimizing for speed + // will allocate space to fit all the the remaingin, unhashed, part of + // the piece, reads the data into it in a single call and hashes it. This + // is the default. If ``optimizing_hashing_for_speed`` is false, a single + // block will be allocated (16 kB), and the unhashed parts of the piece + // are read, one at a time, and hashed in this single block. This is + // appropriate on systems that are memory constrained. + bool optimize_hashing_for_speed; + + // the number of milliseconds to sleep + // in between disk read operations when checking torrents. This defaults + // to 0, but can be set to higher numbers to slow down the rate at which + // data is read from the disk while checking. This may be useful for + // background tasks that doesn't matter if they take a bit longer, as long + // as they leave disk I/O time for other processes. + int file_checks_delay_per_block; + + // the disk cache algorithms available. Set + // session_settings::disk_cache_algorithm to one of these. + enum disk_cache_algo_t + { + // This flushes the entire piece, in the write cache, that was least + // recently written to. + lru, + + // will flush the largest sequences of contiguous blocks from the + // write cache, regarless of the piece's last use time. + largest_contiguous, + + // will prioritize flushing blocks that will avoid having to read them + // back in to verify the hash of the piece once it's done. This is + // especially useful for high throughput setups, where reading from + // the disk is especially expensive. + avoid_readback + }; + + // tells the disk I/O thread which cache flush algorithm to use. + // This is specified by the disk_cache_algo_t enum. + disk_cache_algo_t disk_cache_algorithm; + + // the number of blocks to read into the read cache when a read cache + // miss occurs. Setting this to 0 is essentially the same thing as + // disabling read cache. The number of blocks read into the read cache is + // always capped by the piece boundry. + // + // When a piece in the write cache has ``write_cache_line_size`` + // contiguous blocks in it, they will be flushed. Setting this to 1 + // effectively disables the write cache. + int read_cache_line_size; + + // whenever a contiguous range of this many blocks is found in the write + // cache, it is flushed immediately + int write_cache_line_size; + + // the number of seconds from a disk write errors occur on a torrent + // until libtorrent will take it out of the upload mode, to test if the + // error condition has been fixed. + // + // libtorrent will only do this automatically for auto managed torrents. + // + // You can explicitly take a torrent out of upload only mode using + // set_upload_mode(). + int optimistic_disk_retry; + + // controls if downloaded pieces are verified against the piece hashes in + // the torrent file or not. The default is false, i.e. to verify all + // downloaded data. It may be useful to turn this off for performance + // profiling and simulation scenarios. Do not disable the hash check for + // regular bittorrent clients. + bool disable_hash_checks; + + // if this is true, disk read operations may be re-ordered based on their + // physical disk read offset. This greatly improves throughput when + // uploading to many peers. This assumes a traditional hard drive with a + // read head and spinning platters. If your storage medium is a solid + // state drive, this optimization doesn't give you an benefits + bool allow_reordered_disk_operations; + + // if this is true, i2p torrents are allowed to also get peers from other + // sources than the tracker, and connect to regular IPs, not providing + // any anonymization. This may be useful if the user is not interested in + // the anonymization of i2p, but still wants to be able to connect to i2p + // peers. + bool allow_i2p_mixed; + + // the max number of suggested piece indices received from a peer that's + // remembered. If a peer floods suggest messages, this limit prevents + // libtorrent from using too much RAM. It defaults to 10. + int max_suggest_pieces; + + // If set to true (it defaults to false), piece requests that have been + // skipped enough times when piece messages are received, will be + // considered lost. Requests are considered skipped when the returned + // piece messages are re-ordered compared to the order of the requests. + // This was an attempt to get out of dead-locks caused by BitComet peers + // silently ignoring some requests. It may cause problems at high rates, + // and high level of reordering in the uploading peer, that's why it's + // disabled by default. + bool drop_skipped_requests; + + // determines if the disk I/O should use a normal + // or low priority policy. This defaults to true, which means that + // it's low priority by default. Other processes doing disk I/O will + // normally take priority in this mode. This is meant to improve the + // overall responsiveness of the system while downloading in the + // background. For high-performance server setups, this might not + // be desirable. + bool low_prio_disk; + + // the time between local + // network announces for a torrent. By default, when local service + // discovery is enabled a torrent announces itself every 5 minutes. + // This interval is specified in seconds. + int local_service_announce_interval; + + // the number of seconds between announcing + // torrents to the distributed hash table (DHT). This is specified to + // be 15 minutes which is its default. + int dht_announce_interval; + + // the number of seconds libtorrent + // will keep UDP tracker connection tokens around for. This is specified + // to be 60 seconds, and defaults to that. The higher this value is, the + // fewer packets have to be sent to the UDP tracker. In order for higher + // values to work, the tracker needs to be configured to match the + // expiration time for tokens. + int udp_tracker_token_expiry; + + // if this is set to true, read cache blocks + // that are hit by peer read requests are removed from the disk cache + // to free up more space. This is useful if you don't expect the disk + // cache to create any cache hits from other peers than the one who + // triggered the cache line to be read into the cache in the first place. + bool volatile_read_cache; + + // enables the disk cache to adjust the size + // of a cache line generated by peers to depend on the upload rate + // you are sending to that peer. The intention is to optimize the RAM + // usage of the cache, to read ahead further for peers that you're + // sending faster to. + bool guided_read_cache; + + // the minimum number of seconds any read cache line is kept in the + // cache. This defaults to one second but may be greater if + // ``guided_read_cache`` is enabled. Having a lower bound on the time a + // cache line stays in the cache is an attempt to avoid swapping the same + // pieces in and out of the cache in case there is a shortage of spare + // cache space. + int default_cache_min_age; + + // the number of optimistic unchoke slots to use. It defaults to 0, which + // means automatic. Having a higher number of optimistic unchoke slots + // mean you will find the good peers faster but with the trade-off to use + // up more bandwidth. When this is set to 0, libtorrent opens up 20% of + // your allowed upload slots as optimistic unchoke slots. + int num_optimistic_unchoke_slots; + + // this is a linux-only option and passes in the ``O_NOATIME`` to + // ``open()`` when opening files. This may lead to some disk performance + // improvements. + bool no_atime_storage; + + // the assumed reciprocation rate from peers when using the BitTyrant + // choker. This defaults to 14 kiB/s. If set too high, you will + // over-estimate your peers and be more altruistic while finding the true + // reciprocation rate, if it's set too low, you'll be too stingy and + // waste finding the true reciprocation rate. + int default_est_reciprocation_rate; + + // specifies how many percent the extimated reciprocation rate should be + // increased by each unchoke interval a peer is still choking us back. + // This defaults to 20%. This only applies to the BitTyrant choker. + int increase_est_reciprocation_rate; + + // specifies how many percent the estimated reciprocation rate should be + // decreased by each unchoke interval a peer unchokes us. This default to + // 3%. This only applies to the BitTyrant choker. + int decrease_est_reciprocation_rate; + + // defaults to false. If a torrent has been paused by the auto managed + // feature in libtorrent, i.e. the torrent is paused and auto managed, + // this feature affects whether or not it is automatically started on an + // incoming connection. The main reason to queue torrents, is not to make + // them unavailable, but to save on the overhead of announcing to the + // trackers, the DHT and to avoid spreading one's unchoke slots too thin. + // If a peer managed to find us, even though we're no in the torrent + // anymore, this setting can make us start the torrent and serve it. + bool incoming_starts_queued_torrents; + + // when set to true, the downloaded counter sent to trackers will include + // the actual number of payload bytes donwnloaded including redundant + // bytes. If set to false, it will not include any redundany bytes + bool report_true_downloaded; + + // defaults to true, and controls when a block may be requested twice. If + // this is ``true``, a block may only be requested twice when there's ay + // least one request to every piece that's left to download in the + // torrent. This may slow down progress on some pieces sometimes, but it + // may also avoid downloading a lot of redundant bytes. If this is + // ``false``, libtorrent attempts to use each peer connection to its max, + // by always requesting something, even if it means requesting something + // that has been requested from another peer already. + bool strict_end_game_mode; + + // if set to true, the local peer discovery (or Local Service Discovery) + // will not only use IP multicast, but also broadcast its messages. This + // can be useful when running on networks that don't support multicast. + // Since broadcast messages might be expensive and disruptive on + // networks, only every 8th announce uses broadcast. + bool broadcast_lsd; + + // these all determines if libtorrent should attempt to make outgoing + // connections of the specific type, or allow incoming connection. By + // default all of them are enabled. + bool enable_outgoing_utp; + bool enable_incoming_utp; + bool enable_outgoing_tcp; + bool enable_incoming_tcp; + + // the max number of peers we accept from pex messages from a single peer. + // this limits the number of concurrent peers any of our peers claims to + // be connected to. If they clain to be connected to more than this, we'll + // ignore any peer that exceeds this limit + int max_pex_peers; + + // determines if the storage, when loading resume data files, should + // verify that the file modification time with the timestamps in the + // resume data. This defaults to false, which means timestamps are taken + // into account, and resume data is less likely to accepted (torrents are + // more likely to be fully checked when loaded). It might be useful to + // set this to true if your network is faster than your disk, and it + // would be faster to redownload potentially missed pieces than to go + // through the whole storage to look for them. + bool ignore_resume_timestamps; + + // determines if the storage should check the whole files when resume + // data is incomplete or missing or whether it should simply assume we + // don't have any of the data. By default, this is determined by the + // existance of any of the files. By setting this setting to true, the + // files won't be checked, but will go straight to download mode. + bool no_recheck_incomplete_resume; + + // defaults to false. When set to true, the client tries to hide its + // identity to a certain degree. The peer-ID will no longer include the + // client's fingerprint. The user-agent will be reset to an empty string. + // It will also try to not leak other identifying information, such as + // your local listen port, your IP etc. + // + // If you're using I2P, a VPN or a proxy, it might make sense to enable + // anonymous mode. + bool anonymous_mode; + + // disables any communication that's not going over a proxy. Enabling + // this requires a proxy to be configured as well, see + // ``set_proxy_settings``. The listen sockets are closed, and incoming + // connections will only be accepted through a SOCKS5 or I2P proxy (if a + // peer proxy is set up and is run on the same machine as the tracker + // proxy). This setting also disabled peer country lookups, since those + // are done via DNS lookups that aren't supported by proxies. + bool force_proxy; + + // specifies the number of milliseconds between internal ticks. This is + // the frequency with which bandwidth quota is distributed to peers. It + // should not be more than one second (i.e. 1000 ms). Setting this to a + // low value (around 100) means higher resolution bandwidth quota + // distribution, setting it to a higher value saves CPU cycles. + int tick_interval; + + // specifies whether downloads from web seeds is reported to the + // tracker or not. Defaults to on + bool report_web_seed_downloads; + + // specifies the target share ratio for share mode torrents. This + // defaults to 3, meaning we'll try to upload 3 times as much as we + // download. Setting this very high, will make it very conservative and + // you might end up not downloading anything ever (and not affecting your + // share ratio). It does not make any sense to set this any lower than 2. + // For instance, if only 3 peers need to download the rarest piece, it's + // impossible to download a single piece and upload it more than 3 times. + // If the share_mode_target is set to more than 3, nothing is downloaded. + int share_mode_target; + + // sets the session-global limits of upload and download rate limits, in + // bytes per second. The local rates refer to peers on the local network. + // By default peers on the local network are not rate limited. + // + // These rate limits are only used for local peers (peers within the same + // subnet as the client itself) and it is only used when + // ``session_settings::ignore_limits_on_local_network`` is set to true + // (which it is by default). These rate limits default to unthrottled, + // but can be useful in case you want to treat local peers + // preferentially, but not quite unthrottled. + // + // A value of 0 means unlimited. + int upload_rate_limit; + int download_rate_limit; + int local_upload_rate_limit; + int local_download_rate_limit; + + // sets the rate limit on the DHT. This is specified in bytes per second + // and defaults to 4000. For busy boxes with lots of torrents that + // requires more DHT traffic, this should be raised. + int dht_upload_rate_limit; + + // the max number of unchoked peers in the session. The number of unchoke + // slots may be ignored depending on what ``choking_algorithm`` is set + // to. A value of -1 means infinite. + int unchoke_slots_limit; + + // sets the maximum number of half-open connections libtorrent will have + // when connecting to peers. A half-open connection is one where + // connect() has been called, but the connection still hasn't been + // established (nor failed). Windows XP Service Pack 2 sets a default, + // system wide, limit of the number of half-open connections to 10. So, + // this limit can be used to work nicer together with other network + // applications on that system. The default is to have no limit, and + // passing -1 as the limit, means to have no limit. When limiting the + // number of simultaneous connection attempts, peers will be put in a + // queue waiting for their turn to get connected. + int half_open_limit; + + // sets a global limit on the number of connections opened. The number of + // connections is set to a hard minimum of at least two per torrent, so + // if you set a too low connections limit, and open too many torrents, + // the limit will not be met. + int connections_limit; + + // the number of extra incoming connections allowed temporarily, in order + // to support replacing peers + int connections_slack; + + // the target delay for uTP sockets in milliseconds. A high value will + // make uTP connections more aggressive and cause longer queues in the + // upload bottleneck. It cannot be too low, since the noise in the + // measurements would cause it to send too slow. The default is 50 + // milliseconds. + int utp_target_delay; + + // the number of bytes the uTP congestion window can increase at the most + // in one RTT. This defaults to 300 bytes. If this is set too high, the + // congestion controller reacts too hard to noise and will not be stable, + // if it's set too low, it will react slow to congestion and not back off + // as fast. + int utp_gain_factor; + + // the shortest allowed uTP socket timeout, specified in milliseconds. + // This defaults to 500 milliseconds. The timeout depends on the RTT of + // the connection, but is never smaller than this value. A connection + // times out when every packet in a window is lost, or when a packet is + // lost twice in a row (i.e. the resent packet is lost as well). + // + // The shorter the timeout is, the faster the connection will recover + // from this situation, assuming the RTT is low enough. + int utp_min_timeout; + + // the number of SYN packets that are sent (and timed out) before + // giving up and closing the socket. + int utp_syn_resends; + + // the number of resent packets sent on a closed socket before giving up + int utp_fin_resends; + + // the number of times a packet is sent (and lossed or timed out) + // before giving up and closing the connection. + int utp_num_resends; + + // the number of milliseconds of timeout for the initial SYN packet for + // uTP connections. For each timed out packet (in a row), the timeout is + // doubled. + int utp_connect_timeout; + +#ifndef TORRENT_NO_DEPRECATE + // number of milliseconds of delaying ACKing packets the most + int utp_delayed_ack; +#endif + + // controls if the uTP socket manager is allowed to increase the socket + // buffer if a network interface with a large MTU is used (such as + // loopback or ethernet jumbo frames). This defaults to true and might + // improve uTP throughput. For RAM constrained systems, disabling this + // typically saves around 30kB in user space and probably around 400kB in + // kernel socket buffers (it adjusts the send and receive buffer size on + // the kernel socket, both for IPv4 and IPv6). + bool utp_dynamic_sock_buf; + + // controls how the congestion window is changed when a packet loss is + // experienced. It's specified as a percentage multiplier for ``cwnd``. + // By default it's set to 50 (i.e. cut in half). Do not change this value + // unless you know what you're doing. Never set it higher than 100. + int utp_loss_multiplier; + + // the options for session_settings::mixed_mode_algorithm. + enum bandwidth_mixed_algo_t + { + // disables the mixed mode bandwidth balancing + prefer_tcp = 0, + + // does not throttle uTP, throttles TCP to the same proportion + // of throughput as there are TCP connections + peer_proportional = 1 + }; + + // determines how to treat TCP connections when there are uTP + // connections. Since uTP is designed to yield to TCP, there's an + // inherent problem when using swarms that have both TCP and uTP + // connections. If nothing is done, uTP connections would often be + // starved out for bandwidth by the TCP connections. This mode is + // ``prefer_tcp``. The ``peer_proportional`` mode simply looks at the + // current throughput and rate limits all TCP connections to their + // proportional share based on how many of the connections are TCP. This + // works best if uTP connections are not rate limited by the global rate + // limiter, see rate_limit_utp. + // + // see bandwidth_mixed_algo_t for options. + int mixed_mode_algorithm; + + // determines if uTP connections should be throttled by the global rate + // limiter or not. By default they are. + bool rate_limit_utp; + + // the value passed in to listen() for the listen socket. It is the + // number of outstanding incoming connections to queue up while we're not + // actively waiting for a connection to be accepted. The default is 5 + // which should be sufficient for any normal client. If this is a high + // performance server which expects to receive a lot of connections, or + // used in a simulator or test, it might make sense to raise this number. + // It will not take affect until listen_on() is called again (or for the + // first time). + int listen_queue_size; + + // if true, the ``&ip=`` argument in tracker requests (unless otherwise + // specified) will be set to the intermediate IP address, if the user is + // double NATed. If ther user is not double NATed, this option has no + // affect. + bool announce_double_nat; + + // the number of peers to try to connect to immediately when the first + // tracker response is received for a torrent. This is a boost to given + // to new torrents to accelerate them starting up. The normal connect + // scheduler is run once every second, this allows peers to be connected + // immediately instead of waiting for the session tick to trigger + // connections. + int torrent_connect_boost; + + // determines if seeding (and finished) torrents should attempt to make + // outgoing connections or not. By default this is true. It may be set to + // false in very specific applications where the cost of making outgoing + // connections is high, and there are no or small benefits of doing so. + // For instance, if no nodes are behind a firewall or a NAT, seeds don't + // need to make outgoing connections. + bool seeding_outgoing_connections; + + // if true (which is the default), libtorrent will not connect to any + // peers on priviliged ports (<= 1023). This can mitigate using + // bittorrent swarms for certain DDoS attacks. + bool no_connect_privileged_ports; + + // the maximum number of alerts queued up internally. If alerts are not + // popped, the queue will eventually fill up to this level. This defaults + // to 1000. + int alert_queue_size; + + // the maximum allowed size (in bytes) to be received + // by the metadata extension, i.e. magnet links. It defaults to 1 MiB. + int max_metadata_size; + + // true by default, which means the number of connection attempts per + // second may be limited to below the ``connection_speed``, in case we're + // close to bump up against the limit of number of connections. The + // intention of this setting is to more evenly distribute our connection + // attempts over time, instead of attempting to connectin in batches, and + // timing them out in batches. + bool smooth_connects; + + // defaults to false. When set to true, web connections will include a + // user-agent with every request, as opposed to just the first request in + // a connection. + bool always_send_user_agent; + + // defaults to true. It determines whether the IP filter applies to + // trackers as well as peers. If this is set to false, trackers are + // exempt from the IP filter (if there is one). If no IP filter is set, + // this setting is irrelevant. + bool apply_ip_filter_to_trackers; + + // used to avoid starvation of read jobs in the disk I/O thread. By + // default, read jobs are deferred, sorted by physical disk location and + // serviced once all write jobs have been issued. In scenarios where the + // download rate is enough to saturate the disk, there's a risk the read + // jobs will never be serviced. With this setting, every *x* write job, + // issued in a row, will instead pick one read job off of the sorted + // queue, where *x* is ``read_job_every``. + int read_job_every; + + // defaults to true and will attempt to optimize disk reads by giving the + // operating system heads up of disk read requests as they are queued in + // the disk job queue. This gives a significant performance boost for + // seeding. + bool use_disk_read_ahead; + + // determines whether or not to lock files which libtorrent is + // downloading to or seeding from. This is implemented using + // ``fcntl(F_SETLK)`` on unix systems and by not passing in + // ``SHARE_READ`` and ``SHARE_WRITE`` on windows. This might prevent 3rd + // party processes from corrupting the files under libtorrent's feet. + bool lock_files; + + // sets the listen port for SSL connections. If this is set to 0, no SSL + // listen port is opened. Otherwise a socket is opened on this port. This + // setting is only taken into account when opening the regular listen + // port, and won't re-open the listen socket simply by changing this + // setting. + // + // if this is 0, outgoing SSL connections are disabled + // + // It defaults to port 4433. + int ssl_listen; + + // ``tracker_backoff`` determines how aggressively to back off from + // retrying failing trackers. This value determines *x* in the following + // formula, determining the number of seconds to wait until the next + // retry: + // + // delay = 5 + 5 * x / 100 * fails^2 + // + // It defaults to 250. + // + // This setting may be useful to make libtorrent more or less aggressive + // in hitting trackers. + // + int tracker_backoff; + + // enables banning web seeds. By default, web seeds that send corrupt + // data are banned. + bool ban_web_seeds; + + // specifies the max number of bytes to receive into RAM buffers when + // downloading stuff over HTTP. Specifically when specifying a URL to a + // .torrent file when adding a torrent or when announcing to an HTTP + // tracker. The default is 2 MiB. + int max_http_recv_buffer_size; + + // enables or disables the share mode extension. This is enabled by + // default. + bool support_share_mode; + + // enables or disables the merkle tree torrent support. This is enabled + // by default. + bool support_merkle_torrents; + + // enables or disables reporting redundant bytes to the tracker. This is + // enabled by default. + bool report_redundant_bytes; + + // the version string to advertise for this client in the peer protocol + // handshake. If this is empty the user_agent is used + std::string handshake_client_version; + + // if this is true, the disk cache uses a pool allocator for disk cache + // blocks. Enabling this improves performance of the disk cache with the + // side effect that the disk cache is less likely and slower at returning + // memory to the kernel when cache pressure is low. + bool use_disk_cache_pool; + + // the download and upload rate limits for a torrent to be considered + // active by the queuing mechanism. A torrent whose download rate is less + // than ``inactive_down_rate`` and whose upload rate is less than + // ``inactive_up_rate`` for ``auto_manage_startup`` seconds, is + // considered inactive, and another queued torrent may be startert. + // This logic is disabled if ``dont_count_slow_torrents`` is false. + int inactive_down_rate; + int inactive_up_rate; + }; + + // structure used to hold configuration options for the DHT + // + // The ``dht_settings`` struct used to contain a ``service_port`` member to + // control which port the DHT would listen on and send messages from. This + // field is deprecated and ignored. libtorrent always tries to open the UDP + // socket on the same port as the TCP socket. + struct TORRENT_EXPORT dht_settings + { + // initialized dht_settings to the default values + dht_settings() + : max_peers_reply(100) + , search_branching(5) +#ifndef TORRENT_NO_DEPRECATE + , service_port(0) +#endif + , max_fail_count(20) + , max_torrents(2000) + , max_dht_items(700) + , max_torrent_search_reply(20) + , restrict_routing_ips(true) + , restrict_search_ips(true) + , extended_routing_table(true) + , aggressive_lookups(true) + , privacy_lookups(false) + , enforce_node_id(false) + , ignore_dark_internet(true) + {} + + // the maximum number of peers to send in a reply to ``get_peers`` + int max_peers_reply; + + // the number of concurrent search request the node will send when + // announcing and refreshing the routing table. This parameter is called + // alpha in the kademlia paper + int search_branching; + +#ifndef TORRENT_NO_DEPRECATE + // the listen port for the dht. This is a UDP port. zero means use the + // same as the tcp interface + int service_port; +#endif + + // the maximum number of failed tries to contact a node before it is + // removed from the routing table. If there are known working nodes that + // are ready to replace a failing node, it will be replaced immediately, + // this limit is only used to clear out nodes that don't have any node + // that can replace them. + int max_fail_count; + + // the total number of torrents to track from the DHT. This is simply an + // upper limit to make sure malicious DHT nodes cannot make us allocate + // an unbounded amount of memory. + int max_torrents; + + // max number of items the DHT will store + int max_dht_items; + + // the max number of torrents to return in a torrent search query to the + // DHT + int max_torrent_search_reply; + + // determines if the routing table entries should restrict entries to one + // per IP. This defaults to true, which helps mitigate some attacks on + // the DHT. It prevents adding multiple nodes with IPs with a very close + // CIDR distance. + // + // when set, nodes whose IP address that's in the same /24 (or /64 for + // IPv6) range in the same routing table bucket. This is an attempt to + // mitigate node ID spoofing attacks also restrict any IP to only have a + // single entry in the whole routing table + bool restrict_routing_ips; + + // determines if DHT searches should prevent adding nodes with IPs with + // very close CIDR distance. This also defaults to true and helps + // mitigate certain attacks on the DHT. + bool restrict_search_ips; + + // makes the first buckets in the DHT routing table fit 128, 64, 32 and + // 16 nodes respectively, as opposed to the standard size of 8. All other + // buckets have size 8 still. + bool extended_routing_table; + + // slightly changes the lookup behavior in terms of how many outstanding + // requests we keep. Instead of having branch factor be a hard limit, we + // always keep *branch factor* outstanding requests to the closest nodes. + // i.e. every time we get results back with closer nodes, we query them + // right away. It lowers the lookup times at the cost of more outstanding + // queries. + bool aggressive_lookups; + + // when set, perform lookups in a way that is slightly more expensive, + // but which minimizes the amount of information leaked about you. + bool privacy_lookups; + + // when set, node's whose IDs that are not correctly generated based on + // its external IP are ignored. When a query arrives from such node, an + // error message is returned with a message saying "invalid node ID". + bool enforce_node_id; + + // ignore DHT messages from parts of the internet we wouldn't expect to + // see any traffic from + bool ignore_dark_internet; + }; + + + // The ``pe_settings`` structure is used to control the settings related + // to peer protocol encryption. + struct TORRENT_EXPORT pe_settings + { + // initializes the encryption settings with the default vaues + pe_settings() + : out_enc_policy(enabled) + , in_enc_policy(enabled) + , allowed_enc_level(both) + , prefer_rc4(false) + {} + + // the encoding policy options for use with pe_settings::out_enc_policy + // and pe_settings::in_enc_policy. + enum enc_policy + { + // Only encrypted connections are allowed. Incoming connections that + // are not encrypted are closed and if the encrypted outgoing + // connection fails, a non-encrypted retry will not be made. + forced, + + // encrypted connections are enabled, but non-encrypted connections + // are allowed. An incoming non-encrypted connection will be accepted, + // and if an outgoing encrypted connection fails, a non- encrypted + // connection will be tried. + enabled, + + // only non-encrypted connections are allowed. + disabled + }; + + // the encryption levels, to be used with pe_settings::allowed_enc_level. + enum enc_level + { + // use only plaintext encryption + plaintext = 1, + // use only rc4 encryption + rc4 = 2, + // allow both + both = 3 + }; + + // control the settings for incoming + // and outgoing connections respectively. + // see enc_policy enum for the available options. + boost::uint8_t out_enc_policy; + boost::uint8_t in_enc_policy; + + // determines the encryption level of the + // connections. This setting will adjust which encryption scheme is + // offered to the other peer, as well as which encryption scheme is + // selected by the client. See enc_level enum for options. + boost::uint8_t allowed_enc_level; + + // if the allowed encryption level is both, setting this to + // true will prefer rc4 if both methods are offered, plaintext + // otherwise + bool prefer_rc4; + }; + +} + +#endif diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/session_status.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/session_status.hpp new file mode 100644 index 0000000000..71d964e5a3 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/session_status.hpp @@ -0,0 +1,265 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SESSION_STATUS_HPP_INCLUDED +#define TORRENT_SESSION_STATUS_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include "libtorrent/size_type.hpp" +#include + +namespace libtorrent +{ + + // holds statistics about a current dht_lookup operation. + // a DHT lookup is the travesal of nodes, looking up a + // set of target nodes in the DHT for retrieving and possibly + // storing information in the DHT + struct TORRENT_EXPORT dht_lookup + { + // string literal indicating which kind of lookup this is + char const* type; + + // the number of outstanding request to individual nodes + // this lookup has right now + int outstanding_requests; + + // the total number of requests that have timed out so far + // for this lookup + int timeouts; + + // the total number of responses we have received for this + // lookup so far for this lookup + int responses; + + // the branch factor for this lookup. This is the number of + // nodes we keep outstanding requests to in parallel by default. + // when nodes time out we may increase this. + int branch_factor; + + // the number of nodes left that could be queries for this + // lookup. Many of these are likely to be part of the trail + // while performing the lookup and would never end up actually + // being queried. + int nodes_left; + + // the number of seconds ago the + // last message was sent that's still + // outstanding + int last_sent; + + // the number of outstanding requests + // that have exceeded the short timeout + // and are considered timed out in the + // sense that they increased the branch + // factor + int first_timeout; + }; + + // holds dht routing table stats + struct TORRENT_EXPORT dht_routing_bucket + { + // the total number of nodes and replacement nodes + // in the routing table + int num_nodes; + int num_replacements; + +#ifndef TORRENT_NO_DEPRECATE + // number of seconds since last activity + int last_active; +#endif + }; + + // holds counters and gauges for the uTP sockets + struct TORRENT_EXPORT utp_status + { + // gauges. These are snapshots of the number of + // uTP sockets in each respective state + int num_idle; + int num_syn_sent; + int num_connected; + int num_fin_sent; + int num_close_wait; + + // counters. These are monotonically increasing + // and cumulative counters for their respective event. + boost::uint64_t packet_loss; + boost::uint64_t timeout; + boost::uint64_t packets_in; + boost::uint64_t packets_out; + boost::uint64_t fast_retransmit; + boost::uint64_t packet_resend; + boost::uint64_t samples_above_target; + boost::uint64_t samples_below_target; + boost::uint64_t payload_pkts_in; + boost::uint64_t payload_pkts_out; + boost::uint64_t invalid_pkts_in; + boost::uint64_t redundant_pkts_in; + }; + + // contains session wide state and counters + struct TORRENT_EXPORT session_status + { + // false as long as no incoming connections have been + // established on the listening socket. Every time you change the listen port, this will + // be reset to false. + bool has_incoming_connections; + + // the total download and upload rates accumulated + // from all torrents. This includes bittorrent protocol, DHT and an estimated TCP/IP + // protocol overhead. + int upload_rate; + int download_rate; + + // the total number of bytes downloaded and + // uploaded to and from all torrents. This also includes all the protocol overhead. + size_type total_download; + size_type total_upload; + + // the rate of the payload + // down- and upload only. + int payload_upload_rate; + int payload_download_rate; + + // the total transfers of payload + // only. The payload does not include the bittorrent protocol overhead, but only parts of the + // actual files to be downloaded. + size_type total_payload_download; + size_type total_payload_upload; + + // the estimated TCP/IP overhead in each direction. + int ip_overhead_upload_rate; + int ip_overhead_download_rate; + size_type total_ip_overhead_download; + size_type total_ip_overhead_upload; + + // the upload and download rate used by DHT traffic. Also the total number + // of bytes sent and received to and from the DHT. + int dht_upload_rate; + int dht_download_rate; + size_type total_dht_download; + size_type total_dht_upload; + + // the upload and download rate used by tracker traffic. Also the total number + // of bytes sent and received to and from trackers. + int tracker_upload_rate; + int tracker_download_rate; + size_type total_tracker_download; + size_type total_tracker_upload; + + // the number of bytes that has been received more than once. + // This can happen if a request from a peer times out and is requested from a different + // peer, and then received again from the first one. To make this lower, increase the + // ``request_timeout`` and the ``piece_timeout`` in the session settings. + size_type total_redundant_bytes; + + // the number of bytes that was downloaded which later failed + // the hash-check. + size_type total_failed_bytes; + + // the total number of peer connections this session has. This includes + // incoming connections that still hasn't sent their handshake or outgoing connections + // that still hasn't completed the TCP connection. This number may be slightly higher + // than the sum of all peers of all torrents because the incoming connections may not + // be assigned a torrent yet. + int num_peers; + + // the current number of unchoked peers. + int num_unchoked; + + // the current allowed number of unchoked peers. + int allowed_upload_slots; + + // the number of peers that are + // waiting for more bandwidth quota from the torrent rate limiter. + int up_bandwidth_queue; + int down_bandwidth_queue; + + // count the number of + // bytes the connections are waiting for to be able to send and receive. + int up_bandwidth_bytes_queue; + int down_bandwidth_bytes_queue; + + // tells the number of + // seconds until the next optimistic unchoke change and the start of the next + // unchoke interval. These numbers may be reset prematurely if a peer that is + // unchoked disconnects or becomes notinterested. + int optimistic_unchoke_counter; + int unchoke_counter; + + // the number of peers currently + // waiting on a disk write or disk read to complete before it receives or sends + // any more data on the socket. It'a a metric of how disk bound you are. + int disk_write_queue; + int disk_read_queue; + + // only available when + // built with DHT support. They are all set to 0 if the DHT isn't running. When + // the DHT is running, ``dht_nodes`` is set to the number of nodes in the routing + // table. This number only includes *active* nodes, not cache nodes. The + // ``dht_node_cache`` is set to the number of nodes in the node cache. These nodes + // are used to replace the regular nodes in the routing table in case any of them + // becomes unresponsive. + int dht_nodes; + int dht_node_cache; + + // the number of torrents tracked by the DHT at the moment. + int dht_torrents; + + // an estimation of the total number of nodes in the DHT + // network. + size_type dht_global_nodes; + + // a vector of the currently running DHT lookups. + std::vector active_requests; + + // contains information about every bucket in the DHT routing + // table. + std::vector dht_routing_table; + + // the number of nodes allocated dynamically for a + // particular DHT lookup. This represents roughly the amount of memory used + // by the DHT. + int dht_total_allocations; + + // statistics on the uTP sockets. + utp_status utp_stats; + + // the number of known peers across all torrents. These are not necessarily + // connected peers, just peers we know of. + int peerlist_size; + }; + +} + +#endif // TORRENT_SESSION_STATUS_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/settings.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/settings.hpp new file mode 100644 index 0000000000..3d58e4ba09 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/settings.hpp @@ -0,0 +1,66 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef SETTINGS_HPP_INCLUDED +#define SETTINGS_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include "libtorrent/session_settings.hpp" + +namespace libtorrent +{ + struct lazy_entry; + class entry; + + // internal + enum struct_field_type_t + { + std_string, character, integer, floating_point, + boolean, size_integer, time_integer, integer16 + }; + + // this is used to map struct entries + // to names in a bencoded dictionary to + // save and load the struct + struct bencode_map_entry + { + char const* name; + int offset; // struct offset + int type; + }; + + void load_struct(lazy_entry const& e, void* s, bencode_map_entry const* m, int num); + void save_struct(entry& e, void const* s, bencode_map_entry const* m, int num, void const* def = 0); +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/sha1_hash.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/sha1_hash.hpp new file mode 100644 index 0000000000..5be33db42a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/sha1_hash.hpp @@ -0,0 +1,307 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SHA1_HASH_HPP_INCLUDED +#define TORRENT_SHA1_HASH_HPP_INCLUDED + +#include +#include +#include +#include + +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" + +#if TORRENT_USE_IOSTREAM +#include "libtorrent/escape_string.hpp" // to_hex, from_hex +#include +#include +#endif + +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +namespace libtorrent +{ + + // This type holds a SHA-1 digest or any other kind of 20 byte + // sequence. It implements a number of convenience functions, such + // as bit operations, comparison operators etc. + // + // In libtorrent it is primarily used to hold info-hashes, piece-hashes, + // peer IDs, node IDs etc. + class TORRENT_EXPORT sha1_hash + { + enum { number_size = 20 }; + public: + // the number of bytes of the number + static const int size = number_size; + + // constructs an all-sero sha1-hash + sha1_hash() { clear(); } + + // returns an all-F sha1-hash. i.e. the maximum value + // representable by a 160 bit number (20 bytes). This is + // a static member function. + static sha1_hash max() + { + sha1_hash ret; + memset(ret.m_number, 0xff, size); + return ret; + } + + // returns an all-zero sha1-hash. i.e. the minimum value + // representable by a 160 bit number (20 bytes). This is + // a static member function. + static sha1_hash min() + { + sha1_hash ret; + memset(ret.m_number, 0, size); + return ret; + } + + // copies 20 bytes from the pointer provided, into the sha1-hash. + // The passed in string MUST be at least 20 bytes. NULL terminators + // are ignored, ``s`` is treated like a raw memory buffer. + explicit sha1_hash(char const* s) + { + if (s == 0) clear(); + else std::memcpy(m_number, s, size); + } + explicit sha1_hash(std::string const& s) + { + TORRENT_ASSERT(s.size() >= 20); + int sl = int(s.size()) < size ? int(s.size()) : size; + std::memcpy(m_number, s.c_str(), sl); + } + void assign(std::string const& s) + { + TORRENT_ASSERT(s.size() >= 20); + int sl = int(s.size()) < size ? int(s.size()) : size; + std::memcpy(m_number, s.c_str(), sl); + } + void assign(char const* str) { std::memcpy(m_number, str, size); } + + // set the sha1-hash to all zeroes. + void clear() { std::memset(m_number, 0, number_size); } + + // return true if the sha1-hash is all zero. + bool is_all_zeros() const + { + for (const unsigned char* i = m_number; i < m_number+number_size; ++i) + if (*i != 0) return false; + return true; + } + + // shift left ``n`` bits. + sha1_hash& operator<<=(int n) + { + TORRENT_ASSERT(n >= 0); + int num_bytes = n / 8; + if (num_bytes >= number_size) + { + std::memset(m_number, 0, number_size); + return *this; + } + + if (num_bytes > 0) + { + std::memmove(m_number, m_number + num_bytes, number_size - num_bytes); + std::memset(m_number + number_size - num_bytes, 0, num_bytes); + n -= num_bytes * 8; + } + if (n > 0) + { + for (int i = 0; i < number_size - 1; ++i) + { + m_number[i] <<= n; + m_number[i] |= m_number[i+1] >> (8 - n); + } + m_number[number_size-1] <<= n; + } + return *this; + } + + // shift r ``n`` bits. + sha1_hash& operator>>=(int n) + { + TORRENT_ASSERT(n >= 0); + int num_bytes = n / 8; + if (num_bytes >= number_size) + { + std::memset(m_number, 0, number_size); + return *this; + } + if (num_bytes > 0) + { + std::memmove(m_number + num_bytes, m_number, number_size - num_bytes); + std::memset(m_number, 0, num_bytes); + n -= num_bytes * 8; + } + if (n > 0) + { + for (int i = number_size - 1; i > 0; --i) + { + m_number[i] >>= n; + m_number[i] |= (m_number[i-1] << (8 - n)) & 0xff; + } + m_number[0] >>= n; + } + return *this; + } + + // standard comparison operators + bool operator==(sha1_hash const& n) const + { + return std::equal(n.m_number, n.m_number+number_size, m_number); + } + bool operator!=(sha1_hash const& n) const + { + return !std::equal(n.m_number, n.m_number+number_size, m_number); + } + bool operator<(sha1_hash const& n) const + { + for (int i = 0; i < number_size; ++i) + { + if (m_number[i] < n.m_number[i]) return true; + if (m_number[i] > n.m_number[i]) return false; + } + return false; + } + + // returns a bit-wise negated copy of the sha1-hash + sha1_hash operator~() + { + sha1_hash ret; + for (int i = 0; i< number_size; ++i) + ret.m_number[i] = ~m_number[i]; + return ret; + } + + // returns the bit-wise XOR of the two sha1-hashes. + sha1_hash operator^(sha1_hash const& n) const + { + sha1_hash ret = *this; + ret ^= n; + return ret; + } + + // in-place bit-wise XOR with the passed in sha1_hash. + sha1_hash& operator^=(sha1_hash const& n) + { + for (int i = 0; i< number_size; ++i) + m_number[i] ^= n.m_number[i]; + return *this; + } + + // returns the bit-wise AND of the two sha1-hashes. + sha1_hash operator&(sha1_hash const& n) const + { + sha1_hash ret = *this; + ret &= n; + return ret; + } + + // in-place bit-wise AND of the passed in sha1_hash + sha1_hash& operator&=(sha1_hash const& n) + { + for (int i = 0; i< number_size; ++i) + m_number[i] &= n.m_number[i]; + return *this; + } + + // in-place bit-wise OR of the two sha1-hash. + sha1_hash& operator|=(sha1_hash const& n) + { + for (int i = 0; i< number_size; ++i) + m_number[i] |= n.m_number[i]; + return *this; + } + + // accessors for specific bytes + unsigned char& operator[](int i) + { TORRENT_ASSERT(i >= 0 && i < number_size); return m_number[i]; } + unsigned char const& operator[](int i) const + { TORRENT_ASSERT(i >= 0 && i < number_size); return m_number[i]; } + + typedef const unsigned char* const_iterator; + typedef unsigned char* iterator; + + // start and end iterators for the hash. The value type + // of these iterators is ``unsigned char``. + const_iterator begin() const { return m_number; } + const_iterator end() const { return m_number+number_size; } + iterator begin() { return m_number; } + iterator end() { return m_number+number_size; } + + // return a copy of the 20 bytes representing the sha1-hash as a std::string. + // It's still a binary string with 20 binary characters. + std::string to_string() const + { return std::string((char const*)&m_number[0], number_size); } + + private: + + unsigned char m_number[number_size]; + + }; + + typedef sha1_hash peer_id; + +#if TORRENT_USE_IOSTREAM + + // print a sha1_hash object to an ostream as 40 hexadecimal digits + inline std::ostream& operator<<(std::ostream& os, sha1_hash const& peer) + { + char out[41]; + to_hex((char const*)&peer[0], sha1_hash::size, out); + return os << out; + } + + // read 40 hexadecimal digits from an istream into a sha1_hash + inline std::istream& operator>>(std::istream& is, sha1_hash& peer) + { + char hex[40]; + is.read(hex, 40); + if (!from_hex(hex, 40, (char*)&peer[0])) + is.setstate(std::ios_base::failbit); + return is; + } +#endif // TORRENT_USE_IOSTREAM +} + +#endif // TORRENT_PEER_ID_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/size_type.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/size_type.hpp new file mode 100644 index 0000000000..f4238c7d54 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/size_type.hpp @@ -0,0 +1,53 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SIZE_TYPE_HPP_INCLUDED +#define TORRENT_SIZE_TYPE_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace libtorrent +{ + typedef boost::int64_t size_type; + typedef boost::uint64_t unsigned_size_type; +} + + +#endif diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/sliding_average.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/sliding_average.hpp new file mode 100644 index 0000000000..7db7f33457 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/sliding_average.hpp @@ -0,0 +1,114 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SLIDING_AVERAGE_HPP_INCLUDED +#define TORRENT_SLIDING_AVERAGE_HPP_INCLUDED + +#include +#include // for std::abs + +namespace libtorrent +{ + +// an exponential moving average accumulator. Add samples to it and it keeps +// track of a moving mean value and an average deviation +template +struct sliding_average +{ + sliding_average(): m_mean(0), m_average_deviation(0), m_num_samples(0) {} + + void add_sample(int s) + { + // fixed point + s *= 64; + int deviation; + + if (m_num_samples > 0) + deviation = std::abs(m_mean - s); + + if (m_num_samples < inverted_gain) + ++m_num_samples; + + m_mean += (s - m_mean) / m_num_samples; + + if (m_num_samples > 1) { + // the the exact same thing for deviation off the mean except -1 on + // the samples, because the number of deviation samples always lags + // behind by 1 (you need to actual samples to have a single deviation + // sample). + m_average_deviation += (deviation - m_average_deviation) / (m_num_samples - 1); + } + } + + int mean() const { return m_num_samples > 0 ? (m_mean + 32) / 64 : 0; } + int avg_deviation() const { return m_num_samples > 1 ? (m_average_deviation + 32) / 64 : 0; } + +private: + // both of these are fixed point values (* 64) + int m_mean; + int m_average_deviation; + // the number of samples we have received, but no more than inverted_gain + // this is the effective inverted_gain + int m_num_samples; +}; + +struct average_accumulator +{ + average_accumulator() + : m_num_samples(0) + , m_sample_sum(0) + {} + + void add_sample(int s) + { + ++m_num_samples; + m_sample_sum += s; + } + + int mean() + { + int ret; + if (m_num_samples == 0) ret = 0; + else ret = int(m_sample_sum / m_num_samples); + m_num_samples = 0; + m_sample_sum = 0; + return ret; + } + + int m_num_samples; + boost::uint64_t m_sample_sum; +}; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/socket.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/socket.hpp new file mode 100644 index 0000000000..f063d3b83c --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/socket.hpp @@ -0,0 +1,209 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SOCKET_HPP_INCLUDED +#define TORRENT_SOCKET_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +// if building as Objective C++, asio's template +// parameters Protocol has to be renamed to avoid +// colliding with keywords + +#ifdef __OBJC__ +#define Protocol Protocol_ +#endif + +#if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN +// asio assumes that the windows error codes are defined already +#include +#endif + +#include + +#if BOOST_VERSION < 103500 +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif + +#ifdef __OBJC__ +#undef Protocol +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace libtorrent +{ + +#if BOOST_VERSION < 103500 + using ::asio::ip::tcp; + using ::asio::ip::udp; + using ::asio::async_write; + using ::asio::async_read; + + typedef ::asio::ip::tcp::socket stream_socket; + typedef ::asio::ip::udp::socket datagram_socket; + typedef ::asio::ip::tcp::acceptor socket_acceptor; +#else + using boost::asio::ip::tcp; + using boost::asio::ip::udp; + using boost::asio::async_write; + using boost::asio::async_read; + + typedef boost::asio::ip::tcp::socket stream_socket; + typedef boost::asio::ip::udp::socket datagram_socket; + typedef boost::asio::ip::tcp::acceptor socket_acceptor; + + namespace asio = boost::asio; +#endif + +#if TORRENT_USE_IPV6 +#ifdef IPV6_V6ONLY + struct v6only + { + v6only(bool enable): m_value(enable) {} + template + int level(Protocol const&) const { return IPPROTO_IPV6; } + template + int name(Protocol const&) const { return IPV6_V6ONLY; } + template + int const* data(Protocol const&) const { return &m_value; } + template + size_t size(Protocol const&) const { return sizeof(m_value); } + int m_value; + }; +#endif +#endif + +#ifdef TORRENT_WINDOWS + +#ifndef IPV6_PROTECTION_LEVEL +#define IPV6_PROTECTION_LEVEL 30 +#endif + struct v6_protection_level + { + v6_protection_level(int level): m_value(level) {} + template + int level(Protocol const&) const { return IPPROTO_IPV6; } + template + int name(Protocol const&) const { return IPV6_PROTECTION_LEVEL; } + template + int const* data(Protocol const&) const { return &m_value; } + template + size_t size(Protocol const&) const { return sizeof(m_value); } + int m_value; + }; +#endif + +#ifdef IPV6_TCLASS + struct traffic_class + { + traffic_class(char val): m_value(val) {} + template + int level(Protocol const&) const { return IPPROTO_IPV6; } + template + int name(Protocol const&) const { return IPV6_TCLASS; } + template + int const* data(Protocol const&) const { return &m_value; } + template + size_t size(Protocol const&) const { return sizeof(m_value); } + int m_value; + }; +#endif + + struct type_of_service + { +#ifdef WIN32 + typedef DWORD tos_t; +#else + typedef int tos_t; +#endif + type_of_service(char val): m_value(val) {} + template + int level(Protocol const&) const { return IPPROTO_IP; } + template + int name(Protocol const&) const { return IP_TOS; } + template + tos_t const* data(Protocol const&) const { return &m_value; } + template + size_t size(Protocol const&) const { return sizeof(m_value); } + tos_t m_value; + }; + +#if defined IP_DONTFRAG || defined IP_MTU_DISCOVER || defined IP_DONTFRAGMENT +#define TORRENT_HAS_DONT_FRAGMENT +#endif + +#ifdef TORRENT_HAS_DONT_FRAGMENT + struct dont_fragment + { + dont_fragment(bool val) +#ifdef IP_PMTUDISCOVER_DO + : m_value(val ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT) {} +#else + : m_value(val) {} +#endif + template + int level(Protocol const&) const { return IPPROTO_IP; } + template + int name(Protocol const&) const +#if defined IP_DONTFRAG + { return IP_DONTFRAG; } +#elif defined IP_MTU_DISCOVER + { return IP_MTU_DISCOVER; } +#elif defined IP_DONTFRAGMENT + { return IP_DONTFRAGMENT; } +#else + {} +#endif + template + int const* data(Protocol const&) const { return &m_value; } + template + size_t size(Protocol const&) const { return sizeof(m_value); } + int m_value; + }; +#endif // TORRENT_HAS_DONT_FRAGMENT +} + +#endif // TORRENT_SOCKET_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/socket_io.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/socket_io.hpp new file mode 100644 index 0000000000..6b11d4b7e7 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/socket_io.hpp @@ -0,0 +1,169 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SOCKET_IO_HPP_INCLUDED +#define TORRENT_SOCKET_IO_HPP_INCLUDED + +#include "libtorrent/socket.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/lazy_entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/peer_id.hpp" // for sha1_hash +#include + +namespace libtorrent +{ + TORRENT_EXTRA_EXPORT std::string print_address(address const& addr); + TORRENT_EXTRA_EXPORT std::string print_endpoint(tcp::endpoint const& ep); + TORRENT_EXTRA_EXPORT std::string print_endpoint(udp::endpoint const& ep); + TORRENT_EXTRA_EXPORT std::string address_to_bytes(address const& a); + // internal + TORRENT_EXPORT std::string endpoint_to_bytes(udp::endpoint const& ep); + TORRENT_EXTRA_EXPORT void hash_address(address const& ip, sha1_hash& h); + + namespace detail + { + template + void write_address(address const& a, OutIt& out) + { +#if TORRENT_USE_IPV6 + if (a.is_v4()) + { +#endif + write_uint32(a.to_v4().to_ulong(), out); +#if TORRENT_USE_IPV6 + } + else if (a.is_v6()) + { + typedef address_v6::bytes_type bytes_t; + bytes_t bytes = a.to_v6().to_bytes(); + for (bytes_t::iterator i = bytes.begin() + , end(bytes.end()); i != end; ++i) + write_uint8(*i, out); + } +#endif + } + + template + address read_v4_address(InIt& in) + { + unsigned long ip = read_uint32(in); + return address_v4(ip); + } + +#if TORRENT_USE_IPV6 + template + address read_v6_address(InIt& in) + { + typedef address_v6::bytes_type bytes_t; + bytes_t bytes; + for (bytes_t::iterator i = bytes.begin() + , end(bytes.end()); i != end; ++i) + *i = read_uint8(in); + return address_v6(bytes); + } +#endif + + template + void write_endpoint(Endpoint const& e, OutIt& out) + { + write_address(e.address(), out); + write_uint16(e.port(), out); + } + + template + Endpoint read_v4_endpoint(InIt& in) + { + address addr = read_v4_address(in); + int port = read_uint16(in); + return Endpoint(addr, port); + } + +#if TORRENT_USE_IPV6 + template + Endpoint read_v6_endpoint(InIt& in) + { + address addr = read_v6_address(in); + int port = read_uint16(in); + return Endpoint(addr, port); + } +#endif + + template + void read_endpoint_list(libtorrent::lazy_entry const* n, std::vector& epl) + { + using namespace libtorrent; + if (n->type() != lazy_entry::list_t) return; + for (int i = 0; i < n->list_size(); ++i) + { + lazy_entry const* e = n->list_at(i); + if (e->type() != lazy_entry::string_t) return; + if (e->string_length() < 6) continue; + char const* in = e->string_ptr(); + if (e->string_length() == 6) + epl.push_back(read_v4_endpoint(in)); +#if TORRENT_USE_IPV6 + else if (e->string_length() == 18) + epl.push_back(read_v6_endpoint(in)); +#endif + } + } + } + + template + void read_endpoint_list(libtorrent::entry const* n, std::vector& epl) + { + using namespace libtorrent; + if (n->type() != entry::list_t) return; + entry::list_type const& contacts = n->list(); + for (entry::list_type::const_iterator i = contacts.begin() + , end(contacts.end()); i != end; ++i) + { + if (i->type() != entry::string_t) return; + std::string const& p = i->string(); + if (p.size() < 6) continue; + std::string::const_iterator in = p.begin(); + if (p.size() == 6) + epl.push_back(detail::read_v4_endpoint(in)); +#if TORRENT_USE_IPV6 + else if (p.size() == 18) + epl.push_back(detail::read_v6_endpoint(in)); +#endif + } + } + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/socket_type.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/socket_type.hpp new file mode 100644 index 0000000000..7dc5d435b8 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/socket_type.hpp @@ -0,0 +1,337 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SOCKET_TYPE +#define TORRENT_SOCKET_TYPE + +#include "libtorrent/config.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/socks5_stream.hpp" +#include "libtorrent/http_stream.hpp" +#include "libtorrent/i2p_stream.hpp" +#include "libtorrent/utp_stream.hpp" +#include "libtorrent/io_service.hpp" +#include "libtorrent/max.hpp" +#include "libtorrent/assert.hpp" + +#ifdef TORRENT_USE_OPENSSL +#include "libtorrent/ssl_stream.hpp" +#endif + +#if defined TORRENT_ASIO_DEBUGGING +#include "libtorrent/debug.hpp" +#endif + +#if defined TORRENT_OS2 && defined ioc +#undef ioc +#endif + +#if TORRENT_USE_I2P + +#define TORRENT_SOCKTYPE_I2P_FORWARD(x) \ + case socket_type_int_impl::value: \ + get()->x; break; + +#define TORRENT_SOCKTYPE_I2P_FORWARD_RET(x, def) \ + case socket_type_int_impl::value: \ + return get()->x; + +#else // TORRENT_USE_I2P + +#define TORRENT_SOCKTYPE_I2P_FORWARD(x) +#define TORRENT_SOCKTYPE_I2P_FORWARD_RET(x, def) + +#endif + +#ifdef TORRENT_USE_OPENSSL + +#define TORRENT_SOCKTYPE_SSL_FORWARD(x) \ + case socket_type_int_impl >::value: \ + get >()->x; break; \ + case socket_type_int_impl >::value: \ + get >()->x; break; \ + case socket_type_int_impl >::value: \ + get >()->x; break; \ + case socket_type_int_impl >::value: \ + get >()->x; break; + +#define TORRENT_SOCKTYPE_SSL_FORWARD_RET(x, def) \ + case socket_type_int_impl >::value: \ + return get >()->x; \ + case socket_type_int_impl >::value: \ + return get >()->x; \ + case socket_type_int_impl >::value: \ + return get >()->x; \ + case socket_type_int_impl >::value: \ + return get >()->x; + +#else + +#define TORRENT_SOCKTYPE_SSL_FORWARD(x) +#define TORRENT_SOCKTYPE_SSL_FORWARD_RET(x, def) + +#endif + +#define TORRENT_SOCKTYPE_FORWARD(x) \ + switch (m_type) { \ + case socket_type_int_impl::value: \ + get()->x; break; \ + case socket_type_int_impl::value: \ + get()->x; break; \ + case socket_type_int_impl::value: \ + get()->x; break; \ + case socket_type_int_impl::value: \ + get()->x; break; \ + TORRENT_SOCKTYPE_I2P_FORWARD(x) \ + TORRENT_SOCKTYPE_SSL_FORWARD(x) \ + default: TORRENT_ASSERT(false); \ + } + +#define TORRENT_SOCKTYPE_FORWARD_RET(x, def) \ + switch (m_type) { \ + case socket_type_int_impl::value: \ + return get()->x; \ + case socket_type_int_impl::value: \ + return get()->x; \ + case socket_type_int_impl::value: \ + return get()->x; \ + case socket_type_int_impl::value: \ + return get()->x; \ + TORRENT_SOCKTYPE_I2P_FORWARD_RET(x, def) \ + TORRENT_SOCKTYPE_SSL_FORWARD_RET(x, def) \ + default: TORRENT_ASSERT(false); return def; \ + } + +namespace libtorrent +{ + + template + struct socket_type_int_impl + { enum { value = 0 }; }; + + template <> + struct socket_type_int_impl + { enum { value = 1 }; }; + + template <> + struct socket_type_int_impl + { enum { value = 2 }; }; + + template <> + struct socket_type_int_impl + { enum { value = 3 }; }; + + template <> + struct socket_type_int_impl + { enum { value = 4 }; }; + +#if TORRENT_USE_I2P + template <> + struct socket_type_int_impl + { enum { value = 5 }; }; +#endif + +#ifdef TORRENT_USE_OPENSSL + template <> + struct socket_type_int_impl > + { enum { value = 6 }; }; + + template <> + struct socket_type_int_impl > + { enum { value = 7 }; }; + + template <> + struct socket_type_int_impl > + { enum { value = 8 }; }; + + template <> + struct socket_type_int_impl > + { enum { value = 9 }; }; +#endif + + struct TORRENT_EXTRA_EXPORT socket_type + { + typedef stream_socket::endpoint_type endpoint_type; + typedef stream_socket::protocol_type protocol_type; + + explicit socket_type(io_service& ios): m_io_service(ios), m_type(0) {} + ~socket_type(); + + io_service& get_io_service() const; + bool is_open() const; + + char const* type_name() const; + +#ifndef BOOST_NO_EXCEPTIONS + void open(protocol_type const& p); + void close(); + endpoint_type local_endpoint() const; + endpoint_type remote_endpoint() const; + void bind(endpoint_type const& endpoint); + std::size_t available() const; +#endif + + void open(protocol_type const& p, error_code& ec); + void close(error_code& ec); + endpoint_type local_endpoint(error_code& ec) const; + endpoint_type remote_endpoint(error_code& ec) const; + void bind(endpoint_type const& endpoint, error_code& ec); + std::size_t available(error_code& ec) const; + int type() const; + + + template + std::size_t read_some(Mutable_Buffers const& buffers, error_code& ec) + { TORRENT_SOCKTYPE_FORWARD_RET(read_some(buffers, ec), 0) } + + template + void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) + { TORRENT_SOCKTYPE_FORWARD(async_read_some(buffers, handler)) } + + template + std::size_t write_some(Const_Buffers const& buffers, error_code& ec) + { TORRENT_SOCKTYPE_FORWARD_RET(write_some(buffers, ec), 0) } + + template + void async_write_some(Const_Buffers const& buffers, Handler const& handler) + { TORRENT_SOCKTYPE_FORWARD(async_write_some(buffers, handler)) } + + template + void async_connect(endpoint_type const& endpoint, Handler const& handler) + { TORRENT_SOCKTYPE_FORWARD(async_connect(endpoint, handler)) } + +#ifndef BOOST_NO_EXCEPTIONS + template + void io_control(IO_Control_Command& ioc) + { TORRENT_SOCKTYPE_FORWARD(io_control(ioc)) } + + template + std::size_t read_some(Mutable_Buffers const& buffers) + { TORRENT_SOCKTYPE_FORWARD_RET(read_some(buffers), 0) } +#endif + + template + void io_control(IO_Control_Command& ioc, error_code& ec) + { TORRENT_SOCKTYPE_FORWARD(io_control(ioc, ec)) } + +#ifndef BOOST_NO_EXCEPTIONS + template + void set_option(SettableSocketOption const& opt) + { TORRENT_SOCKTYPE_FORWARD(set_option(opt)) } +#endif + + template + error_code set_option(SettableSocketOption const& opt, error_code& ec) + { TORRENT_SOCKTYPE_FORWARD_RET(set_option(opt, ec), ec) } + +#ifndef BOOST_NO_EXCEPTIONS + template + void get_option(GettableSocketOption& opt) + { TORRENT_SOCKTYPE_FORWARD(get_option(opt)) } +#endif + + template + error_code get_option(GettableSocketOption& opt, error_code& ec) + { TORRENT_SOCKTYPE_FORWARD_RET(get_option(opt, ec), ec) } + + + template + void instantiate(io_service& ios, void* userdata = 0) + { + TORRENT_ASSERT(&ios == &m_io_service); + construct(socket_type_int_impl::value, userdata); + } + + template S* get() + { + if (m_type != socket_type_int_impl::value) return 0; + return (S*)m_data; + } + + template S const* get() const + { + if (m_type != socket_type_int_impl::value) return 0; + return (S const*)m_data; + } + + private: + + void destruct(); + void construct(int type, void* userdata); + + io_service& m_io_service; + int m_type; + enum { storage_size = max9< + sizeof(stream_socket) + , sizeof(socks5_stream) + , sizeof(http_stream) + , sizeof(utp_stream) +#if TORRENT_USE_I2P + , sizeof(i2p_stream) +#else + , 0 +#endif +#ifdef TORRENT_USE_OPENSSL + , sizeof(ssl_stream) + , sizeof(ssl_stream) + , sizeof(ssl_stream) + , sizeof(ssl_stream) +#else + , 0, 0, 0, 0 +#endif + >::value + }; + + size_type m_data[(storage_size + sizeof(size_type) - 1) / sizeof(size_type)]; + }; + + // returns true if this socket is an SSL socket + bool is_ssl(socket_type const& s); + + // returns true if this is a uTP socket + bool is_utp(socket_type const& s); + +#if TORRENT_USE_I2P + // returns true if this is an i2p socket + bool is_i2p(socket_type const& s); +#endif + + // assuming the socket_type s is an ssl socket, make sure it + // verifies the hostname in its SSL handshake + void setup_ssl_hostname(socket_type& s, std::string const& hostname, error_code& ec); + + // properly shuts down SSL sockets. holder keeps s alive + void async_shutdown(socket_type& s, boost::shared_ptr holder); +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/socket_type_fwd.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/socket_type_fwd.hpp new file mode 100644 index 0000000000..20276710ab --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/socket_type_fwd.hpp @@ -0,0 +1,42 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SOCKET_TYPE_FWD_HPP +#define TORRENT_SOCKET_TYPE_FWD_HPP + +namespace libtorrent +{ + struct socket_type; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/socks5_stream.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/socks5_stream.hpp new file mode 100644 index 0000000000..4ca6c68d6b --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/socks5_stream.hpp @@ -0,0 +1,192 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SOCKS5_STREAM_HPP_INCLUDED +#define TORRENT_SOCKS5_STREAM_HPP_INCLUDED + +#include +#include +#include +#include "libtorrent/proxy_base.hpp" +#if defined TORRENT_ASIO_DEBUGGING +#include "libtorrent/debug.hpp" +#endif + +namespace libtorrent { +namespace socks_error { + + // SOCKS5 error values. If an error_code has the + // socks error category (get_socks_category()), these + // are the error values. + enum socks_error_code + { + no_error = 0, + unsupported_version, + unsupported_authentication_method, + unsupported_authentication_version, + authentication_error, + username_required, + general_failure, + command_not_supported, + no_identd, + identd_error, + + num_errors + }; + + // internal + TORRENT_EXPORT boost::system::error_code make_error_code(socks_error_code e); + +} // namespace socks_error + +// returns the error_category for SOCKS5 errors +TORRENT_EXPORT boost::system::error_category& get_socks_category(); + +class socks5_stream : public proxy_base +{ +public: + + explicit socks5_stream(io_service& io_service) + : proxy_base(io_service) + , m_version(5) + , m_command(1) + , m_listen(0) + {} + + void set_version(int v) { m_version = v; } + + void set_command(int c) { m_command = c; } + + void set_username(std::string const& user + , std::string const& password) + { + m_user = user; + m_password = password; + } + + void set_dst_name(std::string const& host) + { + m_dst_name = host; + if (m_dst_name.size() > 255) + m_dst_name.resize(255); + } + + void close(error_code& ec) + { + m_hostname.clear(); + m_dst_name.clear(); + proxy_base::close(ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + void close() + { + m_hostname.clear(); + m_dst_name.clear(); + proxy_base::close(); + } +#endif + + typedef boost::function handler_type; + +//#error fix error messages to use custom error_code category +//#error add async_connect() that takes a hostname and port as well + template + void async_connect(endpoint_type const& endpoint, Handler const& handler) + { + m_remote_endpoint = endpoint; + + // the connect is split up in the following steps: + // 1. resolve name of proxy server + // 2. connect to proxy server + // 3. if version == 5: + // 3.1 send SOCKS5 authentication method message + // 3.2 read SOCKS5 authentication response + // 3.3 send username+password + // 4. send SOCKS command message + + // to avoid unnecessary copying of the handler, + // store it in a shaed_ptr + boost::shared_ptr h(new handler_type(handler)); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::name_lookup"); +#endif + tcp::resolver::query q(m_hostname, to_string(m_port).elems); + m_resolver.async_resolve(q, boost::bind( + &socks5_stream::name_lookup, this, _1, _2, h)); + } + +private: + + void name_lookup(error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h); + void connected(error_code const& e, boost::shared_ptr h); + void handshake1(error_code const& e, boost::shared_ptr h); + void handshake2(error_code const& e, boost::shared_ptr h); + void handshake3(error_code const& e, boost::shared_ptr h); + void handshake4(error_code const& e, boost::shared_ptr h); + void socks_connect(boost::shared_ptr h); + void connect1(error_code const& e, boost::shared_ptr h); + void connect2(error_code const& e, boost::shared_ptr h); + void connect3(error_code const& e, boost::shared_ptr h); + + // send and receive buffer + std::vector m_buffer; + // proxy authentication + std::string m_user; + std::string m_password; + std::string m_dst_name; + int m_version; + int m_command; + // set to one when we're waiting for the + // second message to accept an incoming connection + int m_listen; +}; + +} + +#if BOOST_VERSION >= 103500 + +namespace boost { namespace system { + + template<> struct is_error_code_enum + { static const bool value = true; }; + + template<> struct is_error_condition_enum + { static const bool value = true; }; +} } + +#endif // BOOST_VERSION + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/ssl_stream.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/ssl_stream.hpp new file mode 100644 index 0000000000..9644b8b172 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/ssl_stream.hpp @@ -0,0 +1,308 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SSL_STREAM_HPP_INCLUDED +#define TORRENT_SSL_STREAM_HPP_INCLUDED + +#include "libtorrent/socket.hpp" +#include +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif +// openssl seems to believe it owns +// this name in every single scope +#undef set_key + +namespace libtorrent { + +template +class ssl_stream +{ +public: + + explicit ssl_stream(io_service& io_service, asio::ssl::context& ctx) + : m_sock(io_service, ctx) + { + } + + typedef typename asio::ssl::stream sock_type; + typedef typename sock_type::next_layer_type next_layer_type; + typedef typename Stream::lowest_layer_type lowest_layer_type; + typedef typename Stream::endpoint_type endpoint_type; + typedef typename Stream::protocol_type protocol_type; + + void set_host_name(std::string name) + { +#if OPENSSL_VERSION_NUMBER >= 0x90812f + SSL_set_tlsext_host_name(m_sock.native_handle(), name.c_str()); +#endif + } + + template + void set_verify_callback(T const& fun, error_code& ec) + { m_sock.set_verify_callback(fun, ec); } + +#if BOOST_VERSION >= 104700 + SSL* native_handle() { return m_sock.native_handle(); } +#endif + + typedef boost::function handler_type; + + template + void async_connect(endpoint_type const& endpoint, Handler const& handler) + { + // the connect is split up in the following steps: + // 1. connect to peer + // 2. perform SSL client handshake + + // to avoid unnecessary copying of the handler, + // store it in a shared_ptr + boost::shared_ptr h(new handler_type(handler)); + + m_sock.next_layer().async_connect(endpoint + , boost::bind(&ssl_stream::connected, this, _1, h)); + } + + template + void async_accept_handshake(Handler const& handler) + { + // this is used for accepting SSL connections + boost::shared_ptr h(new handler_type(handler)); + m_sock.async_handshake(asio::ssl::stream_base::server + , boost::bind(&ssl_stream::handshake, this, _1, h)); + } + + void accept_handshake(error_code& ec) + { + // this is used for accepting SSL connections + m_sock.handshake(asio::ssl::stream_base::server, ec); + } + + template + void async_shutdown(Handler const& handler) + { + m_sock.async_shutdown(handler); + } + + void shutdown(error_code& ec) + { + m_sock.shutdown(ec); + } + + template + void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) + { + m_sock.async_read_some(buffers, handler); + } + + template + std::size_t read_some(Mutable_Buffers const& buffers, error_code& ec) + { + return m_sock.read_some(buffers, ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + template + void set_option(SettableSocketOption const& opt) + { + m_sock.next_layer().set_option(opt); + } +#endif + + template + error_code set_option(SettableSocketOption const& opt, error_code& ec) + { + return m_sock.next_layer().set_option(opt, ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + template + void get_option(GettableSocketOption& opt) + { + m_sock.next_layer().get_option(opt); + } +#endif + + template + error_code get_option(GettableSocketOption& opt, error_code& ec) + { + return m_sock.next_layer().get_option(opt, ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + template + std::size_t read_some(Mutable_Buffers const& buffers) + { + return m_sock.read_some(buffers); + } + + template + void io_control(IO_Control_Command& ioc) + { + m_sock.next_layer().io_control(ioc); + } +#endif + + template + void io_control(IO_Control_Command& ioc, error_code& ec) + { + m_sock.next_layer().io_control(ioc, ec); + } + + template + void async_write_some(Const_Buffers const& buffers, Handler const& handler) + { + m_sock.async_write_some(buffers, handler); + } + + template + std::size_t write_some(Const_Buffers const& buffers, error_code& ec) + { + return m_sock.write_some(buffers, ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + std::size_t available() const + { return const_cast(m_sock).next_layer().available(); } +#endif + + std::size_t available(error_code& ec) const + { return const_cast(m_sock).next_layer().available(ec); } + +#ifndef BOOST_NO_EXCEPTIONS + void bind(endpoint_type const& endpoint) + { + m_sock.next_layer().bind(endpoint); + } +#endif + + void bind(endpoint_type const& endpoint, error_code& ec) + { + m_sock.next_layer().bind(endpoint, ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + void open(protocol_type const& p) + { + m_sock.next_layer().open(p); + } +#endif + + void open(protocol_type const& p, error_code& ec) + { + m_sock.next_layer().open(p, ec); + } + + bool is_open() const + { + return const_cast(m_sock).next_layer().is_open(); + } + +#ifndef BOOST_NO_EXCEPTIONS + void close() + { + m_sock.next_layer().close(); + } +#endif + + void close(error_code& ec) + { + m_sock.next_layer().close(ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + endpoint_type remote_endpoint() const + { + return const_cast(m_sock).next_layer().remote_endpoint(); + } +#endif + + endpoint_type remote_endpoint(error_code& ec) const + { + return const_cast(m_sock).next_layer().remote_endpoint(ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + endpoint_type local_endpoint() const + { + return const_cast(m_sock).next_layer().local_endpoint(); + } +#endif + + endpoint_type local_endpoint(error_code& ec) const + { + return const_cast(m_sock).next_layer().local_endpoint(ec); + } + + io_service& get_io_service() + { + return m_sock.get_io_service(); + } + + lowest_layer_type& lowest_layer() + { + return m_sock.lowest_layer(); + } + + next_layer_type& next_layer() + { + return m_sock.next_layer(); + } + +private: + + void connected(error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + return; + } + + m_sock.async_handshake(asio::ssl::stream_base::client + , boost::bind(&ssl_stream::handshake, this, _1, h)); + } + + void handshake(error_code const& e, boost::shared_ptr h) + { + (*h)(e); + } + + asio::ssl::stream m_sock; +}; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/stat.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/stat.hpp new file mode 100644 index 0000000000..a64b0c9b3a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/stat.hpp @@ -0,0 +1,384 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_STAT_HPP_INCLUDED +#define TORRENT_STAT_HPP_INCLUDED + +#include +#include +#include +#include + +#include "libtorrent/size_type.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" + +namespace libtorrent +{ + class TORRENT_EXTRA_EXPORT stat_channel + { + public: + + stat_channel() + : m_counter(0) + , m_5_sec_average(0) + , m_30_sec_average(0) + , m_total_counter(0) + {} + + void operator+=(stat_channel const& s) + { + TORRENT_ASSERT(m_counter >= 0); + TORRENT_ASSERT(m_total_counter >= 0); + TORRENT_ASSERT(s.m_counter >= 0); + m_counter += s.m_counter; + m_total_counter += s.m_counter; + TORRENT_ASSERT(m_counter >= 0); + TORRENT_ASSERT(m_total_counter >= 0); + } + + void add(int count) + { + TORRENT_ASSERT(count >= 0); + + m_counter += count; + TORRENT_ASSERT(m_counter >= 0); + m_total_counter += count; + TORRENT_ASSERT(m_total_counter >= 0); + } + + // should be called once every second + void second_tick(int tick_interval_ms); + int rate() const { return m_5_sec_average; } + int low_pass_rate() const { return m_30_sec_average; } + size_type total() const { return m_total_counter; } + + void offset(size_type c) + { + TORRENT_ASSERT(c >= 0); + TORRENT_ASSERT(m_total_counter >= 0); + m_total_counter += c; + TORRENT_ASSERT(m_total_counter >= 0); + } + + int counter() const { return m_counter; } + + void clear() + { + m_counter = 0; + m_5_sec_average = 0; + m_30_sec_average = 0; + m_total_counter = 0; + } + + private: + + // the accumulator for this second. + int m_counter; + + // sliding average + int m_5_sec_average; + int m_30_sec_average; + + // TODO: this is 4 bytes of padding! + + // total counters + size_type m_total_counter; + }; + + class TORRENT_EXTRA_EXPORT stat + { + friend class invariant_access; + public: + void operator+=(const stat& s) + { + for (int i = 0; i < num_channels; ++i) + m_stat[i] += s.m_stat[i]; + } + + void sent_syn(bool ipv6) + { +#ifndef TORRENT_DISABLE_FULL_STATS + m_stat[upload_ip_protocol].add(ipv6 ? 60 : 40); +#endif + } + + void received_synack(bool ipv6) + { +#ifndef TORRENT_DISABLE_FULL_STATS + // we received SYN-ACK and also sent ACK back + m_stat[download_ip_protocol].add(ipv6 ? 60 : 40); + m_stat[upload_ip_protocol].add(ipv6 ? 60 : 40); +#endif + } + + void received_dht_bytes(int bytes) + { +#ifndef TORRENT_DISABLE_FULL_STATS + TORRENT_ASSERT(bytes >= 0); + m_stat[download_dht_protocol].add(bytes); +#endif + } + + void sent_dht_bytes(int bytes) + { +#ifndef TORRENT_DISABLE_FULL_STATS + TORRENT_ASSERT(bytes >= 0); + m_stat[upload_dht_protocol].add(bytes); +#endif + } + + void received_tracker_bytes(int bytes) + { +#ifndef TORRENT_DISABLE_FULL_STATS + TORRENT_ASSERT(bytes >= 0); + m_stat[download_tracker_protocol].add(bytes); +#endif + } + + void sent_tracker_bytes(int bytes) + { +#ifndef TORRENT_DISABLE_FULL_STATS + TORRENT_ASSERT(bytes >= 0); + m_stat[upload_tracker_protocol].add(bytes); +#endif + } + + void received_bytes(int bytes_payload, int bytes_protocol) + { + TORRENT_ASSERT(bytes_payload >= 0); + TORRENT_ASSERT(bytes_protocol >= 0); + + m_stat[download_payload].add(bytes_payload); + m_stat[download_protocol].add(bytes_protocol); + } + + void sent_bytes(int bytes_payload, int bytes_protocol) + { + TORRENT_ASSERT(bytes_payload >= 0); + TORRENT_ASSERT(bytes_protocol >= 0); + + m_stat[upload_payload].add(bytes_payload); + m_stat[upload_protocol].add(bytes_protocol); + } + + // and IP packet was received or sent + // account for the overhead caused by it + void trancieve_ip_packet(int bytes_transferred, bool ipv6) + { +#ifndef TORRENT_DISABLE_FULL_STATS + // one TCP/IP packet header for the packet + // sent or received, and one for the ACK + // The IPv4 header is 20 bytes + // and IPv6 header is 40 bytes + const int header = (ipv6 ? 40 : 20) + 20; + const int mtu = 1500; + const int packet_size = mtu - header; + const int overhead = (std::max)(1, (bytes_transferred + packet_size - 1) / packet_size) * header; + m_stat[download_ip_protocol].add(overhead); + m_stat[upload_ip_protocol].add(overhead); +#endif + } + +#ifndef TORRENT_DISABLE_FULL_STATS + int upload_ip_overhead() const { return m_stat[upload_ip_protocol].counter(); } + int download_ip_overhead() const { return m_stat[download_ip_protocol].counter(); } + int upload_dht() const { return m_stat[upload_dht_protocol].counter(); } + int download_dht() const { return m_stat[download_dht_protocol].counter(); } + int download_tracker() const { return m_stat[download_tracker_protocol].counter(); } + int upload_tracker() const { return m_stat[upload_tracker_protocol].counter(); } +#else + int upload_ip_overhead() const { return 0; } + int download_ip_overhead() const { return 0; } + int upload_dht() const { return 0; } + int download_dht() const { return 0; } + int download_tracker() const { return 0; } + int upload_tracker() const { return 0; } +#endif + + // should be called once every second + void second_tick(int tick_interval_ms) + { + for (int i = 0; i < num_channels; ++i) + m_stat[i].second_tick(tick_interval_ms); + } + + int low_pass_upload_rate() const + { + return m_stat[upload_payload].low_pass_rate() + + m_stat[upload_protocol].low_pass_rate() +#ifndef TORRENT_DISABLE_FULL_STATS + + m_stat[upload_ip_protocol].low_pass_rate() + + m_stat[upload_dht_protocol].low_pass_rate() + + m_stat[upload_tracker_protocol].low_pass_rate() +#endif + ; + } + + int low_pass_download_rate() const + { + return m_stat[download_payload].low_pass_rate() + + m_stat[download_protocol].low_pass_rate() +#ifndef TORRENT_DISABLE_FULL_STATS + + m_stat[download_ip_protocol].low_pass_rate() + + m_stat[download_dht_protocol].low_pass_rate() + + m_stat[download_tracker_protocol].low_pass_rate() +#endif + ; + } + + int upload_rate() const + { + return m_stat[upload_payload].rate() + + m_stat[upload_protocol].rate() +#ifndef TORRENT_DISABLE_FULL_STATS + + m_stat[upload_ip_protocol].rate() + + m_stat[upload_dht_protocol].rate() + + m_stat[upload_tracker_protocol].rate() +#endif + ; + } + + int download_rate() const + { + return m_stat[download_payload].rate() + + m_stat[download_protocol].rate() +#ifndef TORRENT_DISABLE_FULL_STATS + + m_stat[download_ip_protocol].rate() + + m_stat[download_dht_protocol].rate() + + m_stat[download_tracker_protocol].rate() +#endif + ; + } + + size_type total_upload() const + { + return m_stat[upload_payload].total() + + m_stat[upload_protocol].total() +#ifndef TORRENT_DISABLE_FULL_STATS + + m_stat[upload_ip_protocol].total() + + m_stat[upload_dht_protocol].total() + + m_stat[upload_tracker_protocol].total() +#endif + ; + } + + size_type total_download() const + { + return m_stat[download_payload].total() + + m_stat[download_protocol].total() +#ifndef TORRENT_DISABLE_FULL_STATS + + m_stat[download_ip_protocol].total() + + m_stat[download_dht_protocol].total() + + m_stat[download_tracker_protocol].total() +#endif + ; + } + + int upload_payload_rate() const + { return m_stat[upload_payload].rate(); } + int download_payload_rate() const + { return m_stat[download_payload].rate(); } + + size_type total_payload_upload() const + { return m_stat[upload_payload].total(); } + size_type total_payload_download() const + { return m_stat[download_payload].total(); } + + size_type total_protocol_upload() const + { return m_stat[upload_protocol].total(); } + size_type total_protocol_download() const + { return m_stat[download_protocol].total(); } + + size_type total_transfer(int channel) const + { return m_stat[channel].total(); } + int transfer_rate(int channel) const + { return m_stat[channel].rate(); } + + // this is used to offset the statistics when a + // peer_connection is opened and have some previous + // transfers from earlier connections. + void add_stat(size_type downloaded, size_type uploaded) + { + m_stat[download_payload].offset(downloaded); + m_stat[upload_payload].offset(uploaded); + } + + int last_payload_downloaded() const + { return m_stat[download_payload].counter(); } + int last_payload_uploaded() const + { return m_stat[upload_payload].counter(); } + int last_protocol_downloaded() const + { return m_stat[download_protocol].counter(); } + int last_protocol_uploaded() const + { return m_stat[upload_protocol].counter(); } + + // these are the channels we keep stats for + enum + { + upload_payload, + upload_protocol, + download_payload, + download_protocol, +#ifndef TORRENT_DISABLE_FULL_STATS + upload_ip_protocol, + upload_dht_protocol, + upload_tracker_protocol, + download_ip_protocol, + download_dht_protocol, + download_tracker_protocol, +#endif + num_channels + }; + + void clear() + { + for (int i = 0; i < num_channels; ++i) + m_stat[i].clear(); + } + + stat_channel const& operator[](int i) const + { + TORRENT_ASSERT(i >= 0 && i < num_channels); + return m_stat[i]; + } + + private: + + stat_channel m_stat[num_channels]; + }; + +} + +#endif // TORRENT_STAT_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/storage.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/storage.hpp new file mode 100644 index 0000000000..3e6c10a2a8 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/storage.hpp @@ -0,0 +1,844 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_STORAGE_HPP_INCLUDE +#define TORRENT_STORAGE_HPP_INCLUDE + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/piece_picker.hpp" +#include "libtorrent/intrusive_ptr_base.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/file.hpp" +#include "libtorrent/disk_buffer_holder.hpp" +#include "libtorrent/thread.hpp" +#include "libtorrent/storage_defs.hpp" +#include "libtorrent/allocator.hpp" +#include "libtorrent/bitfield.hpp" + +// OVERVIEW +// +// libtorrent provides a customization point for storage of data. By default, +// (``default_storage``) downloaded files are saved to disk according with the +// general conventions of bittorrent clients, mimicing the original file layout +// when the torrent was created. The libtorrent user may define a custom +// storage to store piece data in a different way. +// +// A custom storage implementation must derive from and implement the +// storage_interface. You must also provide a function that constructs the +// custom storage object and provide this function to the add_torrent() call +// via add_torrent_params. Either passed in to the constructor or by setting +// the add_torrent_params::storage field. +// +// This is an example storage implementation that stores all pieces in a +// ``std::map``, i.e. in RAM. It's not necessarily very useful in practice, but +// illustrates the basics of implementing a custom storage. +// +// .. code:: c++ +// +// struct temp_storage : storage_interface +// { +// temp_storage(file_storage const& fs) : m_files(fs) {} +// void set_file_priority(std::vector const& prio) {} +// virtual bool initialize(bool allocate_files) { return false; } +// virtual bool has_any_file() { return false; } +// virtual int read(char* buf, int slot, int offset, int size) +// { +// std::map >::const_iterator i = m_file_data.find(slot); +// if (i == m_file_data.end()) return 0; +// int available = i->second.size() - offset; +// if (available <= 0) return 0; +// if (available > size) available = size; +// memcpy(buf, &i->second[offset], available); +// return available; +// } +// virtual int write(const char* buf, int slot, int offset, int size) +// { +// std::vector& data = m_file_data[slot]; +// if (data.size() < offset + size) data.resize(offset + size); +// std::memcpy(&data[offset], buf, size); +// return size; +// } +// virtual bool rename_file(int file, std::string const& new_name) +// { assert(false); return false; } +// virtual bool move_storage(std::string const& save_path) { return false; } +// virtual bool verify_resume_data(lazy_entry const& rd, error_code& error) { return false; } +// virtual bool write_resume_data(entry& rd) const { return false; } +// virtual bool move_slot(int src_slot, int dst_slot) { assert(false); return false; } +// virtual bool swap_slots(int slot1, int slot2) { assert(false); return false; } +// virtual bool swap_slots3(int slot1, int slot2, int slot3) { assert(false); return false; } +// virtual size_type physical_offset(int slot, int offset) +// { return slot * m_files.piece_length() + offset; }; +// virtual sha1_hash hash_for_slot(int slot, partial_hash& ph, int piece_size) +// { +// int left = piece_size - ph.offset; +// assert(left >= 0); +// if (left > 0) +// { +// std::vector& data = m_file_data[slot]; +// // if there are padding files, those blocks will be considered +// // completed even though they haven't been written to the storage. +// // in this case, just extend the piece buffer to its full size +// // and fill it with zeroes. +// if (data.size() < piece_size) data.resize(piece_size, 0); +// ph.h.update(&data[ph.offset], left); +// } +// return ph.h.final(); +// } +// virtual bool release_files() { return false; } +// virtual bool delete_files() { return false; } +// +// std::map > m_file_data; +// file_storage m_files; +// }; +// +// storage_interface* temp_storage_constructor( +// file_storage const& fs, file_storage const* mapped +// , std::string const& path, file_pool& fp +// , std::vector const& prio) +// { +// return new temp_storage(fs); +// } + +namespace libtorrent +{ + class session; + struct file_pool; + struct disk_io_job; + struct disk_buffer_pool; + struct session_settings; + + TORRENT_EXTRA_EXPORT std::vector > get_filesizes( + file_storage const& t + , std::string const& p); + + TORRENT_EXTRA_EXPORT bool match_filesizes( + file_storage const& t + , std::string const& p + , std::vector > const& sizes + , bool compact_mode + , std::string* error = 0); + + struct TORRENT_EXTRA_EXPORT partial_hash + { + partial_hash(): offset(0) {} + // the number of bytes in the piece that has been hashed + int offset; + // the sha-1 context + hasher h; + }; + + // The storage interface is a pure virtual class that can be implemented to + // customize how and where data for a torrent is stored. The default storage + // implementation uses regular files in the filesystem, mapping the files in + // the torrent in the way one would assume a torrent is saved to disk. + // Implementing your own storage interface makes it possible to store all + // data in RAM, or in some optimized order on disk (the order the pieces are + // received for instance), or saving multifile torrents in a single file in + // order to be able to take advantage of optimized disk-I/O. + // + // It is also possible to write a thin class that uses the default storage + // but modifies some particular behavior, for instance encrypting the data + // before it's written to disk, and decrypting it when it's read again. + // + // The storage interface is based on slots, each slot is 'piece_size' number + // of bytes. All access is done by writing and reading whole or partial + // slots. One slot is one piece in the torrent, but the data in the slot + // does not necessarily correspond to the piece with the same index (in + // compact allocation mode it won't). + // + // libtorrent comes with two built-in storage implementations; + // ``default_storage`` and ``disabled_storage``. Their constructor functions + // are called default_storage_constructor() and + // ``disabled_storage_constructor`` respectively. The disabled storage does + // just what it sounds like. It throws away data that's written, and it + // reads garbage. It's useful mostly for benchmarking and profiling purpose. + // + struct TORRENT_EXPORT storage_interface + { + // hidden + storage_interface(): m_disk_pool(0), m_settings(0) {} + + + // This function is called when the storage is to be initialized. The + // default storage will create directories and empty files at this point. + // If ``allocate_files`` is true, it will also ``ftruncate`` all files to + // their target size. + // + // Returning ``true`` indicates an error occurred. + virtual bool initialize(bool allocate_files) = 0; + + // This function is called when first checking (or re-checking) the + // storage for a torrent. It should return true if any of the files that + // is used in this storage exists on disk. If so, the storage will be + // checked for existing pieces before starting the download. + virtual bool has_any_file() = 0; + + + // change the priorities of files. + virtual void set_file_priority(std::vector const& prio) = 0; + + // These functions should read or write the data in or to the given + // ``slot`` at the given ``offset``. It should read or write ``num_bufs`` + // buffers sequentially, where the size of each buffer is specified in + // the buffer array ``bufs``. The file::iovec_t type has the following + // members:: + // + // struct iovec_t { void* iov_base; size_t iov_len; }; + // + // The return value is the number of bytes actually read or written, or + // -1 on failure. If it returns -1, the error code is expected to be set + // to + // + // Every buffer in ``bufs`` can be assumed to be page aligned and be of a + // page aligned size, except for the last buffer of the torrent. The + // allocated buffer can be assumed to fit a fully page aligned number of + // bytes though. This is useful when reading and writing the last piece + // of a file in unbuffered mode. + // + // The ``offset`` is aligned to 16 kiB boundries *most of the time*, but + // there are rare exceptions when it's not. Specifically if the read + // cache is disabled/or full and a client requests unaligned data, or the + // file itself is not aligned in the torrent. Most clients request + // aligned data. + virtual int readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs, int flags = file::random_access); + virtual int writev(file::iovec_t const* bufs, int slot, int offset, int num_bufs, int flags = file::random_access); + + // This function is called when a read job is queued. It gives the + // storage wrapper an opportunity to hint the operating system about this + // coming read. For instance, the storage may call + // ``posix_fadvise(POSIX_FADV_WILLNEED)`` or ``fcntl(F_RDADVISE)``. + virtual void hint_read(int, int, int) {} + + // negative return value indicates an error + virtual int read(char* buf, int slot, int offset, int size) = 0; + + // negative return value indicates an error + virtual int write(const char* buf, int slot, int offset, int size) = 0; + + // returns the offset on the physical storage medium for the + // byte at offset ``offset`` in slot ``slot``. + virtual size_type physical_offset(int slot, int offset) = 0; + + // This function is optional. It is supposed to return the first piece, + // starting at ``start`` that is fully contained within a data-region on + // disk (i.e. non-sparse region). The purpose of this is to skip parts of + // files that can be known to contain zeros when checking files. + virtual int sparse_end(int start) const { return start; } + + // This function should move all the files belonging to the storage to + // the new save_path. The default storage moves the single file or the + // directory of the torrent. + // + // Before moving the files, any open file handles may have to be closed, + // like ``release_files()``. + // + // returns one of: + // | no_error = 0 + // | need_full_check = -1 + // | fatal_disk_error = -2 + // | file_exist = -4 + virtual int move_storage(std::string const& save_path, int flags) = 0; + + // This function should verify the resume data ``rd`` with the files + // on disk. If the resume data seems to be up-to-date, return true. If + // not, set ``error`` to a description of what mismatched and return false. + // + // The default storage may compare file sizes and time stamps of the files. + // + // Returning ``false`` indicates an error occurred. + virtual bool verify_resume_data(lazy_entry const& rd, error_code& error) = 0; + + // This function should fill in resume data, the current state of the + // storage, in ``rd``. The default storage adds file timestamps and + // sizes. + // + // Returning ``true`` indicates an error occurred. + virtual bool write_resume_data(entry& rd) const = 0; + + // This function should copy or move the data in slot ``src_slot`` to + // the slot ``dst_slot``. This is only used in compact mode. + // + // If the storage caches slots, this could be implemented more + // efficient than reading and writing the data. + // + // Returning ``true`` indicates an error occurred. + virtual bool move_slot(int src_slot, int dst_slot) = 0; + + // This function should swap the data in ``slot1`` and ``slot2``. The + // default storage uses a scratch buffer to read the data into, then + // moving the other slot and finally writing back the temporary slot's + // data + // + // This is only used in compact mode. + // + // Returning ``true`` indicates an error occurred. + virtual bool swap_slots(int slot1, int slot2) = 0; + + // This function should do a 3-way swap, or shift of the slots. ``slot1`` + // should move to ``slot2``, which should be moved to ``slot3`` which in + // turn should be moved to ``slot1``. + // + // This is only used in compact mode. + // + // Returning ``true`` indicates an error occurred. + virtual bool swap_slots3(int slot1, int slot2, int slot3) = 0; + + // This function should release all the file handles that it keeps open to files + // belonging to this storage. The default implementation just calls + // ``file_pool::release_files(this)``. + // + // Returning ``true`` indicates an error occurred. + virtual bool release_files() = 0; + + // Rename file with index ``file`` to the thame ``new_name``. If there is an error, + // ``true`` should be returned. + virtual bool rename_file(int index, std::string const& new_filename) = 0; + + // This function should delete all files and directories belonging to + // this storage. + // + // Returning ``true`` indicates an error occurred. + // + // The ``disk_buffer_pool`` is used to allocate and free disk buffers. It + // has the following members:: + // + // struct disk_buffer_pool : boost::noncopyable + // { + // char* allocate_buffer(char const* category); + // void free_buffer(char* buf); + // + // char* allocate_buffers(int blocks, char const* category); + // void free_buffers(char* buf, int blocks); + // + // int block_size() const { return m_block_size; } + // + // void release_memory(); + // }; + virtual bool delete_files() = 0; + +#ifndef TORRENT_NO_DEPRECATE + // This function is called each time a file is completely downloaded. The + // storage implementation can perform last operations on a file. The file + // will not be opened for writing after this. + // + // ``index`` is the index of the file that completed. + // + // On windows the default storage implementation clears the sparse file + // flag on the specified file. + virtual void finalize_file(int) {} +#endif + + // access global disk_buffer_pool, for allocating and freeing disk buffers + disk_buffer_pool* disk_pool() { return m_disk_pool; } + + // access global session_settings + session_settings const& settings() const { return *m_settings; } + + // called by the storage implementation to set it into an + // error state. Typically whenever a critical file operation + // fails. + void set_error(std::string const& file, error_code const& ec) const; + + // returns the currently set error code and file path associated with it, + // if set. + error_code const& error() const { return m_error; } + std::string const& error_file() const { return m_error_file; } + + // reset the error state to allow continuing reading and writing + // to the storage + virtual void clear_error() { m_error = error_code(); m_error_file.resize(0); } + + // hidden + mutable error_code m_error; + mutable std::string m_error_file; + + // hidden + virtual ~storage_interface() {} + + // hidden + disk_buffer_pool* m_disk_pool; + session_settings* m_settings; + }; + + // The default implementation of storage_interface. Behaves as a normal + // bittorrent client. It is possible to derive from this class in order to + // override some of its behavior, when implementing a custom storage. + class TORRENT_EXPORT default_storage : public storage_interface, boost::noncopyable + { + public: + // constructs the default_storage based on the give file_storage (fs). + // ``mapped`` is an optional argument (it may be NULL). If non-NULL it + // represents the file mappsing that have been made to the torrent before + // adding it. That's where files are supposed to be saved and looked for + // on disk. ``save_path`` is the root save folder for this torrent. + // ``file_pool`` is the cache of file handles that the storage will use. + // All files it opens will ask the file_pool to open them. ``file_prio`` + // is a vector indicating the priority of files on startup. It may be + // an empty vector. Any file whose index is not represented by the vector + // (because the vector is too short) are assumed to have priority 1. + // this is used to treat files with priority 0 slightly differently. + default_storage(file_storage const& fs, file_storage const* mapped + , std::string const& path, file_pool& fp + , std::vector const& file_prio); + + // hidden + ~default_storage(); + + // hidden + void set_file_priority(std::vector const& prio); +#ifndef TORRENT_NO_DEPRECATE + void finalize_file(int file); +#endif + bool has_any_file(); + bool rename_file(int index, std::string const& new_filename); + bool release_files(); + bool delete_files(); + bool initialize(bool allocate_files); + int move_storage(std::string const& save_path, int flags); + int read(char* buf, int slot, int offset, int size); + int write(char const* buf, int slot, int offset, int size); + int sparse_end(int start) const; + void hint_read(int slot, int offset, int len); + int readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs, int flags = file::random_access); + int writev(file::iovec_t const* buf, int slot, int offset, int num_bufs, int flags = file::random_access); + size_type physical_offset(int slot, int offset); + bool move_slot(int src_slot, int dst_slot); + bool swap_slots(int slot1, int slot2); + bool swap_slots3(int slot1, int slot2, int slot3); + bool verify_resume_data(lazy_entry const& rd, error_code& error); + bool write_resume_data(entry& rd) const; + + // if the files in this storage are mapped, returns the mapped + // file_storage, otherwise returns the original file_storage object. + file_storage const& files() const { return m_mapped_files?*m_mapped_files:m_files; } + + private: + + // this identifies a read or write operation + // so that default_storage::readwritev() knows what to + // do when it's actually touching the file + struct fileop + { + size_type (file::*regular_op)(size_type file_offset + , file::iovec_t const* bufs, int num_bufs, error_code& ec); + size_type (default_storage::*unaligned_op)(boost::intrusive_ptr const& f + , size_type file_offset, file::iovec_t const* bufs, int num_bufs + , error_code& ec); + int cache_setting; + int mode; + }; + + void delete_one_file(std::string const& p); + int readwritev(file::iovec_t const* bufs, int slot, int offset + , int num_bufs, fileop const&); + + size_type read_unaligned(boost::intrusive_ptr const& file_handle + , size_type file_offset, file::iovec_t const* bufs, int num_bufs, error_code& ec); + size_type write_unaligned(boost::intrusive_ptr const& file_handle + , size_type file_offset, file::iovec_t const* bufs, int num_bufs, error_code& ec); + + boost::scoped_ptr m_mapped_files; + file_storage const& m_files; + + // helper function to open a file in the file pool with the right mode + boost::intrusive_ptr open_file(int file, int mode + , error_code& ec) const; + + std::vector m_file_priority; + std::string m_save_path; + // the file pool is typically stored in + // the session, to make all storage + // instances use the same pool + file_pool& m_pool; + + // this is a bitfield with one bit per file. A bit being set means + // we've written to that file previously. If we do write to a file + // whose bit is 0, we set the file size, to make the file allocated + // on disk (in full allocation mode) and just sparsely allocated in + // case of sparse allocation mode + bitfield m_file_created; + + int m_page_size; + bool m_allocate_files; + }; + + // this storage implementation does not write anything to disk + // and it pretends to read, and just leaves garbage in the buffers + // this is useful when simulating many clients on the same machine + // or when running stress tests and want to take the cost of the + // disk I/O out of the picture. This cannot be used for any kind + // of normal bittorrent operation, since it will just send garbage + // to peers and throw away all the data it downloads. It would end + // up being banned immediately + class disabled_storage : public storage_interface, boost::noncopyable + { + public: + disabled_storage(int piece_size) : m_piece_size(piece_size) {} + void set_file_priority(std::vector const&) {} + bool has_any_file() { return false; } + bool rename_file(int, std::string const&) { return false; } + bool release_files() { return false; } + bool delete_files() { return false; } + bool initialize(bool) { return false; } + int move_storage(std::string const&, int) { return 0; } + int read(char*, int, int, int size) { return size; } + int write(char const*, int, int, int size) { return size; } + size_type physical_offset(int, int) { return 0; } + int readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs, int flags = file::random_access); + int writev(file::iovec_t const* bufs, int slot, int offset, int num_bufs, int flags = file::random_access); + bool move_slot(int, int) { return false; } + bool swap_slots(int, int) { return false; } + bool swap_slots3(int, int, int) { return false; } + bool verify_resume_data(lazy_entry const&, error_code&) { return false; } + bool write_resume_data(entry&) const { return false; } + + int m_piece_size; + }; + + // flags for async_move_storage + enum move_flags_t + { + // replace any files in the destination when copying + // or moving the storage + always_replace_files, + + // if any files that we want to copy exist in the destination + // exist, fail the whole operation and don't perform + // any copy or move. There is an inherent race condition + // in this mode. The files are checked for existence before + // the operation starts. In between the check and performing + // the copy, the destination files may be created, in which + // case they are replaced. + fail_if_exist, + + // if any file exist in the target, take those files instead + // of the ones we may have in the source. + dont_replace + }; + + struct disk_io_thread; + + class TORRENT_EXTRA_EXPORT piece_manager + : public intrusive_ptr_base + , boost::noncopyable + { + friend class invariant_access; + friend struct disk_io_thread; + public: + + piece_manager( + boost::shared_ptr const& torrent + , boost::intrusive_ptr info + , std::string const& path + , file_pool& fp + , disk_io_thread& io + , storage_constructor_type sc + , storage_mode_t sm + , std::vector const& file_prio); + + ~piece_manager(); + + boost::intrusive_ptr info() const { return m_info; } + void write_resume_data(entry& rd) const; + + void async_check_fastresume(lazy_entry const* resume_data + , boost::function const& handler); + + void async_check_files(boost::function const& handler); + + void async_rename_file(int index, std::string const& name + , boost::function const& handler); + + void async_read( + peer_request const& r + , boost::function const& handler + , int cache_line_size = 0 + , int cache_expiry = 0); + + void async_read_and_hash( + peer_request const& r + , boost::function const& handler + , int cache_expiry = 0); + + void async_cache(int piece + , boost::function const& handler + , int cache_expiry = 0); + + // returns the write queue size + int async_write( + peer_request const& r + , disk_buffer_holder& buffer + , boost::function const& f); + + void async_hash(int piece, boost::function const& f); + + void async_release_files( + boost::function const& handler + = boost::function()); + + void abort_disk_io(); + + void async_clear_read_cache( + boost::function const& handler + = boost::function()); + + void async_delete_files( + boost::function const& handler + = boost::function()); + + void async_move_storage(std::string const& p, int flags + , boost::function const& handler); + + void async_set_file_priority( + std::vector const& prios + , boost::function const& handler); + + void async_save_resume_data( + boost::function const& handler); + + enum return_t + { + // return values from check_fastresume and check_files + no_error = 0, + need_full_check = -1, + fatal_disk_error = -2, + disk_check_aborted = -3, + file_exist = -4 + }; + + storage_interface* get_storage_impl() { return m_storage.get(); } + + private: + + std::string save_path() const; + + bool verify_resume_data(lazy_entry const& rd, error_code& e) + { return m_storage->verify_resume_data(rd, e); } + + bool is_allocating() const + { return m_state == state_expand_pieces; } + + void mark_failed(int index); + + error_code const& error() const { return m_storage->error(); } + std::string const& error_file() const { return m_storage->error_file(); } + int last_piece() const { return m_last_piece; } + void clear_error() { m_storage->clear_error(); } + + int slot_for(int piece) const; + int piece_for(int slot) const; + + // helper functions for check_dastresume + int check_no_fastresume(error_code& error); + int check_init_storage(error_code& error); + + // if error is set and return value is 'no_error' or 'need_full_check' + // the error message indicates that the fast resume data was rejected + // if 'fatal_disk_error' is returned, the error message indicates what + // when wrong in the disk access + int check_fastresume(lazy_entry const& rd, error_code& error); + + // this function returns true if the checking is complete + int check_files(int& current_slot, int& have_piece, error_code& error); + +#ifndef TORRENT_NO_DEPRECATE + bool compact_allocation() const + { return m_storage_mode == storage_mode_compact; } +#endif + +#ifdef TORRENT_DEBUG + std::string name() const { return m_info->name(); } +#endif + + bool allocate_slots_impl(int num_slots, mutex::scoped_lock& l, bool abort_on_disk = false); + + // updates the ph.h hasher object with the data at the given slot + // and optionally a 'small hash' as well, the hash for + // the partial slot. Returns the number of bytes read + int hash_for_slot(int slot, partial_hash& h, int piece_size + , int small_piece_size = 0, sha1_hash* small_hash = 0); + + void hint_read_impl(int piece_index, int offset, int size); + + int read_impl( + file::iovec_t* bufs + , int piece_index + , int offset + , int num_bufs); + + int write_impl( + file::iovec_t* bufs + , int piece_index + , int offset + , int num_bufs); + + size_type physical_offset(int piece_index, int offset); + + // returns the number of pieces left in the + // file currently being checked + int skip_file() const; + // -1=error 0=ok >0=skip this many pieces + int check_one_piece(int& have_piece); + int identify_data( + sha1_hash const& large_hash + , sha1_hash const& small_hash + , int current_slot); + + void switch_to_full_mode(); + sha1_hash hash_for_piece_impl(int piece, int* readback = 0); + + int release_files_impl() { return m_storage->release_files(); } + int delete_files_impl() { return m_storage->delete_files(); } + int rename_file_impl(int index, std::string const& new_filename) + { return m_storage->rename_file(index, new_filename); } + void set_file_priority_impl(std::vector const& p) + { m_storage->set_file_priority(p); } + + int move_storage_impl(std::string const& save_path, int flags); + + int allocate_slot_for_piece(int piece_index); +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif +#ifdef TORRENT_STORAGE_DEBUG + void debug_log() const; +#endif + boost::intrusive_ptr m_info; + file_storage const& m_files; + + boost::scoped_ptr m_storage; + + storage_mode_t m_storage_mode; + + // slots that haven't had any file storage allocated + std::vector m_unallocated_slots; + // slots that have file storage, but isn't assigned to a piece + std::vector m_free_slots; + + enum + { + has_no_slot = -3 // the piece has no storage + }; + + // maps piece indices to slots. If a piece doesn't + // have any storage, it is set to 'has_no_slot' + std::vector m_piece_to_slot; + + enum + { + unallocated = -1, // the slot is unallocated + unassigned = -2 // the slot is allocated but not assigned to a piece + }; + + // maps slots to piece indices, if a slot doesn't have a piece + // it can either be 'unassigned' or 'unallocated' + std::vector m_slot_to_piece; + + std::string m_save_path; + + mutable mutex m_mutex; + + enum { + // the default initial state + state_none, + // the file checking is complete + state_finished, + // checking the files + state_full_check, + // move pieces to their final position + state_expand_pieces + } m_state; + int m_current_slot; + // used during check. If any piece is found + // that is not in its final position, this + // is set to true + bool m_out_of_place; + // used to move pieces while expanding + // the storage from compact allocation + // to full allocation + aligned_holder m_scratch_buffer; + aligned_holder m_scratch_buffer2; + // the piece that is in the scratch buffer + int m_scratch_piece; + + // the last piece we wrote to or read from + int m_last_piece; + + // this is saved in case we need to instantiate a new + // storage (osed when remapping files) + storage_constructor_type m_storage_constructor; + + // this maps a piece hash to piece index. It will be + // build the first time it is used (to save time if it + // isn't needed) + std::multimap m_hash_to_piece; + + // this map contains partial hashes for downloading + // pieces. This is only accessed from within the + // disk-io thread. + std::map m_piece_hasher; + + disk_io_thread& m_io_thread; + + // the reason for this to be a void pointer + // is to avoid creating a dependency on the + // torrent. This shared_ptr is here only + // to keep the torrent object alive until + // the piece_manager destructs. This is because + // the torrent_info object is owned by the torrent. + boost::shared_ptr m_torrent; + }; + +} + +#endif // TORRENT_STORAGE_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/storage_defs.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/storage_defs.hpp new file mode 100644 index 0000000000..330b065106 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/storage_defs.hpp @@ -0,0 +1,84 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_STORAGE_DEFS_HPP_INCLUDE +#define TORRENT_STORAGE_DEFS_HPP_INCLUDE + +#include "libtorrent/config.hpp" +#include +#include + +namespace libtorrent +{ + struct storage_interface; + class file_storage; + struct file_pool; + + // types of storage allocation used for add_torrent_params::storage_mode. + enum storage_mode_t + { + // All pieces will be written to their final position, all files will be + // allocated in full when the torrent is first started. This is done with + // ``fallocate()`` and similar calls. This mode minimizes fragmentation. + storage_mode_allocate, + + // All pieces will be written to the place where they belong and sparse files + // will be used. This is the recommended, and default mode. + storage_mode_sparse, + + // internal + internal_storage_mode_compact_deprecated, +#ifndef TORRENT_NO_DEPRECATE + storage_mode_compact = internal_storage_mode_compact_deprecated +#endif + }; + + typedef boost::function const&)> storage_constructor_type; + + // the constructor function for the regular file storage. This is the + // default value for add_torrent_params::storage. + TORRENT_EXPORT storage_interface* default_storage_constructor( + file_storage const&, file_storage const* mapped, std::string const&, file_pool& + , std::vector const&); + + // the constructor function for the disabled storage. This can be used for + // testing and benchmarking. It will throw away any data written to + // it and return garbage for anything read from it. + TORRENT_EXPORT storage_interface* disabled_storage_constructor( + file_storage const&, file_storage const* mapped, std::string const&, file_pool& + , std::vector const&); + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/string_util.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/string_util.hpp new file mode 100644 index 0000000000..9113410bb0 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/string_util.hpp @@ -0,0 +1,74 @@ +/* + +Copyright (c) 2012-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_STRING_UTIL_HPP_INCLUDED +#define TORRENT_STRING_UTIL_HPP_INCLUDED + +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + TORRENT_EXTRA_EXPORT bool is_alpha(char c); + // this is used by bdecode_recursive's header file + TORRENT_EXPORT bool is_digit(char c); + TORRENT_EXTRA_EXPORT bool is_print(char c); + TORRENT_EXTRA_EXPORT bool is_space(char c); + TORRENT_EXTRA_EXPORT char to_lower(char c); + + TORRENT_EXTRA_EXPORT int split_string(char const** tags, int buf_size, char* in); + TORRENT_EXTRA_EXPORT bool string_begins_no_case(char const* s1, char const* s2); + TORRENT_EXTRA_EXPORT bool string_equal_no_case(char const* s1, char const* s2); + + TORRENT_EXTRA_EXPORT void url_random(char* begin, char* end); + + // strdup is not part of the C standard. Some systems + // don't have it and it won't be available when building + // in strict ansi mode + char* allocate_string_copy(char const* str); + + // returns p + x such that the pointer is 8 bytes aligned + // x cannot be greater than 7 + void* align_pointer(void* p); + + // searches for separator in the string 'last'. the pointer last points to + // is set to point to the first character following the separator. + // returns a pointer to a null terminated string starting at last, ending + // at the separator (the string is mutated to replace the separator with + // a '\0' character). If there is no separator, but the end of the string, + // the pointer next points to is set to the last null terminator, which will + // make the following invocation return NULL, to indicate the end of the + // string. + TORRENT_EXTRA_EXPORT char* string_tokenize(char* last, char sep, char** next); +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/thread.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/thread.hpp new file mode 100644 index 0000000000..70a2b85dd6 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/thread.hpp @@ -0,0 +1,89 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_THREAD_HPP_INCLUDED +#define TORRENT_THREAD_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include "libtorrent/time.hpp" + +#if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN +// asio assumes that the windows error codes are defined already +#include +#endif + +#if defined TORRENT_BEOS +#include +#endif + +#include // for auto_ptr required by asio + +#include +#include +#include + +namespace libtorrent +{ + typedef boost::asio::detail::thread thread; + typedef boost::asio::detail::mutex mutex; + typedef boost::asio::detail::event event; + + // pauses the calling thread at least for the specified + // number of milliseconds + TORRENT_EXPORT void sleep(int milliseconds); + + struct TORRENT_EXTRA_EXPORT condition_variable + { + condition_variable(); + ~condition_variable(); + void wait(mutex::scoped_lock& l); + void wait_for(mutex::scoped_lock& l, time_duration rel_time); + void notify_all(); + private: +#ifdef BOOST_HAS_PTHREADS + pthread_cond_t m_cond; +#elif defined TORRENT_WINDOWS || defined TORRENT_CYGWIN + HANDLE m_sem; + mutex m_mutex; + int m_num_waiters; +#elif defined TORRENT_BEOS + sem_id m_sem; + mutex m_mutex; + int m_num_waiters; +#else +#error not implemented +#endif + }; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/time.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/time.hpp new file mode 100644 index 0000000000..1b3382cb92 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/time.hpp @@ -0,0 +1,129 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_TIME_HPP_INCLUDED +#define TORRENT_TIME_HPP_INCLUDED + +#include +#include "libtorrent/config.hpp" +#include "libtorrent/ptime.hpp" +#include +#include + +// OVERVIEW +// +// This section contains fundamental time types used internally by +// libtorrent and exposed through various places in the API. The two +// basic types are ``ptime`` and ``time_duration``. The first represents +// a point in time and the second the difference between two points +// in time. +// +// The internal representation of these types is implementation defined +// and they can only be constructed via one of the construction functions +// that take a well defined time unit (seconds, minutes, etc.). They can +// only be turned into well defined time units by the accessor functions +// (total_microseconds(), etc.). +// +// .. note:: +// In a future version of libtorrent, these types will be replaced +// by the standard timer types from ``std::chrono``. +// + +namespace libtorrent +{ + TORRENT_EXTRA_EXPORT char const* time_now_string(); + std::string log_time(); + + // returns the current time, as represented by ptime. The + // resolution of this timer is about 100 ms. + TORRENT_EXPORT ptime const& time_now(); + + // returns the current time as represented by ptime. This is + // more expensive than time_now(), but provides as high resolution + // as the operating system can provide. + TORRENT_EXPORT ptime time_now_hires(); + + // the earliest and latest possible time points + // representable by ptime. + TORRENT_EXPORT ptime min_time(); + TORRENT_EXPORT ptime max_time(); + +#if defined TORRENT_USE_BOOST_DATE_TIME || defined TORRENT_USE_QUERY_PERFORMANCE_TIMER + + // returns a time_duration representing the specified number of seconds, milliseconds + // microseconds, minutes and hours. + TORRENT_EXPORT time_duration seconds(boost::int64_t s); + TORRENT_EXPORT time_duration milliseconds(boost::int64_t s); + TORRENT_EXPORT time_duration microsec(boost::int64_t s); + TORRENT_EXPORT time_duration minutes(boost::int64_t s); + TORRENT_EXPORT time_duration hours(boost::int64_t s); + + // returns the number of seconds, milliseconds and microseconds + // a time_duration represents. + TORRENT_EXPORT boost::int64_t total_seconds(time_duration td); + TORRENT_EXPORT boost::int64_t total_milliseconds(time_duration td); + TORRENT_EXPORT boost::int64_t total_microseconds(time_duration td); + +#elif TORRENT_USE_CLOCK_GETTIME || TORRENT_USE_SYSTEM_TIME || TORRENT_USE_ABSOLUTE_TIME + + // hidden + inline int total_seconds(time_duration td) + { return td.diff / 1000000; } + // hidden + inline int total_milliseconds(time_duration td) + { return td.diff / 1000; } + // hidden + inline boost::int64_t total_microseconds(time_duration td) + { return td.diff; } + + // hidden + inline time_duration microsec(boost::int64_t s) + { return time_duration(s); } + // hidden + inline time_duration milliseconds(boost::int64_t s) + { return time_duration(s * 1000); } + // hidden + inline time_duration seconds(boost::int64_t s) + { return time_duration(s * 1000000); } + // hidden + inline time_duration minutes(boost::int64_t s) + { return time_duration(s * 1000000 * 60); } + // hidden + inline time_duration hours(boost::int64_t s) + { return time_duration(s * 1000000 * 60 * 60); } + +#endif // TORRENT_USE_CLOCK_GETTIME + +} + +#endif // TORRENT_TIME_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/timestamp_history.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/timestamp_history.hpp new file mode 100644 index 0000000000..f40583925a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/timestamp_history.hpp @@ -0,0 +1,81 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TIMESTAMP_HISTORY_HPP +#define TIMESTAMP_HISTORY_HPP + +#include "boost/cstdint.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" + +namespace libtorrent { + +// timestamp history keeps a history of the lowest timestamps we've +// seen in the last 20 minutes +struct TORRENT_EXTRA_EXPORT timestamp_history +{ + enum { history_size = 20 }; + + timestamp_history() : m_index(0), m_initialized(false), m_base(0), m_num_samples(0) {} + bool initialized() const { return m_initialized; } + + // add a sample to the timestamp history. If step is true, it's been + // a minute since the last step + boost::uint32_t add_sample(boost::uint32_t sample, bool step); + boost::uint32_t base() const { TORRENT_ASSERT(m_initialized); return m_base; } + void adjust_base(int change); + +private: + + // this is a circular buffer + boost::uint32_t m_history[history_size]; + + // and this is the index we're currently at + // in the circular buffer + boost::uint16_t m_index; + + bool m_initialized:1; + + // this is the lowest sample seen in the + // last 'history_size' minutes + boost::uint32_t m_base; + + // this is the number of samples since the + // last time we stepped one minute. If we + // don't have enough samples, we won't step + int m_num_samples; +}; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/tommath.h b/apps/Launcher/ext/libtorrent/include/libtorrent/tommath.h new file mode 100644 index 0000000000..1accb00111 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/tommath.h @@ -0,0 +1,584 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ +#ifndef BN_H_ +#define BN_H_ + +#include +#include +#include +#include +#include + +#include "libtorrent/tommath_class.h" + +#ifndef MIN + #define MIN(x,y) ((x)<(y)?(x):(y)) +#endif + +#ifndef MAX + #define MAX(x,y) ((x)>(y)?(x):(y)) +#endif + +#ifdef __cplusplus +extern "C" { + +/* C++ compilers don't like assigning void * to mp_digit * */ +#define OPT_CAST(x) (x *) + +#else + +/* C on the other hand doesn't care */ +#define OPT_CAST(x) + +#endif + + +/* detect 64-bit mode if possible */ +#if defined(__x86_64__) + #if !(defined(MP_64BIT) && defined(MP_16BIT) && defined(MP_8BIT)) + #define MP_64BIT + #endif +#endif + +/* some default configurations. + * + * A "mp_digit" must be able to hold DIGIT_BIT + 1 bits + * A "mp_word" must be able to hold 2*DIGIT_BIT + 1 bits + * + * At the very least a mp_digit must be able to hold 7 bits + * [any size beyond that is ok provided it doesn't overflow the data type] + */ +#ifdef MP_8BIT + typedef unsigned char mp_digit; + typedef unsigned short mp_word; +#elif defined(MP_16BIT) + typedef unsigned short mp_digit; + typedef unsigned long mp_word; +#elif defined(MP_64BIT) + /* for GCC only on supported platforms */ +#ifndef CRYPT + typedef unsigned long long ulong64; + typedef signed long long long64; +#endif + + typedef unsigned long mp_digit; + typedef unsigned long mp_word __attribute__ ((mode(TI))); + + #define DIGIT_BIT 60 +#else + /* this is the default case, 28-bit digits */ + + /* this is to make porting into LibTomCrypt easier :-) */ +#ifndef CRYPT + #if defined(_MSC_VER) || defined(__BORLANDC__) + typedef unsigned __int64 ulong64; + typedef signed __int64 long64; + #else + typedef unsigned long long ulong64; + typedef signed long long long64; + #endif +#endif + + typedef unsigned long mp_digit; + typedef ulong64 mp_word; + +#ifdef MP_31BIT + /* this is an extension that uses 31-bit digits */ + #define DIGIT_BIT 31 +#else + /* default case is 28-bit digits, defines MP_28BIT as a handy macro to test */ + #define DIGIT_BIT 28 + #define MP_28BIT +#endif +#endif + +/* define heap macros */ +#ifndef CRYPT + /* default to libc stuff */ + #ifndef XMALLOC + #define XMALLOC malloc + #define XFREE free + #define XREALLOC realloc + #define XCALLOC calloc + #else + /* prototypes for our heap functions */ + extern void *XMALLOC(size_t n); + extern void *XREALLOC(void *p, size_t n); + extern void *XCALLOC(size_t n, size_t s); + extern void XFREE(void *p); + #endif +#endif + + +/* otherwise the bits per digit is calculated automatically from the size of a mp_digit */ +#ifndef DIGIT_BIT + #define DIGIT_BIT ((int)((CHAR_BIT * sizeof(mp_digit) - 1))) /* bits per digit */ +#endif + +#define MP_DIGIT_BIT DIGIT_BIT +#define MP_MASK ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1)) +#define MP_DIGIT_MAX MP_MASK + +/* equalities */ +#define MP_LT -1 /* less than */ +#define MP_EQ 0 /* equal to */ +#define MP_GT 1 /* greater than */ + +#define MP_ZPOS 0 /* positive integer */ +#define MP_NEG 1 /* negative */ + +#define MP_OKAY 0 /* ok result */ +#define MP_MEM -2 /* out of mem */ +#define MP_VAL -3 /* invalid input */ +#define MP_RANGE MP_VAL + +#define MP_YES 1 /* yes response */ +#define MP_NO 0 /* no response */ + +/* Primality generation flags */ +#define LTM_PRIME_BBS 0x0001 /* BBS style prime */ +#define LTM_PRIME_SAFE 0x0002 /* Safe prime (p-1)/2 == prime */ +#define LTM_PRIME_2MSB_ON 0x0008 /* force 2nd MSB to 1 */ + +typedef int mp_err; + +/* you'll have to tune these... */ +extern int KARATSUBA_MUL_CUTOFF, + KARATSUBA_SQR_CUTOFF, + TOOM_MUL_CUTOFF, + TOOM_SQR_CUTOFF; + +/* define this to use lower memory usage routines (exptmods mostly) */ +/* #define MP_LOW_MEM */ + +/* default precision */ +#ifndef MP_PREC + #ifndef MP_LOW_MEM + #define MP_PREC 32 /* default digits of precision */ + #else + #define MP_PREC 8 /* default digits of precision */ + #endif +#endif + +/* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */ +#define MP_WARRAY (1 << (sizeof(mp_word) * CHAR_BIT - 2 * DIGIT_BIT + 1)) + +/* the infamous mp_int structure */ +typedef struct { + int used, alloc, sign; + mp_digit *dp; +} mp_int; + +/* callback for mp_prime_random, should fill dst with random bytes and return how many read [upto len] */ +typedef int ltm_prime_callback(unsigned char *dst, int len, void *dat); + + +#define USED(m) ((m)->used) +#define DIGIT(m,k) ((m)->dp[(k)]) +#define SIGN(m) ((m)->sign) + +/* error code to char* string */ +char *mp_error_to_string(int code); + +/* ---> init and deinit bignum functions <--- */ +/* init a bignum */ +int mp_init(mp_int *a); + +/* free a bignum */ +void mp_clear(mp_int *a); + +/* init a null terminated series of arguments */ +int mp_init_multi(mp_int *mp, ...); + +/* clear a null terminated series of arguments */ +void mp_clear_multi(mp_int *mp, ...); + +/* exchange two ints */ +void mp_exch(mp_int *a, mp_int *b); + +/* shrink ram required for a bignum */ +int mp_shrink(mp_int *a); + +/* grow an int to a given size */ +int mp_grow(mp_int *a, int size); + +/* init to a given number of digits */ +int mp_init_size(mp_int *a, int size); + +/* ---> Basic Manipulations <--- */ +#define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO) +#define mp_iseven(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 0)) ? MP_YES : MP_NO) +#define mp_isodd(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 1)) ? MP_YES : MP_NO) + +/* set to zero */ +void mp_zero(mp_int *a); + +/* set to a digit */ +void mp_set(mp_int *a, mp_digit b); + +/* set a 32-bit const */ +int mp_set_int(mp_int *a, unsigned long b); + +/* get a 32-bit value */ +unsigned long mp_get_int(mp_int * a); + +/* initialize and set a digit */ +int mp_init_set (mp_int * a, mp_digit b); + +/* initialize and set 32-bit value */ +int mp_init_set_int (mp_int * a, unsigned long b); + +/* copy, b = a */ +int mp_copy(mp_int *a, mp_int *b); + +/* inits and copies, a = b */ +int mp_init_copy(mp_int *a, mp_int *b); + +/* trim unused digits */ +void mp_clamp(mp_int *a); + +/* ---> digit manipulation <--- */ + +/* right shift by "b" digits */ +void mp_rshd(mp_int *a, int b); + +/* left shift by "b" digits */ +int mp_lshd(mp_int *a, int b); + +/* c = a / 2**b */ +int mp_div_2d(mp_int *a, int b, mp_int *c, mp_int *d); + +/* b = a/2 */ +int mp_div_2(mp_int *a, mp_int *b); + +/* c = a * 2**b */ +int mp_mul_2d(mp_int *a, int b, mp_int *c); + +/* b = a*2 */ +int mp_mul_2(mp_int *a, mp_int *b); + +/* c = a mod 2**d */ +int mp_mod_2d(mp_int *a, int b, mp_int *c); + +/* computes a = 2**b */ +int mp_2expt(mp_int *a, int b); + +/* Counts the number of lsbs which are zero before the first zero bit */ +int mp_cnt_lsb(mp_int *a); + +/* I Love Earth! */ + +/* makes a pseudo-random int of a given size */ +int mp_rand(mp_int *a, int digits); + +/* ---> binary operations <--- */ +/* c = a XOR b */ +int mp_xor(mp_int *a, mp_int *b, mp_int *c); + +/* c = a OR b */ +int mp_or(mp_int *a, mp_int *b, mp_int *c); + +/* c = a AND b */ +int mp_and(mp_int *a, mp_int *b, mp_int *c); + +/* ---> Basic arithmetic <--- */ + +/* b = -a */ +int mp_neg(mp_int *a, mp_int *b); + +/* b = |a| */ +int mp_abs(mp_int *a, mp_int *b); + +/* compare a to b */ +int mp_cmp(mp_int *a, mp_int *b); + +/* compare |a| to |b| */ +int mp_cmp_mag(mp_int *a, mp_int *b); + +/* c = a + b */ +int mp_add(mp_int *a, mp_int *b, mp_int *c); + +/* c = a - b */ +int mp_sub(mp_int *a, mp_int *b, mp_int *c); + +/* c = a * b */ +int mp_mul(mp_int *a, mp_int *b, mp_int *c); + +/* b = a*a */ +int mp_sqr(mp_int *a, mp_int *b); + +/* a/b => cb + d == a */ +int mp_div(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* c = a mod b, 0 <= c < b */ +int mp_mod(mp_int *a, mp_int *b, mp_int *c); + +/* ---> single digit functions <--- */ + +/* compare against a single digit */ +int mp_cmp_d(mp_int *a, mp_digit b); + +/* c = a + b */ +int mp_add_d(mp_int *a, mp_digit b, mp_int *c); + +/* c = a - b */ +int mp_sub_d(mp_int *a, mp_digit b, mp_int *c); + +/* c = a * b */ +int mp_mul_d(mp_int *a, mp_digit b, mp_int *c); + +/* a/b => cb + d == a */ +int mp_div_d(mp_int *a, mp_digit b, mp_int *c, mp_digit *d); + +/* a/3 => 3c + d == a */ +int mp_div_3(mp_int *a, mp_int *c, mp_digit *d); + +/* c = a**b */ +int mp_expt_d(mp_int *a, mp_digit b, mp_int *c); + +/* c = a mod b, 0 <= c < b */ +int mp_mod_d(mp_int *a, mp_digit b, mp_digit *c); + +/* ---> number theory <--- */ + +/* d = a + b (mod c) */ +int mp_addmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* d = a - b (mod c) */ +int mp_submod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* d = a * b (mod c) */ +int mp_mulmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* c = a * a (mod b) */ +int mp_sqrmod(mp_int *a, mp_int *b, mp_int *c); + +/* c = 1/a (mod b) */ +int mp_invmod(mp_int *a, mp_int *b, mp_int *c); + +/* c = (a, b) */ +int mp_gcd(mp_int *a, mp_int *b, mp_int *c); + +/* produces value such that U1*a + U2*b = U3 */ +int mp_exteuclid(mp_int *a, mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3); + +/* c = [a, b] or (a*b)/(a, b) */ +int mp_lcm(mp_int *a, mp_int *b, mp_int *c); + +/* finds one of the b'th root of a, such that |c|**b <= |a| + * + * returns error if a < 0 and b is even + */ +int mp_n_root(mp_int *a, mp_digit b, mp_int *c); + +/* special sqrt algo */ +int mp_sqrt(mp_int *arg, mp_int *ret); + +/* is number a square? */ +int mp_is_square(mp_int *arg, int *ret); + +/* computes the jacobi c = (a | n) (or Legendre if b is prime) */ +int mp_jacobi(mp_int *a, mp_int *n, int *c); + +/* used to setup the Barrett reduction for a given modulus b */ +int mp_reduce_setup(mp_int *a, mp_int *b); + +/* Barrett Reduction, computes a (mod b) with a precomputed value c + * + * Assumes that 0 < a <= b*b, note if 0 > a > -(b*b) then you can merely + * compute the reduction as -1 * mp_reduce(mp_abs(a)) [pseudo code]. + */ +int mp_reduce(mp_int *a, mp_int *b, mp_int *c); + +/* setups the montgomery reduction */ +int mp_montgomery_setup(mp_int *a, mp_digit *mp); + +/* computes a = B**n mod b without division or multiplication useful for + * normalizing numbers in a Montgomery system. + */ +int mp_montgomery_calc_normalization(mp_int *a, mp_int *b); + +/* computes x/R == x (mod N) via Montgomery Reduction */ +int mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp); + +/* returns 1 if a is a valid DR modulus */ +int mp_dr_is_modulus(mp_int *a); + +/* sets the value of "d" required for mp_dr_reduce */ +void mp_dr_setup(mp_int *a, mp_digit *d); + +/* reduces a modulo b using the Diminished Radix method */ +int mp_dr_reduce(mp_int *a, mp_int *b, mp_digit mp); + +/* returns true if a can be reduced with mp_reduce_2k */ +int mp_reduce_is_2k(mp_int *a); + +/* determines k value for 2k reduction */ +int mp_reduce_2k_setup(mp_int *a, mp_digit *d); + +/* reduces a modulo b where b is of the form 2**p - k [0 <= a] */ +int mp_reduce_2k(mp_int *a, mp_int *n, mp_digit d); + +/* returns true if a can be reduced with mp_reduce_2k_l */ +int mp_reduce_is_2k_l(mp_int *a); + +/* determines k value for 2k reduction */ +int mp_reduce_2k_setup_l(mp_int *a, mp_int *d); + +/* reduces a modulo b where b is of the form 2**p - k [0 <= a] */ +int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d); + +/* d = a**b (mod c) */ +int mp_exptmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* ---> Primes <--- */ + +/* number of primes */ +#ifdef MP_8BIT + #define PRIME_SIZE 31 +#else + #define PRIME_SIZE 256 +#endif + +/* table of first PRIME_SIZE primes */ +extern const mp_digit ltm_prime_tab[]; + +/* result=1 if a is divisible by one of the first PRIME_SIZE primes */ +int mp_prime_is_divisible(mp_int *a, int *result); + +/* performs one Fermat test of "a" using base "b". + * Sets result to 0 if composite or 1 if probable prime + */ +int mp_prime_fermat(mp_int *a, mp_int *b, int *result); + +/* performs one Miller-Rabin test of "a" using base "b". + * Sets result to 0 if composite or 1 if probable prime + */ +int mp_prime_miller_rabin(mp_int *a, mp_int *b, int *result); + +/* This gives [for a given bit size] the number of trials required + * such that Miller-Rabin gives a prob of failure lower than 2^-96 + */ +int mp_prime_rabin_miller_trials(int size); + +/* performs t rounds of Miller-Rabin on "a" using the first + * t prime bases. Also performs an initial sieve of trial + * division. Determines if "a" is prime with probability + * of error no more than (1/4)**t. + * + * Sets result to 1 if probably prime, 0 otherwise + */ +int mp_prime_is_prime(mp_int *a, int t, int *result); + +/* finds the next prime after the number "a" using "t" trials + * of Miller-Rabin. + * + * bbs_style = 1 means the prime must be congruent to 3 mod 4 + */ +int mp_prime_next_prime(mp_int *a, int t, int bbs_style); + +/* makes a truly random prime of a given size (bytes), + * call with bbs = 1 if you want it to be congruent to 3 mod 4 + * + * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can + * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself + * so it can be NULL + * + * The prime generated will be larger than 2^(8*size). + */ +#define mp_prime_random(a, t, size, bbs, cb, dat) mp_prime_random_ex(a, t, ((size) * 8) + 1, (bbs==1)?LTM_PRIME_BBS:0, cb, dat) + +/* makes a truly random prime of a given size (bits), + * + * Flags are as follows: + * + * LTM_PRIME_BBS - make prime congruent to 3 mod 4 + * LTM_PRIME_SAFE - make sure (p-1)/2 is prime as well (implies LTM_PRIME_BBS) + * LTM_PRIME_2MSB_OFF - make the 2nd highest bit zero + * LTM_PRIME_2MSB_ON - make the 2nd highest bit one + * + * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can + * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself + * so it can be NULL + * + */ +int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat); + +/* ---> radix conversion <--- */ +int mp_count_bits(mp_int *a); + +int mp_unsigned_bin_size(mp_int *a); +int mp_read_unsigned_bin(mp_int *a, const unsigned char *b, int c); +int mp_to_unsigned_bin(mp_int *a, unsigned char *b); +int mp_to_unsigned_bin_n (mp_int * a, unsigned char *b, unsigned long *outlen); + +int mp_signed_bin_size(mp_int *a); +int mp_read_signed_bin(mp_int *a, const unsigned char *b, int c); +int mp_to_signed_bin(mp_int *a, unsigned char *b); +int mp_to_signed_bin_n (mp_int * a, unsigned char *b, unsigned long *outlen); + +int mp_read_radix(mp_int *a, const char *str, int radix); +int mp_toradix(mp_int *a, char *str, int radix); +int mp_toradix_n(mp_int * a, char *str, int radix, int maxlen); +int mp_radix_size(mp_int *a, int radix, int *size); + +int mp_fread(mp_int *a, int radix, FILE *stream); +int mp_fwrite(mp_int *a, int radix, FILE *stream); + +#define mp_read_raw(mp, str, len) mp_read_signed_bin((mp), (str), (len)) +#define mp_raw_size(mp) mp_signed_bin_size(mp) +#define mp_toraw(mp, str) mp_to_signed_bin((mp), (str)) +#define mp_read_mag(mp, str, len) mp_read_unsigned_bin((mp), (str), (len)) +#define mp_mag_size(mp) mp_unsigned_bin_size(mp) +#define mp_tomag(mp, str) mp_to_unsigned_bin((mp), (str)) + +#define mp_tobinary(M, S) mp_toradix((M), (S), 2) +#define mp_tooctal(M, S) mp_toradix((M), (S), 8) +#define mp_todecimal(M, S) mp_toradix((M), (S), 10) +#define mp_tohex(M, S) mp_toradix((M), (S), 16) + +/* lowlevel functions, do not call! */ +int s_mp_add(mp_int *a, mp_int *b, mp_int *c); +int s_mp_sub(mp_int *a, mp_int *b, mp_int *c); +#define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1) +int fast_s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int fast_s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int fast_s_mp_sqr(mp_int *a, mp_int *b); +int s_mp_sqr(mp_int *a, mp_int *b); +int mp_karatsuba_mul(mp_int *a, mp_int *b, mp_int *c); +int mp_toom_mul(mp_int *a, mp_int *b, mp_int *c); +int mp_karatsuba_sqr(mp_int *a, mp_int *b); +int mp_toom_sqr(mp_int *a, mp_int *b); +int fast_mp_invmod(mp_int *a, mp_int *b, mp_int *c); +int mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c); +int fast_mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp); +int mp_exptmod_fast(mp_int *G, mp_int *X, mp_int *P, mp_int *Y, int mode); +int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int mode); +void bn_reverse(unsigned char *s, int len); + +extern const char *mp_s_rmap; + +#ifdef __cplusplus + } +#endif + +#endif + + +/* $Source: /cvs/libtom/libtommath/tommath.h,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/tommath_class.h b/apps/Launcher/ext/libtorrent/include/libtorrent/tommath_class.h new file mode 100644 index 0000000000..af50ecf061 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/tommath_class.h @@ -0,0 +1,1000 @@ +#if !(defined(LTM1) && defined(LTM2) && defined(LTM3)) +#if defined(LTM2) +#define LTM3 +#endif +#if defined(LTM1) +#define LTM2 +#endif +#define LTM1 + +#if defined(LTM_ALL) +#define BN_ERROR_C +#define BN_FAST_MP_INVMOD_C +#define BN_FAST_MP_MONTGOMERY_REDUCE_C +#define BN_FAST_S_MP_MUL_DIGS_C +#define BN_FAST_S_MP_MUL_HIGH_DIGS_C +#define BN_FAST_S_MP_SQR_C +#define BN_MP_2EXPT_C +#define BN_MP_ABS_C +#define BN_MP_ADD_C +#define BN_MP_ADD_D_C +#define BN_MP_ADDMOD_C +#define BN_MP_AND_C +#define BN_MP_CLAMP_C +#define BN_MP_CLEAR_C +#define BN_MP_CLEAR_MULTI_C +#define BN_MP_CMP_C +#define BN_MP_CMP_D_C +#define BN_MP_CMP_MAG_C +#define BN_MP_CNT_LSB_C +#define BN_MP_COPY_C +#define BN_MP_COUNT_BITS_C +#define BN_MP_DIV_C +#define BN_MP_DIV_2_C +#define BN_MP_DIV_2D_C +#define BN_MP_DIV_3_C +#define BN_MP_DIV_D_C +#define BN_MP_DR_IS_MODULUS_C +#define BN_MP_DR_REDUCE_C +#define BN_MP_DR_SETUP_C +#define BN_MP_EXCH_C +#define BN_MP_EXPT_D_C +#define BN_MP_EXPTMOD_C +#define BN_MP_EXPTMOD_FAST_C +#define BN_MP_EXTEUCLID_C +#define BN_MP_FREAD_C +#define BN_MP_FWRITE_C +#define BN_MP_GCD_C +#define BN_MP_GET_INT_C +#define BN_MP_GROW_C +#define BN_MP_INIT_C +#define BN_MP_INIT_COPY_C +#define BN_MP_INIT_MULTI_C +#define BN_MP_INIT_SET_C +#define BN_MP_INIT_SET_INT_C +#define BN_MP_INIT_SIZE_C +#define BN_MP_INVMOD_C +#define BN_MP_INVMOD_SLOW_C +#define BN_MP_IS_SQUARE_C +#define BN_MP_JACOBI_C +#define BN_MP_KARATSUBA_MUL_C +#define BN_MP_KARATSUBA_SQR_C +#define BN_MP_LCM_C +#define BN_MP_LSHD_C +#define BN_MP_MOD_C +#define BN_MP_MOD_2D_C +#define BN_MP_MOD_D_C +#define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C +#define BN_MP_MONTGOMERY_REDUCE_C +#define BN_MP_MONTGOMERY_SETUP_C +#define BN_MP_MUL_C +#define BN_MP_MUL_2_C +#define BN_MP_MUL_2D_C +#define BN_MP_MUL_D_C +#define BN_MP_MULMOD_C +#define BN_MP_N_ROOT_C +#define BN_MP_NEG_C +#define BN_MP_OR_C +#define BN_MP_PRIME_FERMAT_C +#define BN_MP_PRIME_IS_DIVISIBLE_C +#define BN_MP_PRIME_IS_PRIME_C +#define BN_MP_PRIME_MILLER_RABIN_C +#define BN_MP_PRIME_NEXT_PRIME_C +#define BN_MP_PRIME_RABIN_MILLER_TRIALS_C +#define BN_MP_PRIME_RANDOM_EX_C +#define BN_MP_RADIX_SIZE_C +#define BN_MP_RADIX_SMAP_C +#define BN_MP_RAND_C +#define BN_MP_READ_RADIX_C +#define BN_MP_READ_SIGNED_BIN_C +#define BN_MP_READ_UNSIGNED_BIN_C +#define BN_MP_REDUCE_C +#define BN_MP_REDUCE_2K_C +#define BN_MP_REDUCE_2K_L_C +#define BN_MP_REDUCE_2K_SETUP_C +#define BN_MP_REDUCE_2K_SETUP_L_C +#define BN_MP_REDUCE_IS_2K_C +#define BN_MP_REDUCE_IS_2K_L_C +#define BN_MP_REDUCE_SETUP_C +#define BN_MP_RSHD_C +#define BN_MP_SET_C +#define BN_MP_SET_INT_C +#define BN_MP_SHRINK_C +#define BN_MP_SIGNED_BIN_SIZE_C +#define BN_MP_SQR_C +#define BN_MP_SQRMOD_C +#define BN_MP_SQRT_C +#define BN_MP_SUB_C +#define BN_MP_SUB_D_C +#define BN_MP_SUBMOD_C +#define BN_MP_TO_SIGNED_BIN_C +#define BN_MP_TO_SIGNED_BIN_N_C +#define BN_MP_TO_UNSIGNED_BIN_C +#define BN_MP_TO_UNSIGNED_BIN_N_C +#define BN_MP_TOOM_MUL_C +#define BN_MP_TOOM_SQR_C +#define BN_MP_TORADIX_C +#define BN_MP_TORADIX_N_C +#define BN_MP_UNSIGNED_BIN_SIZE_C +#define BN_MP_XOR_C +#define BN_MP_ZERO_C +#define BN_PRIME_TAB_C +#define BN_REVERSE_C +#define BN_S_MP_ADD_C +#define BN_S_MP_EXPTMOD_C +#define BN_S_MP_MUL_DIGS_C +#define BN_S_MP_MUL_HIGH_DIGS_C +#define BN_S_MP_SQR_C +#define BN_S_MP_SUB_C +#define BNCORE_C +#endif + +#if defined(BN_ERROR_C) + #define BN_MP_ERROR_TO_STRING_C +#endif + +#if defined(BN_FAST_MP_INVMOD_C) + #define BN_MP_ISEVEN_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_COPY_C + #define BN_MP_MOD_C + #define BN_MP_SET_C + #define BN_MP_DIV_2_C + #define BN_MP_ISODD_C + #define BN_MP_SUB_C + #define BN_MP_CMP_C + #define BN_MP_ISZERO_C + #define BN_MP_CMP_D_C + #define BN_MP_ADD_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C +#endif + +#if defined(BN_FAST_MP_MONTGOMERY_REDUCE_C) + #define BN_MP_GROW_C + #define BN_MP_RSHD_C + #define BN_MP_CLAMP_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C +#endif + +#if defined(BN_FAST_S_MP_MUL_DIGS_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_FAST_S_MP_SQR_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_2EXPT_C) + #define BN_MP_ZERO_C + #define BN_MP_GROW_C +#endif + +#if defined(BN_MP_ABS_C) + #define BN_MP_COPY_C +#endif + +#if defined(BN_MP_ADD_C) + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C +#endif + +#if defined(BN_MP_ADD_D_C) + #define BN_MP_GROW_C + #define BN_MP_SUB_D_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_ADDMOD_C) + #define BN_MP_INIT_C + #define BN_MP_ADD_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C +#endif + +#if defined(BN_MP_AND_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_CLAMP_C) +#endif + +#if defined(BN_MP_CLEAR_C) +#endif + +#if defined(BN_MP_CLEAR_MULTI_C) + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_CMP_C) + #define BN_MP_CMP_MAG_C +#endif + +#if defined(BN_MP_CMP_D_C) +#endif + +#if defined(BN_MP_CMP_MAG_C) +#endif + +#if defined(BN_MP_CNT_LSB_C) + #define BN_MP_ISZERO_C +#endif + +#if defined(BN_MP_COPY_C) + #define BN_MP_GROW_C +#endif + +#if defined(BN_MP_COUNT_BITS_C) +#endif + +#if defined(BN_MP_DIV_C) + #define BN_MP_ISZERO_C + #define BN_MP_CMP_MAG_C + #define BN_MP_COPY_C + #define BN_MP_ZERO_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_SET_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_ABS_C + #define BN_MP_MUL_2D_C + #define BN_MP_CMP_C + #define BN_MP_SUB_C + #define BN_MP_ADD_C + #define BN_MP_DIV_2D_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_INIT_C + #define BN_MP_INIT_COPY_C + #define BN_MP_LSHD_C + #define BN_MP_RSHD_C + #define BN_MP_MUL_D_C + #define BN_MP_CLAMP_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_DIV_2_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_DIV_2D_C) + #define BN_MP_COPY_C + #define BN_MP_ZERO_C + #define BN_MP_INIT_C + #define BN_MP_MOD_2D_C + #define BN_MP_CLEAR_C + #define BN_MP_RSHD_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C +#endif + +#if defined(BN_MP_DIV_3_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_DIV_D_C) + #define BN_MP_ISZERO_C + #define BN_MP_COPY_C + #define BN_MP_DIV_2D_C + #define BN_MP_DIV_3_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_DR_IS_MODULUS_C) +#endif + +#if defined(BN_MP_DR_REDUCE_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C +#endif + +#if defined(BN_MP_DR_SETUP_C) +#endif + +#if defined(BN_MP_EXCH_C) +#endif + +#if defined(BN_MP_EXPT_D_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_SET_C + #define BN_MP_SQR_C + #define BN_MP_CLEAR_C + #define BN_MP_MUL_C +#endif + +#if defined(BN_MP_EXPTMOD_C) + #define BN_MP_INIT_C + #define BN_MP_INVMOD_C + #define BN_MP_CLEAR_C + #define BN_MP_ABS_C + #define BN_MP_CLEAR_MULTI_C + #define BN_MP_REDUCE_IS_2K_L_C + #define BN_S_MP_EXPTMOD_C + #define BN_MP_DR_IS_MODULUS_C + #define BN_MP_REDUCE_IS_2K_C + #define BN_MP_ISODD_C + #define BN_MP_EXPTMOD_FAST_C +#endif + +#if defined(BN_MP_EXPTMOD_FAST_C) + #define BN_MP_COUNT_BITS_C + #define BN_MP_INIT_C + #define BN_MP_CLEAR_C + #define BN_MP_MONTGOMERY_SETUP_C + #define BN_FAST_MP_MONTGOMERY_REDUCE_C + #define BN_MP_MONTGOMERY_REDUCE_C + #define BN_MP_DR_SETUP_C + #define BN_MP_DR_REDUCE_C + #define BN_MP_REDUCE_2K_SETUP_C + #define BN_MP_REDUCE_2K_C + #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C + #define BN_MP_MULMOD_C + #define BN_MP_SET_C + #define BN_MP_MOD_C + #define BN_MP_COPY_C + #define BN_MP_SQR_C + #define BN_MP_MUL_C + #define BN_MP_EXCH_C +#endif + +#if defined(BN_MP_EXTEUCLID_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_SET_C + #define BN_MP_COPY_C + #define BN_MP_ISZERO_C + #define BN_MP_DIV_C + #define BN_MP_MUL_C + #define BN_MP_SUB_C + #define BN_MP_NEG_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C +#endif + +#if defined(BN_MP_FREAD_C) + #define BN_MP_ZERO_C + #define BN_MP_S_RMAP_C + #define BN_MP_MUL_D_C + #define BN_MP_ADD_D_C + #define BN_MP_CMP_D_C +#endif + +#if defined(BN_MP_FWRITE_C) + #define BN_MP_RADIX_SIZE_C + #define BN_MP_TORADIX_C +#endif + +#if defined(BN_MP_GCD_C) + #define BN_MP_ISZERO_C + #define BN_MP_ABS_C + #define BN_MP_ZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_CNT_LSB_C + #define BN_MP_DIV_2D_C + #define BN_MP_CMP_MAG_C + #define BN_MP_EXCH_C + #define BN_S_MP_SUB_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_GET_INT_C) +#endif + +#if defined(BN_MP_GROW_C) +#endif + +#if defined(BN_MP_INIT_C) +#endif + +#if defined(BN_MP_INIT_COPY_C) + #define BN_MP_COPY_C +#endif + +#if defined(BN_MP_INIT_MULTI_C) + #define BN_MP_ERR_C + #define BN_MP_INIT_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_INIT_SET_C) + #define BN_MP_INIT_C + #define BN_MP_SET_C +#endif + +#if defined(BN_MP_INIT_SET_INT_C) + #define BN_MP_INIT_C + #define BN_MP_SET_INT_C +#endif + +#if defined(BN_MP_INIT_SIZE_C) + #define BN_MP_INIT_C +#endif + +#if defined(BN_MP_INVMOD_C) + #define BN_MP_ISZERO_C + #define BN_MP_ISODD_C + #define BN_FAST_MP_INVMOD_C + #define BN_MP_INVMOD_SLOW_C +#endif + +#if defined(BN_MP_INVMOD_SLOW_C) + #define BN_MP_ISZERO_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_C + #define BN_MP_COPY_C + #define BN_MP_ISEVEN_C + #define BN_MP_SET_C + #define BN_MP_DIV_2_C + #define BN_MP_ISODD_C + #define BN_MP_ADD_C + #define BN_MP_SUB_C + #define BN_MP_CMP_C + #define BN_MP_CMP_D_C + #define BN_MP_CMP_MAG_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C +#endif + +#if defined(BN_MP_IS_SQUARE_C) + #define BN_MP_MOD_D_C + #define BN_MP_INIT_SET_INT_C + #define BN_MP_MOD_C + #define BN_MP_GET_INT_C + #define BN_MP_SQRT_C + #define BN_MP_SQR_C + #define BN_MP_CMP_MAG_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_JACOBI_C) + #define BN_MP_CMP_D_C + #define BN_MP_ISZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_CNT_LSB_C + #define BN_MP_DIV_2D_C + #define BN_MP_MOD_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_KARATSUBA_MUL_C) + #define BN_MP_MUL_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_SUB_C + #define BN_MP_ADD_C + #define BN_MP_LSHD_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_KARATSUBA_SQR_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_SQR_C + #define BN_MP_SUB_C + #define BN_S_MP_ADD_C + #define BN_MP_LSHD_C + #define BN_MP_ADD_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_LCM_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_GCD_C + #define BN_MP_CMP_MAG_C + #define BN_MP_DIV_C + #define BN_MP_MUL_C + #define BN_MP_CLEAR_MULTI_C +#endif + +#if defined(BN_MP_LSHD_C) + #define BN_MP_GROW_C + #define BN_MP_RSHD_C +#endif + +#if defined(BN_MP_MOD_C) + #define BN_MP_INIT_C + #define BN_MP_DIV_C + #define BN_MP_CLEAR_C + #define BN_MP_ADD_C + #define BN_MP_EXCH_C +#endif + +#if defined(BN_MP_MOD_2D_C) + #define BN_MP_ZERO_C + #define BN_MP_COPY_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_MOD_D_C) + #define BN_MP_DIV_D_C +#endif + +#if defined(BN_MP_MONTGOMERY_CALC_NORMALIZATION_C) + #define BN_MP_COUNT_BITS_C + #define BN_MP_2EXPT_C + #define BN_MP_SET_C + #define BN_MP_MUL_2_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C +#endif + +#if defined(BN_MP_MONTGOMERY_REDUCE_C) + #define BN_FAST_MP_MONTGOMERY_REDUCE_C + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #define BN_MP_RSHD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C +#endif + +#if defined(BN_MP_MONTGOMERY_SETUP_C) +#endif + +#if defined(BN_MP_MUL_C) + #define BN_MP_TOOM_MUL_C + #define BN_MP_KARATSUBA_MUL_C + #define BN_FAST_S_MP_MUL_DIGS_C + #define BN_S_MP_MUL_C + #define BN_S_MP_MUL_DIGS_C +#endif + +#if defined(BN_MP_MUL_2_C) + #define BN_MP_GROW_C +#endif + +#if defined(BN_MP_MUL_2D_C) + #define BN_MP_COPY_C + #define BN_MP_GROW_C + #define BN_MP_LSHD_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_MUL_D_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_MULMOD_C) + #define BN_MP_INIT_C + #define BN_MP_MUL_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C +#endif + +#if defined(BN_MP_N_ROOT_C) + #define BN_MP_INIT_C + #define BN_MP_SET_C + #define BN_MP_COPY_C + #define BN_MP_EXPT_D_C + #define BN_MP_MUL_C + #define BN_MP_SUB_C + #define BN_MP_MUL_D_C + #define BN_MP_DIV_C + #define BN_MP_CMP_C + #define BN_MP_SUB_D_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_NEG_C) + #define BN_MP_COPY_C + #define BN_MP_ISZERO_C +#endif + +#if defined(BN_MP_OR_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_PRIME_FERMAT_C) + #define BN_MP_CMP_D_C + #define BN_MP_INIT_C + #define BN_MP_EXPTMOD_C + #define BN_MP_CMP_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_PRIME_IS_DIVISIBLE_C) + #define BN_MP_MOD_D_C +#endif + +#if defined(BN_MP_PRIME_IS_PRIME_C) + #define BN_MP_CMP_D_C + #define BN_MP_PRIME_IS_DIVISIBLE_C + #define BN_MP_INIT_C + #define BN_MP_SET_C + #define BN_MP_PRIME_MILLER_RABIN_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_PRIME_MILLER_RABIN_C) + #define BN_MP_CMP_D_C + #define BN_MP_INIT_COPY_C + #define BN_MP_SUB_D_C + #define BN_MP_CNT_LSB_C + #define BN_MP_DIV_2D_C + #define BN_MP_EXPTMOD_C + #define BN_MP_CMP_C + #define BN_MP_SQRMOD_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_PRIME_NEXT_PRIME_C) + #define BN_MP_CMP_D_C + #define BN_MP_SET_C + #define BN_MP_SUB_D_C + #define BN_MP_ISEVEN_C + #define BN_MP_MOD_D_C + #define BN_MP_INIT_C + #define BN_MP_ADD_D_C + #define BN_MP_PRIME_MILLER_RABIN_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_PRIME_RABIN_MILLER_TRIALS_C) +#endif + +#if defined(BN_MP_PRIME_RANDOM_EX_C) + #define BN_MP_READ_UNSIGNED_BIN_C + #define BN_MP_PRIME_IS_PRIME_C + #define BN_MP_SUB_D_C + #define BN_MP_DIV_2_C + #define BN_MP_MUL_2_C + #define BN_MP_ADD_D_C +#endif + +#if defined(BN_MP_RADIX_SIZE_C) + #define BN_MP_COUNT_BITS_C + #define BN_MP_INIT_COPY_C + #define BN_MP_ISZERO_C + #define BN_MP_DIV_D_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_RADIX_SMAP_C) + #define BN_MP_S_RMAP_C +#endif + +#if defined(BN_MP_RAND_C) + #define BN_MP_ZERO_C + #define BN_MP_ADD_D_C + #define BN_MP_LSHD_C +#endif + +#if defined(BN_MP_READ_RADIX_C) + #define BN_MP_ZERO_C + #define BN_MP_S_RMAP_C + #define BN_MP_RADIX_SMAP_C + #define BN_MP_MUL_D_C + #define BN_MP_ADD_D_C + #define BN_MP_ISZERO_C +#endif + +#if defined(BN_MP_READ_SIGNED_BIN_C) + #define BN_MP_READ_UNSIGNED_BIN_C +#endif + +#if defined(BN_MP_READ_UNSIGNED_BIN_C) + #define BN_MP_GROW_C + #define BN_MP_ZERO_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_REDUCE_C) + #define BN_MP_REDUCE_SETUP_C + #define BN_MP_INIT_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_MUL_C + #define BN_S_MP_MUL_HIGH_DIGS_C + #define BN_FAST_S_MP_MUL_HIGH_DIGS_C + #define BN_MP_MOD_2D_C + #define BN_S_MP_MUL_DIGS_C + #define BN_MP_SUB_C + #define BN_MP_CMP_D_C + #define BN_MP_SET_C + #define BN_MP_LSHD_C + #define BN_MP_ADD_C + #define BN_MP_CMP_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_REDUCE_2K_C) + #define BN_MP_INIT_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_DIV_2D_C + #define BN_MP_MUL_D_C + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_REDUCE_2K_L_C) + #define BN_MP_INIT_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_DIV_2D_C + #define BN_MP_MUL_C + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_REDUCE_2K_SETUP_C) + #define BN_MP_INIT_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_2EXPT_C + #define BN_MP_CLEAR_C + #define BN_S_MP_SUB_C +#endif + +#if defined(BN_MP_REDUCE_2K_SETUP_L_C) + #define BN_MP_INIT_C + #define BN_MP_2EXPT_C + #define BN_MP_COUNT_BITS_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_REDUCE_IS_2K_C) + #define BN_MP_REDUCE_2K_C + #define BN_MP_COUNT_BITS_C +#endif + +#if defined(BN_MP_REDUCE_IS_2K_L_C) +#endif + +#if defined(BN_MP_REDUCE_SETUP_C) + #define BN_MP_2EXPT_C + #define BN_MP_DIV_C +#endif + +#if defined(BN_MP_RSHD_C) + #define BN_MP_ZERO_C +#endif + +#if defined(BN_MP_SET_C) + #define BN_MP_ZERO_C +#endif + +#if defined(BN_MP_SET_INT_C) + #define BN_MP_ZERO_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_SHRINK_C) +#endif + +#if defined(BN_MP_SIGNED_BIN_SIZE_C) + #define BN_MP_UNSIGNED_BIN_SIZE_C +#endif + +#if defined(BN_MP_SQR_C) + #define BN_MP_TOOM_SQR_C + #define BN_MP_KARATSUBA_SQR_C + #define BN_FAST_S_MP_SQR_C + #define BN_S_MP_SQR_C +#endif + +#if defined(BN_MP_SQRMOD_C) + #define BN_MP_INIT_C + #define BN_MP_SQR_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C +#endif + +#if defined(BN_MP_SQRT_C) + #define BN_MP_N_ROOT_C + #define BN_MP_ISZERO_C + #define BN_MP_ZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_DIV_C + #define BN_MP_ADD_C + #define BN_MP_DIV_2_C + #define BN_MP_CMP_MAG_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_SUB_C) + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C +#endif + +#if defined(BN_MP_SUB_D_C) + #define BN_MP_GROW_C + #define BN_MP_ADD_D_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_SUBMOD_C) + #define BN_MP_INIT_C + #define BN_MP_SUB_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C +#endif + +#if defined(BN_MP_TO_SIGNED_BIN_C) + #define BN_MP_TO_UNSIGNED_BIN_C +#endif + +#if defined(BN_MP_TO_SIGNED_BIN_N_C) + #define BN_MP_SIGNED_BIN_SIZE_C + #define BN_MP_TO_SIGNED_BIN_C +#endif + +#if defined(BN_MP_TO_UNSIGNED_BIN_C) + #define BN_REVERSE_C + #define BN_MP_INIT_COPY_C + #define BN_MP_ISZERO_C + #define BN_MP_DIV_2D_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_TO_UNSIGNED_BIN_N_C) + #define BN_MP_UNSIGNED_BIN_SIZE_C + #define BN_MP_TO_UNSIGNED_BIN_C +#endif + +#if defined(BN_MP_TOOM_MUL_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_2D_C + #define BN_MP_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_MUL_C + #define BN_MP_MUL_2_C + #define BN_MP_ADD_C + #define BN_MP_SUB_C + #define BN_MP_DIV_2_C + #define BN_MP_MUL_2D_C + #define BN_MP_MUL_D_C + #define BN_MP_DIV_3_C + #define BN_MP_LSHD_C + #define BN_MP_CLEAR_MULTI_C +#endif + +#if defined(BN_MP_TOOM_SQR_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_2D_C + #define BN_MP_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_SQR_C + #define BN_MP_MUL_2_C + #define BN_MP_ADD_C + #define BN_MP_SUB_C + #define BN_MP_DIV_2_C + #define BN_MP_MUL_2D_C + #define BN_MP_MUL_D_C + #define BN_MP_DIV_3_C + #define BN_MP_LSHD_C + #define BN_MP_CLEAR_MULTI_C +#endif + +#if defined(BN_MP_TORADIX_C) + #define BN_MP_ISZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_DIV_D_C + #define BN_MP_CLEAR_C + #define BN_MP_S_RMAP_C +#endif + +#if defined(BN_MP_TORADIX_N_C) + #define BN_MP_ISZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_DIV_D_C + #define BN_MP_CLEAR_C + #define BN_MP_S_RMAP_C +#endif + +#if defined(BN_MP_UNSIGNED_BIN_SIZE_C) + #define BN_MP_COUNT_BITS_C +#endif + +#if defined(BN_MP_XOR_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_ZERO_C) +#endif + +#if defined(BN_PRIME_TAB_C) +#endif + +#if defined(BN_REVERSE_C) +#endif + +#if defined(BN_S_MP_ADD_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_S_MP_EXPTMOD_C) + #define BN_MP_COUNT_BITS_C + #define BN_MP_INIT_C + #define BN_MP_CLEAR_C + #define BN_MP_REDUCE_SETUP_C + #define BN_MP_REDUCE_C + #define BN_MP_REDUCE_2K_SETUP_L_C + #define BN_MP_REDUCE_2K_L_C + #define BN_MP_MOD_C + #define BN_MP_COPY_C + #define BN_MP_SQR_C + #define BN_MP_MUL_C + #define BN_MP_SET_C + #define BN_MP_EXCH_C +#endif + +#if defined(BN_S_MP_MUL_DIGS_C) + #define BN_FAST_S_MP_MUL_DIGS_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_S_MP_MUL_HIGH_DIGS_C) + #define BN_FAST_S_MP_MUL_HIGH_DIGS_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_S_MP_SQR_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_S_MP_SUB_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BNCORE_C) +#endif + +#ifdef LTM3 +#define LTM_LAST +#endif +#include "libtorrent/tommath_superclass.h" +#include "libtorrent/tommath_class.h" +#else +#define LTM_LAST +#endif + +/* $Source: /cvs/libtom/libtommath/tommath_class.h,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2005/07/28 11:59:32 $ */ diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/tommath_superclass.h b/apps/Launcher/ext/libtorrent/include/libtorrent/tommath_superclass.h new file mode 100644 index 0000000000..641d5b1833 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/tommath_superclass.h @@ -0,0 +1,84 @@ +/* super class file for PK algos */ + +/* default ... include all MPI */ +//#define LTM_ALL + +// these are the only functions used by libtorrent +#define BN_MP_EXPTMOD_C +#define BN_MP_UNSIGNED_BIN_SIZE_C +#define BN_MP_TO_UNSIGNED_BIN_C +#define BN_MP_READ_UNSIGNED_BIN_C +#define BN_MP_SET_INT_C +#define BNCORE_C + +/* RSA only (does not support DH/DSA/ECC) */ +/* #define SC_RSA_1 */ + +/* For reference.... On an Athlon64 optimizing for speed... + + LTM's mpi.o with all functions [striped] is 142KiB in size. + +*/ + +/* Works for RSA only, mpi.o is 68KiB */ +#ifdef SC_RSA_1 + #define BN_MP_SHRINK_C + #define BN_MP_LCM_C + #define BN_MP_PRIME_RANDOM_EX_C + #define BN_MP_INVMOD_C + #define BN_MP_GCD_C + #define BN_MP_MOD_C + #define BN_MP_MULMOD_C + #define BN_MP_ADDMOD_C + #define BN_MP_EXPTMOD_C + #define BN_MP_SET_INT_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_CLEAR_MULTI_C + #define BN_MP_UNSIGNED_BIN_SIZE_C + #define BN_MP_TO_UNSIGNED_BIN_C + #define BN_MP_MOD_D_C + #define BN_MP_PRIME_RABIN_MILLER_TRIALS_C + #define BN_REVERSE_C + #define BN_PRIME_TAB_C + + /* other modifiers */ + #define BN_MP_DIV_SMALL /* Slower division, not critical */ + + /* here we are on the last pass so we turn things off. The functions classes are still there + * but we remove them specifically from the build. This also invokes tweaks in functions + * like removing support for even moduli, etc... + */ +#ifdef LTM_LAST + #undef BN_MP_TOOM_MUL_C + #undef BN_MP_TOOM_SQR_C + #undef BN_MP_KARATSUBA_MUL_C + #undef BN_MP_KARATSUBA_SQR_C + #undef BN_MP_REDUCE_C + #undef BN_MP_REDUCE_SETUP_C + #undef BN_MP_DR_IS_MODULUS_C + #undef BN_MP_DR_SETUP_C + #undef BN_MP_DR_REDUCE_C + #undef BN_MP_REDUCE_IS_2K_C + #undef BN_MP_REDUCE_2K_SETUP_C + #undef BN_MP_REDUCE_2K_C + #undef BN_S_MP_EXPTMOD_C + #undef BN_MP_DIV_3_C + #undef BN_S_MP_MUL_HIGH_DIGS_C + #undef BN_FAST_S_MP_MUL_HIGH_DIGS_C + #undef BN_FAST_MP_INVMOD_C + + /* To safely undefine these you have to make sure your RSA key won't exceed the Comba threshold + * which is roughly 255 digits [7140 bits for 32-bit machines, 15300 bits for 64-bit machines] + * which means roughly speaking you can handle upto 2536-bit RSA keys with these defined without + * trouble. + */ + #undef BN_S_MP_MUL_DIGS_C + #undef BN_S_MP_SQR_C + #undef BN_MP_MONTGOMERY_REDUCE_C +#endif + +#endif + +/* $Source: /cvs/libtom/libtommath/tommath_superclass.h,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2005/05/14 13:29:17 $ */ diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/torrent.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/torrent.hpp new file mode 100644 index 0000000000..fe634f3343 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/torrent.hpp @@ -0,0 +1,1437 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_TORRENT_HPP_INCLUDE +#define TORRENT_TORRENT_HPP_INCLUDE + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/policy.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/stat.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/piece_picker.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/bandwidth_limit.hpp" +#include "libtorrent/bandwidth_queue_entry.hpp" +#include "libtorrent/storage_defs.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/bitfield.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/deadline_timer.hpp" +#include "libtorrent/union_endpoint.hpp" + +#if TORRENT_COMPLETE_TYPES_REQUIRED +#include "libtorrent/peer_connection.hpp" +#endif + +// define as 0 to disable. 1 enables debug output of the pieces and requested +// blocks. 2 also enables trace output of the time critical piece picking +// logic +#define TORRENT_DEBUG_STREAMING 0 + +namespace libtorrent +{ + class http_parser; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + struct logger; +#endif + + class piece_manager; + struct torrent_plugin; + struct bitfield; + struct announce_entry; + struct tracker_request; + struct add_torrent_params; + struct storage_interface; + class bt_peer_connection; + struct listen_socket_t; + + namespace aux + { + struct session_impl; + struct piece_checker_data; + } + + struct time_critical_piece + { + // when this piece was first requested + ptime first_requested; + // when this piece was last requested + ptime last_requested; + // by what time we want this piece + ptime deadline; + // 1 = send alert with piece data when available + int flags; + // how many peers it's been requested from + int peers; + // the piece index + int piece; +#if TORRENT_DEBUG_STREAMING > 0 + // the number of multiple requests are allowed + // to blocks still not downloaded (debugging only) + int timed_out; +#endif + bool operator<(time_critical_piece const& rhs) const + { return deadline < rhs.deadline; } + }; + + // a torrent is a class that holds information + // for a specific download. It updates itself against + // the tracker + class TORRENT_EXTRA_EXPORT torrent: public request_callback + , public boost::enable_shared_from_this + { + public: + + torrent(aux::session_impl& ses, tcp::endpoint const& net_interface + , int block_size, int seq, add_torrent_params const& p + , sha1_hash const& info_hash); + ~torrent(); + +#ifndef TORRENT_DISABLE_ENCRYPTION + sha1_hash const& obfuscated_hash() const + { return m_obfuscated_hash; } +#endif + + // This may be called from multiple threads + sha1_hash const& info_hash() const { return m_info_hash; } + + bool is_deleted() const { return m_deleted; } + + // starts the announce timer + void start(); + + void start_download_url(); + +#ifndef TORRENT_DISABLE_EXTENSIONS + void add_extension(boost::shared_ptr); + void add_extension(boost::function(torrent*, void*)> const& ext + , void* userdata); + void notify_extension_add_peer(tcp::endpoint const& ip, int src, int flags); +#endif + + peer_connection* find_lowest_ranking_peer() const; + +#if TORRENT_USE_ASSERTS + bool has_peer(peer_connection* p) const + { return m_connections.find(p) != m_connections.end(); } +#endif + + // this is called when the torrent has metadata. + // it will initialize the storage and the piece-picker + void init(); + + // find the peer that introduced us to the given endpoint. This is + // used when trying to holepunch. We need the introducer so that we + // can send a rendezvous connect message + bt_peer_connection* find_introducer(tcp::endpoint const& ep) const; + + // if we're connected to a peer at ep, return its peer connection + // only count BitTorrent peers + bt_peer_connection* find_peer(tcp::endpoint const& ep) const; + + void on_resume_data_checked(int ret, disk_io_job const& j); + void on_force_recheck(int ret, disk_io_job const& j); + void on_piece_checked(int ret, disk_io_job const& j); + void files_checked(); + void start_checking(); + + void start_announcing(); + void stop_announcing(); + + void send_share_mode(); + void send_upload_only(); + + void set_share_mode(bool s); + bool share_mode() const { return m_share_mode; } + + bool graceful_pause() const { return m_graceful_pause_mode; } + + void set_upload_mode(bool b); + bool upload_mode() const { return m_upload_mode || m_graceful_pause_mode; } + bool is_upload_only() const { return is_finished() || upload_mode(); } + + int seed_rank(session_settings const& s) const; + + enum flags_t { overwrite_existing = 1 }; + void add_piece(int piece, char const* data, int flags = 0); + void on_disk_write_complete(int ret, disk_io_job const& j + , peer_request p); + void on_disk_cache_complete(int ret, disk_io_job const& j); + + void set_progress_ppm(int p) { m_progress_ppm = p; } + struct read_piece_struct + { + boost::shared_array piece_data; + int blocks_left; + bool fail; + error_code error; + }; + void read_piece(int piece); + void on_disk_read_complete(int ret, disk_io_job const& j, peer_request r, read_piece_struct* rp); + + storage_mode_t storage_mode() const { return (storage_mode_t)m_storage_mode; } + storage_interface* get_storage() + { + if (!m_owning_storage) return 0; + return m_owning_storage->get_storage_impl(); + } + + // this will flag the torrent as aborted. The main + // loop in session_impl will check for this state + // on all torrents once every second, and take + // the necessary actions then. + void abort(); + bool is_aborted() const { return m_abort; } + + void new_external_ip(); + + torrent_status::state_t state() const { return (torrent_status::state_t)m_state; } + void set_state(torrent_status::state_t s); + + session_settings const& settings() const; + + aux::session_impl& session() { return m_ses; } + + void set_sequential_download(bool sd); + bool is_sequential_download() const + { return m_sequential_download; } + + void queue_up(); + void queue_down(); + void set_queue_position(int p); + int queue_position() const { return m_sequence_number; } + + void second_tick(stat& accumulator, int tick_interval_ms); + + // see if we need to connect to web seeds, and if so, + // connect to them + void maybe_connect_web_seeds(); + + std::string name() const; + + stat statistics() const { return m_stat; } + void add_stats(stat const& s); + size_type bytes_left() const; + int block_bytes_wanted(piece_block const& p) const; + void bytes_done(torrent_status& st, bool accurate) const; + size_type quantized_bytes_done() const; + + void ip_filter_updated() { m_policy.ip_filter_updated(); } + + void handle_disk_error(disk_io_job const& j, peer_connection* c = 0); + void clear_error(); + void set_error(error_code const& ec, std::string const& file); + bool has_error() const { return !!m_error; } + error_code error() const { return m_error; } + + void flush_cache(); + void pause(bool graceful = false); + void resume(); + void set_allow_peers(bool b, bool graceful_pause = false); + void set_announce_to_dht(bool b) { m_announce_to_dht = b; } + void set_announce_to_trackers(bool b) { m_announce_to_trackers = b; } + void set_announce_to_lsd(bool b) { m_announce_to_lsd = b; } + + ptime started() const { return m_started; } + void do_pause(); + void do_resume(); + + bool is_paused() const; + bool allows_peers() const { return m_allow_peers; } + bool is_torrent_paused() const { return !m_allow_peers || m_graceful_pause_mode; } + void force_recheck(); + void save_resume_data(int flags); + + bool is_active_download() const; + bool is_active_finished() const; + void update_guage(); + + bool need_save_resume_data() const + { + // save resume data every 15 minutes regardless, just to + // keep stats up to date + return m_need_save_resume_data || time(0) - m_last_saved_resume > 15 * 60; + } + + bool is_auto_managed() const { return m_auto_managed; } + void auto_managed(bool a); + + bool should_check_files() const; + + bool delete_files(); + + // ============ start deprecation ============= + void filter_piece(int index, bool filter); + void filter_pieces(std::vector const& bitmask); + bool is_piece_filtered(int index) const; + void filtered_pieces(std::vector& bitmask) const; + void filter_files(std::vector const& files); +#if !TORRENT_NO_FPU + void file_progress(std::vector& fp) const; +#endif + // ============ end deprecation ============= + + void piece_availability(std::vector& avail) const; + + void set_piece_priority(int index, int priority); + int piece_priority(int index) const; + + void prioritize_pieces(std::vector const& pieces); + void piece_priorities(std::vector*) const; + + void set_file_priority(int index, int priority); + int file_priority(int index) const; + + void prioritize_files(std::vector const& files); + void file_priorities(std::vector*) const; + + void cancel_non_critical(); + void set_piece_deadline(int piece, int t, int flags); + void reset_piece_deadline(int piece); + void clear_time_critical(); + void update_piece_priorities(); + + void status(torrent_status* st, boost::uint32_t flags); + + // this torrent changed state, if the user is subscribing to + // it, add it to the m_state_updates list in session_impl + void state_updated(); + + void file_progress(std::vector& fp, int flags = 0) const; + + void use_interface(std::string net_interface); + tcp::endpoint get_interface() const; + + void connect_to_url_seed(std::list::iterator url); + bool connect_to_peer(policy::peer* peerinfo, bool ignore_limit = false); + + int priority() const { return m_priority; } + void set_priority(int prio) + { + TORRENT_ASSERT(prio <= 255 && prio >= 0); + if (prio > 255) prio = 255; + else if (prio < 0) prio = 0; + m_priority = prio; + state_updated(); + } + +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + void resolve_countries(bool r) + { m_resolve_countries = r; } + + bool resolving_countries() const + { + return m_resolve_countries && !m_ses.settings().force_proxy; + } +#endif + +// -------------------------------------------- + // BANDWIDTH MANAGEMENT + + bandwidth_channel m_bandwidth_channel[2]; + + int bandwidth_throttle(int channel) const; + +// -------------------------------------------- + // PEER MANAGEMENT + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING || defined TORRENT_LOGGING + void log_to_all_peers(char const* message); +#endif + + // add or remove a url that will be attempted for + // finding the file(s) in this torrent. + void add_web_seed(std::string const& url, web_seed_entry::type_t type); + void add_web_seed(std::string const& url, web_seed_entry::type_t type + , std::string const& auth, web_seed_entry::headers_t const& extra_headers); + + void remove_web_seed(std::string const& url, web_seed_entry::type_t type); + void disconnect_web_seed(peer_connection* p); + + void retry_web_seed(peer_connection* p, int retry = 0); + + void remove_web_seed(peer_connection* p); + + std::list web_seeds() const + { return m_web_seeds; } + + std::set web_seeds(web_seed_entry::type_t type) const; + + bool free_upload_slots() const + { return m_num_uploads < m_max_uploads; } + + bool choke_peer(peer_connection& c); + bool unchoke_peer(peer_connection& c, bool optimistic = false); + + // used by peer_connection to attach itself to a torrent + // since incoming connections don't know what torrent + // they're a part of until they have received an info_hash. + // false means attach failed + bool attach_peer(peer_connection* p); + + // this will remove the peer and make sure all + // the pieces it had have their reference counter + // decreased in the piece_picker + void remove_peer(peer_connection* p); + + void cancel_block(piece_block block); + + bool want_more_peers() const; + bool try_connect_peer(); + void add_peer(tcp::endpoint const& adr, int source); + + // the number of peers that belong to this torrent + int num_peers() const { return (int)m_connections.size(); } + int num_seeds() const; + + typedef std::set::iterator peer_iterator; + typedef std::set::const_iterator const_peer_iterator; + + const_peer_iterator begin() const { return m_connections.begin(); } + const_peer_iterator end() const { return m_connections.end(); } + + peer_iterator begin() { return m_connections.begin(); } + peer_iterator end() { return m_connections.end(); } + + void resolve_peer_country(boost::intrusive_ptr const& p) const; + + void get_full_peer_list(std::vector& v) const; + void get_peer_info(std::vector& v); + void get_download_queue(std::vector* queue); + + void refresh_explicit_cache(int cache_size); + +// -------------------------------------------- + // TRACKER MANAGEMENT + + // these are callbacks called by the tracker_connection instance + // (either http_tracker_connection or udp_tracker_connection) + // when this torrent got a response from its tracker request + // or when a failure occured + virtual void tracker_response( + tracker_request const& r + , address const& tracker_ip + , std::list
const& ip_list + , std::vector& e, int interval, int min_interval + , int complete, int incomplete, int downloaded + , address const& external_ip, std::string const& trackerid); + virtual void tracker_request_error(tracker_request const& r + , int response_code, error_code const& ec, const std::string& msg + , int retry_interval); + virtual void tracker_warning(tracker_request const& req + , std::string const& msg); + virtual void tracker_scrape_response(tracker_request const& req + , int complete, int incomplete, int downloaded, int downloaders); + + void update_scrape_state(); + + // if no password and username is set + // this will return an empty string, otherwise + // it will concatenate the login and password + // ready to be sent over http (but without + // base64 encoding). + std::string tracker_login() const; + + // generate the tracker key for this torrent. + // The key is passed to http trackers as ``&key=``. + boost::uint32_t tracker_key() const; + + // if we need a connect boost, connect some peers + // immediately + void do_connect_boost(); + + // returns the absolute time when the next tracker + // announce will take place. + ptime next_announce() const; + + // forcefully sets next_announce to the current time + void force_tracker_request(ptime, int tracker_idx); + void scrape_tracker(); + void announce_with_tracker(tracker_request::event_t e + = tracker_request::none + , address const& bind_interface = address_v4::any()); + int seconds_since_last_scrape() const { return m_last_scrape; } + +#ifndef TORRENT_DISABLE_DHT + void dht_announce(); +#endif + + // sets the username and password that will be sent to + // the tracker + void set_tracker_login(std::string const& name, std::string const& pw); + + // the tcp::endpoint of the tracker that we managed to + // announce ourself at the last time we tried to announce + tcp::endpoint current_tracker() const; + + announce_entry* find_tracker(tracker_request const& r); + +// -------------------------------------------- + // PIECE MANAGEMENT + + void recalc_share_mode(); + + void update_sparse_piece_prio(int piece, int cursor, int reverse_cursor); + + void get_suggested_pieces(std::vector& s) const; + + bool super_seeding() const + { + // we're not super seeding if we're not a seed + return m_super_seeding && is_seed(); + } + + void super_seeding(bool on); + int get_piece_to_super_seed(bitfield const&); + + // returns true if we have downloaded the given piece + bool have_piece(int index) const + { + if (!valid_metadata()) return false; + if (!has_picker()) return is_seed(); + return m_picker->have_piece(index); + } + + // called when we learn that we have a piece + // only once per piece + void we_have(int index); + + int num_have() const + { + // pretend we have every piece when in seed mode + if (m_seed_mode) { + return m_torrent_file->num_pieces(); + } + + return has_picker() + ? m_picker->num_have() + : m_torrent_file->num_pieces(); + } + + // when we get a have message, this is called for that piece + void peer_has(int index, peer_connection const* peer) + { + if (has_picker()) + { + m_picker->inc_refcount(index, peer); + } +#ifdef TORRENT_DEBUG + else + { + TORRENT_ASSERT(is_seed()); + } +#endif + } + + // when we get a bitfield message, this is called for that piece + void peer_has(bitfield const& bits, peer_connection const* peer) + { + if (has_picker()) + { + if (bits.all_set() && bits.size() > 0) + m_picker->inc_refcount_all(peer); + else + m_picker->inc_refcount(bits, peer); + } +#ifdef TORRENT_DEBUG + else + { + TORRENT_ASSERT(is_seed()); + } +#endif + } + + void peer_has_all(peer_connection const* peer) + { + if (has_picker()) + { + m_picker->inc_refcount_all(peer); + } +#ifdef TORRENT_DEBUG + else + { + TORRENT_ASSERT(is_seed()); + } +#endif + } + + void peer_lost(bitfield const& bits, peer_connection const* peer) + { + if (has_picker()) + { + if (bits.all_set() && bits.size() > 0) + m_picker->dec_refcount_all(peer); + else + m_picker->dec_refcount(bits, peer); + } +#ifdef TORRENT_DEBUG + else + { + TORRENT_ASSERT(is_seed()); + } +#endif + } + + void peer_lost(int index, peer_connection const* peer) + { + if (has_picker()) + { + m_picker->dec_refcount(index, peer); + } +#ifdef TORRENT_DEBUG + else + { + TORRENT_ASSERT(is_seed()); + } +#endif + } + + int block_size() const { TORRENT_ASSERT(m_block_size_shift > 0); return 1 << m_block_size_shift; } + peer_request to_req(piece_block const& p) const; + + void disconnect_all(error_code const& ec); + int disconnect_peers(int num, error_code const& ec); + + // this is called wheh the torrent has completed + // the download. It will post an event, disconnect + // all seeds and let the tracker know we're finished. + void completed(); + +#if TORRENT_USE_I2P + void on_i2p_resolve(error_code const& ec, char const* dest); +#endif + + // this is the asio callback that is called when a name + // lookup for a PEER is completed. + void on_peer_name_lookup(error_code const& e, tcp::resolver::iterator i + , peer_id pid); + + // this is the asio callback that is called when a name + // lookup for a WEB SEED is completed. + void on_name_lookup(error_code const& e, tcp::resolver::iterator i + , std::list::iterator url, tcp::endpoint proxy); + + void connect_web_seed(std::list::iterator web, tcp::endpoint a); + + // this is the asio callback that is called when a name + // lookup for a proxy for a web seed is completed. + void on_proxy_name_lookup(error_code const& e, tcp::resolver::iterator i + , std::list::iterator url); + + // remove a web seed, or schedule it for removal in case there + // are outstanding operations on it + void remove_web_seed(std::list::iterator web); + + // this is called when the torrent has finished. i.e. + // all the pieces we have not filtered have been downloaded. + // If no pieces are filtered, this is called first and then + // completed() is called immediately after it. + void finished(); + + // This is the opposite of finished. It is called if we used + // to be finished but enabled some files for download so that + // we wasn't finished anymore. + void resume_download(); + + void async_verify_piece(int piece_index, boost::function const&); + + // this is called from the peer_connection + // each time a piece has failed the hash + // test + void piece_finished(int index, int passed_hash_check); + + // piece_passed is called when a piece passes the hash check + // this will tell all peers that we just got his piece + // and also let the piece picker know that we have this piece + // so it wont pick it for download + void piece_passed(int index); + + // piece_failed is called when a piece fails the hash check + void piece_failed(int index); + + // this will restore the piece picker state for a piece + // by re marking all the requests to blocks in this piece + // that are still outstanding in peers' download queues. + // this is done when a piece fails + void restore_piece_state(int index); + + enum wasted_reason_t + { + piece_timed_out, piece_cancelled, piece_unknown, piece_seed, piece_end_game, piece_closing + , waste_reason_max + }; + void add_redundant_bytes(int b, wasted_reason_t reason); + void add_failed_bytes(int b); + + // this is true if we have all the pieces + bool is_seed() const + { + return valid_metadata() + && (!m_picker + || m_state == torrent_status::seeding + || m_picker->num_have() == m_picker->num_pieces()); + } + + // this is true if we have all the pieces that we want + bool is_finished() const + { + if (is_seed()) return true; + return valid_metadata() && m_torrent_file->num_pieces() + - m_picker->num_have() - m_picker->num_filtered() == 0; + } + + bool is_inactive() const + { return m_inactive; } + + std::string save_path() const; + alert_manager& alerts() const; + piece_picker& picker() + { + TORRENT_ASSERT(m_picker.get()); + return *m_picker; + } + bool has_picker() const + { + return m_picker.get() != 0; + } + policy& get_policy() { return m_policy; } + piece_manager& filesystem(); + torrent_info const& torrent_file() const + { return *m_torrent_file; } + + boost::intrusive_ptr get_torrent_copy(); + + std::string const& uuid() const { return m_uuid; } + void set_uuid(std::string const& s) { m_uuid = s; } + std::string const& url() const { return m_url; } + void set_url(std::string const& s) { m_url = s; } + std::string const& source_feed_url() const { return m_source_feed_url; } + void set_source_feed_url(std::string const& s) { m_source_feed_url = s; } + + std::vector const& trackers() const + { return m_trackers; } + + void replace_trackers(std::vector const& urls); + + // returns true if the tracker was added, and false if it was already + // in the tracker list (in which case the source was added to the + // entry in the list) + bool add_tracker(announce_entry const& url); + + torrent_handle get_handle(); + + void write_resume_data(entry& rd) const; + void read_resume_data(lazy_entry const& rd); + + void seen_complete() { m_last_seen_complete = time(0); } + int time_since_complete() const { return int(time(0) - m_last_seen_complete); } + time_t last_seen_complete() const { return m_last_seen_complete; } + + // LOGGING +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + virtual void debug_log(const char* fmt, ...) const; +#endif + + // DEBUG +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + +// -------------------------------------------- + // RESOURCE MANAGEMENT + + int get_peer_upload_limit(tcp::endpoint ip) const; + int get_peer_download_limit(tcp::endpoint ip) const; + void set_peer_upload_limit(tcp::endpoint ip, int limit); + void set_peer_download_limit(tcp::endpoint ip, int limit); + + void set_upload_limit(int limit, bool state_update = true); + int upload_limit() const; + void set_download_limit(int limit, bool state_update = true); + int download_limit() const; + + void set_max_uploads(int limit, bool state_update = true); + int max_uploads() const { return m_max_uploads; } + void set_max_connections(int limit, bool state_update = true); + int max_connections() const { return m_max_connections; } + + // flags are defined in storage.hpp + void move_storage(std::string const& save_path, int flags); + + // renames the file with the given index to the new name + // the name may include a directory path + // returns false on failure + bool rename_file(int index, std::string const& name); + + // unless this returns true, new connections must wait + // with their initialization. + bool ready_for_connections() const + { return m_connections_initialized; } + bool valid_metadata() const + { return m_torrent_file->is_valid(); } + bool are_files_checked() const + { return m_files_checked; } + + // parses the info section from the given + // bencoded tree and moves the torrent + // to the checker thread for initial checking + // of the storage. + // a return value of false indicates an error + bool set_metadata(char const* metadata_buf, int metadata_size); + + void on_torrent_download(error_code const& ec, http_parser const& parser + , char const* data, int size); + + int sequence_number() const { return m_sequence_number; } + + bool seed_mode() const { return m_seed_mode; } + void leave_seed_mode(bool seed); + + bool all_verified() const + { return int(m_num_verified) == m_torrent_file->num_pieces(); } + bool verified_piece(int piece) const + { + TORRENT_ASSERT(piece < int(m_verified.size())); + TORRENT_ASSERT(piece >= 0); + return m_verified.get_bit(piece); + } + void verified(int piece); + + bool add_merkle_nodes(std::map const& n, int piece); + + // this is called once periodically for torrents + // that are not private + void lsd_announce(); + + void update_last_upload() { m_last_upload = 0; } + + void set_apply_ip_filter(bool b); + bool apply_ip_filter() const { return m_apply_ip_filter; } + + void queue_torrent_check(); + void dequeue_torrent_check(); + + void clear_in_state_update() + { m_in_state_updates = false; } + + void inc_num_connecting() + { ++m_num_connecting; } + void dec_num_connecting() + { + TORRENT_ASSERT(m_num_connecting > 0); + --m_num_connecting; + } + + bool is_ssl_torrent() const { return m_ssl_torrent; } +#ifdef TORRENT_USE_OPENSSL + void set_ssl_cert(std::string const& certificate + , std::string const& private_key + , std::string const& dh_params + , std::string const& passphrase); + void set_ssl_cert_buffer(std::string const& certificate + , std::string const& private_key + , std::string const& dh_params); + boost::asio::ssl::context* ssl_ctx() const { return m_ssl_ctx.get(); } +#endif + + int num_time_critical_pieces() const + { return m_time_critical_pieces.size(); } + + private: + + void on_files_deleted(int ret, disk_io_job const& j); + void on_files_released(int ret, disk_io_job const& j); + void on_torrent_paused(int ret, disk_io_job const& j); + void on_storage_moved(int ret, disk_io_job const& j); + void on_save_resume_data(int ret, disk_io_job const& j); + void on_file_renamed(int ret, disk_io_job const& j); + void on_cache_flushed(int ret, disk_io_job const& j); + + void on_piece_verified(int ret, disk_io_job const& j + , boost::function f); + + int prioritize_tracker(int tracker_index); + int deprioritize_tracker(int tracker_index); + + void on_country_lookup(error_code const& error, tcp::resolver::iterator i + , boost::intrusive_ptr p) const; + bool request_bandwidth_from_session(int channel) const; + + void update_peer_interest(bool was_finished); + void prioritize_udp_trackers(); + + void parse_response(const entry& e, std::vector& peer_list); + + void update_tracker_timer(ptime now); + + static void on_tracker_announce_disp(boost::weak_ptr p + , error_code const& e); + + void on_tracker_announce(); + +#ifndef TORRENT_DISABLE_DHT + static void on_dht_announce_response_disp(boost::weak_ptr t + , std::vector const& peers); + void on_dht_announce_response(std::vector const& peers); + bool should_announce_dht() const; +#endif + + void remove_time_critical_piece(int piece, bool finished = false); + void remove_time_critical_pieces(std::vector const& priority); + void request_time_critical_pieces(); + + policy m_policy; + + // all time totals of uploaded and downloaded payload + // stored in resume data + size_type m_total_uploaded; + size_type m_total_downloaded; + + // if this torrent is running, this was the time + // when it was started. This is used to have a + // bias towards keeping seeding torrents that + // recently was started, to avoid oscillation + ptime m_started; + + boost::intrusive_ptr m_torrent_file; + + // if this pointer is 0, the torrent is in + // a state where the metadata hasn't been + // received yet, or during shutdown. + // the piece_manager keeps the torrent object + // alive by holding a shared_ptr to it and + // the torrent keeps the piece manager alive + // with this intrusive_ptr. This cycle is + // broken when torrent::abort() is called + // Then the torrent releases the piece_manager + // and when the piece_manager is complete with all + // outstanding disk io jobs (that keeps + // the piece_manager alive) it will destruct + // and release the torrent file. The reason for + // this is that the torrent_info is used by + // the piece_manager, and stored in the + // torrent, so the torrent cannot destruct + // before the piece_manager. + boost::intrusive_ptr m_owning_storage; + + // this is a weak (non owninig) pointer to + // the piece_manager. This is used after the torrent + // has been aborted, and it can no longer own + // the object. + piece_manager* m_storage; + +#ifdef TORRENT_USE_OPENSSL + boost::shared_ptr m_ssl_ctx; + +#if BOOST_VERSION >= 104700 + bool verify_peer_cert(bool preverified, boost::asio::ssl::verify_context& ctx); +#endif + + void init_ssl(std::string const& cert); +#endif + + std::set m_connections; + + // of all peers in m_connections, this is the number + // of peers that are outgoing and still waiting to + // complete the connection. This is used to possibly + // kick out these connections when we get incoming + // connections (if we've reached the connection limit) + int m_num_connecting; + + // The list of web seeds in this torrent. Seeds + // with fatal errors are removed from the set + std::list m_web_seeds; + +#ifndef TORRENT_DISABLE_EXTENSIONS + typedef std::list > extension_list_t; + extension_list_t m_extensions; +#endif + + // used for tracker announces + deadline_timer m_tracker_timer; + + // this is the upload and download statistics for the whole torrent. + // it's updated from all its peers once every second. + libtorrent::stat m_stat; + + // ----------------------------- + + // a back reference to the session + // this torrent belongs to. + aux::session_impl& m_ses; + + // used to resolve hostnames for web seeds + mutable tcp::resolver m_host_resolver; + + std::vector m_file_priority; + + // this vector contains the number of bytes completely + // downloaded (as in passed-hash-check) in each file. + // this lets us trigger on individual files completing + std::vector m_file_progress; + + boost::scoped_ptr m_picker; + + std::vector m_trackers; + // this is an index into m_trackers + + // this list is sorted by time_critical_piece::deadline + std::deque m_time_critical_pieces; + + std::string m_trackerid; + std::string m_username; + std::string m_password; + + // the network interfaces outgoing connections + // are opened through. If there is more then one, + // they are used in a round-robin fasion + std::vector m_net_interfaces; + + std::string m_save_path; + + // if we don't have the metadata, this is a url to + // the torrent file + std::string m_url; + + // if this was added from an RSS feed, this is the unique + // identifier in the feed. + std::string m_uuid; + + // if this torrent was added by an RSS feed, this is the + // URL to that feed + std::string m_source_feed_url; + + // this is used as temporary storage while downloading + // the .torrent file from m_url + std::vector m_torrent_file_buf; + + // each bit represents a piece. a set bit means + // the piece has had its hash verified. This + // is only used in seed mode (when m_seed_mode + // is true) + bitfield m_verified; + + // set if there's an error on this torrent + error_code m_error; + // if the error ocurred on a file, this is the file + std::string m_error_file; + + // used if there is any resume data + std::vector m_resume_data; + lazy_entry m_resume_entry; + + // if the torrent is started without metadata, it may + // still be given a name until the metadata is received + // once the metadata is received this field will no + // longer be used and will be reset + boost::scoped_ptr m_name; + + storage_constructor_type m_storage_constructor; + + // the posix time this torrent was added and when + // it was completed. If the torrent isn't yet + // completed, m_completed_time is 0 + time_t m_added_time; + time_t m_completed_time; + time_t m_last_saved_resume; + + // this was the last time _we_ saw a seed in this swarm + time_t m_last_seen_complete; + + // this is the time last any of our peers saw a seed + // in this swarm + time_t m_swarm_last_seen_complete; + + // m_num_verified = m_verified.count() + boost::uint32_t m_num_verified; + +#ifndef TORRENT_DISABLE_ENCRYPTION + // this is SHA1("req2" + info-hash), used for + // encrypted hand shakes + sha1_hash m_obfuscated_hash; +#endif + + // keep a copy if the info-hash here, so it can be accessed from multiple + // threads, and be cheap to access from the client + sha1_hash m_info_hash; + + // the average time it takes to download one time critical piece + boost::uint32_t m_average_piece_time; + // the average piece download time deviation + boost::uint32_t m_piece_time_deviation; + + // the number of bytes that has been + // downloaded that failed the hash-test + boost::uint32_t m_total_failed_bytes; + boost::uint32_t m_total_redundant_bytes; + + // the sequence number for this torrent, this is a + // monotonically increasing number for each added torrent + int m_sequence_number; + + // ============================== + // The following members are specifically + // ordered to make the 24 bit members + // properly 32 bit aligned by inserting + // 8 bits after each one + // ============================== + + // the number of seconds we've been in upload mode + unsigned int m_upload_mode_time:24; + + // the state of this torrent (queued, checking, downloading, etc.) + unsigned int m_state:3; + + // determines the storage state for this torrent. + unsigned int m_storage_mode:2; + + // this is true while tracker announcing is enabled + // is is disabled while paused and checking files + bool m_announcing:1; + + // this is true while the tracker deadline timer + // is in use. i.e. one or more trackers are waiting + // for a reannounce + bool m_waiting_tracker:1; + + // this means we haven't verified the file content + // of the files we're seeding. the m_verified bitfield + // indicates which pieces have been verified and which + // haven't + bool m_seed_mode:1; + +// ---- + + // total time we've been available on this torrent + // does not count when the torrent is stopped or paused + // in seconds + unsigned int m_active_time:24; + + // the index to the last tracker that worked + boost::int8_t m_last_working_tracker; + +// ---- + + // total time we've been finished with this torrent + // does not count when the torrent is stopped or paused + unsigned int m_finished_time:24; + + // in case the piece picker hasn't been constructed + // when this settings is set, this variable will keep + // its value until the piece picker is created + bool m_sequential_download:1; + + // is false by default and set to + // true when the first tracker reponse + // is received + bool m_got_tracker_response:1; + + // this is set to false as long as the connections + // of this torrent hasn't been initialized. If we + // have metadata from the start, connections are + // initialized immediately, if we didn't have metadata, + // they are initialized right after files_checked(). + // valid_resume_data() will return false as long as + // the connections aren't initialized, to avoid + // them from altering the piece-picker before it + // has been initialized with files_checked(). + bool m_connections_initialized:1; + + // if this is true, we're currently super seeding this + // torrent. + bool m_super_seeding:1; + + // this is set when we don't want to load seed_mode, + // paused or auto_managed from the resume data + bool m_override_resume_data:1; + + // this is true while there is a country + // resolution in progress. To avoid flodding + // the DNS request queue, only one ip is resolved + // at a time. + mutable bool m_resolving_country:1; + + // this is true if the user has enabled + // country resolution in this torrent + bool m_resolve_countries:1; + + // set to false when saving resume data. Set to true + // whenever something is downloaded + bool m_need_save_resume_data:1; + +// ---- + + // total time we've been available as a seed on this torrent + // does not count when the torrent is stopped or paused + unsigned int m_seeding_time:24; + + // this is a counter that is decreased every + // second, and when it reaches 0, the policy::pulse() + // is called and the time scaler is reset to 10. + boost::int8_t m_time_scaler; + +// ---- + + // the maximum number of uploads for this torrent + unsigned int m_max_uploads:24; + + // these are the flags sent in on a call to save_resume_data + // we need to save them to check them in write_resume_data + boost::uint8_t m_save_resume_flags; + +// ---- + + // the number of unchoked peers in this torrent + unsigned int m_num_uploads:24; + + // the size of a request block + // each piece is divided into these + // blocks when requested. The block size is + // 1 << m_block_size_shift + unsigned int m_block_size_shift:5; + + // is set to true every time there is an incoming + // connection to this torrent + bool m_has_incoming:1; + + // this is set to true when the files are checked + // before the files are checked, we don't try to + // connect to peers + bool m_files_checked:1; + + // this is true if the torrent has been added to + // checking queue in the session + bool m_queued_for_checking:1; + +// ---- + + // the maximum number of connections for this torrent + unsigned int m_max_connections:24; + + // set to true when this torrent has been paused but + // is waiting to finish all current download requests + // before actually closing all connections + bool m_graceful_pause_mode:1; + + // this is set to true when the torrent starts up + // The first tracker response, when this is true, + // will attempt to connect to a bunch of peers immediately + // and set this to false. We only do this once to get + // the torrent kick-started + bool m_need_connect_boost:1; + + // rotating sequence number for LSD announces sent out. + // used to only use IP broadcast for every 8th lsd announce + boost::uint8_t m_lsd_seq:3; + + // this is set to true if the torrent was started without + // metadata. It is used to save metadata in the resume file + // by default for such torrents. It does not necessarily + // have to be a magnet link. + bool m_magnet_link:1; + + // set to true if the session IP filter applies to this + // torrent or not. Defaults to true. + bool m_apply_ip_filter:1; + + // if set to true, add tracker URLs loaded from resume + // data into this torrent instead of replacing them + bool m_merge_resume_trackers:1; + +// ---- + + // the number of bytes of padding files + boost::uint32_t m_padding:24; + + // this is the priority of the torrent. The higher + // the value is, the more bandwidth is assigned to + // the torrent's peers + boost::uint32_t m_priority:8; + +// ---- + + // the scrape data from the tracker response, this + // is optional and may be 0xffffff + boost::uint32_t m_complete:24; + + // state subscription. If set, a pointer to this torrent + // will be added to the m_state_updates set in session_impl + // whenever this torrent's state changes (any state). + bool m_state_subscription:1; + + // in state_updates list. When adding a torrent to the + // session_impl's m_state_update list, this bit is set + // to never add the same torrent twice + bool m_in_state_updates:1; + + // these represent whether or not this torrent is counted + // in the total counters of active seeds and downloads + // in the session. + bool m_is_active_download:1; + bool m_is_active_finished:1; + + // even if we're not built to support SSL torrents, + // remember that this is an SSL torrent, so that we don't + // accidentally start seeding it without any authentication. + bool m_ssl_torrent:1; + + // this is set to true if we're trying to delete the + // files belonging to it. When set, don't write any + // more blocks to disk! + bool m_deleted:1; + + // set to true while moving the storage + bool m_moving_storage:1; + + // this is true if this torrent is considered inactive from the + // queuing mechanism's point of view. If a torrent doesn't transfer + // at high enough rates, it's inactive. + bool m_inactive:1; + +// ---- + + // the scrape data from the tracker response, this + // is optional and may be 0xffffff + boost::uint32_t m_incomplete:24; + + // is set to true when the torrent has + // been aborted. + bool m_abort:1; + + // true when the torrent should announce to + // the DHT + bool m_announce_to_dht:1; + + // true when this torrent should anncounce to + // trackers + bool m_announce_to_trackers:1; + + // true when this torrent should anncounce to + // the local network + bool m_announce_to_lsd:1; + + // is true if this torrent has allows having peers + bool m_allow_peers:1; + + // set to true when this torrent may not download anything + bool m_upload_mode:1; + + // if this is true, libtorrent may pause and resume + // this torrent depending on queuing rules. Torrents + // started with auto_managed flag set may be added in + // a paused state in case there are no available + // slots. + bool m_auto_managed:1; + + // this is set when the torrent is in share-mode + bool m_share_mode:1; + +// ---- + + // the number of seconds since the last piece passed for + // this torrent + boost::uint64_t m_last_download:24; + + // the number of seconds since the last scrape request to + // one of the trackers in this torrent + boost::uint64_t m_last_scrape:16; + + // the number of seconds since the last byte was uploaded + // from this torrent + boost::uint64_t m_last_upload:24; + +// ---- + + // the scrape data from the tracker response, this + // is optional and may be 0xffffff + unsigned int m_downloaded:24; + + // round-robin index into m_interfaces + mutable boost::uint8_t m_interface_index; + +// ---- + + // progress parts per million (the number of + // millionths of completeness) + unsigned int m_progress_ppm:20; + + // the number of seconds this torrent has been under the inactive + // threshold in terms of sending and receiving data. When this counter + // reaches the settings.inactive_torrent_timeout it will be considered + // inactive and possibly open up another queue slot, to start another, + // queued, torrent. Every second it's above the threshold + boost::int16_t m_inactive_counter; + + // if this is set, accept the save path saved in the resume data, if + // present + bool m_use_resume_save_path:1; + +#if TORRENT_USE_ASSERTS + public: + // set to false until we've loaded resume data + bool m_resume_data_loaded; +#endif + }; +} + +#endif // TORRENT_TORRENT_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/torrent_handle.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/torrent_handle.hpp new file mode 100644 index 0000000000..594feb36be --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/torrent_handle.hpp @@ -0,0 +1,1602 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_TORRENT_HANDLE_HPP_INCLUDED +#define TORRENT_TORRENT_HANDLE_HPP_INCLUDED + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/piece_picker.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/ptime.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/storage.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/bitfield.hpp" +#include "libtorrent/socket.hpp" // tcp::endpoint + +namespace libtorrent +{ + namespace aux + { + struct session_impl; + } + + struct torrent_plugin; + struct peer_info; + struct peer_list_entry; + struct torrent_status; + class torrent; + + // allows torrent_handle to be used in unordered_map and unordered_set. + TORRENT_EXPORT std::size_t hash_value(torrent_status const& ts); + +#ifndef BOOST_NO_EXCEPTIONS + // for compatibility with 0.14 + typedef libtorrent_exception duplicate_torrent; + typedef libtorrent_exception invalid_handle; + void throw_invalid_handle(); +#endif + + // holds the state of a block in a piece. Who we requested + // it from and how far along we are at downloading it. + struct TORRENT_EXPORT block_info + { + // this is the enum used for the block_info::state field. + enum block_state_t + { + // This block has not been downloaded or requested form any peer. + none, + // The block has been requested, but not completely downloaded yet. + requested, + // The block has been downloaded and is currently queued for being + // written to disk. + writing, + // The block has been written to disk. + finished + }; + + private: + TORRENT_UNION addr_t + { + address_v4::bytes_type v4; +#if TORRENT_USE_IPV6 + address_v6::bytes_type v6; +#endif + } addr; + + boost::uint16_t port; + public: + + // The peer is the ip address of the peer this block was downloaded from. + void set_peer(tcp::endpoint const& ep) + { +#if TORRENT_USE_IPV6 + is_v6_addr = ep.address().is_v6(); + if (is_v6_addr) + addr.v6 = ep.address().to_v6().to_bytes(); + else +#endif + addr.v4 = ep.address().to_v4().to_bytes(); + port = ep.port(); + } + tcp::endpoint peer() const + { +#if TORRENT_USE_IPV6 + if (is_v6_addr) + return tcp::endpoint(address_v6(addr.v6), port); + else +#endif + return tcp::endpoint(address_v4(addr.v4), port); + } + + // the number of bytes that have been received for this block + unsigned bytes_progress:15; + + // the total number of bytes in this block. + unsigned block_size:15; + + // the state this block is in (see block_state_t) + unsigned state:2; + + // the number of peers that is currently requesting this block. Typically + // this is 0 or 1, but at the end of the torrent blocks may be requested + // by more peers in parallel to speed things up. + unsigned num_peers:14; + private: +#if TORRENT_USE_IPV6 + // the type of the addr union + unsigned is_v6_addr:1; +#endif + }; + + // This class holds information about pieces that have outstanding requests + // or outstanding writes + struct TORRENT_EXPORT partial_piece_info + { + // the index of the piece in question. ``blocks_in_piece`` is the number + // of blocks in this particular piece. This number will be the same for + // most pieces, but + // the last piece may have fewer blocks than the standard pieces. + int piece_index; + + // the number of blocks in this piece + int blocks_in_piece; + + // the number of blocks that are in the finished state + int finished; + + // the number of blocks that are in the writing state + int writing; + + // the number of blocks that are in the requested state + int requested; + + // this is an array of ``blocks_in_piece`` number of + // items. One for each block in the piece. + // + // .. warning:: This is a pointer that points to an array + // that's owned by the session object. The next time + // get_download_queue() is called, it will be invalidated. + block_info* blocks; + + // the speed classes. These may be used by the piece picker to + // coalesce requests of similar download rates + enum state_t { none, slow, medium, fast }; + + // the download speed class this piece falls into. + // this is used internally to cluster peers of the same + // speed class together when requesting blocks. + // + // set to either ``fast``, ``medium``, ``slow`` or ``none``. It tells + // which download rate category the peers downloading this piece falls + // into. ``none`` means that no peer is currently downloading any part of + // the piece. Peers prefer picking pieces from the same category as + // themselves. The reason for this is to keep the number of partially + // downloaded pieces down. Pieces set to ``none`` can be converted into + // any of ``fast``, ``medium`` or ``slow`` as soon as a peer want to + // download from it. + state_t piece_state; + }; + + // You will usually have to store your torrent handles somewhere, since it's + // the object through which you retrieve information about the torrent and + // aborts the torrent. + // + // .. warning:: + // Any member function that returns a value or fills in a value has to be + // made synchronously. This means it has to wait for the main thread to + // complete the query before it can return. This might potentially be + // expensive if done from within a GUI thread that needs to stay + // responsive. Try to avoid quering for information you don't need, and + // try to do it in as few calls as possible. You can get most of the + // interesting information about a torrent from the + // torrent_handle::status() call. + // + // The default constructor will initialize the handle to an invalid state. + // Which means you cannot perform any operation on it, unless you first + // assign it a valid handle. If you try to perform any operation on an + // uninitialized handle, it will throw ``invalid_handle``. + // + // .. warning:: + // All operations on a torrent_handle may throw libtorrent_exception + // exception, in case the handle is no longer refering to a torrent. + // There is one exception is_valid() will never throw. Since the torrents + // are processed by a background thread, there is no guarantee that a + // handle will remain valid between two calls. + // + struct TORRENT_EXPORT torrent_handle + { + friend class invariant_access; + friend struct aux::session_impl; + friend struct feed; + friend class torrent; + friend std::size_t hash_value(torrent_handle const& th); + + // constructs a torrent handle that does not refer to a torrent. + // i.e. is_valid() will return false. + torrent_handle() {} + + // flags for add_piece(). + enum flags_t { overwrite_existing = 1 }; + + // This function will write ``data`` to the storage as piece ``piece``, + // as if it had been downloaded from a peer. ``data`` is expected to + // point to a buffer of as many bytes as the size of the specified piece. + // The data in the buffer is copied and passed on to the disk IO thread + // to be written at a later point. + // + // By default, data that's already been downloaded is not overwritten by + // this buffer. If you trust this data to be correct (and pass the piece + // hash check) you may pass the overwrite_existing flag. This will + // instruct libtorrent to overwrite any data that may already have been + // downloaded with this data. + // + // Since the data is written asynchronously, you may know that is passed + // or failed the hash check by waiting for piece_finished_alert or + // hash_failed_alert. + void add_piece(int piece, char const* data, int flags = 0) const; + + // This function starts an asynchronous read operation of the specified + // piece from this torrent. You must have completed the download of the + // specified piece before calling this function. + // + // When the read operation is completed, it is passed back through an + // alert, read_piece_alert. Since this alert is a reponse to an explicit + // call, it will always be posted, regardless of the alert mask. + // + // Note that if you read multiple pieces, the read operations are not + // guaranteed to finish in the same order as you initiated them. + void read_piece(int piece) const; + + // Returns true if this piece has been completely downloaded, and false + // otherwise. + bool have_piece(int piece) const; + + // internal + void get_full_peer_list(std::vector& v) const; + + // takes a reference to a vector that will be cleared and filled with one + // entry for each peer connected to this torrent, given the handle is + // valid. If the torrent_handle is invalid, it will throw + // libtorrent_exception exception. Each entry in the vector contains + // information about that particular peer. See peer_info. + void get_peer_info(std::vector& v) const; + + // flags to pass in to status() to specify which properties of the + // torrent to query for. By default all flags are set. + enum status_flags_t + { + // calculates ``distributed_copies``, ``distributed_full_copies`` and + // ``distributed_fraction``. + query_distributed_copies = 1, + // includes partial downloaded blocks in ``total_done`` and + // ``total_wanted_done``. + query_accurate_download_counters = 2, + // includes ``last_seen_complete``. + query_last_seen_complete = 4, + // includes ``pieces``. + query_pieces = 8, + // includes ``verified_pieces`` (only applies to torrents in *seed + // mode*). + query_verified_pieces = 16, + // includes ``torrent_file``, which is all the static information from + // the .torrent file. + query_torrent_file = 32, + // includes ``name``, the name of the torrent. This is either derived + // from the .torrent file, or from the ``&dn=`` magnet link argument + // or possibly some other source. If the name of the torrent is not + // known, this is an empty string. + query_name = 64, + // includes ``save_path``, the path to the directory the files of the + // torrent are saved to. + query_save_path = 128 + }; + + // ``status()`` will return a structure with information about the status + // of this torrent. If the torrent_handle is invalid, it will throw + // libtorrent_exception exception. See torrent_status. The ``flags`` + // argument filters what information is returned in the torrent_status. + // Some information in there is relatively expensive to calculate, and if + // you're not interested in it (and see performance issues), you can + // filter them out. + // + // By default everything is included. The flags you can use to decide + // what to *include* are defined in the status_flags_t enum. + torrent_status status(boost::uint32_t flags = 0xffffffff) const; + + // ``get_download_queue()`` takes a non-const reference to a vector which + // it will fill with information about pieces that are partially + // downloaded or not downloaded at all but partially requested. See + // partial_piece_info for the fields in the returned vector. + void get_download_queue(std::vector& queue) const; + + // flags for set_piece_deadline(). + enum deadline_flags { alert_when_available = 1 }; + + // This function sets or resets the deadline associated with a specific + // piece index (``index``). libtorrent will attempt to download this + // entire piece before the deadline expires. This is not necessarily + // possible, but pieces with a more recent deadline will always be + // prioritized over pieces with a deadline further ahead in time. The + // deadline (and flags) of a piece can be changed by calling this + // function again. + // + // The ``flags`` parameter can be used to ask libtorrent to send an alert + // once the piece has been downloaded, by passing alert_when_available. + // When set, the read_piece_alert alert will be delivered, with the piece + // data, when it's downloaded. + // + // If the piece is already downloaded when this call is made, nothing + // happens, unless the alert_when_available flag is set, in which case it + // will do the same thing as calling read_piece() for ``index``. + // + // ``deadline`` is the number of milliseconds until this piece should be + // completed. + // + // ``reset_piece_deadline`` removes the deadline from the piece. If it + // hasn't already been downloaded, it will no longer be considered a + // priority. + // + // ``clear_piece_deadlines()`` removes deadlines on all pieces in + // the torrent. As if reset_piece_deadline() was called on all pieces. + void set_piece_deadline(int index, int deadline, int flags = 0) const; + void reset_piece_deadline(int index) const; + void clear_piece_deadlines() const; + + // This sets the bandwidth priority of this torrent. The priority of a + // torrent determines how much bandwidth its peers are assigned when + // distributing upload and download rate quotas. A high number gives more + // bandwidth. The priority must be within the range [0, 255]. + // + // The default priority is 0, which is the lowest priority. + // + // To query the priority of a torrent, use the + // ``torrent_handle::status()`` call. + // + // Torrents with higher priority will not nececcarily get as much + // bandwidth as they can consume, even if there's is more quota. Other + // peers will still be weighed in when bandwidth is being distributed. + // With other words, bandwidth is not distributed strictly in order of + // priority, but the priority is used as a weight. + // + // Peers whose Torrent has a higher priority will take precedence when + // distributing unchoke slots. This is a strict prioritization where + // every interested peer on a high priority torrent will be unchoked + // before any other, lower priority, torrents have any peers unchoked. + void set_priority(int prio) const; + +#ifndef TORRENT_NO_DEPRECATE +#if !TORRENT_NO_FPU + // fills the specified vector with the download progress [0, 1] + // of each file in the torrent. The files are ordered as in + // the torrent_info. + TORRENT_DEPRECATED_PREFIX + void file_progress(std::vector& progress) const TORRENT_DEPRECATED; +#endif +#endif + + // flags to be passed in file_progress(). + enum file_progress_flags_t + { + // only calculate file progress at piece granularity. This makes + // the file_progress() call cheaper and also only takes bytes that + // have passed the hash check into account, so progress cannot + // regress in this mode. + piece_granularity = 1 + }; + + // This function fills in the supplied vector with the the number of + // bytes downloaded of each file in this torrent. The progress values are + // ordered the same as the files in the torrent_info. This operation is + // not very cheap. Its complexity is *O(n + mj)*. Where *n* is the number + // of files, *m* is the number of downloading pieces and *j* is the + // number of blocks in a piece. + // + // The ``flags`` parameter can be used to specify the granularity of the + // file progress. If left at the default value of 0, the progress will be + // as accurate as possible, but also more expensive to calculate. If + // ``torrent_handle::piece_granularity`` is specified, the progress will + // be specified in piece granularity. i.e. only pieces that have been + // fully downloaded and passed the hash check count. When specifying + // piece granularity, the operation is a lot cheaper, since libtorrent + // already keeps track of this internally and no calculation is required. + void file_progress(std::vector& progress, int flags = 0) const; + + // If the torrent is in an error state (i.e. ``torrent_status::error`` is + // non-empty), this will clear the error and start the torrent again. + void clear_error() const; + + // ``trackers()`` will return the list of trackers for this torrent. The + // announce entry contains both a string ``url`` which specify the + // announce url for the tracker as well as an int ``tier``, which is + // specifies the order in which this tracker is tried. If you want + // libtorrent to use another list of trackers for this torrent, you can + // use ``replace_trackers()`` which takes a list of the same form as the + // one returned from ``trackers()`` and will replace it. If you want an + // immediate effect, you have to call force_reannounce(). See + // announce_entry. + // + // ``add_tracker()`` will look if the specified tracker is already in the + // set. If it is, it doesn't do anything. If it's not in the current set + // of trackers, it will insert it in the tier specified in the + // announce_entry. + // + // The updated set of trackers will be saved in the resume data, and when + // a torrent is started with resume data, the trackers from the resume + // data will replace the original ones. + std::vector trackers() const; + void replace_trackers(std::vector const&) const; + void add_tracker(announce_entry const&) const; + + // ``add_url_seed()`` adds another url to the torrent's list of url + // seeds. If the given url already exists in that list, the call has no + // effect. The torrent will connect to the server and try to download + // pieces from it, unless it's paused, queued, checking or seeding. + // ``remove_url_seed()`` removes the given url if it exists already. + // ``url_seeds()`` return a set of the url seeds currently in this + // torrent. Note that urls that fails may be removed automatically from + // the list. + // + // See http-seeding_ for more information. + void add_url_seed(std::string const& url) const; + void remove_url_seed(std::string const& url) const; + std::set url_seeds() const; + + // These functions are identical as the ``*_url_seed()`` variants, but + // they operate on `BEP 17`_ web seeds instead of `BEP 19`_. + // + // See http-seeding_ for more information. + void add_http_seed(std::string const& url) const; + void remove_http_seed(std::string const& url) const; + std::set http_seeds() const; + + // add the specified extension to this torrent. The ``ext`` argument is + // a function that will be called from within libtorrent's context + // passing in the internal torrent object and the specified userdata + // pointer. The function is expected to return a shared pointer to + // a torrent_plugin instance. + void add_extension( + boost::function(torrent*, void*)> const& ext + , void* userdata = 0); + + // ``set_metadata`` expects the *info* section of metadata. i.e. The + // buffer passed in will be hashed and verified against the info-hash. If + // it fails, a ``metadata_failed_alert`` will be generated. If it passes, + // a ``metadata_received_alert`` is generated. The function returns true + // if the metadata is successfully set on the torrent, and false + // otherwise. If the torrent already has metadata, this function will not + // affect the torrent, and false will be returned. + bool set_metadata(char const* metadata, int size) const; + + // Returns true if this handle refers to a valid torrent and false if it + // hasn't been initialized or if the torrent it refers to has been + // aborted. Note that a handle may become invalid after it has been added + // to the session. Usually this is because the storage for the torrent is + // somehow invalid or if the filenames are not allowed (and hence cannot + // be opened/created) on your filesystem. If such an error occurs, a + // file_error_alert is generated and all handles that refers to that + // torrent will become invalid. + bool is_valid() const; + + // flags for torrent_session::pause() + enum pause_flags_t { graceful_pause = 1 }; + + // ``pause()``, and ``resume()`` will disconnect all peers and reconnect + // all peers respectively. When a torrent is paused, it will however + // remember all share ratios to all peers and remember all potential (not + // connected) peers. Torrents may be paused automatically if there is a + // file error (e.g. disk full) or something similar. See + // file_error_alert. + // + // To know if a torrent is paused or not, call + // ``torrent_handle::status()`` and inspect ``torrent_status::paused``. + // + // The ``flags`` argument to pause can be set to + // ``torrent_handle::graceful_pause`` which will delay the disconnect of + // peers that we're still downloading outstanding requests from. The + // torrent will not accept any more requests and will disconnect all idle + // peers. As soon as a peer is done transferring the blocks that were + // requested from it, it is disconnected. This is a graceful shut down of + // the torrent in the sense that no downloaded bytes are wasted. + // + // torrents that are auto-managed may be automatically resumed again. It + // does not make sense to pause an auto-managed torrent without making it + // not automanaged first. Torrents are auto-managed by default when added + // to the session. For more information, see queuing_. + void pause(int flags = 0) const; + void resume() const; + + // Explicitly sets the upload mode of the torrent. In upload mode, the + // torrent will not request any pieces. If the torrent is auto managed, + // it will automatically be taken out of upload mode periodically (see + // ``session_settings::optimistic_disk_retry``). Torrents are + // automatically put in upload mode whenever they encounter a disk write + // error. + // + // ``m`` should be true to enter upload mode, and false to leave it. + // + // To test if a torrent is in upload mode, call + // ``torrent_handle::status()`` and inspect + // ``torrent_status::upload_mode``. + void set_upload_mode(bool b) const; + + // Enable or disable share mode for this torrent. When in share mode, the + // torrent will not necessarily be downloaded, especially not the whole + // of it. Only parts that are likely to be distributed to more than 2 + // other peers are downloaded, and only if the previous prediction was + // correct. + void set_share_mode(bool b) const; + + // Instructs libtorrent to flush all the disk caches for this torrent and + // close all file handles. This is done asynchronously and you will be + // notified that it's complete through cache_flushed_alert. + // + // Note that by the time you get the alert, libtorrent may have cached + // more data for the torrent, but you are guaranteed that whatever cached + // data libtorrent had by the time you called + // ``torrent_handle::flush_cache()`` has been written to disk. + void flush_cache() const; + + // Set to true to apply the session global IP filter to this torrent + // (which is the default). Set to false to make this torrent ignore the + // IP filter. + void apply_ip_filter(bool b) const; + + // ``force_recheck`` puts the torrent back in a state where it assumes to + // have no resume data. All peers will be disconnected and the torrent + // will stop announcing to the tracker. The torrent will be added to the + // checking queue, and will be checked (all the files will be read and + // compared to the piece hashes). Once the check is complete, the torrent + // will start connecting to peers again, as normal. + void force_recheck() const; + + // flags used in the save_resume_data call to control additional + // actions or fields to save. + enum save_resume_flags_t + { + // the disk cache will be flushed before creating the resume data. + // This avoids a problem with file timestamps in the resume data in + // case the cache hasn't been flushed yet. + flush_disk_cache = 1, + + // the resume data will contain the metadata from the torrent file as + // well. This is default for any torrent that's added without a + // torrent file (such as a magnet link or a URL). + save_info_dict = 2 + }; + + // ``save_resume_data()`` generates fast-resume data and returns it as an + // entry. This entry is suitable for being bencoded. For more information + // about how fast-resume works, see fast-resume_. + // + // The ``flags`` argument is a bitmask of flags ORed together. see + // save_resume_flags_t + // + // This operation is asynchronous, ``save_resume_data`` will return + // immediately. The resume data is delivered when it's done through an + // save_resume_data_alert. + // + // The fast resume data will be empty in the following cases: + // + // 1. The torrent handle is invalid. + // 2. The torrent is checking (or is queued for checking) its storage, it + // will obviously not be ready to write resume data. + // 3. The torrent hasn't received valid metadata and was started without + // metadata (see libtorrent's metadata-from-peers_ extension) + // + // Note that by the time you receive the fast resume data, it may already + // be invalid if the torrent is still downloading! The recommended + // practice is to first pause the session, then generate the fast resume + // data, and then close it down. Make sure to not remove_torrent() before + // you receive the save_resume_data_alert though. There's no need to + // pause when saving intermittent resume data. + // + //.. warning:: + // If you pause every torrent individually instead of pausing the + // session, every torrent will have its paused state saved in the + // resume data! + // + //.. warning:: + // The resume data contains the modification timestamps for all files. + // If one file has been modified when the torrent is added again, the + // will be rechecked. When shutting down, make sure to flush the disk + // cache before saving the resume data. This will make sure that the + // file timestamps are up to date and won't be modified after saving + // the resume data. The recommended way to do this is to pause the + // torrent, which will flush the cache and disconnect all peers. + // + //.. note:: + // It is typically a good idea to save resume data whenever a torrent + // is completed or paused. In those cases you don't need to pause the + // torrent or the session, since the torrent will do no more writing to + // its files. If you save resume data for torrents when they are + // paused, you can accelerate the shutdown process by not saving resume + // data again for paused torrents. Completed torrents should have their + // resume data saved when they complete and on exit, since their + // statistics might be updated. + // + // In full allocation mode the reume data is never invalidated by + // subsequent writes to the files, since pieces won't move around. This + // means that you don't need to pause before writing resume data in full + // or sparse mode. If you don't, however, any data written to disk after + // you saved resume data and before the session closed is lost. + // + // It also means that if the resume data is out dated, libtorrent will + // not re-check the files, but assume that it is fairly recent. The + // assumption is that it's better to loose a little bit than to re-check + // the entire file. + // + // It is still a good idea to save resume data periodically during + // download as well as when closing down. + // + // Example code to pause and save resume data for all torrents and wait + // for the alerts: + // + // .. code:: c++ + // + // extern int outstanding_resume_data; // global counter of outstanding resume data + // std::vector handles = ses.get_torrents(); + // ses.pause(); + // for (std::vector::iterator i = handles.begin(); + // i != handles.end(); ++i) + // { + // torrent_handle& h = *i; + // if (!h.is_valid()) continue; + // torrent_status s = h.status(); + // if (!s.has_metadata) continue; + // if (!s.need_save_resume_data()) continue; + // + // h.save_resume_data(); + // ++outstanding_resume_data; + // } + // + // while (outstanding_resume_data > 0) + // { + // alert const* a = ses.wait_for_alert(seconds(10)); + // + // // if we don't get an alert within 10 seconds, abort + // if (a == 0) break; + // + // std::auto_ptr holder = ses.pop_alert(); + // + // if (alert_cast(a)) + // { + // process_alert(a); + // --outstanding_resume_data; + // continue; + // } + // + // save_resume_data_alert const* rd = alert_cast(a); + // if (rd == 0) + // { + // process_alert(a); + // continue; + // } + // + // torrent_handle h = rd->handle; + // torrent_status st = h.status(torrent_handle::query_save_path | torrent_handle::query_name); + // std::ofstream out((st.save_path + // + "/" + st.name + ".fastresume").c_str() + // , std::ios_base::binary); + // out.unsetf(std::ios_base::skipws); + // bencode(std::ostream_iterator(out), *rd->resume_data); + // --outstanding_resume_data; + // } + // + //.. note:: + // Note how ``outstanding_resume_data`` is a global counter in this + // example. This is deliberate, otherwise there is a race condition for + // torrents that was just asked to save their resume data, they posted + // the alert, but it has not been received yet. Those torrents would + // report that they don't need to save resume data again, and skipped by + // the initial loop, and thwart the counter otherwise. + void save_resume_data(int flags = 0) const; + + // This function returns true if any whole chunk has been downloaded + // since the torrent was first loaded or since the last time the resume + // data was saved. When saving resume data periodically, it makes sense + // to skip any torrent which hasn't downloaded anything since the last + // time. + // + //.. note:: + // A torrent's resume data is considered saved as soon as the alert is + // posted. It is important to make sure this alert is received and + // handled in order for this function to be meaningful. + bool need_save_resume_data() const; + + // changes whether the torrent is auto managed or not. For more info, + // see queuing_. + void auto_managed(bool m) const; + + // Every torrent that is added is assigned a queue position exactly one + // greater than the greatest queue position of all existing torrents. + // Torrents that are being seeded have -1 as their queue position, since + // they're no longer in line to be downloaded. + // + // When a torrent is removed or turns into a seed, all torrents with + // greater queue positions have their positions decreased to fill in the + // space in the sequence. + // + // ``queue_position()`` returns the torrent's position in the download + // queue. The torrents with the smallest numbers are the ones that are + // being downloaded. The smaller number, the closer the torrent is to the + // front of the line to be started. + // + // The queue position is also available in the torrent_status. + // + // The ``queue_position_*()`` functions adjust the torrents position in + // the queue. Up means closer to the front and down means closer to the + // back of the queue. Top and bottom refers to the front and the back of + // the queue respectively. + int queue_position() const; + void queue_position_up() const; + void queue_position_down() const; + void queue_position_top() const; + void queue_position_bottom() const; + + // Sets or gets the flag that derermines if countries should be resolved + // for the peers of this torrent. It defaults to false. If it is set to + // true, the peer_info structure for the peers in this torrent will have + // their ``country`` member set. See peer_info for more information on + // how to interpret this field. + void resolve_countries(bool r); + bool resolve_countries() const; + + // For SSL torrents, use this to specify a path to a .pem file to use as + // this client's certificate. The certificate must be signed by the + // certificate in the .torrent file to be valid. + // + // The set_ssl_certificate_buffer() overload takes the actual certificate, + // private key and DH params as strings, rather than paths to files. This + // overload is only available when libtorrent is built against boost + // 1.54 or later. + // + // ``cert`` is a path to the (signed) certificate in .pem format + // corresponding to this torrent. + // + // ``private_key`` is a path to the private key for the specified + // certificate. This must be in .pem format. + // + // ``dh_params`` is a path to the Diffie-Hellman parameter file, which + // needs to be in .pem format. You can generate this file using the + // openssl command like this: ``openssl dhparam -outform PEM -out + // dhparams.pem 512``. + // + // ``passphrase`` may be specified if the private key is encrypted and + // requires a passphrase to be decrypted. + // + // Note that when a torrent first starts up, and it needs a certificate, + // it will suspend connecting to any peers until it has one. It's + // typically desirable to resume the torrent after setting the ssl + // certificate. + // + // If you receive a torrent_need_cert_alert, you need to call this to + // provide a valid cert. If you don't have a cert you won't be allowed to + // connect to any peers. + void set_ssl_certificate(std::string const& certificate + , std::string const& private_key + , std::string const& dh_params + , std::string const& passphrase = ""); + void set_ssl_certificate_buffer(std::string const& certificate + , std::string const& private_key + , std::string const& dh_params); + + // Returns the storage implementation for this torrent. This depends on the + // storage contructor function that was passed to add_torrent. + storage_interface* get_storage_impl() const; + + // Returns a pointer to the torrent_info object associated with this + // torrent. The torrent_info object may be a copy of the internal object. + // If the torrent doesn't have metadata, the pointer will not be + // initialized (i.e. a NULL pointer). The torrent may be in a state + // without metadata only if it was started without a .torrent file, e.g. + // by using the libtorrent extension of just supplying a tracker and + // info-hash. + boost::intrusive_ptr torrent_file() const; + +#ifndef TORRENT_NO_DEPRECATE + + // ================ start deprecation ============ + + // deprecated in 1.0 + // use status() instead (with query_save_path) + TORRENT_DEPRECATED_PREFIX + std::string save_path() const TORRENT_DEPRECATED; + + // deprecated in 1.0 + // use status() instead (with query_name) + // returns the name of this torrent, in case it doesn't + // have metadata it returns the name assigned to it + // when it was added. + TORRENT_DEPRECATED_PREFIX + std::string name() const TORRENT_DEPRECATED; + + // use torrent_file() instead + TORRENT_DEPRECATED_PREFIX + const torrent_info& get_torrent_info() const TORRENT_DEPRECATED; + + // deprecated in 0.16, feature will be removed + TORRENT_DEPRECATED_PREFIX + int get_peer_upload_limit(tcp::endpoint ip) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + int get_peer_download_limit(tcp::endpoint ip) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_peer_upload_limit(tcp::endpoint ip, int limit) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_peer_download_limit(tcp::endpoint ip, int limit) const TORRENT_DEPRECATED; + + // deprecated in 0.16, feature will be removed + TORRENT_DEPRECATED_PREFIX + void set_ratio(float up_down_ratio) const TORRENT_DEPRECATED; + + // deprecated in 0.16. use status() instead + TORRENT_DEPRECATED_PREFIX + bool is_seed() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + bool is_finished() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + bool is_paused() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + bool is_auto_managed() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + bool is_sequential_download() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + bool has_metadata() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + bool super_seeding() const TORRENT_DEPRECATED; + + // deprecated in 0.13 + // all these are deprecated, use piece + // priority functions instead + // marks the piece with the given index as filtered + // it will not be downloaded + TORRENT_DEPRECATED_PREFIX + void filter_piece(int index, bool filter) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void filter_pieces(std::vector const& pieces) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + bool is_piece_filtered(int index) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + std::vector filtered_pieces() const TORRENT_DEPRECATED; + // marks the file with the given index as filtered + // it will not be downloaded + TORRENT_DEPRECATED_PREFIX + void filter_files(std::vector const& files) const TORRENT_DEPRECATED; + + // deprecated in 0.14 + // use save_resume_data() instead. It is async. and + // will return the resume data in an alert + TORRENT_DEPRECATED_PREFIX + entry write_resume_data() const TORRENT_DEPRECATED; + // ================ end deprecation ============ +#endif + + // ``use_interface()`` sets the network interface this torrent will use + // when it opens outgoing connections. By default, it uses the same + // interface as the session uses to listen on. The parameter must be a + // string containing one or more, comma separated, ip-address (either an + // IPv4 or IPv6 address). When specifying multiple interfaces, the + // torrent will round-robin which interface to use for each outgoing + // conneciton. This is useful for clients that are multi-homed. + void use_interface(const char* net_interface) const; + + // Fills the specified ``std::vector`` with the availability for + // each piece in this torrent. libtorrent does not keep track of + // availability for seeds, so if the torrent is seeding the availability + // for all pieces is reported as 0. + // + // The piece availability is the number of peers that we are connected + // that has advertized having a particular piece. This is the information + // that libtorrent uses in order to prefer picking rare pieces. + void piece_availability(std::vector& avail) const; + + // These functions are used to set and get the prioritiy of individual + // pieces. By default all pieces have priority 1. That means that the + // random rarest first algorithm is effectively active for all pieces. + // You may however change the priority of individual pieces. There are 8 + // different priority levels: + // + // 0. piece is not downloaded at all + // 1. normal priority. Download order is dependent on availability + // 2. higher than normal priority. Pieces are preferred over pieces with + // the same availability, but not over pieces with lower availability + // 3. pieces are as likely to be picked as partial pieces. + // 4. pieces are preferred over partial pieces, but not over pieces with + // lower availability + // 5. *currently the same as 4* + // 6. piece is as likely to be picked as any piece with availability 1 + // 7. maximum priority, availability is disregarded, the piece is + // preferred over any other piece with lower priority + // + // The exact definitions of these priorities are implementation details, + // and subject to change. The interface guarantees that higher number + // means higher priority, and that 0 means do not download. + // + // ``piece_priority`` sets or gets the priority for an individual piece, + // specified by ``index``. + // + // ``prioritize_pieces`` takes a vector of integers, one integer per + // piece in the torrent. All the piece priorities will be updated with + // the priorities in the vector. + // + // ``piece_priorities`` returns a vector with one element for each piece + // in the torrent. Each element is the current priority of that piece. + void piece_priority(int index, int priority) const; + int piece_priority(int index) const; + void prioritize_pieces(std::vector const& pieces) const; + std::vector piece_priorities() const; + + // ``index`` must be in the range [0, number_of_files). + // + // ``file_priority()`` queries or sets the priority of file ``index``. + // + // ``prioritize_files()`` takes a vector that has at as many elements as + // there are files in the torrent. Each entry is the priority of that + // file. The function sets the priorities of all the pieces in the + // torrent based on the vector. + // + // ``file_priorities()`` returns a vector with the priorities of all + // files. + // + // The priority values are the same as for piece_priority(). + // + // Whenever a file priority is changed, all other piece priorities are + // reset to match the file priorities. In order to maintain special + // priorities for particular pieces, piece_priority() has to be called + // again for those pieces. + // + // You cannot set the file priorities on a torrent that does not yet have + // metadata or a torrent that is a seed. ``file_priority(int, int)`` and + // prioritize_files() are both no-ops for such torrents. + void file_priority(int index, int priority) const; + int file_priority(int index) const; + void prioritize_files(std::vector const& files) const; + std::vector file_priorities() const; + + // ``force_reannounce()`` will force this torrent to do another tracker + // request, to receive new peers. The ``seconds`` argument specifies how + // many seconds from now to issue the tracker announces. + // + // If the tracker's ``min_interval`` has not passed since the last + // announce, the forced announce will be scheduled to happen immediately + // as the ``min_interval`` expires. This is to honor trackers minimum + // re-announce interval settings. + // + // The ``tracker_index`` argument specifies which tracker to re-announce. + // If set to -1 (which is the default), all trackers are re-announce. + // + // ``force_dht_announce`` will announce the torrent to the DHT + // immediately. + void force_reannounce(int seconds = 0, int tracker_index = -1) const; + void force_dht_announce() const; + +#ifndef TORRENT_NO_DEPRECATE + // forces a reannounce in the specified amount of time. + // This overrides the default announce interval, and no + // announce will take place until the given time has + // timed out. + TORRENT_DEPRECATED_PREFIX + void force_reannounce(boost::posix_time::time_duration) const TORRENT_DEPRECATED; +#endif + + // ``scrape_tracker()`` will send a scrape request to the tracker. A + // scrape request queries the tracker for statistics such as total number + // of incomplete peers, complete peers, number of downloads etc. + // + // This request will specifically update the ``num_complete`` and + // ``num_incomplete`` fields in the torrent_status struct once it + // completes. When it completes, it will generate a scrape_reply_alert. + // If it fails, it will generate a scrape_failed_alert. + void scrape_tracker() const; + + // ``set_upload_limit`` will limit the upload bandwidth used by this + // particular torrent to the limit you set. It is given as the number of + // bytes per second the torrent is allowed to upload. + // ``set_download_limit`` works the same way but for download bandwidth + // instead of upload bandwidth. Note that setting a higher limit on a + // torrent then the global limit + // (``session_settings::upload_rate_limit``) will not override the global + // rate limit. The torrent can never upload more than the global rate + // limit. + // + // ``upload_limit`` and ``download_limit`` will return the current limit + // setting, for upload and download, respectively. + void set_upload_limit(int limit) const; + int upload_limit() const; + void set_download_limit(int limit) const; + int download_limit() const; + + // ``set_sequential_download()`` enables or disables *sequential + // download*. When enabled, the piece picker will pick pieces in sequence + // instead of rarest first. In this mode, piece priorities are ignored, + // with the exception of priority 7, which are still preferred over the + // sequential piece order. + // + // Enabling sequential download will affect the piece distribution + // negatively in the swarm. It should be used sparingly. + void set_sequential_download(bool sd) const; + + // ``connect_peer()`` is a way to manually connect to peers that one + // believe is a part of the torrent. If the peer does not respond, or is + // not a member of this torrent, it will simply be disconnected. No harm + // can be done by using this other than an unnecessary connection attempt + // is made. If the torrent is uninitialized or in queued or checking + // mode, this will throw libtorrent_exception. The second (optional) + // argument will be bitwised ORed into the source mask of this peer. + // Typically this is one of the source flags in peer_info. i.e. + // ``tracker``, ``pex``, ``dht`` etc. + void connect_peer(tcp::endpoint const& adr, int source = 0) const; + + // ``set_max_uploads()`` sets the maximum number of peers that's unchoked + // at the same time on this torrent. If you set this to -1, there will be + // no limit. This defaults to infinite. The primary setting controlling + // this is the global unchoke slots limit, set by unchoke_slots_limit in + // session_settings. + // + // ``max_uploads()`` returns the current settings. + void set_max_uploads(int max_uploads) const; + int max_uploads() const; + + // ``set_max_connections()`` sets the maximum number of connection this + // torrent will open. If all connections are used up, incoming + // connections may be refused or poor connections may be closed. This + // must be at least 2. The default is unlimited number of connections. If + // -1 is given to the function, it means unlimited. There is also a + // global limit of the number of connections, set by + // ``connections_limit`` in session_settings. + // + // ``max_connections()`` returns the current settings. + void set_max_connections(int max_connections) const; + int max_connections() const; + + // sets a username and password that will be sent along in the HTTP-request + // of the tracker announce. Set this if the tracker requires authorization. + void set_tracker_login(std::string const& name + , std::string const& password) const; + + // Moves the file(s) that this torrent are currently seeding from or + // downloading to. If the given ``save_path`` is not located on the same + // drive as the original save path, the files will be copied to the new + // drive and removed from their original location. This will block all + // other disk IO, and other torrents download and upload rates may drop + // while copying the file. + // + // Since disk IO is performed in a separate thread, this operation is + // also asynchronous. Once the operation completes, the + // ``storage_moved_alert`` is generated, with the new path as the + // message. If the move fails for some reason, + // ``storage_moved_failed_alert`` is generated instead, containing the + // error message. + // + // The ``flags`` argument determines the behavior of the copying/moving + // of the files in the torrent. see move_flags_t. + // + // * always_replace_files = 0 + // * fail_if_exist = 1 + // * dont_replace = 2 + // + // ``always_replace_files`` is the default and replaces any file that + // exist in both the source directory and the target directory. + // + // ``fail_if_exist`` first check to see that none of the copy operations + // would cause an overwrite. If it would, it will fail. Otherwise it will + // proceed as if it was in ``always_replace_files`` mode. Note that there + // is an inherent race condition here. If the files in the target + // directory appear after the check but before the copy or move + // completes, they will be overwritten. When failing because of files + // already existing in the target path, the ``error`` of + // ``move_storage_failed_alert`` is set to + // ``boost::system::errc::file_exists``. + // + // The intention is that a client may use this as a probe, and if it + // fails, ask the user which mode to use. The client may then re-issue + // the ``move_storage`` call with one of the other modes. + // + // ``dont_replace`` always takes the existing file in the target + // directory, if there is one. The source files will still be removed in + // that case. + // + // Files that have been renamed to have absolute pahts are not moved by + // this function. Keep in mind that files that don't belong to the + // torrent but are stored in the torrent's directory may be moved as + // well. This goes for files that have been renamed to absolute paths + // that still end up inside the save path. + void move_storage(std::string const& save_path, int flags = 0) const; + + // Renames the file with the given index asynchronously. The rename + // operation is complete when either a file_renamed_alert or + // file_rename_failed_alert is posted. + void rename_file(int index, std::string const& new_name) const; + +#ifndef TORRENT_NO_DEPRECATE +#if TORRENT_USE_WSTRING + // all wstring APIs are deprecated since 0.16.11 + // instead, use the wchar -> utf8 conversion functions + // and pass in utf8 strings + TORRENT_DEPRECATED_PREFIX + void move_storage(std::wstring const& save_path, int flags = 0) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void rename_file(int index, std::wstring const& new_name) const TORRENT_DEPRECATED; +#endif // TORRENT_USE_WSTRING +#endif // TORRENT_NO_DEPRECATE + + // Enables or disabled super seeding/initial seeding for this torrent. + // The torrent needs to be a seed for this to take effect. + void super_seeding(bool on) const; + + // ``info_hash()`` returns the info-hash of the torrent. If this handle + // is to a torrent that hasn't loaded yet (for instance by being added) + // by a URL, the returned value is undefined. + sha1_hash info_hash() const; + + // comparison operators. The order of the torrents is unspecified + // but stable. + bool operator==(const torrent_handle& h) const + { return m_torrent.lock() == h.m_torrent.lock(); } + bool operator!=(const torrent_handle& h) const + { return m_torrent.lock() != h.m_torrent.lock(); } + bool operator<(const torrent_handle& h) const + { return m_torrent.lock() < h.m_torrent.lock(); } + + // This function is intended only for use by plugins and the alert + // dispatch function. Any code that runs in libtorrent's network thread + // may not use the public API of torrent_handle. Doing so results in a + // dead-lock. For such routines, the ``native_handle`` gives access to + // the underlying type representing the torrent. This type does not have + // a stable API and should be relied on as little as possible. + boost::shared_ptr native_handle() const; + + private: + + torrent_handle(boost::weak_ptr const& t) + : m_torrent(t) + {} + + boost::weak_ptr m_torrent; + + }; + + // holds a snapshot of the status of a torrent, as queried by + // torrent_handle::status(). + struct TORRENT_EXPORT torrent_status + { + // hidden + torrent_status(); + ~torrent_status(); + + // compres if the torrent status objects come from the same torrent. i.e. + // only the torrent_handle field is compared. + bool operator==(torrent_status const& st) const + { return handle == st.handle; } + + // a handle to the torrent whose status the object represents. + torrent_handle handle; + + // the different overall states a torrent can be in + enum state_t + { + // The torrent is in the queue for being checked. But there + // currently is another torrent that are being checked. + // This torrent will wait for its turn. + queued_for_checking, + + // The torrent has not started its download yet, and is + // currently checking existing files. + checking_files, + + // The torrent is trying to download metadata from peers. + // This assumes the metadata_transfer extension is in use. + downloading_metadata, + + // The torrent is being downloaded. This is the state + // most torrents will be in most of the time. The progress + // meter will tell how much of the files that has been + // downloaded. + downloading, + + // In this state the torrent has finished downloading but + // still doesn't have the entire torrent. i.e. some pieces + // are filtered and won't get downloaded. + finished, + + // In this state the torrent has finished downloading and + // is a pure seeder. + seeding, + + // If the torrent was started in full allocation mode, this + // indicates that the (disk) storage for the torrent is + // allocated. + allocating, + + // The torrent is currently checking the fastresume data and + // comparing it to the files on disk. This is typically + // completed in a fraction of a second, but if you add a + // large number of torrents at once, they will queue up. + checking_resume_data + }; + + // may be set to an error message describing why the torrent + // was paused, in case it was paused by an error. If the torrent + // is not paused or if it's paused but not because of an error, + // this string is empty. + std::string error; + + // the path to the directory where this torrent's files are stored. + // It's typically the path as was given to async_add_torrent() or + // add_torrent() when this torrent was started. This field is only + // included if the torrent status is queried with + // ``torrent_handle::query_save_path``. + std::string save_path; + + // the name of the torrent. Typically this is derived from the + // .torrent file. In case the torrent was started without metadata, + // and hasn't completely received it yet, it returns the name given + // to it when added to the session. See ``session::add_torrent``. + // This field is only included if the torrent status is queried + // with ``torrent_handle::query_name``. + std::string name; + + // set to point to the ``torrent_info`` object for this torrent. It's + // only included if the torrent status is queried with + // ``torrent_handle::query_torrent_file``. + boost::intrusive_ptr torrent_file; + + // the time until the torrent will announce itself to the tracker. + boost::posix_time::time_duration next_announce; + + // the time the tracker want us to wait until we announce ourself + // again the next time. + boost::posix_time::time_duration announce_interval; + + // the URL of the last working tracker. If no tracker request has + // been successful yet, it's set to an empty string. + std::string current_tracker; + + // the number of bytes downloaded and uploaded to all peers, accumulated, + // *this session* only. The session is considered to restart when a + // torrent is paused and restarted again. When a torrent is paused, these + // counters are reset to 0. If you want complete, persistent, stats, see + // ``all_time_upload`` and ``all_time_download``. + size_type total_download; + size_type total_upload; + + // counts the amount of bytes send and received this session, but only + // the actual payload data (i.e the interesting data), these counters + // ignore any protocol overhead. The session is considered to restart + // when a torrent is paused and restarted again. When a torrent is + // paused, these counters are reset to 0. + size_type total_payload_download; + size_type total_payload_upload; + + // the number of bytes that has been downloaded and that has failed the + // piece hash test. In other words, this is just how much crap that has + // been downloaded since the torrent was last started. If a torrent is + // paused and then restarted again, this counter will be reset. + size_type total_failed_bytes; + + // the number of bytes that has been downloaded even though that data + // already was downloaded. The reason for this is that in some situations + // the same data can be downloaded by mistake. When libtorrent sends + // requests to a peer, and the peer doesn't send a response within a + // certain timeout, libtorrent will re-request that block. Another + // situation when libtorrent may re-request blocks is when the requests + // it sends out are not replied in FIFO-order (it will re-request blocks + // that are skipped by an out of order block). This is supposed to be as + // low as possible. This only counts bytes since the torrent was last + // started. If a torrent is paused and then restarted again, this counter + // will be reset. + size_type total_redundant_bytes; + + // a bitmask that represents which pieces we have (set to true) and the + // pieces we don't have. It's a pointer and may be set to 0 if the + // torrent isn't downloading or seeding. + bitfield pieces; + + // a bitmask representing which pieces has had their hash checked. This + // only applies to torrents in *seed mode*. If the torrent is not in seed + // mode, this bitmask may be empty. + bitfield verified_pieces; + + // the total number of bytes of the file(s) that we have. All this does + // not necessarily has to be downloaded during this session (that's + // ``total_payload_download``). + size_type total_done; + + // the number of bytes we have downloaded, only counting the pieces that + // we actually want to download. i.e. excluding any pieces that we have + // but have priority 0 (i.e. not wanted). + size_type total_wanted_done; + + // The total number of bytes we want to download. This may be smaller + // than the total torrent size in case any pieces are prioritized to 0, + // i.e. not wanted + size_type total_wanted; + + // are accumulated upload and download payload byte counters. They are + // saved in and restored from resume data to keep totals across sessions. + size_type all_time_upload; + size_type all_time_download; + + // the posix-time when this torrent was added. i.e. what ``time(NULL)`` + // returned at the time. + time_t added_time; + + // the posix-time when this torrent was finished. If the torrent is not + // yet finished, this is 0. + time_t completed_time; + + // the time when we, or one of our peers, last saw a complete copy of + // this torrent. + time_t last_seen_complete; + + // The allocation mode for the torrent. See storage_mode_t for the + // options. For more information, see storage-allocation_. + storage_mode_t storage_mode; + + // a value in the range [0, 1], that represents the progress of the + // torrent's current task. It may be checking files or downloading. + float progress; + + // progress parts per million (progress * 1000000) when disabling + // floating point operations, this is the only option to query progress + + // reflects the same value as ``progress``, but instead in a range [0, + // 1000000] (ppm = parts per million). When floating point operations are + // disabled, this is the only alternative to the floating point value in + // progress. + int progress_ppm; + + // the position this torrent has in the download + // queue. If the torrent is a seed or finished, this is -1. + int queue_position; + + // the total rates for all peers for this torrent. These will usually + // have better precision than summing the rates from all peers. The rates + // are given as the number of bytes per second. + int download_rate; + int upload_rate; + + // the total transfer rate of payload only, not counting protocol + // chatter. This might be slightly smaller than the other rates, but if + // projected over a long time (e.g. when calculating ETA:s) the + // difference may be noticeable. + int download_payload_rate; + int upload_payload_rate; + + // the number of peers that are seeding that this client is + // currently connected to. + int num_seeds; + + // the number of peers this torrent currently is connected to. Peer + // connections that are in the half-open state (is attempting to connect) + // or are queued for later connection attempt do not count. Although they + // are visible in the peer list when you call get_peer_info(). + int num_peers; + + // if the tracker sends scrape info in its announce reply, these fields + // will be set to the total number of peers that have the whole file and + // the total number of peers that are still downloading. set to -1 if the + // tracker did not send any scrape data in its announce reply. + int num_complete; + int num_incomplete; + + // the number of seeds in our peer list and the total number of peers + // (including seeds). We are not necessarily connected to all the peers + // in our peer list. This is the number of peers we know of in total, + // including banned peers and peers that we have failed to connect to. + int list_seeds; + int list_peers; + + // the number of peers in this torrent's peer list that is a candidate to + // be connected to. i.e. It has fewer connect attempts than the max fail + // count, it is not a seed if we are a seed, it is not banned etc. If + // this is 0, it means we don't know of any more peers that we can try. + int connect_candidates; + + // the number of pieces that has been downloaded. It is equivalent to: + // ``std::accumulate(pieces->begin(), pieces->end())``. So you don't have + // to count yourself. This can be used to see if anything has updated + // since last time if you want to keep a graph of the pieces up to date. + int num_pieces; + + // the number of distributed copies of the torrent. Note that one copy + // may be spread out among many peers. It tells how many copies there are + // currently of the rarest piece(s) among the peers this client is + // connected to. + int distributed_full_copies; + + // tells the share of pieces that have more copies than the rarest + // piece(s). Divide this number by 1000 to get the fraction. + // + // For example, if ``distributed_full_copies`` is 2 and + // ``distrbuted_fraction`` is 500, it means that the rarest pieces have + // only 2 copies among the peers this torrent is connected to, and that + // 50% of all the pieces have more than two copies. + // + // If we are a seed, the piece picker is deallocated as an optimization, + // and piece availability is no longer tracked. In this case the + // distributed copies members are set to -1. + int distributed_fraction; + + // the number of distributed copies of the file. note that one copy may + // be spread out among many peers. This is a floating point + // representation of the distributed copies. + // + // the integer part tells how many copies + // there are of the rarest piece(s) + // + // the fractional part tells the fraction of pieces that + // have more copies than the rarest piece(s). + float distributed_copies; + + // the size of a block, in bytes. A block is a sub piece, it is the + // number of bytes that each piece request asks for and the number of + // bytes that each bit in the ``partial_piece_info``'s bitset represents, + // see get_download_queue(). This is typically 16 kB, but it may be + // larger if the pieces are larger. + int block_size; + + // the number of unchoked peers in this torrent. + int num_uploads; + + // the number of peer connections this torrent has, including half-open + // connections that hasn't completed the bittorrent handshake yet. This + // is always >= ``num_peers``. + int num_connections; + + // the set limit of upload slots (unchoked peers) for this torrent. + int uploads_limit; + + // the set limit of number of connections for this torrent. + int connections_limit; + + // the number of peers in this torrent that are waiting for more + // bandwidth quota from the torrent rate limiter. This can determine if + // the rate you get from this torrent is bound by the torrents limit or + // not. If there is no limit set on this torrent, the peers might still + // be waiting for bandwidth quota from the global limiter, but then they + // are counted in the ``session_status`` object. + int up_bandwidth_queue; + int down_bandwidth_queue; + + // the number of seconds since any peer last uploaded from this torrent + // and the last time a downloaded piece passed the hash check, + // respectively. + int time_since_upload; + int time_since_download; + + // These keep track of the number of seconds this torrent has been active + // (not paused) and the number of seconds it has been active while being + // finished and active while being a seed. ``seeding_time`` should be <= + // ``finished_time`` which should be <= ``active_time``. They are all + // saved in and restored from resume data, to keep totals across + // sessions. + int active_time; + int finished_time; + int seeding_time; + + // A rank of how important it is to seed the torrent, it is used to + // determine which torrents to seed and which to queue. It is based on + // the peer to seed ratio from the tracker scrape. For more information, + // see queuing_. Higher value means more important to seed + int seed_rank; + + // the number of seconds since this torrent acquired scrape data. + // If it has never done that, this value is -1. + int last_scrape; + + // the number of regions of non-downloaded pieces in the torrent. This is + // an interesting metric on windows vista, since there is a limit on the + // number of sparse regions in a single file there. + int sparse_regions; + + // the priority of this torrent + int priority; + + // the main state the torrent is in. See torrent_status::state_t. + state_t state; + + // true if this torrent has unsaved changes + // to its download state and statistics since the last resume data + // was saved. + bool need_save_resume; + + // true if the session global IP filter applies + // to this torrent. This defaults to true. + bool ip_filter_applies; + + // true if the torrent is blocked from downloading. This typically + // happens when a disk write operation fails. If the torrent is + // auto-managed, it will periodically be taken out of this state, in the + // hope that the disk condition (be it disk full or permission errors) + // has been resolved. If the torrent is not auto-managed, you have to + // explicitly take it out of the upload mode by calling set_upload_mode() + // on the torrent_handle. + bool upload_mode; + + // true if the torrent is currently in share-mode, i.e. not downloading + // the torrent, but just helping the swarm out. + bool share_mode; + + // true if the torrent is in super seeding mode + bool super_seeding; + + // set to true if the torrent is paused and false otherwise. It's only + // true if the torrent itself is paused. If the torrent is not running + // because the session is paused, this is still false. To know if a + // torrent is active or not, you need to inspect both + // ``torrent_status::paused`` and ``session::is_paused()``. + bool paused; + + // set to true if the torrent is auto managed, i.e. libtorrent is + // responsible for determining whether it should be started or queued. + // For more info see queuing_ + bool auto_managed; + + // true when the torrent is in sequential download mode. In this mode + // pieces are downloaded in order rather than rarest first. + bool sequential_download; + + // true if all pieces have been downloaded. + bool is_seeding; + + // true if all pieces that have a priority > 0 are downloaded. There is + // only a distinction between finished and seeding if some pieces or + // files have been set to priority 0, i.e. are not downloaded. + bool is_finished; + + // true if this torrent has metadata (either it was started from a + // .torrent file or the metadata has been downloaded). The only scenario + // where this can be false is when the torrent was started torrent-less + // (i.e. with just an info-hash and tracker ip, a magnet link for + // instance). + bool has_metadata; + + // true if there has ever been an incoming connection attempt to this + // torrent. + bool has_incoming; + + // true if the torrent is in seed_mode. If the torrent was started in + // seed mode, it will leave seed mode once all pieces have been checked + // or as soon as one piece fails the hash check. + bool seed_mode; + + // this is true if this torrent's storage is currently being moved from + // one location to another. This may potentially be a long operation + // if a large file ends up being copied from one drive to another. + bool moving_storage; + + // the info-hash for this torrent + sha1_hash info_hash; + }; + +} + +#endif // TORRENT_TORRENT_HANDLE_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/torrent_info.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/torrent_info.hpp new file mode 100644 index 0000000000..5ad3e38d8a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/torrent_info.hpp @@ -0,0 +1,763 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_TORRENT_INFO_HPP_INCLUDED +#define TORRENT_TORRENT_INFO_HPP_INCLUDED + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/lazy_entry.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/size_type.hpp" +#include "libtorrent/ptime.hpp" +#include "libtorrent/intrusive_ptr_base.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/file_storage.hpp" +#include "libtorrent/copy_ptr.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/policy.hpp" // for policy::peer + +namespace libtorrent +{ + class peer_connection; + struct session_settings; + + enum + { + // wait at least 5 seconds before retrying a failed tracker + tracker_retry_delay_min = 5 + // when tracker_failed_max trackers + // has failed, wait 60 minutes instead + , tracker_retry_delay_max = 60 * 60 + }; + + TORRENT_EXTRA_EXPORT int merkle_num_leafs(int); + TORRENT_EXTRA_EXPORT int merkle_num_nodes(int); + TORRENT_EXTRA_EXPORT int merkle_get_parent(int); + TORRENT_EXTRA_EXPORT int merkle_get_sibling(int); + TORRENT_EXTRA_EXPORT void trim_path_element(std::string& path_element); + + // this class holds information about one bittorrent tracker, as it + // relates to a specific torrent. + struct TORRENT_EXPORT announce_entry + { + // constructs a tracker announce entry with ``u`` as the URL. + announce_entry(std::string const& u); + announce_entry(); + ~announce_entry(); + + // tracker URL as it appeared in the torrent file + std::string url; + + // the current ``&trackerid=`` argument passed to the tracker. + // this is optional and is normally empty (in which case no + // trackerid is sent). + std::string trackerid; + + // if this tracker has returned an error or warning message + // that message is stored here + std::string message; + + // if this tracker failed the last time it was contacted + // this error code specifies what error occurred + error_code last_error; + + // returns the number of seconds to the next announce on + // this tracker. ``min_announce_in()`` returns the number of seconds until we are + // allowed to force another tracker update with this tracker. + // + // If the last time this tracker was contacted failed, ``last_error`` is the error + // code describing what error occurred. + int next_announce_in() const; + int min_announce_in() const; + + // the time of next tracker announce + ptime next_announce; + + // no announces before this time + ptime min_announce; + + // TODO: include the number of peers received from this tracker, at last announce + + // these are either -1 or the scrape information this tracker last responded with. *incomplete* is + // the current number of downloaders in the swarm, *complete* is the current number + // of seeds in the swarm and *downloaded* is the cumulative number of completed + // downloads of this torrent, since the beginning of time (from this tracker's point + // of view). + + // if this tracker has returned scrape data, these fields are filled + // in with valid numbers. Otherwise they are set to -1. + // the number of current downloaders + int scrape_incomplete; + int scrape_complete; + int scrape_downloaded; + + // the tier this tracker belongs to + boost::uint8_t tier; + + // the max number of failures to announce to this tracker in + // a row, before this tracker is not used anymore. 0 means unlimited + boost::uint8_t fail_limit; + + // the number of times in a row we have failed to announce to this + // tracker. + boost::uint8_t fails:7; + + // true while we're waiting for a response from the tracker. + bool updating:1; + + // flags for the source bitmask, each indicating where + // we heard about this tracker + enum tracker_source + { + // the tracker was part of the .torrent file + source_torrent = 1, + // the tracker was added programatically via the add_troacker()_ function + source_client = 2, + // the tracker was part of a magnet link + source_magnet_link = 4, + // the tracker was received from the swarm via tracker exchange + source_tex = 8 + }; + + // a bitmask specifying which sources we got this tracker from. + boost::uint8_t source:4; + + // set to true the first time we receive a valid response + // from this tracker. + bool verified:1; + + // set to true when we get a valid response from an announce + // with event=started. If it is set, we won't send start in the subsequent + // announces. + bool start_sent:1; + + // set to true when we send a event=completed. + bool complete_sent:1; + + // this is false the stats sent to this tracker will be 0 + bool send_stats:1; + + // reset announce counters and clears the started sent flag. + // The announce_entry will look like we've never talked to + // the tracker. + void reset() + { + start_sent = false; + next_announce = min_time(); + min_announce = min_time(); + } + + // updates the failure counter and time-outs for re-trying. + // This is called when the tracker announce fails. + void failed(session_settings const& sett, int retry_interval = 0); + +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 1.0 + TORRENT_DEPRECATED_PREFIX + bool will_announce(ptime now) const TORRENT_DEPRECATED + { + return now <= next_announce + && (fails < fail_limit || fail_limit == 0) + && !updating; + } +#endif + + // returns true if we can announec to this tracker now. + // The current time is passed in as ``now``. The ``is_seed`` + // argument is necessary because once we become a seed, we + // need to announce right away, even if the re-announce timer + // hasn't expired yet. + bool can_announce(ptime now, bool is_seed) const; + + // returns true if the last time we tried to announce to this + // tracker succeeded, or if we haven't tried yet. + bool is_working() const + { return fails == 0; } + + // trims whitespace characters from the beginning of the URL. + void trim(); + }; + + // the web_seed_entry holds information about a web seed (also known + // as URL seed or HTTP seed). It is essentially a URL with some state + // associated with it. For more information, see `BEP 17`_ and `BEP 19`_. + struct web_seed_entry + { + // http seeds are different from url seeds in the + // protocol they use. http seeds follows the original + // http seed spec. by John Hoffman + enum type_t { url_seed, http_seed }; + + typedef std::vector > headers_t; + + web_seed_entry(std::string const& url_, type_t type_ + , std::string const& auth_ = std::string() + , headers_t const& extra_headers_ = headers_t()); + + // URL and type comparison + bool operator==(web_seed_entry const& e) const + { return url == e.url && type == e.type; } + + // URL and type less-than comparison + bool operator<(web_seed_entry const& e) const + { + if (url < e.url) return true; + if (url > e.url) return false; + return type < e.type; + } + + // The URL of the web seed + std::string url; + + // The type of web seed (see type_t) + type_t type; + + // Optional authentication. If this is set, it's passed + // in as HTTP basic auth to the web seed. The format is: + // username:password. + std::string auth; + + // Any extra HTTP headers that need to be passed to the web seed + headers_t extra_headers; + + // if this is > now, we can't reconnect yet + ptime retry; + + // this is initialized to true, but if we discover the + // server not to support it, it's set to false, and we + // make larger requests. + bool supports_keepalive; + + // this indicates whether or not we're resolving the + // hostname of this URL + bool resolving; + + // if the user wanted to remove this while + // we were resolving it. In this case, we set + // the removed flag to true, to make the resolver + // callback remove it + bool removed; + + // if the hostname of the web seed has been resolved, + // this is its IP address + tcp::endpoint endpoint; + + // this is the peer_info field used for the + // connection, just to count hash failures + // it's also used to hold the peer_connection + // pointer, when the web seed is connected + policy::ipv4_peer peer_info; + + // if the web server doesn't support keepalive or a block request was + // interrupted, the block received so far is kept here for the next + // connection to pick up + peer_request restart_request; + std::vector restart_piece; + }; + +#ifndef BOOST_NO_EXCEPTIONS + // for backwards compatibility with 0.14 + typedef libtorrent_exception invalid_torrent_file; +#endif + + // This class represents the information stored in a .torrent file + class TORRENT_EXPORT torrent_info : public intrusive_ptr_base + { + public: + + // The constructor that takes an info-hash will initialize the info-hash + // to the given value, but leave all other fields empty. This is used + // internally when downloading torrents without the metadata. The + // metadata will be created by libtorrent as soon as it has been + // downloaded from the swarm. + // + // The constructor that takes a lazy_entry will create a torrent_info + // object from the information found in the given torrent_file. The + // lazy_entry represents a tree node in an bencoded file. To load an + // ordinary .torrent file into a lazy_entry, use lazy_bdecode(). + // + // The version that takes a buffer pointer and a size will decode it as a + // .torrent file and initialize the torrent_info object for you. + // + // The version that takes a filename will simply load the torrent file + // and decode it inside the constructor, for convenience. This might not + // be the most suitable for applications that want to be able to report + // detailed errors on what might go wrong. + // + // There is an upper limit on the size of the torrent file that will be + // loaded by the overload taking a filename. If it's important that even + // very large torrent files are loaded, use one of the other overloads. + // + // The overloads that takes an ``error_code const&`` never throws if an + // error occur, they will simply set the error code to describe what went + // wrong and not fully initialize the torrent_info object. The overloads + // that do not take the extra error_code parameter will always throw if + // an error occurs. These overloads are not available when building + // without exception support. + // + // The ``flags`` argument is currently unused. +#ifndef BOOST_NO_EXCEPTIONS + torrent_info(lazy_entry const& torrent_file, int flags = 0); + torrent_info(char const* buffer, int size, int flags = 0); + torrent_info(std::string const& filename, int flags = 0); +#ifndef TORRENT_NO_DEPRECATE +#if TORRENT_USE_WSTRING + // all wstring APIs are deprecated since 0.16.11 + // instead, use the wchar -> utf8 conversion functions + // and pass in utf8 strings + TORRENT_DEPRECATED_PREFIX + torrent_info(std::wstring const& filename, int flags = 0) TORRENT_DEPRECATED; +#endif // TORRENT_USE_WSTRING +#endif // TORRENT_NO_DEPRECATE +#endif + torrent_info(torrent_info const& t, int flags = 0); + torrent_info(sha1_hash const& info_hash, int flags = 0); + torrent_info(lazy_entry const& torrent_file, error_code& ec, int flags = 0); + torrent_info(char const* buffer, int size, error_code& ec, int flags = 0); + torrent_info(std::string const& filename, error_code& ec, int flags = 0); +#ifndef TORRENT_NO_DEPRECATE +#if TORRENT_USE_WSTRING + // all wstring APIs are deprecated since 0.16.11 + // instead, use the wchar -> utf8 conversion functions + // and pass in utf8 strings + TORRENT_DEPRECATED_PREFIX + torrent_info(std::wstring const& filename, error_code& ec, int flags = 0) TORRENT_DEPRECATED; +#endif // TORRENT_USE_WSTRING +#endif // TORRENT_NO_DEPRECATE + + // frees all storage associated with this torrent_info object + ~torrent_info(); + + // The file_storage object contains the information on how to map the pieces to + // files. It is separated from the torrent_info object because when creating torrents + // a storage object needs to be created without having a torrent file. When renaming files + // in a storage, the storage needs to make its own copy of the file_storage in order + // to make its mapping differ from the one in the torrent file. + // + // ``orig_files()`` returns the original (unmodified) file storage for this torrent. This + // is used by the web server connection, which needs to request files with the original + // names. Filename may be chaged using ``torrent_info::rename_file()``. + // + // For more information on the file_storage object, see the separate document on how + // to create torrents. + file_storage const& files() const { return m_files; } + file_storage const& orig_files() const { return m_orig_files ? *m_orig_files : m_files; } + + // Renames a the file with the specified index to the new name. The new filename is + // reflected by the ``file_storage`` returned by ``files()`` but not by the one + // returned by ``orig_files()``. + // + // If you want to rename the base name of the torrent (for a multifile torrent), you + // can copy the ``file_storage`` (see files() and orig_files() ), change the name, and + // then use `remap_files()`_. + // + // The ``new_filename`` can both be a relative path, in which case the file name + // is relative to the ``save_path`` of the torrent. If the ``new_filename`` is + // an absolute path (i.e. ``is_complete(new_filename) == true``), then the file + // is detached from the ``save_path`` of the torrent. In this case the file is + // not moved when move_storage() is invoked. + void rename_file(int index, std::string const& new_filename) + { + copy_on_write(); + m_files.rename_file(index, new_filename); + } +#ifndef TORRENT_NO_DEPRECATE +#if TORRENT_USE_WSTRING + // all wstring APIs are deprecated since 0.16.11 + // instead, use the wchar -> utf8 conversion functions + // and pass in utf8 strings + TORRENT_DEPRECATED_PREFIX + void rename_file(int index, std::wstring const& new_filename) TORRENT_DEPRECATED; +#endif // TORRENT_USE_WSTRING +#endif // TORRENT_NO_DEPRECATE + + // Remaps the file storage to a new file layout. This can be used to, for instance, + // download all data in a torrent to a single file, or to a number of fixed size + // sector aligned files, regardless of the number and sizes of the files in the torrent. + // + // The new specified ``file_storage`` must have the exact same size as the current one. + void remap_files(file_storage const& f); + + // ``add_tracker()`` adds a tracker to the announce-list. The ``tier`` determines the order in + // which the trackers are to be tried. + // + // The ``trackers()`` function will return a sorted vector of ``announce_entry``. + // Each announce entry contains a string, which is the tracker url, and a tier index. The + // tier index is the high-level priority. No matter which trackers that works or not, the + // ones with lower tier will always be tried before the one with higher tier number. + // For more information, see announce_entry_. + void add_tracker(std::string const& url, int tier = 0); + std::vector const& trackers() const { return m_urls; } + +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 0.16. Use web_seeds() instead + TORRENT_DEPRECATED_PREFIX + std::vector url_seeds() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + std::vector http_seeds() const TORRENT_DEPRECATED; +#endif // TORRENT_NO_DEPRECATE + + // ``web_seeds()`` returns all url seeds and http seeds in the torrent. Each entry + // is a ``web_seed_entry`` and may refer to either a url seed or http seed. + // + // ``add_url_seed()`` and ``add_http_seed()`` adds one url to the list of + // url/http seeds. Currently, the only transport protocol supported for the url + // is http. + // + // The ``extern_auth`` argument can be used for other athorization schemese than + // basic HTTP authorization. If set, it will override any username and password + // found in the URL itself. The string will be sent as the HTTP authorization header's + // value (without specifying "Basic"). + // + // The ``extra_headers`` argument defaults to an empty list, but can be used to + // insert custom HTTP headers in the requests to a specific web seed. + // + // See http-seeding_ for more information. + void add_url_seed(std::string const& url + , std::string const& extern_auth = std::string() + , web_seed_entry::headers_t const& extra_headers = web_seed_entry::headers_t()); + void add_http_seed(std::string const& url + , std::string const& extern_auth = std::string() + , web_seed_entry::headers_t const& extra_headers = web_seed_entry::headers_t()); + std::vector const& web_seeds() const + { return m_web_seeds; } + + // ``total_size()``, ``piece_length()`` and ``num_pieces()`` returns the total + // number of bytes the torrent-file represents (all the files in it), the number of byte for + // each piece and the total number of pieces, respectively. The difference between + // ``piece_size()`` and ``piece_length()`` is that ``piece_size()`` takes + // the piece index as argument and gives you the exact size of that piece. It will always + // be the same as ``piece_length()`` except in the case of the last piece, which may + // be smaller. + size_type total_size() const { return m_files.total_size(); } + int piece_length() const { return m_files.piece_length(); } + int num_pieces() const { return m_files.num_pieces(); } + + // returns the info-hash of the torrent + const sha1_hash& info_hash() const { return m_info_hash; } + +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 1.0. Use the variants that take an index instead + // internal_file_entry is no longer exposed in the API + typedef file_storage::iterator file_iterator; + typedef file_storage::reverse_iterator reverse_file_iterator; + + // This class will need some explanation. First of all, to get a list of all files + // in the torrent, you can use ``begin_files()``, ``end_files()``, + // ``rbegin_files()`` and ``rend_files()``. These will give you standard vector + // iterators with the type ``internal_file_entry``, which is an internal type. + // + // You can resolve it into the public representation of a file (``file_entry``) + // using the ``file_storage::at`` function, which takes an index and an iterator. + TORRENT_DEPRECATED_PREFIX + file_iterator begin_files() const TORRENT_DEPRECATED { return m_files.begin_deprecated(); } + TORRENT_DEPRECATED_PREFIX + file_iterator end_files() const TORRENT_DEPRECATED { return m_files.end_deprecated(); } + reverse_file_iterator rbegin_files() const TORRENT_DEPRECATED { return m_files.rbegin_deprecated(); } + TORRENT_DEPRECATED_PREFIX + reverse_file_iterator rend_files() const TORRENT_DEPRECATED { return m_files.rend_deprecated(); } + + TORRENT_DEPRECATED_PREFIX + file_iterator file_at_offset(size_type offset) const TORRENT_DEPRECATED + { return m_files.file_at_offset_deprecated(offset); } + +#endif // TORRENT_NO_DEPRECATE + + // If you need index-access to files you can use the ``num_files()`` and ``file_at()`` + // to access files using indices. + int num_files() const { return m_files.num_files(); } + file_entry file_at(int index) const { return m_files.at(index); } + + // This function will map a piece index, a byte offset within that piece and + // a size (in bytes) into the corresponding files with offsets where that data + // for that piece is supposed to be stored. See file_slice. + std::vector map_block(int piece, size_type offset, int size) const + { return m_files.map_block(piece, offset, size); } + + // This function will map a range in a specific file into a range in the torrent. + // The ``file_offset`` parameter is the offset in the file, given in bytes, where + // 0 is the start of the file. See peer_request. + // + // The input range is assumed to be valid within the torrent. ``file_offset`` + // + ``size`` is not allowed to be greater than the file size. ``file_index`` + // must refer to a valid file, i.e. it cannot be >= ``num_files()``. + peer_request map_file(int file, size_type offset, int size) const + { return m_files.map_file(file, offset, size); } + +#ifndef TORRENT_NO_DEPRECATE +// ------- start deprecation ------- +// these functions will be removed in a future version + TORRENT_DEPRECATED_PREFIX + torrent_info(entry const& torrent_file) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void print(std::ostream& os) const TORRENT_DEPRECATED; +// ------- end deprecation ------- +#endif + + // Returns the SSL root certificate for the torrent, if it is an SSL + // torrent. Otherwise returns an empty string. The certificate is + // the the public certificate in x509 format. + std::string ssl_cert() const; + + // returns true if this torrent_info object has a torrent loaded. + // This is primarily used to determine if a magnet link has had its + // metadata resolved yet or not. + bool is_valid() const { return m_files.is_valid(); } + + // returns true if this torrent is private. i.e., it should not be + // distributed on the trackerless network (the kademlia DHT). + bool priv() const { return m_private; } + + // returns true if this is an i2p torrent. This is determined by whether + // or not it has a tracker whose URL domain name ends with ".i2p". i2p + // torrents disable the DHT and local peer discovery as well as talking + // to peers over anything other than the i2p network. + bool is_i2p() const { return m_i2p; } + + // ``hash_for_piece()`` takes a piece-index and returns the 20-bytes sha1-hash for that + // piece and ``info_hash()`` returns the 20-bytes sha1-hash for the info-section of the + // torrent file. + // ``hash_for_piece_ptr()`` returns a pointer to the 20 byte sha1 digest for the piece. + // Note that the string is not null-terminated. + int piece_size(int index) const { return m_files.piece_size(index); } + sha1_hash hash_for_piece(int index) const + { return sha1_hash(hash_for_piece_ptr(index)); } + char const* hash_for_piece_ptr(int index) const + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_files.num_pieces()); + if (is_merkle_torrent()) + { + TORRENT_ASSERT(index < int(m_merkle_tree.size() - m_merkle_first_leaf)); + return (const char*)&m_merkle_tree[m_merkle_first_leaf + index][0]; + } + else + { + TORRENT_ASSERT(m_piece_hashes); + TORRENT_ASSERT(m_piece_hashes >= m_info_section.get()); + TORRENT_ASSERT(m_piece_hashes < m_info_section.get() + m_info_section_size); + TORRENT_ASSERT(index < int(m_info_section_size / 20)); + return &m_piece_hashes[index*20]; + } + } + + // ``merkle_tree()`` returns a reference to the merkle tree for this torrent, if any. + // + // ``set_merkle_tree()`` moves the passed in merkle tree into the torrent_info object. + // i.e. ``h`` will not be identical after the call. You need to set the merkle tree for + // a torrent that you've just created (as a merkle torrent). The merkle tree is retrieved + // from the ``create_torrent::merkle_tree()`` function, and need to be saved separately + // from the torrent file itself. Once it's added to libtorrent, the merkle tree will be + // persisted in the resume data. + std::vector const& merkle_tree() const { return m_merkle_tree; } + void set_merkle_tree(std::vector& h) + { TORRENT_ASSERT(h.size() == m_merkle_tree.size() ); m_merkle_tree.swap(h); } + + // ``name()`` returns the name of the torrent. + // + // ``comment()`` returns the comment associated with the torrent. If there's no comment, + // it will return an empty string. ``creation_date()`` returns the creation date of + // the torrent as time_t (`posix time`_). If there's no time stamp in the torrent file, + // the optional object will be uninitialized. + // + // Both the name and the comment is UTF-8 encoded strings. + // + // ``creator()`` returns the creator string in the torrent. If there is no creator string + // it will return an empty string. + // + // .. _`posix time`: http://www.opengroup.org/onlinepubs/009695399/functions/time.html + const std::string& name() const { return m_files.name(); } + boost::optional creation_date() const; + const std::string& creator() const + { return m_created_by; } + const std::string& comment() const + { return m_comment; } + + // dht nodes to add to the routing table/bootstrap from + typedef std::vector > nodes_t; + + // If this torrent contains any DHT nodes, they are put in this vector in their original + // form (host name and port number). + nodes_t const& nodes() const + { return m_nodes; } + + // This is used when creating torrent. Use this to add a known DHT node. It may + // be used, by the client, to bootstrap into the DHT network. + void add_node(std::pair const& node) + { m_nodes.push_back(node); } + + // populates the torrent_info by providing just the info-dict buffer. This is used when + // loading a torrent from a magnet link for instance, where we only have the info-dict. + // The lazy_entry ``e`` points to a parsed info-dictionary. ``ec`` returns an error code + // if something fails (typically if the info dictionary is malformed). ``flags`` are currently + // unused. + bool parse_info_section(lazy_entry const& e, error_code& ec, int flags); + + // This function looks up keys from the info-dictionary of the loaded torrent file. + // It can be used to access extension values put in the .torrent file. If the specified + // key cannot be found, it returns NULL. + lazy_entry const* info(char const* key) const + { + if (m_info_dict.type() == lazy_entry::none_t) + { + error_code ec; + lazy_bdecode(m_info_section.get(), m_info_section.get() + + m_info_section_size, m_info_dict, ec); + if (ec) return NULL; + } + return m_info_dict.dict_find(key); + } + + // swap the content of this and ``ti```. + void swap(torrent_info& ti); + + // ``metadata()`` returns a the raw info section of the torrent file. The size + // of the metadata is returned by ``metadata_size()``. + int metadata_size() const { return m_info_section_size; } + boost::shared_array metadata() const + { return m_info_section; } + + // internal + bool add_merkle_nodes(std::map const& subtree + , int piece); + std::map build_merkle_list(int piece) const; + + // returns whether or not this is a merkle torrent. + // see BEP30__. + // + // __ http://bittorrent.org/beps/bep_0030.html + bool is_merkle_torrent() const { return !m_merkle_tree.empty(); } + + // if we're logging member offsets, we need access to them + private: + +#if TORRENT_USE_INVARIANT_CHECKS + friend class invariant_access; + void check_invariant() const; +#endif + + // not assignable + torrent_info const& operator=(torrent_info const&); + + void copy_on_write(); + bool parse_torrent_file(lazy_entry const& libtorrent, error_code& ec, int flags); + + // the index to the first leaf. This is where the hash for the + // first piece is stored + boost::uint32_t m_merkle_first_leaf; + + file_storage m_files; + + // if m_files is modified, it is first copied into + // m_orig_files so that the original name and + // filenames are preserved. + copy_ptr m_orig_files; + + // the urls to the trackers + std::vector m_urls; + std::vector m_web_seeds; + nodes_t m_nodes; + + // if this is a merkle torrent, this is the merkle + // tree. It has space for merkle_num_nodes(merkle_num_leafs(num_pieces)) + // hashes + std::vector m_merkle_tree; + + // this is a copy of the info section from the torrent. + // it use maintained in this flat format in order to + // make it available through the metadata extension + boost::shared_array m_info_section; + + // this is a pointer into the m_info_section buffer + // pointing to the first byte of the first sha-1 hash + char const* m_piece_hashes; + + // if a comment is found in the torrent file + // this will be set to that comment + std::string m_comment; + + // an optional string naming the software used + // to create the torrent file + std::string m_created_by; + + // the info section parsed. points into m_info_section + // parsed lazily + mutable lazy_entry m_info_dict; + + // if a creation date is found in the torrent file + // this will be set to that, otherwise it'll be + // 1970, Jan 1 + time_t m_creation_date; + + // the hash that identifies this torrent + sha1_hash m_info_hash; + + // the number of bytes in m_info_section + boost::uint32_t m_info_section_size:24; + + // this is used when creating a torrent. If there's + // only one file there are cases where it's impossible + // to know if it should be written as a multifile torrent + // or not. e.g. test/test there's one file and one directory + // and they have the same name. + bool m_multifile:1; + + // this is true if the torrent is private. i.e., is should not + // be announced on the dht + bool m_private:1; + + // this is true if one of the trackers has an .i2p top + // domain in its hostname. This means the DHT and LSD + // features are disabled for this torrent (unless the + // settings allows mixing i2p peers with regular peers) + bool m_i2p:1; + }; + +} + +#endif // TORRENT_TORRENT_INFO_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/tracker_manager.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/tracker_manager.hpp new file mode 100644 index 0000000000..a48dab2160 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/tracker_manager.hpp @@ -0,0 +1,307 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_TRACKER_MANAGER_HPP_INCLUDED +#define TORRENT_TRACKER_MANAGER_HPP_INCLUDED + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/peer.hpp" // peer_entry +#include "libtorrent/session_settings.hpp" // proxy_settings +#include "libtorrent/deadline_timer.hpp" +#include "libtorrent/connection_queue.hpp" +#include "libtorrent/intrusive_ptr_base.hpp" +#include "libtorrent/size_type.hpp" +#include "libtorrent/union_endpoint.hpp" +#include "libtorrent/udp_socket.hpp" // for udp_socket_observer +#ifdef TORRENT_USE_OPENSSL +#include +#endif + +namespace libtorrent +{ + struct request_callback; + class tracker_manager; + struct timeout_handler; + struct tracker_connection; + namespace aux { struct session_impl; } + + // returns -1 if gzip header is invalid or the header size in bytes + TORRENT_EXTRA_EXPORT int gzip_header(const char* buf, int size); + + struct TORRENT_EXTRA_EXPORT tracker_request + { + tracker_request() + : kind(announce_request) + , downloaded(-1) + , uploaded(-1) + , left(-1) + , corrupt(0) + , redundant(0) + , listen_port(0) + , event(none) + , key(0) + , num_want(0) + , send_stats(true) + , apply_ip_filter(true) +#ifdef TORRENT_USE_OPENSSL + , ssl_ctx(0) +#endif + {} + + enum + { + announce_request, + scrape_request + } kind; + + enum event_t + { + none, + completed, + started, + stopped, + paused + }; + + sha1_hash info_hash; + peer_id pid; + size_type downloaded; + size_type uploaded; + size_type left; + size_type corrupt; + size_type redundant; + unsigned short listen_port; + event_t event; + std::string url; + std::string trackerid; + boost::uint32_t key; + int num_want; + address bind_ip; + bool send_stats; + bool apply_ip_filter; +#ifdef TORRENT_USE_OPENSSL + boost::asio::ssl::context* ssl_ctx; +#endif + }; + + struct TORRENT_EXTRA_EXPORT request_callback + { + friend class tracker_manager; + request_callback(): m_manager(0) {} + virtual ~request_callback() {} + virtual void tracker_warning(tracker_request const& req + , std::string const& msg) = 0; + virtual void tracker_scrape_response(tracker_request const& /*req*/ + , int /*complete*/, int /*incomplete*/, int /*downloads*/ + , int /*downloaders*/) {} + virtual void tracker_response( + tracker_request const& req + , address const& tracker_ip + , std::list
const& ip_list + , std::vector& peers + , int interval + , int min_interval + , int complete + , int incomplete + , int downloaded + , address const& external_ip + , std::string const& trackerid) = 0; + virtual void tracker_request_error( + tracker_request const& req + , int response_code + , error_code const& ec + , const std::string& msg + , int retry_interval) = 0; + + union_endpoint m_tracker_address; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + virtual void debug_log(const char* fmt, ...) const = 0; +#else + private: +#endif + tracker_manager* m_manager; + }; + + struct TORRENT_EXTRA_EXPORT timeout_handler + : intrusive_ptr_base + , boost::noncopyable + { + timeout_handler(io_service& str); + + void set_timeout(int completion_timeout, int read_timeout); + void restart_read_timeout(); + void cancel(); + bool cancelled() const { return m_abort; } + + virtual void on_timeout(error_code const& ec) = 0; + virtual ~timeout_handler() {} + + io_service& get_io_service() { return m_timeout.get_io_service(); } + + private: + + void timeout_callback(error_code const&); + + boost::intrusive_ptr self() + { return boost::intrusive_ptr(this); } + + // used for timeouts + // this is set when the request has been sent + ptime m_start_time; + // this is set every time something is received + ptime m_read_time; + // the asio async operation + deadline_timer m_timeout; + + int m_completion_timeout; + int m_read_timeout; + + typedef mutex mutex_t; + mutable mutex_t m_mutex; + bool m_abort; + }; + + struct TORRENT_EXTRA_EXPORT tracker_connection + : timeout_handler + { + tracker_connection(tracker_manager& man + , tracker_request const& req + , io_service& ios + , boost::weak_ptr r); + + boost::shared_ptr requester() const; + virtual ~tracker_connection() {} + + tracker_request const& tracker_req() const { return m_req; } + + void fail(error_code const& ec, int code = -1, char const* msg = "" + , int interval = 0, int min_interval = 0); + virtual void start() = 0; + virtual void close(); + address const& bind_interface() const { return m_req.bind_ip; } + void sent_bytes(int bytes); + void received_bytes(int bytes); + virtual bool on_receive(error_code const& ec, udp::endpoint const& ep + , char const* buf, int size) { return false; } + virtual bool on_receive_hostname(error_code const& ec, char const* hostname + , char const* buf, int size) { return false; } + + boost::intrusive_ptr self() + { return boost::intrusive_ptr(this); } + + protected: + + void fail_impl(error_code const& ec, int code = -1, std::string msg = std::string() + , int interval = 0, int min_interval = 0); + + boost::weak_ptr m_requester; + + tracker_manager& m_man; + + private: + + const tracker_request m_req; + }; + + class TORRENT_EXTRA_EXPORT tracker_manager: public udp_socket_observer, boost::noncopyable + { + public: + + tracker_manager(aux::session_impl& ses, proxy_settings const& ps) + : m_ses(ses) + , m_proxy(ps) + , m_abort(false) {} + ~tracker_manager(); + + void queue_request( + io_service& ios + , connection_queue& cc + , tracker_request r + , std::string const& auth + , boost::weak_ptr c + = boost::weak_ptr()); + void abort_all_requests(bool all = false); + + void remove_request(tracker_connection const*); + bool empty() const; + int num_requests() const; + + void sent_bytes(int bytes); + void received_bytes(int bytes); + + virtual bool incoming_packet(error_code const& e, udp::endpoint const& ep + , char const* buf, int size); + + // this is only used for SOCKS packets, since + // they may be addressed to hostname + virtual bool incoming_packet(error_code const& e, char const* hostname + , char const* buf, int size); + + private: + + typedef mutex mutex_t; + mutable mutex_t m_mutex; + + typedef std::list > + tracker_connections_t; + tracker_connections_t m_connections; + aux::session_impl& m_ses; + proxy_settings const& m_proxy; + bool m_abort; + }; +} + +#endif // TORRENT_TRACKER_MANAGER_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/udp_socket.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/udp_socket.hpp new file mode 100644 index 0000000000..2a448227ad --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/udp_socket.hpp @@ -0,0 +1,311 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UDP_SOCKET_HPP_INCLUDED +#define TORRENT_UDP_SOCKET_HPP_INCLUDED + +#include "libtorrent/socket.hpp" +#include "libtorrent/io_service.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/buffer.hpp" +#include "libtorrent/thread.hpp" +#include "libtorrent/deadline_timer.hpp" + +#include + +namespace libtorrent +{ + class connection_queue; + + struct udp_socket_observer + { + // return true if the packet was handled (it won't be + // propagated to the next observer) + virtual bool incoming_packet(error_code const& ec + , udp::endpoint const&, char const* buf, int size) = 0; + virtual bool incoming_packet(error_code const& ec + , char const* hostname, char const* buf, int size) { return false; } + + // called when the socket becomes writeable, after having + // failed with EWOULDBLOCK + virtual void writable() {} + + // called every time the socket is drained of packets + virtual void socket_drained() {} + }; + + class udp_socket + { + public: + udp_socket(io_service& ios, connection_queue& cc); + ~udp_socket(); + + enum flags_t { dont_drop = 1, peer_connection = 2, dont_queue = 4 }; + + bool is_open() const + { + return m_ipv4_sock.is_open() +#if TORRENT_USE_IPV6 + || m_ipv6_sock.is_open() +#endif + ; + } + io_service& get_io_service() { return m_ipv4_sock.get_io_service(); } + + void subscribe(udp_socket_observer* o); + void unsubscribe(udp_socket_observer* o); + + // this is only valid when using a socks5 proxy + void send_hostname(char const* hostname, int port, char const* p + , int len, error_code& ec, int flags = 0); + + void send(udp::endpoint const& ep, char const* p, int len + , error_code& ec, int flags = 0); + void bind(udp::endpoint const& ep, error_code& ec); + void close(); + int local_port() const { return m_bind_port; } + + void set_proxy_settings(proxy_settings const& ps); + proxy_settings const& get_proxy_settings() { return m_proxy_settings; } + void set_force_proxy(bool f) { m_force_proxy = f; } + + bool is_closed() const { return m_abort; } + tcp::endpoint local_endpoint(error_code& ec) const + { + udp::endpoint ep = m_ipv4_sock.local_endpoint(ec); + return tcp::endpoint(ep.address(), ep.port()); + } + + void set_buf_size(int s); + + template + void get_option(SocketOption const& opt, error_code& ec) + { + m_ipv4_sock.get_option(opt, ec); +#if TORRENT_USE_IPV6 + m_ipv6_sock.get_option(opt, ec); +#endif + } + + template + void set_option(SocketOption const& opt, error_code& ec) + { + m_ipv4_sock.set_option(opt, ec); +#if TORRENT_USE_IPV6 + m_ipv6_sock.set_option(opt, ec); +#endif + } + + template + void get_option(SocketOption& opt, error_code& ec) + { + m_ipv4_sock.get_option(opt, ec); + } + + udp::endpoint proxy_addr() const { return m_proxy_addr; } + + protected: + + struct queued_packet + { + udp::endpoint ep; + char* hostname; + buffer buf; + int flags; + }; + + // number of outstanding UDP socket operations + // using the UDP socket buffer + int num_outstanding() const + { + return m_v4_outstanding +#if TORRENT_USE_IPV6 + + m_v6_outstanding +#endif + ; + } + + private: + + // non-copyable + udp_socket(udp_socket const&); + udp_socket& operator=(udp_socket const&); + + // observers on this udp socket + std::vector m_observers; + std::vector m_added_observers; + + // this is true while iterating over the observers + // vector, invoking observer hooks. We may not + // add new observers during this time, since it + // may invalidate the iterator. If this is true, + // instead add new observers to m_added_observers + // and they will be added later + bool m_observers_locked; + + void call_handler(error_code const& ec, udp::endpoint const& ep + , char const* buf, int size); + void call_handler(error_code const& ec, const char* host + , char const* buf, int size); + void call_drained_handler(); + void call_writable_handler(); + + void on_writable(error_code const& ec, udp::socket* s); + + void setup_read(udp::socket* s); + void on_read(error_code const& ec, udp::socket* s); + void on_read_impl(udp::socket* sock, udp::endpoint const& ep + , error_code const& e, std::size_t bytes_transferred); + void on_name_lookup(error_code const& e, tcp::resolver::iterator i); + void on_timeout(); + void on_connect(int ticket); + void on_connected(error_code const& ec, int ticket); + void handshake1(error_code const& e); + void handshake2(error_code const& e); + void handshake3(error_code const& e); + void handshake4(error_code const& e); + void socks_forward_udp(); + void connect1(error_code const& e); + void connect2(error_code const& e); + void hung_up(error_code const& e); + + void drain_queue(); + + void wrap(udp::endpoint const& ep, char const* p, int len, error_code& ec); + void wrap(char const* hostname, int port, char const* p, int len, error_code& ec); + void unwrap(error_code const& e, char const* buf, int size); + +#if TORRENT_USE_ASSERTS + +#if defined BOOST_HAS_PTHREADS + mutable pthread_t m_thread; +#endif + bool is_single_thread() const + { +#if defined BOOST_HAS_PTHREADS + if (m_thread == 0) + m_thread = pthread_self(); + return m_thread == pthread_self(); +#endif + return true; + } +#endif + + udp::socket m_ipv4_sock; + int m_buf_size; + + // if the buffer size is attempted + // to be changed while the buffer is + // being used, this member is set to + // the desired size, and it's resized + // later + int m_new_buf_size; + char* m_buf; + +#if TORRENT_USE_IPV6 + udp::socket m_ipv6_sock; +#endif + + boost::uint16_t m_bind_port; + boost::uint8_t m_v4_outstanding; +#if TORRENT_USE_IPV6 + boost::uint8_t m_v6_outstanding; +#endif + + tcp::socket m_socks5_sock; + int m_connection_ticket; + proxy_settings m_proxy_settings; + connection_queue& m_cc; + tcp::resolver m_resolver; + char m_tmp_buf[270]; + bool m_queue_packets; + bool m_tunnel_packets; + bool m_force_proxy; + bool m_abort; + + // this is the endpoint the proxy server lives at. + // when performing a UDP associate, we get another + // endpoint (presumably on the same IP) where we're + // supposed to send UDP packets. + udp::endpoint m_proxy_addr; + + // this is where UDP packets that are to be forwarded + // are sent. The result from UDP ASSOCIATE is stored + // in here. + udp::endpoint m_udp_proxy_addr; + + // while we're connecting to the proxy + // we have to queue the packets, we'll flush + // them once we're connected + std::deque m_queue; + + // counts the number of outstanding async + // operations hanging on this socket + int m_outstanding_ops; + +#if TORRENT_USE_IPV6 + bool m_v6_write_subscribed:1; +#endif + bool m_v4_write_subscribed:1; + +#if TORRENT_USE_ASSERTS + bool m_started; + int m_magic; + int m_outstanding_when_aborted; + int m_outstanding_connect; + int m_outstanding_timeout; + int m_outstanding_resolve; + int m_outstanding_connect_queue; + int m_outstanding_socks; + + char timeout_stack[2000]; +#endif + }; + + struct rate_limited_udp_socket : public udp_socket + { + rate_limited_udp_socket(io_service& ios, connection_queue& cc); + void set_rate_limit(int limit) { m_rate_limit = limit; } + bool send(udp::endpoint const& ep, char const* p, int len + , error_code& ec, int flags = 0); + + private: + + int m_rate_limit; + int m_quota; + ptime m_last_tick; + }; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/udp_tracker_connection.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/udp_tracker_connection.hpp new file mode 100644 index 0000000000..adc2b89412 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/udp_tracker_connection.hpp @@ -0,0 +1,144 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UDP_TRACKER_CONNECTION_HPP_INCLUDED +#define TORRENT_UDP_TRACKER_CONNECTION_HPP_INCLUDED + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/udp_socket.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/peer.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + namespace aux { struct session_impl; } + + class TORRENT_EXTRA_EXPORT udp_tracker_connection: public tracker_connection + { + friend class tracker_manager; + public: + + udp_tracker_connection( + io_service& ios + , connection_queue& cc + , tracker_manager& man + , tracker_request const& req + , boost::weak_ptr c + , aux::session_impl& ses + , proxy_settings const& ps); + + void start(); + void close(); + + private: + + enum action_t + { + action_connect, + action_announce, + action_scrape, + action_error + }; + + boost::intrusive_ptr self() + { return boost::intrusive_ptr(this); } + + void name_lookup(error_code const& error, tcp::resolver::iterator i); + void timeout(error_code const& error); + void start_announce(); + + bool on_receive(error_code const& e, udp::endpoint const& ep + , char const* buf, int size); + bool on_receive_hostname(error_code const& e, char const* hostname + , char const* buf, int size); + bool on_connect_response(char const* buf, int size); + bool on_announce_response(char const* buf, int size); + bool on_scrape_response(char const* buf, int size); + + // wraps tracker_connection::fail + void fail(error_code const& ec, int code = -1 + , char const* msg = "", int interval = 0, int min_interval = 0); + + void send_udp_connect(); + void send_udp_announce(); + void send_udp_scrape(); + + virtual void on_timeout(error_code const& ec); + + udp::endpoint pick_target_endpoint() const; + + bool m_abort; + std::string m_hostname; + udp::endpoint m_target; + std::list m_endpoints; + + int m_transaction_id; + aux::session_impl& m_ses; + int m_attempts; + + struct connection_cache_entry + { + boost::int64_t connection_id; + ptime expires; + }; + + static std::map m_connection_cache; + static mutex m_cache_mutex; + + action_t m_state; + + proxy_settings m_proxy; + }; + +} + +#endif // TORRENT_UDP_TRACKER_CONNECTION_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/union_endpoint.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/union_endpoint.hpp new file mode 100644 index 0000000000..f611fd531a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/union_endpoint.hpp @@ -0,0 +1,134 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UNION_ENDPOINT_HPP_INCLUDED +#define TORRENT_UNION_ENDPOINT_HPP_INCLUDED + +#include "libtorrent/socket.hpp" +#include "libtorrent/address.hpp" + +namespace libtorrent +{ + + struct union_endpoint + { + union_endpoint(tcp::endpoint const& ep) + { + *this = ep; + } + + union_endpoint(udp::endpoint const& ep) + { + *this = ep; + } + + union_endpoint() + { + *this = tcp::endpoint(); + } + + union_endpoint& operator=(udp::endpoint const& ep) + { +#if TORRENT_USE_IPV6 + v4 = ep.address().is_v4(); + if (v4) + addr.v4 = ep.address().to_v4().to_bytes(); + else + addr.v6 = ep.address().to_v6().to_bytes(); +#else + addr.v4 = ep.address().to_v4().to_bytes(); +#endif + port = ep.port(); + return *this; + } + + operator udp::endpoint() const + { +#if TORRENT_USE_IPV6 + if (v4) return udp::endpoint(address_v4(addr.v4), port); + else return udp::endpoint(address_v6(addr.v6), port); +#else + return udp::endpoint(address_v4(addr.v4), port); +#endif + } + + union_endpoint& operator=(tcp::endpoint const& ep) + { +#if TORRENT_USE_IPV6 + v4 = ep.address().is_v4(); + if (v4) + addr.v4 = ep.address().to_v4().to_bytes(); + else + addr.v6 = ep.address().to_v6().to_bytes(); +#else + addr.v4 = ep.address().to_v4().to_bytes(); +#endif + port = ep.port(); + return *this; + } + + libtorrent::address address() const + { +#if TORRENT_USE_IPV6 + if (v4) return address_v4(addr.v4); + else return address_v6(addr.v6); +#else + return address_v4(addr.v4); +#endif + } + + operator tcp::endpoint() const + { +#if TORRENT_USE_IPV6 + if (v4) return tcp::endpoint(address_v4(addr.v4), port); + else return tcp::endpoint(address_v6(addr.v6), port); +#else + return tcp::endpoint(address_v4(addr.v4), port); +#endif + } + + TORRENT_UNION addr_t + { + address_v4::bytes_type v4; +#if TORRENT_USE_IPV6 + address_v6::bytes_type v6; +#endif + } addr; + boost::uint16_t port; +#if TORRENT_USE_IPV6 + bool v4:1; +#endif + }; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/upnp.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/upnp.hpp new file mode 100644 index 0000000000..f28cc112ff --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/upnp.hpp @@ -0,0 +1,393 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UPNP_HPP +#define TORRENT_UPNP_HPP + +#include "libtorrent/socket.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/broadcast_socket.hpp" +#include "libtorrent/http_connection.hpp" +#include "libtorrent/connection_queue.hpp" +#include "libtorrent/intrusive_ptr_base.hpp" +#include "libtorrent/thread.hpp" +#include "libtorrent/deadline_timer.hpp" + +#include +#include +#include +#include +#include + + +#if defined(TORRENT_UPNP_LOGGING) +#include +#endif + +namespace libtorrent +{ + + namespace upnp_errors + { + // error codes for the upnp_error_category. They hold error codes + // returned by UPnP routers when mapping ports + enum error_code_enum + { + // No error + no_error = 0, + // One of the arguments in the request is invalid + invalid_argument = 402, + // The request failed + action_failed = 501, + // The specified value does not exist in the array + value_not_in_array = 714, + // The source IP address cannot be wild-carded, but + // must be fully specified + source_ip_cannot_be_wildcarded = 715, + // The external port cannot be wildcarded, but must + // be specified + external_port_cannot_be_wildcarded = 716, + // The port mapping entry specified conflicts with a + // mapping assigned previously to another client + port_mapping_conflict = 718, + // Internal and external port value must be the same + internal_port_must_match_external = 724, + // The NAT implementation only supports permanent + // lease times on port mappings + only_permanent_leases_supported = 725, + // RemoteHost must be a wildcard and cannot be a + // specific IP addres or DNS name + remote_host_must_be_wildcard = 726, + // ExternalPort must be a wildcard and cannot be a + // specific port + external_port_must_be_wildcard = 727 + }; + + // hidden + TORRENT_EXPORT boost::system::error_code make_error_code(error_code_enum e); + } + + // the boost.system error category for UPnP errors + TORRENT_EXPORT boost::system::error_category& get_upnp_category(); + +// int: port-mapping index +// address: external address as queried from router +// int: external port +// std::string: error message +// an empty string as error means success +// a port-mapping index of -1 means it's +// an informational log message +typedef boost::function portmap_callback_t; +typedef boost::function log_callback_t; + +// TODO: support using the windows API for UPnP operations as well +class TORRENT_EXTRA_EXPORT upnp : public intrusive_ptr_base +{ +public: + upnp(io_service& ios, connection_queue& cc + , address const& listen_interface, std::string const& user_agent + , portmap_callback_t const& cb, log_callback_t const& lcb + , bool ignore_nonrouters, void* state = 0); + ~upnp(); + + void* drain_state(); + + enum protocol_type { none = 0, udp = 1, tcp = 2 }; + + // Attempts to add a port mapping for the specified protocol. Valid protocols are + // ``upnp::tcp`` and ``upnp::udp`` for the UPnP class and ``natpmp::tcp`` and + // ``natpmp::udp`` for the NAT-PMP class. + // + // ``external_port`` is the port on the external address that will be mapped. This + // is a hint, you are not guaranteed that this port will be available, and it may + // end up being something else. In the portmap_alert_ notification, the actual + // external port is reported. + // + // ``local_port`` is the port in the local machine that the mapping should forward + // to. + // + // The return value is an index that identifies this port mapping. This is used + // to refer to mappings that fails or succeeds in the portmap_error_alert_ and + // portmap_alert_ respectively. If The mapping fails immediately, the return value + // is -1, which means failure. There will not be any error alert notification for + // mappings that fail with a -1 return value. + int add_mapping(protocol_type p, int external_port, int local_port); + + // This function removes a port mapping. ``mapping_index`` is the index that refers + // to the mapping you want to remove, which was returned from add_mapping(). + void delete_mapping(int mapping_index); + + bool get_mapping(int mapping_index, int& local_port, int& external_port, int& protocol) const; + + void discover_device(); + void close(); + + // This is only available for UPnP routers. If the model is advertized by + // the router, it can be queried through this function. + std::string router_model() + { + mutex::scoped_lock l(m_mutex); + return m_model; + } + +private: + + void map_timer(error_code const& ec); + void try_map_upnp(mutex::scoped_lock& l, bool timer = false); + void discover_device_impl(mutex::scoped_lock& l); + static address_v4 upnp_multicast_address; + static udp::endpoint upnp_multicast_endpoint; + + // there are routers that's don't support timed + // port maps, without returning error 725. It seems + // safer to always assume that we have to ask for + // permanent leases + enum { default_lease_time = 0 }; + + void resend_request(error_code const& e); + void on_reply(udp::endpoint const& from, char* buffer + , std::size_t bytes_transferred); + + struct rootdevice; + void next(rootdevice& d, int i, mutex::scoped_lock& l); + void update_map(rootdevice& d, int i, mutex::scoped_lock& l); + + + void on_upnp_xml(error_code const& e + , libtorrent::http_parser const& p, rootdevice& d + , http_connection& c); + void on_upnp_get_ip_address_response(error_code const& e + , libtorrent::http_parser const& p, rootdevice& d + , http_connection& c); + void on_upnp_map_response(error_code const& e + , libtorrent::http_parser const& p, rootdevice& d + , int mapping, http_connection& c); + void on_upnp_unmap_response(error_code const& e + , libtorrent::http_parser const& p, rootdevice& d + , int mapping, http_connection& c); + void on_expire(error_code const& e); + + void disable(error_code const& ec, mutex::scoped_lock& l); + void return_error(int mapping, int code, mutex::scoped_lock& l); + void log(char const* msg, mutex::scoped_lock& l); + + void get_ip_address(rootdevice& d); + void delete_port_mapping(rootdevice& d, int i); + void create_port_mapping(http_connection& c, rootdevice& d, int i); + void post(upnp::rootdevice const& d, char const* soap + , char const* soap_action, mutex::scoped_lock& l); + + int num_mappings() const { return int(m_mappings.size()); } + + struct global_mapping_t + { + global_mapping_t() + : protocol(none) + , external_port(0) + , local_port(0) + {} + int protocol; + int external_port; + int local_port; + }; + + struct mapping_t + { + enum action_t { action_none, action_add, action_delete }; + mapping_t() + : action(action_none) + , local_port(0) + , external_port(0) + , protocol(none) + , failcount(0) + {} + + // the time the port mapping will expire + ptime expires; + + int action; + + // the local port for this mapping. If this is set + // to 0, the mapping is not in use + int local_port; + + // the external (on the NAT router) port + // for the mapping. This is the port we + // should announce to others + int external_port; + + // 2 = udp, 1 = tcp + int protocol; + + // the number of times this mapping has failed + int failcount; + }; + + struct rootdevice + { + rootdevice(): service_namespace(0) + , port(0) + , lease_duration(default_lease_time) + , supports_specific_external(true) + , disabled(false) + , non_router(false) + { +#if TORRENT_USE_ASSERTS + magic = 1337; +#endif + } + +#if TORRENT_USE_ASSERTS + ~rootdevice() + { + TORRENT_ASSERT(magic == 1337); + magic = 0; + } +#endif + + // the interface url, through which the list of + // supported interfaces are fetched + std::string url; + + // the url to the WANIP or WANPPP interface + std::string control_url; + // either the WANIP namespace or the WANPPP namespace + char const* service_namespace; + + std::vector mapping; + + // this is the hostname, port and path + // component of the url or the control_url + // if it has been found + std::string hostname; + int port; + std::string path; + address external_ip; + + int lease_duration; + // true if the device supports specifying a + // specific external port, false if it doesn't + bool supports_specific_external; + + bool disabled; + + // this is true if the IP of this device is not + // one of our default routes. i.e. it may be someone + // else's router, we just happen to have multicast + // enabled across networks + // this is only relevant if ignore_non_routers is set. + bool non_router; + + mutable boost::shared_ptr upnp_connection; + +#if TORRENT_USE_ASSERTS + int magic; +#endif + void close() const + { + TORRENT_ASSERT(magic == 1337); + if (!upnp_connection) return; + upnp_connection->close(); + upnp_connection.reset(); + } + + bool operator<(rootdevice const& rhs) const + { return url < rhs.url; } + }; + + struct upnp_state_t + { + std::vector mappings; + std::set devices; + }; + + std::vector m_mappings; + + std::string const& m_user_agent; + + // the set of devices we've found + std::set m_devices; + + portmap_callback_t m_callback; + log_callback_t m_log_callback; + + // current retry count + int m_retry_count; + + io_service& m_io_service; + + // the udp socket used to send and receive + // multicast messages on the network + broadcast_socket m_socket; + + // used to resend udp packets in case + // they time out + deadline_timer m_broadcast_timer; + + // timer used to refresh mappings + deadline_timer m_refresh_timer; + + // this timer fires one second after the last UPnP response. This is the + // point where we assume we have received most or all SSDP reponses. If we + // are ignoring non-routers and at this point we still haven't received a + // response from a router UPnP device, we override the ignoring behavior and + // map them anyway. + deadline_timer m_map_timer; + + bool m_disabled; + bool m_closing; + bool m_ignore_non_routers; + + connection_queue& m_cc; + + mutex m_mutex; + + std::string m_model; +}; + +} + +#if BOOST_VERSION >= 103500 + +namespace boost { namespace system { + + template<> struct is_error_code_enum + { static const bool value = true; }; + + template<> struct is_error_condition_enum + { static const bool value = true; }; +} } + +#endif // BOOST_VERSION + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/utf8.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/utf8.hpp new file mode 100644 index 0000000000..a89f677292 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/utf8.hpp @@ -0,0 +1,77 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UTF8_HPP_INCLUDED +#define TORRENT_UTF8_HPP_INCLUDED + +#include "libtorrent/config.hpp" + +// on windows we need these functions for +// convert_to_native and convert_from_native +#if TORRENT_USE_WSTRING || defined TORRENT_WINDOWS + +#include +#include + +namespace libtorrent +{ + + // results from UTF-8 conversion functions utf8_wchar and + // wchar_utf8 + enum utf8_conv_result_t + { + // conversion successful + conversion_ok, + + // partial character in source, but hit end + source_exhausted, + + // insuff. room in target for conversion + target_exhausted, + + // source sequence is illegal/malformed + source_illegal + }; + + // ``utf8_wchar`` converts a UTF-8 string (``utf8``) to a wide character + // string (``wide``). ``wchar_utf8`` converts a wide character string + // (``wide``) to a UTF-8 string (``utf8``). The return value is one of + // the enumeration values from utf8_conv_result_t. + TORRENT_EXPORT utf8_conv_result_t utf8_wchar( + const std::string &utf8, std::wstring &wide); + TORRENT_EXPORT utf8_conv_result_t wchar_utf8( + const std::wstring &wide, std::string &utf8); +} +#endif // !BOOST_NO_STD_WSTRING + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/utp_socket_manager.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/utp_socket_manager.hpp new file mode 100644 index 0000000000..0c0f06a763 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/utp_socket_manager.hpp @@ -0,0 +1,174 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UTP_SOCKET_MANAGER_HPP_INCLUDED +#define TORRENT_UTP_SOCKET_MANAGER_HPP_INCLUDED + +#include + +#include "libtorrent/socket_type.hpp" +#include "libtorrent/session_status.hpp" +#include "libtorrent/enum_net.hpp" + +namespace libtorrent +{ + class udp_socket; + class utp_stream; + struct utp_socket_impl; + + typedef boost::function const&)> incoming_utp_callback_t; + + struct utp_socket_manager : udp_socket_observer + { + utp_socket_manager(session_settings const& sett, udp_socket& s, incoming_utp_callback_t cb); + ~utp_socket_manager(); + + void get_status(utp_status& s) const; + + // return false if this is not a uTP packet + virtual bool incoming_packet(error_code const& ec, udp::endpoint const& ep + , char const* p, int size); + virtual bool incoming_packet(error_code const& ec, char const* host, char const* p, int size) + { return false; } + virtual void writable(); + + virtual void socket_drained(); + + void tick(ptime now); + + tcp::endpoint local_endpoint(address const& remote, error_code& ec) const; + int local_port(error_code& ec) const; + + // flags for send_packet + enum { dont_fragment = 1 }; + void send_packet(udp::endpoint const& ep, char const* p, int len + , error_code& ec, int flags = 0); + void subscribe_writable(utp_socket_impl* s); + + // internal, used by utp_stream + void remove_socket(boost::uint16_t id); + + utp_socket_impl* new_utp_socket(utp_stream* str); + int gain_factor() const { return m_sett.utp_gain_factor; } + int target_delay() const { return m_sett.utp_target_delay * 1000; } + int syn_resends() const { return m_sett.utp_syn_resends; } + int fin_resends() const { return m_sett.utp_fin_resends; } + int num_resends() const { return m_sett.utp_num_resends; } + int connect_timeout() const { return m_sett.utp_connect_timeout; } + int min_timeout() const { return m_sett.utp_min_timeout; } + int loss_multiplier() const { return m_sett.utp_loss_multiplier; } + bool allow_dynamic_sock_buf() const { return m_sett.utp_dynamic_sock_buf; } + + void mtu_for_dest(address const& addr, int& link_mtu, int& utp_mtu); + void set_sock_buf(int size); + int num_sockets() const { return m_utp_sockets.size(); } + + void defer_ack(utp_socket_impl* s); + void subscribe_drained(utp_socket_impl* s); + + enum counter_t + { + packet_loss = 0, + timeout, + packets_in, + packets_out, + fast_retransmit, + packet_resend, + samples_above_target, + samples_below_target, + payload_pkts_in, + payload_pkts_out, + invalid_pkts_in, + redundant_pkts_in, + + num_counters + }; + + // used to keep stats of uTP events + void inc_stats_counter(int counter); + + private: + udp_socket& m_sock; + incoming_utp_callback_t m_cb; + + // replace with a hash-map + typedef std::multimap socket_map_t; + socket_map_t m_utp_sockets; + + // this is a list of sockets that needs to send an ack. + // once the UDP socket is drained, all of these will + // have a chance to do that. This is to avoid sending + // an ack for every single packet + std::vector m_deferred_acks; + + // sockets that have received or sent packets this + // round, may subscribe to the event of draining the + // UDP socket. At that point they may call the + // user callback function to indicate bytes have been + // sent or received. + std::vector m_drained_event; + + // list of sockets that received EWOULDBLOCK from the + // underlying socket. They are notified when the socket + // becomes writable again + std::vector m_stalled_sockets; + + // the last socket we received a packet on + utp_socket_impl* m_last_socket; + + int m_new_connection; + + session_settings const& m_sett; + + // this is a copy of the routing table, used + // to initialize MTU sizes of uTP sockets + mutable std::vector m_routes; + + // the timestamp for the last time we updated + // the routing table + mutable ptime m_last_route_update; + + // cache of interfaces + mutable std::vector m_interfaces; + mutable ptime m_last_if_update; + + // the buffer size of the socket. This is used + // to now lower the buffer size + int m_sock_buf_size; + + // stats counters + boost::uint64_t m_counters[num_counters]; + }; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/utp_stream.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/utp_stream.hpp new file mode 100644 index 0000000000..7d8afe0194 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/utp_stream.hpp @@ -0,0 +1,464 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UTP_STREAM_HPP_INCLUDED +#define TORRENT_UTP_STREAM_HPP_INCLUDED + +#include "libtorrent/connection_queue.hpp" +#include "libtorrent/proxy_base.hpp" +#include "libtorrent/udp_socket.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/packet_buffer.hpp" +#include "libtorrent/error_code.hpp" + +#include +#include +#include + +#ifndef BOOST_NO_EXCEPTIONS +#include +#endif + +#define CCONTROL_TARGET 100 + +namespace libtorrent +{ + struct utp_socket_manager; + + // internal: some MTU and protocol header sizes constants + enum + { + TORRENT_IPV4_HEADER = 20, + TORRENT_IPV6_HEADER = 40, + TORRENT_UDP_HEADER = 8, + TORRENT_SOCKS5_HEADER = 6, // plus the size of the destination address + + TORRENT_ETHERNET_MTU = 1500, + TORRENT_TEREDO_MTU = 1280, + TORRENT_INET_MIN_MTU = 576, + TORRENT_INET_MAX_MTU = 0xffff + }; + + // internal: the point of the bif_endian_int is two-fold + // one purpuse is to not have any alignment requirements + // so that any byffer received from the network can be cast + // to it and read as an integer of various sizes without + // triggering a bus error. The other purpose is to convert + // from network byte order to host byte order when read and + // written, to offer a convenient interface to both interpreting + // and writing network packets + template struct big_endian_int + { + big_endian_int& operator=(T v) + { + char* p = m_storage; + detail::write_impl(v, p); + return *this; + } + operator T() const + { + const char* p = m_storage; + return detail::read_impl(p, detail::type()); + } + private: + char m_storage[sizeof(T)]; + }; + + typedef big_endian_int be_uint64; + typedef big_endian_int be_uint32; + typedef big_endian_int be_uint16; + typedef big_endian_int be_int64; + typedef big_endian_int be_int32; + typedef big_endian_int be_int16; + +/* + uTP header from BEP 29 + + 0 4 8 16 24 32 + +-------+-------+---------------+---------------+---------------+ + | type | ver | extension | connection_id | + +-------+-------+---------------+---------------+---------------+ + | timestamp_microseconds | + +---------------+---------------+---------------+---------------+ + | timestamp_difference_microseconds | + +---------------+---------------+---------------+---------------+ + | wnd_size | + +---------------+---------------+---------------+---------------+ + | seq_nr | ack_nr | + +---------------+---------------+---------------+---------------+ + +*/ + +// internal: the different kinds of uTP packets +enum utp_socket_state_t +{ ST_DATA, ST_FIN, ST_STATE, ST_RESET, ST_SYN, NUM_TYPES }; + +struct utp_header +{ + unsigned char type_ver; + unsigned char extension; + be_uint16 connection_id; + be_uint32 timestamp_microseconds; + be_uint32 timestamp_difference_microseconds; + be_uint32 wnd_size; + be_uint16 seq_nr; + be_uint16 ack_nr; + + int get_type() const { return type_ver >> 4; } + int get_version() const { return type_ver & 0xf; } +}; + +struct utp_socket_impl; + +utp_socket_impl* construct_utp_impl(boost::uint16_t recv_id + , boost::uint16_t send_id, void* userdata + , utp_socket_manager* sm); +void detach_utp_impl(utp_socket_impl* s); +void delete_utp_impl(utp_socket_impl* s); +bool should_delete(utp_socket_impl* s); +void tick_utp_impl(utp_socket_impl* s, ptime const& now); +void utp_init_mtu(utp_socket_impl* s, int link_mtu, int utp_mtu); +bool utp_incoming_packet(utp_socket_impl* s, char const* p + , int size, udp::endpoint const& ep, ptime receive_time); +bool utp_match(utp_socket_impl* s, udp::endpoint const& ep, boost::uint16_t id); +udp::endpoint utp_remote_endpoint(utp_socket_impl* s); +boost::uint16_t utp_receive_id(utp_socket_impl* s); +int utp_socket_state(utp_socket_impl const* s); +void utp_send_ack(utp_socket_impl* s); +void utp_socket_drained(utp_socket_impl* s); +void utp_writable(utp_socket_impl* s); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING +int socket_impl_size(); +#endif + +// this is the user-level stream interface to utp sockets. +// the reason why it's split up in a utp_stream class and +// an implementation class is because the socket state has +// to be able to out-live the user level socket. For instance +// when sending data on a stream and then closing it, the +// state holding the send buffer has to be kept around until +// it has been flushed, which may be longer than the client +// will keep the utp_stream object around for. +// for more details, see utp_socket_impl, which is analogous +// to the kernel state for a socket. It's defined in utp_stream.cpp +class TORRENT_EXTRA_EXPORT utp_stream +{ +public: + + typedef utp_stream lowest_layer_type; + typedef stream_socket::endpoint_type endpoint_type; + typedef stream_socket::protocol_type protocol_type; + + explicit utp_stream(asio::io_service& io_service); + ~utp_stream(); + + lowest_layer_type& lowest_layer() { return *this; } + + // used for incoming connections + void set_impl(utp_socket_impl* s); + utp_socket_impl* get_impl(); + +#ifndef BOOST_NO_EXCEPTIONS + template + void io_control(IO_Control_Command& ioc) {} +#endif + + template + void io_control(IO_Control_Command& ioc, error_code& ec) {} + +#ifndef BOOST_NO_EXCEPTIONS + void bind(endpoint_type const& /*endpoint*/) {} +#endif + + void bind(endpoint_type const& endpoint, error_code& ec); + +#ifndef BOOST_NO_EXCEPTIONS + template + void set_option(SettableSocketOption const& opt) {} +#endif + + template + error_code set_option(SettableSocketOption const& opt, error_code& ec) { return ec; } + +#ifndef BOOST_NO_EXCEPTIONS + template + void get_option(GettableSocketOption& opt) {} +#endif + + template + error_code get_option(GettableSocketOption& opt, error_code& ec) { return ec; } + + + void close(); + void close(error_code const& /*ec*/) { close(); } + bool is_open() const { return m_open; } + + int read_buffer_size() const; + static void on_read(void* self, size_t bytes_transferred, error_code const& ec, bool kill); + static void on_write(void* self, size_t bytes_transferred, error_code const& ec, bool kill); + static void on_connect(void* self, error_code const& ec, bool kill); + + typedef void(*handler_t)(void*, size_t, error_code const&, bool); + typedef void(*connect_handler_t)(void*, error_code const&, bool); + + void add_read_buffer(void* buf, size_t len); + void set_read_handler(handler_t h); + void add_write_buffer(void const* buf, size_t len); + void set_write_handler(handler_t h); + size_t read_some(bool clear_buffers); + + int send_delay() const; + int recv_delay() const; + + void do_connect(tcp::endpoint const& ep, connect_handler_t h); + + endpoint_type local_endpoint() const + { + error_code ec; + return local_endpoint(ec); + } + + endpoint_type local_endpoint(error_code& ec) const; + + endpoint_type remote_endpoint() const + { + error_code ec; + return remote_endpoint(ec); + } + + endpoint_type remote_endpoint(error_code& ec) const; + + std::size_t available() const; + std::size_t available(error_code& /*ec*/) const { return available(); } + + asio::io_service& get_io_service() { return m_io_service; } + + template + void async_connect(endpoint_type const& endpoint, Handler const& handler) + { + if (!endpoint.address().is_v4()) + { + m_io_service.post(boost::bind(handler, asio::error::operation_not_supported, 0)); + return; + } + + if (m_impl == 0) + { + m_io_service.post(boost::bind(handler, asio::error::not_connected, 0)); + return; + } + + m_connect_handler = handler; + do_connect(endpoint, &utp_stream::on_connect); + } + + template + void async_read_some(boost::asio::null_buffers const& buffers, Handler const& handler) + { + TORRENT_ASSERT(false); + } + + template + void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) + { + if (m_impl == 0) + { + m_io_service.post(boost::bind(handler, asio::error::not_connected, 0)); + return; + } + + TORRENT_ASSERT(!m_read_handler); + if (m_read_handler) + { + m_io_service.post(boost::bind(handler, asio::error::operation_not_supported, 0)); + return; + } + int bytes_added = 0; + for (typename Mutable_Buffers::const_iterator i = buffers.begin() + , end(buffers.end()); i != end; ++i) + { + if (buffer_size(*i) == 0) continue; + using asio::buffer_cast; + using asio::buffer_size; + add_read_buffer(buffer_cast(*i), buffer_size(*i)); + bytes_added += buffer_size(*i); + } + if (bytes_added == 0) + { + // if we're reading 0 bytes, post handler immediately + // asio's SSL layer depends on this behavior + m_io_service.post(boost::bind(handler, error_code(), 0)); + return; + } + + m_read_handler = handler; + set_read_handler(&utp_stream::on_read); + } + + void do_async_connect(endpoint_type const& ep + , boost::function const& handler); + + template + void open(Protocol const& p, error_code& ec) + { m_open = true; } + + template + void open(Protocol const& p) + { m_open = true; } + + template + std::size_t read_some(Mutable_Buffers const& buffers, error_code& ec) + { + TORRENT_ASSERT(!m_read_handler); + if (m_impl == 0) + { + ec = asio::error::not_connected; + return 0; + } + + if (read_buffer_size() == 0) + { + ec = asio::error::would_block; + return 0; + } +#if TORRENT_USE_ASSERTS + size_t buf_size = 0; +#endif + + for (typename Mutable_Buffers::const_iterator i = buffers.begin() + , end(buffers.end()); i != end; ++i) + { + using asio::buffer_cast; + using asio::buffer_size; + add_read_buffer(buffer_cast(*i), buffer_size(*i)); +#if TORRENT_USE_ASSERTS + buf_size += buffer_size(*i); +#endif + } + std::size_t ret = read_some(true); + TORRENT_ASSERT(ret <= buf_size); + TORRENT_ASSERT(ret > 0); + return ret; + } + + template + std::size_t write_some(Const_Buffers const& buffers, error_code& ec) + { + TORRENT_ASSERT(false && "not implemented!"); + // TODO: implement blocking write. Low priority since it's not used (yet) + return 0; + } + +#ifndef BOOST_NO_EXCEPTIONS + template + std::size_t read_some(Mutable_Buffers const& buffers) + { + error_code ec; + std::size_t ret = read_some(buffers, ec); + if (ec) + boost::throw_exception(boost::system::system_error(ec)); + return ret; + } + + template + std::size_t write_some(Const_Buffers const& buffers) + { + error_code ec; + std::size_t ret = write_some(buffers, ec); + if (ec) + boost::throw_exception(boost::system::system_error(ec)); + return ret; + } +#endif + + template + void async_write_some(boost::asio::null_buffers const& buffers, Handler const& handler) + { + TORRENT_ASSERT(false); + } + + template + void async_write_some(Const_Buffers const& buffers, Handler const& handler) + { + if (m_impl == 0) + { + m_io_service.post(boost::bind(handler, asio::error::not_connected, 0)); + return; + } + + TORRENT_ASSERT(!m_write_handler); + if (m_write_handler) + { + m_io_service.post(boost::bind(handler, asio::error::operation_not_supported, 0)); + return; + } + + int bytes_added = 0; + for (typename Const_Buffers::const_iterator i = buffers.begin() + , end(buffers.end()); i != end; ++i) + { + if (buffer_size(*i) == 0) continue; + using asio::buffer_cast; + using asio::buffer_size; + add_write_buffer((void*)buffer_cast(*i), buffer_size(*i)); + bytes_added += buffer_size(*i); + } + if (bytes_added == 0) + { + // if we're reading 0 bytes, post handler immediately + // asio's SSL layer depends on this behavior + m_io_service.post(boost::bind(handler, error_code(), 0)); + return; + } + m_write_handler = handler; + set_write_handler(&utp_stream::on_write); + } + +//private: + + void cancel_handlers(error_code const&); + + boost::function1 m_connect_handler; + boost::function2 m_read_handler; + boost::function2 m_write_handler; + + asio::io_service& m_io_service; + utp_socket_impl* m_impl; + + // this field requires another 8 bytes (including padding) + bool m_open; +}; + +} + +#endif diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/version.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/version.hpp new file mode 100644 index 0000000000..b83085e1a0 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/version.hpp @@ -0,0 +1,47 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_VERSION_HPP_INCLUDED +#define TORRENT_VERSION_HPP_INCLUDED + +#define LIBTORRENT_VERSION_MAJOR 1 +#define LIBTORRENT_VERSION_MINOR 0 +#define LIBTORRENT_VERSION_TINY 5 + +// the format of this version is: MMmmtt +// M = Major version, m = minor version, t = tiny version +#define LIBTORRENT_VERSION_NUM ((LIBTORRENT_VERSION_MAJOR * 10000) + (LIBTORRENT_VERSION_MINOR * 100) + LIBTORRENT_VERSION_TINY) + +#define LIBTORRENT_VERSION "1.0.5.0" +#define LIBTORRENT_REVISION "$Rev: 11072 $" + +#endif diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/web_connection_base.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/web_connection_base.hpp new file mode 100644 index 0000000000..1299b1ebd5 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/web_connection_base.hpp @@ -0,0 +1,167 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef WEB_CONNECTION_BASE_HPP_INCLUDED +#define WEB_CONNECTION_BASE_HPP_INCLUDED + +#include +#include +#include +#include +#include + +#include "libtorrent/debug.hpp" + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/buffer.hpp" +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/storage.hpp" +#include "libtorrent/stat.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/config.hpp" +// parse_url +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/http_parser.hpp" + +namespace libtorrent +{ + class torrent; + + namespace detail + { + struct session_impl; + } + + class TORRENT_EXTRA_EXPORT web_connection_base + : public peer_connection + { + friend class invariant_access; + public: + + // this is the constructor where the we are the active part. + // The peer_conenction should handshake and verify that the + // other end has the correct id + web_connection_base( + aux::session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote + , web_seed_entry& web); + void start(); + + ~web_connection_base(); + + // called from the main loop when this connection has any + // work to do. + void on_sent(error_code const& error + , std::size_t bytes_transferred); + + virtual std::string const& url() const = 0; + + bool in_handshake() const; + + // the following functions appends messages + // to the send buffer + void write_choke() {} + void write_unchoke() {} + void write_interested() {} + void write_not_interested() {} + virtual void write_request(peer_request const& r) = 0; + void write_cancel(peer_request const& r) {} + void write_have(int index) {} + void write_piece(peer_request const& r, disk_buffer_holder& buffer) { TORRENT_ASSERT(false); } + void write_keepalive() {} + void on_connected(); + void write_reject_request(peer_request const&) {} + void write_allow_fast(int) {} + void write_suggest(int piece) {} + +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + + virtual void get_specific_peer_info(peer_info& p) const; + + protected: + + virtual void add_headers(std::string& request + , proxy_settings const& ps, bool using_proxy) const; + + // this has one entry per bittorrent request + std::deque m_requests; + + std::string m_server_string; + http_parser m_parser; + std::string m_basic_auth; + std::string m_host; + int m_port; + std::string m_path; + + std::string m_external_auth; + web_seed_entry::headers_t m_extra_headers; + + // the first request will contain a little bit more data + // than subsequent ones, things that aren't critical are left + // out to save bandwidth. + bool m_first_request; + + // true if we're using ssl + bool m_ssl; + + // the number of bytes into the receive buffer where + // current read cursor is. + int m_body_start; + }; +} + +#endif // TORRENT_WEB_CONNECTION_BASE_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/web_peer_connection.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/web_peer_connection.hpp new file mode 100644 index 0000000000..3e45046d77 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/web_peer_connection.hpp @@ -0,0 +1,165 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_WEB_PEER_CONNECTION_HPP_INCLUDED +#define TORRENT_WEB_PEER_CONNECTION_HPP_INCLUDED + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/web_connection_base.hpp" +#include "libtorrent/disk_buffer_holder.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/http_parser.hpp" + +namespace libtorrent +{ + class torrent; + + namespace detail + { + struct session_impl; + } + + class TORRENT_EXTRA_EXPORT web_peer_connection + : public web_connection_base + { + friend class invariant_access; + public: + + // this is the constructor where the we are the active part. + // The peer_conenction should handshake and verify that the + // other end has the correct id + web_peer_connection( + aux::session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote + , web_seed_entry& web); + + virtual void on_connected(); + + virtual int type() const { return peer_connection::url_seed_connection; } + + // called from the main loop when this connection has any + // work to do. + void on_receive(error_code const& error + , std::size_t bytes_transferred); + + std::string const& url() const { return m_url; } + + virtual void get_specific_peer_info(peer_info& p) const; + virtual void disconnect(error_code const& ec, int error = 0); + + virtual void write_request(peer_request const& r); + + virtual bool received_invalid_data(int index, bool single_peer); + + private: + + bool maybe_harvest_block(); + + // returns the block currently being + // downloaded. And the progress of that + // block. If the peer isn't downloading + // a piece for the moment, the boost::optional + // will be invalid. + boost::optional downloading_piece_progress() const; + + void handle_padfile(buffer::const_interval& recv_buffer); + + // this has one entry per http-request + // (might be more than the bt requests) + std::deque m_file_requests; + + std::string m_url; + + web_seed_entry* m_web; + + // this is used for intermediate storage of pieces + // that are received in more than one HTTP response + // TODO: 1 if we make this be a disk_buffer_holder instead + // we would save a copy sometimes + // use allocate_disk_receive_buffer and release_disk_receive_buffer + std::vector m_piece; + + // the number of bytes received in the current HTTP + // response. used to know where in the buffer the + // next response starts + size_type m_received_body; + + // position in the current range response + size_type m_range_pos; + + // the position in the current block + int m_block_pos; + + // this is the offset inside the current receive + // buffer where the next chunk header will be. + // this is updated for each chunk header that's + // parsed. It does not necessarily point to a valid + // offset in the receive buffer, if we haven't received + // it yet. This offset never includes the HTTP header + size_type m_chunk_pos; + + // this is the number of bytes we've already received + // from the next chunk header we're waiting for + int m_partial_chunk_header; + + // the number of responses we've received so far on + // this connection + int m_num_responses; + }; +} + +#endif // TORRENT_WEB_PEER_CONNECTION_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/xml_parse.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/xml_parse.hpp new file mode 100644 index 0000000000..674f2f21f9 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/xml_parse.hpp @@ -0,0 +1,71 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_XML_PARSE_HPP +#define TORRENT_XML_PARSE_HPP + +#include +#include + +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/escape_string.hpp" + +#include + +namespace libtorrent +{ + enum + { + xml_start_tag, + xml_end_tag, + xml_empty_tag, + xml_declaration_tag, + xml_string, + xml_attribute, + xml_comment, + xml_parse_error, + // used for tags that don't follow the convention of + // key-value pairs inside the tag brackets. Like !DOCTYPE + xml_tag_content + }; + + // callback(int type, char const* name, char const* val) + // str2 is only used for attributes. name is element or attribute + // name and val is attribute value + TORRENT_EXTRA_EXPORT void xml_parse(char* p, char* end + , boost::function callback); +} + + +#endif + diff --git a/apps/Launcher/ext/libtorrent/libtorrent-rasterbar-cmake.pc.in b/apps/Launcher/ext/libtorrent/libtorrent-rasterbar-cmake.pc.in new file mode 100644 index 0000000000..ee5f519430 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/libtorrent-rasterbar-cmake.pc.in @@ -0,0 +1,6 @@ +Name: libtorrent-rasterbar +Description: Bittorrent library. +Version: @VERSION@ +Libs: -L${CMAKE_INSTALL_PREFIX}/lib -ltorrent-rasterbar +Cflags: -I${CMAKE_INSTALL_PREFIX}/include -I${CMAKE_INSTALL_PREFIX}/include/libtorrent @COMPILETIME_OPTIONS@ @CXX_DEFINES@ + diff --git a/apps/Launcher/ext/libtorrent/src/ConvertUTF.cpp b/apps/Launcher/ext/libtorrent/src/ConvertUTF.cpp new file mode 100644 index 0000000000..f93a1d56f1 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/ConvertUTF.cpp @@ -0,0 +1,539 @@ +/* + * Copyright 2001-2004 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + +/* --------------------------------------------------------------------- + + Conversions between UTF32, UTF-16, and UTF-8. Source code file. + Author: Mark E. Davis, 1994. + Rev History: Rick McGowan, fixes & updates May 2001. + Sept 2001: fixed const & error conditions per + mods suggested by S. Parent & A. Lillich. + June 2002: Tim Dodd added detection and handling of incomplete + source sequences, enhanced error detection, added casts + to eliminate compiler warnings. + July 2003: slight mods to back out aggressive FFFE detection. + Jan 2004: updated switches in from-UTF8 conversions. + Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions. + + See the header file "ConvertUTF.h" for complete documentation. + +------------------------------------------------------------------------ */ + + +#include "libtorrent/ConvertUTF.h" +#ifdef CVTUTF_DEBUG +#include +#endif + +static const int halfShift = 10; /* used for shifting by 10 bits */ + +static const UTF32 halfBase = 0x0010000UL; +static const UTF32 halfMask = 0x3FFUL; + +#define UNI_SUR_HIGH_START (UTF32)0xD800 +#define UNI_SUR_HIGH_END (UTF32)0xDBFF +#define UNI_SUR_LOW_START (UTF32)0xDC00 +#define UNI_SUR_LOW_END (UTF32)0xDFFF +#define false 0 +#define true 1 + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF32toUTF16 ( + const UTF32** sourceStart, const UTF32* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF32* source = *sourceStart; + UTF16* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + if (target >= targetEnd) { + result = targetExhausted; break; + } + ch = *source++; + if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = (UTF16)ch; /* normal case */ + } + } else if (ch > UNI_MAX_LEGAL_UTF32) { + if (flags == strictConversion) { + result = sourceIllegal; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + if (target + 1 >= targetEnd) { + --source; /* Back up source pointer! */ + result = targetExhausted; break; + } + ch -= halfBase; + *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); + *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF16toUTF32 ( + const UTF16** sourceStart, const UTF16* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF16* source = *sourceStart; + UTF32* target = *targetStart; + UTF32 ch, ch2; + while (source < sourceEnd) { + const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ + ch = *source++; + /* If we have a surrogate pair, convert to UTF32 first. */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { + /* If the 16 bits following the high surrogate are in the source buffer... */ + if (source < sourceEnd) { + ch2 = *source; + /* If it's a low surrogate, convert to UTF32. */ + if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { + ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + + (ch2 - UNI_SUR_LOW_START) + halfBase; + ++source; + } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --source; /* return to the high surrogate */ + result = sourceExhausted; + break; + } + } else if (flags == strictConversion) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + if (target >= targetEnd) { + source = oldSource; /* Back up source pointer! */ + result = targetExhausted; break; + } + *target++ = ch; + } + *sourceStart = source; + *targetStart = target; +#ifdef CVTUTF_DEBUG +if (result == sourceIllegal) { + fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2); + fflush(stderr); +} +#endif + return result; +} + +/* --------------------------------------------------------------------- */ + +/* + * Index into the table below with the first byte of a UTF-8 sequence to + * get the number of trailing bytes that are supposed to follow it. + * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is + * left as-is for anyone who may want to do such conversion, which was + * allowed in earlier algorithms. + */ +static const char trailingBytesForUTF8[256] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 +}; + +/* + * Magic values subtracted from a buffer value during UTF8 conversion. + * This table contains as many values as there might be trailing bytes + * in a UTF-8 sequence. + */ +static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, + 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; + +/* + * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed + * into the first byte, depending on how many bytes follow. There are + * as many entries in this table as there are UTF-8 sequence types. + * (I.e., one byte sequence, two byte... etc.). Remember that sequencs + * for *legal* UTF-8 will be 4 or fewer bytes total. + */ +static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + +/* --------------------------------------------------------------------- */ + +/* The interface converts a whole buffer to avoid function-call overhead. + * Constants have been gathered. Loops & conditionals have been removed as + * much as possible for efficiency, in favor of drop-through switches. + * (See "Note A" at the bottom of the file for equivalent code.) + * If your compiler supports it, the "isLegalUTF8" call can be turned + * into an inline function. + */ + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF16toUTF8 ( + const UTF16** sourceStart, const UTF16* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF16* source = *sourceStart; + UTF8* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + unsigned short bytesToWrite = 0; + const UTF32 byteMask = 0xBF; + const UTF32 byteMark = 0x80; + const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ + ch = *source++; + /* If we have a surrogate pair, convert to UTF32 first. */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { + /* If the 16 bits following the high surrogate are in the source buffer... */ + if (source < sourceEnd) { + UTF32 ch2 = *source; + /* If it's a low surrogate, convert to UTF32. */ + if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { + ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + + (ch2 - UNI_SUR_LOW_START) + halfBase; + ++source; + } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --source; /* return to the high surrogate */ + result = sourceExhausted; + break; + } + } else if (flags == strictConversion) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + /* Figure out how many bytes the result will require */ + if (ch < (UTF32)0x80) { bytesToWrite = 1; + } else if (ch < (UTF32)0x800) { bytesToWrite = 2; + } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; + } else if (ch < (UTF32)0x110000) { bytesToWrite = 4; + } else { bytesToWrite = 3; + ch = UNI_REPLACEMENT_CHAR; + } + + target += bytesToWrite; + if (target > targetEnd) { + source = oldSource; /* Back up source pointer! */ + target -= bytesToWrite; result = targetExhausted; break; + } + switch (bytesToWrite) { /* note: everything falls through. */ + case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +/* + * Utility routine to tell whether a sequence of bytes is legal UTF-8. + * This must be called with the length pre-determined by the first byte. + * If not calling this from ConvertUTF8to*, then the length can be set by: + * length = trailingBytesForUTF8[*source]+1; + * and the sequence is illegal right away if there aren't that many bytes + * available. + * If presented with a length > 4, this returns false. The Unicode + * definition of UTF-8 goes up to 4-byte sequences. + */ + +static Boolean isLegalUTF8(const UTF8 *source, int length) { + UTF8 a; + const UTF8 *srcptr = source+length; + switch (length) { + default: return false; + /* Everything else falls through when "true"... */ + case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + case 2: if ((a = (*--srcptr)) > 0xBF) return false; + + switch (*source) { + /* no fall-through in this inner switch */ + case 0xE0: if (a < 0xA0) return false; break; + case 0xED: if (a > 0x9F) return false; break; + case 0xF0: if (a < 0x90) return false; break; + case 0xF4: if (a > 0x8F) return false; break; + default: if (a < 0x80) return false; + } + + case 1: if (*source >= 0x80 && *source < 0xC2) return false; + } + if (*source > 0xF4) return false; + return true; +} + +/* --------------------------------------------------------------------- */ + +/* + * Exported function to return whether a UTF-8 sequence is legal or not. + * This is not used here; it's just exported. + */ +Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) { + int length = trailingBytesForUTF8[*source]+1; + if (source+length > sourceEnd) { + return false; + } + return isLegalUTF8(source, length); +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF8toUTF16 ( + const UTF8** sourceStart, const UTF8* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF8* source = *sourceStart; + UTF16* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch = 0; + unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; + if (source + extraBytesToRead >= sourceEnd) { + result = sourceExhausted; break; + } + /* Do this check whether lenient or strict */ + if (! isLegalUTF8(source, extraBytesToRead+1)) { + result = sourceIllegal; + break; + } + /* + * The cases all fall through. See "Note A" below. + */ + switch (extraBytesToRead) { + case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ + case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ + case 3: ch += *source++; ch <<= 6; + case 2: ch += *source++; ch <<= 6; + case 1: ch += *source++; ch <<= 6; + case 0: ch += *source++; + } + ch -= offsetsFromUTF8[extraBytesToRead]; + + if (target >= targetEnd) { + source -= (extraBytesToRead+1); /* Back up source pointer! */ + result = targetExhausted; break; + } + if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + source -= (extraBytesToRead+1); /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = (UTF16)ch; /* normal case */ + } + } else if (ch > UNI_MAX_UTF16) { + if (flags == strictConversion) { + result = sourceIllegal; + source -= (extraBytesToRead+1); /* return to the start */ + break; /* Bail out; shouldn't continue */ + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + if (target + 1 >= targetEnd) { + source -= (extraBytesToRead+1); /* Back up source pointer! */ + result = targetExhausted; break; + } + ch -= halfBase; + *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); + *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF32toUTF8 ( + const UTF32** sourceStart, const UTF32* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF32* source = *sourceStart; + UTF8* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + unsigned short bytesToWrite = 0; + const UTF32 byteMask = 0xBF; + const UTF32 byteMark = 0x80; + ch = *source++; + if (flags == strictConversion ) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + /* + * Figure out how many bytes the result will require. Turn any + * illegally large UTF32 things (> Plane 17) into replacement chars. + */ + if (ch < (UTF32)0x80) { bytesToWrite = 1; + } else if (ch < (UTF32)0x800) { bytesToWrite = 2; + } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; + } else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4; + } else { bytesToWrite = 3; + ch = UNI_REPLACEMENT_CHAR; + result = sourceIllegal; + } + + target += bytesToWrite; + if (target > targetEnd) { + --source; /* Back up source pointer! */ + target -= bytesToWrite; result = targetExhausted; break; + } + switch (bytesToWrite) { /* note: everything falls through. */ + case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF8toUTF32 ( + const UTF8** sourceStart, const UTF8* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF8* source = *sourceStart; + UTF32* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch = 0; + unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; + if (source + extraBytesToRead >= sourceEnd) { + result = sourceExhausted; break; + } + /* Do this check whether lenient or strict */ + if (! isLegalUTF8(source, extraBytesToRead+1)) { + result = sourceIllegal; + break; + } + /* + * The cases all fall through. See "Note A" below. + */ + switch (extraBytesToRead) { + case 5: ch += *source++; ch <<= 6; + case 4: ch += *source++; ch <<= 6; + case 3: ch += *source++; ch <<= 6; + case 2: ch += *source++; ch <<= 6; + case 1: ch += *source++; ch <<= 6; + case 0: ch += *source++; + } + ch -= offsetsFromUTF8[extraBytesToRead]; + + if (target >= targetEnd) { + source -= (extraBytesToRead+1); /* Back up the source pointer! */ + result = targetExhausted; break; + } + if (ch <= UNI_MAX_LEGAL_UTF32) { + /* + * UTF-16 surrogate values are illegal in UTF-32, and anything + * over Plane 17 (> 0x10FFFF) is illegal. + */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + source -= (extraBytesToRead+1); /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = ch; + } + } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */ + result = sourceIllegal; + *target++ = UNI_REPLACEMENT_CHAR; + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- + + Note A. + The fall-through switches in UTF-8 reading code save a + temp variable, some decrements & conditionals. The switches + are equivalent to the following loop: + { + int tmpBytesToRead = extraBytesToRead+1; + do { + ch += *source++; + --tmpBytesToRead; + if (tmpBytesToRead) ch <<= 6; + } while (tmpBytesToRead > 0); + } + In UTF-8 writing code, the switches on "bytesToWrite" are + similarly unrolled loops. + + --------------------------------------------------------------------- */ diff --git a/apps/Launcher/ext/libtorrent/src/GeoIP.c b/apps/Launcher/ext/libtorrent/src/GeoIP.c new file mode 100644 index 0000000000..cb4437ee7e --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/GeoIP.c @@ -0,0 +1,1077 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */ +/* GeoIP.c + * + * Copyright (C) 2006 MaxMind LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libtorrent/GeoIP.h" + +#include "libtorrent/ConvertUTF.h" + +#ifndef WIN32 +#include +#include +#include /* For ntohl */ +#include + +#include + +#else +#include +#include +#define snprintf _snprintf +#endif +#include +#include +#include +#include +#include +#include /* for fstat */ +#include /* for fstat */ + +#ifdef HAVE_STDINT_H +#include /* For uint32_t */ +#endif + +#ifndef INADDR_NONE +#define INADDR_NONE -1 +#endif + +#define COUNTRY_BEGIN 16776960 +#define STATE_BEGIN_REV0 16700000 +#define STATE_BEGIN_REV1 16000000 +#define STRUCTURE_INFO_MAX_SIZE 20 +#define DATABASE_INFO_MAX_SIZE 100 +#define MAX_ORG_RECORD_LENGTH 300 +#define US_OFFSET 1 +#define CANADA_OFFSET 677 +#define WORLD_OFFSET 1353 +#define FIPS_RANGE 360 + +#define CHECK_ERR(err, msg) { \ + if (err != Z_OK) { \ + fprintf(stderr, "%s error: %d\n", msg, err); \ + exit(1); \ + } \ +} + +const char GeoIP_country_code[253][3] = { "--","AP","EU","AD","AE","AF","AG","AI","AL","AM","AN", + "AO","AQ","AR","AS","AT","AU","AW","AZ","BA","BB", + "BD","BE","BF","BG","BH","BI","BJ","BM","BN","BO", + "BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD", + "CF","CG","CH","CI","CK","CL","CM","CN","CO","CR", + "CU","CV","CX","CY","CZ","DE","DJ","DK","DM","DO", + "DZ","EC","EE","EG","EH","ER","ES","ET","FI","FJ", + "FK","FM","FO","FR","FX","GA","GB","GD","GE","GF", + "GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT", + "GU","GW","GY","HK","HM","HN","HR","HT","HU","ID", + "IE","IL","IN","IO","IQ","IR","IS","IT","JM","JO", + "JP","KE","KG","KH","KI","KM","KN","KP","KR","KW", + "KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT", + "LU","LV","LY","MA","MC","MD","MG","MH","MK","ML", + "MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV", + "MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI", + "NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF", + "PG","PH","PK","PL","PM","PN","PR","PS","PT","PW", + "PY","QA","RE","RO","RU","RW","SA","SB","SC","SD", + "SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO", + "SR","ST","SV","SY","SZ","TC","TD","TF","TG","TH", + "TJ","TK","TM","TN","TO","TL","TR","TT","TV","TW", + "TZ","UA","UG","UM","US","UY","UZ","VA","VC","VE", + "VG","VI","VN","VU","WF","WS","YE","YT","RS","ZA", + "ZM","ME","ZW","A1","A2","O1","AX","GG","IM","JE", + "BL","MF"}; + +const char GeoIP_country_code3[253][4] = { "--","AP","EU","AND","ARE","AFG","ATG","AIA","ALB","ARM","ANT", + "AGO","AQ","ARG","ASM","AUT","AUS","ABW","AZE","BIH","BRB", + "BGD","BEL","BFA","BGR","BHR","BDI","BEN","BMU","BRN","BOL", + "BRA","BHS","BTN","BV","BWA","BLR","BLZ","CAN","CC","COD", + "CAF","COG","CHE","CIV","COK","CHL","CMR","CHN","COL","CRI", + "CUB","CPV","CX","CYP","CZE","DEU","DJI","DNK","DMA","DOM", + "DZA","ECU","EST","EGY","ESH","ERI","ESP","ETH","FIN","FJI", + "FLK","FSM","FRO","FRA","FX","GAB","GBR","GRD","GEO","GUF", + "GHA","GIB","GRL","GMB","GIN","GLP","GNQ","GRC","GS","GTM", + "GUM","GNB","GUY","HKG","HM","HND","HRV","HTI","HUN","IDN", + "IRL","ISR","IND","IO","IRQ","IRN","ISL","ITA","JAM","JOR", + "JPN","KEN","KGZ","KHM","KIR","COM","KNA","PRK","KOR","KWT", + "CYM","KAZ","LAO","LBN","LCA","LIE","LKA","LBR","LSO","LTU", + "LUX","LVA","LBY","MAR","MCO","MDA","MDG","MHL","MKD","MLI", + "MMR","MNG","MAC","MNP","MTQ","MRT","MSR","MLT","MUS","MDV", + "MWI","MEX","MYS","MOZ","NAM","NCL","NER","NFK","NGA","NIC", + "NLD","NOR","NPL","NRU","NIU","NZL","OMN","PAN","PER","PYF", + "PNG","PHL","PAK","POL","SPM","PCN","PRI","PSE","PRT","PLW", + "PRY","QAT","REU","ROU","RUS","RWA","SAU","SLB","SYC","SDN", + "SWE","SGP","SHN","SVN","SJM","SVK","SLE","SMR","SEN","SOM", + "SUR","STP","SLV","SYR","SWZ","TCA","TCD","TF","TGO","THA", + "TJK","TKL","TKM","TUN","TON","TLS","TUR","TTO","TUV","TWN", + "TZA","UKR","UGA","UM","USA","URY","UZB","VAT","VCT","VEN", + "VGB","VIR","VNM","VUT","WLF","WSM","YEM","YT","SRB","ZAF", + "ZMB","MNE","ZWE","A1","A2","O1","ALA","GGY","IMN","JEY", + "BLM","MAF"}; + +const char * GeoIP_country_name[253] = {"N/A","Asia/Pacific Region","Europe","Andorra","United Arab Emirates","Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia","Netherlands Antilles", + "Angola","Antarctica","Argentina","American Samoa","Austria","Australia","Aruba","Azerbaijan","Bosnia and Herzegovina","Barbados", + "Bangladesh","Belgium","Burkina Faso","Bulgaria","Bahrain","Burundi","Benin","Bermuda","Brunei Darussalam","Bolivia", + "Brazil","Bahamas","Bhutan","Bouvet Island","Botswana","Belarus","Belize","Canada","Cocos (Keeling) Islands","Congo, The Democratic Republic of the", + "Central African Republic","Congo","Switzerland","Cote D'Ivoire","Cook Islands","Chile","Cameroon","China","Colombia","Costa Rica", + "Cuba","Cape Verde","Christmas Island","Cyprus","Czech Republic","Germany","Djibouti","Denmark","Dominica","Dominican Republic", + "Algeria","Ecuador","Estonia","Egypt","Western Sahara","Eritrea","Spain","Ethiopia","Finland","Fiji", + "Falkland Islands (Malvinas)","Micronesia, Federated States of","Faroe Islands","France","France, Metropolitan","Gabon","United Kingdom","Grenada","Georgia","French Guiana", + "Ghana","Gibraltar","Greenland","Gambia","Guinea","Guadeloupe","Equatorial Guinea","Greece","South Georgia and the South Sandwich Islands","Guatemala", + "Guam","Guinea-Bissau","Guyana","Hong Kong","Heard Island and McDonald Islands","Honduras","Croatia","Haiti","Hungary","Indonesia", + "Ireland","Israel","India","British Indian Ocean Territory","Iraq","Iran, Islamic Republic of","Iceland","Italy","Jamaica","Jordan", + "Japan","Kenya","Kyrgyzstan","Cambodia","Kiribati","Comoros","Saint Kitts and Nevis","Korea, Democratic People's Republic of","Korea, Republic of","Kuwait", + "Cayman Islands","Kazakhstan","Lao People's Democratic Republic","Lebanon","Saint Lucia","Liechtenstein","Sri Lanka","Liberia","Lesotho","Lithuania", + "Luxembourg","Latvia","Libyan Arab Jamahiriya","Morocco","Monaco","Moldova, Republic of","Madagascar","Marshall Islands","Macedonia","Mali", + "Myanmar","Mongolia","Macau","Northern Mariana Islands","Martinique","Mauritania","Montserrat","Malta","Mauritius","Maldives", + "Malawi","Mexico","Malaysia","Mozambique","Namibia","New Caledonia","Niger","Norfolk Island","Nigeria","Nicaragua", + "Netherlands","Norway","Nepal","Nauru","Niue","New Zealand","Oman","Panama","Peru","French Polynesia", + "Papua New Guinea","Philippines","Pakistan","Poland","Saint Pierre and Miquelon","Pitcairn Islands","Puerto Rico","Palestinian Territory","Portugal","Palau", + "Paraguay","Qatar","Reunion","Romania","Russian Federation","Rwanda","Saudi Arabia","Solomon Islands","Seychelles","Sudan", + "Sweden","Singapore","Saint Helena","Slovenia","Svalbard and Jan Mayen","Slovakia","Sierra Leone","San Marino","Senegal","Somalia","Suriname", + "Sao Tome and Principe","El Salvador","Syrian Arab Republic","Swaziland","Turks and Caicos Islands","Chad","French Southern Territories","Togo","Thailand", + "Tajikistan","Tokelau","Turkmenistan","Tunisia","Tonga","Timor-Leste","Turkey","Trinidad and Tobago","Tuvalu","Taiwan", + "Tanzania, United Republic of","Ukraine","Uganda","United States Minor Outlying Islands","United States","Uruguay","Uzbekistan","Holy See (Vatican City State)","Saint Vincent and the Grenadines","Venezuela", + "Virgin Islands, British","Virgin Islands, U.S.","Vietnam","Vanuatu","Wallis and Futuna","Samoa","Yemen","Mayotte","Serbia","South Africa", + "Zambia","Montenegro","Zimbabwe","Anonymous Proxy","Satellite Provider","Other","Aland Islands","Guernsey","Isle of Man","Jersey", + "Saint Barthelemy","Saint Martin"}; + +/* Possible continent codes are AF, AS, EU, NA, OC, SA for Africa, Asia, Europe, North America, Oceania +and South America. */ + +const char GeoIP_country_continent[253][3] = {"--","AS","EU","EU","AS","AS","SA","SA","EU","AS","SA", + "AF","AN","SA","OC","EU","OC","SA","AS","EU","SA", + "AS","EU","AF","EU","AS","AF","AF","SA","AS","SA", + "SA","SA","AS","AF","AF","EU","SA","NA","AS","AF", + "AF","AF","EU","AF","OC","SA","AF","AS","SA","SA", + "SA","AF","AS","AS","EU","EU","AF","EU","SA","SA", + "AF","SA","EU","AF","AF","AF","EU","AF","EU","OC", + "SA","OC","EU","EU","EU","AF","EU","SA","AS","SA", + "AF","EU","SA","AF","AF","SA","AF","EU","SA","SA", + "OC","AF","SA","AS","AF","SA","EU","SA","EU","AS", + "EU","AS","AS","AS","AS","AS","EU","EU","SA","AS", + "AS","AF","AS","AS","OC","AF","SA","AS","AS","AS", + "SA","AS","AS","AS","SA","EU","AS","AF","AF","EU", + "EU","EU","AF","AF","EU","EU","AF","OC","EU","AF", + "AS","AS","AS","OC","SA","AF","SA","EU","AF","AS", + "AF","NA","AS","AF","AF","OC","AF","OC","AF","SA", + "EU","EU","AS","OC","OC","OC","AS","SA","SA","OC", + "OC","AS","AS","EU","SA","OC","SA","AS","EU","OC", + "SA","AS","AF","EU","AS","AF","AS","OC","AF","AF", + "EU","AS","AF","EU","EU","EU","AF","EU","AF","AF", + "SA","AF","SA","AS","AF","SA","AF","AF","AF","AS", + "AS","OC","AS","AF","OC","AS","AS","SA","OC","AS", + "AF","EU","AF","OC","NA","SA","AS","EU","SA","SA", + "SA","SA","AS","OC","OC","OC","AS","AF","EU","AF", + "AF","EU","AF","--","--","--","EU","EU","EU","EU", + "SA","SA"}; + +const char * GeoIPDBDescription[NUM_DB_TYPES] = {NULL, "GeoIP Country Edition", "GeoIP City Edition, Rev 1", "GeoIP Region Edition, Rev 1", "GeoIP ISP Edition", "GeoIP Organization Edition", "GeoIP City Edition, Rev 0", "GeoIP Region Edition, Rev 0","GeoIP Proxy Edition","GeoIP ASNum Edition","GeoIP Netspeed Edition","GeoIP Domain Name Edition"}; + +char * custom_directory = NULL; + +void GeoIP_setup_custom_directory (char * dir) { + custom_directory = dir; +} +/* +char *_GeoIP_full_path_to(const char *file_name) { + int len; + char *path = malloc(sizeof(char) * 1024); + + if (custom_directory == NULL){ +#ifndef WIN32 + memset(path, 0, sizeof(char) * 1024); + snprintf(path, sizeof(char) * 1024 - 1, "%s/%s", GEOIPDATADIR, file_name); +#else + char buf[MAX_PATH], *p, *q = NULL; + memset(buf, 0, sizeof(buf)); + len = GetModuleFileName(GetModuleHandle(NULL), buf, sizeof(buf) - 1); + for (p = buf + len; p > buf; p--) + if (*p == '\\') + { + if (!q) + q = p; + else + *p = '/'; + } + *q = 0; + memset(path, 0, sizeof(char) * 1024); + snprintf(path, sizeof(char) * 1024 - 1, "%s/%s", buf, file_name); +#endif + } else { + len = strlen(custom_directory); + if (custom_directory[len-1] != '/') { + snprintf(path, sizeof(char) * 1024 - 1, "%s/%s",custom_directory, file_name); + } else { + snprintf(path, sizeof(char) * 1024 - 1, "%s%s", custom_directory, file_name); + } + } + return path; +} + +char ** GeoIPDBFileName = NULL; + +void _GeoIP_setup_dbfilename() { + if (NULL == GeoIPDBFileName) { + GeoIPDBFileName = malloc(sizeof(char *) * NUM_DB_TYPES); + memset(GeoIPDBFileName, 0, sizeof(char *) * NUM_DB_TYPES); + + GeoIPDBFileName[GEOIP_COUNTRY_EDITION] = _GeoIP_full_path_to("GeoIP.dat"); + GeoIPDBFileName[GEOIP_REGION_EDITION_REV0] = _GeoIP_full_path_to("GeoIPRegion.dat"); + GeoIPDBFileName[GEOIP_REGION_EDITION_REV1] = _GeoIP_full_path_to("GeoIPRegion.dat"); + GeoIPDBFileName[GEOIP_CITY_EDITION_REV0] = _GeoIP_full_path_to("GeoIPCity.dat"); + GeoIPDBFileName[GEOIP_CITY_EDITION_REV1] = _GeoIP_full_path_to("GeoIPCity.dat"); + GeoIPDBFileName[GEOIP_ISP_EDITION] = _GeoIP_full_path_to("GeoIPISP.dat"); + GeoIPDBFileName[GEOIP_ORG_EDITION] = _GeoIP_full_path_to("GeoIPOrg.dat"); + GeoIPDBFileName[GEOIP_PROXY_EDITION] = _GeoIP_full_path_to("GeoIPProxy.dat"); + GeoIPDBFileName[GEOIP_ASNUM_EDITION] = _GeoIP_full_path_to("GeoIPASNum.dat"); + GeoIPDBFileName[GEOIP_NETSPEED_EDITION] = _GeoIP_full_path_to("GeoIPNetSpeed.dat"); + GeoIPDBFileName[GEOIP_DOMAIN_EDITION] = _GeoIP_full_path_to("GeoIPDomain.dat"); + } +} +*/ + +static +int _file_exists(const char *file_name) { + struct stat file_stat; + return( (stat(file_name, &file_stat) == 0) ? 1:0); +} +/* +int GeoIP_db_avail(int type) { + const char * filePath; + if (type < 0 || type >= NUM_DB_TYPES) { + return 0; + } + _GeoIP_setup_dbfilename(); + filePath = GeoIPDBFileName[type]; + if (NULL == filePath) { + return 0; + } + return _file_exists(filePath); +} +*/ +static +void _setup_segments(GeoIP * gi) { + int i, j; + unsigned char delim[3]; + unsigned char buf[SEGMENT_RECORD_LENGTH]; + + gi->databaseSegments = NULL; + + /* default to GeoIP Country Edition */ + gi->databaseType = GEOIP_COUNTRY_EDITION; + gi->record_length = STANDARD_RECORD_LENGTH; + fseek(gi->GeoIPDatabase, -3l, SEEK_END); + for (i = 0; i < STRUCTURE_INFO_MAX_SIZE; i++) { + fread(delim, 1, 3, gi->GeoIPDatabase); + if (delim[0] == 255 && delim[1] == 255 && delim[2] == 255) { + fread(&gi->databaseType, 1, 1, gi->GeoIPDatabase); + if (gi->databaseType >= 106) { + /* backwards compatibility with databases from April 2003 and earlier */ + gi->databaseType -= 105; + } + + if (gi->databaseType == GEOIP_REGION_EDITION_REV0) { + /* Region Edition, pre June 2003 */ + gi->databaseSegments = (unsigned int*)malloc(sizeof(unsigned int)); + gi->databaseSegments[0] = STATE_BEGIN_REV0; + } else if (gi->databaseType == GEOIP_REGION_EDITION_REV1) { + /* Region Edition, post June 2003 */ + gi->databaseSegments = (unsigned int*)malloc(sizeof(unsigned int)); + gi->databaseSegments[0] = STATE_BEGIN_REV1; + } else if (gi->databaseType == GEOIP_CITY_EDITION_REV0 || + gi->databaseType == GEOIP_CITY_EDITION_REV1 || + gi->databaseType == GEOIP_ORG_EDITION || + gi->databaseType == GEOIP_ISP_EDITION || + gi->databaseType == GEOIP_ASNUM_EDITION) { + /* City/Org Editions have two segments, read offset of second segment */ + gi->databaseSegments = (unsigned int*)malloc(sizeof(unsigned int)); + gi->databaseSegments[0] = 0; + fread(buf, SEGMENT_RECORD_LENGTH, 1, gi->GeoIPDatabase); + for (j = 0; j < SEGMENT_RECORD_LENGTH; j++) { + gi->databaseSegments[0] += (buf[j] << (j * 8)); + } + if (gi->databaseType == GEOIP_ORG_EDITION || + gi->databaseType == GEOIP_ISP_EDITION) + gi->record_length = ORG_RECORD_LENGTH; + } + break; + } else { + fseek(gi->GeoIPDatabase, -4l, SEEK_CUR); + } + } + if (gi->databaseType == GEOIP_COUNTRY_EDITION || + gi->databaseType == GEOIP_PROXY_EDITION || + gi->databaseType == GEOIP_NETSPEED_EDITION) { + gi->databaseSegments = (unsigned int*)malloc(sizeof(unsigned int)); + gi->databaseSegments[0] = COUNTRY_BEGIN; + } +} + +static +int _check_mtime(GeoIP *gi) { + struct stat buf; + if (gi->flags & GEOIP_CHECK_CACHE) { + if (stat(gi->file_path, &buf) != -1) { + if (buf.st_mtime != gi->mtime) { + int name_len; + wchar_t* wfilename; + wchar_t const* dst_start; + char const* src_start; + /* GeoIP Database file updated */ + if (gi->flags & (GEOIP_MEMORY_CACHE | GEOIP_MMAP_CACHE)) { +#if !defined WIN32 && !defined __OS2__ + if ( gi->flags & GEOIP_MMAP_CACHE) { + munmap(gi->cache, gi->size); + gi->cache = NULL; + } else +#endif + { + /* reload database into memory cache */ + if ((gi->cache = (unsigned char*) realloc(gi->cache, buf.st_size)) == NULL) { + fprintf(stderr,"Out of memory when reloading %s\n",gi->file_path); + return -1; + } + } + } + /* refresh filehandle */ + fclose(gi->GeoIPDatabase); +#ifdef WIN32 + assert(sizeof(wchar_t) == 2); + name_len = strlen(gi->file_path); + wfilename = malloc((name_len + 1) * sizeof(wchar_t)); + dst_start = wfilename; + src_start = gi->file_path; + ConvertUTF8toUTF16((const UTF8**)&src_start, (const UTF8*)src_start + + name_len+1, (UTF16**)&dst_start, (UTF16*)dst_start + name_len + 1 + , lenientConversion); + gi->GeoIPDatabase = _wfopen(wfilename,L"rb"); + free(wfilename); +#else + gi->GeoIPDatabase = fopen(gi->file_path,"rb"); +#endif + if (gi->GeoIPDatabase == NULL) { + fprintf(stderr,"Error Opening file %s when reloading\n",gi->file_path); + return -1; + } + gi->mtime = buf.st_mtime; + gi->size = buf.st_size; + +#if !defined WIN32 && !defined __OS2__ + if ( gi->flags & GEOIP_MMAP_CACHE) { + gi->cache = (unsigned char*)mmap(NULL, buf.st_size, PROT_READ, MAP_PRIVATE, fileno(gi->GeoIPDatabase), 0); + if ( gi->cache == MAP_FAILED ) { + + fprintf(stderr,"Error remapping file %s when reloading\n",gi->file_path); + gi->cache = 0; + return -1; + } + } else +#endif + if ( gi->flags & GEOIP_MEMORY_CACHE ) { + if (fread(gi->cache, sizeof(unsigned char), buf.st_size, gi->GeoIPDatabase) != (size_t) buf.st_size) { + fprintf(stderr,"Error reading file %s when reloading\n",gi->file_path); + return -1; + } + } + if (gi->databaseSegments != NULL) { + free(gi->databaseSegments); + gi->databaseSegments = NULL; + } + _setup_segments(gi); + if (gi->databaseSegments == NULL) { + fprintf(stderr, "Error reading file %s -- corrupt\n", gi->file_path); + return -1; + } + if (gi->flags & GEOIP_INDEX_CACHE) { + gi->index_cache = (unsigned char *) realloc(gi->index_cache, sizeof(unsigned char) * ((gi->databaseSegments[0] * (long)gi->record_length * 2))); + if (gi->index_cache != NULL) { + fseek(gi->GeoIPDatabase, 0, SEEK_SET); + if (fread(gi->index_cache, sizeof(unsigned char), gi->databaseSegments[0] * (long)gi->record_length * 2, gi->GeoIPDatabase) != (size_t) (gi->databaseSegments[0]*(long)gi->record_length * 2)) { + fprintf(stderr,"Error reading file %s where reloading\n",gi->file_path); + return -1; + } + } + } + } + } + } + return 0; +} + +unsigned int _GeoIP_seek_record (GeoIP *gi, unsigned long ipnum) { + int depth; + unsigned int x; + unsigned char stack_buffer[2 * MAX_RECORD_LENGTH]; + const unsigned char *buf = (gi->cache == NULL) ? stack_buffer : NULL; + unsigned int offset = 0; + + const unsigned char * p; + int j; + + _check_mtime(gi); + for (depth = 31; depth >= 0; depth--) { + if (gi->cache == NULL && gi->index_cache == NULL) { + /* read from disk */ + fseek(gi->GeoIPDatabase, (long)gi->record_length * 2 * offset, SEEK_SET); + fread(stack_buffer,gi->record_length,2,gi->GeoIPDatabase); + } else if (gi->index_cache == NULL) { + /* simply point to record in memory */ + buf = gi->cache + (long)gi->record_length * 2 *offset; + } else { + buf = gi->index_cache + (long)gi->record_length * 2 * offset; + } + + if (ipnum & (1 << depth)) { + /* Take the right-hand branch */ + if ( gi->record_length == 3 ) { + /* Most common case is completely unrolled and uses constants. */ + x = (buf[3*1 + 0] << (0*8)) + + (buf[3*1 + 1] << (1*8)) + + (buf[3*1 + 2] << (2*8)); + + } else { + /* General case */ + j = gi->record_length; + p = &buf[2*j]; + x = 0; + do { + x <<= 8; + x += *(--p); + } while ( --j ); + } + + } else { + /* Take the left-hand branch */ + if ( gi->record_length == 3 ) { + /* Most common case is completely unrolled and uses constants. */ + x = (buf[3*0 + 0] << (0*8)) + + (buf[3*0 + 1] << (1*8)) + + (buf[3*0 + 2] << (2*8)); + } else { + /* General case */ + j = gi->record_length; + p = &buf[1*j]; + x = 0; + do { + x <<= 8; + x += *(--p); + } while ( --j ); + } + } + + if (x >= gi->databaseSegments[0]) { + gi->netmask = 32 - depth; + return x; + } + offset = x; + } + + /* shouldn't reach here */ + fprintf(stderr,"Error Traversing Database for ipnum = %lu - Perhaps database is corrupt?\n",ipnum); + return 0; +} + +unsigned long +_GeoIP_addr_to_num(const char *addr) +{ + unsigned int c, octet, t; + unsigned long ipnum; + int i = 3; + + octet = ipnum = 0; + while ((c = *addr++)) { + if (c == '.') { + if (octet > 255) + return 0; + ipnum <<= 8; + ipnum += octet; + i--; + octet = 0; + } else { + t = octet; + octet <<= 3; + octet += t; + octet += t; + c -= '0'; + if (c > 9) + return 0; + octet += c; + } + } + if ((octet > 255) || (i != 0)) + return 0; + ipnum <<= 8; + return ipnum + octet; +} +/* +GeoIP* GeoIP_open_type (int type, int flags) { + GeoIP * gi; + const char * filePath; + if (type < 0 || type >= NUM_DB_TYPES) { + printf("Invalid database type %d\n", type); + return NULL; + } + _GeoIP_setup_dbfilename(); + filePath = GeoIPDBFileName[type]; + if (filePath == NULL) { + printf("Invalid database type %d\n", type); + return NULL; + } + gi = GeoIP_open (filePath, flags); + return gi; +} + +GeoIP* GeoIP_new (int flags) { + GeoIP * gi; + _GeoIP_setup_dbfilename(); + gi = GeoIP_open (GeoIPDBFileName[GEOIP_COUNTRY_EDITION], flags); + return gi; +} +*/ + +GeoIP* GeoIP_open (const char * filename, int flags) { + struct stat buf; + GeoIP * gi; + size_t len; +#ifdef WIN32 + int name_len; + wchar_t* wfilename; + wchar_t const* dst_start; + char const* src_start; +#endif + + gi = (GeoIP *)malloc(sizeof(GeoIP)); + if (gi == NULL) + return NULL; + len = sizeof(char) * (strlen(filename)+1); + gi->file_path = (char*)malloc(len); + if (gi->file_path == NULL) { + free(gi); + return NULL; + } + strncpy(gi->file_path, filename, len); +#ifdef WIN32 + assert(sizeof(wchar_t) == 2); + name_len = strlen(filename); + wfilename = malloc((name_len + 1) * sizeof(wchar_t)); + dst_start = wfilename; + src_start = filename; + ConvertUTF8toUTF16((const UTF8**)&src_start, (const UTF8*)src_start + + name_len+1, (UTF16**)&dst_start, (UTF16*)dst_start + name_len + 1 + , lenientConversion); + gi->GeoIPDatabase = _wfopen(wfilename,L"rb"); + free(wfilename); +#else + gi->GeoIPDatabase = fopen(filename,"rb"); +#endif + if (gi->GeoIPDatabase == NULL) { + fprintf(stderr,"Error Opening file %s\n",filename); + free(gi->file_path); + free(gi); + return NULL; + } else { + if (flags & (GEOIP_MEMORY_CACHE | GEOIP_MMAP_CACHE) ) { + if (fstat(fileno(gi->GeoIPDatabase), &buf) == -1) { + fprintf(stderr,"Error stating file %s\n",filename); + free(gi->file_path); + free(gi); + return NULL; + } + gi->mtime = buf.st_mtime; + gi->size = buf.st_size; +#if !defined WIN32 && !defined __OS2__ + /* MMAP added my Peter Shipley */ + if ( flags & GEOIP_MMAP_CACHE) { + gi->cache = (unsigned char*)mmap(NULL, buf.st_size, PROT_READ, MAP_PRIVATE, fileno(gi->GeoIPDatabase), 0); + if ( gi->cache == MAP_FAILED ) { + fprintf(stderr,"Error mmaping file %s\n",filename); + free(gi->file_path); + free(gi); + return NULL; + } + } else +#endif + { + gi->cache = (unsigned char *) malloc(sizeof(unsigned char) * buf.st_size); + + if (gi->cache != NULL) { + if (fread(gi->cache, sizeof(unsigned char), buf.st_size, gi->GeoIPDatabase) != (size_t) buf.st_size) { + fprintf(stderr,"Error reading file %s\n",filename); + free(gi->cache); + free(gi->file_path); + free(gi); + return NULL; + } + } + } + } else { + if (flags & GEOIP_CHECK_CACHE) { + if (fstat(fileno(gi->GeoIPDatabase), &buf) == -1) { + fprintf(stderr,"Error stating file %s\n",filename); + free(gi->file_path); + free(gi); + return NULL; + } + gi->mtime = buf.st_mtime; + } + gi->cache = NULL; + } + gi->flags = flags; + gi->charset = GEOIP_CHARSET_ISO_8859_1; + + _setup_segments(gi); + if (flags & GEOIP_INDEX_CACHE) { + gi->index_cache = (unsigned char *) malloc(sizeof(unsigned char) * ((gi->databaseSegments[0] * (long)gi->record_length * 2))); + if (gi->index_cache != NULL) { + fseek(gi->GeoIPDatabase, 0, SEEK_SET); + if (fread(gi->index_cache, sizeof(unsigned char), gi->databaseSegments[0] * (long)gi->record_length * 2, gi->GeoIPDatabase) != (size_t) (gi->databaseSegments[0]*(long)gi->record_length * 2)) { + fprintf(stderr,"Error reading file %s\n",filename); + free(gi->databaseSegments); + free(gi->index_cache); + free(gi); + return NULL; + } + } + } else { + gi->index_cache = NULL; + } + return gi; + } +} + +void GeoIP_delete (GeoIP *gi) { + if (gi == NULL ) + return; + if (gi->GeoIPDatabase != NULL) + fclose(gi->GeoIPDatabase); + if (gi->cache != NULL) { +#if !defined WIN32 && !defined __OS2__ + if ( gi->flags & GEOIP_MMAP_CACHE) { + munmap(gi->cache, gi->size); + } else +#endif + { + free(gi->cache); + } + gi->cache = NULL; + } + if (gi->index_cache != NULL) + free(gi->index_cache); + if (gi->file_path != NULL) + free(gi->file_path); + if (gi->databaseSegments != NULL) + free(gi->databaseSegments); + free(gi); +} + +const char *GeoIP_country_code_by_name (GeoIP* gi, const char *name) { + int country_id; + country_id = GeoIP_id_by_name(gi, name); + return (country_id > 0) ? GeoIP_country_code[country_id] : NULL; +} + +const char *GeoIP_country_code3_by_name (GeoIP* gi, const char *name) { + int country_id; + country_id = GeoIP_id_by_name(gi, name); + return (country_id > 0) ? GeoIP_country_code3[country_id] : NULL; +} + +const char *GeoIP_country_name_by_name (GeoIP* gi, const char *name) { + int country_id; + country_id = GeoIP_id_by_name(gi, name); + return (country_id > 0) ? GeoIP_country_name[country_id] : NULL; +} + +unsigned long _GeoIP_lookupaddress (const char *host) { + unsigned long addr = inet_addr(host); + struct hostent phe2; + struct hostent * phe = &phe2; + char *buf = NULL; + int buflength = 16384; + int herr = 0; + int result = 0; +#ifdef HAVE_GETHOSTBYNAME_R + buf = malloc(buflength); +#endif + if (addr == INADDR_NONE) { +#ifdef HAVE_GETHOSTBYNAME_R + while (1) { + /* we use gethostbyname_r here because it is thread-safe and gethostbyname is not */ +#ifdef GETHOSTBYNAME_R_RETURNS_INT + result = gethostbyname_r(host,&phe2,buf,buflength,&phe,&herr); +#else + phe = gethostbyname_r(host,&phe2,buf,buflength,&herr); +#endif + if (herr != ERANGE) + break; + if (result == 0) + break; + /* double the buffer if the buffer is too small */ + buflength = buflength * 2; + buf = realloc(buf,buflength); + } +#endif +#ifndef HAVE_GETHOSTBYNAME_R + /* Some systems do not support gethostbyname_r, such as Mac OS X */ + phe = gethostbyname(host); +#endif + if (!phe || result != 0) { + free(buf); + return 0; + } + addr = *((unsigned long *) phe->h_addr_list[0]); + } +#ifdef HAVE_GETHOSTBYNAME_R + free(buf); +#endif + return ntohl(addr); +} + +int GeoIP_id_by_name (GeoIP* gi, const char *name) { + unsigned long ipnum; + int ret; + if (name == NULL) { + return 0; + } + if (gi->databaseType != GEOIP_COUNTRY_EDITION && gi->databaseType != GEOIP_PROXY_EDITION && gi->databaseType != GEOIP_NETSPEED_EDITION) { + printf("Invalid database type %s, expected %s\n", GeoIPDBDescription[(int)gi->databaseType], GeoIPDBDescription[GEOIP_COUNTRY_EDITION]); + return 0; + } + if (!(ipnum = _GeoIP_lookupaddress(name))) + return 0; + ret = _GeoIP_seek_record(gi, ipnum) - COUNTRY_BEGIN; + return ret; + +} + +const char *GeoIP_country_code_by_addr (GeoIP* gi, const char *addr) { + int country_id; + country_id = GeoIP_id_by_addr(gi, addr); + return (country_id > 0) ? GeoIP_country_code[country_id] : NULL; +} + +const char *GeoIP_country_code3_by_addr (GeoIP* gi, const char *addr) { + int country_id; + country_id = GeoIP_id_by_addr(gi, addr); + return (country_id > 0) ? GeoIP_country_code3[country_id] : NULL; + return GeoIP_country_code3[country_id]; +} + +const char *GeoIP_country_name_by_addr (GeoIP* gi, const char *addr) { + int country_id; + country_id = GeoIP_id_by_addr(gi, addr); + return (country_id > 0) ? GeoIP_country_name[country_id] : NULL; + return GeoIP_country_name[country_id]; +} + +const char *GeoIP_country_name_by_ipnum (GeoIP* gi, unsigned long ipnum) { + int country_id; + country_id = GeoIP_id_by_ipnum(gi, ipnum); + return (country_id > 0) ? GeoIP_country_name[country_id] : NULL; +} + +const char *GeoIP_country_code_by_ipnum (GeoIP* gi, unsigned long ipnum) { + int country_id; + country_id = GeoIP_id_by_ipnum(gi, ipnum); + return (country_id > 0) ? GeoIP_country_code[country_id] : NULL; +} + +const char *GeoIP_country_code3_by_ipnum (GeoIP* gi, unsigned long ipnum) { + int country_id; + country_id = GeoIP_id_by_ipnum(gi, ipnum); + return (country_id > 0) ? GeoIP_country_code3[country_id] : NULL; +} + +int GeoIP_country_id_by_addr (GeoIP* gi, const char *addr) { + return GeoIP_id_by_addr(gi, addr); +} + +int GeoIP_country_id_by_name (GeoIP* gi, const char *host) { + return GeoIP_id_by_name(gi, host); +} + +int GeoIP_id_by_addr (GeoIP* gi, const char *addr) { + unsigned long ipnum; + int ret; + if (addr == NULL) { + return 0; + } + if (gi->databaseType != GEOIP_COUNTRY_EDITION && + gi->databaseType != GEOIP_PROXY_EDITION && + gi->databaseType != GEOIP_NETSPEED_EDITION) { + printf("Invalid database type %s, expected %s\n", + GeoIPDBDescription[(int)gi->databaseType], + GeoIPDBDescription[GEOIP_COUNTRY_EDITION]); + return 0; + } + ipnum = _GeoIP_addr_to_num(addr); + ret = _GeoIP_seek_record(gi, ipnum) - COUNTRY_BEGIN; + return ret; +} + +int GeoIP_id_by_ipnum (GeoIP* gi, unsigned long ipnum) { + int ret; + if (ipnum == 0) { + return 0; + } + if (gi->databaseType != GEOIP_COUNTRY_EDITION && + gi->databaseType != GEOIP_PROXY_EDITION && + gi->databaseType != GEOIP_NETSPEED_EDITION) { + printf("Invalid database type %s, expected %s\n", + GeoIPDBDescription[(int)gi->databaseType], + GeoIPDBDescription[GEOIP_COUNTRY_EDITION]); + return 0; + } + ret = _GeoIP_seek_record(gi, ipnum) - COUNTRY_BEGIN; + return ret; +} + +char *GeoIP_database_info (GeoIP* gi) { + int i; + unsigned char buf[3]; + char *retval; + int hasStructureInfo = 0; + + if(gi == NULL) + return NULL; + + _check_mtime(gi); + fseek(gi->GeoIPDatabase, -3l, SEEK_END); + + /* first get past the database structure information */ + for (i = 0; i < STRUCTURE_INFO_MAX_SIZE; i++) { + fread(buf, 1, 3, gi->GeoIPDatabase); + if (buf[0] == 255 && buf[1] == 255 && buf[2] == 255) { + hasStructureInfo = 1; + break; + } + fseek(gi->GeoIPDatabase, -4l, SEEK_CUR); + } + if (hasStructureInfo == 1) { + fseek(gi->GeoIPDatabase, -6l, SEEK_CUR); + } else { + /* no structure info, must be pre Sep 2002 database, go back to end */ + fseek(gi->GeoIPDatabase, -3l, SEEK_END); + } + + for (i = 0; i < DATABASE_INFO_MAX_SIZE; i++) { + fread(buf, 1, 3, gi->GeoIPDatabase); + if (buf[0] == 0 && buf[1] == 0 && buf[2] == 0) { + retval = (char*)malloc(sizeof(char) * (i+1)); + if (retval == NULL) { + return NULL; + } + fread(retval, 1, i, gi->GeoIPDatabase); + retval[i] = '\0'; + return retval; + } + fseek(gi->GeoIPDatabase, -4l, SEEK_CUR); + } + return NULL; +} + +/* GeoIP Region Edition functions */ + +void GeoIP_assign_region_by_inetaddr(GeoIP* gi, unsigned long inetaddr, GeoIPRegion *region) { + unsigned int seek_region; + + /* This also writes in the terminating NULs (if you decide to + * keep them) and clear any fields that are not set. */ + memset(region, 0, sizeof(GeoIPRegion)); + + seek_region = _GeoIP_seek_record(gi, ntohl(inetaddr)); + + if (gi->databaseType == GEOIP_REGION_EDITION_REV0) { + /* Region Edition, pre June 2003 */ + seek_region -= STATE_BEGIN_REV0; + if (seek_region >= 1000) { + region->country_code[0] = 'U'; + region->country_code[1] = 'S'; + region->region[0] = (char) ((seek_region - 1000)/26 + 65); + region->region[1] = (char) ((seek_region - 1000)%26 + 65); + } else { + memcpy(region->country_code, GeoIP_country_code[seek_region], 2); + } + } else if (gi->databaseType == GEOIP_REGION_EDITION_REV1) { + /* Region Edition, post June 2003 */ + seek_region -= STATE_BEGIN_REV1; + if (seek_region < US_OFFSET) { + /* Unknown */ + /* we don't need to do anything here b/c we memset region to 0 */ + } else if (seek_region < CANADA_OFFSET) { + /* USA State */ + region->country_code[0] = 'U'; + region->country_code[1] = 'S'; + region->region[0] = (char) ((seek_region - US_OFFSET)/26 + 65); + region->region[1] = (char) ((seek_region - US_OFFSET)%26 + 65); + } else if (seek_region < WORLD_OFFSET) { + /* Canada Province */ + region->country_code[0] = 'C'; + region->country_code[1] = 'A'; + region->region[0] = (char) ((seek_region - CANADA_OFFSET)/26 + 65); + region->region[1] = (char) ((seek_region - CANADA_OFFSET)%26 + 65); + } else { + /* Not US or Canada */ + memcpy(region->country_code, GeoIP_country_code[(seek_region - WORLD_OFFSET) / FIPS_RANGE], 2); + } + } +} + +static +GeoIPRegion * _get_region(GeoIP* gi, unsigned long ipnum) { + GeoIPRegion * region; + + region = (GeoIPRegion*)malloc(sizeof(GeoIPRegion)); + if (region) { + GeoIP_assign_region_by_inetaddr(gi, htonl(ipnum), region); + } + return region; +} + +GeoIPRegion * GeoIP_region_by_addr (GeoIP* gi, const char *addr) { + unsigned long ipnum; + if (addr == NULL) { + return 0; + } + if (gi->databaseType != GEOIP_REGION_EDITION_REV0 && + gi->databaseType != GEOIP_REGION_EDITION_REV1) { + printf("Invalid database type %s, expected %s\n", GeoIPDBDescription[(int)gi->databaseType], GeoIPDBDescription[GEOIP_REGION_EDITION_REV1]); + return 0; + } + ipnum = _GeoIP_addr_to_num(addr); + return _get_region(gi, ipnum); +} + +GeoIPRegion * GeoIP_region_by_name (GeoIP* gi, const char *name) { + unsigned long ipnum; + if (name == NULL) { + return 0; + } + if (gi->databaseType != GEOIP_REGION_EDITION_REV0 && + gi->databaseType != GEOIP_REGION_EDITION_REV1) { + printf("Invalid database type %s, expected %s\n", GeoIPDBDescription[(int)gi->databaseType], GeoIPDBDescription[GEOIP_REGION_EDITION_REV1]); + return 0; + } + if (!(ipnum = _GeoIP_lookupaddress(name))) + return 0; + return _get_region(gi, ipnum); +} + +GeoIPRegion * GeoIP_region_by_ipnum (GeoIP* gi, unsigned long ipnum) { + if (gi->databaseType != GEOIP_REGION_EDITION_REV0 && + gi->databaseType != GEOIP_REGION_EDITION_REV1) { + printf("Invalid database type %s, expected %s\n", GeoIPDBDescription[(int)gi->databaseType], GeoIPDBDescription[GEOIP_REGION_EDITION_REV1]); + return 0; + } + return _get_region(gi, ipnum); +} + +void GeoIPRegion_delete (GeoIPRegion *gir) { + free(gir); +} + +/* GeoIP Organization, ISP and AS Number Edition private method */ +static +char *_get_name (GeoIP* gi, unsigned long ipnum) { + int seek_org; + char buf[MAX_ORG_RECORD_LENGTH]; + char * org_buf, * buf_pointer; + int record_pointer; + size_t len; + + if (gi->databaseType != GEOIP_ORG_EDITION && + gi->databaseType != GEOIP_ISP_EDITION && + gi->databaseType != GEOIP_ASNUM_EDITION) { + printf("Invalid database type %s, expected %s\n", GeoIPDBDescription[(int)gi->databaseType], GeoIPDBDescription[GEOIP_ORG_EDITION]); + return 0; + } + + seek_org = _GeoIP_seek_record(gi, ipnum); + if (seek_org == gi->databaseSegments[0]) + return NULL; + + record_pointer = seek_org + (2 * gi->record_length - 1) * gi->databaseSegments[0]; + + if (gi->cache == NULL) { + fseek(gi->GeoIPDatabase, record_pointer, SEEK_SET); + fread(buf, sizeof(char), MAX_ORG_RECORD_LENGTH, gi->GeoIPDatabase); + len = sizeof(char) * (strlen(buf)+1); + org_buf = (char*)malloc(len); + strncpy(org_buf, buf, len); + } else { + buf_pointer = (char*)(gi->cache + (long)record_pointer); + len = sizeof(char) * (strlen(buf_pointer)+1); + org_buf = (char*)malloc(len); + strncpy(org_buf, buf_pointer, len); + } + return org_buf; +} + +char *GeoIP_name_by_ipnum (GeoIP* gi, unsigned long ipnum) { + return _get_name(gi,ipnum); +} + +char *GeoIP_name_by_addr (GeoIP* gi, const char *addr) { + unsigned long ipnum; + if (addr == NULL) { + return 0; + } + ipnum = _GeoIP_addr_to_num(addr); + return _get_name(gi, ipnum); +} + +char *GeoIP_name_by_name (GeoIP* gi, const char *name) { + unsigned long ipnum; + if (name == NULL) { + return 0; + } + if (!(ipnum = _GeoIP_lookupaddress(name))) + return 0; + return _get_name(gi, ipnum); +} + +char *GeoIP_org_by_ipnum (GeoIP* gi, unsigned long ipnum) { + return GeoIP_name_by_ipnum(gi, ipnum); +} + +char *GeoIP_org_by_addr (GeoIP* gi, const char *addr) { + return GeoIP_name_by_addr(gi, addr); +} + +char *GeoIP_org_by_name (GeoIP* gi, const char *name) { + return GeoIP_name_by_name(gi, name); +} + +unsigned char GeoIP_database_edition (GeoIP* gi) { + return gi->databaseType; +} + +int GeoIP_charset( GeoIP* gi){ + return gi->charset; +} + +int GeoIP_set_charset( GeoIP* gi, int charset ){ + int old_charset = gi->charset; + gi->charset = charset; + return old_charset; +} + +int GeoIP_last_netmask (GeoIP* gi) { + return gi->netmask; +} + diff --git a/apps/Launcher/ext/libtorrent/src/Makefile.am b/apps/Launcher/ext/libtorrent/src/Makefile.am new file mode 100644 index 0000000000..a285470e49 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/Makefile.am @@ -0,0 +1,141 @@ +AUTOMAKE_OPTIONS = subdir-objects + +lib_LTLIBRARIES = libtorrent-rasterbar.la + +if ENABLE_DHT +KADEMLIA_SOURCES = \ + kademlia/dht_tracker.cpp \ + kademlia/find_data.cpp \ + kademlia/node.cpp \ + kademlia/node_id.cpp \ + kademlia/refresh.cpp \ + kademlia/routing_table.cpp \ + kademlia/rpc_manager.cpp \ + kademlia/logging.cpp \ + kademlia/traversal_algorithm.cpp \ + kademlia/get_peers.cpp \ + kademlia/get_item.cpp \ + kademlia/item.cpp \ + ../ed25519/src/add_scalar.cpp \ + ../ed25519/src/fe.cpp \ + ../ed25519/src/ge.cpp \ + ../ed25519/src/key_exchange.cpp \ + ../ed25519/src/keypair.cpp \ + ../ed25519/src/sc.cpp \ + ../ed25519/src/seed.cpp \ + ../ed25519/src/sha512.cpp \ + ../ed25519/src/sign.cpp \ + ../ed25519/src/verify.cpp +endif + +if WITH_SHIPPED_GEOIP +GEOIP_SOURCES = GeoIP.c +endif + +if WITH_OPENSSL +ASIO_OPENSSL_SOURCES = asio_ssl.cpp +endif + +libtorrent_rasterbar_la_SOURCES = \ + web_connection_base.cpp \ + alert.cpp \ + alert_manager.cpp \ + allocator.cpp \ + asio.cpp \ + assert.cpp \ + bandwidth_limit.cpp \ + bandwidth_manager.cpp \ + bandwidth_queue_entry.cpp \ + bloom_filter.cpp \ + broadcast_socket.cpp \ + bt_peer_connection.cpp \ + chained_buffer.cpp \ + connection_queue.cpp \ + ConvertUTF.cpp \ + create_torrent.cpp \ + disk_buffer_holder.cpp \ + disk_buffer_pool.cpp \ + disk_io_thread.cpp \ + entry.cpp \ + enum_net.cpp \ + error_code.cpp \ + escape_string.cpp \ + file.cpp \ + file_pool.cpp \ + file_storage.cpp \ + gzip.cpp \ + hasher.cpp \ + http_connection.cpp \ + http_parser.cpp \ + http_seed_connection.cpp \ + http_stream.cpp \ + http_tracker_connection.cpp \ + i2p_stream.cpp \ + identify_client.cpp \ + instantiate_connection.cpp \ + ip_filter.cpp \ + ip_voter.cpp \ + lazy_bdecode.cpp \ + logger.cpp \ + lsd.cpp \ + lt_trackers.cpp \ + magnet_uri.cpp \ + metadata_transfer.cpp \ + mpi.c \ + natpmp.cpp \ + parse_url.cpp \ + pe_crypto.cpp \ + peer_connection.cpp \ + piece_picker.cpp \ + packet_buffer.cpp \ + policy.cpp \ + puff.cpp \ + random.cpp \ + rss.cpp \ + session.cpp \ + session_impl.cpp \ + settings.cpp \ + sha1.cpp \ + smart_ban.cpp \ + socket_io.cpp \ + socket_type.cpp \ + socks5_stream.cpp \ + stat.cpp \ + storage.cpp \ + string_util.cpp \ + thread.cpp \ + torrent.cpp \ + torrent_handle.cpp \ + torrent_info.cpp \ + time.cpp \ + timestamp_history.cpp \ + tracker_manager.cpp \ + udp_socket.cpp \ + udp_tracker_connection.cpp \ + upnp.cpp \ + ut_metadata.cpp \ + ut_pex.cpp \ + utf8.cpp \ + utp_socket_manager.cpp \ + utp_stream.cpp \ + web_peer_connection.cpp \ + xml_parse.cpp \ + \ + $(KADEMLIA_SOURCES) \ + $(GEOIP_SOURCES) \ + $(ASIO_OPENSSL_SOURCES) + +#libtorrent_rasterbar_la_LDFLAGS = $(LDFLAGS) -version-info $(INTERFACE_VERSION_INFO) +libtorrent_rasterbar_la_LDFLAGS = -version-info $(INTERFACE_VERSION_INFO) + +#libtorrent_rasterbar_la_LIBADD = @BOOST_SYSTEM_LIB@ @BOOST_FILESYSTEM_LIB@ @BOOST_THREAD_LIB@ @OPENSSL_LIBS@ +libtorrent_rasterbar_la_LIBADD = @BOOST_SYSTEM_LIB@ @OPENSSL_LIBS@ + +#AM_CXXFLAGS= -ftemplate-depth-100 -I$(top_srcdir)/include @DEBUGFLAGS@ @OPENSSL_INCLUDES@ +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/ed25519/src @DEBUGFLAGS@ @OPENSSL_INCLUDES@ +AM_CFLAGS = -I$(top_srcdir)/ed25519/src -std=c99 + +#AM_CFLAGS= -I$(top_srcdir)/include @DEBUGFLAGS@ +#AM_LDFLAGS = $(LDFLAGS) @BOOST_SYSTEM_LIB@ @BOOST_FILESYSTEM_LIB@ @BOOST_THREAD_LIB@ @OPENSSL_LDFLAGS@ @OPENSSL_LIBS@ +AM_LDFLAGS = @OPENSSL_LDFLAGS@ + diff --git a/apps/Launcher/ext/libtorrent/src/Makefile.in b/apps/Launcher/ext/libtorrent/src/Makefile.in new file mode 100644 index 0000000000..99b32c067c --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/Makefile.in @@ -0,0 +1,1116 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = src +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_python.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_check_geoip.m4 \ + $(top_srcdir)/m4/ax_check_openssl.m4 \ + $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/gettext-lib.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkgconfig.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +libtorrent_rasterbar_la_DEPENDENCIES = +am__libtorrent_rasterbar_la_SOURCES_DIST = web_connection_base.cpp \ + alert.cpp alert_manager.cpp allocator.cpp asio.cpp assert.cpp \ + bandwidth_limit.cpp bandwidth_manager.cpp \ + bandwidth_queue_entry.cpp bloom_filter.cpp \ + broadcast_socket.cpp bt_peer_connection.cpp chained_buffer.cpp \ + connection_queue.cpp ConvertUTF.cpp create_torrent.cpp \ + disk_buffer_holder.cpp disk_buffer_pool.cpp disk_io_thread.cpp \ + entry.cpp enum_net.cpp error_code.cpp escape_string.cpp \ + file.cpp file_pool.cpp file_storage.cpp gzip.cpp hasher.cpp \ + http_connection.cpp http_parser.cpp http_seed_connection.cpp \ + http_stream.cpp http_tracker_connection.cpp i2p_stream.cpp \ + identify_client.cpp instantiate_connection.cpp ip_filter.cpp \ + ip_voter.cpp lazy_bdecode.cpp logger.cpp lsd.cpp \ + lt_trackers.cpp magnet_uri.cpp metadata_transfer.cpp mpi.c \ + natpmp.cpp parse_url.cpp pe_crypto.cpp peer_connection.cpp \ + piece_picker.cpp packet_buffer.cpp policy.cpp puff.cpp \ + random.cpp rss.cpp session.cpp session_impl.cpp settings.cpp \ + sha1.cpp smart_ban.cpp socket_io.cpp socket_type.cpp \ + socks5_stream.cpp stat.cpp storage.cpp string_util.cpp \ + thread.cpp torrent.cpp torrent_handle.cpp torrent_info.cpp \ + time.cpp timestamp_history.cpp tracker_manager.cpp \ + udp_socket.cpp udp_tracker_connection.cpp upnp.cpp \ + ut_metadata.cpp ut_pex.cpp utf8.cpp utp_socket_manager.cpp \ + utp_stream.cpp web_peer_connection.cpp xml_parse.cpp \ + kademlia/dht_tracker.cpp kademlia/find_data.cpp \ + kademlia/node.cpp kademlia/node_id.cpp kademlia/refresh.cpp \ + kademlia/routing_table.cpp kademlia/rpc_manager.cpp \ + kademlia/logging.cpp kademlia/traversal_algorithm.cpp \ + kademlia/get_peers.cpp kademlia/get_item.cpp kademlia/item.cpp \ + ../ed25519/src/add_scalar.cpp ../ed25519/src/fe.cpp \ + ../ed25519/src/ge.cpp ../ed25519/src/key_exchange.cpp \ + ../ed25519/src/keypair.cpp ../ed25519/src/sc.cpp \ + ../ed25519/src/seed.cpp ../ed25519/src/sha512.cpp \ + ../ed25519/src/sign.cpp ../ed25519/src/verify.cpp GeoIP.c \ + asio_ssl.cpp +am__dirstamp = $(am__leading_dot)dirstamp +@ENABLE_DHT_TRUE@am__objects_1 = kademlia/dht_tracker.lo \ +@ENABLE_DHT_TRUE@ kademlia/find_data.lo kademlia/node.lo \ +@ENABLE_DHT_TRUE@ kademlia/node_id.lo kademlia/refresh.lo \ +@ENABLE_DHT_TRUE@ kademlia/routing_table.lo \ +@ENABLE_DHT_TRUE@ kademlia/rpc_manager.lo kademlia/logging.lo \ +@ENABLE_DHT_TRUE@ kademlia/traversal_algorithm.lo \ +@ENABLE_DHT_TRUE@ kademlia/get_peers.lo kademlia/get_item.lo \ +@ENABLE_DHT_TRUE@ kademlia/item.lo ../ed25519/src/add_scalar.lo \ +@ENABLE_DHT_TRUE@ ../ed25519/src/fe.lo ../ed25519/src/ge.lo \ +@ENABLE_DHT_TRUE@ ../ed25519/src/key_exchange.lo \ +@ENABLE_DHT_TRUE@ ../ed25519/src/keypair.lo \ +@ENABLE_DHT_TRUE@ ../ed25519/src/sc.lo ../ed25519/src/seed.lo \ +@ENABLE_DHT_TRUE@ ../ed25519/src/sha512.lo \ +@ENABLE_DHT_TRUE@ ../ed25519/src/sign.lo \ +@ENABLE_DHT_TRUE@ ../ed25519/src/verify.lo +@WITH_SHIPPED_GEOIP_TRUE@am__objects_2 = GeoIP.lo +@WITH_OPENSSL_TRUE@am__objects_3 = asio_ssl.lo +am_libtorrent_rasterbar_la_OBJECTS = web_connection_base.lo alert.lo \ + alert_manager.lo allocator.lo asio.lo assert.lo \ + bandwidth_limit.lo bandwidth_manager.lo \ + bandwidth_queue_entry.lo bloom_filter.lo broadcast_socket.lo \ + bt_peer_connection.lo chained_buffer.lo connection_queue.lo \ + ConvertUTF.lo create_torrent.lo disk_buffer_holder.lo \ + disk_buffer_pool.lo disk_io_thread.lo entry.lo enum_net.lo \ + error_code.lo escape_string.lo file.lo file_pool.lo \ + file_storage.lo gzip.lo hasher.lo http_connection.lo \ + http_parser.lo http_seed_connection.lo http_stream.lo \ + http_tracker_connection.lo i2p_stream.lo identify_client.lo \ + instantiate_connection.lo ip_filter.lo ip_voter.lo \ + lazy_bdecode.lo logger.lo lsd.lo lt_trackers.lo magnet_uri.lo \ + metadata_transfer.lo mpi.lo natpmp.lo parse_url.lo \ + pe_crypto.lo peer_connection.lo piece_picker.lo \ + packet_buffer.lo policy.lo puff.lo random.lo rss.lo session.lo \ + session_impl.lo settings.lo sha1.lo smart_ban.lo socket_io.lo \ + socket_type.lo socks5_stream.lo stat.lo storage.lo \ + string_util.lo thread.lo torrent.lo torrent_handle.lo \ + torrent_info.lo time.lo timestamp_history.lo \ + tracker_manager.lo udp_socket.lo udp_tracker_connection.lo \ + upnp.lo ut_metadata.lo ut_pex.lo utf8.lo utp_socket_manager.lo \ + utp_stream.lo web_peer_connection.lo xml_parse.lo \ + $(am__objects_1) $(am__objects_2) $(am__objects_3) +libtorrent_rasterbar_la_OBJECTS = \ + $(am_libtorrent_rasterbar_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libtorrent_rasterbar_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(libtorrent_rasterbar_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(libtorrent_rasterbar_la_SOURCES) +DIST_SOURCES = $(am__libtorrent_rasterbar_la_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(top_srcdir)/build-aux/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_PYTHON_LIB = @BOOST_PYTHON_LIB@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +COMPILETIME_OPTIONS = @COMPILETIME_OPTIONS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEBUGFLAGS = @DEBUGFLAGS@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GEOIP_CFLAGS = @GEOIP_CFLAGS@ +GEOIP_LIBS = @GEOIP_LIBS@ +GREP = @GREP@ +ICONV_LIBS = @ICONV_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTERFACE_VERSION_INFO = @INTERFACE_VERSION_INFO@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_INCLUDES = @OPENSSL_INCLUDES@ +OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_INSTALL_PARAMS = @PYTHON_INSTALL_PARAMS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AUTOMAKE_OPTIONS = subdir-objects +lib_LTLIBRARIES = libtorrent-rasterbar.la +@ENABLE_DHT_TRUE@KADEMLIA_SOURCES = \ +@ENABLE_DHT_TRUE@ kademlia/dht_tracker.cpp \ +@ENABLE_DHT_TRUE@ kademlia/find_data.cpp \ +@ENABLE_DHT_TRUE@ kademlia/node.cpp \ +@ENABLE_DHT_TRUE@ kademlia/node_id.cpp \ +@ENABLE_DHT_TRUE@ kademlia/refresh.cpp \ +@ENABLE_DHT_TRUE@ kademlia/routing_table.cpp \ +@ENABLE_DHT_TRUE@ kademlia/rpc_manager.cpp \ +@ENABLE_DHT_TRUE@ kademlia/logging.cpp \ +@ENABLE_DHT_TRUE@ kademlia/traversal_algorithm.cpp \ +@ENABLE_DHT_TRUE@ kademlia/get_peers.cpp \ +@ENABLE_DHT_TRUE@ kademlia/get_item.cpp \ +@ENABLE_DHT_TRUE@ kademlia/item.cpp \ +@ENABLE_DHT_TRUE@ ../ed25519/src/add_scalar.cpp \ +@ENABLE_DHT_TRUE@ ../ed25519/src/fe.cpp \ +@ENABLE_DHT_TRUE@ ../ed25519/src/ge.cpp \ +@ENABLE_DHT_TRUE@ ../ed25519/src/key_exchange.cpp \ +@ENABLE_DHT_TRUE@ ../ed25519/src/keypair.cpp \ +@ENABLE_DHT_TRUE@ ../ed25519/src/sc.cpp \ +@ENABLE_DHT_TRUE@ ../ed25519/src/seed.cpp \ +@ENABLE_DHT_TRUE@ ../ed25519/src/sha512.cpp \ +@ENABLE_DHT_TRUE@ ../ed25519/src/sign.cpp \ +@ENABLE_DHT_TRUE@ ../ed25519/src/verify.cpp + +@WITH_SHIPPED_GEOIP_TRUE@GEOIP_SOURCES = GeoIP.c +@WITH_OPENSSL_TRUE@ASIO_OPENSSL_SOURCES = asio_ssl.cpp +libtorrent_rasterbar_la_SOURCES = \ + web_connection_base.cpp \ + alert.cpp \ + alert_manager.cpp \ + allocator.cpp \ + asio.cpp \ + assert.cpp \ + bandwidth_limit.cpp \ + bandwidth_manager.cpp \ + bandwidth_queue_entry.cpp \ + bloom_filter.cpp \ + broadcast_socket.cpp \ + bt_peer_connection.cpp \ + chained_buffer.cpp \ + connection_queue.cpp \ + ConvertUTF.cpp \ + create_torrent.cpp \ + disk_buffer_holder.cpp \ + disk_buffer_pool.cpp \ + disk_io_thread.cpp \ + entry.cpp \ + enum_net.cpp \ + error_code.cpp \ + escape_string.cpp \ + file.cpp \ + file_pool.cpp \ + file_storage.cpp \ + gzip.cpp \ + hasher.cpp \ + http_connection.cpp \ + http_parser.cpp \ + http_seed_connection.cpp \ + http_stream.cpp \ + http_tracker_connection.cpp \ + i2p_stream.cpp \ + identify_client.cpp \ + instantiate_connection.cpp \ + ip_filter.cpp \ + ip_voter.cpp \ + lazy_bdecode.cpp \ + logger.cpp \ + lsd.cpp \ + lt_trackers.cpp \ + magnet_uri.cpp \ + metadata_transfer.cpp \ + mpi.c \ + natpmp.cpp \ + parse_url.cpp \ + pe_crypto.cpp \ + peer_connection.cpp \ + piece_picker.cpp \ + packet_buffer.cpp \ + policy.cpp \ + puff.cpp \ + random.cpp \ + rss.cpp \ + session.cpp \ + session_impl.cpp \ + settings.cpp \ + sha1.cpp \ + smart_ban.cpp \ + socket_io.cpp \ + socket_type.cpp \ + socks5_stream.cpp \ + stat.cpp \ + storage.cpp \ + string_util.cpp \ + thread.cpp \ + torrent.cpp \ + torrent_handle.cpp \ + torrent_info.cpp \ + time.cpp \ + timestamp_history.cpp \ + tracker_manager.cpp \ + udp_socket.cpp \ + udp_tracker_connection.cpp \ + upnp.cpp \ + ut_metadata.cpp \ + ut_pex.cpp \ + utf8.cpp \ + utp_socket_manager.cpp \ + utp_stream.cpp \ + web_peer_connection.cpp \ + xml_parse.cpp \ + \ + $(KADEMLIA_SOURCES) \ + $(GEOIP_SOURCES) \ + $(ASIO_OPENSSL_SOURCES) + + +#libtorrent_rasterbar_la_LDFLAGS = $(LDFLAGS) -version-info $(INTERFACE_VERSION_INFO) +libtorrent_rasterbar_la_LDFLAGS = -version-info $(INTERFACE_VERSION_INFO) + +#libtorrent_rasterbar_la_LIBADD = @BOOST_SYSTEM_LIB@ @BOOST_FILESYSTEM_LIB@ @BOOST_THREAD_LIB@ @OPENSSL_LIBS@ +libtorrent_rasterbar_la_LIBADD = @BOOST_SYSTEM_LIB@ @OPENSSL_LIBS@ + +#AM_CXXFLAGS= -ftemplate-depth-100 -I$(top_srcdir)/include @DEBUGFLAGS@ @OPENSSL_INCLUDES@ +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/ed25519/src @DEBUGFLAGS@ @OPENSSL_INCLUDES@ +AM_CFLAGS = -I$(top_srcdir)/ed25519/src -std=c99 + +#AM_CFLAGS= -I$(top_srcdir)/include @DEBUGFLAGS@ +#AM_LDFLAGS = $(LDFLAGS) @BOOST_SYSTEM_LIB@ @BOOST_FILESYSTEM_LIB@ @BOOST_THREAD_LIB@ @OPENSSL_LDFLAGS@ @OPENSSL_LIBS@ +AM_LDFLAGS = @OPENSSL_LDFLAGS@ +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .cpp .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } +kademlia/$(am__dirstamp): + @$(MKDIR_P) kademlia + @: > kademlia/$(am__dirstamp) +kademlia/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) kademlia/$(DEPDIR) + @: > kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/dht_tracker.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/find_data.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/node.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/node_id.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/refresh.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/routing_table.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/rpc_manager.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/logging.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/traversal_algorithm.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/get_peers.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/get_item.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/item.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/$(am__dirstamp): + @$(MKDIR_P) ../ed25519/src + @: > ../ed25519/src/$(am__dirstamp) +../ed25519/src/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) ../ed25519/src/$(DEPDIR) + @: > ../ed25519/src/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/add_scalar.lo: ../ed25519/src/$(am__dirstamp) \ + ../ed25519/src/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/fe.lo: ../ed25519/src/$(am__dirstamp) \ + ../ed25519/src/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/ge.lo: ../ed25519/src/$(am__dirstamp) \ + ../ed25519/src/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/key_exchange.lo: ../ed25519/src/$(am__dirstamp) \ + ../ed25519/src/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/keypair.lo: ../ed25519/src/$(am__dirstamp) \ + ../ed25519/src/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/sc.lo: ../ed25519/src/$(am__dirstamp) \ + ../ed25519/src/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/seed.lo: ../ed25519/src/$(am__dirstamp) \ + ../ed25519/src/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/sha512.lo: ../ed25519/src/$(am__dirstamp) \ + ../ed25519/src/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/sign.lo: ../ed25519/src/$(am__dirstamp) \ + ../ed25519/src/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/verify.lo: ../ed25519/src/$(am__dirstamp) \ + ../ed25519/src/$(DEPDIR)/$(am__dirstamp) + +libtorrent-rasterbar.la: $(libtorrent_rasterbar_la_OBJECTS) $(libtorrent_rasterbar_la_DEPENDENCIES) $(EXTRA_libtorrent_rasterbar_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libtorrent_rasterbar_la_LINK) -rpath $(libdir) $(libtorrent_rasterbar_la_OBJECTS) $(libtorrent_rasterbar_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f ../ed25519/src/*.$(OBJEXT) + -rm -f ../ed25519/src/*.lo + -rm -f kademlia/*.$(OBJEXT) + -rm -f kademlia/*.lo + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/add_scalar.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/fe.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/ge.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/key_exchange.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/keypair.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/sc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/seed.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/sha512.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/sign.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/verify.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ConvertUTF.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GeoIP.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alert.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alert_manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/allocator.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_ssl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/assert.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bandwidth_limit.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bandwidth_manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bandwidth_queue_entry.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bloom_filter.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/broadcast_socket.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bt_peer_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chained_buffer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connection_queue.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/create_torrent.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/disk_buffer_holder.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/disk_buffer_pool.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/disk_io_thread.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/entry.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/enum_net.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error_code.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/escape_string.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_pool.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_storage.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gzip.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hasher.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_parser.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_seed_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_stream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_tracker_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/i2p_stream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/identify_client.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/instantiate_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip_filter.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip_voter.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lazy_bdecode.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logger.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsd.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lt_trackers.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/magnet_uri.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/metadata_transfer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mpi.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/natpmp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet_buffer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse_url.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pe_crypto.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/peer_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/piece_picker.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/policy.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/puff.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/random.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rss.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/settings.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha1.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smart_ban.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket_io.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket_type.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socks5_stream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stat.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/storage.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/string_util.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thread.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/time.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timestamp_history.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent_handle.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent_info.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tracker_manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/udp_socket.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/udp_tracker_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upnp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ut_metadata.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ut_pex.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utf8.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utp_socket_manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utp_stream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/web_connection_base.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/web_peer_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xml_parse.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/dht_tracker.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/find_data.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/get_item.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/get_peers.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/item.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/logging.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/node.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/node_id.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/refresh.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/routing_table.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/rpc_manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/traversal_algorithm.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +.cpp.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + -rm -rf ../ed25519/src/.libs ../ed25519/src/_libs + -rm -rf kademlia/.libs kademlia/_libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f ../ed25519/src/$(DEPDIR)/$(am__dirstamp) + -rm -f ../ed25519/src/$(am__dirstamp) + -rm -f kademlia/$(DEPDIR)/$(am__dirstamp) + -rm -f kademlia/$(am__dirstamp) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ../ed25519/src/$(DEPDIR) ./$(DEPDIR) kademlia/$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ../ed25519/src/$(DEPDIR) ./$(DEPDIR) kademlia/$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-libLTLIBRARIES install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/apps/Launcher/ext/libtorrent/src/alert.cpp b/apps/Launcher/ext/libtorrent/src/alert.cpp new file mode 100644 index 0000000000..84a8df12b6 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/alert.cpp @@ -0,0 +1,1246 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg, Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include + +#include "libtorrent/config.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/io_service.hpp" +#include "libtorrent/socket_io.hpp" +#include "libtorrent/time.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/extensions.hpp" +#include + +namespace libtorrent { + + alert::alert() : m_timestamp(time_now()) {} + alert::~alert() {} + ptime alert::timestamp() const { return m_timestamp; } + + torrent_alert::torrent_alert(torrent_handle const& h) + : handle(h) + {} + + std::string torrent_alert::message() const + { + if (!handle.is_valid()) return " - "; + torrent_status st = handle.status(torrent_handle::query_name); + if (st.name.empty()) + { + char msg[41]; + to_hex((char const*)&st.info_hash[0], 20, msg); + return msg; + } + return st.name; + } + + peer_alert::peer_alert(torrent_handle const& h, tcp::endpoint const& i + , peer_id const& pi) + : torrent_alert(h) + , ip(i) + , pid(pi) + {} + + std::string peer_alert::message() const + { + error_code ec; + return torrent_alert::message() + " peer (" + print_endpoint(ip) + + ", " + identify_client(pid) + ")"; + } + + tracker_alert::tracker_alert(torrent_handle const& h + , std::string const& u) + : torrent_alert(h) + , url(u) + {} + + std::string tracker_alert::message() const + { + return torrent_alert::message() + " (" + url + ")"; + } + + read_piece_alert::read_piece_alert(torrent_handle const& h + , int p, boost::shared_array d, int s) + : torrent_alert(h) + , buffer(d) + , piece(p) + , size(s) + {} + + read_piece_alert::read_piece_alert(torrent_handle h, int p, error_code e) + : torrent_alert(h) + , ec(e) + , piece(p) + , size(0) + {} + + std::string read_piece_alert::message() const + { + char msg[200]; + if (ec) + { + snprintf(msg, sizeof(msg), "%s: read_piece %u failed: %s" + , torrent_alert::message().c_str() , piece, ec.message().c_str()); + } + else + { + snprintf(msg, sizeof(msg), "%s: read_piece %u successful" + , torrent_alert::message().c_str() , piece); + } + return msg; + } + + file_completed_alert::file_completed_alert(torrent_handle const& h + , int idx) + : torrent_alert(h) + , index(idx) + {} + + std::string file_completed_alert::message() const + { + char msg[200 + TORRENT_MAX_PATH]; + snprintf(msg, sizeof(msg), "%s: file %d finished downloading" + , torrent_alert::message().c_str(), index); + return msg; + } + + file_renamed_alert::file_renamed_alert(torrent_handle const& h + , std::string const& n + , int idx) + : torrent_alert(h) + , name(n) + , index(idx) + {} + + std::string file_renamed_alert::message() const + { + char msg[200 + TORRENT_MAX_PATH * 2]; + snprintf(msg, sizeof(msg), "%s: file %d renamed to %s", torrent_alert::message().c_str() + , index, name.c_str()); + return msg; + } + + file_rename_failed_alert::file_rename_failed_alert(torrent_handle const& h + , int idx + , error_code ec) + : torrent_alert(h) + , index(idx) + , error(ec) + {} + + std::string file_rename_failed_alert::message() const + { + char ret[200 + TORRENT_MAX_PATH * 2]; + snprintf(ret, sizeof(ret), "%s: failed to rename file %d: %s" + , torrent_alert::message().c_str(), index, convert_from_native(error.message()).c_str()); + return ret; + } + + performance_alert::performance_alert(torrent_handle const& h + , performance_warning_t w) + : torrent_alert(h) + , warning_code(w) + {} + + std::string performance_alert::message() const + { + static char const* warning_str[] = + { + "max outstanding disk writes reached", + "max outstanding piece requests reached", + "upload limit too low (download rate will suffer)", + "download limit too low (upload rate will suffer)", + "send buffer watermark too low (upload rate will suffer)", + "too many optimistic unchoke slots", + "using bittyrant unchoker with no upload rate limit set", + "the disk queue limit is too high compared to the cache size. The disk queue eats into the cache size", + "too few ports allowed for outgoing connections", + "too few file descriptors are allowed for this process. connection limit lowered" + }; + + return torrent_alert::message() + ": performance warning: " + + warning_str[warning_code]; + } + + state_changed_alert::state_changed_alert(torrent_handle const& h + , torrent_status::state_t st + , torrent_status::state_t prev_st) + : torrent_alert(h) + , state(st) + , prev_state(prev_st) + {} + + std::string state_changed_alert::message() const + { + static char const* state_str[] = + {"checking (q)", "checking", "dl metadata" + , "downloading", "finished", "seeding", "allocating" + , "checking (r)"}; + + return torrent_alert::message() + ": state changed to: " + + state_str[state]; + } + + tracker_error_alert::tracker_error_alert(torrent_handle const& h + , int times + , int status + , std::string const& u + , error_code const& e + , std::string const& m) + : tracker_alert(h, u) + , times_in_row(times) + , status_code(status) + , error(e) + , msg(m) + { + TORRENT_ASSERT(!url.empty()); + } + + std::string tracker_error_alert::message() const + { + char ret[400]; + snprintf(ret, sizeof(ret), "%s (%d) %s \"%s\" (%d)" + , tracker_alert::message().c_str(), status_code + , error.message().c_str(), msg.c_str(), times_in_row); + return ret; + } + + tracker_warning_alert::tracker_warning_alert(torrent_handle const& h + , std::string const& u + , std::string const& m) + : tracker_alert(h, u) + , msg(m) + { TORRENT_ASSERT(!url.empty()); } + + std::string tracker_warning_alert::message() const + { + return tracker_alert::message() + " warning: " + msg; + } + + scrape_reply_alert::scrape_reply_alert(torrent_handle const& h + , int incomp + , int comp + , std::string const& u) + : tracker_alert(h, u) + , incomplete(incomp) + , complete(comp) + { + TORRENT_ASSERT(!url.empty()); + } + + std::string scrape_reply_alert::message() const + { + char ret[400]; + snprintf(ret, sizeof(ret), "%s scrape reply: %u %u" + , tracker_alert::message().c_str(), incomplete, complete); + return ret; + } + + scrape_failed_alert::scrape_failed_alert(torrent_handle const& h + , std::string const& u + , error_code const& e) + : tracker_alert(h, u) + , msg(convert_from_native(e.message())) + { + TORRENT_ASSERT(!url.empty()); + } + + scrape_failed_alert::scrape_failed_alert(torrent_handle const& h + , std::string const& u + , std::string const& m) + : tracker_alert(h, u) + , msg(m) + { + TORRENT_ASSERT(!url.empty()); + } + + std::string scrape_failed_alert::message() const + { + return tracker_alert::message() + " scrape failed: " + msg; + } + + tracker_reply_alert::tracker_reply_alert(torrent_handle const& h + , int np + , std::string const& u) + : tracker_alert(h, u) + , num_peers(np) + { + TORRENT_ASSERT(!url.empty()); + } + + std::string tracker_reply_alert::message() const + { + char ret[400]; + snprintf(ret, sizeof(ret), "%s received peers: %u" + , tracker_alert::message().c_str(), num_peers); + return ret; + } + + dht_reply_alert::dht_reply_alert(torrent_handle const& h + , int np) + : tracker_alert(h, "") + , num_peers(np) + {} + + std::string dht_reply_alert::message() const + { + char ret[400]; + snprintf(ret, sizeof(ret), "%s received DHT peers: %u" + , tracker_alert::message().c_str(), num_peers); + return ret; + } + + tracker_announce_alert::tracker_announce_alert(torrent_handle const& h + , std::string const& u, int e) + : tracker_alert(h, u) + , event(e) + { + TORRENT_ASSERT(!url.empty()); + } + + std::string tracker_announce_alert::message() const + { + const static char* event_str[] = {"none", "completed", "started", "stopped", "paused"}; + TORRENT_ASSERT_VAL(event < int(sizeof(event_str)/sizeof(event_str[0])), event); + return tracker_alert::message() + " sending announce (" + event_str[event] + ")"; + } + + hash_failed_alert::hash_failed_alert( + torrent_handle const& h + , int index) + : torrent_alert(h) + , piece_index(index) + + { + TORRENT_ASSERT(index >= 0); + } + + std::string hash_failed_alert::message() const + { + char ret[400]; + snprintf(ret, sizeof(ret), "%s hash for piece %u failed" + , torrent_alert::message().c_str(), piece_index); + return ret; + } + + peer_ban_alert::peer_ban_alert(torrent_handle h, tcp::endpoint const& ep + , peer_id const& peer_id) + : peer_alert(h, ep, peer_id) + {} + + std::string peer_ban_alert::message() const + { + return peer_alert::message() + " banned peer"; + } + + peer_unsnubbed_alert::peer_unsnubbed_alert(torrent_handle h, tcp::endpoint const& ep + , peer_id const& peer_id) + : peer_alert(h, ep, peer_id) + {} + + std::string peer_unsnubbed_alert::message() const + { + return peer_alert::message() + " peer unsnubbed"; + } + + peer_snubbed_alert::peer_snubbed_alert(torrent_handle h, tcp::endpoint const& ep + , peer_id const& peer_id) + : peer_alert(h, ep, peer_id) + {} + + std::string peer_snubbed_alert::message() const + { + return peer_alert::message() + " peer snubbed"; + } + + peer_error_alert::peer_error_alert(torrent_handle const& h, tcp::endpoint const& ep + , peer_id const& peer_id, error_code const& e) + : peer_alert(h, ep, peer_id) + , error(e) + { +#ifndef TORRENT_NO_DEPRECATE + msg = convert_from_native(error.message()); +#endif + } + + std::string peer_error_alert::message() const + { + return peer_alert::message() + " peer error: " + convert_from_native(error.message()); + } + + invalid_request_alert::invalid_request_alert(torrent_handle const& h, tcp::endpoint const& ep + , peer_id const& peer_id, peer_request const& r) + : peer_alert(h, ep, peer_id) + , request(r) + {} + + std::string invalid_request_alert::message() const + { + char ret[200]; + snprintf(ret, sizeof(ret), "%s peer sent an invalid piece request (piece: %u start: %u len: %u)" + , peer_alert::message().c_str(), request.piece, request.start, request.length); + return ret; + } + + torrent_finished_alert::torrent_finished_alert( + const torrent_handle& h) + : torrent_alert(h) + {} + + std::string torrent_finished_alert::message() const + { + return torrent_alert::message() + " torrent finished downloading"; + } + + piece_finished_alert::piece_finished_alert( + const torrent_handle& h + , int piece_num) + : torrent_alert(h) + , piece_index(piece_num) + { + TORRENT_ASSERT(piece_index >= 0); + } + + std::string piece_finished_alert::message() const + { + char ret[200]; + snprintf(ret, sizeof(ret), "%s piece: %u finished downloading" + , torrent_alert::message().c_str(), piece_index); + return ret; + } + + request_dropped_alert::request_dropped_alert(const torrent_handle& h, tcp::endpoint const& ep + , peer_id const& peer_id, int block_num, int piece_num) + : peer_alert(h, ep, peer_id) + , block_index(block_num) + , piece_index(piece_num) + { + TORRENT_ASSERT(block_index >= 0 && piece_index >= 0); + } + + std::string request_dropped_alert::message() const + { + char ret[200]; + snprintf(ret, sizeof(ret), "%s peer dropped block ( piece: %u block: %u)" + , torrent_alert::message().c_str(), piece_index, block_index); + return ret; + } + + block_timeout_alert::block_timeout_alert(const torrent_handle& h, tcp::endpoint const& ep + , peer_id const& peer_id, int block_num, int piece_num) + : peer_alert(h, ep, peer_id) + , block_index(block_num) + , piece_index(piece_num) + { + TORRENT_ASSERT(block_index >= 0 && piece_index >= 0); + } + + std::string block_timeout_alert::message() const + { + char ret[200]; + snprintf(ret, sizeof(ret), "%s peer timed out request ( piece: %u block: %u)" + , torrent_alert::message().c_str(), piece_index, block_index); + return ret; + } + + block_finished_alert::block_finished_alert(const torrent_handle& h, tcp::endpoint const& ep + , peer_id const& peer_id, int block_num, int piece_num) + : peer_alert(h, ep, peer_id) + , block_index(block_num) + , piece_index(piece_num) + { + TORRENT_ASSERT(block_index >= 0 && piece_index >= 0); + } + + std::string block_finished_alert::message() const + { + char ret[200]; + snprintf(ret, sizeof(ret), "%s block finished downloading (piece: %u block: %u)" + , torrent_alert::message().c_str(), piece_index, block_index); + return ret; + } + + block_downloading_alert::block_downloading_alert(const torrent_handle& h, tcp::endpoint const& ep + , peer_id const& peer_id, char const* speedmsg, int block_num, int piece_num) + : peer_alert(h, ep, peer_id) + , peer_speedmsg(speedmsg) + , block_index(block_num) + , piece_index(piece_num) + { + TORRENT_ASSERT(block_index >= 0 && piece_index >= 0); + } + + std::string block_downloading_alert::message() const + { + char ret[200]; + snprintf(ret, sizeof(ret), "%s requested block (piece: %u block: %u) %s" + , torrent_alert::message().c_str(), piece_index, block_index, peer_speedmsg); + return ret; + } + + unwanted_block_alert::unwanted_block_alert(const torrent_handle& h, tcp::endpoint const& ep + , peer_id const& peer_id, int block_num, int piece_num) + : peer_alert(h, ep, peer_id) + , block_index(block_num) + , piece_index(piece_num) + { + TORRENT_ASSERT(block_index >= 0 && piece_index >= 0); + } + + std::string unwanted_block_alert::message() const + { + char ret[200]; + snprintf(ret, sizeof(ret), "%s received block not in download queue (piece: %u block: %u)" + , torrent_alert::message().c_str(), piece_index, block_index); + return ret; + } + + storage_moved_alert::storage_moved_alert(torrent_handle const& h, std::string const& p) + : torrent_alert(h) + , path(p) + {} + + std::string storage_moved_alert::message() const + { + return torrent_alert::message() + " moved storage to: " + + path; + } + + storage_moved_failed_alert::storage_moved_failed_alert(torrent_handle const& h + , error_code const& e) + : torrent_alert(h) + , error(e) + {} + + std::string storage_moved_failed_alert::message() const + { + return torrent_alert::message() + " storage move failed: " + + convert_from_native(error.message()); + } + + torrent_deleted_alert::torrent_deleted_alert(torrent_handle const& h, sha1_hash const& ih) + : torrent_alert(h) + { + info_hash = ih; + } + + std::string torrent_deleted_alert::message() const + { + return torrent_alert::message() + " deleted"; + } + + torrent_delete_failed_alert::torrent_delete_failed_alert(torrent_handle const& h, error_code const& e, sha1_hash const& ih) + : torrent_alert(h) + , error(e) + , info_hash(ih) + { +#ifndef TORRENT_NO_DEPRECATE + msg = convert_from_native(error.message()); +#endif + } + + std::string torrent_delete_failed_alert::message() const + { + return torrent_alert::message() + " torrent deletion failed: " + + convert_from_native(error.message()); + } + + save_resume_data_failed_alert::save_resume_data_failed_alert(torrent_handle const& h + , error_code const& e) + : torrent_alert(h) + , error(e) + { +#ifndef TORRENT_NO_DEPRECATE + msg = convert_from_native(error.message()); +#endif + } + + std::string save_resume_data_failed_alert::message() const + { + return torrent_alert::message() + " resume data was not generated: " + + convert_from_native(error.message()); + } + + torrent_paused_alert::torrent_paused_alert(torrent_handle const& h) + : torrent_alert(h) + {} + + std::string torrent_paused_alert::message() const + { + return torrent_alert::message() + " paused"; + } + + torrent_resumed_alert::torrent_resumed_alert(torrent_handle const& h) + : torrent_alert(h) + {} + + std::string torrent_resumed_alert::message() const + { + return torrent_alert::message() + " resumed"; + } + + torrent_checked_alert::torrent_checked_alert(torrent_handle const& h) + : torrent_alert(h) + {} + + std::string torrent_checked_alert::message() const + { + return torrent_alert::message() + " checked"; + } + + url_seed_alert::url_seed_alert( + torrent_handle const& h + , std::string const& u + , error_code const& e) + : torrent_alert(h) + , url(u) + , msg(convert_from_native(e.message())) + {} + + url_seed_alert::url_seed_alert( + torrent_handle const& h + , std::string const& u + , std::string const& m) + : torrent_alert(h) + , url(u) + , msg(m) + {} + + std::string url_seed_alert::message() const + { + return torrent_alert::message() + " url seed (" + + url + ") failed: " + msg; + } + + file_error_alert::file_error_alert( + std::string const& f + , torrent_handle const& h + , error_code const& e) + : torrent_alert(h) + , file(f) + , error(e) + { +#ifndef TORRENT_NO_DEPRECATE + msg = convert_from_native(error.message()); +#endif + } + + std::string file_error_alert::message() const + { + return torrent_alert::message() + " file (" + file + ") error: " + + convert_from_native(error.message()); + } + + metadata_failed_alert::metadata_failed_alert(const torrent_handle& h, error_code e) + : torrent_alert(h) + , error(e) + {} + + std::string metadata_failed_alert::message() const + { + return torrent_alert::message() + " invalid metadata received"; + } + + metadata_received_alert::metadata_received_alert( + const torrent_handle& h) + : torrent_alert(h) + {} + + std::string metadata_received_alert::message() const + { + return torrent_alert::message() + " metadata successfully received"; + } + + udp_error_alert::udp_error_alert( + udp::endpoint const& ep + , error_code const& ec) + : endpoint(ep) + , error(ec) + {} + + std::string udp_error_alert::message() const + { + error_code ec; + return "UDP error: " + convert_from_native(error.message()) + " from: " + endpoint.address().to_string(ec); + } + + external_ip_alert::external_ip_alert(address const& ip) + : external_address(ip) + {} + + std::string external_ip_alert::message() const + { + error_code ec; + return "external IP received: " + external_address.to_string(ec); + } + + save_resume_data_alert::save_resume_data_alert(boost::shared_ptr const& rd + , torrent_handle const& h) + : torrent_alert(h) + , resume_data(rd) + {} + + std::string save_resume_data_alert::message() const + { + return torrent_alert::message() + " resume data generated"; + } + + listen_failed_alert::listen_failed_alert( + tcp::endpoint const& ep + , int op + , error_code const& ec + , socket_type_t t) + : endpoint(ep) + , error(ec) + , operation(op) + , sock_type(t) + {} + + std::string listen_failed_alert::message() const + { + static char const* op_str[] = + { + "parse_addr", + "open", + "bind", + "listen", + "get_peer_name", + "accept" + }; + static char const* type_str[] = + { + "TCP", "TCP/SSL", "UDP", "I2P", "Socks5" + }; + char ret[250]; + snprintf(ret, sizeof(ret), "listening on %s failed: [%s] [%s] %s" + , print_endpoint(endpoint).c_str() + , op_str[operation] + , type_str[sock_type] + , convert_from_native(error.message()).c_str()); + return ret; + } + + listen_succeeded_alert::listen_succeeded_alert(tcp::endpoint const& ep, socket_type_t t) + : endpoint(ep) + , sock_type(t) + {} + + std::string listen_succeeded_alert::message() const + { + static char const* type_str[] = + { + "TCP", "TCP/SSL", "UDP" + }; + char ret[200]; + snprintf(ret, sizeof(ret), "successfully listening on [%s] %s" + , type_str[sock_type], print_endpoint(endpoint).c_str()); + return ret; + } + + portmap_error_alert::portmap_error_alert(int i, int t, error_code const& e) + : mapping(i), map_type(t), error(e) + { +#ifndef TORRENT_NO_DEPRECATE + msg = convert_from_native(error.message()); +#endif + } + + std::string portmap_error_alert::message() const + { + static char const* type_str[] = {"NAT-PMP", "UPnP"}; + return std::string("could not map port using ") + type_str[map_type] + + ": " + convert_from_native(error.message()); + } + + portmap_alert::portmap_alert(int i, int port, int t) + : mapping(i), external_port(port), map_type(t) + {} + + std::string portmap_alert::message() const + { + static char const* type_str[] = {"NAT-PMP", "UPnP"}; + char ret[200]; + snprintf(ret, sizeof(ret), "successfully mapped port using %s. external port: %u" + , type_str[map_type], external_port); + return ret; + } + + portmap_log_alert::portmap_log_alert(int t, std::string const& m) + : map_type(t), msg(m) + {} + + std::string portmap_log_alert::message() const + { + static char const* type_str[] = {"NAT-PMP", "UPnP"}; + char ret[600]; + snprintf(ret, sizeof(ret), "%s: %s", type_str[map_type], msg.c_str()); + return ret; + } + + fastresume_rejected_alert::fastresume_rejected_alert(torrent_handle const& h + , error_code const& e) + : torrent_alert(h) + , error(e) + { +#ifndef TORRENT_NO_DEPRECATE + msg = convert_from_native(error.message()); +#endif + } + + std::string fastresume_rejected_alert::message() const + { + return torrent_alert::message() + " fast resume rejected: " + + convert_from_native(error.message()); + } + + peer_blocked_alert::peer_blocked_alert(torrent_handle const& h, address const& i + , int r) + : torrent_alert(h) + , ip(i) + , reason(r) + {} + + std::string peer_blocked_alert::message() const + { + error_code ec; + char ret[600]; + char const* reason_str[] = + { + "ip_filter", + "port_filter", + "i2p_mixed", + "privileged_ports", + "utp_disabled", + "tcp_disabled" + }; + + snprintf(ret, sizeof(ret), "%s: blocked peer: %s [%s]" + , torrent_alert::message().c_str(), ip.to_string(ec).c_str() + , reason_str[reason]); + return ret; + } + + dht_announce_alert::dht_announce_alert(address const& i, int p + , sha1_hash const& ih) + : ip(i) + , port(p) + , info_hash(ih) + {} + + std::string dht_announce_alert::message() const + { + error_code ec; + char ih_hex[41]; + to_hex((const char*)&info_hash[0], 20, ih_hex); + char msg[200]; + snprintf(msg, sizeof(msg), "incoming dht announce: %s:%u (%s)" + , ip.to_string(ec).c_str(), port, ih_hex); + return msg; + } + + dht_get_peers_alert::dht_get_peers_alert(sha1_hash const& ih) + : info_hash(ih) + {} + + std::string dht_get_peers_alert::message() const + { + char ih_hex[41]; + to_hex((const char*)&info_hash[0], 20, ih_hex); + char msg[200]; + snprintf(msg, sizeof(msg), "incoming dht get_peers: %s", ih_hex); + return msg; + } + + stats_alert::stats_alert(torrent_handle const& h, int in + , stat const& s) + : torrent_alert(h) + , interval(in) + { + for (int i = 0; i < num_channels; ++i) + transferred[i] = s[i].counter(); + } + + std::string stats_alert::message() const + { + char msg[200]; + snprintf(msg, sizeof(msg), "%s: [%d] %d %d %d %d %d %d %d %d %d %d" + , torrent_alert::message().c_str() + , interval + , transferred[0] + , transferred[1] + , transferred[2] + , transferred[3] +#ifndef TORRENT_DISABLE_FULL_STATS + , transferred[4] + , transferred[5] + , transferred[6] + , transferred[7] + , transferred[8] + , transferred[9] +#endif + ); + return msg; + } + + cache_flushed_alert::cache_flushed_alert(torrent_handle const& h): torrent_alert(h) {} + + anonymous_mode_alert::anonymous_mode_alert(torrent_handle const& h + , int k, std::string const& s) + : torrent_alert(h) + , kind(k) + , str(s) + {} + + std::string anonymous_mode_alert::message() const + { + char msg[200]; + char const* msgs[] = { + "tracker is not anonymous, set a proxy" + }; + snprintf(msg, sizeof(msg), "%s: %s: %s" + , torrent_alert::message().c_str() + , msgs[kind], str.c_str()); + return msg; + } + + lsd_peer_alert::lsd_peer_alert(torrent_handle const& h + , tcp::endpoint const& i) + : peer_alert(h, i, peer_id(0)) + {} + + std::string lsd_peer_alert::message() const + { + char msg[200]; + snprintf(msg, sizeof(msg), "%s: received peer from local service discovery" + , peer_alert::message().c_str()); + return msg; + } + + trackerid_alert::trackerid_alert(torrent_handle const& h + , std::string const& u + , const std::string& id) + : tracker_alert(h, u) + , trackerid(id) + {} + + std::string trackerid_alert::message() const + { + return "trackerid received: " + trackerid; + } + + dht_bootstrap_alert::dht_bootstrap_alert() + {} + + std::string dht_bootstrap_alert::message() const + { + return "DHT bootstrap complete"; + } + + rss_alert::rss_alert(feed_handle h, std::string const& u, int s, error_code const& ec) + : handle(h), url(u), state(s), error(ec) + {} + + std::string rss_alert::message() const + { + char msg[600]; + char const* state_msg[] = {"updating", "updated", "error"}; + snprintf(msg, sizeof(msg), "RSS feed %s: %s (%s)" + , url.c_str(), state_msg[state], convert_from_native(error.message()).c_str()); + return msg; + } + + torrent_error_alert::torrent_error_alert(torrent_handle const& h + , error_code const& e) + : torrent_alert(h) + , error(e) + {} + + std::string torrent_error_alert::message() const + { + char msg[200]; + snprintf(msg, sizeof(msg), " ERROR: %s", convert_from_native(error.message()).c_str()); + return torrent_alert::message() + msg; + } + + torrent_added_alert::torrent_added_alert(torrent_handle const& h) + : torrent_alert(h) + {} + + std::string torrent_added_alert::message() const + { + return torrent_alert::message() + " added"; + } + + torrent_removed_alert::torrent_removed_alert(torrent_handle const& h, sha1_hash const& ih) + : torrent_alert(h) + , info_hash(ih) + {} + + std::string torrent_removed_alert::message() const + { + return torrent_alert::message() + " removed"; + } + + torrent_need_cert_alert::torrent_need_cert_alert(torrent_handle const& h) + : torrent_alert(h) + {} + + std::string torrent_need_cert_alert::message() const + { + return torrent_alert::message() + " needs SSL certificate"; + } + + static char const* type_str[] = { + "null", + "TCP", + "Socks5/TCP", + "HTTP", + "uTP", + "i2p", + "SSL/TCP", + "SSL/Socks5", + "HTTPS", + "SSL/uTP" + }; + + incoming_connection_alert::incoming_connection_alert(int t, tcp::endpoint const& i) + : socket_type(t) + , ip(i) + {} + + std::string incoming_connection_alert::message() const + { + char msg[600]; + error_code ec; + snprintf(msg, sizeof(msg), "incoming connection from %s (%s)" + , print_endpoint(ip).c_str(), type_str[socket_type]); + return msg; + } + + peer_connect_alert::peer_connect_alert(torrent_handle h, tcp::endpoint const& ep + , peer_id const& peer_id, int type) + : peer_alert(h, ep, peer_id) + , socket_type(type) + {} + + std::string peer_connect_alert::message() const + { + char msg[600]; + error_code ec; + snprintf(msg, sizeof(msg), "%s connecting to peer (%s)" + , peer_alert::message().c_str(), type_str[socket_type]); + return msg; + } + + add_torrent_alert::add_torrent_alert(torrent_handle h, add_torrent_params const& p, error_code ec) + : torrent_alert(h) + , params(p) + , error(ec) + {} + + std::string add_torrent_alert::message() const + { + char msg[600]; + char info_hash[41]; + char const* torrent_name = info_hash; + if (params.ti) torrent_name = params.ti->name().c_str(); + else if (!params.name.empty()) torrent_name = params.name.c_str(); + else if (!params.url.empty()) torrent_name = params.url.c_str(); + else to_hex((const char*)¶ms.info_hash[0], 20, info_hash); + + if (error) + { + snprintf(msg, sizeof(msg), "failed to add torrent \"%s\": [%s] %s" + , torrent_name, error.category().name() + , convert_from_native(error.message()).c_str()); + } + else + { + snprintf(msg, sizeof(msg), "added torrent: %s", torrent_name); + } + return msg; + } + + std::string state_update_alert::message() const + { + char msg[600]; + snprintf(msg, sizeof(msg), "state updates for %d torrents", int(status.size())); + return msg; + } + + torrent_update_alert::torrent_update_alert(torrent_handle h, sha1_hash const& old_hash, sha1_hash const& new_hash) + : torrent_alert(h) + , old_ih(old_hash) + , new_ih(new_hash) + {} + + std::string torrent_update_alert::message() const + { + char msg[200]; + snprintf(msg, sizeof(msg), " torrent changed info-hash from: %s to %s" + , to_hex(old_ih.to_string()).c_str() + , to_hex(new_ih.to_string()).c_str()); + return torrent_alert::message() + msg; + } + + rss_item_alert::rss_item_alert(feed_handle h, feed_item const& item) + : handle(h) + , item(item) + {} + + std::string rss_item_alert::message() const + { + char msg[500]; + snprintf(msg, sizeof(msg), "feed [%s] has new RSS item %s" + , handle.get_feed_status().title.c_str() + , item.title.empty() ? item.url.c_str() : item.title.c_str()); + return msg; + } + + peer_disconnected_alert::peer_disconnected_alert(torrent_handle const& h, tcp::endpoint const& ep + , peer_id const& peer_id, error_code const& e) + : peer_alert(h, ep, peer_id) + , error(e) + { +#ifndef TORRENT_NO_DEPRECATE + msg = convert_from_native(error.message()); +#endif + } + + std::string peer_disconnected_alert::message() const + { + char msg[600]; + snprintf(msg, sizeof(msg), "%s disconnecting: [%s] %s", peer_alert::message().c_str() + , error.category().name(), convert_from_native(error.message()).c_str()); + return msg; + } + + dht_error_alert::dht_error_alert(int op, error_code const& ec) + : error(ec), operation(op_t(op)) + {} + + std::string dht_error_alert::message() const + { + const static char* const operation_names[] = + { + "unknown", + "hostname lookup" + }; + + int op = operation; + if (op < 0 || op >= int(sizeof(operation_names)/sizeof(operation_names[0]))) + op = 0; + + char msg[600]; + snprintf(msg, sizeof(msg), "DHT error [%s] (%d) %s" + , operation_names[op] + , error.value() + , convert_from_native(error.message()).c_str()); + return msg; + } + + dht_immutable_item_alert::dht_immutable_item_alert(sha1_hash const& t, entry const& i) + : target(t), item(i) + {} + + std::string dht_immutable_item_alert::message() const + { + char msg[1050]; + snprintf(msg, sizeof(msg), "DHT immutable item %s [ %s ]" + , to_hex(target.to_string()).c_str() + , item.to_string().c_str()); + return msg; + } + + dht_mutable_item_alert::dht_mutable_item_alert(boost::array k + , boost::array sig + , boost::uint64_t sequence + , std::string const& s + , entry const& i) + : key(k), signature(sig), seq(sequence), salt(s), item(i) + {} + + std::string dht_mutable_item_alert::message() const + { + char msg[1050]; + snprintf(msg, sizeof(msg), "DHT mutable item (key=%s salt=%s seq=%" PRId64 ") [ %s ]" + , to_hex(std::string(&key[0], 32)).c_str() + , salt.c_str() + , seq + , item.to_string().c_str()); + return msg; + } + + dht_put_alert::dht_put_alert(sha1_hash const& t) + : target(t) + , seq(0) + {} + + dht_put_alert::dht_put_alert(boost::array key + , boost::array sig + , std::string s + , boost::uint64_t sequence_number) + : target(0) + , public_key(key) + , signature(sig) + , salt(s) + , seq(sequence_number) + {} + + std::string dht_put_alert::message() const + { + char msg[1050]; + snprintf(msg, sizeof(msg), "DHT put complete (key=%s sig=%s salt=%s seq=%" PRId64 ")" + , to_hex(std::string(&public_key[0], 32)).c_str() + , to_hex(std::string(&signature[0], 64)).c_str() + , salt.c_str() + , seq); + return msg; + } + + i2p_alert::i2p_alert(error_code const& ec) + : error(ec) + {} + + std::string i2p_alert::message() const + { + char msg[600]; + snprintf(msg, sizeof(msg), "i2p_error: [%s] %s" + , error.category().name(), convert_from_native(error.message()).c_str()); + return msg; + } + +} // namespace libtorrent + diff --git a/apps/Launcher/ext/libtorrent/src/alert_manager.cpp b/apps/Launcher/ext/libtorrent/src/alert_manager.cpp new file mode 100644 index 0000000000..4f6dd60050 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/alert_manager.cpp @@ -0,0 +1,196 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg, Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" +#include "libtorrent/alert_manager.hpp" +#include "libtorrent/alert_types.hpp" + +#ifndef TORRENT_DISABLE_EXTENSIONS +#include "libtorrent/extensions.hpp" +#endif + +namespace libtorrent +{ + + alert_manager::alert_manager(int queue_limit, boost::uint32_t alert_mask) + : m_alert_mask(alert_mask) + , m_queue_size_limit(queue_limit) + {} + + alert_manager::~alert_manager() + { + while (!m_alerts.empty()) + { + TORRENT_ASSERT(alert_cast(m_alerts.front()) == 0 + && "shutting down session with remaining resume data alerts in the alert queue. " + "You proabably wany to make sure you always wait for all resume data " + "alerts before shutting down"); + delete m_alerts.front(); + m_alerts.pop_front(); + } + } + + alert const* alert_manager::wait_for_alert(time_duration max_wait) + { + mutex::scoped_lock lock(m_mutex); + + if (!m_alerts.empty()) return m_alerts.front(); + + // this call can be interrupted prematurely by other signals + m_condition.wait_for(lock, max_wait); + if (!m_alerts.empty()) return m_alerts.front(); + + return NULL; + } + + void alert_manager::set_dispatch_function(boost::function)> const& fun) + { + mutex::scoped_lock lock(m_mutex); + + m_dispatch = fun; + + std::deque alerts; + m_alerts.swap(alerts); + lock.unlock(); + + while (!alerts.empty()) + { + TORRENT_TRY { + m_dispatch(std::auto_ptr(alerts.front())); + } TORRENT_CATCH(std::exception&) {} + alerts.pop_front(); + } + } + + void dispatch_alert(boost::function dispatcher + , alert* alert_) + { + std::auto_ptr holder(alert_); + dispatcher(*alert_); + } + + void alert_manager::post_alert_ptr(alert* alert_) + { + std::auto_ptr a(alert_); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (ses_extension_list_t::iterator i = m_ses_extensions.begin() + , end(m_ses_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->on_alert(alert_); + } TORRENT_CATCH(std::exception&) {} + } +#endif + + mutex::scoped_lock lock(m_mutex); + post_impl(a, lock); + } + + void alert_manager::post_alert(const alert& alert_) + { + std::auto_ptr a(alert_.clone()); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (ses_extension_list_t::iterator i = m_ses_extensions.begin() + , end(m_ses_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->on_alert(&alert_); + } TORRENT_CATCH(std::exception&) {} + } +#endif + + mutex::scoped_lock lock(m_mutex); + post_impl(a, lock); + } + + void alert_manager::post_impl(std::auto_ptr& alert_, mutex::scoped_lock& l) + { + if (m_dispatch) + { + TORRENT_ASSERT(m_alerts.empty()); + TORRENT_TRY { + m_dispatch(alert_); + } TORRENT_CATCH(std::exception&) {} + } + else if (m_alerts.size() < m_queue_size_limit || !alert_->discardable()) + { + m_alerts.push_back(alert_.release()); + if (m_alerts.size() == 1) + m_condition.notify_all(); + } + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + void alert_manager::add_extension(boost::shared_ptr ext) + { + m_ses_extensions.push_back(ext); + } +#endif + + std::auto_ptr alert_manager::get() + { + mutex::scoped_lock lock(m_mutex); + + if (m_alerts.empty()) + return std::auto_ptr(0); + + alert* result = m_alerts.front(); + m_alerts.pop_front(); + return std::auto_ptr(result); + } + + void alert_manager::get_all(std::deque* alerts) + { + mutex::scoped_lock lock(m_mutex); + if (m_alerts.empty()) return; + m_alerts.swap(*alerts); + } + + bool alert_manager::pending() const + { + mutex::scoped_lock lock(m_mutex); + + return !m_alerts.empty(); + } + + size_t alert_manager::set_alert_queue_size_limit(size_t queue_size_limit_) + { + mutex::scoped_lock lock(m_mutex); + + std::swap(m_queue_size_limit, queue_size_limit_); + return queue_size_limit_; + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/allocator.cpp b/apps/Launcher/ext/libtorrent/src/allocator.cpp new file mode 100644 index 0000000000..310cf0b312 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/allocator.cpp @@ -0,0 +1,201 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/allocator.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" + +#if defined TORRENT_BEOS +#include +#include // malloc/free +#elif !defined TORRENT_WINDOWS +#include // valloc/free +#include // _SC_PAGESIZE +#endif + +#if TORRENT_USE_MEMALIGN || TORRENT_USE_POSIX_MEMALIGN || defined TORRENT_WINDOWS +#include // memalign and _aligned_malloc +#include // _aligned_malloc on mingw +#endif + +#ifdef TORRENT_WINDOWS +// windows.h must be included after stdlib.h under mingw +#include +#endif + +#ifdef TORRENT_MINGW +#define _aligned_malloc __mingw_aligned_malloc +#define _aligned_free __mingw_aligned_free +#endif + +#ifdef TORRENT_DEBUG_BUFFERS +#ifndef TORRENT_WINDOWS +#include +#endif +#include "libtorrent/size_type.hpp" + +struct alloc_header +{ + libtorrent::size_type size; + int magic; + char stack[3072]; +}; + +#endif + +namespace libtorrent +{ + + int page_size() + { + static int s = 0; + if (s != 0) return s; + +#ifdef TORRENT_WINDOWS + SYSTEM_INFO si; + GetSystemInfo(&si); + s = si.dwPageSize; +#elif defined TORRENT_BEOS + s = B_PAGE_SIZE; +#else + s = sysconf(_SC_PAGESIZE); +#endif + // assume the page size is 4 kiB if we + // fail to query it + if (s <= 0) s = 4096; + return s; + } + + char* page_aligned_allocator::malloc(size_type bytes) + { + TORRENT_ASSERT(bytes > 0); + // just sanity check (this needs to be pretty high + // for cases where the cache size is several gigabytes) + TORRENT_ASSERT(bytes < 0x30000000); + + TORRENT_ASSERT(int(bytes) >= page_size()); +#ifdef TORRENT_DEBUG_BUFFERS + int page = page_size(); + int num_pages = (bytes + (page-1)) / page + 2; + int orig_bytes = bytes; + bytes = num_pages * page; +#endif + + char* ret; +#if TORRENT_USE_POSIX_MEMALIGN + if (posix_memalign((void**)&ret, page_size(), bytes) != 0) ret = NULL; +#elif TORRENT_USE_MEMALIGN + ret = (char*)memalign(page_size(), bytes); +#elif defined TORRENT_WINDOWS + ret = (char*)_aligned_malloc(bytes, page_size()); +#elif defined TORRENT_BEOS + area_id id = create_area("", &ret, B_ANY_ADDRESS + , (bytes + page_size() - 1) & (page_size()-1), B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); + if (id < B_OK) return NULL; + ret = (char*)ret; +#else + ret = (char*)valloc(bytes); +#endif + if (ret == NULL) return NULL; + +#ifdef TORRENT_DEBUG_BUFFERS + // make the two surrounding pages non-readable and -writable + alloc_header* h = (alloc_header*)ret; + h->size = orig_bytes; + h->magic = 0x1337; + print_backtrace(h->stack, sizeof(h->stack)); + +#ifdef TORRENT_WINDOWS +#define mprotect(buf, size, prot) VirtualProtect(buf, size, prot, NULL) +#define PROT_READ PAGE_READONLY +#endif + mprotect(ret, page, PROT_READ); + mprotect(ret + (num_pages-1) * page, page, PROT_READ); + +#ifdef TORRENT_WINDOWS +#undef mprotect +#undef PROT_READ +#endif +// fprintf(stderr, "malloc: %p head: %p tail: %p size: %d\n", ret + page, ret, ret + page + bytes, int(bytes)); + + return ret + page; +#endif // TORRENT_DEBUG_BUFFERS + + return ret; + } + + void page_aligned_allocator::free(char* block) + { + if (block == 0) return; + +#ifdef TORRENT_DEBUG_BUFFERS + +#ifdef TORRENT_WINDOWS +#define mprotect(buf, size, prot) VirtualProtect(buf, size, prot, NULL) +#define PROT_READ PAGE_READONLY +#define PROT_WRITE PAGE_READWRITE +#endif + int page = page_size(); + // make the two surrounding pages non-readable and -writable + mprotect(block - page, page, PROT_READ | PROT_WRITE); + alloc_header* h = (alloc_header*)(block - page); + int num_pages = (h->size + (page-1)) / page + 2; + TORRENT_ASSERT(h->magic == 0x1337); + mprotect(block + (num_pages-2) * page, page, PROT_READ | PROT_WRITE); +// fprintf(stderr, "free: %p head: %p tail: %p size: %d\n", block, block - page, block + h->size, int(h->size)); + h->magic = 0; + block -= page; + +#ifdef TORRENT_WINDOWS +#undef mprotect +#undef PROT_READ +#undef PROT_WRITE +#endif + + print_backtrace(h->stack, sizeof(h->stack)); + +#endif // TORRENT_DEBUG_BUFFERS + +#ifdef TORRENT_WINDOWS + _aligned_free(block); +#elif defined TORRENT_BEOS + area_id id = area_for(block); + if (id < B_OK) return; + delete_area(id); +#else + ::free(block); +#endif // TORRENT_WINDOWS + } + + +} + diff --git a/apps/Launcher/ext/libtorrent/src/asio.cpp b/apps/Launcher/ext/libtorrent/src/asio.cpp new file mode 100644 index 0000000000..bde4c1ff33 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/asio.cpp @@ -0,0 +1,23 @@ +// builds all boost.asio source as a separate compilation unit +#include + +#ifndef BOOST_ASIO_SOURCE +#define BOOST_ASIO_SOURCE +#endif + +#include "libtorrent/config.hpp" + +#define TORRENT_HAS_ASIO_DECL x ## BOOST_ASIO_DECL + +// only define BOOST_ASIO_DECL if it hasn't already been defined +// or if it has been defined to an empty string +#if TORRENT_HAS_ASIO_DECL == x +#define BOOST_ASIO_DECL BOOST_SYMBOL_EXPORT +#endif + +#if BOOST_VERSION >= 104500 +#include +#elif BOOST_VERSION >= 104400 +#include +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/asio_ssl.cpp b/apps/Launcher/ext/libtorrent/src/asio_ssl.cpp new file mode 100644 index 0000000000..f762048e35 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/asio_ssl.cpp @@ -0,0 +1,22 @@ +// builds all boost.asio SSL source as a separate compilation unit +#include +#include + +#ifndef BOOST_ASIO_SOURCE +#define BOOST_ASIO_SOURCE +#endif + +#include "libtorrent/config.hpp" + +#define TORRENT_HAS_ASIO_DECL x ## BOOST_ASIO_DECL + +// only define BOOST_ASIO_DECL if it hasn't already been defined +// or if it has been defined to an empty string +#if TORRENT_HAS_ASIO_DECL == x +#define BOOST_ASIO_DECL BOOST_SYMBOL_EXPORT +#endif + +#if BOOST_VERSION >= 104610 +#include +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/assert.cpp b/apps/Launcher/ext/libtorrent/src/assert.cpp new file mode 100644 index 0000000000..47d996938d --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/assert.cpp @@ -0,0 +1,277 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" + +#if TORRENT_PRODUCTION_ASSERTS +#include +#endif + +#if defined TORRENT_DEBUG || defined TORRENT_ASIO_DEBUGGING \ + || TORRENT_RELEASE_ASSERTS || defined TORRENT_DEBUG_BUFFERS + +#ifdef __APPLE__ +#include +#endif + +#include +#include +#include + +// uClibc++ doesn't have cxxabi.h +#if defined __GNUC__ && __GNUC__ >= 3 \ + && !defined __UCLIBCXX_MAJOR__ + +#include + +std::string demangle(char const* name) +{ +// in case this string comes + // this is needed on linux + char const* start = strchr(name, '('); + if (start != 0) + { + ++start; + } + else + { + // this is needed on macos x + start = strstr(name, "0x"); + if (start != 0) + { + start = strchr(start, ' '); + if (start != 0) ++start; + else start = name; + } + else start = name; + } + + char const* end = strchr(start, '+'); + if (end) while (*(end-1) == ' ') --end; + + std::string in; + if (end == 0) in.assign(start); + else in.assign(start, end); + + size_t len; + int status; + char* unmangled = ::abi::__cxa_demangle(in.c_str(), 0, &len, &status); + if (unmangled == 0) return in; + std::string ret(unmangled); + free(unmangled); + return ret; +} +#elif defined WIN32 + +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 // XP + +#include "windows.h" +#include "dbghelp.h" + +std::string demangle(char const* name) +{ + char demangled_name[256]; + if (UnDecorateSymbolName(name, demangled_name, sizeof(demangled_name), UNDNAME_NO_THROW_SIGNATURES) == 0) + demangled_name[0] = 0; + return demangled_name; +} + +#else +std::string demangle(char const* name) { return name; } +#endif + +#include +#include +#include +#include "libtorrent/version.hpp" + +#if TORRENT_USE_EXECINFO +#include + +TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth) +{ + void* stack[50]; + int size = backtrace(stack, 50); + char** symbols = backtrace_symbols(stack, size); + + for (int i = 1; i < size && len > 0; ++i) + { + int ret = snprintf(out, len, "%d: %s\n", i, demangle(symbols[i]).c_str()); + out += ret; + len -= ret; + if (i - 1 == max_depth && max_depth > 0) break; + } + + free(symbols); +} + +// visual studio 9 and up appears to support this +#elif defined WIN32 && _MSC_VER >= 1500 + +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 // XP + +#include "windows.h" +#include "libtorrent/utf8.hpp" + +#include "winbase.h" +#include "dbghelp.h" + +TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth) +{ + typedef USHORT (WINAPI *RtlCaptureStackBackTrace_t)( + __in ULONG FramesToSkip, + __in ULONG FramesToCapture, + __out PVOID *BackTrace, + __out_opt PULONG BackTraceHash); + + static RtlCaptureStackBackTrace_t RtlCaptureStackBackTrace = 0; + + if (RtlCaptureStackBackTrace == 0) + { + // we don't actually have to free this library, everyone has it loaded + HMODULE lib = LoadLibrary(TEXT("kernel32.dll")); + RtlCaptureStackBackTrace = (RtlCaptureStackBackTrace_t)GetProcAddress(lib, "RtlCaptureStackBackTrace"); + if (RtlCaptureStackBackTrace == 0) + { + out[0] = 0; + return; + } + } + + int i; + void* stack[50]; + int size = CaptureStackBackTrace(0, 50, stack, 0); + + SYMBOL_INFO* symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR), 1); + symbol->MaxNameLen = MAX_SYM_NAME; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + + HANDLE p = GetCurrentProcess(); + static bool sym_initialized = false; + if (!sym_initialized) + { + sym_initialized = true; + SymInitialize(p, NULL, true); + } + for (i = 0; i < size && len > 0; ++i) + { + int ret; + if (SymFromAddr(p, uintptr_t(stack[i]), 0, symbol)) + ret = snprintf(out, len, "%d: %s\n", i, symbol->Name); + else + ret = snprintf(out, len, "%d: \n", i); + + out += ret; + len -= ret; + if (i == max_depth && max_depth > 0) break; + } + free(symbol); +} + +#else + +TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth) {} + +#endif + +#endif + +#if TORRENT_USE_ASSERTS || defined TORRENT_ASIO_DEBUGGING + +#if TORRENT_PRODUCTION_ASSERTS +char const* libtorrent_assert_log = "asserts.log"; +// the number of asserts we've printed to the log +boost::detail::atomic_count assert_counter(0); +#endif + +TORRENT_EXPORT void assert_fail(char const* expr, int line, char const* file + , char const* function, char const* value, int kind) +{ +#if TORRENT_PRODUCTION_ASSERTS + // no need to flood the assert log with infinite number of asserts + if (++assert_counter > 500) return; + + FILE* out = fopen(libtorrent_assert_log, "a+"); + if (out == 0) out = stderr; +#else + FILE* out = stderr; +#endif + + char stack[8192]; + stack[0] = '\0'; + print_backtrace(stack, sizeof(stack), 0); + + char const* message = "assertion failed. Please file a bugreport at " + "http://code.google.com/p/libtorrent/issues\n" + "Please include the following information:\n\n" + "version: " LIBTORRENT_VERSION "\n" + LIBTORRENT_REVISION "\n"; + + switch (kind) + { + case 1: + message = "A precondition of a libtorrent function has been violated.\n" + "This indicates a bug in the client application using libtorrent\n"; + } + + fprintf(out, "%s\n" + "file: '%s'\n" + "line: %d\n" + "function: %s\n" + "expression: %s\n" + "%s%s\n" + "stack:\n" + "%s\n" + , message, file, line, function, expr + , value ? value : "", value ? "\n" : "" + , stack); + + // if production asserts are defined, don't abort, just print the error +#if TORRENT_PRODUCTION_ASSERTS + if (out != stderr) fclose(out); +#else + // send SIGINT to the current process + // to break into the debugger + raise(SIGINT); + abort(); +#endif +} + +#else + +TORRENT_EXPORT void assert_fail(char const* expr, int line, char const* file + , char const* function, char const* value, int kind) {} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/bandwidth_limit.cpp b/apps/Launcher/ext/libtorrent/src/bandwidth_limit.cpp new file mode 100644 index 0000000000..155251fa97 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/bandwidth_limit.cpp @@ -0,0 +1,94 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/bandwidth_limit.hpp" +#include + +namespace libtorrent +{ + bandwidth_channel::bandwidth_channel() + : tmp(0) + , distribute_quota(0) + , m_quota_left(0) + , m_limit(0) + {} + + // 0 means infinite + void bandwidth_channel::throttle(int limit) + { + TORRENT_ASSERT(limit >= 0); + // if the throttle is more than this, we might overflow + TORRENT_ASSERT(limit < INT_MAX); + m_limit = limit; + } + + int bandwidth_channel::quota_left() const + { + if (m_limit == 0) return inf; + return (std::max)(int(m_quota_left), 0); + } + + void bandwidth_channel::update_quota(int dt_milliseconds) + { + if (m_limit == 0) return; + m_quota_left += (m_limit * dt_milliseconds + 500) / 1000; + if (m_quota_left > m_limit * 3) m_quota_left = m_limit * 3; + distribute_quota = int((std::max)(m_quota_left, boost::int64_t(0))); +// fprintf(stderr, "%p: [%d]: + %"PRId64" limit: %"PRId64" quota_left: %"PRId64"\n", this +// , dt_milliseconds, (m_limit * dt_milliseconds + 500) / 1000, m_limit +// , m_quota_left); + } + + // this is used when connections disconnect with + // some quota left. It's returned to its bandwidth + // channels. + void bandwidth_channel::return_quota(int amount) + { + TORRENT_ASSERT(amount >= 0); + if (m_limit == 0) return; + TORRENT_ASSERT(m_quota_left <= m_quota_left + amount); + m_quota_left += amount; + } + + void bandwidth_channel::use_quota(int amount) + { + TORRENT_ASSERT(amount >= 0); + TORRENT_ASSERT(m_limit >= 0); + if (m_limit == 0) return; + +// fprintf(stderr, "%p: - %"PRId64" limit: %"PRId64" quota_left: %"PRId64"\n", this +// , amount, m_limit, m_quota_left); + m_quota_left -= amount; + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/bandwidth_manager.cpp b/apps/Launcher/ext/libtorrent/src/bandwidth_manager.cpp new file mode 100644 index 0000000000..8ad2a7e3cd --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/bandwidth_manager.cpp @@ -0,0 +1,241 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/bandwidth_manager.hpp" +#include "libtorrent/time.hpp" + +namespace libtorrent +{ + + bandwidth_manager::bandwidth_manager(int channel +#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT + , bool log +#endif + ) + : m_queued_bytes(0) + , m_channel(channel) + , m_abort(false) + { +#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT + if (log) + m_log.open("bandwidth_limiter.log", std::ios::trunc); + m_start = time_now(); +#endif + } + + void bandwidth_manager::close() + { + m_abort = true; + + queue_t tm; + tm.swap(m_queue); + m_queued_bytes = 0; + + while (!tm.empty()) + { + bw_request& bwr = tm.back(); + bwr.peer->assign_bandwidth(m_channel, bwr.assigned); + tm.pop_back(); + } + } + +#if TORRENT_USE_ASSERTS + bool bandwidth_manager::is_queued(bandwidth_socket const* peer) const + { + for (queue_t::const_iterator i = m_queue.begin() + , end(m_queue.end()); i != end; ++i) + { + if (i->peer.get() == peer) return true; + } + return false; + } +#endif + + int bandwidth_manager::queue_size() const + { + return m_queue.size(); + } + + boost::int64_t bandwidth_manager::queued_bytes() const + { + return m_queued_bytes; + } + + // non prioritized means that, if there's a line for bandwidth, + // others will cut in front of the non-prioritized peers. + // this is used by web seeds + int bandwidth_manager::request_bandwidth(boost::intrusive_ptr const& peer + , int blk, int priority + , bandwidth_channel* chan1 + , bandwidth_channel* chan2 + , bandwidth_channel* chan3 + , bandwidth_channel* chan4 + , bandwidth_channel* chan5 + ) + { + INVARIANT_CHECK; + if (m_abort) return 0; + + TORRENT_ASSERT(blk > 0); + TORRENT_ASSERT(priority > 0); + TORRENT_ASSERT(!is_queued(peer.get())); + + bw_request bwr(peer, blk, priority); + int i = 0; + if (chan1 && chan1->throttle() > 0 && chan1->need_queueing(blk)) + bwr.channel[i++] = chan1; + if (chan2 && chan2->throttle() > 0 && chan2->need_queueing(blk)) + bwr.channel[i++] = chan2; + if (chan3 && chan3->throttle() > 0 && chan3->need_queueing(blk)) + bwr.channel[i++] = chan3; + if (chan4 && chan4->throttle() > 0 && chan4->need_queueing(blk)) + bwr.channel[i++] = chan4; + if (chan5 && chan5->throttle() > 0 && chan5->need_queueing(blk)) + bwr.channel[i++] = chan5; + + if (i == 0) + { + // the connection is not rate limited by any of its + // bandwidth channels, or it doesn't belong to any + // channels. There's no point in adding it to + // the queue, just satisfy the request immediately + return blk; + } + + m_queued_bytes += blk; + m_queue.push_back(bwr); + return 0; + } + +#if TORRENT_USE_INVARIANT_CHECKS + void bandwidth_manager::check_invariant() const + { + boost::int64_t queued = 0; + for (queue_t::const_iterator i = m_queue.begin() + , end(m_queue.end()); i != end; ++i) + { + queued += i->request_size - i->assigned; + } + TORRENT_ASSERT(queued == m_queued_bytes); + } +#endif + + void bandwidth_manager::update_quotas(time_duration const& dt) + { + if (m_abort) return; + if (m_queue.empty()) return; + + INVARIANT_CHECK; + + boost::int64_t dt_milliseconds = total_milliseconds(dt); + if (dt_milliseconds > 3000) dt_milliseconds = 3000; + + // for each bandwidth channel, call update_quota(dt) + + std::vector channels; + + queue_t tm; + + for (queue_t::iterator i = m_queue.begin(); + i != m_queue.end();) + { + if (i->peer->is_disconnecting()) + { + m_queued_bytes -= i->request_size - i->assigned; + + // return all assigned quota to all the + // bandwidth channels this peer belongs to + for (int j = 0; j < bw_request::max_bandwidth_channels && i->channel[j]; ++j) + { + bandwidth_channel* bwc = i->channel[j]; + bwc->return_quota(i->assigned); + } + + i->assigned = 0; + tm.push_back(*i); + i = m_queue.erase(i); + continue; + } + for (int j = 0; j < bw_request::max_bandwidth_channels && i->channel[j]; ++j) + { + bandwidth_channel* bwc = i->channel[j]; + bwc->tmp = 0; + } + ++i; + } + + for (queue_t::iterator i = m_queue.begin() + , end(m_queue.end()); i != end; ++i) + { + for (int j = 0; j < bw_request::max_bandwidth_channels && i->channel[j]; ++j) + { + bandwidth_channel* bwc = i->channel[j]; + if (bwc->tmp == 0) channels.push_back(bwc); + TORRENT_ASSERT(INT_MAX - bwc->tmp > i->priority); + bwc->tmp += i->priority; + } + } + + for (std::vector::iterator i = channels.begin() + , end(channels.end()); i != end; ++i) + { + (*i)->update_quota(int(dt_milliseconds)); + } + + for (queue_t::iterator i = m_queue.begin(); + i != m_queue.end();) + { + int a = i->assign_bandwidth(); + if (i->assigned == i->request_size + || (i->ttl <= 0 && i->assigned > 0)) + { + a += i->request_size - i->assigned; + TORRENT_ASSERT(i->assigned <= i->request_size); + tm.push_back(*i); + i = m_queue.erase(i); + } + else + { + ++i; + } + m_queued_bytes -= a; + } + + while (!tm.empty()) + { + bw_request& bwr = tm.back(); + bwr.peer->assign_bandwidth(m_channel, bwr.assigned); + tm.pop_back(); + } + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/bandwidth_queue_entry.cpp b/apps/Launcher/ext/libtorrent/src/bandwidth_queue_entry.cpp new file mode 100644 index 0000000000..0be7e3c0fa --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/bandwidth_queue_entry.cpp @@ -0,0 +1,74 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include "libtorrent/bandwidth_queue_entry.hpp" +#include +#include + +namespace libtorrent +{ + bw_request::bw_request(boost::intrusive_ptr const& pe + , int blk, int prio) + : peer(pe) + , priority(prio) + , assigned(0) + , request_size(blk) + , ttl(20) + { + TORRENT_ASSERT(priority > 0); + std::memset(channel, 0, sizeof(channel)); + } + + int bw_request::assign_bandwidth() + { + TORRENT_ASSERT(assigned < request_size); + int quota = request_size - assigned; + TORRENT_ASSERT(quota >= 0); + --ttl; + if (quota == 0) return quota; + + for (int j = 0; j < 5 && channel[j]; ++j) + { + if (channel[j]->throttle() == 0) continue; + if (channel[j]->tmp == 0) continue; + quota = (std::min)(int(boost::int64_t(channel[j]->distribute_quota) + * priority / channel[j]->tmp), quota); + } + assigned += quota; + for (int j = 0; j < 5 && channel[j]; ++j) + channel[j]->use_quota(quota); + TORRENT_ASSERT(assigned <= request_size); + return quota; + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/bloom_filter.cpp b/apps/Launcher/ext/libtorrent/src/bloom_filter.cpp new file mode 100644 index 0000000000..72c4317f46 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/bloom_filter.cpp @@ -0,0 +1,76 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/bloom_filter.hpp" + +namespace libtorrent +{ + bool has_bits(boost::uint8_t const* k, boost::uint8_t const* bits, int len) + { + boost::uint32_t idx1 = boost::uint32_t(k[0]) | (boost::uint32_t(k[1]) << 8); + boost::uint32_t idx2 = boost::uint32_t(k[2]) | (boost::uint32_t(k[3]) << 8); + idx1 %= len * 8; + idx2 %= len * 8; + return (bits[idx1/8] & (1 << (idx1 & 7))) != 0 + && (bits[idx2/8] & (1 << (idx2 & 7))) != 0; + } + + void set_bits(boost::uint8_t const* k, boost::uint8_t* bits, int len) + { + boost::uint32_t idx1 = boost::uint32_t(k[0]) | (boost::uint32_t(k[1]) << 8); + boost::uint32_t idx2 = boost::uint32_t(k[2]) | (boost::uint32_t(k[3]) << 8); + idx1 %= len * 8; + idx2 %= len * 8; + bits[idx1/8] |= (1 << (idx1 & 7)); + bits[idx2/8] |= (1 << (idx2 & 7)); + } + + int count_zero_bits(boost::uint8_t const* bits, int len) + { + // number of bits _not_ set in a nibble + boost::uint8_t bitcount[16] = + { + // 0000, 0001, 0010, 0011, 0100, 0101, 0110, 0111, + // 1000, 1001, 1010, 1011, 1100, 1101, 1110, 1111 + 4, 3, 3, 2, 3, 2, 2, 1, + 3, 2, 2, 1, 2, 1, 1, 0 + }; + int ret = 0; + for (int i = 0; i < len; ++i) + { + ret += bitcount[bits[i] & 0xf]; + ret += bitcount[(bits[i] >> 4) & 0xf]; + } + return ret; + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/broadcast_socket.cpp b/apps/Launcher/ext/libtorrent/src/broadcast_socket.cpp new file mode 100644 index 0000000000..e44dcb722e --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/broadcast_socket.cpp @@ -0,0 +1,450 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include + +#include "libtorrent/config.hpp" +#if defined TORRENT_OS2 +#include +#endif + +#include + +#include "libtorrent/socket.hpp" +#include "libtorrent/enum_net.hpp" +#include "libtorrent/broadcast_socket.hpp" +#include "libtorrent/assert.hpp" + +#if defined TORRENT_ASIO_DEBUGGING +#include "libtorrent/debug.hpp" +#endif + +#ifdef TORRENT_DEBUG +#include "libtorrent/socket_io.hpp" +#endif + +#if BOOST_VERSION < 103500 +#include +#include +#else +#include +#include +#endif + +#ifdef TORRENT_WINDOWS +#include // for if_nametoindex +#endif + +namespace libtorrent +{ + bool is_local(address const& a) + { + TORRENT_TRY { +#if TORRENT_USE_IPV6 + if (a.is_v6()) + { + return a.to_v6().is_loopback() + || a.to_v6().is_link_local() + || a.to_v6().is_multicast_link_local(); + } +#endif + address_v4 a4 = a.to_v4(); + unsigned long ip = a4.to_ulong(); + return ((ip & 0xff000000) == 0x0a000000 // 10.x.x.x + || (ip & 0xfff00000) == 0xac100000 // 172.16.x.x + || (ip & 0xffff0000) == 0xc0a80000 // 192.168.x.x + || (ip & 0xffff0000) == 0xa9fe0000 // 169.254.x.x + || (ip & 0xff000000) == 0x7f000000); // 127.x.x.x + } TORRENT_CATCH(std::exception&) { return false; } + } + + bool is_loopback(address const& addr) + { +#if TORRENT_USE_IPV6 + TORRENT_TRY { + if (addr.is_v4()) + return addr.to_v4() == address_v4::loopback(); + else + return addr.to_v6() == address_v6::loopback(); + } TORRENT_CATCH(std::exception&) { return false; } +#else + return addr.to_v4() == address_v4::loopback(); +#endif + } + + bool is_multicast(address const& addr) + { +#if TORRENT_USE_IPV6 + TORRENT_TRY { + if (addr.is_v4()) + return addr.to_v4().is_multicast(); + else + return addr.to_v6().is_multicast(); + } TORRENT_CATCH(std::exception&) { return false; } +#else + return addr.to_v4().is_multicast(); +#endif + } + + bool is_any(address const& addr) + { + TORRENT_TRY { +#if TORRENT_USE_IPV6 + if (addr.is_v4()) + return addr.to_v4() == address_v4::any(); + else if (addr.to_v6().is_v4_mapped()) + return (addr.to_v6().to_v4() == address_v4::any()); + else + return addr.to_v6() == address_v6::any(); +#else + return addr.to_v4() == address_v4::any(); +#endif + } TORRENT_CATCH(std::exception&) { return false; } + } + + bool is_teredo(address const& addr) + { +#if TORRENT_USE_IPV6 + TORRENT_TRY { + if (!addr.is_v6()) return false; + boost::uint8_t teredo_prefix[] = {0x20, 0x01, 0, 0}; + address_v6::bytes_type b = addr.to_v6().to_bytes(); + return memcmp(&b[0], teredo_prefix, 4) == 0; + } TORRENT_CATCH(std::exception&) { return false; } +#else + return false; +#endif + } + + bool supports_ipv6() + { +#if TORRENT_USE_IPV6 + TORRENT_TRY { + error_code ec; + address::from_string("::1", ec); + return !ec; + } TORRENT_CATCH(std::exception&) { return false; } +#else + return false; +#endif + } + + address guess_local_address(io_service& ios) + { + // make a best guess of the interface we're using and its IP + error_code ec; + std::vector const& interfaces = enum_net_interfaces(ios, ec); + address ret = address_v4::any(); + for (std::vector::const_iterator i = interfaces.begin() + , end(interfaces.end()); i != end; ++i) + { + address const& a = i->interface_address; + if (is_loopback(a) + || is_multicast(a) + || is_any(a)) continue; + + // prefer a v4 address, but return a v6 if + // there are no v4 + if (a.is_v4()) return a; + + if (ret != address_v4::any()) + ret = a; + } + return ret; + } + + // count the length of the common bit prefix + int common_bits(unsigned char const* b1 + , unsigned char const* b2, int n) + { + for (int i = 0; i < n; ++i, ++b1, ++b2) + { + unsigned char a = *b1 ^ *b2; + if (a == 0) continue; + int ret = i * 8 + 8; + for (; a > 0; a >>= 1) --ret; + return ret; + } + return n * 8; + } + + // returns the number of bits in that differ from the right + // between the addresses. The larger number, the further apart + // the IPs are + int cidr_distance(address const& a1, address const& a2) + { +#if TORRENT_USE_IPV6 + if (a1.is_v4() && a2.is_v4()) + { +#endif + // both are v4 + address_v4::bytes_type b1 = a1.to_v4().to_bytes(); + address_v4::bytes_type b2 = a2.to_v4().to_bytes(); + return address_v4::bytes_type().size() * 8 + - common_bits(b1.data(), b2.data(), b1.size()); +#if TORRENT_USE_IPV6 + } + + address_v6::bytes_type b1; + address_v6::bytes_type b2; + if (a1.is_v4()) b1 = address_v6::v4_mapped(a1.to_v4()).to_bytes(); + else b1 = a1.to_v6().to_bytes(); + if (a2.is_v4()) b2 = address_v6::v4_mapped(a2.to_v4()).to_bytes(); + else b2 = a2.to_v6().to_bytes(); + return address_v6::bytes_type().size() * 8 + - common_bits(b1.data(), b2.data(), b1.size()); +#endif + } + + broadcast_socket::broadcast_socket( + udp::endpoint const& multicast_endpoint + , receive_handler_t const& handler) + : m_multicast_endpoint(multicast_endpoint) + , m_on_receive(handler) + , m_outstanding_operations(0) + , m_abort(false) + { + TORRENT_ASSERT(is_multicast(m_multicast_endpoint.address())); + + using namespace asio::ip::multicast; + } + + void broadcast_socket::open(io_service& ios, error_code& ec, bool loopback) + { + std::vector interfaces = enum_net_interfaces(ios, ec); + +#if TORRENT_USE_IPV6 + if (m_multicast_endpoint.address().is_v6()) + open_multicast_socket(ios, address_v6::any(), loopback, ec); + else +#endif + open_multicast_socket(ios, address_v4::any(), loopback, ec); + + for (std::vector::const_iterator i = interfaces.begin() + , end(interfaces.end()); i != end; ++i) + { + // only multicast on compatible networks + if (i->interface_address.is_v4() != m_multicast_endpoint.address().is_v4()) continue; + // ignore any loopback interface + if (!loopback && is_loopback(i->interface_address)) continue; + + ec = error_code(); + + // if_nametoindex was introduced in vista +#if TORRENT_USE_IPV6 \ + && (!defined TORRENT_WINDOWS || _WIN32_WINNT >= 0x0600) \ + && !defined TORRENT_MINGW + + if (i->interface_address.is_v6() && + i->interface_address.to_v6().is_link_local()) { + address_v6 addr6 = i->interface_address.to_v6(); + addr6.scope_id(if_nametoindex(i->name)); + open_multicast_socket(ios, addr6, loopback, ec); + + address_v4 const& mask = i->netmask.is_v4() ? + i->netmask.to_v4() : address_v4(); + open_unicast_socket(ios, addr6, mask); + continue; + } + +#endif + open_multicast_socket(ios, i->interface_address, loopback, ec); +#ifdef TORRENT_DEBUG +// fprintf(stderr, "broadcast socket [ if: %s group: %s mask: %s ] %s\n" +// , i->interface_address.to_string().c_str() +// , m_multicast_endpoint.address().to_string().c_str() +// , i->netmask.to_string().c_str() +// , ec.message().c_str()); +#endif + open_unicast_socket(ios, i->interface_address + , i->netmask.is_v4() ? i->netmask.to_v4() : address_v4()); + } + } + + void broadcast_socket::open_multicast_socket(io_service& ios + , address const& addr, bool loopback, error_code& ec) + { + using namespace asio::ip::multicast; + + boost::shared_ptr s(new datagram_socket(ios)); + s->open(addr.is_v4() ? udp::v4() : udp::v6(), ec); + if (ec) return; + s->set_option(datagram_socket::reuse_address(true), ec); + if (ec) return; + s->bind(udp::endpoint(addr, m_multicast_endpoint.port()), ec); + if (ec) return; + s->set_option(join_group(m_multicast_endpoint.address()), ec); + if (ec) return; + s->set_option(hops(255), ec); + if (ec) return; + s->set_option(enable_loopback(loopback), ec); + if (ec) return; + m_sockets.push_back(socket_entry(s)); + socket_entry& se = m_sockets.back(); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("broadcast_socket::on_receive"); +#endif + s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer)) + , se.remote, boost::bind(&broadcast_socket::on_receive, this, &se, _1, _2)); + ++m_outstanding_operations; + } + + void broadcast_socket::open_unicast_socket(io_service& ios, address const& addr + , address_v4 const& mask) + { + using namespace asio::ip::multicast; + error_code ec; + boost::shared_ptr s(new datagram_socket(ios)); + s->open(addr.is_v4() ? udp::v4() : udp::v6(), ec); + if (ec) return; + s->bind(udp::endpoint(addr, 0), ec); + if (ec) return; + + m_unicast_sockets.push_back(socket_entry(s, mask)); + socket_entry& se = m_unicast_sockets.back(); + + // allow sending broadcast messages + asio::socket_base::broadcast option(true); + s->set_option(option, ec); + if (!ec) se.broadcast = true; + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("broadcast_socket::on_receive"); +#endif + s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer)) + , se.remote, boost::bind(&broadcast_socket::on_receive, this, &se, _1, _2)); + ++m_outstanding_operations; + } + + void broadcast_socket::send(char const* buffer, int size, error_code& ec, int flags) + { + for (std::list::iterator i = m_unicast_sockets.begin() + , end(m_unicast_sockets.end()); i != end; ++i) + { + if (!i->socket) continue; + error_code e; + i->socket->send_to(asio::buffer(buffer, size), m_multicast_endpoint, 0, e); + + // if the user specified the broadcast flag, send one to the broadcast + // address as well + if ((flags & broadcast_socket::broadcast) && i->can_broadcast()) + i->socket->send_to(asio::buffer(buffer, size) + , udp::endpoint(i->broadcast_address(), m_multicast_endpoint.port()), 0, e); + +#ifdef TORRENT_DEBUG +// fprintf(stderr, " sending on unicast %s to: %s\n", print_address(i->socket->local_endpoint().address()).c_str() +// , print_endpoint(m_multicast_endpoint).c_str()); +#endif + if (e) + { +#ifdef TORRENT_DEBUG +// fprintf(stderr, " ERROR: %s\n", e.message().c_str()); +#endif + i->socket->close(e); + i->socket.reset(); + } + } + + for (std::list::iterator i = m_sockets.begin() + , end(m_sockets.end()); i != end; ++i) + { + if (!i->socket) continue; + error_code e; + i->socket->send_to(asio::buffer(buffer, size), m_multicast_endpoint, 0, e); +#ifdef TORRENT_DEBUG +// extern std::string print_address(address const& addr); +// extern std::string print_endpoint(udp::endpoint const& ep); +// fprintf(stderr, " sending on multicast %s to: %s\n", print_address(i->socket->local_endpoint().address()).c_str() +// , print_endpoint(m_multicast_endpoint).c_str()); +#endif + if (e) + { +#ifdef TORRENT_DEBUG +// fprintf(stderr, " ERROR: %s\n", e.message().c_str()); +#endif + i->socket->close(e); + i->socket.reset(); + } + } + } + + void broadcast_socket::on_receive(socket_entry* s, error_code const& ec + , std::size_t bytes_transferred) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("broadcast_socket::on_receive"); +#endif + TORRENT_ASSERT(m_outstanding_operations > 0); + --m_outstanding_operations; + + if (ec || bytes_transferred == 0 || !m_on_receive) + { + maybe_abort(); + return; + } + m_on_receive(s->remote, s->buffer, bytes_transferred); + + if (maybe_abort()) return; + if (!s->socket) return; +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("broadcast_socket::on_receive"); +#endif + s->socket->async_receive_from(asio::buffer(s->buffer, sizeof(s->buffer)) + , s->remote, boost::bind(&broadcast_socket::on_receive, this, s, _1, _2)); + ++m_outstanding_operations; + } + + bool broadcast_socket::maybe_abort() + { + bool ret = m_abort; + if (m_abort && m_outstanding_operations == 0) + { + // it's important that m_on_receive is cleared + // before the object is destructed, since it may + // hold a reference to ourself, which would otherwise + // cause an infinite recursion destructing the objects + receive_handler_t().swap(m_on_receive); + } + return ret; + } + + void broadcast_socket::close() + { + std::for_each(m_sockets.begin(), m_sockets.end(), boost::bind(&socket_entry::close, _1)); + std::for_each(m_unicast_sockets.begin(), m_unicast_sockets.end(), boost::bind(&socket_entry::close, _1)); + + m_abort = true; + maybe_abort(); + } +} + + diff --git a/apps/Launcher/ext/libtorrent/src/bt_peer_connection.cpp b/apps/Launcher/ext/libtorrent/src/bt_peer_connection.cpp new file mode 100644 index 0000000000..2d0befd259 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/bt_peer_connection.cpp @@ -0,0 +1,3408 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +Copyright (c) 2007-2014, Arvid Norberg, Un Shyam +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include + +#ifdef TORRENT_USE_OPENSSL +#include // autp_ptr +#endif + +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/identify_client.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/socket_io.hpp" +#include "libtorrent/version.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/broadcast_socket.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/peer_info.hpp" +#include "libtorrent/random.hpp" +#include "libtorrent/alloca.hpp" + +#ifndef TORRENT_DISABLE_ENCRYPTION +#include "libtorrent/pe_crypto.hpp" +#include "libtorrent/hasher.hpp" +#endif + +using boost::shared_ptr; +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + const bt_peer_connection::message_handler + bt_peer_connection::m_message_handler[] = + { + &bt_peer_connection::on_choke, + &bt_peer_connection::on_unchoke, + &bt_peer_connection::on_interested, + &bt_peer_connection::on_not_interested, + &bt_peer_connection::on_have, + &bt_peer_connection::on_bitfield, + &bt_peer_connection::on_request, + &bt_peer_connection::on_piece, + &bt_peer_connection::on_cancel, + &bt_peer_connection::on_dht_port, + 0, 0, 0, + // FAST extension messages + &bt_peer_connection::on_suggest_piece, + &bt_peer_connection::on_have_all, + &bt_peer_connection::on_have_none, + &bt_peer_connection::on_reject_request, + &bt_peer_connection::on_allowed_fast, +#ifndef TORRENT_DISABLE_EXTENSIONS + 0, 0, + &bt_peer_connection::on_extended +#endif + }; + + + bt_peer_connection::bt_peer_connection( + session_impl& ses + , shared_ptr s + , tcp::endpoint const& remote + , policy::peer* peerinfo + , peer_id const& pid + , boost::weak_ptr tor + , bool outgoing) + : peer_connection(ses, tor, s, remote + , peerinfo, outgoing) + , m_state(read_protocol_identifier) + , m_supports_extensions(false) + , m_supports_dht_port(false) + , m_supports_fast(false) +#if TORRENT_USE_ASSERTS + , m_sent_bitfield(false) + , m_in_constructor(true) + , m_sent_handshake(false) +#endif +#ifndef TORRENT_DISABLE_ENCRYPTION + , m_encrypted(false) + , m_rc4_encrypted(false) +#endif +#ifndef TORRENT_DISABLE_EXTENSIONS + , m_upload_only_id(0) + , m_holepunch_id(0) +#endif + , m_our_peer_id(pid) +#ifndef TORRENT_DISABLE_ENCRYPTION + , m_sync_bytes_read(0) +#endif +#ifndef TORRENT_DISABLE_EXTENSIONS + , m_dont_have_id(0) + , m_share_mode_id(0) +#endif + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** bt_peer_connection"); +#endif + +#if TORRENT_USE_ASSERTS + m_in_constructor = false; +#endif +#ifndef TORRENT_DISABLE_EXTENSIONS + memset(m_reserved_bits, 0, sizeof(m_reserved_bits)); +#endif + } + + void bt_peer_connection::start() + { + peer_connection::start(); + + // start in the state where we are trying to read the + // handshake from the other side + reset_recv_buffer(20); + setup_receive(); + } + + bt_peer_connection::~bt_peer_connection() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + } + + void bt_peer_connection::on_connected() + { + if (is_disconnecting()) return; + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + if (t->graceful_pause()) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** ON_CONNECTED [ graceful-paused ]"); +#endif + disconnect(error_code(errors::torrent_paused), 0); + return; + } + +#ifndef TORRENT_DISABLE_ENCRYPTION + + boost::uint8_t out_enc_policy = m_ses.get_pe_settings().out_enc_policy; + +#ifdef TORRENT_USE_OPENSSL + // never try an encrypted connection when already using SSL + if (is_ssl(*get_socket())) + out_enc_policy = pe_settings::disabled; +#endif +#ifdef TORRENT_VERBOSE_LOGGING + char const* policy_name[] = {"forced", "enabled", "disabled"}; + peer_log("*** outgoing encryption policy: %s", policy_name[out_enc_policy]); +#endif + + if (out_enc_policy == pe_settings::forced) + { + write_pe1_2_dhkey(); + if (is_disconnecting()) return; + + m_state = read_pe_dhkey; + reset_recv_buffer(dh_key_len); + setup_receive(); + } + else if (out_enc_policy == pe_settings::enabled) + { + TORRENT_ASSERT(peer_info_struct()); + + policy::peer* pi = peer_info_struct(); + if (pi->pe_support == true) + { + // toggle encryption support flag, toggled back to + // true if encrypted portion of the handshake + // completes correctly + pi->pe_support = false; + + // if this fails, we need to reconnect + // fast. + fast_reconnect(true); + + write_pe1_2_dhkey(); + if (is_disconnecting()) return; + m_state = read_pe_dhkey; + reset_recv_buffer(dh_key_len); + setup_receive(); + } + else // pi->pe_support == false + { + // toggled back to false if standard handshake + // completes correctly (without encryption) + pi->pe_support = true; + + write_handshake(); + reset_recv_buffer(20); + setup_receive(); + } + } + else if (out_enc_policy == pe_settings::disabled) +#endif + { + write_handshake(); + + // start in the state where we are trying to read the + // handshake from the other side + reset_recv_buffer(20); + setup_receive(); + } + } + + void bt_peer_connection::on_metadata() + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** ON_METADATA"); +#endif + // connections that are still in the handshake + // will send their bitfield when the handshake + // is done + if (m_state < read_packet_size) return; + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + write_bitfield(); +#ifndef TORRENT_DISABLE_DHT + if (m_supports_dht_port && m_ses.m_dht) + write_dht_port(m_ses.m_external_udp_port); +#endif + } + + void bt_peer_connection::write_dht_port(int listen_port) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> DHT_PORT [ %d ]", listen_port); +#endif + char msg[] = {0,0,0,3, msg_dht_port, 0, 0}; + char* ptr = msg + 5; + detail::write_uint16(listen_port, ptr); + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_have_all() + { + INVARIANT_CHECK; + TORRENT_ASSERT(m_sent_handshake && !m_sent_bitfield); +#if TORRENT_USE_ASSERTS + m_sent_bitfield = true; +#endif +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> HAVE_ALL"); +#endif + char msg[] = {0,0,0,1, msg_have_all}; + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_have_none() + { + INVARIANT_CHECK; + TORRENT_ASSERT(m_sent_handshake && !m_sent_bitfield); +#if TORRENT_USE_ASSERTS + m_sent_bitfield = true; +#endif +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> HAVE_NONE"); +#endif + char msg[] = {0,0,0,1, msg_have_none}; + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_reject_request(peer_request const& r) + { + INVARIANT_CHECK; + +#ifdef TORRENT_STATS + ++m_ses.m_piece_rejects; +#endif + + if (!m_supports_fast) return; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> REJECT_PIECE [ piece: %d | s: %d | l: %d ]" + , r.piece, r.start, r.length); +#endif + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + TORRENT_ASSERT(associated_torrent().lock()->valid_metadata()); + + char msg[] = {0,0,0,13, msg_reject_request,0,0,0,0, 0,0,0,0, 0,0,0,0}; + char* ptr = msg + 5; + detail::write_int32(r.piece, ptr); // index + detail::write_int32(r.start, ptr); // begin + detail::write_int32(r.length, ptr); // length + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_allow_fast(int piece) + { + INVARIANT_CHECK; + + if (!m_supports_fast) return; + + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + TORRENT_ASSERT(associated_torrent().lock()->valid_metadata()); + + char msg[] = {0,0,0,5, msg_allowed_fast, 0, 0, 0, 0}; + char* ptr = msg + 5; + detail::write_int32(piece, ptr); + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_suggest(int piece) + { + INVARIANT_CHECK; + + if (!m_supports_fast) return; + + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + TORRENT_ASSERT(associated_torrent().lock()->valid_metadata()); + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + if (m_sent_suggested_pieces.empty()) + m_sent_suggested_pieces.resize(t->torrent_file().num_pieces(), false); + + if (m_sent_suggested_pieces[piece]) return; + m_sent_suggested_pieces.set_bit(piece); + + char msg[] = {0,0,0,5, msg_suggest_piece, 0, 0, 0, 0}; + char* ptr = msg + 5; + detail::write_int32(piece, ptr); + send_buffer(msg, sizeof(msg)); + } + + char random_byte() + { return random() & 0xff; } + + void bt_peer_connection::get_specific_peer_info(peer_info& p) const + { + TORRENT_ASSERT(!associated_torrent().expired()); + + if (is_interesting()) p.flags |= peer_info::interesting; + if (is_choked()) p.flags |= peer_info::choked; + if (is_peer_interested()) p.flags |= peer_info::remote_interested; + if (has_peer_choked()) p.flags |= peer_info::remote_choked; + if (support_extensions()) p.flags |= peer_info::supports_extensions; + if (is_outgoing()) p.flags |= peer_info::local_connection; +#if TORRENT_USE_I2P + if (is_i2p(*get_socket())) p.flags |= peer_info::i2p_socket; +#endif + if (is_utp(*get_socket())) p.flags |= peer_info::utp_socket; + if (is_ssl(*get_socket())) p.flags |= peer_info::ssl_socket; + +#ifndef TORRENT_DISABLE_ENCRYPTION + if (m_encrypted) + { + p.flags |= m_rc4_encrypted + ? peer_info::rc4_encrypted + : peer_info::plaintext_encrypted; + } +#endif + + if (!is_connecting() && in_handshake()) + p.flags |= peer_info::handshake; + if (is_connecting() && !is_queued()) p.flags |= peer_info::connecting; + if (is_queued()) p.flags |= peer_info::queued; + + p.client = m_client_version; + p.connection_type = peer_info::standard_bittorrent; + } + + bool bt_peer_connection::in_handshake() const + { + return m_state < read_packet_size; + } + +#ifndef TORRENT_DISABLE_ENCRYPTION + + void bt_peer_connection::write_pe1_2_dhkey() + { + INVARIANT_CHECK; + + TORRENT_ASSERT(!m_encrypted); + TORRENT_ASSERT(!m_rc4_encrypted); + TORRENT_ASSERT(!m_dh_key_exchange.get()); + TORRENT_ASSERT(!m_sent_handshake); + +#ifdef TORRENT_VERBOSE_LOGGING + if (is_outgoing()) + peer_log("*** initiating encrypted handshake"); +#endif + + m_dh_key_exchange.reset(new (std::nothrow) dh_key_exchange); + if (!m_dh_key_exchange || !m_dh_key_exchange->good()) + { + disconnect(errors::no_memory); + return; + } + + int pad_size = random() % 512; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(" pad size: %d", pad_size); +#endif + + char msg[dh_key_len + 512]; + char* ptr = msg; + int buf_size = dh_key_len + pad_size; + + memcpy(ptr, m_dh_key_exchange->get_local_key(), dh_key_len); + ptr += dh_key_len; + + std::generate(ptr, ptr + pad_size, random_byte); + send_buffer(msg, buf_size); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(" sent DH key"); +#endif + } + + void bt_peer_connection::write_pe3_sync() + { + INVARIANT_CHECK; + + TORRENT_ASSERT(!m_encrypted); + TORRENT_ASSERT(!m_rc4_encrypted); + TORRENT_ASSERT(is_outgoing()); + TORRENT_ASSERT(!m_sent_handshake); + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + hasher h; + sha1_hash const& info_hash = t->torrent_file().info_hash(); + char const* const secret = m_dh_key_exchange->get_secret(); + + int pad_size = random() % 512; + + // synchash,skeyhash,vc,crypto_provide,len(pad),pad,len(ia) + char msg[20 + 20 + 8 + 4 + 2 + 512 + 2]; + char* ptr = msg; + + // sync hash (hash('req1',S)) + h.reset(); + h.update("req1",4); + h.update(secret, dh_key_len); + sha1_hash sync_hash = h.final(); + + memcpy(ptr, &sync_hash[0], 20); + ptr += 20; + + // stream key obfuscated hash [ hash('req2',SKEY) xor hash('req3',S) ] + h.reset(); + h.update("req2",4); + h.update((const char*)info_hash.begin(), 20); + sha1_hash streamkey_hash = h.final(); + + h.reset(); + h.update("req3",4); + h.update(secret, dh_key_len); + sha1_hash obfsc_hash = h.final(); + obfsc_hash ^= streamkey_hash; + + memcpy(ptr, &obfsc_hash[0], 20); + ptr += 20; + + // Discard DH key exchange data, setup RC4 keys + init_pe_rc4_handler(secret, info_hash); + m_dh_key_exchange.reset(); // secret should be invalid at this point + + // write the verification constant and crypto field + int encrypt_size = sizeof(msg) - 512 + pad_size - 40; + + boost::uint8_t crypto_provide = m_ses.get_pe_settings().allowed_enc_level; + + // this is an invalid setting, but let's just make the best of the situation + if ((crypto_provide & pe_settings::both) == 0) crypto_provide = pe_settings::both; + +#ifdef TORRENT_VERBOSE_LOGGING + char const* level[] = {"plaintext", "rc4", "plaintext rc4"}; + peer_log(" crypto provide : [ %s ]" + , level[crypto_provide-1]); +#endif + + write_pe_vc_cryptofield(ptr, encrypt_size, crypto_provide, pad_size); + m_enc_handler->encrypt(ptr, encrypt_size); + send_buffer(msg, sizeof(msg) - 512 + pad_size); + } + + void bt_peer_connection::write_pe4_sync(int crypto_select) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(!is_outgoing()); + TORRENT_ASSERT(!m_encrypted); + TORRENT_ASSERT(!m_rc4_encrypted); + TORRENT_ASSERT(crypto_select == 0x02 || crypto_select == 0x01); + TORRENT_ASSERT(!m_sent_handshake); + + int pad_size = random() % 512; + + const int buf_size = 8 + 4 + 2 + pad_size; + char msg[512 + 8 + 4 + 2]; + write_pe_vc_cryptofield(msg, sizeof(msg), crypto_select, pad_size); + + m_enc_handler->encrypt(msg, buf_size); + send_buffer(msg, buf_size); + + // encryption method has been negotiated + if (crypto_select == 0x02) + m_rc4_encrypted = true; + else // 0x01 + m_rc4_encrypted = false; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(" crypto select : [ %s ]" + , (crypto_select == 0x01) ? "plaintext" : "rc4"); +#endif + } + + void bt_peer_connection::write_pe_vc_cryptofield(char* write_buf, int len + , int crypto_field, int pad_size) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(crypto_field <= 0x03 && crypto_field > 0); + // vc,crypto_field,len(pad),pad, (len(ia)) + TORRENT_ASSERT((len >= 8+4+2+pad_size+2 && is_outgoing()) + || (len >= 8+4+2+pad_size && !is_outgoing())); + TORRENT_ASSERT(!m_sent_handshake); + + // encrypt(vc, crypto_provide/select, len(Pad), len(IA)) + // len(pad) is zero for now, len(IA) only for outgoing connections + + // vc + memset(write_buf, 0, 8); + write_buf += 8; + + detail::write_uint32(crypto_field, write_buf); + detail::write_uint16(pad_size, write_buf); // len (pad) + + // fill pad with zeroes + std::generate(write_buf, write_buf + pad_size, random_byte); + write_buf += pad_size; + + // append len(ia) if we are initiating + if (is_outgoing()) + detail::write_uint16(handshake_len, write_buf); // len(IA) + } + + void bt_peer_connection::init_pe_rc4_handler(char const* secret, sha1_hash const& stream_key) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(secret); + + hasher h; + static const char keyA[] = "keyA"; + static const char keyB[] = "keyB"; + + // encryption rc4 longkeys + // outgoing connection : hash ('keyA',S,SKEY) + // incoming connection : hash ('keyB',S,SKEY) + + if (is_outgoing()) h.update(keyA, 4); else h.update(keyB, 4); + h.update(secret, dh_key_len); + h.update((char const*)stream_key.begin(), 20); + const sha1_hash local_key = h.final(); + + h.reset(); + + // decryption rc4 longkeys + // outgoing connection : hash ('keyB',S,SKEY) + // incoming connection : hash ('keyA',S,SKEY) + + if (is_outgoing()) h.update(keyB, 4); else h.update(keyA, 4); + h.update(secret, dh_key_len); + h.update((char const*)stream_key.begin(), 20); + const sha1_hash remote_key = h.final(); + + TORRENT_ASSERT(!m_enc_handler.get()); + m_enc_handler.reset(new (std::nothrow) rc4_handler); + m_enc_handler->set_incoming_key(&remote_key[0], 20); + m_enc_handler->set_outgoing_key(&local_key[0], 20); + + if (!m_enc_handler) + { + disconnect(errors::no_memory); + return; + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(" computed RC4 keys"); +#endif + } + + int bt_peer_connection::get_syncoffset(char const* src, int src_size, + char const* target, int target_size) const + { + TORRENT_ASSERT(target_size >= src_size); + TORRENT_ASSERT(src_size > 0); + TORRENT_ASSERT(src); + TORRENT_ASSERT(target); + + int traverse_limit = target_size - src_size; + + // TODO: this could be optimized using knuth morris pratt + for (int i = 0; i < traverse_limit; ++i) + { + char const* target_ptr = target + i; + if (std::equal(src, src+src_size, target_ptr)) + return i; + } + +// // Partial sync +// for (int i = 0; i < target_size; ++i) +// { +// // first is iterator in src[] at which mismatch occurs +// // second is iterator in target[] at which mismatch occurs +// std::pair ret; +// int src_sync_size; +// if (i > traverse_limit) // partial sync test +// { +// ret = std::mismatch(src, src + src_size - (i - traverse_limit), &target[i]); +// src_sync_size = ret.first - src; +// if (src_sync_size == (src_size - (i - traverse_limit))) +// return i; +// } +// else // complete sync test +// { +// ret = std::mismatch(src, src + src_size, &target[i]); +// src_sync_size = ret.first - src; +// if (src_sync_size == src_size) +// return i; +// } +// } + + // no complete sync + return -1; + } +#endif // #ifndef TORRENT_DISABLE_ENCRYPTION + + void bt_peer_connection::append_const_send_buffer(char const* buffer, int size) + { +#ifndef TORRENT_DISABLE_ENCRYPTION + if (m_encrypted && m_rc4_encrypted) + { + // if we're encrypting this buffer, we need to make a copy + // since we'll mutate it + char* buf = (char*)malloc(size); + memcpy(buf, buffer, size); + bt_append_send_buffer(buf, size, boost::bind(&::free, _1)); + } + else +#endif + { + peer_connection::append_const_send_buffer(buffer, size); + } + } + +#ifndef TORRENT_DISABLE_ENCRYPTION + void encrypt(char* buf, int len, void* userdata) + { + rc4_handler* rc4 = (rc4_handler*)userdata; + rc4->encrypt(buf, len); + } +#endif + + void bt_peer_connection::send_buffer(char const* buf, int size, int flags + , void (*f)(char*, int, void*), void* ud) + { + TORRENT_ASSERT(f == 0); + TORRENT_ASSERT(ud == 0); + TORRENT_ASSERT(buf); + TORRENT_ASSERT(size > 0); + + void* userdata = 0; + void (*fun)(char*, int, void*) = 0; +#ifndef TORRENT_DISABLE_ENCRYPTION + if (m_encrypted && m_rc4_encrypted) + { + fun = encrypt; + userdata = m_enc_handler.get(); + } +#endif + + peer_connection::send_buffer(buf, size, flags, fun, userdata); + } + + void bt_peer_connection::write_handshake() + { + INVARIANT_CHECK; + + TORRENT_ASSERT(!m_sent_handshake); +#if TORRENT_USE_ASSERTS + m_sent_handshake = true; +#endif + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + // add handshake to the send buffer + const char version_string[] = "BitTorrent protocol"; + const int string_len = sizeof(version_string)-1; + + char handshake[1 + string_len + 8 + 20 + 20]; + char* ptr = handshake; + // length of version string + detail::write_uint8(string_len, ptr); + // protocol identifier + memcpy(ptr, version_string, string_len); + ptr += string_len; + // 8 zeroes + memset(ptr, 0, 8); + +#ifndef TORRENT_DISABLE_DHT + // indicate that we support the DHT messages + *(ptr + 7) |= 0x01; +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + // we support extensions + *(ptr + 5) |= 0x10; +#endif + + if (m_ses.m_settings.support_merkle_torrents) + { + // we support merkle torrents + *(ptr + 5) |= 0x08; + } + + // we support FAST extension + *(ptr + 7) |= 0x04; + +#ifdef TORRENT_VERBOSE_LOGGING + std::string bitmask; + for (int k = 0; k < 8; ++k) + { + for (int j = 0; j < 8; ++j) + { + if (ptr[k] & (0x80 >> j)) bitmask += '1'; + else bitmask += '0'; + } + } + peer_log("==> EXTENSION [ %s ]", bitmask.c_str()); +#endif + ptr += 8; + + // info hash + sha1_hash const& ih = t->torrent_file().info_hash(); + memcpy(ptr, &ih[0], 20); + ptr += 20; + + // peer id + if (m_ses.m_settings.anonymous_mode) + { + // in anonymous mode, every peer connection + // has a unique peer-id + for (int i = 0; i < 20; ++i) + m_our_peer_id[i] = random() & 0xff; + } + + memcpy(ptr, &m_our_peer_id[0], 20); + ptr += 20; + +#ifdef TORRENT_VERBOSE_LOGGING + { + char hex_pid[41]; + to_hex((char const*)&m_our_peer_id[0], 20, hex_pid); + hex_pid[40] = 0; + peer_log(">>> sent peer_id: %s client: %s" + , hex_pid, identify_client(m_our_peer_id).c_str()); + } + peer_log("==> HANDSHAKE [ ih: %s ]", to_hex(ih.to_string()).c_str()); +#endif + send_buffer(handshake, sizeof(handshake)); + } + + boost::optional bt_peer_connection::downloading_piece_progress() const + { + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + buffer::const_interval recv_buffer = receive_buffer(); + // are we currently receiving a 'piece' message? + if (m_state != read_packet + || recv_buffer.left() <= 9 + || recv_buffer[0] != msg_piece) + return boost::optional(); + + const char* ptr = recv_buffer.begin + 1; + peer_request r; + r.piece = detail::read_int32(ptr); + r.start = detail::read_int32(ptr); + r.length = packet_size() - 9; + + // is any of the piece message header data invalid? + if (!verify_piece(r)) + return boost::optional(); + + piece_block_progress p; + + p.piece_index = r.piece; + p.block_index = r.start / t->block_size(); + p.bytes_downloaded = recv_buffer.left() - 9; + p.full_block_bytes = r.length; + + return boost::optional(p); + } + + + // message handlers + + // ----------------------------- + // --------- KEEPALIVE --------- + // ----------------------------- + + void bt_peer_connection::on_keepalive() + { + INVARIANT_CHECK; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== KEEPALIVE"); +#endif + incoming_keepalive(); + } + + // ----------------------------- + // ----------- CHOKE ----------- + // ----------------------------- + + void bt_peer_connection::on_choke(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + m_statistics.received_bytes(0, received); + if (packet_size() != 1) + { + disconnect(errors::invalid_choke, 2); + return; + } + if (!packet_finished()) return; + + incoming_choke(); + if (is_disconnecting()) return; + if (!m_supports_fast) + { + // we just got choked, and the peer that choked use + // doesn't support fast extensions, so we have to + // assume that the choke message implies that all + // of our requests are rejected. Go through them and + // pretend that we received reject request messages + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + while (!download_queue().empty()) + { + piece_block const& b = download_queue().front().block; + peer_request r; + r.piece = b.piece_index; + r.start = b.block_index * t->block_size(); + r.length = t->block_size(); + // if it's the last piece, make sure to + // set the length of the request to not + // exceed the end of the torrent. This is + // necessary in order to maintain a correct + // m_outsanding_bytes + if (r.piece == t->torrent_file().num_pieces() - 1) + { + r.length = (std::min)(t->torrent_file().piece_size( + r.piece) - r.start, r.length); + } + incoming_reject_request(r); + } + } + } + + // ----------------------------- + // ---------- UNCHOKE ---------- + // ----------------------------- + + void bt_peer_connection::on_unchoke(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + m_statistics.received_bytes(0, received); + if (packet_size() != 1) + { + disconnect(errors::invalid_unchoke, 2); + return; + } + if (!packet_finished()) return; + + incoming_unchoke(); + } + + // ----------------------------- + // -------- INTERESTED --------- + // ----------------------------- + + void bt_peer_connection::on_interested(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + m_statistics.received_bytes(0, received); + if (packet_size() != 1) + { + disconnect(errors::invalid_interested, 2); + return; + } + if (!packet_finished()) return; + + incoming_interested(); + } + + // ----------------------------- + // ------ NOT INTERESTED ------- + // ----------------------------- + + void bt_peer_connection::on_not_interested(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + m_statistics.received_bytes(0, received); + if (packet_size() != 1) + { + disconnect(errors::invalid_not_interested, 2); + return; + } + if (!packet_finished()) return; + + incoming_not_interested(); + } + + // ----------------------------- + // ----------- HAVE ------------ + // ----------------------------- + + void bt_peer_connection::on_have(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + m_statistics.received_bytes(0, received); + if (packet_size() != 5) + { + disconnect(errors::invalid_have, 2); + return; + } + if (!packet_finished()) return; + + buffer::const_interval recv_buffer = receive_buffer(); + + const char* ptr = recv_buffer.begin + 1; + int index = detail::read_int32(ptr); + + incoming_have(index); + } + + // ----------------------------- + // --------- BITFIELD ---------- + // ----------------------------- + + void bt_peer_connection::on_bitfield(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + m_statistics.received_bytes(0, received); + // if we don't have the metedata, we cannot + // verify the bitfield size + if (t->valid_metadata() + && packet_size() - 1 != (t->torrent_file().num_pieces() + 7) / 8) + { + disconnect(errors::invalid_bitfield_size, 2); + return; + } + + if (!packet_finished()) return; + + buffer::const_interval recv_buffer = receive_buffer(); + + bitfield bits; + bits.borrow_bytes((char*)recv_buffer.begin + 1 + , t->valid_metadata()?get_bitfield().size():(packet_size()-1)*8); + + incoming_bitfield(bits); + } + + // ----------------------------- + // ---------- REQUEST ---------- + // ----------------------------- + + void bt_peer_connection::on_request(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + m_statistics.received_bytes(0, received); + if (packet_size() != 13) + { + disconnect(errors::invalid_request, 2); + return; + } + if (!packet_finished()) return; + + buffer::const_interval recv_buffer = receive_buffer(); + + peer_request r; + const char* ptr = recv_buffer.begin + 1; + r.piece = detail::read_int32(ptr); + r.start = detail::read_int32(ptr); + r.length = detail::read_int32(ptr); + + incoming_request(r); + } + + // ----------------------------- + // ----------- PIECE ----------- + // ----------------------------- + + void bt_peer_connection::on_piece(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + + buffer::const_interval recv_buffer = receive_buffer(); + int recv_pos = receive_pos(); // recv_buffer.end - recv_buffer.begin; + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + bool merkle = (unsigned char)recv_buffer.begin[0] == 250; + if (merkle) + { + if (recv_pos == 1) + { + set_soft_packet_size(13); + m_statistics.received_bytes(0, received); + return; + } + if (recv_pos < 13) + { + m_statistics.received_bytes(0, received); + return; + } + if (recv_pos == 13) + { + const char* ptr = recv_buffer.begin + 9; + int list_size = detail::read_int32(ptr); + // now we know how long the bencoded hash list is + // and we can allocate the disk buffer and receive + // into it + + if (list_size > packet_size() - 13) + { + disconnect(errors::invalid_hash_list, 2); + return; + } + + if (packet_size() - 13 - list_size > t->block_size()) + { + disconnect(errors::packet_too_large, 2); + return; + } + + TORRENT_ASSERT(!has_disk_receive_buffer()); + if (!allocate_disk_receive_buffer(packet_size() - 13 - list_size)) + { + m_statistics.received_bytes(0, received); + return; + } + } + } + else + { + if (recv_pos == 1) + { + TORRENT_ASSERT(!has_disk_receive_buffer()); + + if (packet_size() - 9 > t->block_size()) + { + disconnect(errors::packet_too_large, 2); + return; + } + + if (!allocate_disk_receive_buffer(packet_size() - 9)) + { + m_statistics.received_bytes(0, received); + return; + } + } + } + TORRENT_ASSERT(has_disk_receive_buffer() || packet_size() == 9); + // classify the received data as protocol chatter + // or data payload for the statistics + int piece_bytes = 0; + + int header_size = merkle?13:9; + + peer_request p; + int list_size = 0; + + if (recv_pos >= header_size) + { + const char* ptr = recv_buffer.begin + 1; + p.piece = detail::read_int32(ptr); + p.start = detail::read_int32(ptr); + + if (merkle) + { + list_size = detail::read_int32(ptr); + p.length = packet_size() - list_size - header_size; + header_size += list_size; + } + else + { + p.length = packet_size() - header_size; + } + } + + if (recv_pos <= header_size) + { + // only received protocol data + m_statistics.received_bytes(0, received); + } + else if (recv_pos - received >= header_size) + { + // only received payload data + m_statistics.received_bytes(received, 0); + piece_bytes = received; + } + else + { + // received a bit of both + TORRENT_ASSERT(recv_pos - received < header_size); + TORRENT_ASSERT(recv_pos > header_size); + TORRENT_ASSERT(header_size - (recv_pos - received) <= header_size); + m_statistics.received_bytes( + recv_pos - header_size + , header_size - (recv_pos - received)); + piece_bytes = recv_pos - header_size; + } + + if (recv_pos < header_size) return; + +#ifdef TORRENT_VERBOSE_LOGGING +// peer_log("<== PIECE_FRAGMENT p: %d start: %d length: %d" +// , p.piece, p.start, p.length); +#endif + + if (recv_pos - received < header_size && recv_pos >= header_size) + { + // call this once, the first time the entire header + // has been received + start_receive_piece(p); + if (is_disconnecting()) return; + } + + TORRENT_ASSERT(has_disk_receive_buffer() || packet_size() == header_size); + + incoming_piece_fragment(piece_bytes); + if (!packet_finished()) return; + + if (merkle && list_size > 0) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== HASHPIECE [ piece: %d list: %d ]", p.piece, list_size); +#endif + lazy_entry hash_list; + error_code ec; + if (lazy_bdecode(recv_buffer.begin + 13, recv_buffer.begin+ 13 + list_size + , hash_list, ec) != 0) + { + disconnect(errors::invalid_hash_piece, 2); + return; + } + + // the list has this format: + // [ [node-index, hash], [node-index, hash], ... ] + if (hash_list.type() != lazy_entry::list_t) + { + disconnect(errors::invalid_hash_list, 2); + return; + } + + std::map nodes; + for (int i = 0; i < hash_list.list_size(); ++i) + { + lazy_entry const* e = hash_list.list_at(i); + if (e->type() != lazy_entry::list_t + || e->list_size() != 2 + || e->list_at(0)->type() != lazy_entry::int_t + || e->list_at(1)->type() != lazy_entry::string_t + || e->list_at(1)->string_length() != 20) continue; + + nodes.insert(std::make_pair(int(e->list_int_value_at(0)) + , sha1_hash(e->list_at(1)->string_ptr()))); + } + if (!nodes.empty() && !t->add_merkle_nodes(nodes, p.piece)) + { + disconnect(errors::invalid_hash_piece, 2); + return; + } + } + + disk_buffer_holder holder(m_ses, release_disk_receive_buffer()); + incoming_piece(p, holder); + } + + // ----------------------------- + // ---------- CANCEL ----------- + // ----------------------------- + + void bt_peer_connection::on_cancel(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + m_statistics.received_bytes(0, received); + if (packet_size() != 13) + { + disconnect(errors::invalid_cancel, 2); + return; + } + if (!packet_finished()) return; + + buffer::const_interval recv_buffer = receive_buffer(); + + peer_request r; + const char* ptr = recv_buffer.begin + 1; + r.piece = detail::read_int32(ptr); + r.start = detail::read_int32(ptr); + r.length = detail::read_int32(ptr); + + incoming_cancel(r); + } + + // ----------------------------- + // --------- DHT PORT ---------- + // ----------------------------- + + void bt_peer_connection::on_dht_port(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + m_statistics.received_bytes(0, received); + if (packet_size() != 3) + { + disconnect(errors::invalid_dht_port, 2); + return; + } + if (!packet_finished()) return; + + buffer::const_interval recv_buffer = receive_buffer(); + + const char* ptr = recv_buffer.begin + 1; + int listen_port = detail::read_uint16(ptr); + + incoming_dht_port(listen_port); + + if (!m_supports_dht_port) + { + m_supports_dht_port = true; +#ifndef TORRENT_DISABLE_DHT + if (m_supports_dht_port && m_ses.m_dht) + write_dht_port(m_ses.m_external_udp_port); +#endif + } + } + + void bt_peer_connection::on_suggest_piece(int received) + { + INVARIANT_CHECK; + + m_statistics.received_bytes(0, received); + if (!m_supports_fast) + { + disconnect(errors::invalid_suggest, 2); + return; + } + + if (!packet_finished()) return; + + buffer::const_interval recv_buffer = receive_buffer(); + + const char* ptr = recv_buffer.begin + 1; + int piece = detail::read_uint32(ptr); + incoming_suggest(piece); + } + + void bt_peer_connection::on_have_all(int received) + { + INVARIANT_CHECK; + + m_statistics.received_bytes(0, received); + if (!m_supports_fast) + { + disconnect(errors::invalid_have_all, 2); + return; + } + incoming_have_all(); + } + + void bt_peer_connection::on_have_none(int received) + { + INVARIANT_CHECK; + + m_statistics.received_bytes(0, received); + if (!m_supports_fast) + { + disconnect(errors::invalid_have_none, 2); + return; + } + incoming_have_none(); + } + + void bt_peer_connection::on_reject_request(int received) + { + INVARIANT_CHECK; + + m_statistics.received_bytes(0, received); + if (!m_supports_fast) + { + disconnect(errors::invalid_reject, 2); + return; + } + + if (!packet_finished()) return; + + buffer::const_interval recv_buffer = receive_buffer(); + + peer_request r; + const char* ptr = recv_buffer.begin + 1; + r.piece = detail::read_int32(ptr); + r.start = detail::read_int32(ptr); + r.length = detail::read_int32(ptr); + + incoming_reject_request(r); + } + + void bt_peer_connection::on_allowed_fast(int received) + { + INVARIANT_CHECK; + + m_statistics.received_bytes(0, received); + if (!m_supports_fast) + { + disconnect(errors::invalid_allow_fast, 2); + return; + } + + if (!packet_finished()) return; + buffer::const_interval recv_buffer = receive_buffer(); + const char* ptr = recv_buffer.begin + 1; + int index = detail::read_int32(ptr); + + incoming_allowed_fast(index); + } + + // ----------------------------- + // -------- RENDEZVOUS --------- + // ----------------------------- + +#ifndef TORRENT_DISABLE_EXTENSIONS + void bt_peer_connection::on_holepunch() + { + INVARIANT_CHECK; + + if (!packet_finished()) return; + + // we can't accept holepunch messages from peers + // that don't support the holepunch extension + // because we wouldn't be able to respond + if (m_holepunch_id == 0) return; + + buffer::const_interval recv_buffer = receive_buffer(); + TORRENT_ASSERT(*recv_buffer.begin == msg_extended); + ++recv_buffer.begin; + TORRENT_ASSERT(*recv_buffer.begin == holepunch_msg); + ++recv_buffer.begin; + + const char* ptr = recv_buffer.begin; + + // ignore invalid messages + if (recv_buffer.left() < 2) return; + + int msg_type = detail::read_uint8(ptr); + int addr_type = detail::read_uint8(ptr); + + tcp::endpoint ep; + + if (addr_type == 0) + { + if (recv_buffer.left() < 2 + 4 + 2) return; + // IPv4 address + ep = detail::read_v4_endpoint(ptr); + } +#if TORRENT_USE_IPV6 + else if (addr_type == 1) + { + // IPv6 address + if (recv_buffer.left() < 2 + 18 + 2) return; + ep = detail::read_v6_endpoint(ptr); + } +#endif + else + { +#if defined TORRENT_VERBOSE_LOGGING + error_code ec; + static const char* hp_msg_name[] = {"rendezvous", "connect", "failed"}; + peer_log("<== HOLEPUNCH [ msg: %s from %s to: unknown address type ]" + , (msg_type >= 0 && msg_type < 3 ? hp_msg_name[msg_type] : "unknown message type") + , print_address(remote().address()).c_str()); +#endif + + return; // unknown address type + } + + boost::shared_ptr t = associated_torrent().lock(); + if (!t) return; + + switch (msg_type) + { + case hp_rendezvous: // rendezvous + { +#if defined TORRENT_VERBOSE_LOGGING + peer_log("<== HOLEPUNCH [ msg: rendezvous to: %s ]" + , print_address(ep.address()).c_str()); +#endif + // this peer is asking us to introduce it to + // the peer at 'ep'. We need to find which of + // our connections points to that endpoint + bt_peer_connection* p = t->find_peer(ep); + if (p == 0) + { + // we're not connected to this peer + write_holepunch_msg(hp_failed, ep, hp_not_connected); + break; + } + if (!p->supports_holepunch()) + { + write_holepunch_msg(hp_failed, ep, hp_no_support); + break; + } + if (p == this) + { + write_holepunch_msg(hp_failed, ep, hp_no_self); + break; + } + + write_holepunch_msg(hp_connect, ep, 0); + p->write_holepunch_msg(hp_connect, remote(), 0); + } break; + case hp_connect: + { + // add or find the peer with this endpoint + policy::peer* p = t->get_policy().add_peer(ep, peer_id(0), peer_info::pex, 0); + if (p == 0 || p->connection) + { +#if defined TORRENT_VERBOSE_LOGGING + peer_log("<== HOLEPUNCH [ msg:connect to: %s error: failed to add peer ]" + , print_address(ep.address()).c_str()); +#endif + // we either couldn't add this peer, or it's + // already connected. Just ignore the connect message + break; + } + if (p->banned) + { +#if defined TORRENT_VERBOSE_LOGGING + peer_log("<== HOLEPUNCH [ msg:connect to: %s error: peer banned ]" + , print_address(ep.address()).c_str()); +#endif + // this peer is banned, don't connect to it + break; + + } + // to make sure we use the uTP protocol + p->supports_utp = true; + // #error make sure we make this a connection candidate + // in case it has too many failures for instance + t->connect_to_peer(p, true); + // mark this connection to be in holepunch mode + // so that it will retry faster and stick to uTP while it's + // retrying + if (p->connection) + p->connection->set_holepunch_mode(); +#if defined TORRENT_VERBOSE_LOGGING + peer_log("<== HOLEPUNCH [ msg:connect to: %s ]" + , print_address(ep.address()).c_str()); +#endif + } break; + case hp_failed: + { + boost::uint32_t error = detail::read_uint32(ptr); +#if defined TORRENT_VERBOSE_LOGGING + error_code ec; + char const* err_msg[] = {"no such peer", "not connected", "no support", "no self"}; + peer_log("<== HOLEPUNCH [ msg:failed error: %d msg: %s ]", error + , ((error > 0 && error < 5)?err_msg[error-1]:"unknown message id")); +#endif + // #error deal with holepunch errors + (void)error; + } break; +#if defined TORRENT_VERBOSE_LOGGING + default: + { + error_code ec; + peer_log("<== HOLEPUNCH [ msg: unknown message type (%d) to: %s ]" + , msg_type, print_address(ep.address()).c_str()); + } +#endif + } + } + + void bt_peer_connection::write_holepunch_msg(int type, tcp::endpoint const& ep, int error) + { + char buf[35]; + char* ptr = buf + 6; + detail::write_uint8(type, ptr); + if (ep.address().is_v4()) detail::write_uint8(0, ptr); + else detail::write_uint8(1, ptr); + detail::write_endpoint(ep, ptr); + +#if defined TORRENT_VERBOSE_LOGGING + error_code ec; + static const char* hp_msg_name[] = {"rendezvous", "connect", "failed"}; + static const char* hp_error_string[] = {"", "no such peer", "not connected", "no support", "no self"}; + peer_log("==> HOLEPUNCH [ msg: %s to: %s error: %s ]" + , (type >= 0 && type < 3 ? hp_msg_name[type] : "unknown message type") + , print_address(ep.address()).c_str() + , hp_error_string[error]); +#endif + if (type == hp_failed) + { + detail::write_uint32(error, ptr); + } + + // write the packet length and type + char* hdr = buf; + detail::write_uint32(ptr - buf - 4, hdr); + detail::write_uint8(msg_extended, hdr); + detail::write_uint8(m_holepunch_id, hdr); + + TORRENT_ASSERT(ptr <= buf + sizeof(buf)); + + send_buffer(buf, ptr - buf); + } +#endif // TORRENT_DISABLE_EXTENSIONS + + // ----------------------------- + // --------- EXTENDED ---------- + // ----------------------------- + +#ifndef TORRENT_DISABLE_EXTENSIONS + void bt_peer_connection::on_extended(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + m_statistics.received_bytes(0, received); + if (packet_size() < 2) + { + disconnect(errors::invalid_extended, 2); + return; + } + + if (associated_torrent().expired()) + { + disconnect(errors::invalid_extended, 2); + return; + } + + buffer::const_interval recv_buffer = receive_buffer(); + if (recv_buffer.left() < 2) return; + + TORRENT_ASSERT(*recv_buffer.begin == msg_extended); + ++recv_buffer.begin; + + int extended_id = detail::read_uint8(recv_buffer.begin); + + if (extended_id == 0) + { + on_extended_handshake(); + disconnect_if_redundant(); + return; + } + + if (extended_id == upload_only_msg) + { + if (!packet_finished()) return; + if (packet_size() != 3) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== UPLOAD_ONLY [ ERROR: unexpected packet size: %d ]", packet_size()); +#endif + return; + } + bool ul = detail::read_uint8(recv_buffer.begin) != 0; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== UPLOAD_ONLY [ %s ]", (ul?"true":"false")); +#endif + set_upload_only(ul); + return; + } + + if (extended_id == share_mode_msg) + { + if (!packet_finished()) return; + if (packet_size() != 3) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== SHARE_MODE [ ERROR: unexpected packet size: %d ]", packet_size()); +#endif + return; + } + bool sm = detail::read_uint8(recv_buffer.begin) != 0; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== SHARE_MODE [ %s ]", (sm?"true":"false")); +#endif + set_share_mode(sm); + return; + } + + if (extended_id == holepunch_msg) + { + if (!packet_finished()) return; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== HOLEPUNCH"); +#endif + on_holepunch(); + return; + } + + if (extended_id == dont_have_msg) + { + if (!packet_finished()) return; + if (packet_size() != 6) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== DONT_HAVE [ ERROR: unexpected packet size: %d ]", packet_size()); +#endif + return; + } + int piece = detail::read_uint32(recv_buffer.begin); + incoming_dont_have(piece); + return; + } + +#ifdef TORRENT_VERBOSE_LOGGING + if (packet_finished()) + peer_log("<== EXTENSION MESSAGE [ msg: %d size: %d ]" + , extended_id, packet_size()); +#endif + + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_extended(packet_size() - 2, extended_id + , recv_buffer)) + return; + } + + disconnect(errors::invalid_message, 2); + return; + } + + void bt_peer_connection::on_extended_handshake() + { + if (!packet_finished()) return; + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + buffer::const_interval recv_buffer = receive_buffer(); + + lazy_entry root; + error_code ec; + int pos; + int ret = lazy_bdecode(recv_buffer.begin + 2, recv_buffer.end, root, ec, &pos); + if (ret != 0 || ec || root.type() != lazy_entry::dict_t) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** invalid extended handshake: %s pos: %d" + , ec.message().c_str(), pos); +#endif + return; + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== EXTENDED HANDSHAKE: %s", print_entry(root).c_str()); +#endif + + for (extension_list_t::iterator i = m_extensions.begin(); + !m_extensions.empty() && i != m_extensions.end();) + { + // a false return value means that the extension + // isn't supported by the other end. So, it is removed. + if (!(*i)->on_extension_handshake(root)) + i = m_extensions.erase(i); + else + ++i; + } + if (is_disconnecting()) return; + + // upload_only + if (lazy_entry const* m = root.dict_find_dict("m")) + { + m_upload_only_id = boost::uint8_t(m->dict_find_int_value("upload_only", 0)); + m_holepunch_id = boost::uint8_t(m->dict_find_int_value("ut_holepunch", 0)); + m_dont_have_id = boost::uint8_t(m->dict_find_int_value("lt_donthave", 0)); + } + + // there is supposed to be a remote listen port + int listen_port = int(root.dict_find_int_value("p")); + if (listen_port > 0 && peer_info_struct() != 0) + { + t->get_policy().update_peer_port(listen_port + , peer_info_struct(), peer_info::incoming); + received_listen_port(); + if (is_disconnecting()) return; + } + + // there should be a version too + // but where do we put that info? + + int last_seen_complete = boost::uint8_t(root.dict_find_int_value("complete_ago", -1)); + if (last_seen_complete >= 0) set_last_seen_complete(last_seen_complete); + + std::string client_info = root.dict_find_string_value("v"); + if (!client_info.empty()) m_client_version = client_info; + + int reqq = int(root.dict_find_int_value("reqq")); + if (reqq > 0) + { + max_out_request_queue(reqq); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** MAX OUT REQUEST QUEUE [ %d ]", reqq); +#endif + } + + if (root.dict_find_int_value("upload_only", 0)) + set_upload_only(true); + + if (m_ses.m_settings.support_share_mode + && root.dict_find_int_value("share_mode", 0)) + set_share_mode(true); + + std::string myip = root.dict_find_string_value("yourip"); + if (!myip.empty()) + { + if (myip.size() == address_v4::bytes_type().size()) + { + address_v4::bytes_type bytes; + std::copy(myip.begin(), myip.end(), bytes.begin()); + m_ses.set_external_address(address_v4(bytes) + , aux::session_impl::source_peer, remote().address()); + } +#if TORRENT_USE_IPV6 + else if (myip.size() == address_v6::bytes_type().size()) + { + address_v6::bytes_type bytes; + std::copy(myip.begin(), myip.end(), bytes.begin()); + address_v6 ipv6_address(bytes); + if (ipv6_address.is_v4_mapped()) + m_ses.set_external_address(ipv6_address.to_v4() + , aux::session_impl::source_peer, remote().address()); + else + m_ses.set_external_address(ipv6_address + , aux::session_impl::source_peer, remote().address()); + } +#endif + } + + // if we're finished and this peer is uploading only + // disconnect it + if (t->is_finished() && upload_only() + && t->settings().close_redundant_connections + && !t->share_mode()) + disconnect(errors::upload_upload_connection); + } +#endif // TORRENT_DISABLE_EXTENSIONS + + bool bt_peer_connection::dispatch_message(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + + // this means the connection has been closed already + if (associated_torrent().expired()) + { + m_statistics.received_bytes(0, received); + return false; + } + + buffer::const_interval recv_buffer = receive_buffer(); + + TORRENT_ASSERT(recv_buffer.left() >= 1); + int packet_type = (unsigned char)recv_buffer[0]; + + if (m_ses.m_settings.support_merkle_torrents + && packet_type == 250) packet_type = msg_piece; + + if (packet_type < 0 + || packet_type >= num_supported_messages + || m_message_handler[packet_type] == 0) + { +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_unknown_message(packet_size(), packet_type + , buffer::const_interval(recv_buffer.begin+1 + , recv_buffer.end))) + return packet_finished(); + } +#endif + + m_statistics.received_bytes(0, received); + // What's going on here?! + // break in debug builds to allow investigation +// TORRENT_ASSERT(false); + disconnect(errors::invalid_message); + return packet_finished(); + } + + TORRENT_ASSERT(m_message_handler[packet_type] != 0); + +#ifdef TORRENT_DEBUG + size_type cur_payload_dl = m_statistics.last_payload_downloaded(); + size_type cur_protocol_dl = m_statistics.last_protocol_downloaded(); +#endif + // call the correct handler for this packet type + (this->*m_message_handler[packet_type])(received); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() - cur_payload_dl >= 0); + TORRENT_ASSERT(m_statistics.last_protocol_downloaded() - cur_protocol_dl >= 0); + size_type stats_diff = m_statistics.last_payload_downloaded() - cur_payload_dl + + m_statistics.last_protocol_downloaded() - cur_protocol_dl; + TORRENT_ASSERT(stats_diff == received); +#endif + + return packet_finished(); + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + void bt_peer_connection::write_upload_only() + { + INVARIANT_CHECK; + + boost::shared_ptr t = associated_torrent().lock(); + if (m_upload_only_id == 0) return; + if (t->share_mode()) return; + + // if we send upload-only, the other end is very likely to disconnect + // us, at least if it's a seed. If we don't want to close redundant + // connections, don't sent upload-only + if (!m_ses.settings().close_redundant_connections) return; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> UPLOAD_ONLY [ %d ]" + , int(t->is_upload_only() && !t->super_seeding())); +#endif + + char msg[7] = {0, 0, 0, 3, msg_extended}; + char* ptr = msg + 5; + detail::write_uint8(m_upload_only_id, ptr); + // if we're super seeding, we don't want to make peers + // think that we only have a single piece and is upload + // only, since they might disconnect immediately when + // they have downloaded a single piece, although we'll + // make another piece available + detail::write_uint8(t->is_upload_only() && !t->super_seeding(), ptr); + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_share_mode() + { + INVARIANT_CHECK; + + boost::shared_ptr t = associated_torrent().lock(); + if (m_share_mode_id == 0) return; + + char msg[7] = {0, 0, 0, 3, msg_extended}; + char* ptr = msg + 5; + detail::write_uint8(m_share_mode_id, ptr); + detail::write_uint8(t->share_mode(), ptr); + send_buffer(msg, sizeof(msg)); + } +#endif + + void bt_peer_connection::write_keepalive() + { + INVARIANT_CHECK; + + // Don't require the bitfield to have been sent at this point + // the case where m_sent_bitfield may not be true is if the + // torrent doesn't have any metadata, and a peer is timimg out. + // then the keep-alive message will be sent before the bitfield + // this is a violation to the original protocol, but necessary + // for the metadata extension. + TORRENT_ASSERT(m_sent_handshake); + + char msg[] = {0,0,0,0}; + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_cancel(peer_request const& r) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + TORRENT_ASSERT(associated_torrent().lock()->valid_metadata()); + + char msg[17] = {0,0,0,13, msg_cancel}; + char* ptr = msg + 5; + detail::write_int32(r.piece, ptr); // index + detail::write_int32(r.start, ptr); // begin + detail::write_int32(r.length, ptr); // length + send_buffer(msg, sizeof(msg)); + + if (!m_supports_fast) + incoming_reject_request(r); + } + + void bt_peer_connection::write_request(peer_request const& r) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + TORRENT_ASSERT(associated_torrent().lock()->valid_metadata()); + + char msg[17] = {0,0,0,13, msg_request}; + char* ptr = msg + 5; + + detail::write_int32(r.piece, ptr); // index + detail::write_int32(r.start, ptr); // begin + detail::write_int32(r.length, ptr); // length + send_buffer(msg, sizeof(msg), message_type_request); + } + + void bt_peer_connection::write_bitfield() + { + INVARIANT_CHECK; + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + TORRENT_ASSERT(m_sent_handshake && !m_sent_bitfield); + TORRENT_ASSERT(t->valid_metadata()); + + // in this case, have_all or have_none should be sent instead + TORRENT_ASSERT(!m_supports_fast || !t->is_seed() || t->num_have() != 0); + + if (t->super_seeding()) + { + if (m_supports_fast) write_have_none(); + + // if we are super seeding, pretend to not have any piece + // and don't send a bitfield +#if TORRENT_USE_ASSERTS + m_sent_bitfield = true; +#endif + + // bootstrap superseeding by sending two have message + int piece = t->get_piece_to_super_seed(get_bitfield()); + if (piece >= 0) superseed_piece(-1, piece); + piece = t->get_piece_to_super_seed(get_bitfield()); + if (piece >= 0) superseed_piece(-1, piece); + return; + } + else if (m_supports_fast && t->is_seed() && !m_ses.settings().lazy_bitfields) + { + write_have_all(); + send_allowed_set(); + return; + } + else if (m_supports_fast && t->num_have() == 0) + { + write_have_none(); + send_allowed_set(); + return; + } + else if (t->num_have() == 0) + { + // don't send a bitfield if we don't have any pieces +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(" *** NOT SENDING BITFIELD"); +#endif +#if TORRENT_USE_ASSERTS + m_sent_bitfield = true; +#endif + return; + } + + int num_pieces = t->torrent_file().num_pieces(); + + int lazy_pieces[50]; + int num_lazy_pieces = 0; + int lazy_piece = 0; + + if (t->is_seed() && m_ses.settings().lazy_bitfields +#ifndef TORRENT_DISABLE_ENCRYPTION + && !m_encrypted +#endif + ) + { + num_lazy_pieces = (std::min)(50, num_pieces / 10); + if (num_lazy_pieces < 1) num_lazy_pieces = 1; + for (int i = 0; i < num_pieces; ++i) + { + if (int(random() % (num_pieces - i)) >= num_lazy_pieces - lazy_piece) continue; + lazy_pieces[lazy_piece++] = i; + } + TORRENT_ASSERT(lazy_piece == num_lazy_pieces); + } + + const int packet_size = (num_pieces + 7) / 8 + 5; + + char* msg = TORRENT_ALLOCA(char, packet_size); + if (msg == 0) return; // out of memory + unsigned char* ptr = (unsigned char*)msg; + + detail::write_int32(packet_size - 4, ptr); + detail::write_uint8(msg_bitfield, ptr); + + if (t->is_seed()) + { + memset(ptr, 0xff, packet_size - 5); + + // Clear trailing bits + unsigned char *p = ((unsigned char *)msg) + packet_size - 1; + *p = (0xff << ((8 - (num_pieces & 7)) & 7)) & 0xff; + } + else + { + memset(ptr, 0, packet_size - 5); + piece_picker const& p = t->picker(); + int mask = 0x80; + for (int i = 0; i < num_pieces; ++i) + { + if (p.have_piece(i)) *ptr |= mask; + mask >>= 1; + if (mask == 0) + { + mask = 0x80; + ++ptr; + } + } + } + for (int c = 0; c < num_lazy_pieces; ++c) + msg[5 + lazy_pieces[c] / 8] &= ~(0x80 >> (lazy_pieces[c] & 7)); + +#ifdef TORRENT_VERBOSE_LOGGING + + std::string bitfield_string; + bitfield_string.resize(num_pieces); + for (int k = 0; k < num_pieces; ++k) + { + if (msg[5 + k / 8] & (0x80 >> (k % 8))) bitfield_string[k] = '1'; + else bitfield_string[k] = '0'; + } + peer_log("==> BITFIELD [ %s ]", bitfield_string.c_str()); +#endif +#if TORRENT_USE_ASSERTS + m_sent_bitfield = true; +#endif + + send_buffer(msg, packet_size); + + if (num_lazy_pieces > 0) + { + for (int i = 0; i < num_lazy_pieces; ++i) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> HAVE [ piece: %d ]", lazy_pieces[i]); +#endif + write_have(lazy_pieces[i]); + } + // TODO: if we're finished, send upload_only message + } + + if (m_supports_fast) + send_allowed_set(); + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + void bt_peer_connection::write_extensions() + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_supports_extensions); + TORRENT_ASSERT(m_sent_handshake); + + entry handshake; + entry::dictionary_type& m = handshake["m"].dict(); + + // if we're using a proxy, our listen port won't be useful + // anyway. + if (!m_ses.settings().force_proxy && is_outgoing()) + handshake["p"] = m_ses.listen_port(); + + // only send the port in case we bade the connection + // on incoming connections the other end already knows + // our listen port + if (!m_ses.m_settings.anonymous_mode) + { + handshake["v"] = m_ses.settings().handshake_client_version.empty() + ? m_ses.settings().user_agent : m_ses.settings().handshake_client_version; + } + + std::string remote_address; + std::back_insert_iterator out(remote_address); + detail::write_address(remote().address(), out); + handshake["yourip"] = remote_address; + handshake["reqq"] = m_ses.settings().max_allowed_in_request_queue; + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + m["upload_only"] = upload_only_msg; + m["ut_holepunch"] = holepunch_msg; + if (m_ses.m_settings.support_share_mode) + m["share_mode"] = share_mode_msg; + m["lt_donthave"] = dont_have_msg; + + int complete_ago = -1; + if (t->last_seen_complete() > 0) complete_ago = t->time_since_complete(); + handshake["complete_ago"] = complete_ago; + + // if we're using lazy bitfields or if we're super seeding, don't say + // we're upload only, since it might make peers disconnect + // don't tell anyone we're upload only when in share mode + // we want to stay connected to seeds + // if we're super seeding, we don't want to make peers + // think that we only have a single piece and is upload + // only, since they might disconnect immediately when + // they have downloaded a single piece, although we'll + // make another piece available + if (t->is_upload_only() + && !t->share_mode() + && !t->super_seeding() + && (!m_ses.settings().lazy_bitfields +#ifndef TORRENT_DISABLE_ENCRYPTION + || m_encrypted +#endif + )) + handshake["upload_only"] = 1; + + if (m_ses.m_settings.support_share_mode + && t->share_mode()) + handshake["share_mode"] = 1; + + // loop backwards, to make the first extension be the last + // to fill in the handshake (i.e. give the first extensions priority) + for (extension_list_t::reverse_iterator i = m_extensions.rbegin() + , end(m_extensions.rend()); i != end; ++i) + { + (*i)->add_handshake(handshake); + } + +#ifndef NDEBUG + // make sure there are not conflicting extensions + std::set ext; + for (entry::dictionary_type::const_iterator i = m.begin() + , end(m.end()); i != end; ++i) + { + if (i->second.type() != entry::int_t) continue; + int val = int(i->second.integer()); + TORRENT_ASSERT(ext.find(val) == ext.end()); + ext.insert(val); + } +#endif + + std::vector dict_msg; + bencode(std::back_inserter(dict_msg), handshake); + + char msg[6]; + char* ptr = msg; + + // write the length of the message + detail::write_int32((int)dict_msg.size() + 2, ptr); + detail::write_uint8(msg_extended, ptr); + // signal handshake message + detail::write_uint8(0, ptr); + send_buffer(msg, sizeof(msg)); + send_buffer(&dict_msg[0], dict_msg.size()); + +#if defined TORRENT_VERBOSE_LOGGING + peer_log("==> EXTENDED HANDSHAKE: %s", handshake.to_string().c_str()); +#endif + } +#endif + + void bt_peer_connection::write_choke() + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + + if (is_choked()) return; + char msg[] = {0,0,0,1,msg_choke}; + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_unchoke() + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + + char msg[] = {0,0,0,1,msg_unchoke}; + send_buffer(msg, sizeof(msg)); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + (*i)->sent_unchoke(); + } +#endif + } + + void bt_peer_connection::write_interested() + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + + char msg[] = {0,0,0,1,msg_interested}; + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_not_interested() + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + + char msg[] = {0,0,0,1,msg_not_interested}; + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_have(int index) + { + INVARIANT_CHECK; + TORRENT_ASSERT(associated_torrent().lock()->valid_metadata()); + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < associated_torrent().lock()->torrent_file().num_pieces()); + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + + char msg[] = {0,0,0,5,msg_have,0,0,0,0}; + char* ptr = msg + 5; + detail::write_int32(index, ptr); + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_piece(peer_request const& r, disk_buffer_holder& buffer) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + bool merkle = t->torrent_file().is_merkle_torrent() && r.start == 0; + // the hash piece looks like this: + // uint8_t msg + // uint32_t piece index + // uint32_t start + // uint32_t list len + // var bencoded list + // var piece data + char msg[4 + 1 + 4 + 4 + 4]; + char* ptr = msg; + TORRENT_ASSERT(r.length <= 16 * 1024); + detail::write_int32(r.length + 1 + 4 + 4, ptr); + if (m_ses.m_settings.support_merkle_torrents && merkle) + detail::write_uint8(250, ptr); + else + detail::write_uint8(msg_piece, ptr); + detail::write_int32(r.piece, ptr); + detail::write_int32(r.start, ptr); + + // if this is a merkle torrent and the start offset + // is 0, we need to include the merkle node hashes + if (merkle) + { + std::vector piece_list_buf; + entry piece_list; + entry::list_type& l = piece_list.list(); + std::map merkle_node_list = t->torrent_file().build_merkle_list(r.piece); + for (std::map::iterator i = merkle_node_list.begin() + , end(merkle_node_list.end()); i != end; ++i) + { + l.push_back(entry(entry::list_t)); + l.back().list().push_back(i->first); + l.back().list().push_back(i->second.to_string()); + } + bencode(std::back_inserter(piece_list_buf), piece_list); + detail::write_int32(piece_list_buf.size(), ptr); + + char* ptr = msg; + detail::write_int32(r.length + 1 + 4 + 4 + 4 + piece_list_buf.size(), ptr); + + send_buffer(msg, 17); + send_buffer(&piece_list_buf[0], piece_list_buf.size()); + } + else + { + send_buffer(msg, 13); + } + + bt_append_send_buffer(buffer.get(), r.length + , boost::bind(&session_impl::free_disk_buffer + , boost::ref(m_ses), _1)); + buffer.release(); + + m_payloads.push_back(range(send_buffer_size() - r.length, r.length)); + setup_send(); + } + + namespace + { + struct match_peer_id + { + match_peer_id(peer_id const& id, peer_connection const* pc) + : m_id(id), m_pc(pc) + { TORRENT_ASSERT(pc); } + + bool operator()(policy::peer const* p) const + { + return p->connection != m_pc + && p->connection + && p->connection->pid() == m_id + && !p->connection->pid().is_all_zeros() + && p->address() == m_pc->remote().address(); + } + + peer_id const& m_id; + peer_connection const* m_pc; + }; + } + + // -------------------------- + // RECEIVE DATA + // -------------------------- + + void bt_peer_connection::on_receive(error_code const& error + , std::size_t bytes_transferred) + { + INVARIANT_CHECK; + + if (error) + { + m_statistics.received_bytes(0, bytes_transferred); + return; + } + + boost::shared_ptr t = associated_torrent().lock(); + +#ifndef TORRENT_DISABLE_ENCRYPTION + TORRENT_ASSERT(in_handshake() || !m_rc4_encrypted || m_encrypted); + if (m_rc4_encrypted && m_encrypted) + { + std::pair wr_buf = wr_recv_buffers(bytes_transferred); + m_enc_handler->decrypt(wr_buf.first.begin, wr_buf.first.left()); + if (wr_buf.second.left()) m_enc_handler->decrypt(wr_buf.second.begin, wr_buf.second.left()); + } +#endif + + buffer::const_interval recv_buffer = receive_buffer(); + +#ifndef TORRENT_DISABLE_ENCRYPTION + // m_state is set to read_pe_dhkey in initial state + // (read_protocol_identifier) for incoming, or in constructor + // for outgoing + if (m_state == read_pe_dhkey) + { + m_statistics.received_bytes(0, bytes_transferred); + + TORRENT_ASSERT(!m_encrypted); + TORRENT_ASSERT(!m_rc4_encrypted); + TORRENT_ASSERT(packet_size() == dh_key_len); + TORRENT_ASSERT(recv_buffer == receive_buffer()); + + if (!packet_finished()) return; + + // write our dh public key. m_dh_key_exchange is + // initialized in write_pe1_2_dhkey() + if (!is_outgoing()) write_pe1_2_dhkey(); + if (is_disconnecting()) return; + + // read dh key, generate shared secret + if (m_dh_key_exchange->compute_secret(recv_buffer.begin) != 0) + { + disconnect(errors::no_memory); + return; + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** received DH key"); +#endif + + // PadA/B can be a max of 512 bytes, and 20 bytes more for + // the sync hash (if incoming), or 8 bytes more for the + // encrypted verification constant (if outgoing). Instead + // of requesting the maximum possible, request the maximum + // possible to ensure we do not overshoot the standard + // handshake. + + if (is_outgoing()) + { + m_state = read_pe_syncvc; + write_pe3_sync(); + + // initial payload is the standard handshake, this is + // always rc4 if sent here. m_rc4_encrypted is flagged + // again according to peer selection. + m_rc4_encrypted = true; + m_encrypted = true; + write_handshake(); + m_rc4_encrypted = false; + m_encrypted = false; + + // vc,crypto_select,len(pad),pad, encrypt(handshake) + // 8+4+2+0+handshake_len + reset_recv_buffer(8+4+2+0+handshake_len); + } + else + { + // already written dh key + m_state = read_pe_synchash; + // synchash,skeyhash,vc,crypto_provide,len(pad),pad,encrypt(handshake) + reset_recv_buffer(20+20+8+4+2+0+handshake_len); + } + TORRENT_ASSERT(!packet_finished()); + return; + } + + // cannot fall through into + if (m_state == read_pe_synchash) + { + TORRENT_ASSERT(!m_encrypted); + TORRENT_ASSERT(!m_rc4_encrypted); + TORRENT_ASSERT(!is_outgoing()); + TORRENT_ASSERT(recv_buffer == receive_buffer()); + + if (recv_buffer.left() < 20) + { + m_statistics.received_bytes(0, bytes_transferred); + + if (packet_finished()) + disconnect(errors::sync_hash_not_found, 1); + return; + } + + if (!m_sync_hash.get()) + { + TORRENT_ASSERT(m_sync_bytes_read == 0); + hasher h; + + // compute synchash (hash('req1',S)) + h.update("req1", 4); + h.update(m_dh_key_exchange->get_secret(), dh_key_len); + + m_sync_hash.reset(new (std::nothrow) sha1_hash(h.final())); + if (!m_sync_hash) + { + m_statistics.received_bytes(0, bytes_transferred); + disconnect(errors::no_memory); + return; + } + } + + int syncoffset = get_syncoffset((char*)m_sync_hash->begin(), 20 + , recv_buffer.begin, recv_buffer.left()); + + // No sync + if (syncoffset == -1) + { + m_statistics.received_bytes(0, bytes_transferred); + + std::size_t bytes_processed = recv_buffer.left() - 20; + m_sync_bytes_read += bytes_processed; + if (m_sync_bytes_read >= 512) + { + disconnect(errors::sync_hash_not_found, 1); + return; + } + + cut_receive_buffer(bytes_processed, (std::min)(packet_size() + , (512+20) - m_sync_bytes_read)); + + TORRENT_ASSERT(!packet_finished()); + return; + } + // found complete sync + else + { + std::size_t bytes_processed = syncoffset + 20; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** sync point (hash) found at offset %d" + , m_sync_bytes_read + bytes_processed - 20); +#endif + m_state = read_pe_skey_vc; + // skey,vc - 28 bytes + m_sync_hash.reset(); + int transferred_used = bytes_processed - recv_buffer.left() + bytes_transferred; + TORRENT_ASSERT(transferred_used <= int(bytes_transferred)); + m_statistics.received_bytes(0, transferred_used); + bytes_transferred -= transferred_used; + cut_receive_buffer(bytes_processed, 28); + } + } + + if (m_state == read_pe_skey_vc) + { + m_statistics.received_bytes(0, bytes_transferred); + bytes_transferred = 0; + + TORRENT_ASSERT(!m_encrypted); + TORRENT_ASSERT(!m_rc4_encrypted); + TORRENT_ASSERT(!is_outgoing()); + TORRENT_ASSERT(packet_size() == 28); + + if (!packet_finished()) return; + + recv_buffer = receive_buffer(); + + aux::session_impl::torrent_map::const_iterator i; + + for (i = m_ses.m_torrents.begin(); i != m_ses.m_torrents.end(); ++i) + { + torrent const& ti = *i->second; + + sha1_hash const& skey_hash = ti.obfuscated_hash(); + sha1_hash obfs_hash = m_dh_key_exchange->get_hash_xor_mask(); + obfs_hash ^= skey_hash; + + if (std::equal(recv_buffer.begin, recv_buffer.begin + 20, + (char*)&obfs_hash[0])) + { + if (!t) + { + attach_to_torrent(ti.info_hash(), false); + if (is_disconnecting()) return; + + t = associated_torrent().lock(); + TORRENT_ASSERT(t); + } + + init_pe_rc4_handler(m_dh_key_exchange->get_secret(), ti.info_hash()); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** stream key found, torrent located"); +#endif + break; + } + } + + if (!m_enc_handler.get()) + { + disconnect(errors::invalid_info_hash, 1); + return; + } + + // verify constant + buffer::interval wr_recv_buf = wr_recv_buffer(); + m_enc_handler->decrypt(wr_recv_buf.begin + 20, 8); + wr_recv_buf.begin += 28; + + const char sh_vc[] = {0,0,0,0, 0,0,0,0}; + if (!std::equal(sh_vc, sh_vc+8, recv_buffer.begin + 20)) + { + disconnect(errors::invalid_encryption_constant, 2); + return; + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** verification constant found"); +#endif + m_state = read_pe_cryptofield; + reset_recv_buffer(4 + 2); + } + + // cannot fall through into + if (m_state == read_pe_syncvc) + { + TORRENT_ASSERT(is_outgoing()); + TORRENT_ASSERT(!m_encrypted); + TORRENT_ASSERT(!m_rc4_encrypted); + TORRENT_ASSERT(recv_buffer == receive_buffer()); + + if (recv_buffer.left() < 8) + { + m_statistics.received_bytes(0, bytes_transferred); + if (packet_finished()) + disconnect(errors::invalid_encryption_constant, 2); + return; + } + + // generate the verification constant + if (!m_sync_vc.get()) + { + TORRENT_ASSERT(m_sync_bytes_read == 0); + + m_sync_vc.reset(new (std::nothrow) char[8]); + if (!m_sync_vc) + { + disconnect(errors::no_memory); + return; + } + std::fill(m_sync_vc.get(), m_sync_vc.get() + 8, 0); + m_enc_handler->decrypt(m_sync_vc.get(), 8); + } + + TORRENT_ASSERT(m_sync_vc.get()); + int syncoffset = get_syncoffset(m_sync_vc.get(), 8 + , recv_buffer.begin, recv_buffer.left()); + + // No sync + if (syncoffset == -1) + { + std::size_t bytes_processed = recv_buffer.left() - 8; + m_sync_bytes_read += bytes_processed; + m_statistics.received_bytes(0, bytes_transferred); + + if (m_sync_bytes_read >= 512) + { + disconnect(errors::invalid_encryption_constant, 2); + return; + } + + cut_receive_buffer(bytes_processed, (std::min)(packet_size() + , (512+8) - m_sync_bytes_read)); + + TORRENT_ASSERT(!packet_finished()); + } + // found complete sync + else + { + std::size_t bytes_processed = syncoffset + 8; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** sync point (verification constant) found at offset %d" + , m_sync_bytes_read + bytes_processed - 8); +#endif + int transferred_used = bytes_processed - recv_buffer.left() + bytes_transferred; + TORRENT_ASSERT(transferred_used <= int(bytes_transferred)); + m_statistics.received_bytes(0, transferred_used); + bytes_transferred -= transferred_used; + + cut_receive_buffer(bytes_processed, 4 + 2); + + // delete verification constant + m_sync_vc.reset(); + m_state = read_pe_cryptofield; + // fall through + } + } + + if (m_state == read_pe_cryptofield) // local/remote + { + TORRENT_ASSERT(!m_encrypted); + TORRENT_ASSERT(!m_rc4_encrypted); + TORRENT_ASSERT(packet_size() == 4+2); + m_statistics.received_bytes(0, bytes_transferred); + bytes_transferred = 0; + + if (!packet_finished()) return; + + buffer::interval wr_buf = wr_recv_buffer(); + m_enc_handler->decrypt(wr_buf.begin, packet_size()); + + recv_buffer = receive_buffer(); + + boost::uint32_t crypto_field = detail::read_uint32(recv_buffer.begin); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** crypto %s : [%s%s ]" + , is_outgoing() ? "select" : "provide" + , (crypto_field & 1) ? " plaintext" : "" + , (crypto_field & 2) ? " rc4" : ""); +#endif + + if (!is_outgoing()) + { + // select a crypto method + int allowed_encryption = m_ses.get_pe_settings().allowed_enc_level; + boost::uint32_t crypto_select = crypto_field & allowed_encryption; + + // when prefer_rc4 is set, keep the most significant bit + // otherwise keep the least significant one + if (m_ses.get_pe_settings().prefer_rc4) + { + boost::uint32_t mask = (std::numeric_limits::max)(); + while (crypto_select & (mask << 1)) + { + mask <<= 1; + crypto_select = crypto_select & mask; + } + } + else + { + boost::uint32_t mask = (std::numeric_limits::max)(); + while (crypto_select & (mask >> 1)) + { + mask >>= 1; + crypto_select = crypto_select & mask; + } + } + + if (crypto_select == 0) + { + disconnect(errors::unsupported_encryption_mode, 1); + return; + } + + // write the pe4 step + write_pe4_sync(crypto_select); + } + else // is_outgoing() + { + // check if crypto select is valid + int allowed_encryption = m_ses.get_pe_settings().allowed_enc_level; + + crypto_field &= allowed_encryption; + if (crypto_field == 0) + { + // we don't allow any of the offered encryption levels + disconnect(errors::unsupported_encryption_mode_selected, 2); + return; + } + + if (crypto_field == pe_settings::plaintext) + m_rc4_encrypted = false; + else if (crypto_field == pe_settings::rc4) + m_rc4_encrypted = true; + } + + int len_pad = detail::read_int16(recv_buffer.begin); + if (len_pad < 0 || len_pad > 512) + { + disconnect(errors::invalid_pad_size, 2); + return; + } + + m_state = read_pe_pad; + if (!is_outgoing()) + reset_recv_buffer(len_pad + 2); // len(IA) at the end of pad + else + { + if (len_pad == 0) + { + m_encrypted = true; + m_state = init_bt_handshake; + } + else + reset_recv_buffer(len_pad); + } + } + + if (m_state == read_pe_pad) + { + TORRENT_ASSERT(!m_encrypted); + m_statistics.received_bytes(0, bytes_transferred); + bytes_transferred = 0; + if (!packet_finished()) return; + + int pad_size = is_outgoing() ? packet_size() : packet_size() - 2; + + buffer::interval wr_buf = wr_recv_buffer(); + m_enc_handler->decrypt(wr_buf.begin, packet_size()); + + recv_buffer = receive_buffer(); + + if (!is_outgoing()) + { + recv_buffer.begin += pad_size; + int len_ia = detail::read_int16(recv_buffer.begin); + + if (len_ia < 0) + { + disconnect(errors::invalid_encrypt_handshake, 2); + return; + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** len(IA) : %d", len_ia); +#endif + if (len_ia == 0) + { + // everything after this is Encrypt2 + m_encrypted = true; + m_state = init_bt_handshake; + } + else + { + m_state = read_pe_ia; + reset_recv_buffer(len_ia); + } + } + else // is_outgoing() + { + // everything that arrives after this is Encrypt2 + m_encrypted = true; + m_state = init_bt_handshake; + } + } + + if (m_state == read_pe_ia) + { + m_statistics.received_bytes(0, bytes_transferred); + bytes_transferred = 0; + TORRENT_ASSERT(!is_outgoing()); + TORRENT_ASSERT(!m_encrypted); + + if (!packet_finished()) return; + + // ia is always rc4, so decrypt it + buffer::interval wr_buf = wr_recv_buffer(); + m_enc_handler->decrypt(wr_buf.begin, packet_size()); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** decrypted ia : %d bytes", packet_size()); +#endif + + if (!m_rc4_encrypted) + { + m_enc_handler.reset(); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** destroyed rc4 keys"); +#endif + } + + // everything that arrives after this is encrypted + m_encrypted = true; + + m_state = read_protocol_identifier; + cut_receive_buffer(0, 20); + } + + if (m_state == init_bt_handshake) + { + m_statistics.received_bytes(0, bytes_transferred); + bytes_transferred = 0; + TORRENT_ASSERT(m_encrypted); + + // decrypt remaining received bytes + if (m_rc4_encrypted) + { + buffer::interval wr_buf = wr_recv_buffer(); + wr_buf.begin += packet_size(); + m_enc_handler->decrypt(wr_buf.begin, wr_buf.left()); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** decrypted remaining %d bytes", wr_buf.left()); +#endif + } + else // !m_rc4_encrypted + { + m_enc_handler.reset(); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** destroyed encryption handler"); +#endif + } + + // payload stream, start with 20 handshake bytes + m_state = read_protocol_identifier; + reset_recv_buffer(20); + + // encrypted portion of handshake completed, toggle + // peer_info pe_support flag back to true + if (is_outgoing() && + m_ses.get_pe_settings().out_enc_policy == pe_settings::enabled) + { + policy::peer* pi = peer_info_struct(); + TORRENT_ASSERT(pi); + + pi->pe_support = true; + } + } + +#endif // #ifndef TORRENT_DISABLE_ENCRYPTION + + if (m_state == read_protocol_identifier) + { + m_statistics.received_bytes(0, bytes_transferred); + bytes_transferred = 0; + TORRENT_ASSERT(packet_size() == 20); + + if (!packet_finished()) return; + recv_buffer = receive_buffer(); + + int packet_size = recv_buffer[0]; + const char protocol_string[] = "\x13" "BitTorrent protocol"; + + if (packet_size != 19 || + memcmp(recv_buffer.begin, protocol_string, 20) != 0) + { +#ifndef TORRENT_DISABLE_ENCRYPTION +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** unrecognized protocol header"); +#endif + +#ifdef TORRENT_USE_OPENSSL + if (is_ssl(*get_socket())) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** SSL peers are not allowed to use any other encryption"); +#endif + disconnect(errors::invalid_info_hash, 1); + return; + } +#endif // TORRENT_USE_OPENSSL + + if (!is_outgoing() + && m_ses.get_pe_settings().in_enc_policy == pe_settings::disabled) + { + disconnect(errors::no_incoming_encrypted); + return; + } + + // Don't attempt to perform an encrypted handshake + // within an encrypted connection. For local connections, + // we're expected to already have passed the encrypted + // handshake by this point + if (m_encrypted || is_outgoing()) + { + disconnect(errors::invalid_info_hash, 1); + return; + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** attempting encrypted connection"); +#endif + m_state = read_pe_dhkey; + cut_receive_buffer(0, dh_key_len); + TORRENT_ASSERT(!packet_finished()); + return; +#else + disconnect(errors::invalid_info_hash, 1); + return; +#endif // TORRENT_DISABLE_ENCRYPTION + } + else + { +#ifndef TORRENT_DISABLE_ENCRYPTION + TORRENT_ASSERT(m_state != read_pe_dhkey); + + if (!is_outgoing() + && m_ses.get_pe_settings().in_enc_policy == pe_settings::forced + && !m_encrypted + && !is_ssl(*get_socket())) + { + disconnect(errors::no_incoming_regular); + return; + } +#endif + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== BitTorrent protocol"); +#endif + } + + m_state = read_info_hash; + reset_recv_buffer(28); + } + + // fall through + if (m_state == read_info_hash) + { + m_statistics.received_bytes(0, bytes_transferred); + bytes_transferred = 0; + TORRENT_ASSERT(packet_size() == 28); + + if (!packet_finished()) return; + recv_buffer = receive_buffer(); + + +#ifdef TORRENT_VERBOSE_LOGGING + std::string extensions; + extensions.resize(8 * 8); + for (int i=0; i < 8; ++i) + { + for (int j=0; j < 8; ++j) + { + if (recv_buffer[i] & (0x80 >> j)) extensions[i*8+j] = '1'; + else extensions[i*8+j] = '0'; + } + } + peer_log("<== EXTENSIONS [ %s ext: %s%s%s]" + , extensions.c_str() + , (recv_buffer[7] & 0x01) ? "DHT " : "" + , (recv_buffer[7] & 0x04) ? "FAST " : "" + , (recv_buffer[5] & 0x10) ? "extension " : ""); +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + std::memcpy(m_reserved_bits, recv_buffer.begin, 8); + if ((recv_buffer[5] & 0x10)) + m_supports_extensions = true; +#endif + if (recv_buffer[7] & 0x01) + m_supports_dht_port = true; + + if (recv_buffer[7] & 0x04) + m_supports_fast = true; + + // ok, now we have got enough of the handshake. Is this connection + // attached to a torrent? + if (!t) + { + // now, we have to see if there's a torrent with the + // info_hash we got from the peer + sha1_hash info_hash; + std::copy(recv_buffer.begin + 8, recv_buffer.begin + 28 + , (char*)info_hash.begin()); + +#ifndef TORRENT_DISABLE_ENCRYPTION + bool allow_encrypted = m_encrypted && m_rc4_encrypted; +#else + bool allow_encrypted = true; +#endif + + attach_to_torrent(info_hash, allow_encrypted); + if (is_disconnecting()) return; + } + else + { + // verify info hash + if (!std::equal(recv_buffer.begin + 8, recv_buffer.begin + 28 + , (const char*)t->torrent_file().info_hash().begin())) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** received invalid info_hash"); +#endif + disconnect(errors::invalid_info_hash, 1); + return; + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<<< info_hash received"); +#endif + } + + t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + // if this is a local connection, we have already + // sent the handshake + if (!is_outgoing()) write_handshake(); +// if (t->valid_metadata()) +// write_bitfield(); + TORRENT_ASSERT(m_sent_handshake); + + if (is_disconnecting()) return; + + TORRENT_ASSERT(t->get_policy().has_connection(this)); + + m_state = read_peer_id; + reset_recv_buffer(20); + } + + // fall through + if (m_state == read_peer_id) + { + TORRENT_ASSERT(m_sent_handshake); + m_statistics.received_bytes(0, bytes_transferred); +// bytes_transferred = 0; + if (!t) + { + TORRENT_ASSERT(!packet_finished()); // TODO + return; + } + TORRENT_ASSERT(packet_size() == 20); + + if (!packet_finished()) return; + recv_buffer = receive_buffer(); + +#ifdef TORRENT_VERBOSE_LOGGING + { + char hex_pid[41]; + to_hex(recv_buffer.begin, 20, hex_pid); + hex_pid[40] = 0; + char ascii_pid[21]; + ascii_pid[20] = 0; + for (int i = 0; i != 20; ++i) + { + if (is_print(recv_buffer.begin[i])) ascii_pid[i] = recv_buffer.begin[i]; + else ascii_pid[i] = '.'; + } + peer_log("<<< received peer_id: %s client: %s ascii: \"%s\"" + , hex_pid, identify_client(peer_id(recv_buffer.begin)).c_str(), ascii_pid); + } +#endif + peer_id pid; + std::copy(recv_buffer.begin, recv_buffer.begin + 20, (char*)pid.begin()); + set_pid(pid); + + if (t->settings().allow_multiple_connections_per_ip) + { + // now, let's see if this connection should be closed + policy& p = t->get_policy(); + policy::iterator i = std::find_if(p.begin_peer(), p.end_peer() + , match_peer_id(pid, this)); + if (i != p.end_peer()) + { + TORRENT_ASSERT((*i)->connection->pid() == pid); + // we found another connection with the same peer-id + // which connection should be closed in order to be + // sure that the other end closes the same connection? + // the peer with greatest peer-id is the one allowed to + // initiate connections. So, if our peer-id is greater than + // the others, we should close the incoming connection, + // if not, we should close the outgoing one. + if (pid < m_our_peer_id && is_outgoing()) + { + (*i)->connection->disconnect(errors::duplicate_peer_id); + } + else + { + disconnect(errors::duplicate_peer_id); + return; + } + } + } + + // disconnect if the peer has the same peer-id as ourself + // since it most likely is ourself then + if (pid == m_our_peer_id) + { + if (peer_info_struct()) t->get_policy().ban_peer(peer_info_struct()); + disconnect(errors::self_connection, 1); + return; + } + + m_client_version = identify_client(pid); + boost::optional f = client_fingerprint(pid); + if (f && std::equal(f->name, f->name + 2, "BC")) + { + // if this is a bitcomet client, lower the request queue size limit + if (max_out_request_queue() > 50) max_out_request_queue(50); + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end;) + { + if (!(*i)->on_handshake(m_reserved_bits)) + { + i = m_extensions.erase(i); + } + else + { + ++i; + } + } + if (is_disconnecting()) return; + + if (m_supports_extensions) write_extensions(); +#endif + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== HANDSHAKE"); +#endif + // consider this a successful connection, reset the failcount + if (peer_info_struct()) t->get_policy().set_failcount(peer_info_struct(), 0); + +#ifndef TORRENT_DISABLE_ENCRYPTION + // Toggle pe_support back to false if this is a + // standard successful connection + if (is_outgoing() && !m_encrypted && + m_ses.get_pe_settings().out_enc_policy == pe_settings::enabled) + { + policy::peer* pi = peer_info_struct(); + TORRENT_ASSERT(pi); + + pi->pe_support = false; + } +#endif + + m_state = read_packet_size; + reset_recv_buffer(5); + if (t->ready_for_connections()) + { + write_bitfield(); +#ifndef TORRENT_DISABLE_DHT + if (m_supports_dht_port && m_ses.m_dht) + write_dht_port(m_ses.m_external_udp_port); +#endif + } + + TORRENT_ASSERT(!packet_finished()); + return; + } + + // cannot fall through into + if (m_state == read_packet_size) + { + // Make sure this is not fallen though into + TORRENT_ASSERT(recv_buffer == receive_buffer()); + TORRENT_ASSERT(packet_size() == 5); + + if (!t) return; + + if (recv_buffer.left() < 4) + { + m_statistics.received_bytes(0, bytes_transferred); + return; + } + int transferred_used = 4 - recv_buffer.left() + bytes_transferred; + TORRENT_ASSERT(transferred_used <= int(bytes_transferred)); + m_statistics.received_bytes(0, transferred_used); + bytes_transferred -= transferred_used; + + const char* ptr = recv_buffer.begin; + int packet_size = detail::read_int32(ptr); + + // don't accept packets larger than 1 MB + if (packet_size > 1024*1024 || packet_size < 0) + { + m_statistics.received_bytes(0, bytes_transferred); + // packet too large + disconnect(errors::packet_too_large, 2); + return; + } + + if (packet_size == 0) + { + m_statistics.received_bytes(0, bytes_transferred); + incoming_keepalive(); + if (is_disconnecting()) return; + // keepalive message + m_state = read_packet_size; + cut_receive_buffer(4, 5); + return; + } + else + { + if (recv_buffer.left() < 5) return; + + m_state = read_packet; + cut_receive_buffer(4, packet_size); + TORRENT_ASSERT(bytes_transferred == 1); + recv_buffer = receive_buffer(); + TORRENT_ASSERT(recv_buffer.left() == 1); + } + } + + if (m_state == read_packet) + { + TORRENT_ASSERT(recv_buffer == receive_buffer()); + if (!t) + { + m_statistics.received_bytes(0, bytes_transferred); + disconnect(errors::torrent_removed, 1); + return; + } +#ifdef TORRENT_DEBUG + size_type cur_payload_dl = m_statistics.last_payload_downloaded(); + size_type cur_protocol_dl = m_statistics.last_protocol_downloaded(); +#endif + if (dispatch_message(bytes_transferred)) + { + m_state = read_packet_size; + reset_recv_buffer(5); + } +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() - cur_payload_dl >= 0); + TORRENT_ASSERT(m_statistics.last_protocol_downloaded() - cur_protocol_dl >= 0); + size_type stats_diff = m_statistics.last_payload_downloaded() - cur_payload_dl + + m_statistics.last_protocol_downloaded() - cur_protocol_dl; + TORRENT_ASSERT(stats_diff == size_type(bytes_transferred)); +#endif + TORRENT_ASSERT(!packet_finished()); + return; + } + + TORRENT_ASSERT(!packet_finished()); + } + + // -------------------------- + // SEND DATA + // -------------------------- + + void bt_peer_connection::on_sent(error_code const& error + , std::size_t bytes_transferred) + { + INVARIANT_CHECK; + + if (error) + { + m_statistics.sent_bytes(0, bytes_transferred); + return; + } + + // manage the payload markers + int amount_payload = 0; + if (!m_payloads.empty()) + { + for (std::vector::iterator i = m_payloads.begin(); + i != m_payloads.end(); ++i) + { + i->start -= bytes_transferred; + if (i->start < 0) + { + if (i->start + i->length <= 0) + { + amount_payload += i->length; + } + else + { + amount_payload += -i->start; + i->length -= -i->start; + i->start = 0; + } + } + } + } + + // TODO: move the erasing into the loop above + // remove all payload ranges that has been sent + m_payloads.erase( + std::remove_if(m_payloads.begin(), m_payloads.end(), range_below_zero) + , m_payloads.end()); + + TORRENT_ASSERT(amount_payload <= (int)bytes_transferred); + m_statistics.sent_bytes(amount_payload, bytes_transferred - amount_payload); + + if (amount_payload > 0) + { + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + if (t) t->update_last_upload(); + } + } + +#if TORRENT_USE_INVARIANT_CHECKS + void bt_peer_connection::check_invariant() const + { + boost::shared_ptr t = associated_torrent().lock(); + +#ifndef TORRENT_DISABLE_ENCRYPTION + TORRENT_ASSERT( (bool(m_state != read_pe_dhkey) || m_dh_key_exchange.get()) + || !is_outgoing()); + + TORRENT_ASSERT(!m_rc4_encrypted || m_enc_handler.get()); +#endif + if (!in_handshake()) + { + TORRENT_ASSERT(m_sent_handshake); + } + + if (!m_payloads.empty()) + { + for (std::vector::const_iterator i = m_payloads.begin(); + i != m_payloads.end() - 1; ++i) + { + TORRENT_ASSERT(i->start + i->length <= (i+1)->start); + } + } + } +#endif + +} + diff --git a/apps/Launcher/ext/libtorrent/src/chained_buffer.cpp b/apps/Launcher/ext/libtorrent/src/chained_buffer.cpp new file mode 100644 index 0000000000..5fb8eb33d0 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/chained_buffer.cpp @@ -0,0 +1,159 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/chained_buffer.hpp" +#include "libtorrent/assert.hpp" + +namespace libtorrent +{ + void chained_buffer::pop_front(int bytes_to_pop) + { + TORRENT_ASSERT(bytes_to_pop <= m_bytes); + while (bytes_to_pop > 0 && !m_vec.empty()) + { + buffer_t& b = m_vec.front(); + if (b.used_size > bytes_to_pop) + { + b.start += bytes_to_pop; + b.used_size -= bytes_to_pop; + m_bytes -= bytes_to_pop; + TORRENT_ASSERT(m_bytes <= m_capacity); + TORRENT_ASSERT(m_bytes >= 0); + TORRENT_ASSERT(m_capacity >= 0); + break; + } + + b.free(b.buf); + m_bytes -= b.used_size; + m_capacity -= b.size; + bytes_to_pop -= b.used_size; + TORRENT_ASSERT(m_bytes >= 0); + TORRENT_ASSERT(m_capacity >= 0); + TORRENT_ASSERT(m_bytes <= m_capacity); + m_vec.pop_front(); + } + } + + void chained_buffer::append_buffer(char* buffer, int s, int used_size + , boost::function const& destructor) + { + TORRENT_ASSERT(s >= used_size); + buffer_t b; + b.buf = buffer; + b.size = s; + b.start = buffer; + b.used_size = used_size; + b.free = destructor; + m_vec.push_back(b); + + m_bytes += used_size; + m_capacity += s; + TORRENT_ASSERT(m_bytes <= m_capacity); + } + + // returns the number of bytes available at the + // end of the last chained buffer. + int chained_buffer::space_in_last_buffer() + { + if (m_vec.empty()) return 0; + buffer_t& b = m_vec.back(); + return b.size - b.used_size - (b.start - b.buf); + } + + // tries to copy the given buffer to the end of the + // last chained buffer. If there's not enough room + // it returns false + char* chained_buffer::append(char const* buf, int s) + { + char* insert = allocate_appendix(s); + if (insert == 0) return 0; + memcpy(insert, buf, s); + return insert; + } + + // tries to allocate memory from the end + // of the last buffer. If there isn't + // enough room, returns 0 + char* chained_buffer::allocate_appendix(int s) + { + if (m_vec.empty()) return 0; + buffer_t& b = m_vec.back(); + char* insert = b.start + b.used_size; + if (insert + s > b.buf + b.size) return 0; + b.used_size += s; + m_bytes += s; + TORRENT_ASSERT(m_bytes <= m_capacity); + return insert; + } + + std::list const& chained_buffer::build_iovec(int to_send) + { + m_tmp_vec.clear(); + + for (std::list::iterator i = m_vec.begin() + , end(m_vec.end()); to_send > 0 && i != end; ++i) + { + if (i->used_size > to_send) + { + TORRENT_ASSERT(to_send > 0); + m_tmp_vec.push_back(asio::const_buffer(i->start, to_send)); + break; + } + TORRENT_ASSERT(i->used_size > 0); + m_tmp_vec.push_back(asio::const_buffer(i->start, i->used_size)); + to_send -= i->used_size; + } + return m_tmp_vec; + } + + chained_buffer::~chained_buffer() + { +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(!m_destructed); + m_destructed = true; +#endif + TORRENT_ASSERT(m_bytes >= 0); + TORRENT_ASSERT(m_capacity >= 0); + for (std::list::iterator i = m_vec.begin() + , end(m_vec.end()); i != end; ++i) + { + i->free(i->buf); + } +#ifdef TORRENT_DEBUG + m_bytes = -1; + m_capacity = -1; + m_vec.clear(); +#endif + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/connection_queue.cpp b/apps/Launcher/ext/libtorrent/src/connection_queue.cpp new file mode 100644 index 0000000000..e34d72551a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/connection_queue.cpp @@ -0,0 +1,350 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include "libtorrent/config.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/connection_queue.hpp" +#include "libtorrent/io_service.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/error.hpp" + +#if defined TORRENT_ASIO_DEBUGGING +#include "libtorrent/debug.hpp" +#endif + +namespace libtorrent +{ + + connection_queue::connection_queue(io_service& ios): m_next_ticket(0) + , m_num_connecting(0) + , m_half_open_limit(0) + , m_abort(false) + , m_num_timers(0) + , m_timer(ios) +#ifdef TORRENT_DEBUG + , m_in_timeout_function(false) +#endif + { +#ifdef TORRENT_CONNECTION_LOGGING + m_log.open("connection_queue.log"); +#endif + } + + int connection_queue::free_slots() const + { + mutex_t::scoped_lock l(m_mutex); + return m_half_open_limit == 0 ? (std::numeric_limits::max)() + : m_half_open_limit - m_queue.size(); + } + + void connection_queue::enqueue(boost::function const& on_connect + , boost::function const& on_timeout + , time_duration timeout, int priority) + { + mutex_t::scoped_lock l(m_mutex); + + INVARIANT_CHECK; + + TORRENT_ASSERT(priority >= 0); + TORRENT_ASSERT(priority < 3); + + entry* e = 0; + + if (priority <= 0) + { + m_queue.push_back(entry()); + e = &m_queue.back(); + } + else // priority > 0 + { + m_queue.push_front(entry()); + e = &m_queue.front(); + } + + e->priority = priority; + e->on_connect = on_connect; + e->on_timeout = on_timeout; + e->ticket = m_next_ticket; + e->timeout = timeout; + ++m_next_ticket; + + if (m_next_ticket >= (1 << 29)) + m_next_ticket = 0; + + if (m_num_connecting < m_half_open_limit + || m_half_open_limit == 0) + m_timer.get_io_service().post(boost::bind( + &connection_queue::on_try_connect, this)); + } + + bool connection_queue::done(int ticket) + { + mutex_t::scoped_lock l(m_mutex); + + INVARIANT_CHECK; + + std::list::iterator i = std::find_if(m_queue.begin() + , m_queue.end(), boost::bind(&entry::ticket, _1) == ticket); + if (i == m_queue.end()) + { + // this might not be here in case on_timeout calls remove + return false; + } + if (i->connecting) --m_num_connecting; + m_queue.erase(i); + + if (m_num_connecting < m_half_open_limit + || m_half_open_limit == 0) + m_timer.get_io_service().post(boost::bind( + &connection_queue::on_try_connect, this)); + return true; + } + + void connection_queue::close() + { + error_code ec; + mutex_t::scoped_lock l(m_mutex); + if (m_num_connecting == 0) m_timer.cancel(ec); + m_abort = true; + + std::list tmp; + tmp.swap(m_queue); + m_num_connecting = 0; + + // we don't want to call the timeout callback while we're locked + // since that is a recipie for dead-locks + l.unlock(); + + while (!tmp.empty()) + { + entry& e = tmp.front(); + if (e.priority > 1) + { + mutex_t::scoped_lock ll(m_mutex); + if (e.connecting) ++m_num_connecting; + m_queue.push_back(e); + tmp.pop_front(); + continue; + } + TORRENT_TRY { + if (e.connecting) e.on_timeout(); + else e.on_connect(-1); + } TORRENT_CATCH(std::exception&) {} + tmp.pop_front(); + } + } + + void connection_queue::limit(int limit) + { + TORRENT_ASSERT(limit >= 0); + m_half_open_limit = limit; + } + + int connection_queue::limit() const + { return m_half_open_limit; } + +#if TORRENT_USE_INVARIANT_CHECKS + void connection_queue::check_invariant() const + { + int num_connecting = 0; + for (std::list::const_iterator i = m_queue.begin(); + i != m_queue.end(); ++i) + { + if (i->connecting) ++num_connecting; + else TORRENT_ASSERT(i->expires == max_time()); + } + TORRENT_ASSERT(num_connecting == m_num_connecting); + } + +#endif + + void connection_queue::try_connect(connection_queue::mutex_t::scoped_lock& l) + { + INVARIANT_CHECK; + +#ifdef TORRENT_CONNECTION_LOGGING + m_log << log_time() << " " << free_slots() << std::endl; +#endif + // if this is enabled, UPnP connections will be blocked when shutting down +// if (m_abort) return; + + if (m_num_connecting >= m_half_open_limit + && m_half_open_limit > 0) return; + + if (m_queue.empty()) + { + error_code ec; + m_timer.cancel(ec); + return; + } + + // all entries are connecting, no need to look for new ones + if (int(m_queue.size()) == m_num_connecting) + return; + + std::list::iterator i = std::find_if(m_queue.begin() + , m_queue.end(), boost::bind(&entry::connecting, _1) == false); + + std::list to_connect; + + while (i != m_queue.end()) + { + TORRENT_ASSERT(i->connecting == false); + ptime expire = time_now_hires() + i->timeout; + if (m_num_connecting == 0) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("connection_queue::on_timeout"); +#endif + error_code ec; + m_timer.expires_at(expire, ec); + m_timer.async_wait(boost::bind(&connection_queue::on_timeout, this, _1)); + ++m_num_timers; + } + i->connecting = true; + ++m_num_connecting; + i->expires = expire; + + INVARIANT_CHECK; + + to_connect.push_back(*i); + +#ifdef TORRENT_CONNECTION_LOGGING + m_log << log_time() << " " << free_slots() << std::endl; +#endif + + if (m_num_connecting >= m_half_open_limit + && m_half_open_limit > 0) break; + if (m_num_connecting == int(m_queue.size())) break; + i = std::find_if(i, m_queue.end(), boost::bind(&entry::connecting, _1) == false); + } + + l.unlock(); + + while (!to_connect.empty()) + { + entry& ent = to_connect.front(); + TORRENT_TRY { + ent.on_connect(ent.ticket); + } TORRENT_CATCH(std::exception&) {} + to_connect.pop_front(); + } + + } + +#ifdef TORRENT_DEBUG + struct function_guard + { + function_guard(bool& v): val(v) { TORRENT_ASSERT(!val); val = true; } + ~function_guard() { val = false; } + + bool& val; + }; +#endif + + void connection_queue::on_timeout(error_code const& e) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("connection_queue::on_timeout"); +#endif + mutex_t::scoped_lock l(m_mutex); + --m_num_timers; + + INVARIANT_CHECK; +#ifdef TORRENT_DEBUG + function_guard guard_(m_in_timeout_function); +#endif + + TORRENT_ASSERT(!e || e == error::operation_aborted); + + // if there was an error, it's most likely operation aborted, + // we should just quit. However, in case there are still connections + // in connecting state, and there are no other timer invocations + // we need to stick around still. + if (e && (m_num_connecting == 0 || m_num_timers > 0)) return; + + ptime next_expire = max_time(); + ptime now = time_now_hires() + milliseconds(100); + std::list timed_out; + for (std::list::iterator i = m_queue.begin(); + !m_queue.empty() && i != m_queue.end();) + { + if (i->connecting && i->expires < now) + { + std::list::iterator j = i; + ++i; + timed_out.splice(timed_out.end(), m_queue, j, i); + --m_num_connecting; + continue; + } + if (i->connecting && i->expires < next_expire) + next_expire = i->expires; + ++i; + } + + // we don't want to call the timeout callback while we're locked + // since that is a recepie for dead-locks + l.unlock(); + + for (std::list::iterator i = timed_out.begin() + , end(timed_out.end()); i != end; ++i) + { + TORRENT_ASSERT(i->connecting); + TORRENT_ASSERT(i->ticket != -1); + TORRENT_TRY { + i->on_timeout(); + } TORRENT_CATCH(std::exception&) {} + } + + l.lock(); + + if (next_expire < max_time()) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("connection_queue::on_timeout"); +#endif + error_code ec; + m_timer.expires_at(next_expire, ec); + m_timer.async_wait(boost::bind(&connection_queue::on_timeout, this, _1)); + ++m_num_timers; + } + try_connect(l); + } + + void connection_queue::on_try_connect() + { + mutex_t::scoped_lock l(m_mutex); + try_connect(l); + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/create_torrent.cpp b/apps/Launcher/ext/libtorrent/src/create_torrent.cpp new file mode 100644 index 0000000000..c814be8dbb --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/create_torrent.cpp @@ -0,0 +1,657 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/create_torrent.hpp" +#include "libtorrent/file_pool.hpp" +#include "libtorrent/storage.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/torrent_info.hpp" // for merkle_*() + +#include +#include + +#include +#include + +#define MAX_SYMLINK_PATH 200 + +namespace libtorrent +{ + + namespace detail + { + int get_file_attributes(std::string const& p) + { +#ifdef TORRENT_WINDOWS + +#if TORRENT_USE_WSTRING + std::wstring path = convert_to_wstring(p); + DWORD attr = GetFileAttributesW(path.c_str()); +#else + std::string path = convert_to_native(p); + DWORD attr = GetFileAttributesA(path.c_str()); +#endif // TORRENT_USE_WSTRING + if (attr == INVALID_FILE_ATTRIBUTES) return 0; + if (attr & FILE_ATTRIBUTE_HIDDEN) return file_storage::attribute_hidden; + return 0; +#else + struct stat s; + if (lstat(convert_to_native(p).c_str(), &s) < 0) return 0; + int file_attr = 0; + if (s.st_mode & S_IXUSR) + file_attr += file_storage::attribute_executable; + if (S_ISLNK(s.st_mode)) + file_attr += file_storage::attribute_symlink; + return file_attr; +#endif + } + +#ifndef TORRENT_WINDOWS + std::string get_symlink_path_impl(char const* path) + { + char buf[MAX_SYMLINK_PATH]; + std::string f = convert_to_native(path); + int char_read = readlink(f.c_str(),buf,MAX_SYMLINK_PATH); + if (char_read < 0) return ""; + if (char_read < MAX_SYMLINK_PATH) buf[char_read] = 0; + else buf[0] = 0; + return convert_from_native(buf); + } +#endif + + std::string get_symlink_path(std::string const& p) + { +#if defined TORRENT_WINDOWS + return ""; +#else + std::string path = convert_to_native(p); + return get_symlink_path_impl(p.c_str()); +#endif + } + + void add_files_impl(file_storage& fs, std::string const& p + , std::string const& l, boost::function pred, boost::uint32_t flags) + { + std::string f = combine_path(p, l); + if (!pred(f)) return; + error_code ec; + file_status s; + stat_file(f, &s, ec, (flags & create_torrent::symlinks) ? dont_follow_links : 0); + if (ec) return; + + // recurse into directories + bool recurse = (s.mode & file_status::directory) != 0; + + // if the file is not a link or we're following links, and it's a directory + // only then should we recurse +#ifndef TORRENT_WINDOWS + if ((s.mode & file_status::link) && (flags & create_torrent::symlinks)) + recurse = false; +#endif + + if (recurse) + { + for (directory i(f, ec); !i.done(); i.next(ec)) + { + std::string leaf = i.file(); + if (ignore_subdir(leaf)) continue; + add_files_impl(fs, p, combine_path(l, leaf), pred, flags); + } + } + else + { + // #error use the fields from s + int file_flags = get_file_attributes(f); + + // mask all bits to check if the file is a symlink + if ((file_flags & file_storage::attribute_symlink) + && (flags & create_torrent::symlinks)) + { + std::string sym_path = get_symlink_path(f); + fs.add_file(l, 0, file_flags, s.mtime, sym_path); + } + else + { + fs.add_file(l, s.file_size, file_flags, s.mtime); + } + } + } + } + + struct piece_holder + { + piece_holder(int bytes): m_piece(page_aligned_allocator::malloc(bytes)) {} + ~piece_holder() { page_aligned_allocator::free(m_piece); } + char* bytes() { return m_piece; } + private: + char* m_piece; + }; + +#if TORRENT_USE_WSTRING + void set_piece_hashes(create_torrent& t, std::wstring const& p + , boost::function const& f, error_code& ec) + { + file_pool fp; + std::string utf8; + wchar_utf8(p, utf8); +#if TORRENT_USE_UNC_PATHS + utf8 = canonicalize_path(utf8); +#endif + boost::scoped_ptr st( + default_storage_constructor(const_cast(t.files()), 0, utf8, fp + , std::vector())); + + // calculate the hash for all pieces + int num = t.num_pieces(); + std::vector buf(t.piece_length()); + for (int i = 0; i < num; ++i) + { + // read hits the disk and will block. Progress should + // be updated in between reads + st->read(&buf[0], i, 0, t.piece_size(i)); + if (st->error()) + { + ec = st->error(); + return; + } + hasher h(&buf[0], t.piece_size(i)); + t.set_hash(i, h.final()); + f(i); + } + } +#endif + + void set_piece_hashes(create_torrent& t, std::string const& p + , boost::function f, error_code& ec) + { + file_pool fp; +#if TORRENT_USE_UNC_PATHS + std::string path = canonicalize_path(p); +#else + std::string const& path = p; +#endif + + if (t.files().num_files() == 0) + { + ec = error_code(errors::no_files_in_torrent, get_libtorrent_category()); + return; + } + + boost::scoped_ptr st( + default_storage_constructor(const_cast(t.files()), 0, path, fp + , std::vector())); + + // if we're calculating file hashes as well, use this hasher + hasher filehash; + int file_idx = 0; + size_type left_in_file = t.files().at(0).size; + + // calculate the hash for all pieces + int num = t.num_pieces(); + piece_holder buf(t.piece_length()); + for (int i = 0; i < num; ++i) + { + // read hits the disk and will block. Progress should + // be updated in between reads + st->read(buf.bytes(), i, 0, t.piece_size(i)); + if (st->error()) + { + ec = st->error(); + return; + } + + if (t.should_add_file_hashes()) + { + int left_in_piece = t.piece_size(i); + int this_piece_size = left_in_piece; + // the number of bytes from this file we just read + while (left_in_piece > 0) + { + int to_hash_for_file = int((std::min)(size_type(left_in_piece), left_in_file)); + if (to_hash_for_file > 0) + { + int offset = this_piece_size - left_in_piece; + filehash.update(buf.bytes() + offset, to_hash_for_file); + } + left_in_file -= to_hash_for_file; + left_in_piece -= to_hash_for_file; + if (left_in_file == 0) + { + if (!t.files().at(file_idx).pad_file) + t.set_file_hash(file_idx, filehash.final()); + filehash.reset(); + file_idx++; + if (file_idx >= t.files().num_files()) break; + left_in_file = t.files().at(file_idx).size; + } + } + } + + hasher h(buf.bytes(), t.piece_size(i)); + t.set_hash(i, h.final()); + f(i); + } + } + + create_torrent::~create_torrent() {} + + create_torrent::create_torrent(file_storage& fs, int piece_size + , int pad_file_limit, int flags, int alignment) + : m_files(fs) + , m_creation_date(time(0)) + , m_multifile(fs.num_files() > 1) + , m_private(false) + , m_merkle_torrent((flags & merkle) != 0) + , m_include_mtime((flags & modification_time) != 0) + , m_include_symlinks((flags & symlinks) != 0) + , m_calculate_file_hashes((flags & calculate_file_hashes) != 0) + { + TORRENT_ASSERT(fs.num_files() > 0); + + // return instead of crash in release mode + if (fs.num_files() == 0) return; + + if (!m_multifile && has_parent_path(m_files.file_path(0))) m_multifile = true; + + // a piece_size of 0 means automatic + if (piece_size == 0 && !m_merkle_torrent) + { + const int target_size = 40 * 1024; + piece_size = int(fs.total_size() / (target_size / 20)); + + int i = 16*1024; + for (; i < 2*1024*1024; i *= 2) + { + if (piece_size > i) continue; + break; + } + piece_size = i; + } + else if (piece_size == 0 && m_merkle_torrent) + { + piece_size = 64*1024; + } + + // make sure the size is an even power of 2 +#ifndef NDEBUG + for (int i = 0; i < 32; ++i) + { + if (piece_size & (1 << i)) + { + TORRENT_ASSERT((piece_size & ~(1 << i)) == 0); + break; + } + } +#endif + m_files.set_piece_length(piece_size); + if (flags & optimize) + m_files.optimize(pad_file_limit, alignment); + m_files.set_num_pieces(static_cast( + (m_files.total_size() + m_files.piece_length() - 1) / m_files.piece_length())); + m_piece_hash.resize(m_files.num_pieces()); + } + + create_torrent::create_torrent(torrent_info const& ti) + : m_files(const_cast(ti.files())) + , m_creation_date(time(0)) + , m_multifile(ti.num_files() > 1) + , m_private(ti.priv()) + , m_merkle_torrent(ti.is_merkle_torrent()) + , m_include_mtime(false) + , m_include_symlinks(false) + , m_calculate_file_hashes(false) + { + TORRENT_ASSERT(ti.is_valid()); + if (ti.creation_date()) m_creation_date = *ti.creation_date(); + + if (!ti.creator().empty()) set_creator(ti.creator().c_str()); + if (!ti.comment().empty()) set_comment(ti.comment().c_str()); + + torrent_info::nodes_t const& nodes = ti.nodes(); + for (torrent_info::nodes_t::const_iterator i = nodes.begin() + , end(nodes.end()); i != end; ++i) + add_node(*i); + + std::vector const& trackers = ti.trackers(); + for (std::vector::const_iterator i = trackers.begin() + , end(trackers.end()); i != end; ++i) + add_tracker(i->url, i->tier); + + std::vector const& web_seeds = ti.web_seeds(); + for (std::vector::const_iterator i = web_seeds.begin() + , end(web_seeds.end()); i != end; ++i) + { + if (i->type == web_seed_entry::url_seed) + add_url_seed(i->url); + else if (i->type == web_seed_entry::http_seed) + add_http_seed(i->url); + } + + m_piece_hash.resize(m_files.num_pieces()); + for (int i = 0; i < num_pieces(); ++i) set_hash(i, ti.hash_for_piece(i)); + + m_info_dict = bdecode(&ti.metadata()[0], &ti.metadata()[0] + ti.metadata_size()); + m_info_hash = ti.info_hash(); + } + + entry create_torrent::generate() const + { + TORRENT_ASSERT(m_files.piece_length() > 0); + + entry dict; + + if (m_files.num_files() == 0) + return dict; + + if (!m_urls.empty()) dict["announce"] = m_urls.front().first; + + if (!m_nodes.empty()) + { + entry& nodes = dict["nodes"]; + entry::list_type& nodes_list = nodes.list(); + for (nodes_t::const_iterator i = m_nodes.begin() + , end(m_nodes.end()); i != end; ++i) + { + entry::list_type node; + node.push_back(entry(i->first)); + node.push_back(entry(i->second)); + nodes_list.push_back(entry(node)); + } + } + + if (m_urls.size() > 1) + { + entry trackers(entry::list_t); + entry tier(entry::list_t); + int current_tier = m_urls.front().second; + for (std::vector::const_iterator i = m_urls.begin(); + i != m_urls.end(); ++i) + { + if (i->second != current_tier) + { + current_tier = i->second; + trackers.list().push_back(tier); + tier.list().clear(); + } + tier.list().push_back(entry(i->first)); + } + trackers.list().push_back(tier); + dict["announce-list"] = trackers; + } + + if (!m_comment.empty()) + dict["comment"] = m_comment; + + dict["creation date"] = m_creation_date; + + if (!m_created_by.empty()) + dict["created by"] = m_created_by; + + if (!m_url_seeds.empty()) + { + if (m_url_seeds.size() == 1) + { + dict["url-list"] = m_url_seeds.front(); + } + else + { + entry& list = dict["url-list"]; + for (std::vector::const_iterator i + = m_url_seeds.begin(); i != m_url_seeds.end(); ++i) + { + list.list().push_back(entry(*i)); + } + } + } + + if (!m_http_seeds.empty()) + { + if (m_http_seeds.size() == 1) + { + dict["httpseeds"] = m_http_seeds.front(); + } + else + { + entry& list = dict["httpseeds"]; + for (std::vector::const_iterator i + = m_http_seeds.begin(); i != m_http_seeds.end(); ++i) + { + list.list().push_back(entry(*i)); + } + } + } + + entry& info = dict["info"]; + if (m_info_dict.type() == entry::dictionary_t) + { + info = m_info_dict; + return dict; + } + + info["name"] = m_files.name(); + + if (!m_root_cert.empty()) + info["ssl-cert"] = m_root_cert; + + if (m_private) info["private"] = 1; + + if (!m_multifile) + { + file_entry e = m_files.at(0); + if (m_include_mtime) info["mtime"] = e.mtime; + info["length"] = e.size; + if (e.pad_file + || e.hidden_attribute + || e.executable_attribute + || e.symlink_attribute) + { + std::string& attr = info["attr"].string(); + if (e.pad_file) attr += 'p'; + if (e.hidden_attribute) attr += 'h'; + if (e.executable_attribute) attr += 'x'; + if (m_include_symlinks && e.symlink_attribute) attr += 'l'; + } + if (m_include_symlinks + && e.symlink_attribute) + { + entry& sympath_e = info["symlink path"]; + + std::string split = split_path(e.symlink_path); + for (char const* e = split.c_str(); e != 0; e = next_path_element(e)) + sympath_e.list().push_back(entry(e)); + } + if (!m_filehashes.empty()) + { + info["sha1"] = m_filehashes[0].to_string(); + } + } + else + { + if (!info.find_key("files")) + { + entry& files = info["files"]; + + for (int i = 0; i < m_files.num_files(); ++i) + { + files.list().push_back(entry()); + entry& file_e = files.list().back(); + if (m_include_mtime && m_files.mtime(i)) file_e["mtime"] = m_files.mtime(i); + file_e["length"] = m_files.file_size(i); + entry& path_e = file_e["path"]; + + TORRENT_ASSERT(has_parent_path(m_files.file_path(i))); + + std::string split = split_path(m_files.file_path(i)); + TORRENT_ASSERT(split.c_str() == m_files.name()); + + for (char const* e = next_path_element(split.c_str()); + e != 0; e = next_path_element(e)) + path_e.list().push_back(entry(e)); + + int flags = m_files.file_flags(i); + if (flags != 0) + { + std::string& attr = file_e["attr"].string(); + if (flags & file_storage::flag_pad_file) attr += 'p'; + if (flags & file_storage::flag_hidden) attr += 'h'; + if (flags & file_storage::flag_executable) attr += 'x'; + if (m_include_symlinks && (flags & file_storage::flag_symlink)) attr += 'l'; + } + + if (m_include_symlinks + && (flags & file_storage::flag_symlink)) + { + entry& sympath_e = file_e["symlink path"]; + + std::string split = split_path(m_files.symlink(i)); + for (char const* e = split.c_str(); e != 0; e = next_path_element(e)) + sympath_e.list().push_back(entry(e)); + } + if (!m_filehashes.empty() && m_filehashes[i] != sha1_hash()) + { + file_e["sha1"] = m_filehashes[i].to_string(); + } + } + } + } + + info["piece length"] = m_files.piece_length(); + if (m_merkle_torrent) + { + int num_leafs = merkle_num_leafs(m_files.num_pieces()); + int num_nodes = merkle_num_nodes(num_leafs); + int first_leaf = num_nodes - num_leafs; + m_merkle_tree.resize(num_nodes); + int num_pieces = m_piece_hash.size(); + for (int i = 0; i < num_pieces; ++i) + m_merkle_tree[first_leaf + i] = m_piece_hash[i]; + sha1_hash filler(0); + for (int i = num_pieces; i < num_leafs; ++i) + m_merkle_tree[first_leaf + i] = filler; + + // now that we have initialized all leaves, build + // each level bottom-up + int level_start = first_leaf; + int level_size = num_leafs; + while (level_start > 0) + { + int parent = merkle_get_parent(level_start); + for (int i = level_start; i < level_start + level_size; i += 2, ++parent) + { + hasher h; + h.update((char const*)&m_merkle_tree[i][0], 20); + h.update((char const*)&m_merkle_tree[i+1][0], 20); + m_merkle_tree[parent] = h.final(); + } + level_start = merkle_get_parent(level_start); + level_size /= 2; + } + TORRENT_ASSERT(level_size == 1); + std::string& p = info["root hash"].string(); + p.assign((char const*)&m_merkle_tree[0][0], 20); + } + else + { + std::string& p = info["pieces"].string(); + + for (std::vector::const_iterator i = m_piece_hash.begin(); + i != m_piece_hash.end(); ++i) + { + p.append((char*)i->begin(), sha1_hash::size); + } + } + + std::vector buf; + bencode(std::back_inserter(buf), info); + m_info_hash = hasher(&buf[0], buf.size()).final(); + + return dict; + + } + + void create_torrent::add_tracker(std::string const& url, int tier) + { + m_urls.push_back(announce_entry(url, tier)); + + std::sort(m_urls.begin(), m_urls.end() + , boost::bind(&announce_entry::second, _1) < boost::bind(&announce_entry::second, _2)); + } + + void create_torrent::set_root_cert(std::string const& cert) + { + m_root_cert = cert; + } + + void create_torrent::set_hash(int index, sha1_hash const& h) + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < (int)m_piece_hash.size()); + m_piece_hash[index] = h; + } + + void create_torrent::set_file_hash(int index, sha1_hash const& h) + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < (int)m_files.num_files()); + if (m_filehashes.empty()) m_filehashes.resize(m_files.num_files()); + m_filehashes[index] = h; + } + + void create_torrent::add_node(std::pair const& node) + { + m_nodes.push_back(node); + } + + void create_torrent::add_url_seed(std::string const& url) + { + m_url_seeds.push_back(url); + } + + void create_torrent::add_http_seed(std::string const& url) + { + m_http_seeds.push_back(url); + } + + void create_torrent::set_comment(char const* str) + { + if (str == 0) m_comment.clear(); + else m_comment = str; + } + + void create_torrent::set_creator(char const* str) + { + if (str == 0) m_created_by.clear(); + else m_created_by = str; + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/disk_buffer_holder.cpp b/apps/Launcher/ext/libtorrent/src/disk_buffer_holder.cpp new file mode 100644 index 0000000000..02ac19d511 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/disk_buffer_holder.cpp @@ -0,0 +1,70 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/disk_buffer_holder.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/disk_io_thread.hpp" + +namespace libtorrent +{ + + disk_buffer_holder::disk_buffer_holder(aux::session_impl& ses, char* buf) + : m_disk_pool(ses.m_disk_thread), m_buf(buf) + { + TORRENT_ASSERT(buf == 0 || m_disk_pool.is_disk_buffer(buf)); + } + + disk_buffer_holder::disk_buffer_holder(disk_buffer_pool& iothread, char* buf) + : m_disk_pool(iothread), m_buf(buf) + { + TORRENT_ASSERT(buf == 0 || m_disk_pool.is_disk_buffer(buf)); + } + + void disk_buffer_holder::reset(char* buf) + { + if (m_buf) m_disk_pool.free_buffer(m_buf); + m_buf = buf; + } + + char* disk_buffer_holder::release() + { + char* ret = m_buf; + m_buf = 0; + return ret; + } + + disk_buffer_holder::~disk_buffer_holder() + { + if (m_buf) m_disk_pool.free_buffer(m_buf); + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/disk_buffer_pool.cpp b/apps/Launcher/ext/libtorrent/src/disk_buffer_pool.cpp new file mode 100644 index 0000000000..0f3efb7121 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/disk_buffer_pool.cpp @@ -0,0 +1,242 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/disk_buffer_pool.hpp" +#include "libtorrent/assert.hpp" +#include + +#if TORRENT_USE_MLOCK && !defined TORRENT_WINDOWS +#include +#endif + +#ifdef TORRENT_DISK_STATS +#include "libtorrent/time.hpp" +#endif + +namespace libtorrent +{ + disk_buffer_pool::disk_buffer_pool(int block_size) + : m_block_size(block_size) + , m_in_use(0) +#ifndef TORRENT_DISABLE_POOL_ALLOCATOR + , m_using_pool_allocator(false) + , m_pool(block_size, m_settings.cache_buffer_chunk_size) +#endif + { +#if defined TORRENT_DISK_STATS || defined TORRENT_STATS + m_allocations = 0; +#endif +#ifdef TORRENT_DISK_STATS + m_log.open("disk_buffers.log", std::ios::trunc); + m_categories["read cache"] = 0; + m_categories["write cache"] = 0; + + m_disk_access_log.open("disk_access.log", std::ios::trunc); +#endif +#if TORRENT_USE_ASSERTS + m_magic = 0x1337; +#endif + } + +#if TORRENT_USE_ASSERTS + disk_buffer_pool::~disk_buffer_pool() + { + TORRENT_ASSERT(m_magic == 0x1337); + m_magic = 0; + } +#endif + +#if TORRENT_USE_ASSERTS || defined TORRENT_DISK_STATS + bool disk_buffer_pool::is_disk_buffer(char* buffer + , mutex::scoped_lock& l) const + { + TORRENT_ASSERT(m_magic == 0x1337); +#ifdef TORRENT_DISK_STATS + if (m_buf_to_category.find(buffer) + == m_buf_to_category.end()) return false; +#endif +#ifdef TORRENT_DISABLE_POOL_ALLOCATOR + return true; +#else + if (m_using_pool_allocator) + return m_pool.is_from(buffer); + else + return true; +#endif + } + + bool disk_buffer_pool::is_disk_buffer(char* buffer) const + { + mutex::scoped_lock l(m_pool_mutex); + return is_disk_buffer(buffer, l); + } +#endif + + char* disk_buffer_pool::allocate_buffer(char const* category) + { + mutex::scoped_lock l(m_pool_mutex); + TORRENT_ASSERT(m_magic == 0x1337); + char* ret; +#ifdef TORRENT_DISABLE_POOL_ALLOCATOR + ret = page_aligned_allocator::malloc(m_block_size); +#else + if (m_using_pool_allocator) + { + ret = (char*)m_pool.malloc(); + m_pool.set_next_size(m_settings.cache_buffer_chunk_size); + } + else + { + ret = page_aligned_allocator::malloc(m_block_size); + } +#endif + ++m_in_use; +#if TORRENT_USE_MLOCK + if (m_settings.lock_disk_cache) + { +#ifdef TORRENT_WINDOWS + VirtualLock(ret, m_block_size); +#else + mlock(ret, m_block_size); +#endif + } +#endif + +#if defined TORRENT_DISK_STATS || defined TORRENT_STATS + ++m_allocations; +#endif +#ifdef TORRENT_DISK_STATS + ++m_categories[category]; + m_buf_to_category[ret] = category; + m_log << log_time() << " " << category << ": " << m_categories[category] << "\n"; +#endif + TORRENT_ASSERT(ret == 0 || is_disk_buffer(ret, l)); + return ret; + } + +#ifdef TORRENT_DISK_STATS + void disk_buffer_pool::rename_buffer(char* buf, char const* category) + { + mutex::scoped_lock l(m_pool_mutex); + TORRENT_ASSERT(is_disk_buffer(buf, l)); + TORRENT_ASSERT(m_categories.find(m_buf_to_category[buf]) + != m_categories.end()); + std::string const& prev_category = m_buf_to_category[buf]; + --m_categories[prev_category]; + m_log << log_time() << " " << prev_category << ": " << m_categories[prev_category] << "\n"; + + ++m_categories[category]; + m_buf_to_category[buf] = category; + m_log << log_time() << " " << category << ": " << m_categories[category] << "\n"; + TORRENT_ASSERT(m_categories.find(m_buf_to_category[buf]) + != m_categories.end()); + } +#endif + + void disk_buffer_pool::free_multiple_buffers(char** bufvec, int numbufs) + { + char** end = bufvec + numbufs; + // sort the pointers in order to maximize cache hits + std::sort(bufvec, end); + + mutex::scoped_lock l(m_pool_mutex); + for (; bufvec != end; ++bufvec) + { + char* buf = *bufvec; + TORRENT_ASSERT(buf); + free_buffer_impl(buf, l);; + } + } + + void disk_buffer_pool::free_buffer(char* buf) + { + mutex::scoped_lock l(m_pool_mutex); + free_buffer_impl(buf, l); + } + + void disk_buffer_pool::free_buffer_impl(char* buf, mutex::scoped_lock& l) + { + TORRENT_ASSERT(buf); + TORRENT_ASSERT(m_magic == 0x1337); + TORRENT_ASSERT(is_disk_buffer(buf, l)); +#if defined TORRENT_DISK_STATS || defined TORRENT_STATS + --m_allocations; +#endif +#ifdef TORRENT_DISK_STATS + TORRENT_ASSERT(m_categories.find(m_buf_to_category[buf]) + != m_categories.end()); + std::string const& category = m_buf_to_category[buf]; + --m_categories[category]; + m_log << log_time() << " " << category << ": " << m_categories[category] << "\n"; + m_buf_to_category.erase(buf); +#endif +#if TORRENT_USE_MLOCK + if (m_settings.lock_disk_cache) + { +#ifdef TORRENT_WINDOWS + VirtualUnlock(buf, m_block_size); +#else + munlock(buf, m_block_size); +#endif + } +#endif +#ifdef TORRENT_DISABLE_POOL_ALLOCATOR + page_aligned_allocator::free(buf); +#else + if (m_using_pool_allocator) + m_pool.free(buf); + else + page_aligned_allocator::free(buf); +#endif + --m_in_use; + +#ifndef TORRENT_DISABLE_POOL_ALLOCATOR + // should we switch which allocator to use? + if (m_in_use == 0 && m_settings.use_disk_cache_pool != m_using_pool_allocator) + { + m_pool.release_memory(); + m_using_pool_allocator = m_settings.use_disk_cache_pool; + } +#endif + } + + void disk_buffer_pool::release_memory() + { + TORRENT_ASSERT(m_magic == 0x1337); +#ifndef TORRENT_DISABLE_POOL_ALLOCATOR + mutex::scoped_lock l(m_pool_mutex); + if (m_using_pool_allocator) + m_pool.release_memory(); +#endif + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/disk_io_thread.cpp b/apps/Launcher/ext/libtorrent/src/disk_io_thread.cpp new file mode 100644 index 0000000000..6120a8a1fa --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/disk_io_thread.cpp @@ -0,0 +1,2537 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +/* + Disk queue elevator patch by Morten Husveit +*/ + +#include "libtorrent/storage.hpp" +#include "libtorrent/disk_io_thread.hpp" +#include "libtorrent/disk_buffer_holder.hpp" +#include "libtorrent/alloca.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/error.hpp" +#include "libtorrent/file_pool.hpp" +#include +#include + +#include "libtorrent/time.hpp" + +#if TORRENT_USE_MLOCK && !defined TORRENT_WINDOWS +#include +#endif + +#ifdef TORRENT_BSD +#include +#endif + +#if TORRENT_USE_RLIMIT +#include +#endif + +#ifdef TORRENT_LINUX +#include +#endif + +namespace libtorrent +{ + bool should_cancel_on_abort(disk_io_job const& j); + bool is_read_operation(disk_io_job const& j); + bool operation_has_buffer(disk_io_job const& j); + +// ------- disk_io_thread ------ + + disk_io_thread::disk_io_thread(io_service& ios + , boost::function const& queue_callback + , file_pool& fp + , int block_size) + : disk_buffer_pool(block_size) + , m_abort(false) + , m_waiting_to_shutdown(false) + , m_queue_buffer_size(0) + , m_last_file_check(time_now_hires()) + , m_last_stats_flip(time_now()) + , m_physical_ram(0) + , m_exceeded_write_queue(false) + , m_ios(ios) + , m_queue_callback(queue_callback) + , m_work(io_service::work(m_ios)) + , m_file_pool(fp) +#if TORRENT_USE_ASSERTS + , m_magic(0x1337) +#endif + , m_disk_io_thread(boost::bind(&disk_io_thread::thread_fun, this)) + { + // don't do anything in here. Essentially all members + // of this object are owned by the newly created thread. + // initialize stuff in thread_fun(). + } + + disk_io_thread::~disk_io_thread() + { + TORRENT_ASSERT(m_magic == 0x1337); +#if TORRENT_USE_ASSERTS + m_magic = 0xdead; +#endif + TORRENT_ASSERT(m_abort == true); + } + + void disk_io_thread::abort() + { + TORRENT_ASSERT(m_magic == 0x1337); + + mutex::scoped_lock l(m_queue_mutex); + disk_io_job j; + m_waiting_to_shutdown = true; + j.action = disk_io_job::abort_thread; + j.start_time = time_now_hires(); + + TORRENT_ASSERT(l.locked()); + m_jobs.insert(m_jobs.begin(), j); + m_signal.signal(l); + } + + void disk_io_thread::join() + { + TORRENT_ASSERT(m_magic == 0x1337); + + m_disk_io_thread.join(); + mutex::scoped_lock l(m_queue_mutex); + TORRENT_ASSERT(m_abort == true); + m_jobs.clear(); + } + + bool disk_io_thread::can_write() const + { + TORRENT_ASSERT(m_magic == 0x1337); + mutex::scoped_lock l(m_queue_mutex); + return !m_exceeded_write_queue; + } + + void disk_io_thread::flip_stats(ptime now) + { + TORRENT_ASSERT(m_magic == 0x1337); + + // calling mean() will actually reset the accumulators + m_cache_stats.average_queue_time = m_queue_time.mean(); + m_cache_stats.average_read_time = m_read_time.mean(); + m_cache_stats.average_write_time = m_write_time.mean(); + m_cache_stats.average_hash_time = m_hash_time.mean(); + m_cache_stats.average_job_time = m_job_time.mean(); + m_cache_stats.average_sort_time = m_sort_time.mean(); + + m_last_stats_flip = now; + } + + void disk_io_thread::get_cache_info(sha1_hash const& ih, std::vector& ret) const + { + TORRENT_ASSERT(m_magic == 0x1337); + + mutex::scoped_lock l(m_piece_mutex); + ret.clear(); + ret.reserve(m_pieces.size()); + for (cache_t::const_iterator i = m_pieces.begin() + , end(m_pieces.end()); i != end; ++i) + { + torrent_info const& ti = *i->storage->info(); + if (ti.info_hash() != ih) continue; + cached_piece_info info; + info.next_to_hash = i->next_block_to_hash; + info.piece = i->piece; + info.last_use = i->expire; + info.kind = cached_piece_info::write_cache; + int blocks_in_piece = (ti.piece_size(i->piece) + (m_block_size) - 1) / m_block_size; + info.blocks.resize(blocks_in_piece); + for (int b = 0; b < blocks_in_piece; ++b) + if (i->blocks[b].buf) info.blocks[b] = true; + ret.push_back(info); + } + for (cache_t::const_iterator i = m_read_pieces.begin() + , end(m_read_pieces.end()); i != end; ++i) + { + torrent_info const& ti = *i->storage->info(); + if (ti.info_hash() != ih) continue; + cached_piece_info info; + info.next_to_hash = i->next_block_to_hash; + info.piece = i->piece; + info.last_use = i->expire; + info.kind = cached_piece_info::read_cache; + int blocks_in_piece = (ti.piece_size(i->piece) + (m_block_size) - 1) / m_block_size; + info.blocks.resize(blocks_in_piece); + for (int b = 0; b < blocks_in_piece; ++b) + if (i->blocks[b].buf) info.blocks[b] = true; + ret.push_back(info); + } + } + + cache_status disk_io_thread::status() const + { + mutex::scoped_lock l(m_piece_mutex); + m_cache_stats.total_used_buffers = in_use(); + m_cache_stats.queued_bytes = m_queue_buffer_size; + + cache_status ret = m_cache_stats; + + ret.job_queue_length = m_jobs.size() + m_sorted_read_jobs.size(); + ret.read_queue_size = m_sorted_read_jobs.size(); + + return ret; + } + + // aborts read operations + void disk_io_thread::stop(boost::intrusive_ptr s) + { + mutex::scoped_lock l(m_queue_mutex); + // read jobs are aborted, write and move jobs are syncronized + for (std::deque::iterator i = m_jobs.begin(); + i != m_jobs.end();) + { + if (i->storage != s) + { + ++i; + continue; + } + if (should_cancel_on_abort(*i)) + { + if (i->action == disk_io_job::write) + { + TORRENT_ASSERT(m_queue_buffer_size >= i->buffer_size); + m_queue_buffer_size -= i->buffer_size; + } + post_callback(*i, -3); + i = m_jobs.erase(i); + continue; + } + ++i; + } + disk_io_job j; + j.action = disk_io_job::abort_torrent; + j.storage = s; + add_job(j, l); + } + + struct update_last_use + { + update_last_use(int exp): expire(exp) {} + void operator()(disk_io_thread::cached_piece_entry& p) + { + TORRENT_ASSERT(p.storage); + p.expire = time_now() + seconds(expire); + } + int expire; + }; + + disk_io_thread::cache_piece_index_t::iterator disk_io_thread::find_cached_piece( + disk_io_thread::cache_t& cache + , disk_io_job const& j, mutex::scoped_lock& l) + { + cache_piece_index_t& idx = cache.get<0>(); + cache_piece_index_t::iterator i + = idx.find(std::pair(j.storage.get(), j.piece)); + TORRENT_ASSERT(i == idx.end() || (i->storage == j.storage && i->piece == j.piece)); + return i; + } + + void disk_io_thread::flush_expired_pieces() + { + ptime now = time_now(); + + mutex::scoped_lock l(m_piece_mutex); + + INVARIANT_CHECK; + // flush write cache + cache_lru_index_t& widx = m_pieces.get<1>(); + cache_lru_index_t::iterator i = widx.begin(); + time_duration cut_off = seconds(m_settings.cache_expiry); + while (i != widx.end() && now - i->expire > cut_off) + { + TORRENT_ASSERT(i->storage); + flush_range(const_cast(*i), 0, INT_MAX, l); + TORRENT_ASSERT(i->num_blocks == 0); + + // we want to keep the piece in here to have an accurate + // number for next_block_to_hash, if we're in avoid_readback mode + + bool erase = m_settings.disk_cache_algorithm != session_settings::avoid_readback; + if (!erase) + { + // however, if we've already hashed the whole piece, in-order + // there's no need to keep it around + int piece_size = i->storage->info()->piece_size(i->piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + erase = i->next_block_to_hash == blocks_in_piece; + } + + if (erase) widx.erase(i++); + else ++i; + } + + if (m_settings.explicit_read_cache) return; + + // flush read cache + std::vector bufs; + cache_lru_index_t& ridx = m_read_pieces.get<1>(); + i = ridx.begin(); + while (i != ridx.end() && now - i->expire > cut_off) + { + drain_piece_bufs(const_cast(*i), bufs, l); + ridx.erase(i++); + } + if (!bufs.empty()) free_multiple_buffers(&bufs[0], bufs.size()); + } + + int disk_io_thread::drain_piece_bufs(cached_piece_entry& p, std::vector& buf + , mutex::scoped_lock& l) + { + int piece_size = p.storage->info()->piece_size(p.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + int ret = 0; + + for (int i = 0; i < blocks_in_piece; ++i) + { + if (p.blocks[i].buf == 0) continue; + buf.push_back(p.blocks[i].buf); + ++ret; + p.blocks[i].buf = 0; + --p.num_blocks; + --m_cache_stats.cache_size; + --m_cache_stats.read_cache_size; + } + return ret; + } + + // returns the number of blocks that were freed + int disk_io_thread::free_piece(cached_piece_entry& p, mutex::scoped_lock& l) + { + int piece_size = p.storage->info()->piece_size(p.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + int ret = 0; + + // build a vector of all the buffers we need to free + // and free them all in one go + std::vector buffers; + for (int i = 0; i < blocks_in_piece; ++i) + { + if (p.blocks[i].buf == 0) continue; + buffers.push_back(p.blocks[i].buf); + ++ret; + p.blocks[i].buf = 0; + --p.num_blocks; + --m_cache_stats.cache_size; + --m_cache_stats.read_cache_size; + } + if (!buffers.empty()) free_multiple_buffers(&buffers[0], buffers.size()); + return ret; + } + + // returns the number of blocks that were freed + int disk_io_thread::clear_oldest_read_piece( + int num_blocks, ignore_t ignore, mutex::scoped_lock& l) + { + INVARIANT_CHECK; + + cache_lru_index_t& idx = m_read_pieces.get<1>(); + if (idx.empty()) return 0; + + cache_lru_index_t::iterator i = idx.begin(); + if (i->piece == ignore.piece && i->storage == ignore.storage) + { + ++i; + if (i == idx.end()) return 0; + } + + // don't replace an entry that hasn't expired yet + if (time_now() < i->expire) return 0; + int blocks = 0; + + // build a vector of all the buffers we need to free + // and free them all in one go + std::vector buffers; + if (num_blocks >= i->num_blocks) + { + blocks = drain_piece_bufs(const_cast(*i), buffers, l); + } + else + { + // delete blocks from the start and from the end + // until num_blocks have been freed + int end = (i->storage->info()->piece_size(i->piece) + m_block_size - 1) / m_block_size - 1; + int start = 0; + + while (num_blocks) + { + // if we have a volatile read cache, only clear + // from the end, since we're already clearing + // from the start as blocks are read + if (!m_settings.volatile_read_cache) + { + while (i->blocks[start].buf == 0 && start <= end) ++start; + if (start > end) break; + buffers.push_back(i->blocks[start].buf); + i->blocks[start].buf = 0; + ++blocks; + --const_cast(*i).num_blocks; + --m_cache_stats.cache_size; + --m_cache_stats.read_cache_size; + --num_blocks; + if (!num_blocks) break; + } + + while (i->blocks[end].buf == 0 && start <= end) --end; + if (start > end) break; + buffers.push_back(i->blocks[end].buf); + i->blocks[end].buf = 0; + ++blocks; + --const_cast(*i).num_blocks; + --m_cache_stats.cache_size; + --m_cache_stats.read_cache_size; + --num_blocks; + } + } + if (i->num_blocks == 0) idx.erase(i); + + if (!buffers.empty()) free_multiple_buffers(&buffers[0], buffers.size()); + return blocks; + } + + int contiguous_blocks(disk_io_thread::cached_piece_entry const& b) + { + int ret = 0; + int current = 0; + int blocks_in_piece = (b.storage->info()->piece_size(b.piece) + 16 * 1024 - 1) / (16 * 1024); + for (int i = 0; i < blocks_in_piece; ++i) + { + if (b.blocks[i].buf) ++current; + else + { + if (current > ret) ret = current; + current = 0; + } + } + if (current > ret) ret = current; + return ret; + } + + int disk_io_thread::flush_contiguous_blocks(cached_piece_entry& p + , mutex::scoped_lock& l, int lower_limit, bool avoid_readback) + { + // first find the largest range of contiguous blocks + int len = 0; + int current = 0; + int pos = 0; + int start = 0; + int blocks_in_piece = (p.storage->info()->piece_size(p.piece) + + m_block_size - 1) / m_block_size; + + if (avoid_readback) + { + start = p.next_block_to_hash; + for (int i = p.next_block_to_hash; i < blocks_in_piece; ++i) + { + if (p.blocks[i].buf) ++current; + else break; + } + } + else + { + for (int i = 0; i < blocks_in_piece; ++i) + { + if (p.blocks[i].buf) ++current; + else + { + if (current > len) + { + len = current; + pos = start; + } + current = 0; + start = i + 1; + } + } + } + if (current > len) + { + len = current; + pos = start; + } + + if (len < lower_limit || len <= 0) return 0; + len = flush_range(p, pos, pos + len, l); + return len; + } + + bool cmp_contiguous(disk_io_thread::cached_piece_entry const& lhs + , disk_io_thread::cached_piece_entry const& rhs) + { + return lhs.num_contiguous_blocks < rhs.num_contiguous_blocks; + } + + // flushes 'blocks' blocks from the cache + int disk_io_thread::flush_cache_blocks(mutex::scoped_lock& l + , int blocks, ignore_t ignore, int options) + { + // first look if there are any read cache entries that can + // be cleared + int ret = 0; + int tmp = 0; + do { + tmp = clear_oldest_read_piece(blocks, ignore, l); + blocks -= tmp; + ret += tmp; + } while (tmp > 0 && blocks > 0); + + if (blocks == 0) return ret; + + if (options & dont_flush_write_blocks) return ret; + + // if we don't have any blocks in the cache, no need to go look for any + if (m_cache_stats.cache_size == 0) return ret; + + if (m_settings.disk_cache_algorithm == session_settings::lru) + { + cache_lru_index_t& idx = m_pieces.get<1>(); + while (blocks > 0) + { + cache_lru_index_t::iterator i = idx.begin(); + if (i == idx.end()) return ret; + tmp = flush_range(const_cast(*i), 0, INT_MAX, l); + idx.erase(i); + blocks -= tmp; + ret += tmp; + } + } + else if (m_settings.disk_cache_algorithm == session_settings::largest_contiguous) + { + cache_lru_index_t& idx = m_pieces.get<1>(); + while (blocks > 0) + { + cache_lru_index_t::iterator i = std::max_element(idx.begin(), idx.end(), &cmp_contiguous); + if (i == idx.end()) return ret; + tmp = flush_contiguous_blocks(const_cast(*i), l); + if (i->num_blocks == 0) idx.erase(i); + blocks -= tmp; + ret += tmp; + } + } + else if (m_settings.disk_cache_algorithm == session_settings::avoid_readback) + { + cache_lru_index_t& idx = m_pieces.get<1>(); + for (cache_lru_index_t::iterator i = idx.begin(); i != idx.end();) + { + cached_piece_entry& p = const_cast(*i); + cache_lru_index_t::iterator piece = i; + ++i; + + if (!piece->blocks[p.next_block_to_hash].buf) continue; + int piece_size = p.storage->info()->piece_size(p.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + int start = p.next_block_to_hash; + int end = start + 1; + while (end < blocks_in_piece && p.blocks[end].buf) ++end; + tmp = flush_range(p, start, end, l); + p.num_contiguous_blocks = contiguous_blocks(p); + if (p.num_blocks == 0 && p.next_block_to_hash == blocks_in_piece) + idx.erase(piece); + blocks -= tmp; + ret += tmp; + if (blocks <= 0) break; + } + + // if we still need to flush blocks, flush the largest contiguous blocks + // regardless of if we'll have to read them back later + while (blocks > 0) + { + cache_lru_index_t::iterator i = std::max_element(idx.begin(), idx.end(), &cmp_contiguous); + if (i == idx.end() || i->num_blocks == 0) return ret; + tmp = flush_contiguous_blocks(const_cast(*i), l); + // at this point, we will for sure need a read-back for + // this piece anyway. We might as well save some time looping + // over the disk cache by deleting the entry + if (i->num_blocks == 0) idx.erase(i); + blocks -= tmp; + ret += tmp; + } + } + return ret; + } + + int disk_io_thread::flush_range(cached_piece_entry& p + , int start, int end, mutex::scoped_lock& l) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(start < end); + + int piece_size = p.storage->info()->piece_size(p.piece); +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " flushing " << piece_size << std::endl; +#endif + TORRENT_ASSERT(piece_size > 0); + + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + int buffer_size = 0; + int offset = 0; + + boost::scoped_array buf; + file::iovec_t* iov = 0; + int iov_counter = 0; + if (m_settings.coalesce_writes) buf.reset(new (std::nothrow) char[piece_size]); + else iov = TORRENT_ALLOCA(file::iovec_t, blocks_in_piece); + + end = (std::min)(end, blocks_in_piece); + int num_write_calls = 0; + ptime write_start = time_now_hires(); + for (int i = start; i <= end; ++i) + { + if (i == end || p.blocks[i].buf == 0) + { + if (buffer_size == 0) continue; + + TORRENT_ASSERT(buffer_size <= i * m_block_size); + l.unlock(); + if (iov) + { + int ret = p.storage->write_impl(iov, p.piece, (std::min)( + i * m_block_size, piece_size) - buffer_size, iov_counter); + iov_counter = 0; + if (ret > 0) ++num_write_calls; + } + else + { + TORRENT_ASSERT(buf); + file::iovec_t b = { buf.get(), size_t(buffer_size) }; + int ret = p.storage->write_impl(&b, p.piece, (std::min)( + i * m_block_size, piece_size) - buffer_size, 1); + if (ret > 0) ++num_write_calls; + } + l.lock(); + ++m_cache_stats.writes; +// std::cerr << " flushing p: " << p.piece << " bytes: " << buffer_size << std::endl; + buffer_size = 0; + offset = 0; + continue; + } + int block_size = (std::min)(piece_size - i * m_block_size, m_block_size); + TORRENT_ASSERT(offset + block_size <= piece_size); + TORRENT_ASSERT(offset + block_size > 0); + if (iov) + { + TORRENT_ASSERT(!buf); + iov[iov_counter].iov_base = p.blocks[i].buf; + iov[iov_counter].iov_len = block_size; + ++iov_counter; + } + else + { + TORRENT_ASSERT(buf); + TORRENT_ASSERT(iov == 0); + std::memcpy(buf.get() + offset, p.blocks[i].buf, block_size); + offset += m_block_size; + } + buffer_size += block_size; + TORRENT_ASSERT(p.num_blocks > 0); + --p.num_blocks; + ++m_cache_stats.blocks_written; + --m_cache_stats.cache_size; + if (i == p.next_block_to_hash) ++p.next_block_to_hash; + } + + ptime done = time_now_hires(); + + int ret = 0; + disk_io_job j; + j.storage = p.storage; + j.action = disk_io_job::write; + j.buffer = 0; + j.piece = p.piece; + test_error(j); + std::vector buffers; + for (int i = start; i < end; ++i) + { + if (p.blocks[i].buf == 0) continue; + j.buffer_size = (std::min)(piece_size - i * m_block_size, m_block_size); + int result = j.error ? -1 : j.buffer_size; + j.offset = i * m_block_size; + j.callback = p.blocks[i].callback; + buffers.push_back(p.blocks[i].buf); + post_callback(j, result); + p.blocks[i].callback.clear(); + p.blocks[i].buf = 0; + ++ret; + } + if (!buffers.empty()) free_multiple_buffers(&buffers[0], buffers.size()); + + if (num_write_calls > 0) + { + m_write_time.add_sample(total_microseconds(done - write_start) / num_write_calls); + m_cache_stats.cumulative_write_time += total_milliseconds(done - write_start); + } + if (ret > 0) + p.num_contiguous_blocks = contiguous_blocks(p); + + TORRENT_ASSERT(buffer_size == 0); +// std::cerr << " flushing p: " << p.piece << " cached_blocks: " << m_cache_stats.cache_size << std::endl; +#ifdef TORRENT_DEBUG + for (int i = start; i < end; ++i) + TORRENT_ASSERT(p.blocks[i].buf == 0); +#endif + return ret; + } + + // returns -1 on failure + int disk_io_thread::cache_block(disk_io_job& j + , boost::function& handler + , int cache_expire + , mutex::scoped_lock& l) + { + INVARIANT_CHECK; + TORRENT_ASSERT(find_cached_piece(m_pieces, j, l) == m_pieces.end()); + TORRENT_ASSERT((j.offset & (m_block_size-1)) == 0); + TORRENT_ASSERT(j.cache_min_time >= 0); + cached_piece_entry p; + + int piece_size = j.storage->info()->piece_size(j.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + // there's no point in caching the piece if + // there's only one block in it + if (blocks_in_piece <= 1) return -1; + +#ifdef TORRENT_DISK_STATS + rename_buffer(j.buffer, "write cache"); +#endif + + p.piece = j.piece; + p.storage = j.storage; + p.expire = time_now() + seconds(j.cache_min_time); + p.num_blocks = 1; + p.num_contiguous_blocks = 1; + p.next_block_to_hash = 0; + p.blocks.reset(new (std::nothrow) cached_block_entry[blocks_in_piece]); + if (!p.blocks) return -1; + int block = j.offset / m_block_size; +// std::cerr << " adding cache entry for p: " << j.piece << " block: " << block << " cached_blocks: " << m_cache_stats.cache_size << std::endl; + p.blocks[block].buf = j.buffer; + p.blocks[block].callback.swap(handler); + ++m_cache_stats.cache_size; + cache_lru_index_t& idx = m_pieces.get<1>(); + TORRENT_ASSERT(p.storage); + idx.insert(p); + return 0; + } + + // fills a piece with data from disk, returns the total number of bytes + // read or -1 if there was an error + int disk_io_thread::read_into_piece(cached_piece_entry& p, int start_block + , int options, int num_blocks, mutex::scoped_lock& l) + { + TORRENT_ASSERT(num_blocks > 0); + int piece_size = p.storage->info()->piece_size(p.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + + int end_block = start_block; + int num_read = 0; + + int iov_counter = 0; + file::iovec_t* iov = TORRENT_ALLOCA(file::iovec_t, (std::min)(blocks_in_piece - start_block, num_blocks)); + + int piece_offset = start_block * m_block_size; + + int ret = 0; + + boost::scoped_array buf; + for (int i = start_block; i < blocks_in_piece + && ((options & ignore_cache_size) + || in_use() < m_settings.cache_size); ++i) + { + int block_size = (std::min)(piece_size - piece_offset, m_block_size); + TORRENT_ASSERT(piece_offset <= piece_size); + + // this is a block that is already allocated + // free it and allocate a new one + if (p.blocks[i].buf) + { + free_buffer(p.blocks[i].buf); + --p.num_blocks; + --m_cache_stats.cache_size; + --m_cache_stats.read_cache_size; + } + p.blocks[i].buf = allocate_buffer("read cache"); + + // the allocation failed, break + if (p.blocks[i].buf == 0) + { + free_piece(p, l); + return -1; + } + ++p.num_blocks; + ++m_cache_stats.cache_size; + ++m_cache_stats.read_cache_size; + ++end_block; + ++num_read; + iov[iov_counter].iov_base = p.blocks[i].buf; + iov[iov_counter].iov_len = block_size; + ++iov_counter; + piece_offset += m_block_size; + if (num_read >= num_blocks) break; + } + + if (end_block == start_block) + { + // something failed. Free all buffers + // we just allocated + free_piece(p, l); + return -2; + } + + TORRENT_ASSERT(iov_counter <= (std::min)(blocks_in_piece - start_block, num_blocks)); + + // the buffer_size is the size of the buffer we need to read + // all these blocks. + const int buffer_size = (std::min)((end_block - start_block) * m_block_size + , piece_size - start_block * m_block_size); + TORRENT_ASSERT(buffer_size > 0); + TORRENT_ASSERT(buffer_size <= piece_size); + TORRENT_ASSERT(buffer_size + start_block * m_block_size <= piece_size); + + if (m_settings.coalesce_reads) + buf.reset(new (std::nothrow) char[buffer_size]); + + if (buf) + { + l.unlock(); + file::iovec_t b = { buf.get(), size_t(buffer_size) }; + ret = p.storage->read_impl(&b, p.piece, start_block * m_block_size, 1); + l.lock(); + ++m_cache_stats.reads; + if (p.storage->error()) + { + free_piece(p, l); + return -1; + } + + if (ret != buffer_size) + { + // this means the file wasn't big enough for this read + char msg[70]; + snprintf(msg, sizeof(msg), "reading p: %d b: %d s: %d (read: %d)", p.piece, start_block, buffer_size, ret); + p.storage->get_storage_impl()->set_error(msg, errors::file_too_short); + free_piece(p, l); + return -1; + } + + int offset = 0; + for (int i = 0; i < iov_counter; ++i) + { + TORRENT_ASSERT(iov[i].iov_base); + TORRENT_ASSERT(iov[i].iov_len > 0); + TORRENT_ASSERT(int(offset + iov[i].iov_len) <= buffer_size); + std::memcpy(iov[i].iov_base, buf.get() + offset, iov[i].iov_len); + offset += iov[i].iov_len; + } + } + else + { + l.unlock(); + ret = p.storage->read_impl(iov, p.piece, start_block * m_block_size, iov_counter); + l.lock(); + ++m_cache_stats.reads; + if (p.storage->error()) + { + free_piece(p, l); + return -1; + } + + if (ret != buffer_size) + { + // this means the file wasn't big enough for this read + char msg[70]; + snprintf(msg, sizeof(msg), "reading p: %d b: %d s: %d (read: %d)", p.piece, start_block, buffer_size, ret); + p.storage->get_storage_impl()->set_error(msg, errors::file_too_short); + free_piece(p, l); + return -1; + } + } + + TORRENT_ASSERT(ret == buffer_size); + return ret; + } + + // returns -1 on read error, -2 if there isn't any space in the cache + // or the number of bytes read + int disk_io_thread::cache_read_block(disk_io_job const& j, mutex::scoped_lock& l) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(j.cache_min_time >= 0); + + // this function will create a new cached_piece_entry + // and requires that it doesn't already exist + cache_piece_index_t& idx = m_read_pieces.get<0>(); + TORRENT_ASSERT(find_cached_piece(m_read_pieces, j, l) == idx.end()); + + int piece_size = j.storage->info()->piece_size(j.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + + int start_block = j.offset / m_block_size; + + int blocks_to_read = blocks_in_piece - start_block; + blocks_to_read = (std::min)(blocks_to_read, (std::max)((m_settings.cache_size + + m_cache_stats.read_cache_size - in_use())/2, 3)); + blocks_to_read = (std::min)(blocks_to_read, m_settings.read_cache_line_size); + if (j.max_cache_line > 0) blocks_to_read = (std::min)(blocks_to_read, j.max_cache_line); + + if (in_use() + blocks_to_read > m_settings.cache_size) + { + int clear = in_use() + blocks_to_read - m_settings.cache_size; + if (flush_cache_blocks(l, clear, ignore_t(j.piece, j.storage.get()) + , dont_flush_write_blocks) < clear) + return -2; + } + + cached_piece_entry p; + p.piece = j.piece; + p.storage = j.storage; + p.expire = time_now() + seconds(j.cache_min_time); + p.num_blocks = 0; + p.num_contiguous_blocks = 0; + p.next_block_to_hash = 0; + p.blocks.reset(new (std::nothrow) cached_block_entry[blocks_in_piece]); + if (!p.blocks) return -1; + + int ret = read_into_piece(p, start_block, 0, blocks_to_read, l); + + TORRENT_ASSERT(p.storage); + if (ret >= 0) idx.insert(p); + + return ret; + } + +#if TORRENT_USE_INVARIANT_CHECKS + void disk_io_thread::check_invariant() const + { + int cached_write_blocks = 0; + cache_piece_index_t const& idx = m_pieces.get<0>(); + for (cache_piece_index_t::const_iterator i = idx.begin() + , end(idx.end()); i != end; ++i) + { + cached_piece_entry const& p = *i; + TORRENT_ASSERT(p.blocks); +// TORRENT_ASSERT(p.num_contiguous_blocks == contiguous_blocks(p)); + + TORRENT_ASSERT(p.storage); + int piece_size = p.storage->info()->piece_size(p.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + int blocks = 0; + for (int k = 0; k < blocks_in_piece; ++k) + { + if (p.blocks[k].buf) + { +#if !defined TORRENT_DISABLE_POOL_ALLOCATOR && defined TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_ASSERT(is_disk_buffer(p.blocks[k].buf)); +#endif + ++blocks; + } + } +// TORRENT_ASSERT(blocks == p.num_blocks); + cached_write_blocks += blocks; + } + + int cached_read_blocks = 0; + for (cache_t::const_iterator i = m_read_pieces.begin() + , end(m_read_pieces.end()); i != end; ++i) + { + cached_piece_entry const& p = *i; + TORRENT_ASSERT(p.blocks); + + int piece_size = p.storage->info()->piece_size(p.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + int blocks = 0; + for (int k = 0; k < blocks_in_piece; ++k) + { + if (p.blocks[k].buf) + { +#if !defined TORRENT_DISABLE_POOL_ALLOCATOR && defined TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_ASSERT(is_disk_buffer(p.blocks[k].buf)); +#endif + ++blocks; + } + } +// TORRENT_ASSERT(blocks == p.num_blocks); + cached_read_blocks += blocks; + } + + TORRENT_ASSERT(cached_read_blocks == m_cache_stats.read_cache_size); + TORRENT_ASSERT(cached_read_blocks + cached_write_blocks == m_cache_stats.cache_size); + +#ifdef TORRENT_DISK_STATS + int read_allocs = m_categories.find(std::string("read cache"))->second; + int write_allocs = m_categories.find(std::string("write cache"))->second; + TORRENT_ASSERT(cached_read_blocks == read_allocs); + TORRENT_ASSERT(cached_write_blocks == write_allocs); +#endif + + // when writing, there may be a one block difference, right before an old piece + // is flushed + TORRENT_ASSERT(m_cache_stats.cache_size <= m_settings.cache_size + 1); + } +#endif + + // reads the full piece specified by j into the read cache + // returns the iterator to it and whether or not it already + // was in the cache (hit). + int disk_io_thread::cache_piece(disk_io_job const& j, cache_piece_index_t::iterator& p + , bool& hit, int options, mutex::scoped_lock& l) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(j.cache_min_time >= 0); + + cache_piece_index_t& idx = m_read_pieces.get<0>(); + p = find_cached_piece(m_read_pieces, j, l); + + hit = true; + int ret = 0; + + int piece_size = j.storage->info()->piece_size(j.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + + if (p != m_read_pieces.end() && p->num_blocks != blocks_in_piece) + { + INVARIANT_CHECK; + // we have the piece in the cache, but not all of the blocks + ret = read_into_piece(const_cast(*p), 0 + , options, blocks_in_piece, l); + hit = false; + if (ret < 0) return ret; + idx.modify(p, update_last_use(j.cache_min_time)); + } + else if (p == m_read_pieces.end()) + { + INVARIANT_CHECK; + // if the piece cannot be found in the cache, + // read the whole piece starting at the block + // we got a request for. + + cached_piece_entry pe; + pe.piece = j.piece; + pe.storage = j.storage; + pe.expire = time_now() + seconds(j.cache_min_time); + pe.num_blocks = 0; + pe.num_contiguous_blocks = 0; + pe.next_block_to_hash = 0; + pe.blocks.reset(new (std::nothrow) cached_block_entry[blocks_in_piece]); + if (!pe.blocks) return -1; + ret = read_into_piece(pe, 0, options, INT_MAX, l); + + hit = false; + if (ret < 0) return ret; + TORRENT_ASSERT(pe.storage); + p = idx.insert(pe).first; + } + else + { + idx.modify(p, update_last_use(j.cache_min_time)); + } + TORRENT_ASSERT(!m_read_pieces.empty()); + TORRENT_ASSERT(p->piece == j.piece); + TORRENT_ASSERT(p->storage == j.storage); + return ret; + } + + // cache the entire piece and hash it + int disk_io_thread::read_piece_from_cache_and_hash(disk_io_job const& j, sha1_hash& h) + { + TORRENT_ASSERT(j.buffer); + + TORRENT_ASSERT(j.cache_min_time >= 0); + + mutex::scoped_lock l(m_piece_mutex); + + int piece_size = j.storage->info()->piece_size(j.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + + if (in_use() + blocks_in_piece >= m_settings.cache_size) + { + flush_cache_blocks(l, in_use() - m_settings.cache_size + blocks_in_piece); + } + + cache_piece_index_t::iterator p; + bool hit; + int ret = cache_piece(j, p, hit, ignore_cache_size, l); + if (ret < 0) return ret; + + if (!m_settings.disable_hash_checks) + { + hasher ctx; + + for (int i = 0; i < blocks_in_piece; ++i) + { + TORRENT_ASSERT(p->blocks[i].buf); + ctx.update((char const*)p->blocks[i].buf, (std::min)(piece_size, m_block_size)); + piece_size -= m_block_size; + } + h = ctx.final(); + } + + ret = copy_from_piece(const_cast(*p), hit, j, l); + TORRENT_ASSERT(ret > 0); + if (ret < 0) return ret; + cache_piece_index_t& idx = m_read_pieces.get<0>(); + if (p->num_blocks == 0) idx.erase(p); + else idx.modify(p, update_last_use(j.cache_min_time)); + + // if read cache is disabled or we exceeded the + // limit, remove this piece from the cache + // also, if the piece wasn't in the cache when + // the function was called, and we're using an + // explicit read cache, remove it again + if (in_use() >= m_settings.cache_size + || !m_settings.use_read_cache + || (m_settings.explicit_read_cache && !hit)) + { + TORRENT_ASSERT(!m_read_pieces.empty()); + TORRENT_ASSERT(p->piece == j.piece); + TORRENT_ASSERT(p->storage == j.storage); + if (p != m_read_pieces.end()) + { + free_piece(const_cast(*p), l); + m_read_pieces.erase(p); + } + } + + ret = j.buffer_size; + ++m_cache_stats.blocks_read; + if (hit) ++m_cache_stats.blocks_read_hit; + return ret; + } + + // this doesn't modify the read cache, it only + // checks to see if the given read request can + // be fully satisfied from the given cached piece + // this is similar to copy_from_piece() but it + // doesn't do anything but determining if it's a + // cache hit or not + bool disk_io_thread::is_cache_hit(cached_piece_entry& p + , disk_io_job const& j, mutex::scoped_lock& l) + { + int block = j.offset / m_block_size; + int block_offset = j.offset & (m_block_size-1); + int size = j.buffer_size; + int min_blocks_to_read = block_offset > 0 && (size > m_block_size - block_offset) ? 2 : 1; + TORRENT_ASSERT(size <= m_block_size); + int start_block = block; + // if we have to read more than one block, and + // the first block is there, make sure we test + // for the second block + if (p.blocks[start_block].buf != 0 && min_blocks_to_read > 1) + ++start_block; + +#ifdef TORRENT_DEBUG + int piece_size = j.storage->info()->piece_size(j.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + TORRENT_ASSERT(start_block < blocks_in_piece); +#endif + + return p.blocks[start_block].buf != 0; + } + + int disk_io_thread::copy_from_piece(cached_piece_entry& p, bool& hit + , disk_io_job const& j, mutex::scoped_lock& l) + { + TORRENT_ASSERT(j.buffer); + + // copy from the cache and update the last use timestamp + int block = j.offset / m_block_size; + int block_offset = j.offset & (m_block_size-1); + int buffer_offset = 0; + int size = j.buffer_size; + int min_blocks_to_read = block_offset > 0 && (size > m_block_size - block_offset) ? 2 : 1; + TORRENT_ASSERT(size <= m_block_size); + int start_block = block; + if (p.blocks[start_block].buf != 0 && min_blocks_to_read > 1) + ++start_block; + + int piece_size = j.storage->info()->piece_size(j.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + TORRENT_ASSERT(start_block < blocks_in_piece); + + // if block_offset > 0, we need to read two blocks, and then + // copy parts of both, because it's not aligned to the block + // boundaries + if (p.blocks[start_block].buf == 0) + { + // if we use an explicit read cache, pretend there's no + // space to force hitting disk without caching anything + if (m_settings.explicit_read_cache) return -2; + + int end_block = start_block; + while (end_block < blocks_in_piece && p.blocks[end_block].buf == 0) ++end_block; + + int blocks_to_read = end_block - block; + blocks_to_read = (std::min)(blocks_to_read, (std::max)((m_settings.cache_size + + m_cache_stats.read_cache_size - in_use())/2, 3)); + blocks_to_read = (std::min)(blocks_to_read, m_settings.read_cache_line_size); + blocks_to_read = (std::max)(blocks_to_read, min_blocks_to_read); + if (j.max_cache_line > 0) blocks_to_read = (std::min)(blocks_to_read, j.max_cache_line); + + // if we don't have enough space for the new piece, try flushing something else + if (in_use() + blocks_to_read > m_settings.cache_size) + { + int clear = in_use() + blocks_to_read - m_settings.cache_size; + if (flush_cache_blocks(l, clear, ignore_t(p.piece, p.storage.get()) + , dont_flush_write_blocks) < clear) + return -2; + } + + int ret = read_into_piece(p, block, 0, blocks_to_read, l); + hit = false; + if (ret < 0) return ret; + if (ret < size + block_offset) return -2; + TORRENT_ASSERT(p.blocks[block].buf); + } + + // build a vector of all the buffers we need to free + // and free them all in one go + std::vector buffers; + while (size > 0) + { + TORRENT_ASSERT(p.blocks[block].buf); + int to_copy = (std::min)(m_block_size + - block_offset, size); + std::memcpy(j.buffer + buffer_offset + , p.blocks[block].buf + block_offset + , to_copy); + size -= to_copy; + block_offset = 0; + buffer_offset += to_copy; + if (m_settings.volatile_read_cache) + { + // if volatile read cache is set, the assumption is + // that no other peer is likely to request the same + // piece. Therefore, for each request out of the cache + // we clear the block that was requested and any blocks + // the peer skipped + for (int i = block; i >= 0 && p.blocks[i].buf; --i) + { + buffers.push_back(p.blocks[i].buf); + p.blocks[i].buf = 0; + --p.num_blocks; + --m_cache_stats.cache_size; + --m_cache_stats.read_cache_size; + } + } + ++block; + } + if (!buffers.empty()) free_multiple_buffers(&buffers[0], buffers.size()); + return j.buffer_size; + } + + int disk_io_thread::try_read_from_cache(disk_io_job const& j, bool& hit, int flags) + { + TORRENT_ASSERT(m_magic == 0x1337); + + TORRENT_ASSERT(j.buffer); + TORRENT_ASSERT(j.cache_min_time >= 0); + + mutex::scoped_lock l(m_piece_mutex); + if (!m_settings.use_read_cache) + { + hit = false; + return -2; + } + + cache_piece_index_t& idx = m_read_pieces.get<0>(); + cache_piece_index_t::iterator p + = find_cached_piece(m_read_pieces, j, l); + + hit = true; + int ret = 0; + + // if the piece cannot be found in the cache, + // read the whole piece starting at the block + // we got a request for. + if (p == idx.end()) + { + if (flags & cache_only) return -2; + // if we use an explicit read cache and we + // couldn't find the block in the cache, + // pretend that there's not enough space + // to cache it, to force the read operation + // go go straight to disk + if (m_settings.explicit_read_cache) return -2; + + ret = cache_read_block(j, l); + hit = false; + if (ret < 0) return ret; + + p = find_cached_piece(m_read_pieces, j, l); + TORRENT_ASSERT(!m_read_pieces.empty()); + TORRENT_ASSERT(p->piece == j.piece); + TORRENT_ASSERT(p->storage == j.storage); + } + + TORRENT_ASSERT(p != idx.end()); + + ret = copy_from_piece(const_cast(*p), hit, j, l); + if (ret < 0) return ret; + if (p->num_blocks == 0) idx.erase(p); + else idx.modify(p, update_last_use(j.cache_min_time)); + + ret = j.buffer_size; + ++m_cache_stats.blocks_read; + if (hit) ++m_cache_stats.blocks_read_hit; + return ret; + } + + size_type disk_io_thread::queue_buffer_size() const + { + TORRENT_ASSERT(m_magic == 0x1337); + + mutex::scoped_lock l(m_queue_mutex); + return m_queue_buffer_size; + } + + typedef std::list > job_queue_t; + void completion_queue_handler(job_queue_t* completed_jobs) + { + boost::shared_ptr holder(completed_jobs); + + for (job_queue_t::iterator i = completed_jobs->begin() + , end(completed_jobs->end()); i != end; ++i) + { + TORRENT_TRY + { + i->first.callback(i->second, i->first); + } + TORRENT_CATCH(std::exception& e) + {} + } + } + + int disk_io_thread::add_job(disk_io_job const& j + , mutex::scoped_lock& l + , boost::function const& f) + { + TORRENT_ASSERT(m_magic == 0x1337); + + const_cast(j).start_time = time_now_hires(); + + if (j.action == disk_io_job::write) + { + m_queue_buffer_size += j.buffer_size; + if (m_queue_buffer_size >= m_settings.max_queued_disk_bytes + && m_settings.max_queued_disk_bytes > 0) + m_exceeded_write_queue = true; + } +/* + else if (j.action == disk_io_job::read) + { + // if this is a cache hit, return it right away! + // this is OK because the cache is actually protected by + // the m_piece_mutex + bool hit = false; + if (j.buffer == 0) + { + // this is OK because the disk_buffer pool has its + // own mutex to protect the pool allocator + const_cast(j).buffer = allocate_buffer("send buffer"); + } + int ret = try_read_from_cache(j, hit, cache_only); + if (hit && ret >= 0) + { + TORRENT_ASSERT(f); + const_cast(j).callback.swap( + const_cast&>(f)); + job_queue_t* q = new job_queue_t; + q->push_back(std::make_pair(j, ret)); + m_ios.post(boost::bind(completion_queue_handler, q)); + return m_queue_buffer_size; + } + free_buffer(j.buffer); + const_cast(j).buffer = 0; + } +*/ + TORRENT_ASSERT(l.locked()); + m_jobs.push_back(j); + m_jobs.back().callback.swap(const_cast&>(f)); + + m_signal.signal(l); + return m_queue_buffer_size; + } + + int disk_io_thread::add_job(disk_io_job const& j + , boost::function const& f) + { + TORRENT_ASSERT(m_magic == 0x1337); + TORRENT_ASSERT(!m_abort); + TORRENT_ASSERT(j.storage + || j.action == disk_io_job::abort_thread + || j.action == disk_io_job::update_settings); + TORRENT_ASSERT(j.buffer_size <= m_block_size); + mutex::scoped_lock l(m_queue_mutex); + TORRENT_ASSERT(m_magic == 0x1337); + return add_job(j, l, f); + } + + bool disk_io_thread::test_error(disk_io_job& j) + { + TORRENT_ASSERT(m_magic == 0x1337); + + TORRENT_ASSERT(j.storage); + error_code const& ec = j.storage->error(); + if (ec) + { + j.buffer = 0; + j.str.clear(); + j.error = ec; + j.error_file = j.storage->error_file(); + j.storage->clear_error(); + return true; + } + return false; + } + + void disk_io_thread::post_callback(disk_io_job const& j, int ret) + { + if (!j.callback) return; + m_queued_completions.push_back(std::make_pair(j, ret)); + } + + enum action_flags_t + { + read_operation = 1 + , buffer_operation = 2 + , cancel_on_abort = 4 + }; + + static const boost::uint8_t action_flags[] = + { + read_operation + buffer_operation + cancel_on_abort // read + , buffer_operation // write + , 0 // hash + , 0 // move_storage + , 0 // release_files + , 0 // delete_files + , 0 // check_fastresume + , cancel_on_abort // check_files + , 0 // save_resume_data + , 0 // rename_file + , 0 // abort_thread + , 0 // clear_read_cache + , 0 // abort_torrent + , cancel_on_abort // update_settings + , read_operation + cancel_on_abort // read_and_hash + , read_operation + cancel_on_abort // cache_piece + , 0 // file_priority +#ifndef TORRENT_NO_DEPRECATE + , 0 // finalize_file +#endif + }; + + bool should_cancel_on_abort(disk_io_job const& j) + { + TORRENT_ASSERT(j.action >= 0 && j.action < int(sizeof(action_flags))); + return action_flags[j.action] & cancel_on_abort; + } + + bool is_read_operation(disk_io_job const& j) + { + TORRENT_ASSERT(j.action >= 0 && j.action < int(sizeof(action_flags))); + return action_flags[j.action] & read_operation; + } + + bool operation_has_buffer(disk_io_job const& j) + { + TORRENT_ASSERT(j.action >= 0 && j.action < int(sizeof(action_flags))); + return action_flags[j.action] & buffer_operation; + } + + void disk_io_thread::thread_fun() + { +#ifdef TORRENT_DISK_STATS + m_log.open("disk_io_thread.log", std::ios::trunc); +#endif + + // figure out how much physical RAM there is in + // this machine. This is used for automatically + // sizing the disk cache size when it's set to + // automatic. +#ifdef TORRENT_BSD +#ifdef HW_MEMSIZE + int mib[2] = { CTL_HW, HW_MEMSIZE }; +#else + // not entirely sure this sysctl supports 64 + // bit return values, but it's probably better + // than not building + int mib[2] = { CTL_HW, HW_PHYSMEM }; +#endif + size_t len = sizeof(m_physical_ram); + if (sysctl(mib, 2, &m_physical_ram, &len, NULL, 0) != 0) + m_physical_ram = 0; +#elif defined TORRENT_WINDOWS + MEMORYSTATUSEX ms; + ms.dwLength = sizeof(MEMORYSTATUSEX); + if (GlobalMemoryStatusEx(&ms)) + m_physical_ram = ms.ullTotalPhys; + else + m_physical_ram = 0; +#elif defined TORRENT_LINUX + m_physical_ram = sysconf(_SC_PHYS_PAGES); + m_physical_ram *= sysconf(_SC_PAGESIZE); +#elif defined TORRENT_AMIGA + m_physical_ram = AvailMem(MEMF_PUBLIC); +#endif + +#if TORRENT_USE_RLIMIT + if (m_physical_ram > 0) + { + struct rlimit r; + if (getrlimit(RLIMIT_AS, &r) == 0 && r.rlim_cur != RLIM_INFINITY) + { + if (m_physical_ram > r.rlim_cur) + m_physical_ram = r.rlim_cur; + } + } +#endif + // 1 = forward in list, -1 = backwards in list + int elevator_direction = 1; + + read_jobs_t::iterator elevator_job_pos = m_sorted_read_jobs.begin(); + size_type last_elevator_pos = 0; + bool need_update_elevator_pos = false; + int immediate_jobs_in_row = 0; + + for (;;) + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " idle" << std::endl; +#endif + + TORRENT_ASSERT(m_magic == 0x1337); + + mutex::scoped_lock jl(m_queue_mutex); + + if (m_queued_completions.size() >= 30 || (m_jobs.empty() && !m_queued_completions.empty())) + { + job_queue_t* q = new job_queue_t; + q->swap(m_queued_completions); + m_ios.post(boost::bind(completion_queue_handler, q)); + } + + + ptime job_start; + while (m_jobs.empty() && m_sorted_read_jobs.empty() && !m_abort) + { + // if there hasn't been an event in one second + // see if we should flush the cache +// if (!m_signal.timed_wait(jl, boost::posix_time::seconds(1))) +// flush_expired_pieces(); + m_signal.wait(jl); + m_signal.clear(jl); + + job_start = time_now(); + if (job_start >= m_last_stats_flip + seconds(1)) flip_stats(job_start); + } + + if (m_abort && m_jobs.empty()) + { + jl.unlock(); + + mutex::scoped_lock l(m_piece_mutex); + // flush all disk caches + cache_piece_index_t& widx = m_pieces.get<0>(); + for (cache_piece_index_t::iterator i = widx.begin() + , end(widx.end()); i != end; ++i) + flush_range(const_cast(*i), 0, INT_MAX, l); + +#ifdef TORRENT_DISABLE_POOL_ALLOCATOR + // since we're aborting the thread, we don't actually + // need to free all the blocks individually. We can just + // clear the piece list and the memory will be freed when we + // destruct the m_pool. If we're not using a pool, we actually + // have to free everything individually though + cache_piece_index_t& idx = m_read_pieces.get<0>(); + for (cache_piece_index_t::iterator i = idx.begin() + , end(idx.end()); i != end; ++i) + free_piece(const_cast(*i), l); +#endif + + m_pieces.clear(); + m_read_pieces.clear(); + // release the io_service to allow the run() call to return + // we do this once we stop posting new callbacks to it. + m_work.reset(); + + TORRENT_ASSERT(m_magic == 0x1337); + + return; + } + + disk_io_job j; + + ptime now = time_now_hires(); + ptime operation_start = now; + + // make sure we don't starve out the read queue by just issuing + // write jobs constantly, mix in a read job every now and then + // with a configurable ratio + // this rate must increase to every other jobs if the queued + // up read jobs increases too far. + int read_job_every = m_settings.read_job_every; + + int unchoke_limit = m_settings.unchoke_slots_limit; + if (unchoke_limit < 0) unchoke_limit = 100; + + if (int(m_sorted_read_jobs.size()) > unchoke_limit * 2) + { + int range = unchoke_limit; + int exceed = m_sorted_read_jobs.size() - range * 2; + read_job_every = (exceed * 1 + (range - exceed) * read_job_every) / 2; + if (read_job_every < 1) read_job_every = 1; + } + + bool pick_read_job = m_jobs.empty() + || (immediate_jobs_in_row >= read_job_every + && !m_sorted_read_jobs.empty()); + + if (!pick_read_job) + { + // we have a job in the job queue. If it's + // a read operation and we are allowed to + // reorder jobs, sort it into the read job + // list and continue, otherwise just pop it + // and use it later + j = m_jobs.front(); + m_jobs.pop_front(); + if (j.action == disk_io_job::write) + { + TORRENT_ASSERT(m_queue_buffer_size >= j.buffer_size); + m_queue_buffer_size -= j.buffer_size; + + if (m_exceeded_write_queue) + { + int low_watermark = m_settings.max_queued_disk_bytes_low_watermark == 0 + || m_settings.max_queued_disk_bytes_low_watermark >= m_settings.max_queued_disk_bytes + ? size_type(m_settings.max_queued_disk_bytes) * 7 / 8 + : m_settings.max_queued_disk_bytes_low_watermark; + + if (m_queue_buffer_size < low_watermark + || m_settings.max_queued_disk_bytes == 0) + { + m_exceeded_write_queue = false; + // we just dropped below the high watermark of number of bytes + // queued for writing to the disk. Notify the session so that it + // can trigger all the connections waiting for this event + if (m_queue_callback) m_ios.post(m_queue_callback); + } + } + } + + jl.unlock(); + + bool defer = false; + + if (is_read_operation(j)) + { + defer = true; + + // at this point the operation we're looking + // at is a read operation. If this read operation + // can be fully satisfied by the read cache, handle + // it immediately + if (m_settings.use_read_cache) + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " check_cache_hit" << std::endl; +#endif + // unfortunately we need to lock the cache + // if the cache querying function would be + // made asyncronous, this would not be + // necessary anymore + mutex::scoped_lock l(m_piece_mutex); + cache_piece_index_t::iterator p + = find_cached_piece(m_read_pieces, j, l); + + cache_piece_index_t& idx = m_read_pieces.get<0>(); + // if it's a cache hit, process the job immediately + if (p != idx.end() && is_cache_hit(const_cast(*p), j, l)) + defer = false; + } + } + + if (m_settings.use_disk_read_ahead && defer) + { + j.storage->hint_read_impl(j.piece, j.offset, j.buffer_size); + } + + TORRENT_ASSERT(j.offset >= 0); + if (m_settings.allow_reordered_disk_operations && defer) + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " sorting_job" << std::endl; +#endif + ptime sort_start = time_now_hires(); + + size_type phys_off = j.storage->physical_offset(j.piece, j.offset); + need_update_elevator_pos = need_update_elevator_pos || m_sorted_read_jobs.empty(); + m_sorted_read_jobs.insert(std::pair(phys_off, j)); + + ptime now = time_now_hires(); + m_sort_time.add_sample(total_microseconds(now - sort_start)); + m_job_time.add_sample(total_microseconds(now - operation_start)); + m_cache_stats.cumulative_sort_time += total_milliseconds(now - sort_start); + m_cache_stats.cumulative_job_time += total_milliseconds(now - operation_start); + continue; + } + + ++immediate_jobs_in_row; + } + else + { + // the job queue is empty, pick the next read job + // from the sorted job list. So we don't need the + // job queue lock anymore + jl.unlock(); + + immediate_jobs_in_row = 0; + + TORRENT_ASSERT(!m_sorted_read_jobs.empty()); + + // if m_sorted_read_jobs used to be empty, + // we need to update the elevator position + if (need_update_elevator_pos) + { + elevator_job_pos = m_sorted_read_jobs.lower_bound(last_elevator_pos); + need_update_elevator_pos = false; + } + + // if we've reached the end, change the elevator direction + if (elevator_job_pos == m_sorted_read_jobs.end()) + { + elevator_direction = -1; + --elevator_job_pos; + } + TORRENT_ASSERT(!m_sorted_read_jobs.empty()); + + TORRENT_ASSERT(elevator_job_pos != m_sorted_read_jobs.end()); + j = elevator_job_pos->second; + read_jobs_t::iterator to_erase = elevator_job_pos; + + // if we've reached the begining of the sorted list, + // change the elvator direction + if (elevator_job_pos == m_sorted_read_jobs.begin()) + elevator_direction = 1; + + // move the elevator before erasing the job we're processing + // to keep the iterator valid + if (elevator_direction > 0) ++elevator_job_pos; + else --elevator_job_pos; + + TORRENT_ASSERT(to_erase != elevator_job_pos); + last_elevator_pos = to_erase->first; + m_sorted_read_jobs.erase(to_erase); + } + + m_queue_time.add_sample(total_microseconds(now - j.start_time)); + + // if there's a buffer in this job, it will be freed + // when this holder is destructed, unless it has been + // released. + disk_buffer_holder holder(*this + , operation_has_buffer(j) ? j.buffer : 0); + + flush_expired_pieces(); + + int ret = 0; + + TORRENT_ASSERT(j.storage + || j.action == disk_io_job::abort_thread + || j.action == disk_io_job::update_settings); +#ifdef TORRENT_DISK_STATS + ptime start = time_now(); +#endif + + if (j.cache_min_time < 0) + j.cache_min_time = j.cache_min_time == 0 ? m_settings.default_cache_min_age + : (std::max)(m_settings.default_cache_min_age, j.cache_min_time); + + TORRENT_TRY + { + + if (j.storage && j.storage->get_storage_impl()->m_settings == 0) + j.storage->get_storage_impl()->m_settings = &m_settings; + + switch (j.action) + { + case disk_io_job::update_settings: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " update_settings " << std::endl; +#endif + TORRENT_ASSERT(j.buffer); + session_settings const* s = ((session_settings*)j.buffer); + TORRENT_ASSERT(s->cache_size >= -1); + TORRENT_ASSERT(s->cache_expiry > 0); + +#if defined TORRENT_WINDOWS + if (m_settings.low_prio_disk != s->low_prio_disk) + { + m_file_pool.set_low_prio_io(s->low_prio_disk); + // we need to close all files, since the prio + // only takes affect when files are opened + m_file_pool.release(0); + } +#endif + m_settings = *s; + delete s; + + m_file_pool.resize(m_settings.file_pool_size); +#if defined __APPLE__ && defined __MACH__ && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 + setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD + , m_settings.low_prio_disk ? IOPOL_THROTTLE : IOPOL_DEFAULT); +#elif defined IOPRIO_WHO_PROCESS + syscall(ioprio_set, IOPRIO_WHO_PROCESS, getpid(), IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE + , m_settings.get_bool(settings_pack::low_prio_disk) ? 7: 0)); +#endif + if (m_settings.cache_size == -1) + { + // the cache size is set to automatic. Make it + // depend on the amount of physical RAM + // if we don't know how much RAM we have, just set the + // cache size to 16 MiB (1024 blocks) + if (m_physical_ram == 0) + m_settings.cache_size = 1024; + else + m_settings.cache_size = m_physical_ram / 8 / m_block_size; + } + break; + } + case disk_io_job::abort_torrent: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " abort_torrent " << std::endl; +#endif + mutex::scoped_lock jl(m_queue_mutex); + for (std::deque::iterator i = m_jobs.begin(); + i != m_jobs.end();) + { + if (i->storage != j.storage) + { + ++i; + continue; + } + if (should_cancel_on_abort(*i)) + { + if (i->action == disk_io_job::write) + { + TORRENT_ASSERT(m_queue_buffer_size >= i->buffer_size); + m_queue_buffer_size -= i->buffer_size; + } + post_callback(*i, -3); + i = m_jobs.erase(i); + continue; + } + ++i; + } + // now clear all the read jobs + for (read_jobs_t::iterator i = m_sorted_read_jobs.begin(); + i != m_sorted_read_jobs.end();) + { + if (i->second.storage != j.storage) + { + ++i; + continue; + } + post_callback(i->second, -3); + if (elevator_job_pos == i) ++elevator_job_pos; + m_sorted_read_jobs.erase(i++); + } + jl.unlock(); + + mutex::scoped_lock l(m_piece_mutex); + + // build a vector of all the buffers we need to free + // and free them all in one go + std::vector buffers; + for (cache_t::iterator i = m_read_pieces.begin(); + i != m_read_pieces.end();) + { + if (i->storage == j.storage) + { + drain_piece_bufs(const_cast(*i), buffers, l); + i = m_read_pieces.erase(i); + } + else + { + ++i; + } + } + l.unlock(); + if (!buffers.empty()) free_multiple_buffers(&buffers[0], buffers.size()); + release_memory(); + break; + } + case disk_io_job::abort_thread: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " abort_thread " << std::endl; +#endif + // clear all read jobs + mutex::scoped_lock jl(m_queue_mutex); + + for (std::deque::iterator i = m_jobs.begin(); + i != m_jobs.end();) + { + if (should_cancel_on_abort(*i)) + { + if (i->action == disk_io_job::write) + { + TORRENT_ASSERT(m_queue_buffer_size >= i->buffer_size); + m_queue_buffer_size -= i->buffer_size; + } + post_callback(*i, -3); + i = m_jobs.erase(i); + continue; + } + ++i; + } + jl.unlock(); + + for (read_jobs_t::iterator i = m_sorted_read_jobs.begin(); + i != m_sorted_read_jobs.end();) + { + if (i->second.storage != j.storage) + { + ++i; + continue; + } + post_callback(i->second, -3); + if (elevator_job_pos == i) ++elevator_job_pos; + m_sorted_read_jobs.erase(i++); + } + + m_abort = true; + break; + } + case disk_io_job::read_and_hash: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " read_and_hash " << j.buffer_size << std::endl; +#endif + INVARIANT_CHECK; + TORRENT_ASSERT(j.buffer == 0); + j.buffer = allocate_buffer("send buffer"); + TORRENT_ASSERT(j.buffer_size <= m_block_size); + if (j.buffer == 0) + { + ret = -1; +#if BOOST_VERSION == 103500 + j.error = error_code(boost::system::posix_error::not_enough_memory + , get_posix_category()); +#elif BOOST_VERSION > 103500 + j.error = error_code(boost::system::errc::not_enough_memory + , get_posix_category()); +#else + j.error = error::no_memory; +#endif + j.str.clear(); + break; + } + + disk_buffer_holder read_holder(*this, j.buffer); + + // read the entire piece and verify the piece hash + // since we need to check the hash, this function + // will ignore the cache size limit (at least for + // reading and hashing, not for keeping it around) + sha1_hash h; + ret = read_piece_from_cache_and_hash(j, h); + + // -2 means there's no space in the read cache + // or that the read cache is disabled + if (ret == -1) + { + test_error(j); + break; + } + if (!m_settings.disable_hash_checks) + ret = (j.storage->info()->hash_for_piece(j.piece) == h)?ret:-3; + if (ret == -3) + { + j.storage->mark_failed(j.piece); + j.error = errors::failed_hash_check; + j.str.clear(); + j.buffer = 0; + break; + } + + TORRENT_ASSERT(j.buffer == read_holder.get()); + read_holder.release(); +#if TORRENT_DISK_STATS + rename_buffer(j.buffer, "released send buffer"); +#endif + break; + } +#ifndef TORRENT_NO_DEPRECATE + case disk_io_job::finalize_file: + break; +#endif + case disk_io_job::read: + { + if (test_error(j)) + { + ret = -1; + break; + } +#ifdef TORRENT_DISK_STATS + m_log << log_time(); +#endif + INVARIANT_CHECK; + if (j.buffer == 0) j.buffer = allocate_buffer("send buffer"); + TORRENT_ASSERT(j.buffer_size <= m_block_size); + if (j.buffer == 0) + { +#ifdef TORRENT_DISK_STATS + m_log << " read 0" << std::endl; +#endif + ret = -1; +#if BOOST_VERSION == 103500 + j.error = error_code(boost::system::posix_error::not_enough_memory + , get_posix_category()); +#elif BOOST_VERSION > 103500 + j.error = error_code(boost::system::errc::not_enough_memory + , get_posix_category()); +#else + j.error = error::no_memory; +#endif + j.str.clear(); + break; + } + + disk_buffer_holder read_holder(*this, j.buffer); + + bool hit; + ret = try_read_from_cache(j, hit); + +#ifdef TORRENT_DISK_STATS + m_log << (hit?" read-cache-hit ":" read ") << j.buffer_size << std::endl; +#endif + // -2 means there's no space in the read cache + // or that the read cache is disabled + if (ret == -1) + { + j.buffer = 0; + test_error(j); + break; + } + else if (ret == -2) + { + file::iovec_t b = { j.buffer, size_t(j.buffer_size) }; + ret = j.storage->read_impl(&b, j.piece, j.offset, 1); + if (ret < 0) + { + test_error(j); + break; + } + if (ret != j.buffer_size) + { + char msg[70]; + snprintf(msg, sizeof(msg), "reading p: %d o: %d s: %d (read: %d)", j.piece, j.offset, j.buffer_size, ret); + + // this means the file wasn't big enough for this read + j.buffer = 0; + j.error = errors::file_too_short; + j.error_file = msg; + j.str.clear(); + ret = -1; + break; + } + ++m_cache_stats.blocks_read; + hit = false; + } + if (!hit) + { + ptime now = time_now_hires(); + m_read_time.add_sample(total_microseconds(now - operation_start)); + m_cache_stats.cumulative_read_time += total_milliseconds(now - operation_start); + } + TORRENT_ASSERT(j.buffer == read_holder.get()); + read_holder.release(); +#if TORRENT_DISK_STATS + rename_buffer(j.buffer, "released send buffer"); +#endif + break; + } + case disk_io_job::write: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " write " << j.buffer_size << std::endl; +#endif + mutex::scoped_lock l(m_piece_mutex); + INVARIANT_CHECK; + + TORRENT_ASSERT(!j.storage->error()); + TORRENT_ASSERT(j.cache_min_time >= 0); + + if (in_use() >= m_settings.cache_size) + { + flush_cache_blocks(l, in_use() - m_settings.cache_size + 1); + if (test_error(j)) break; + } + TORRENT_ASSERT(!j.storage->error()); + + cache_piece_index_t& idx = m_pieces.get<0>(); + cache_piece_index_t::iterator p = find_cached_piece(m_pieces, j, l); + int block = j.offset / m_block_size; + TORRENT_ASSERT(j.buffer); + TORRENT_ASSERT(j.buffer_size <= m_block_size); + int piece_size = j.storage->info()->piece_size(j.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + if (p != idx.end()) + { + bool recalc_contiguous = false; + TORRENT_ASSERT(p->blocks[block].buf == 0); + if (p->blocks[block].buf) + { + free_buffer(p->blocks[block].buf); + --m_cache_stats.cache_size; + --const_cast(*p).num_blocks; + } + else if ((block > 0 && p->blocks[block-1].buf) + || (block < blocks_in_piece-1 && p->blocks[block+1].buf) + || p->num_blocks == 0) + { + // update the contiguous blocks counter for this piece. Only if it has + // an adjacent block. If it doesn't, we already know it couldn't have + // increased the largest contiguous block span in this piece + recalc_contiguous = true; + } + p->blocks[block].buf = j.buffer; + p->blocks[block].callback.swap(j.callback); +#ifdef TORRENT_DISK_STATS + rename_buffer(j.buffer, "write cache"); +#endif + ++m_cache_stats.cache_size; + ++const_cast(*p).num_blocks; + if (recalc_contiguous) + { + const_cast(*p).num_contiguous_blocks = contiguous_blocks(*p); + } + idx.modify(p, update_last_use(j.cache_min_time)); + // we might just have created a contiguous range + // that meets the requirement to be flushed. try it + // if we're in avoid_readback mode, don't do this. Only flush + // pieces when we need more space in the cache (which will avoid + // flushing blocks out-of-order) or when we issue a hash job, + // wich indicates the piece is completely downloaded + flush_contiguous_blocks(const_cast(*p) + , l, m_settings.write_cache_line_size + , m_settings.disk_cache_algorithm == session_settings::avoid_readback); + + if (p->num_blocks == 0 && p->next_block_to_hash == 0) idx.erase(p); + test_error(j); + TORRENT_ASSERT(!j.storage->error()); + } + else + { + TORRENT_ASSERT(!j.storage->error()); + if (cache_block(j, j.callback, j.cache_min_time, l) < 0) + { + l.unlock(); + ptime start = time_now_hires(); + file::iovec_t iov = {j.buffer, size_t(j.buffer_size) }; + ret = j.storage->write_impl(&iov, j.piece, j.offset, 1); + l.lock(); + if (ret < 0) + { + test_error(j); + break; + } + ptime done = time_now_hires(); + m_write_time.add_sample(total_microseconds(done - start)); + m_cache_stats.cumulative_write_time += total_milliseconds(done - start); + // we successfully wrote the block. Ignore previous errors + j.storage->clear_error(); + break; + } + TORRENT_ASSERT(!j.storage->error()); + } + // we've now inserted the buffer + // in the cache, we should not + // free it at the end + holder.release(); + + if (in_use() > m_settings.cache_size) + { + flush_cache_blocks(l, in_use() - m_settings.cache_size); + test_error(j); + } + TORRENT_ASSERT(!j.storage->error()); + + break; + } + case disk_io_job::cache_piece: + { + mutex::scoped_lock l(m_piece_mutex); + + if (test_error(j)) + { + ret = -1; + break; + } +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " cache " << j.piece << std::endl; +#endif + INVARIANT_CHECK; + TORRENT_ASSERT(j.buffer == 0); + + cache_piece_index_t::iterator p; + bool hit; + ret = cache_piece(j, p, hit, 0, l); + if (ret == -2) ret = -1; + + if (ret < 0) test_error(j); + break; + } + case disk_io_job::hash: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " hash" << std::endl; +#endif + TORRENT_ASSERT(!j.storage->error()); + mutex::scoped_lock l(m_piece_mutex); + INVARIANT_CHECK; + + cache_piece_index_t& idx = m_pieces.get<0>(); + cache_piece_index_t::iterator i = find_cached_piece(m_pieces, j, l); + if (i != idx.end()) + { + TORRENT_ASSERT(i->storage); + ret = flush_range(const_cast(*i), 0, INT_MAX, l); + idx.erase(i); + if (test_error(j)) + { + ret = -1; + j.storage->mark_failed(j.piece); + break; + } + } + l.unlock(); + if (m_settings.disable_hash_checks) + { + ret = 0; + break; + } + + ptime hash_start = time_now_hires(); + + int readback = 0; + sha1_hash h = j.storage->hash_for_piece_impl(j.piece, &readback); + if (test_error(j)) + { + ret = -1; + j.storage->mark_failed(j.piece); + break; + } + + m_cache_stats.total_read_back += readback / m_block_size; + + ret = (j.storage->info()->hash_for_piece(j.piece) == h)?0:-2; + if (ret == -2) j.storage->mark_failed(j.piece); + + ptime done = time_now_hires(); + m_hash_time.add_sample(total_microseconds(done - hash_start)); + m_cache_stats.cumulative_hash_time += total_milliseconds(done - hash_start); + break; + } + case disk_io_job::move_storage: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " move" << std::endl; +#endif + TORRENT_ASSERT(j.buffer == 0); + ret = j.storage->move_storage_impl(j.str, j.piece); + if (ret == piece_manager::file_exist) + { + j.error = error_code(boost::system::errc::file_exists, get_system_category()); + j.error_file = -1; + j.buffer = NULL; + break; + } + if (ret != piece_manager::no_error && ret != piece_manager::need_full_check) + { + test_error(j); + break; + } + j.str = j.storage->save_path(); + break; + } + case disk_io_job::release_files: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " release" << std::endl; +#endif + TORRENT_ASSERT(j.buffer == 0); + + mutex::scoped_lock l(m_piece_mutex); + INVARIANT_CHECK; + + for (cache_t::iterator i = m_pieces.begin(); i != m_pieces.end();) + { + if (i->storage == j.storage) + { + flush_range(const_cast(*i), 0, INT_MAX, l); + i = m_pieces.erase(i); + } + else + { + ++i; + } + } + l.unlock(); + release_memory(); + + ret = j.storage->release_files_impl(); + if (ret != 0) test_error(j); + break; + } + case disk_io_job::clear_read_cache: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " clear-cache" << std::endl; +#endif + TORRENT_ASSERT(j.buffer == 0); + + mutex::scoped_lock l(m_piece_mutex); + INVARIANT_CHECK; + + for (cache_t::iterator i = m_read_pieces.begin(); + i != m_read_pieces.end();) + { + if (i->storage == j.storage) + { + free_piece(const_cast(*i), l); + i = m_read_pieces.erase(i); + } + else + { + ++i; + } + } + l.unlock(); + release_memory(); + ret = 0; + break; + } + case disk_io_job::delete_files: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " delete" << std::endl; +#endif + TORRENT_ASSERT(j.buffer == 0); + + mutex::scoped_lock l(m_piece_mutex); + INVARIANT_CHECK; + + // delete all write cache entries for this storage + cache_piece_index_t& idx = m_pieces.get<0>(); + cache_piece_index_t::iterator start = idx.lower_bound(std::pair(j.storage.get(), 0)); + cache_piece_index_t::iterator end = idx.upper_bound(std::pair(j.storage.get(), INT_MAX)); + + // build a vector of all the buffers we need to free + // and free them all in one go + std::vector buffers; + torrent_info const& ti = *j.storage->info(); + for (cache_piece_index_t::iterator i = start; i != end; ++i) + { + int blocks_in_piece = (ti.piece_size(i->piece) + m_block_size - 1) / m_block_size; + cached_piece_entry& e = const_cast(*i); + for (int j = 0; j < blocks_in_piece; ++j) + { + if (i->blocks[j].buf == 0) continue; + buffers.push_back(i->blocks[j].buf); + i->blocks[j].buf = 0; + --m_cache_stats.cache_size; + TORRENT_ASSERT(e.num_blocks > 0); + --e.num_blocks; + } + TORRENT_ASSERT(i->num_blocks == 0); + } + idx.erase(start, end); + l.unlock(); + if (!buffers.empty()) free_multiple_buffers(&buffers[0], buffers.size()); + release_memory(); + + ret = j.storage->delete_files_impl(); + if (ret != 0) test_error(j); + break; + } + case disk_io_job::check_fastresume: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " check_fastresume" << std::endl; +#endif + lazy_entry const* rd = (lazy_entry const*)j.buffer; + TORRENT_ASSERT(rd != 0); + ret = j.storage->check_fastresume(*rd, j.error); + test_error(j); + break; + } + case disk_io_job::check_files: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " check_files" << std::endl; +#endif + int piece_size = j.storage->info()->piece_length(); + for (int processed = 0; processed < 4 * 1024 * 1024; processed += piece_size) + { + ptime now = time_now_hires(); + TORRENT_ASSERT(now >= m_last_file_check); + // this happens sometimes on windows for some reason + if (now < m_last_file_check) now = m_last_file_check; + +#if BOOST_VERSION > 103600 + if (now - m_last_file_check < milliseconds(m_settings.file_checks_delay_per_block)) + { + int sleep_time = m_settings.file_checks_delay_per_block + * (piece_size / (16 * 1024)) + - total_milliseconds(now - m_last_file_check); + if (sleep_time < 0) sleep_time = 0; + TORRENT_ASSERT(sleep_time < 5 * 1000); + + sleep(sleep_time); + } + m_last_file_check = time_now_hires(); +#endif + + ptime hash_start = time_now_hires(); + if (m_waiting_to_shutdown) break; + + ret = j.storage->check_files(j.piece, j.offset, j.error); + + ptime done = time_now_hires(); + m_hash_time.add_sample(total_microseconds(done - hash_start)); + m_cache_stats.cumulative_hash_time += total_milliseconds(done - hash_start); + + TORRENT_TRY { + TORRENT_ASSERT(j.callback); + if (j.callback && ret == piece_manager::need_full_check) + post_callback(j, ret); + } TORRENT_CATCH(std::exception&) {} + if (ret != piece_manager::need_full_check) break; + } + if (test_error(j)) + { + ret = piece_manager::fatal_disk_error; + break; + } + TORRENT_ASSERT(ret != -2 || j.error); + + // if the check is not done, add it at the end of the job queue + if (ret == piece_manager::need_full_check) + { + // offset needs to be reset to 0 so that the disk + // job sorting can be done correctly + j.offset = 0; + add_job(j, j.callback); + continue; + } + break; + } + case disk_io_job::save_resume_data: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " save_resume_data" << std::endl; +#endif + j.resume_data.reset(new entry(entry::dictionary_t)); + j.storage->write_resume_data(*j.resume_data); + ret = 0; + break; + } + case disk_io_job::rename_file: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " rename_file" << std::endl; +#endif + ret = j.storage->rename_file_impl(j.piece, j.str); + if (ret != 0) + { + test_error(j); + } + break; + } + case disk_io_job::file_priority: + { + std::vector* p + = reinterpret_cast*>(j.buffer); + j.storage->set_file_priority_impl(*p); + delete p; + ret = 0; + break; + } + } + } + TORRENT_CATCH(std::exception& e) + { + TORRENT_DECLARE_DUMMY(std::exception, e); + ret = -1; + TORRENT_TRY { + j.str = e.what(); + } TORRENT_CATCH(std::exception&) {} + } + + TORRENT_ASSERT(!j.storage || !j.storage->error()); + + ptime done = time_now_hires(); + m_job_time.add_sample(total_microseconds(done - operation_start)); + m_cache_stats.cumulative_job_time += total_milliseconds(done - operation_start); + +// if (!j.callback) std::cerr << "DISK THREAD: no callback specified" << std::endl; +// else std::cerr << "DISK THREAD: invoking callback" << std::endl; + TORRENT_TRY { + TORRENT_ASSERT(ret != -2 || j.error + || j.action == disk_io_job::hash); +#if TORRENT_DISK_STATS + if ((j.action == disk_io_job::read || j.action == disk_io_job::read_and_hash) + && j.buffer != 0) + rename_buffer(j.buffer, "posted send buffer"); +#endif + post_callback(j, ret); + } TORRENT_CATCH(std::exception&) { + TORRENT_ASSERT(false); + } + } + TORRENT_ASSERT(false); + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/entry.cpp b/apps/Launcher/ext/libtorrent/src/entry.cpp new file mode 100644 index 0000000000..93a7a30271 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/entry.cpp @@ -0,0 +1,627 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#if TORRENT_USE_IOSTREAM +#include +#endif +#include +#include "libtorrent/entry.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/lazy_entry.hpp" +#include "libtorrent/escape_string.hpp" + +#if defined(_MSC_VER) +#define for if (false) {} else for +#endif + +namespace +{ + template + void call_destructor(T* o) + { + TORRENT_ASSERT(o); + o->~T(); + } +} + +namespace libtorrent +{ + namespace detail + { + TORRENT_EXPORT char const* integer_to_str(char* buf, int size, entry::integer_type val) + { + int sign = 0; + if (val < 0) + { + sign = 1; + val = -val; + } + buf[--size] = '\0'; + if (val == 0) buf[--size] = '0'; + for (; size > sign && val != 0;) + { + buf[--size] = '0' + char(val % 10); + val /= 10; + } + if (sign) buf[--size] = '-'; + return buf + size; + } + } + + entry& entry::operator[](char const* key) + { + dictionary_type::iterator i = dict().find(key); + if (i != dict().end()) return i->second; + dictionary_type::iterator ret = dict().insert( + std::pair(key, entry())).first; + return ret->second; + } + + entry& entry::operator[](std::string const& key) + { + dictionary_type::iterator i = dict().find(key); + if (i != dict().end()) return i->second; + dictionary_type::iterator ret = dict().insert( + std::make_pair(key, entry())).first; + return ret->second; + } + + entry* entry::find_key(char const* key) + { + dictionary_type::iterator i = dict().find(key); + if (i == dict().end()) return 0; + return &i->second; + } + + entry const* entry::find_key(char const* key) const + { + dictionary_type::const_iterator i = dict().find(key); + if (i == dict().end()) return 0; + return &i->second; + } + + entry* entry::find_key(std::string const& key) + { + dictionary_type::iterator i = dict().find(key); + if (i == dict().end()) return 0; + return &i->second; + } + + entry const* entry::find_key(std::string const& key) const + { + dictionary_type::const_iterator i = dict().find(key); + if (i == dict().end()) return 0; + return &i->second; + } + +#ifndef BOOST_NO_EXCEPTIONS + const entry& entry::operator[](char const* key) const + { + dictionary_type::const_iterator i = dict().find(key); + if (i == dict().end()) throw type_error( + (std::string("key not found: ") + key).c_str()); + return i->second; + } + + const entry& entry::operator[](std::string const& key) const + { + return (*this)[key.c_str()]; + } +#endif + + entry::data_type entry::type() const + { +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + return (entry::data_type)m_type; + } + + entry::~entry() { destruct(); } + + void entry::operator=(const entry& e) + { + destruct(); + copy(e); + } + + entry::integer_type& entry::integer() + { + if (m_type == undefined_t) construct(int_t); +#ifndef BOOST_NO_EXCEPTIONS + if (m_type != int_t) throw_type_error(); +#elif defined TORRENT_DEBUG + TORRENT_ASSERT(m_type_queried); +#endif + TORRENT_ASSERT(m_type == int_t); + return *reinterpret_cast(data); + } + + entry::integer_type const& entry::integer() const + { +#ifndef BOOST_NO_EXCEPTIONS + if (m_type != int_t) throw_type_error(); +#elif defined TORRENT_DEBUG + TORRENT_ASSERT(m_type_queried); +#endif + TORRENT_ASSERT(m_type == int_t); + return *reinterpret_cast(data); + } + + entry::string_type& entry::string() + { + if (m_type == undefined_t) construct(string_t); +#ifndef BOOST_NO_EXCEPTIONS + if (m_type != string_t) throw_type_error(); +#elif defined TORRENT_DEBUG + TORRENT_ASSERT(m_type_queried); +#endif + TORRENT_ASSERT(m_type == string_t); + return *reinterpret_cast(data); + } + + entry::string_type const& entry::string() const + { +#ifndef BOOST_NO_EXCEPTIONS + if (m_type != string_t) throw_type_error(); +#elif defined TORRENT_DEBUG + TORRENT_ASSERT(m_type_queried); +#endif + TORRENT_ASSERT(m_type == string_t); + return *reinterpret_cast(data); + } + + entry::list_type& entry::list() + { + if (m_type == undefined_t) construct(list_t); +#ifndef BOOST_NO_EXCEPTIONS + if (m_type != list_t) throw_type_error(); +#elif defined TORRENT_DEBUG + TORRENT_ASSERT(m_type_queried); +#endif + TORRENT_ASSERT(m_type == list_t); + return *reinterpret_cast(data); + } + + entry::list_type const& entry::list() const + { +#ifndef BOOST_NO_EXCEPTIONS + if (m_type != list_t) throw_type_error(); +#elif defined TORRENT_DEBUG + TORRENT_ASSERT(m_type_queried); +#endif + TORRENT_ASSERT(m_type == list_t); + return *reinterpret_cast(data); + } + + entry::dictionary_type& entry::dict() + { + if (m_type == undefined_t) construct(dictionary_t); +#ifndef BOOST_NO_EXCEPTIONS + if (m_type != dictionary_t) throw_type_error(); +#elif defined TORRENT_DEBUG + TORRENT_ASSERT(m_type_queried); +#endif + TORRENT_ASSERT(m_type == dictionary_t); + return *reinterpret_cast(data); + } + + entry::dictionary_type const& entry::dict() const + { +#ifndef BOOST_NO_EXCEPTIONS + if (m_type != dictionary_t) throw_type_error(); +#elif defined TORRENT_DEBUG + TORRENT_ASSERT(m_type_queried); +#endif + TORRENT_ASSERT(m_type == dictionary_t); + return *reinterpret_cast(data); + } + + entry::entry() + : m_type(undefined_t) + { +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + } + + entry::entry(data_type t) + : m_type(undefined_t) + { + construct(t); +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + } + + entry::entry(const entry& e) + : m_type(undefined_t) + { + copy(e); +#ifdef TORRENT_DEBUG + m_type_queried = e.m_type_queried; +#endif + } + + entry::entry(dictionary_type const& v) + : m_type(undefined_t) + { +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + new(data) dictionary_type(v); + m_type = dictionary_t; + } + + entry::entry(string_type const& v) + : m_type(undefined_t) + { +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + new(data) string_type(v); + m_type = string_t; + } + + entry::entry(list_type const& v) + : m_type(undefined_t) + { +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + new(data) list_type(v); + m_type = list_t; + } + + entry::entry(integer_type const& v) + : m_type(undefined_t) + { +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + new(data) integer_type(v); + m_type = int_t; + } + + // convert a lazy_entry into an old skool entry + void entry::operator=(lazy_entry const& e) + { + switch (e.type()) + { + case lazy_entry::string_t: + this->string() = e.string_value(); + break; + case lazy_entry::int_t: + this->integer() = e.int_value(); + break; + case lazy_entry::dict_t: + { + dictionary_type& d = this->dict(); + for (int i = 0; i < e.dict_size(); ++i) + { + std::pair elem = e.dict_at(i); + d[elem.first] = *elem.second; + } + break; + } + case lazy_entry::list_t: + { + list_type& l = this->list(); + for (int i = 0; i < e.list_size(); ++i) + { + l.push_back(entry()); + l.back() = *e.list_at(i); + } + break; + } + case lazy_entry::none_t: + destruct(); + break; + } + } + + void entry::operator=(dictionary_type const& v) + { + destruct(); + new(data) dictionary_type(v); + m_type = dictionary_t; +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + } + + void entry::operator=(string_type const& v) + { + destruct(); + new(data) string_type(v); + m_type = string_t; +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + } + + void entry::operator=(list_type const& v) + { + destruct(); + new(data) list_type(v); + m_type = list_t; +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + } + + void entry::operator=(integer_type const& v) + { + destruct(); + new(data) integer_type(v); + m_type = int_t; +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + } + + bool entry::operator==(entry const& e) const + { + if (m_type != e.m_type) return false; + + switch(m_type) + { + case int_t: + return integer() == e.integer(); + case string_t: + return string() == e.string(); + case list_t: + return list() == e.list(); + case dictionary_t: + return dict() == e.dict(); + default: + TORRENT_ASSERT(m_type == undefined_t); + return true; + } + } + + void entry::construct(data_type t) + { + switch(t) + { + case int_t: + new(data) integer_type; + break; + case string_t: + new(data) string_type; + break; + case list_t: + new(data) list_type; + break; + case dictionary_t: + new (data) dictionary_type; + break; + default: + TORRENT_ASSERT(t == undefined_t); + } + m_type = t; +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + } + + void entry::copy(entry const& e) + { + switch (e.type()) + { + case int_t: + new(data) integer_type(e.integer()); + break; + case string_t: + new(data) string_type(e.string()); + break; + case list_t: + new(data) list_type(e.list()); + break; + case dictionary_t: + new (data) dictionary_type(e.dict()); + break; + default: + TORRENT_ASSERT(e.type() == undefined_t); + } + m_type = e.type(); +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + } + + void entry::destruct() + { + switch(m_type) + { + case int_t: + call_destructor(reinterpret_cast(data)); + break; + case string_t: + call_destructor(reinterpret_cast(data)); + break; + case list_t: + call_destructor(reinterpret_cast(data)); + break; + case dictionary_t: + call_destructor(reinterpret_cast(data)); + break; + default: + TORRENT_ASSERT(m_type == undefined_t); + break; + } + m_type = undefined_t; +#ifdef TORRENT_DEBUG + m_type_queried = false; +#endif + } + + void entry::swap(entry& e) + { + bool clear_this = false; + bool clear_that = false; + + if (m_type == undefined_t && e.m_type == undefined_t) + return; + + if (m_type == undefined_t) + { + construct((data_type)e.m_type); + clear_that = true; + } + + if (e.m_type == undefined_t) + { + e.construct((data_type)m_type); + clear_this = true; + } + + if (m_type == e.m_type) + { + switch (m_type) + { + case int_t: + std::swap(*reinterpret_cast(data) + , *reinterpret_cast(e.data)); + break; + case string_t: + std::swap(*reinterpret_cast(data) + , *reinterpret_cast(e.data)); + break; + case list_t: + std::swap(*reinterpret_cast(data) + , *reinterpret_cast(e.data)); + break; + case dictionary_t: + std::swap(*reinterpret_cast(data) + , *reinterpret_cast(e.data)); + break; + default: + break; + } + + if (clear_this) + destruct(); + + if (clear_that) + e.destruct(); + } + else + { + // currently, only swapping entries of the same type or where one + // of the entries is uninitialized is supported. + TORRENT_ASSERT(false && "not implemented"); + } + } + + std::string entry::to_string() const + { + std::string ret; + to_string_impl(ret, 0); + return ret; + } + + void entry::to_string_impl(std::string& out, int indent) const + { + TORRENT_ASSERT(indent >= 0); + for (int i = 0; i < indent; ++i) out += " "; + switch (m_type) + { + case int_t: + out += libtorrent::to_string(integer()).elems; + out += "\n"; + break; + case string_t: + { + bool binary_string = false; + for (std::string::const_iterator i = string().begin(); i != string().end(); ++i) + { + if (!is_print(static_cast(*i))) + { + binary_string = true; + break; + } + } + if (binary_string) + { + out += to_hex(string()); + out += "\n"; + } + else + { + out += string(); + out += "\n"; + } + } break; + case list_t: + { + out += "list\n"; + for (list_type::const_iterator i = list().begin(); i != list().end(); ++i) + { + i->to_string_impl(out, indent+1); + } + } break; + case dictionary_t: + { + out += "dictionary\n"; + for (dictionary_type::const_iterator i = dict().begin(); i != dict().end(); ++i) + { + bool binary_string = false; + for (std::string::const_iterator k = i->first.begin(); k != i->first.end(); ++k) + { + if (!is_print(static_cast(*k))) + { + binary_string = true; + break; + } + } + for (int j = 0; j < indent+1; ++j) out += " "; + out += "["; + if (binary_string) out += to_hex(i->first); + else out += i->first; + out += "]"; + + if (i->second.type() != entry::string_t + && i->second.type() != entry::int_t) + out += "\n"; + else out += " "; + i->second.to_string_impl(out, indent+2); + } + } break; + default: + out += "\n"; + } + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/enum_net.cpp b/apps/Launcher/ext/libtorrent/src/enum_net.cpp new file mode 100644 index 0000000000..0994a6304f --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/enum_net.cpp @@ -0,0 +1,1066 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" +#include +#include +#include // for wcstombscstombs +#include "libtorrent/enum_net.hpp" +#include "libtorrent/broadcast_socket.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/assert.hpp" +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif + +#if TORRENT_USE_IFCONF +#include +#include +#include +#include +#include +#endif + +#if TORRENT_USE_SYSCTL +#include +#include +#include +#endif + +#if TORRENT_USE_GETIPFORWARDTABLE || TORRENT_USE_GETADAPTERSADDRESSES +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#endif + +#if TORRENT_USE_NETLINK +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#if TORRENT_USE_IFADDRS +#include +#endif + +#if defined(TORRENT_OS2) && !defined(IF_NAMESIZE) +#define IF_NAMESIZE IFNAMSIZ +#endif + +namespace libtorrent { namespace +{ + + address inaddr_to_address(in_addr const* ina, int len = 4) + { + typedef asio::ip::address_v4::bytes_type bytes_t; + bytes_t b; + std::memset(&b[0], 0, b.size()); + if (len > 0) std::memcpy(&b[0], ina, (std::min)(len, int(b.size()))); + return address_v4(b); + } + +#if TORRENT_USE_IPV6 + address inaddr6_to_address(in6_addr const* ina6, int len = 16) + { + typedef asio::ip::address_v6::bytes_type bytes_t; + bytes_t b; + std::memset(&b[0], 0, b.size()); + if (len > 0) std::memcpy(&b[0], ina6, (std::min)(len, int(b.size()))); + return address_v6(b); + } +#endif + + int sockaddr_len(sockaddr const* sin) + { +#if TORRENT_HAS_SALEN + return sin->sa_len; +#else + return sin->sa_family == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6); +#endif + } + + address sockaddr_to_address(sockaddr const* sin, int assume_family = -1) + { + if (sin->sa_family == AF_INET || assume_family == AF_INET) + return inaddr_to_address(&((sockaddr_in const*)sin)->sin_addr + , sockaddr_len(sin) - offsetof(sockaddr, sa_data)); +#if TORRENT_USE_IPV6 + else if (sin->sa_family == AF_INET6 || assume_family == AF_INET6) + return inaddr6_to_address(&((sockaddr_in6 const*)sin)->sin6_addr + , sockaddr_len(sin) - offsetof(sockaddr, sa_data)); +#endif + return address(); + } + +#if TORRENT_USE_NETLINK + + int read_nl_sock(int sock, char *buf, int bufsize, int seq, int pid) + { + nlmsghdr* nl_hdr; + + int msg_len = 0; + + do + { + int read_len = recv(sock, buf, bufsize - msg_len, 0); + if (read_len < 0) return -1; + + nl_hdr = (nlmsghdr*)buf; + + if ((NLMSG_OK(nl_hdr, read_len) == 0) || (nl_hdr->nlmsg_type == NLMSG_ERROR)) + return -1; + + if (nl_hdr->nlmsg_type == NLMSG_DONE) break; + + buf += read_len; + msg_len += read_len; + + if ((nl_hdr->nlmsg_flags & NLM_F_MULTI) == 0) break; + + } while((nl_hdr->nlmsg_seq != seq) || (nl_hdr->nlmsg_pid != pid)); + return msg_len; + } + + bool parse_route(int s, nlmsghdr* nl_hdr, ip_route* rt_info) + { + rtmsg* rt_msg = (rtmsg*)NLMSG_DATA(nl_hdr); + + if((rt_msg->rtm_family != AF_INET && rt_msg->rtm_family != AF_INET6) || (rt_msg->rtm_table != RT_TABLE_MAIN + && rt_msg->rtm_table != RT_TABLE_LOCAL)) + return false; + + int if_index = 0; + int rt_len = RTM_PAYLOAD(nl_hdr); + for (rtattr* rt_attr = (rtattr*)RTM_RTA(rt_msg); + RTA_OK(rt_attr,rt_len); rt_attr = RTA_NEXT(rt_attr,rt_len)) + { + switch(rt_attr->rta_type) + { + case RTA_OIF: + if_index = *(int*)RTA_DATA(rt_attr); + break; + case RTA_GATEWAY: +#if TORRENT_USE_IPV6 + if (rt_msg->rtm_family == AF_INET6) + { + rt_info->gateway = inaddr6_to_address((in6_addr*)RTA_DATA(rt_attr)); + } + else +#endif + { + rt_info->gateway = inaddr_to_address((in_addr*)RTA_DATA(rt_attr)); + } + break; + case RTA_DST: +#if TORRENT_USE_IPV6 + if (rt_msg->rtm_family == AF_INET6) + { + rt_info->destination = inaddr6_to_address((in6_addr*)RTA_DATA(rt_attr)); + } + else +#endif + { + rt_info->destination = inaddr_to_address((in_addr*)RTA_DATA(rt_attr)); + } + break; + } + } + + if_indextoname(if_index, rt_info->name); + ifreq req; + memset(&req, 0, sizeof(req)); + if_indextoname(if_index, req.ifr_name); + ioctl(s, SIOCGIFMTU, &req); + rt_info->mtu = req.ifr_mtu; +// obviously this doesn't work correctly. How do you get the netmask for a route? +// if (ioctl(s, SIOCGIFNETMASK, &req) == 0) { +// rt_info->netmask = sockaddr_to_address(&req.ifr_addr, req.ifr_addr.sa_family); +// } + return true; + } +#endif + +#if TORRENT_USE_SYSCTL +#ifdef TORRENT_OS2 +int _System __libsocket_sysctl(int* mib, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen); +#endif + + bool parse_route(int s, rt_msghdr* rtm, ip_route* rt_info) + { + sockaddr* rti_info[RTAX_MAX]; + sockaddr* sa = (sockaddr*)(rtm + 1); + for (int i = 0; i < RTAX_MAX; ++i) + { + if ((rtm->rtm_addrs & (1 << i)) == 0) + { + rti_info[i] = 0; + continue; + } + rti_info[i] = sa; + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) + + sa = (sockaddr*)((char*)(sa) + ROUNDUP(sa->sa_len)); + +#undef ROUNDUP + } + + sa = rti_info[RTAX_GATEWAY]; + if (sa == 0 + || rti_info[RTAX_DST] == 0 + || rti_info[RTAX_NETMASK] == 0 + || (sa->sa_family != AF_INET +#if TORRENT_USE_IPV6 + && sa->sa_family != AF_INET6 +#endif + )) + return false; + + rt_info->gateway = sockaddr_to_address(rti_info[RTAX_GATEWAY]); + rt_info->destination = sockaddr_to_address(rti_info[RTAX_DST]); + rt_info->netmask = sockaddr_to_address(rti_info[RTAX_NETMASK] + , rt_info->destination.is_v4() ? AF_INET : AF_INET6); + if_indextoname(rtm->rtm_index, rt_info->name); + + ifreq req; + memset(&req, 0, sizeof(req)); + if_indextoname(rtm->rtm_index, req.ifr_name); + if (ioctl(s, SIOCGIFMTU, &req) < 0) return false; + rt_info->mtu = req.ifr_mtu; + + return true; + } +#endif + +#if TORRENT_USE_IFADDRS + bool iface_from_ifaddrs(ifaddrs *ifa, ip_interface &rv, error_code& ec) + { + int family = ifa->ifa_addr->sa_family; + + if (family != AF_INET +#if TORRENT_USE_IPV6 + && family != AF_INET6 +#endif + ) + { + return false; + } + + strncpy(rv.name, ifa->ifa_name, sizeof(rv.name)); + rv.name[sizeof(rv.name)-1] = 0; + + // determine address + rv.interface_address = sockaddr_to_address(ifa->ifa_addr); + // determine netmask + if (ifa->ifa_netmask != NULL) + { + rv.netmask = sockaddr_to_address(ifa->ifa_netmask); + } + return true; + } +#endif + +}} // + +namespace libtorrent +{ + + // return (a1 & mask) == (a2 & mask) + bool match_addr_mask(address const& a1, address const& a2, address const& mask) + { + // all 3 addresses needs to belong to the same family + if (a1.is_v4() != a2.is_v4()) return false; + if (a1.is_v4() != mask.is_v4()) return false; + +#if TORRENT_USE_IPV6 + if (a1.is_v6()) + { + address_v6::bytes_type b1; + address_v6::bytes_type b2; + address_v6::bytes_type m; + b1 = a1.to_v6().to_bytes(); + b2 = a2.to_v6().to_bytes(); + m = mask.to_v6().to_bytes(); + for (int i = 0; i < int(b1.size()); ++i) + { + b1[i] &= m[i]; + b2[i] &= m[i]; + } + return memcmp(&b1[0], &b2[0], b1.size()) == 0; + } +#endif + return (a1.to_v4().to_ulong() & mask.to_v4().to_ulong()) + == (a2.to_v4().to_ulong() & mask.to_v4().to_ulong()); + } + + bool in_local_network(io_service& ios, address const& addr, error_code& ec) + { + std::vector net = enum_net_interfaces(ios, ec); + if (ec) return false; + for (std::vector::iterator i = net.begin() + , end(net.end()); i != end; ++i) + { + if (match_addr_mask(addr, i->interface_address, i->netmask)) + return true; + } + return false; + } + +#if TORRENT_USE_GETIPFORWARDTABLE + address build_netmask(int bits, int family) + { + if (family == AF_INET) + { + typedef asio::ip::address_v4::bytes_type bytes_t; + bytes_t b; + std::memset(&b[0], 0xff, b.size()); + for (int i = sizeof(bytes_t)/8-1; i > 0; --i) + { + if (bits < 8) + { + b[i] <<= bits; + break; + } + b[i] = 0; + bits -= 8; + } + return address_v4(b); + } +#if TORRENT_USE_IPV6 + else if (family == AF_INET6) + { + typedef asio::ip::address_v6::bytes_type bytes_t; + bytes_t b; + std::memset(&b[0], 0xff, b.size()); + for (int i = sizeof(bytes_t)/8-1; i > 0; --i) + { + if (bits < 8) + { + b[i] <<= bits; + break; + } + b[i] = 0; + bits -= 8; + } + return address_v6(b); + } +#endif + else + { + return address(); + } + } +#endif + + std::vector enum_net_interfaces(io_service& ios, error_code& ec) + { + std::vector ret; +#if TORRENT_USE_IFADDRS + int s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) + { + ec = error_code(errno, asio::error::system_category); + return ret; + } + + ifaddrs *ifaddr; + if (getifaddrs(&ifaddr) == -1) + { + ec = error_code(errno, asio::error::system_category); + close(s); + return ret; + } + + for (ifaddrs* ifa = ifaddr; ifa; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr == 0) continue; + if ((ifa->ifa_flags & IFF_UP) == 0) continue; + + int family = ifa->ifa_addr->sa_family; + if (family == AF_INET +#if TORRENT_USE_IPV6 + || family == AF_INET6 +#endif + ) + { + ip_interface iface; + if (iface_from_ifaddrs(ifa, iface, ec)) + { + ifreq req; + memset(&req, 0, sizeof(req)); + // -1 to leave a null terminator + strncpy(req.ifr_name, iface.name, IF_NAMESIZE - 1); + if (ioctl(s, SIOCGIFMTU, &req) < 0) + { + continue; + } + iface.mtu = req.ifr_mtu; + ret.push_back(iface); + } + } + } + close(s); + freeifaddrs(ifaddr); +// MacOS X, BSD and solaris +#elif TORRENT_USE_IFCONF + int s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) + { + ec = error_code(errno, asio::error::system_category); + return ret; + } + ifconf ifc; + // make sure the buffer is aligned to hold ifreq structs + ifreq buf[40]; + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = (char*)buf; + if (ioctl(s, SIOCGIFCONF, &ifc) < 0) + { + ec = error_code(errno, asio::error::system_category); + close(s); + return ret; + } + + char *ifr = (char*)ifc.ifc_req; + int remaining = ifc.ifc_len; + + while (remaining > 0) + { + ifreq const& item = *reinterpret_cast(ifr); + +#ifdef _SIZEOF_ADDR_IFREQ + int current_size = _SIZEOF_ADDR_IFREQ(item); +#elif defined TORRENT_BSD + int current_size = item.ifr_addr.sa_len + IFNAMSIZ; +#else + int current_size = sizeof(ifreq); +#endif + + if (remaining < current_size) break; + + if (item.ifr_addr.sa_family == AF_INET +#if TORRENT_USE_IPV6 + || item.ifr_addr.sa_family == AF_INET6 +#endif + ) + { + ip_interface iface; + iface.interface_address = sockaddr_to_address(&item.ifr_addr); + strcpy(iface.name, item.ifr_name); + + ifreq req; + memset(&req, 0, sizeof(req)); + // -1 to leave a null terminator + strncpy(req.ifr_name, item.ifr_name, IF_NAMESIZE - 1); + if (ioctl(s, SIOCGIFMTU, &req) < 0) + { + ec = error_code(errno, asio::error::system_category); + close(s); + return ret; + } +#ifndef TORRENT_OS2 + iface.mtu = req.ifr_mtu; +#else + iface.mtu = req.ifr_metric; // according to tcp/ip reference +#endif + + memset(&req, 0, sizeof(req)); + strncpy(req.ifr_name, item.ifr_name, IF_NAMESIZE - 1); + if (ioctl(s, SIOCGIFNETMASK, &req) < 0) + { +#if TORRENT_USE_IPV6 + if (iface.interface_address.is_v6()) + { + // this is expected to fail (at least on MacOS X) + iface.netmask = address_v6::any(); + } + else +#endif + { + ec = error_code(errno, asio::error::system_category); + close(s); + return ret; + } + } + else + { + iface.netmask = sockaddr_to_address(&req.ifr_addr, item.ifr_addr.sa_family); + } + ret.push_back(iface); + } + + ifr += current_size; + remaining -= current_size; + } + close(s); + +#elif TORRENT_USE_GETADAPTERSADDRESSES + +#if _WIN32_WINNT >= 0x0501 + // Load Iphlpapi library + HMODULE iphlp = LoadLibraryA("Iphlpapi.dll"); + if (iphlp) + { + // Get GetAdaptersAddresses() pointer + typedef ULONG (WINAPI *GetAdaptersAddresses_t)(ULONG,ULONG,PVOID,PIP_ADAPTER_ADDRESSES,PULONG); + GetAdaptersAddresses_t GetAdaptersAddresses = (GetAdaptersAddresses_t)GetProcAddress( + iphlp, "GetAdaptersAddresses"); + + if (GetAdaptersAddresses) + { + PIP_ADAPTER_ADDRESSES adapter_addresses = 0; + ULONG out_buf_size = 0; + if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER + | GAA_FLAG_SKIP_ANYCAST, NULL, adapter_addresses, &out_buf_size) != ERROR_BUFFER_OVERFLOW) + { + FreeLibrary(iphlp); + ec = asio::error::operation_not_supported; + return std::vector(); + } + + adapter_addresses = (IP_ADAPTER_ADDRESSES*)malloc(out_buf_size); + if (!adapter_addresses) + { + FreeLibrary(iphlp); + ec = asio::error::no_memory; + return std::vector(); + } + + if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER + | GAA_FLAG_SKIP_ANYCAST, NULL, adapter_addresses, &out_buf_size) == NO_ERROR) + { + for (PIP_ADAPTER_ADDRESSES adapter = adapter_addresses; + adapter != 0; adapter = adapter->Next) + { + ip_interface r; + strncpy(r.name, adapter->AdapterName, sizeof(r.name)); + r.name[sizeof(r.name)-1] = 0; + r.mtu = adapter->Mtu; + IP_ADAPTER_UNICAST_ADDRESS* unicast = adapter->FirstUnicastAddress; + while (unicast) + { + r.interface_address = sockaddr_to_address(unicast->Address.lpSockaddr); + + ret.push_back(r); + + unicast = unicast->Next; + } + } + } + + // Free memory + free(adapter_addresses); + FreeLibrary(iphlp); + return ret; + } + FreeLibrary(iphlp); + } +#endif + + SOCKET s = socket(AF_INET, SOCK_DGRAM, 0); + if (s == SOCKET_ERROR) + { + ec = error_code(WSAGetLastError(), asio::error::system_category); + return ret; + } + + INTERFACE_INFO buffer[30]; + DWORD size; + + if (WSAIoctl(s, SIO_GET_INTERFACE_LIST, 0, 0, buffer, + sizeof(buffer), &size, 0, 0) != 0) + { + ec = error_code(WSAGetLastError(), asio::error::system_category); + closesocket(s); + return ret; + } + closesocket(s); + + int n = size / sizeof(INTERFACE_INFO); + + ip_interface iface; + for (int i = 0; i < n; ++i) + { + iface.interface_address = sockaddr_to_address(&buffer[i].iiAddress.Address); + if (iface.interface_address == address_v4::any()) continue; + iface.netmask = sockaddr_to_address(&buffer[i].iiNetmask.Address + , iface.interface_address.is_v4() ? AF_INET : AF_INET6); + iface.name[0] = 0; + iface.mtu = 1500; // how to get the MTU? + ret.push_back(iface); + } + +#else + +#ifdef _MSC_VER +#pragma message ( "THIS OS IS NOT RECOGNIZED, enum_net_interfaces WILL PROBABLY NOT WORK" ) +#else +#warning "THIS OS IS NOT RECOGNIZED, enum_net_interfaces WILL PROBABLY NOT WORK" +#endif + + // make a best guess of the interface we're using and its IP + udp::resolver r(ios); + udp::resolver::iterator i = r.resolve(udp::resolver::query(asio::ip::host_name(ec), "0"), ec); + if (ec) return ret; + ip_interface iface; + for (;i != udp::resolver_iterator(); ++i) + { + iface.interface_address = i->endpoint().address(); + iface.mtu = 1500; + if (iface.interface_address.is_v4()) + iface.netmask = address_v4::netmask(iface.interface_address.to_v4()); + ret.push_back(iface); + } +#endif + return ret; + } + + address get_default_gateway(io_service& ios, error_code& ec) + { + std::vector ret = enum_routes(ios, ec); +#if defined TORRENT_WINDOWS || defined TORRENT_MINGW + std::vector::iterator i = std::find_if(ret.begin(), ret.end() + , boost::bind(&is_loopback, boost::bind(&ip_route::destination, _1))); +#else + std::vector::iterator i = std::find_if(ret.begin(), ret.end() + , boost::bind(&ip_route::destination, _1) == address()); +#endif + if (i == ret.end()) return address(); + return i->gateway; + } + + std::vector enum_routes(io_service& ios, error_code& ec) + { + std::vector ret; + +#if TORRENT_USE_SYSCTL +/* + struct rt_msg + { + rt_msghdr m_rtm; + char buf[512]; + }; + + rt_msg m; + int len = sizeof(rt_msg); + bzero(&m, len); + m.m_rtm.rtm_type = RTM_GET; + m.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY; + m.m_rtm.rtm_version = RTM_VERSION; + m.m_rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; + m.m_rtm.rtm_seq = 0; + m.m_rtm.rtm_msglen = len; + + int s = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); + if (s == -1) + { + ec = error_code(errno, asio::error::system_category); + return std::vector(); + } + + int n = write(s, &m, len); + if (n == -1) + { + ec = error_code(errno, asio::error::system_category); + close(s); + return std::vector(); + } + else if (n != len) + { + ec = asio::error::operation_not_supported; + close(s); + return std::vector(); + } + bzero(&m, len); + + n = read(s, &m, len); + if (n == -1) + { + ec = error_code(errno, asio::error::system_category); + close(s); + return std::vector(); + } + + for (rt_msghdr* ptr = &m.m_rtm; (char*)ptr < ((char*)&m.m_rtm) + n; ptr = (rt_msghdr*)(((char*)ptr) + ptr->rtm_msglen)) + { + std::cout << " rtm_msglen: " << ptr->rtm_msglen << std::endl; + std::cout << " rtm_type: " << ptr->rtm_type << std::endl; + if (ptr->rtm_errno) + { + ec = error_code(ptr->rtm_errno, asio::error::system_category); + return std::vector(); + } + if (m.m_rtm.rtm_flags & RTF_UP == 0 + || m.m_rtm.rtm_flags & RTF_GATEWAY == 0) + { + ec = asio::error::operation_not_supported; + return address_v4::any(); + } + if (ptr->rtm_addrs & RTA_DST == 0 + || ptr->rtm_addrs & RTA_GATEWAY == 0 + || ptr->rtm_addrs & RTA_NETMASK == 0) + { + ec = asio::error::operation_not_supported; + return std::vector(); + } + if (ptr->rtm_msglen > len - ((char*)ptr - ((char*)&m.m_rtm))) + { + ec = asio::error::operation_not_supported; + return std::vector(); + } + int min_len = sizeof(rt_msghdr) + 2 * sizeof(sockaddr_in); + if (m.m_rtm.rtm_msglen < min_len) + { + ec = asio::error::operation_not_supported; + return std::vector(); + } + + ip_route r; + // destination + char* p = m.buf; + sockaddr_in* sin = (sockaddr_in*)p; + r.destination = sockaddr_to_address((sockaddr*)p); + + // gateway + p += sin->sin_len; + sin = (sockaddr_in*)p; + r.gateway = sockaddr_to_address((sockaddr*)p); + + // netmask + p += sin->sin_len; + sin = (sockaddr_in*)p; + r.netmask = sockaddr_to_address((sockaddr*)p); + ret.push_back(r); + } + close(s); +*/ + int mib[6] = { CTL_NET, PF_ROUTE, 0, AF_UNSPEC, NET_RT_DUMP, 0}; + + size_t needed = 0; +#ifdef TORRENT_OS2 + if (__libsocket_sysctl(mib, 6, 0, &needed, 0, 0) < 0) +#else + if (sysctl(mib, 6, 0, &needed, 0, 0) < 0) +#endif + { + ec = error_code(errno, asio::error::system_category); + return std::vector(); + } + + if (needed <= 0) + { + return std::vector(); + } + + boost::scoped_array buf(new (std::nothrow) char[needed]); + if (buf.get() == 0) + { + ec = asio::error::no_memory; + return std::vector(); + } + +#ifdef TORRENT_OS2 + if (__libsocket_sysctl(mib, 6, buf.get(), &needed, 0, 0) < 0) +#else + if (sysctl(mib, 6, buf.get(), &needed, 0, 0) < 0) +#endif + { + ec = error_code(errno, asio::error::system_category); + return std::vector(); + } + + char* end = buf.get() + needed; + + int s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) + { + ec = error_code(errno, asio::error::system_category); + return std::vector(); + } + rt_msghdr* rtm; + for (char* next = buf.get(); next < end; next += rtm->rtm_msglen) + { + rtm = (rt_msghdr*)next; + if (rtm->rtm_version != RTM_VERSION) + continue; + + ip_route r; + if (parse_route(s, rtm, &r)) ret.push_back(r); + } + close(s); + +#elif TORRENT_USE_GETIPFORWARDTABLE +/* + move this to enum_net_interfaces + // Load Iphlpapi library + HMODULE iphlp = LoadLibraryA("Iphlpapi.dll"); + if (!iphlp) + { + ec = asio::error::operation_not_supported; + return std::vector(); + } + + // Get GetAdaptersInfo() pointer + typedef DWORD (WINAPI *GetAdaptersInfo_t)(PIP_ADAPTER_INFO, PULONG); + GetAdaptersInfo_t GetAdaptersInfo = (GetAdaptersInfo_t)GetProcAddress(iphlp, "GetAdaptersInfo"); + if (!GetAdaptersInfo) + { + FreeLibrary(iphlp); + ec = asio::error::operation_not_supported; + return std::vector(); + } + + PIP_ADAPTER_INFO adapter_info = 0; + ULONG out_buf_size = 0; + if (GetAdaptersInfo(adapter_info, &out_buf_size) != ERROR_BUFFER_OVERFLOW) + { + FreeLibrary(iphlp); + ec = asio::error::operation_not_supported; + return std::vector(); + } + + adapter_info = (IP_ADAPTER_INFO*)malloc(out_buf_size); + if (!adapter_info) + { + FreeLibrary(iphlp); + ec = asio::error::no_memory; + return std::vector(); + } + + if (GetAdaptersInfo(adapter_info, &out_buf_size) == NO_ERROR) + { + for (PIP_ADAPTER_INFO adapter = adapter_info; + adapter != 0; adapter = adapter->Next) + { + + ip_route r; + r.destination = address::from_string(adapter->IpAddressList.IpAddress.String, ec); + r.gateway = address::from_string(adapter->GatewayList.IpAddress.String, ec); + r.netmask = address::from_string(adapter->IpAddressList.IpMask.String, ec); + strncpy(r.name, adapter->AdapterName, sizeof(r.name)); + + if (ec) + { + ec = error_code(); + continue; + } + ret.push_back(r); + } + } + + // Free memory + free(adapter_info); + FreeLibrary(iphlp); +*/ + + // Load Iphlpapi library + HMODULE iphlp = LoadLibraryA("Iphlpapi.dll"); + if (!iphlp) + { + ec = asio::error::operation_not_supported; + return std::vector(); + } + + typedef DWORD (WINAPI *GetIfEntry_t)(PMIB_IFROW pIfRow); + GetIfEntry_t GetIfEntry = (GetIfEntry_t)GetProcAddress(iphlp, "GetIfEntry"); + if (!GetIfEntry) + { + ec = asio::error::operation_not_supported; + return std::vector(); + } + +#if _WIN32_WINNT >= 0x0600 + typedef DWORD (WINAPI *GetIpForwardTable2_t)( + ADDRESS_FAMILY, PMIB_IPFORWARD_TABLE2*); + typedef void (WINAPI *FreeMibTable_t)(PVOID Memory); + + GetIpForwardTable2_t GetIpForwardTable2 = (GetIpForwardTable2_t)GetProcAddress( + iphlp, "GetIpForwardTable2"); + FreeMibTable_t FreeMibTable = (FreeMibTable_t)GetProcAddress( + iphlp, "FreeMibTable"); + if (GetIpForwardTable2 && FreeMibTable) + { + MIB_IPFORWARD_TABLE2* routes = NULL; + int res = GetIpForwardTable2(AF_UNSPEC, &routes); + if (res == NO_ERROR) + { + for (int i = 0; i < routes->NumEntries; ++i) + { + ip_route r; + r.gateway = sockaddr_to_address((const sockaddr*)&routes->Table[i].NextHop); + r.destination = sockaddr_to_address( + (const sockaddr*)&routes->Table[i].DestinationPrefix.Prefix); + r.netmask = build_netmask(routes->Table[i].SitePrefixLength + , routes->Table[i].DestinationPrefix.Prefix.si_family); + MIB_IFROW ifentry; + ifentry.dwIndex = routes->Table[i].InterfaceIndex; + if (GetIfEntry(&ifentry) == NO_ERROR) + { + wcstombs(r.name, ifentry.wszName, sizeof(r.name)); + r.mtu = ifentry.dwMtu; + ret.push_back(r); + } + } + } + if (routes) FreeMibTable(routes); + FreeLibrary(iphlp); + return ret; + } +#endif + + // Get GetIpForwardTable() pointer + typedef DWORD (WINAPI *GetIpForwardTable_t)(PMIB_IPFORWARDTABLE pIpForwardTable,PULONG pdwSize,BOOL bOrder); + + GetIpForwardTable_t GetIpForwardTable = (GetIpForwardTable_t)GetProcAddress( + iphlp, "GetIpForwardTable"); + if (!GetIpForwardTable) + { + FreeLibrary(iphlp); + ec = asio::error::operation_not_supported; + return std::vector(); + } + + MIB_IPFORWARDTABLE* routes = NULL; + ULONG out_buf_size = 0; + if (GetIpForwardTable(routes, &out_buf_size, FALSE) != ERROR_INSUFFICIENT_BUFFER) + { + FreeLibrary(iphlp); + ec = asio::error::operation_not_supported; + return std::vector(); + } + + routes = (MIB_IPFORWARDTABLE*)malloc(out_buf_size); + if (!routes) + { + FreeLibrary(iphlp); + ec = asio::error::no_memory; + return std::vector(); + } + + if (GetIpForwardTable(routes, &out_buf_size, FALSE) == NO_ERROR) + { + for (int i = 0; i < routes->dwNumEntries; ++i) + { + ip_route r; + r.destination = inaddr_to_address((in_addr const*)&routes->table[i].dwForwardDest); + r.netmask = inaddr_to_address((in_addr const*)&routes->table[i].dwForwardMask); + r.gateway = inaddr_to_address((in_addr const*)&routes->table[i].dwForwardNextHop); + MIB_IFROW ifentry; + ifentry.dwIndex = routes->table[i].dwForwardIfIndex; + if (GetIfEntry(&ifentry) == NO_ERROR) + { + wcstombs(r.name, ifentry.wszName, sizeof(r.name)); + r.name[sizeof(r.name)-1] = 0; + r.mtu = ifentry.dwMtu; + ret.push_back(r); + } + } + } + + // Free memory + free(routes); + FreeLibrary(iphlp); +#elif TORRENT_USE_NETLINK + enum { BUFSIZE = 8192 }; + + int sock = socket(PF_ROUTE, SOCK_DGRAM, NETLINK_ROUTE); + if (sock < 0) + { + ec = error_code(errno, asio::error::system_category); + return std::vector(); + } + + int seq = 0; + + char msg[BUFSIZE]; + memset(msg, 0, BUFSIZE); + nlmsghdr* nl_msg = (nlmsghdr*)msg; + + nl_msg->nlmsg_len = NLMSG_LENGTH(sizeof(rtmsg)); + nl_msg->nlmsg_type = RTM_GETROUTE; + nl_msg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; + nl_msg->nlmsg_seq = seq++; + nl_msg->nlmsg_pid = getpid(); + + if (send(sock, nl_msg, nl_msg->nlmsg_len, 0) < 0) + { + ec = error_code(errno, asio::error::system_category); + close(sock); + return std::vector(); + } + + int len = read_nl_sock(sock, msg, BUFSIZE, seq, getpid()); + if (len < 0) + { + ec = error_code(errno, asio::error::system_category); + close(sock); + return std::vector(); + } + + int s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) + { + ec = error_code(errno, asio::error::system_category); + return std::vector(); + } + for (; NLMSG_OK(nl_msg, len); nl_msg = NLMSG_NEXT(nl_msg, len)) + { + ip_route r; + if (parse_route(s, nl_msg, &r)) ret.push_back(r); + } + close(s); + close(sock); + +#endif + return ret; + } + +} + + diff --git a/apps/Launcher/ext/libtorrent/src/error_code.cpp b/apps/Launcher/ext/libtorrent/src/error_code.cpp new file mode 100644 index 0000000000..68853a1026 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/error_code.cpp @@ -0,0 +1,355 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include + +#include "libtorrent/config.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/escape_string.hpp" // for to_string() + +namespace libtorrent +{ +#if BOOST_VERSION >= 103500 + + struct libtorrent_error_category : boost::system::error_category + { + virtual const char* name() const BOOST_SYSTEM_NOEXCEPT; + virtual std::string message(int ev) const BOOST_SYSTEM_NOEXCEPT; + virtual boost::system::error_condition default_error_condition(int ev) const BOOST_SYSTEM_NOEXCEPT + { return boost::system::error_condition(ev, *this); } + }; + + const char* libtorrent_error_category::name() const BOOST_SYSTEM_NOEXCEPT + { + return "libtorrent error"; + } + + std::string libtorrent_error_category::message(int ev) const BOOST_SYSTEM_NOEXCEPT + { + static char const* msgs[] = + { + "no error", + "torrent file collides with file from another torrent", + "hash check failed", + "torrent file is not a dictionary", + "missing or invalid 'info' section in torrent file", + "'info' entry is not a dictionary", + "invalid or missing 'piece length' entry in torrent file", + "missing name in torrent file", + "invalid 'name' of torrent (possible exploit attempt)", + "invalid length of torrent", + "failed to parse files from torrent file", + "invalid or missing 'pieces' entry in torrent file", + "incorrect number of piece hashes in torrent file", + "too many pieces in torrent", + "invalid metadata received from swarm", + "invalid bencoding", + "no files in torrent", + "invalid escaped string", + "session is closing", + "torrent already exists in session", + "invalid torrent handle used", + "invalid type requested from entry", + "missing info-hash from URI", + "file too short", + "unsupported URL protocol", + "failed to parse URL", + "peer sent 0 length piece", + "parse failed", + "invalid file format tag", + "missing info-hash", + "mismatching info-hash", + "invalid hostname", + "invalid port", + "port blocked by port-filter", + "expected closing ] for address", + "destructing torrent", + "timed out", + "upload to upload connection", + "uninteresting upload-only peer", + "invalid info-hash", + "torrent paused", + "'have'-message with higher index than the number of pieces", + "bitfield of invalid size", + "too many piece requests while choked", + "invalid piece packet", + "out of memory", + "torrent aborted", + "connected to ourselves", + "invalid piece size", + "timed out: no interest", + "timed out: inactivity", + "timed out: no handshake", + "timed out: no request", + "invalid choke message", + "invalid unchoke message", + "invalid interested message", + "invalid not-interested message", + "invalid request message", + "invalid hash list", + "invalid hash piece message", + "invalid cancel message", + "invalid dht-port message", + "invalid suggest piece message", + "invalid have-all message", + "invalid have-none message", + "invalid reject message", + "invalid allow-fast message", + "invalid extended message", + "invalid message", + "sync hash not found", + "unable to verify encryption constant", + "plaintext mode not provided", + "rc4 mode not provided", + "unsupported encryption mode", + "peer selected unsupported encryption mode", + "invalid encryption pad size", + "invalid encryption handshake", + "incoming encrypted connections disabled", + "incoming regular connections disabled", + "duplicate peer-id", + "torrent removed", + "packet too large", + "", + "HTTP error", + "missing location header", + "invalid redirection", + "redirecting", + "invalid HTTP range", + "missing content-length", + "banned by IP filter", + "too many connections", + "peer banned", + "stopping torrent", + "too many corrupt pieces", + "torrent is not ready to accept peers", + "peer is not properly constructed", + "session is closing", + "optimistic disconnect", + "torrent finished", + "no router found", + "metadata too large", + "invalid metadata request", + "invalid metadata size", + "invalid metadata offset", + "invalid metadata message", + "pex message too large", + "invalid pex message", + "invalid lt_tracker message", + "pex messages sent too frequent (possible attack)", + "torrent has no metadata", + "invalid dont-have message", + "SSL connection required", + "invalid SSL certificate", + "not an SSL torrent", + "", + "", + "", + "", + "", + "", + +// natpmp errors + "unsupported protocol version", + "not authorized to create port map (enable NAT-PMP on your router)", + "network failure", + "out of resources", + "unsupported opcode", + "", + "", + "", + "", + "", + +// fastresume errors + "missing or invalid 'file sizes' entry", + "no files in resume data", + "missing 'slots' and 'pieces' entry", + "mismatching number of files", + "mismatching file size", + "mismatching file timestamp", + "not a dictionary", + "invalid 'blocks per piece' entry", + "missing slots list", + "file has more slots than torrent", + "invalid entry type in slot list", + "invalid piece index in slot list", + "pieces needs to be reordered", + "", + "", + "", + "", + "", + "", + "", + +// HTTP errors + "Invalid HTTP header", + "missing Location header in HTTP redirect", + "failed to decompress HTTP response", + "", + "", + "", + "", + "", + "", + "", + +// i2p errors + "no i2p router is set up", + "", + "", + "", + "", + "", + "", + "", + "", + "", + +// tracker errors + "scrape not available on tracker", + "invalid tracker response", + "invalid peer dictionary entry", + "tracker sent a failure message", + "missing or invalid 'files' entry", + "missing or invalid 'hash' entry", + "missing or invalid 'peers' and 'peers6' entry", + "udp tracker response packet has invalid size", + "invalid transaction id in udp tracker response", + "invalid action field in udp tracker response", +#ifndef TORRENT_NO_DEPRECATE + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + +// bdecode errors + "expected string in bencoded string", + "expected colon in bencoded string", + "unexpected end of file in bencoded string", + "expected value (list, dict, int or string) in bencoded string", + "bencoded nesting depth exceeded", + "bencoded item count limit exceeded", + "integer overflow", +#endif + }; + if (ev < 0 || ev >= int(sizeof(msgs)/sizeof(msgs[0]))) + return "Unknown error"; + return msgs[ev]; + } + + boost::system::error_category& get_libtorrent_category() + { + static libtorrent_error_category libtorrent_category; + return libtorrent_category; + } + + struct TORRENT_EXPORT http_error_category : boost::system::error_category + { + virtual const char* name() const BOOST_SYSTEM_NOEXCEPT + { return "http error"; } + virtual std::string message(int ev) const BOOST_SYSTEM_NOEXCEPT + { + std::string ret; + ret += to_string(ev).elems; + ret += " "; + switch (ev) + { + case errors::cont: ret += "Continue"; break; + case errors::ok: ret += "OK"; break; + case errors::created: ret += "Created"; break; + case errors::accepted: ret += "Accepted"; break; + case errors::no_content: ret += "No Content"; break; + case errors::multiple_choices: ret += "Multiple Choices"; break; + case errors::moved_permanently: ret += "Moved Permanently"; break; + case errors::moved_temporarily: ret += "Moved Temporarily"; break; + case errors::not_modified: ret += "Not Modified"; break; + case errors::bad_request: ret += "Bad Request"; break; + case errors::unauthorized: ret += "Unauthorized"; break; + case errors::forbidden: ret += "Forbidden"; break; + case errors::not_found: ret += "Not Found"; break; + case errors::internal_server_error: ret += "Internal Server Error"; break; + case errors::not_implemented: ret += "Not Implemented"; break; + case errors::bad_gateway: ret += "Bad Gateway"; break; + case errors::service_unavailable: ret += "Service Unavailable"; break; + default: ret += "(unknown HTTP error)"; break; + } + return ret; + } + virtual boost::system::error_condition default_error_condition( + int ev) const BOOST_SYSTEM_NOEXCEPT + { return boost::system::error_condition(ev, *this); } + }; + + boost::system::error_category& get_http_category() + { + static http_error_category http_category; + return http_category; + } +#endif + +#ifndef BOOST_NO_EXCEPTIONS + const char* libtorrent_exception::what() const throw() + { + if (!m_msg) + { + std::string msg = convert_from_native(m_error.message()); + m_msg = allocate_string_copy(msg.c_str()); + } + + return m_msg; + } + + libtorrent_exception::~libtorrent_exception() throw() + { + free(m_msg); + } +#endif + + namespace errors + { + // hidden + boost::system::error_code make_error_code(error_code_enum e) + { + return boost::system::error_code(e, get_libtorrent_category()); + } + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/escape_string.cpp b/apps/Launcher/ext/libtorrent/src/escape_string.cpp new file mode 100644 index 0000000000..e6400314dd --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/escape_string.cpp @@ -0,0 +1,641 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/parse_url.hpp" +#include "libtorrent/random.hpp" + +#ifdef TORRENT_WINDOWS +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#endif + +#include "libtorrent/utf8.hpp" +#include "libtorrent/thread.hpp" + +#if TORRENT_USE_ICONV +#include +#include +#endif + +namespace libtorrent +{ + + // lexical_cast's result depends on the locale. We need + // a well defined result + boost::array::digits10> to_string(size_type n) + { + boost::array::digits10> ret; + char *p = &ret.back(); + *p = '\0'; + unsigned_size_type un = n; + if (n < 0) un = -un; // TODO: warning C4146: unary minus operator applied to unsigned type, result still unsigned + do { + *--p = '0' + un % 10; + un /= 10; + } while (un); + if (n < 0) *--p = '-'; + std::memmove(&ret[0], p, &ret.back() - p + 1); + return ret; + } + + std::string unescape_string(std::string const& s, error_code& ec) + { + std::string ret; + for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) + { + if(*i == '+') + { + ret += ' '; + } + else if (*i != '%') + { + ret += *i; + } + else + { + ++i; + if (i == s.end()) + { + ec = errors::invalid_escaped_string; + return ret; + } + + int high; + if(*i >= '0' && *i <= '9') high = *i - '0'; + else if(*i >= 'A' && *i <= 'F') high = *i + 10 - 'A'; + else if(*i >= 'a' && *i <= 'f') high = *i + 10 - 'a'; + else + { + ec = errors::invalid_escaped_string; + return ret; + } + + ++i; + if (i == s.end()) + { + ec = errors::invalid_escaped_string; + return ret; + } + + int low; + if(*i >= '0' && *i <= '9') low = *i - '0'; + else if(*i >= 'A' && *i <= 'F') low = *i + 10 - 'A'; + else if(*i >= 'a' && *i <= 'f') low = *i + 10 - 'a'; + else + { + ec = errors::invalid_escaped_string; + return ret; + } + + ret += char(high * 16 + low); + } + } + return ret; + } + + // http://www.ietf.org/rfc/rfc2396.txt + // section 2.3 + static const char unreserved_chars[] = + // when determining if a url needs encoding + // % should be ok + "%+" + // reserved + ";?:@=&,$/" + // unreserved (special characters) ' excluded, + // since some buggy trackers fail with those + "-_!.~*()" + // unreserved (alphanumerics) + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789"; + + static const char hex_chars[] = "0123456789abcdef"; + + // the offset is used to ignore the first characters in the unreserved_chars table. + static std::string escape_string_impl(const char* str, int len, int offset) + { + TORRENT_ASSERT(str != 0); + TORRENT_ASSERT(len >= 0); + TORRENT_ASSERT(offset >= 0); + TORRENT_ASSERT(offset < int(sizeof(unreserved_chars))-1); + + std::string ret; + for (int i = 0; i < len; ++i) + { + if (std::strchr(unreserved_chars+offset, *str) && *str != 0) + { + ret += *str; + } + else + { + ret += '%'; + ret += hex_chars[((unsigned char)*str) >> 4]; + ret += hex_chars[((unsigned char)*str) & 15]; + } + ++str; + } + return ret; + } + + std::string escape_string(const char* str, int len) + { + return escape_string_impl(str, len, 11); + } + + std::string escape_path(const char* str, int len) + { + return escape_string_impl(str, len, 10); + } + + bool need_encoding(char const* str, int len) + { + for (int i = 0; i < len; ++i) + { + if (std::strchr(unreserved_chars, *str) == 0 || *str == 0) + return true; + ++str; + } + return false; + } + + void convert_path_to_posix(std::string& path) + { + for (std::string::iterator i = path.begin() + , end(path.end()); i != end; ++i) + if (*i == '\\') *i = '/'; + } + + std::string read_until(char const*& str, char delim, char const* end) + { + TORRENT_ASSERT(str <= end); + + std::string ret; + while (str != end && *str != delim) + { + ret += *str; + ++str; + } + // skip the delimiter as well + while (str != end && *str == delim) ++str; + return ret; + } + + std::string maybe_url_encode(std::string const& url) + { + std::string protocol, host, auth, path; + int port; + error_code ec; + boost::tie(protocol, auth, host, port, path) = parse_url_components(url, ec); + if (ec) return url; + + // first figure out if this url contains unencoded characters + if (!need_encoding(path.c_str(), path.size())) + return url; + + char msg[TORRENT_MAX_PATH*4]; + snprintf(msg, sizeof(msg), "%s://%s%s%s%s%s%s", protocol.c_str(), auth.c_str() + , auth.empty()?"":"@", host.c_str() + , port == -1 ? "" : ":" + , port == -1 ? "" : to_string(port).elems + , escape_path(path.c_str(), path.size()).c_str()); + return msg; + } + + std::string base64encode(const std::string& s) + { + static const char base64_table[] = + { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' + }; + + unsigned char inbuf[3]; + unsigned char outbuf[4]; + + std::string ret; + for (std::string::const_iterator i = s.begin(); i != s.end();) + { + // available input is 1,2 or 3 bytes + // since we read 3 bytes at a time at most + int available_input = (std::min)(3, int(s.end()-i)); + + // clear input buffer + std::fill(inbuf, inbuf+3, 0); + + // read a chunk of input into inbuf + std::copy(i, i + available_input, inbuf); + i += available_input; + + // encode inbuf to outbuf + outbuf[0] = (inbuf[0] & 0xfc) >> 2; + outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf [1] & 0xf0) >> 4); + outbuf[2] = ((inbuf[1] & 0x0f) << 2) | ((inbuf [2] & 0xc0) >> 6); + outbuf[3] = inbuf[2] & 0x3f; + + // write output + for (int j = 0; j < available_input+1; ++j) + { + ret += base64_table[outbuf[j]]; + } + + // write pad + for (int j = 0; j < 3 - available_input; ++j) + { + ret += '='; + } + } + return ret; + } + + std::string base32encode(std::string const& s) + { + static const char base32_table[] = + { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '2', '3', '4', '5', '6', '7' + }; + + int input_output_mapping[] = {0, 2, 4, 5, 7, 8}; + + unsigned char inbuf[5]; + unsigned char outbuf[8]; + + std::string ret; + for (std::string::const_iterator i = s.begin(); i != s.end();) + { + int available_input = (std::min)(5, int(s.end()-i)); + + // clear input buffer + std::fill(inbuf, inbuf+5, 0); + + // read a chunk of input into inbuf + std::copy(i, i + available_input, inbuf); + i += available_input; + + // encode inbuf to outbuf + outbuf[0] = (inbuf[0] & 0xf8) >> 3; + outbuf[1] = ((inbuf[0] & 0x07) << 2) | ((inbuf[1] & 0xc0) >> 6); + outbuf[2] = ((inbuf[1] & 0x3e) >> 1); + outbuf[3] = ((inbuf[1] & 0x01) << 4) | ((inbuf[2] & 0xf0) >> 4); + outbuf[4] = ((inbuf[2] & 0x0f) << 1) | ((inbuf[3] & 0x80) >> 7); + outbuf[5] = ((inbuf[3] & 0x7c) >> 2); + outbuf[6] = ((inbuf[3] & 0x03) << 3) | ((inbuf[4] & 0xe0) >> 5); + outbuf[7] = inbuf[4] & 0x1f; + + // write output + int num_out = input_output_mapping[available_input]; + for (int j = 0; j < num_out; ++j) + { + ret += base32_table[outbuf[j]]; + } + + // write pad + for (int j = 0; j < 8 - num_out; ++j) + { + ret += '='; + } + } + return ret; + } + + std::string base32decode(std::string const& s) + { + unsigned char inbuf[8]; + unsigned char outbuf[5]; + + std::string ret; + for (std::string::const_iterator i = s.begin(); i != s.end();) + { + int available_input = (std::min)(8, int(s.end()-i)); + + int pad_start = 0; + if (available_input < 8) pad_start = available_input; + + // clear input buffer + std::fill(inbuf, inbuf+8, 0); + for (int j = 0; j < available_input; ++j) + { + char in = std::toupper(*i++); + if (in >= 'A' && in <= 'Z') + inbuf[j] = in - 'A'; + else if (in >= '2' && in <= '7') + inbuf[j] = in - '2' + ('Z' - 'A') + 1; + else if (in == '=') + { + inbuf[j] = 0; + if (pad_start == 0) pad_start = j; + } + else if (in == '1') + inbuf[j] = 'I' - 'A'; + else + return std::string(); + TORRENT_ASSERT(inbuf[j] == (inbuf[j] & 0x1f)); + } + + // decode inbuf to outbuf + outbuf[0] = inbuf[0] << 3; + outbuf[0] |= inbuf[1] >> 2; + outbuf[1] = (inbuf[1] & 0x3) << 6; + outbuf[1] |= inbuf[2] << 1; + outbuf[1] |= (inbuf[3] & 0x10) >> 4; + outbuf[2] = (inbuf[3] & 0x0f) << 4; + outbuf[2] |= (inbuf[4] & 0x1e) >> 1; + outbuf[3] = (inbuf[4] & 0x01) << 7; + outbuf[3] |= (inbuf[5] & 0x1f) << 2; + outbuf[3] |= (inbuf[6] & 0x18) >> 3; + outbuf[4] = (inbuf[6] & 0x07) << 5; + outbuf[4] |= inbuf[7]; + + int input_output_mapping[] = {5, 1, 1, 2, 2, 3, 4, 4, 5}; + int num_out = input_output_mapping[pad_start]; + + // write output + std::copy(outbuf, outbuf + num_out, std::back_inserter(ret)); + } + return ret; + } + + std::string url_has_argument( + std::string const& url, std::string argument, std::string::size_type* out_pos) + { + size_t i = url.find('?'); + if (i == std::string::npos) return std::string(); + ++i; + + argument += '='; + + if (url.compare(i, argument.size(), argument) == 0) + { + size_t pos = i + argument.size(); + if (out_pos) *out_pos = pos; + return url.substr(pos, url.find('&', pos) - pos); + } + argument.insert(0, "&"); + i = url.find(argument, i); + if (i == std::string::npos) return std::string(); + size_t pos = i + argument.size(); + if (out_pos) *out_pos = pos; + return url.substr(pos, url.find('&', pos) - pos); + } + + TORRENT_EXTRA_EXPORT std::string to_hex(std::string const& s) + { + std::string ret; + for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) + { + ret += hex_chars[((unsigned char)*i) >> 4]; + ret += hex_chars[((unsigned char)*i) & 0xf]; + } + return ret; + } + + TORRENT_EXTRA_EXPORT void to_hex(char const *in, int len, char* out) + { + for (char const* end = in + len; in < end; ++in) + { + *out++ = hex_chars[((unsigned char)*in) >> 4]; + *out++ = hex_chars[((unsigned char)*in) & 0xf]; + } + *out = '\0'; + } + + TORRENT_EXTRA_EXPORT int hex_to_int(char in) + { + if (in >= '0' && in <= '9') return int(in) - '0'; + if (in >= 'A' && in <= 'F') return int(in) - 'A' + 10; + if (in >= 'a' && in <= 'f') return int(in) - 'a' + 10; + return -1; + } + + TORRENT_EXTRA_EXPORT bool is_hex(char const *in, int len) + { + for (char const* end = in + len; in < end; ++in) + { + int t = hex_to_int(*in); + if (t == -1) return false; + } + return true; + } + + TORRENT_EXTRA_EXPORT bool from_hex(char const *in, int len, char* out) + { + for (char const* end = in + len; in < end; ++in, ++out) + { + int t = hex_to_int(*in); + if (t == -1) return false; + *out = t << 4; + ++in; + t = hex_to_int(*in); + if (t == -1) return false; + *out |= t & 15; + } + return true; + } + +#if defined TORRENT_WINDOWS && TORRENT_USE_WSTRING + std::wstring convert_to_wstring(std::string const& s) + { + std::wstring ret; + int result = libtorrent::utf8_wchar(s, ret); + if (result == 0) return ret; + + ret.clear(); + const char* end = &s[0] + s.size(); + for (const char* i = &s[0]; i < end;) + { + wchar_t c = '.'; + result = std::mbtowc(&c, i, end - i); + if (result > 0) i += result; + else ++i; + ret += c; + } + return ret; + } + + std::string convert_from_wstring(std::wstring const& s) + { + std::string ret; + int result = libtorrent::wchar_utf8(s, ret); + if (result == 0) return ret; + + ret.clear(); + const wchar_t* end = &s[0] + s.size(); + for (const wchar_t* i = &s[0]; i < end;) + { + char c[10]; + TORRENT_ASSERT(sizeof(c) >= MB_CUR_MAX); + result = std::wctomb(c, *i); + if (result > 0) + { + i += result; + ret.append(c, result); + } + else + { + ++i; + ret += "."; + } + } + return ret; + } +#endif + +#if TORRENT_USE_ICONV + std::string iconv_convert_impl(std::string const& s, iconv_t h) + { + std::string ret; + size_t insize = s.size(); + size_t outsize = insize * 4; + ret.resize(outsize); + char const* in = s.c_str(); + char* out = &ret[0]; + // posix has a weird iconv signature. implementations + // differ on what this signature should be, so we use + // a macro to let config.hpp determine it + size_t retval = iconv(h, TORRENT_ICONV_ARG &in, &insize, + &out, &outsize); + if (retval == (size_t)-1) return s; + // if this string has an invalid utf-8 sequence in it, don't touch it + if (insize != 0) return s; + // not sure why this would happen, but it seems to be possible + if (outsize > s.size() * 4) return s; + // outsize is the number of bytes unused of the out-buffer + TORRENT_ASSERT(ret.size() >= outsize); + ret.resize(ret.size() - outsize); + return ret; + } + + std::string convert_to_native(std::string const& s) + { + static mutex iconv_mutex; + // only one thread can use this handle at a time + mutex::scoped_lock l(iconv_mutex); + + // the empty string represents the local dependent encoding + static iconv_t iconv_handle = iconv_open("", "UTF-8"); + if (iconv_handle == iconv_t(-1)) return s; + return iconv_convert_impl(s, iconv_handle); + } + + std::string convert_from_native(std::string const& s) + { + static mutex iconv_mutex; + // only one thread can use this handle at a time + mutex::scoped_lock l(iconv_mutex); + + // the empty string represents the local dependent encoding + static iconv_t iconv_handle = iconv_open("UTF-8", ""); + if (iconv_handle == iconv_t(-1)) return s; + return iconv_convert_impl(s, iconv_handle); + } + +#elif defined TORRENT_WINDOWS + + std::string convert_to_native(std::string const& s) + { + std::wstring ws; + libtorrent::utf8_wchar(s, ws); + std::string ret; + ret.resize(ws.size() * 4 + 1); + std::size_t size = WideCharToMultiByte(CP_ACP, 0, ws.c_str(), -1, &ret[0], ret.size(), NULL, NULL); + if (size == std::size_t(-1)) return s; + if (size != 0 && ret[size - 1] == '\0') --size; + ret.resize(size); + return ret; + } + + std::string convert_from_native(std::string const& s) + { + std::wstring ws; + ws.resize(s.size() + 1); + std::size_t size = MultiByteToWideChar(CP_ACP, 0, s.c_str(), -1, &ws[0], ws.size()); + if (size == std::size_t(-1)) return s; + if (size != 0 && ws[size - 1] == '\0') --size; + ws.resize(size); + std::string ret; + libtorrent::wchar_utf8(ws, ret); + return ret; + } + +#elif TORRENT_USE_LOCALE + + std::string convert_to_native(std::string const& s) + { + std::wstring ws; + libtorrent::utf8_wchar(s, ws); + std::size_t size = wcstombs(0, ws.c_str(), 0); + if (size == std::size_t(-1)) return s; + std::string ret; + ret.resize(size); + size = wcstombs(&ret[0], ws.c_str(), size + 1); + if (size == std::size_t(-1)) return s; + ret.resize(size); + return ret; + } + + std::string convert_from_native(std::string const& s) + { + std::wstring ws; + ws.resize(s.size()); + std::size_t size = mbstowcs(&ws[0], s.c_str(), s.size()); + if (size == std::size_t(-1)) return s; + std::string ret; + libtorrent::wchar_utf8(ws, ret); + return ret; + } + +#endif + +} + diff --git a/apps/Launcher/ext/libtorrent/src/file.cpp b/apps/Launcher/ext/libtorrent/src/file.cpp new file mode 100644 index 0000000000..a3219b0862 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/file.cpp @@ -0,0 +1,2412 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +/* + Physical file offset patch by Morten Husveit +*/ + +#define _FILE_OFFSET_BITS 64 +#define _LARGE_FILES 1 + +// on mingw this is necessary to enable 64-bit time_t, specifically used for +// the stat struct. Without this, modification times returned by stat may be +// incorrect and consistently fail resume data +#ifndef __MINGW_USE_VC2005_COMPAT +# define __MINGW_USE_VC2005_COMPAT +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/alloca.hpp" +#include "libtorrent/allocator.hpp" // page_size +#include "libtorrent/escape_string.hpp" // for string conversion + +#include +#include + +#include + +#ifdef TORRENT_WINDOWS +// windows part + +#ifndef PtrToPtr64 +#define PtrToPtr64(x) (x) +#endif + +#include "libtorrent/utf8.hpp" + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#ifndef TORRENT_MINGW +#include // for _getcwd, _mkdir +#else +#include +#endif +#include +#else +// posix part + +#include +#include // for F_LOG2PHYS +#include +#include +#include + +#ifdef TORRENT_LINUX +// linux specifics + +#ifdef TORRENT_ANDROID +#include +#define statvfs statfs +#define fstatvfs fstatfs +#else +#include +#endif + +#include +#include +#ifdef HAVE_LINUX_FIEMAP_H +#include // FIEMAP_* +#include // FS_IOC_FIEMAP +#endif + +#ifdef TORRENT_ANDROID +#include +#define lseek lseek64 +#endif + +#include // For __NR_fallocate + +// circumvent the lack of support in glibc +static int my_fallocate(int fd, int mode, loff_t offset, loff_t len) +{ +#ifdef __NR_fallocate + // the man page on fallocate differes between versions of linux. + // it appears that fallocate in fact sets errno and returns -1 + // on failure. + return syscall(__NR_fallocate, fd, mode, offset, len); +#else + // pretend that the system call doesn't exist + errno = ENOSYS; + return -1; +#endif +} + +#elif defined __APPLE__ && defined __MACH__ && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 +// mac specifics + +#include + +#endif + +#undef _FILE_OFFSET_BITS + +// make sure the _FILE_OFFSET_BITS define worked +// on this platform. It's supposed to make file +// related functions support 64-bit offsets. +// this test makes sure lseek() returns a type +// at least 64 bits wide +BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8); + +#endif // posix part + +#include "libtorrent/file.hpp" +#include +#include + +// for convert_to_wstring and convert_to_native +#include "libtorrent/escape_string.hpp" +#include +#include "libtorrent/assert.hpp" + +#ifdef TORRENT_DEBUG +BOOST_STATIC_ASSERT((libtorrent::file::rw_mask & libtorrent::file::no_buffer) == 0); +BOOST_STATIC_ASSERT((libtorrent::file::rw_mask & libtorrent::file::attribute_mask) == 0); +BOOST_STATIC_ASSERT((libtorrent::file::no_buffer & libtorrent::file::attribute_mask) == 0); +#endif + +#if defined TORRENT_WINDOWS && defined UNICODE && !TORRENT_USE_WSTRING + +#ifdef _MSC_VER +#pragma message ( "wide character support not available. Files will be saved using narrow string names" ) +#else +#warning "wide character support not available. Files will be saved using narrow string names" +#endif + +#endif // TORRENT_WINDOWS + +namespace libtorrent +{ + +#ifdef TORRENT_WINDOWS + std::string convert_separators(std::string p) + { + for (int i = 0; i < int(p.size()); ++i) + if (p[i] == '/') p[i] = '\\'; + return p; + } + + time_t file_time_to_posix(FILETIME f) + { + const boost::uint64_t posix_time_offset = 11644473600LL; + boost::uint64_t ft = (boost::uint64_t(f.dwHighDateTime) << 32) + | f.dwLowDateTime; + + // windows filetime is specified in 100 nanoseconds resolution. + // convert to seconds + return time_t(ft / 10000000 - posix_time_offset); + } +#endif + + void stat_file(std::string inf, file_status* s + , error_code& ec, int flags) + { + ec.clear(); +#ifdef TORRENT_WINDOWS + // apparently windows doesn't expect paths + // to directories to ever end with a \ or / + if (!inf.empty() && (inf[inf.size() - 1] == '\\' + || inf[inf.size() - 1] == '/')) + inf.resize(inf.size() - 1); + +#if TORRENT_USE_WSTRING && defined TORRENT_WINDOWS +#define GetFileAttributesEx_ GetFileAttributesExW + std::wstring f = convert_to_wstring(inf); +#else +#define GetFileAttributesEx_ GetFileAttributesExA + std::string f = convert_to_native(inf); +#endif + WIN32_FILE_ATTRIBUTE_DATA data; + if (!GetFileAttributesEx(f.c_str(), GetFileExInfoStandard, &data)) + { + ec.assign(GetLastError(), get_system_category()); + return; + } + + s->file_size = (boost::uint64_t(data.nFileSizeHigh) << 32) | data.nFileSizeLow; + s->ctime = file_time_to_posix(data.ftCreationTime); + s->atime = file_time_to_posix(data.ftLastAccessTime); + s->mtime = file_time_to_posix(data.ftLastWriteTime); + + s->mode = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + ? file_status::directory + : (data.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) + ? file_status::character_special : file_status::regular_file; + +#else + + // posix version + + std::string f = convert_to_native(inf); + + struct stat ret; + int retval; + if (flags & dont_follow_links) + retval = ::lstat(f.c_str(), &ret); + else + retval = ::stat(f.c_str(), &ret); + if (retval < 0) + { + ec.assign(errno, generic_category()); + return; + } + + s->file_size = ret.st_size; + s->atime = ret.st_atime; + s->mtime = ret.st_mtime; + s->ctime = ret.st_ctime; + + s->mode = (S_ISREG(ret.st_mode) ? file_status::regular_file : 0) + | (S_ISDIR(ret.st_mode) ? file_status::directory : 0) + | (S_ISLNK(ret.st_mode) ? file_status::link : 0) + | (S_ISFIFO(ret.st_mode) ? file_status::fifo : 0) + | (S_ISCHR(ret.st_mode) ? file_status::character_special : 0) + | (S_ISBLK(ret.st_mode) ? file_status::block_special : 0) + | (S_ISSOCK(ret.st_mode) ? file_status::socket : 0); + +#endif // TORRENT_WINDOWS + } + + void rename(std::string const& inf, std::string const& newf, error_code& ec) + { + ec.clear(); + +#if TORRENT_USE_WSTRING && defined TORRENT_WINDOWS + std::wstring f1 = convert_to_wstring(inf); + std::wstring f2 = convert_to_wstring(newf); + if (_wrename(f1.c_str(), f2.c_str()) < 0) +#else + std::string f1 = convert_to_native(inf); + std::string f2 = convert_to_native(newf); + if (::rename(f1.c_str(), f2.c_str()) < 0) +#endif + { + ec.assign(errno, generic_category()); + return; + } + } + + void create_directories(std::string const& f, error_code& ec) + { + ec.clear(); + if (is_directory(f, ec)) return; + if (ec != boost::system::errc::no_such_file_or_directory) + return; + ec.clear(); + if (is_root_path(f)) return; + if (has_parent_path(f)) + { + create_directories(parent_path(f), ec); + if (ec) return; + } + create_directory(f, ec); + } + + void create_directory(std::string const& f, error_code& ec) + { + ec.clear(); + +#if defined TORRENT_WINDOWS && TORRENT_USE_WSTRING +#define CreateDirectory_ CreateDirectoryW + std::wstring n = convert_to_wstring(f); +#else +#define CreateDirectory_ CreateDirectoryA + std::string n = convert_to_native(f); +#endif + +#ifdef TORRENT_WINDOWS + if (CreateDirectory_(n.c_str(), 0) == 0 + && GetLastError() != ERROR_ALREADY_EXISTS) + ec.assign(GetLastError(), get_system_category()); +#else + int ret = mkdir(n.c_str(), 0777); + if (ret < 0 && errno != EEXIST) + ec.assign(errno, generic_category()); +#endif + } + + bool is_directory(std::string const& f, error_code& ec) + { + ec.clear(); + error_code e; + file_status s; + stat_file(f, &s, e); + if (!e && s.mode & file_status::directory) return true; + ec = e; + return false; + } + + void recursive_copy(std::string const& old_path, std::string const& new_path, error_code& ec) + { + TORRENT_ASSERT(!ec); + if (is_directory(old_path, ec)) + { + create_directory(new_path, ec); + if (ec) return; + for (directory i(old_path, ec); !i.done(); i.next(ec)) + { + std::string f = i.file(); + if (f == ".." || f == ".") continue; + recursive_copy(combine_path(old_path, f), combine_path(new_path, f), ec); + if (ec) return; + } + } + else if (!ec) + { + copy_file(old_path, new_path, ec); + } + } + + void copy_file(std::string const& inf, std::string const& newf, error_code& ec) + { + ec.clear(); +#if TORRENT_USE_WSTRING && defined TORRENT_WINDOWS +#define CopyFile_ CopyFileW + std::wstring f1 = convert_to_wstring(inf); + std::wstring f2 = convert_to_wstring(newf); +#else +#define CopyFile_ CopyFileA + std::string f1 = convert_to_native(inf); + std::string f2 = convert_to_native(newf); +#endif + +#ifdef TORRENT_WINDOWS + if (CopyFile_(f1.c_str(), f2.c_str(), false) == 0) + ec.assign(GetLastError(), get_system_category()); +#elif defined __APPLE__ && defined __MACH__ && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 + // this only works on 10.5 + copyfile_state_t state = copyfile_state_alloc(); + if (copyfile(f1.c_str(), f2.c_str(), state, COPYFILE_ALL) < 0) + ec.assign(errno, generic_category()); + copyfile_state_free(state); +#else + int infd = ::open(inf.c_str(), O_RDONLY); + if (infd < 0) + { + ec.assign(errno, generic_category()); + return; + } + + // rely on default umask to filter x and w permissions + // for group and others + int permissions = S_IRUSR | S_IWUSR + | S_IRGRP | S_IWGRP + | S_IROTH | S_IWOTH; + + int outfd = ::open(newf.c_str(), O_WRONLY | O_CREAT, permissions); + if (outfd < 0) + { + close(infd); + ec.assign(errno, generic_category()); + return; + } + char buffer[4096]; + for (;;) + { + int num_read = read(infd, buffer, sizeof(buffer)); + if (num_read == 0) break; + if (num_read < 0) + { + ec.assign(errno, generic_category()); + break; + } + int num_written = write(outfd, buffer, num_read); + if (num_written < num_read) + { + ec.assign(errno, generic_category()); + break; + } + if (num_read < int(sizeof(buffer))) break; + } + close(infd); + close(outfd); +#endif // TORRENT_WINDOWS + } + + std::string split_path(std::string const& f) + { + if (f.empty()) return f; + + std::string ret; + char const* start = f.c_str(); + char const* p = start; + while (*start != 0) + { + while (*p != '/' + && *p != '\0' +#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) + && *p != '\\' +#endif + ) ++p; + if (p - start > 0) + { + ret.append(start, p - start); + ret.append(1, '\0'); + } + if (*p != 0) ++p; + start = p; + } + ret.append(1, '\0'); + return ret; + } + + char const* next_path_element(char const* p) + { + p += strlen(p) + 1; + if (*p == 0) return 0; + return p; + } + + std::string extension(std::string const& f) + { + for (int i = f.size() - 1; i >= 0; --i) + { + if (f[i] == '/') break; +#ifdef TORRENT_WINDOWS + if (f[i] == '\\') break; +#endif + if (f[i] != '.') continue; + return f.substr(i); + } + return ""; + } + + std::string remove_extension(std::string const& f) + { + char const* slash = strrchr(f.c_str(), '/'); +#ifdef TORRENT_WINDOWS + slash = (std::max)((char const*)strrchr(f.c_str(), '\\'), slash); +#endif + char const* ext = strrchr(f.c_str(), '.'); + // if we don't have an extension, just return f + if (ext == 0 || ext == &f[0] || (slash != NULL && ext < slash)) return f; + return f.substr(0, ext - &f[0]); + } + + void replace_extension(std::string& f, std::string const& ext) + { + for (int i = f.size() - 1; i >= 0; --i) + { + if (f[i] == '/') break; +#ifdef TORRENT_WINDOWS + if (f[i] == '\\') break; +#endif + + if (f[i] != '.') continue; + + f.resize(i); + break; + } + f += '.'; + f += ext; + } + + bool is_root_path(std::string const& f) + { + if (f.empty()) return false; + +#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) + // match \\ form + if (f == "\\\\") return true; + int i = 0; + // match the xx:\ or xx:/ form + while (f[i] && is_alpha(f[i])) ++i; + if (i == int(f.size()-2) && f[i] == ':' && (f[i+1] == '\\' || f[i+1] == '/')) + return true; + // match network paths \\computer_name\ form + if (f.size() > 2 && f[0] == '\\' && f[1] == '\\') + { + // we don't care about the last character, since it's OK for it + // to be a slash or a back slash + bool found = false; + for (int i = 2; i < int(f.size()) - 1; ++i) + { + if (f[i] != '\\' && f[i] != '/') continue; + // there is a directory separator in here, + // i.e. this is not the root + found = true; + break; + } + if (!found) return true; + } +#else + // as well as parent_path("/") should be "/". + if (f == "/") return true; +#endif + return false; + } + + bool has_parent_path(std::string const& f) + { + if (f.empty()) return false; + if (is_root_path(f)) return false; + + int len = f.size() - 1; + // if the last character is / or \ ignore it + if (f[len] == '/' || f[len] == '\\') --len; + while (len >= 0) + { + if (f[len] == '/' || f[len] == '\\') + break; + --len; + } + + return len >= 0; + } + + std::string parent_path(std::string const& f) + { + if (f.empty()) return f; + +#ifdef TORRENT_WINDOWS + if (f == "\\\\") return ""; +#endif + if (f == "/") return ""; + + int len = f.size(); + // if the last character is / or \ ignore it + if (f[len-1] == '/' || f[len-1] == '\\') --len; + while (len > 0) + { + --len; + if (f[len] == '/' || f[len] == '\\') + break; + } + + if (f[len] == '/' || f[len] == '\\') ++len; + return std::string(f.c_str(), len); + } + + std::string filename(std::string const& f) + { + if (f.empty()) return ""; + char const* first = f.c_str(); + char const* sep = strrchr(first, '/'); +#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) + char const* altsep = strrchr(first, '\\'); + if (sep == 0 || altsep > sep) sep = altsep; +#endif + if (sep == 0) return f; + + if (sep - first == int(f.size()) - 1) + { + // if the last character is a / (or \) + // ignore it + int len = 0; + while (sep > first) + { + --sep; + if (*sep == '/' +#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) + || *sep == '\\' +#endif + ) + return std::string(sep + 1, len); + ++len; + } + return std::string(first, len); + + } + return std::string(sep + 1); + } + + std::string combine_path(std::string const& lhs, std::string const& rhs) + { + TORRENT_ASSERT(!is_complete(rhs)); + if (lhs.empty() || lhs == ".") return rhs; + if (rhs.empty() || rhs == ".") return lhs; + +#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) +#define TORRENT_SEPARATOR "\\" + bool need_sep = lhs[lhs.size()-1] != '\\' && lhs[lhs.size()-1] != '/'; +#else +#define TORRENT_SEPARATOR "/" + bool need_sep = lhs[lhs.size()-1] != '/'; +#endif + std::string ret; + int target_size = lhs.size() + rhs.size() + 2; + ret.resize(target_size); + target_size = snprintf(&ret[0], target_size, "%s%s%s", lhs.c_str() + , (need_sep?TORRENT_SEPARATOR:""), rhs.c_str()); + ret.resize(target_size); + return ret; + } + + std::string current_working_directory() + { +#if defined TORRENT_WINDOWS && !defined TORRENT_MINGW +#if TORRENT_USE_WSTRING + wchar_t cwd[TORRENT_MAX_PATH]; + _wgetcwd(cwd, sizeof(cwd) / sizeof(wchar_t)); +#else + char cwd[TORRENT_MAX_PATH]; + _getcwd(cwd, sizeof(cwd)); +#endif // TORRENT_USE_WSTRING +#else + char cwd[TORRENT_MAX_PATH]; + if (getcwd(cwd, sizeof(cwd)) == 0) return "/"; +#endif +#if defined TORRENT_WINDOWS && !defined TORRENT_MINGW && TORRENT_USE_WSTRING + return convert_from_wstring(cwd); +#else + return convert_from_native(cwd); +#endif + } + +#if TORRENT_USE_UNC_PATHS + std::string canonicalize_path(std::string const& f) + { + std::string ret; + ret.resize(f.size()); + char* write_cur = &ret[0]; + char* last_write_sep = write_cur; + + char const* read_cur = f.c_str(); + char const* last_read_sep = read_cur; + + // the last_*_sep pointers point to one past + // the last path separator encountered and is + // initializes to the first character in the path + while (*read_cur) + { + if (*read_cur != '\\') + { + *write_cur++ = *read_cur++; + continue; + } + int element_len = read_cur - last_read_sep; + if (element_len == 1 && memcmp(last_read_sep, ".", 1) == 0) + { + --write_cur; + ++read_cur; + last_read_sep = read_cur; + continue; + } + if (element_len == 2 && memcmp(last_read_sep, "..", 2) == 0) + { + // find the previous path separator + if (last_write_sep > &ret[0]) + { + --last_write_sep; + while (last_write_sep > &ret[0] + && last_write_sep[-1] != '\\') + --last_write_sep; + } + write_cur = last_write_sep; + // find the previous path separator + if (last_write_sep > &ret[0]) + { + --last_write_sep; + while (last_write_sep > &ret[0] + && last_write_sep[-1] != '\\') + --last_write_sep; + } + ++read_cur; + last_read_sep = read_cur; + continue; + } + *write_cur++ = *read_cur++; + last_write_sep = write_cur; + last_read_sep = read_cur; + } + // terminate destination string + *write_cur = 0; + ret.resize(write_cur - &ret[0]); + return ret; + } +#endif + + size_type file_size(std::string const& f) + { + error_code ec; + file_status s; + stat_file(f, &s, ec); + if (ec) return 0; + return s.file_size; + } + + bool exists(std::string const& f) + { + error_code ec; + file_status s; + stat_file(f, &s, ec); + if (ec) return false; + return true; + } + + void remove(std::string const& inf, error_code& ec) + { + ec.clear(); + +#ifdef TORRENT_WINDOWS + // windows does not allow trailing / or \ in + // the path when removing files + std::string pruned; + if (inf[inf.size() - 1] == '/' + || inf[inf.size() - 1] == '\\') + pruned = inf.substr(0, inf.size() - 1); + else + pruned = inf; +#if TORRENT_USE_WSTRING +#define DeleteFile_ DeleteFileW +#define RemoveDirectory_ RemoveDirectoryW + std::wstring f = convert_to_wstring(pruned); +#else +#define DeleteFile_ DeleteFileA +#define RemoveDirectory_ RemoveDirectoryA + std::string f = convert_to_native(pruned); +#endif + if (DeleteFile_(f.c_str()) == 0) + { + if (GetLastError() == ERROR_ACCESS_DENIED) + { + if (RemoveDirectory_(f.c_str()) != 0) + return; + } + ec.assign(GetLastError(), get_system_category()); + return; + } +#else // TORRENT_WINDOWS + std::string f = convert_to_native(inf); + if (::remove(f.c_str()) < 0) + { + ec.assign(errno, generic_category()); + return; + } +#endif // TORRENT_WINDOWS + } + + void remove_all(std::string const& f, error_code& ec) + { + ec.clear(); + + file_status s; + stat_file(f, &s, ec); + if (ec) return; + + if (s.mode & file_status::directory) + { + for (directory i(f, ec); !i.done(); i.next(ec)) + { + if (ec) return; + std::string p = i.file(); + if (p == "." || p == "..") continue; + remove_all(combine_path(f, p), ec); + if (ec) return; + } + } + remove(f, ec); + } + + std::string complete(std::string const& f) + { + if (is_complete(f)) return f; + if (f == ".") return current_working_directory(); + return combine_path(current_working_directory(), f); + } + + bool is_complete(std::string const& f) + { + if (f.empty()) return false; +#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) + int i = 0; + // match the xx:\ or xx:/ form + while (f[i] && is_alpha(f[i])) ++i; + if (i < int(f.size()-1) && f[i] == ':' && (f[i+1] == '\\' || f[i+1] == '/')) + return true; + + // match the \\ form + if (int(f.size()) >= 2 && f[0] == '\\' && f[1] == '\\') + return true; + return false; +#else + if (f[0] == '/') return true; + return false; +#endif + } + + directory::directory(std::string const& path, error_code& ec) + : m_done(false) + { + ec.clear(); +#ifdef TORRENT_WINDOWS + m_inode = 0; + // the path passed to FindFirstFile() must be + // a pattern + std::string f = convert_separators(path); + if (!f.empty() && f[f.size()-1] != '\\') f += "\\*"; + else f += "*"; +#if TORRENT_USE_WSTRING +#define FindFirstFile_ FindFirstFileW + std::wstring p = convert_to_wstring(f); +#else +#define FindFirstFile_ FindFirstFileA + std::string p = convert_to_native(f); +#endif + m_handle = FindFirstFile_(p.c_str(), &m_fd); + if (m_handle == INVALID_HANDLE_VALUE) + { + ec.assign(GetLastError(), boost::system::get_system_category()); + m_done = true; + return; + } +#else + + memset(&m_dirent, 0, sizeof(dirent)); + m_name[0] = 0; + + // the path passed to opendir() may not + // end with a / + std::string p = path; + if (!path.empty() && path[path.size()-1] == '/') + p.resize(path.size()-1); + + p = convert_to_native(p); + m_handle = opendir(p.c_str()); + if (m_handle == 0) + { + ec.assign(errno, generic_category()); + m_done = true; + return; + } + // read the first entry + next(ec); +#endif + } + + directory::~directory() + { +#ifdef TORRENT_WINDOWS + if (m_handle != INVALID_HANDLE_VALUE) + FindClose(m_handle); +#else + if (m_handle) closedir(m_handle); +#endif + } + + boost::uint64_t directory::inode() const + { +#ifdef TORRENT_WINDOWS + return m_inode; +#else + return m_dirent.d_ino; +#endif + } + + std::string directory::file() const + { +#ifdef TORRENT_WINDOWS +#if TORRENT_USE_WSTRING + return convert_from_wstring(m_fd.cFileName); +#else + return convert_from_native(m_fd.cFileName); +#endif +#else + return convert_from_native(m_dirent.d_name); +#endif + } + + void directory::next(error_code& ec) + { + ec.clear(); +#ifdef TORRENT_WINDOWS +#if TORRENT_USE_WSTRING +#define FindNextFile_ FindNextFileW +#else +#define FindNextFile_ FindNextFileA +#endif + if (FindNextFile_(m_handle, &m_fd) == 0) + { + m_done = true; + int err = GetLastError(); + if (err != ERROR_NO_MORE_FILES) + ec.assign(err, boost::system::get_system_category()); + } + ++m_inode; +#else + dirent* dummy; + if (readdir_r(m_handle, &m_dirent, &dummy) != 0) + { + ec.assign(errno, generic_category()); + m_done = true; + } + if (dummy == 0) m_done = true; +#endif + } + +#ifdef TORRENT_WINDOWS + struct overlapped_t + { + overlapped_t() + { + memset(&ol, 0, sizeof(ol)); + ol.hEvent = CreateEvent(0, true, false, 0); + } + ~overlapped_t() + { + if (ol.hEvent != INVALID_HANDLE_VALUE) + CloseHandle(ol.hEvent); + } + int wait(HANDLE file, error_code& ec) + { + if (ol.hEvent != INVALID_HANDLE_VALUE + && WaitForSingleObject(ol.hEvent, INFINITE) == WAIT_FAILED) + { + ec.assign(GetLastError(), get_system_category()); + return -1; + } + + DWORD ret = -1; + if (GetOverlappedResult(file, &ol, &ret, false) == 0) + { + DWORD last_error = GetLastError(); + if (last_error != ERROR_HANDLE_EOF) + { +#ifdef ERROR_CANT_WAIT + TORRENT_ASSERT(last_error != ERROR_CANT_WAIT); +#endif + ec.assign(last_error, get_system_category()); + return -1; + } + } + return ret; + } + + OVERLAPPED ol; + }; +#endif // TORRENT_WINDOWS + + +#ifdef TORRENT_WINDOWS + bool get_manage_volume_privs(); + + // this needs to be run before CreateFile + bool file::has_manage_volume_privs = get_manage_volume_privs(); +#endif + + file::file() +#ifdef TORRENT_WINDOWS + : m_file_handle(INVALID_HANDLE_VALUE) +#else + : m_fd(-1) +#endif + , m_open_mode(0) +#if defined TORRENT_WINDOWS || defined TORRENT_LINUX + , m_sector_size(0) +#endif + {} + + file::file(std::string const& path, int mode, error_code& ec) +#ifdef TORRENT_WINDOWS + : m_file_handle(INVALID_HANDLE_VALUE) +#else + : m_fd(-1) +#endif + , m_open_mode(0) + { + // the return value is not important, since the + // error code contains the same information + open(path, mode, ec); + } + + file::~file() + { + close(); + } + + bool file::open(std::string const& path, int mode, error_code& ec) + { + close(); +#ifdef TORRENT_WINDOWS + + struct open_mode_t + { + DWORD rw_mode; + DWORD create_mode; + }; + + const static open_mode_t mode_array[] = + { + // read_only + {GENERIC_READ, OPEN_EXISTING}, + // write_only + {GENERIC_WRITE, OPEN_ALWAYS}, + // read_write + {GENERIC_WRITE | GENERIC_READ, OPEN_ALWAYS}, + }; + + const static DWORD attrib_array[] = + { + FILE_ATTRIBUTE_NORMAL, // no attrib + FILE_ATTRIBUTE_HIDDEN, // hidden + FILE_ATTRIBUTE_NORMAL, // executable + FILE_ATTRIBUTE_HIDDEN, // hidden + executable + }; + + const static DWORD share_array[] = + { + // read only (no locking) + FILE_SHARE_READ | FILE_SHARE_WRITE, + // write only (no locking) + FILE_SHARE_READ, + // read/write (no locking) + FILE_SHARE_READ, + }; + + std::string p = convert_separators(path); +#if TORRENT_USE_UNC_PATHS + // UNC paths must be absolute + // network paths are already UNC paths + if (path.substr(0,2) == "\\\\") p = path; + else p = "\\\\?\\" + (is_complete(p) ? p : combine_path(current_working_directory(), p)); +#endif + +#if TORRENT_USE_WSTRING +#define CreateFile_ CreateFileW + m_path = convert_to_wstring(p); +#else +#define CreateFile_ CreateFileA + m_path = convert_to_native(p); +#endif + + TORRENT_ASSERT((mode & rw_mask) < sizeof(mode_array)/sizeof(mode_array[0])); + open_mode_t const& m = mode_array[mode & rw_mask]; + DWORD a = attrib_array[(mode & attribute_mask) >> 12]; + + // one might think it's a good idea to pass in FILE_FLAG_RANDOM_ACCESS. It + // turns out that it isn't. That flag will break your operating system: + // http://support.microsoft.com/kb/2549369 + + DWORD flags + = ((mode & random_access) ? 0 : FILE_FLAG_SEQUENTIAL_SCAN) + | (a ? a : FILE_ATTRIBUTE_NORMAL) + | ((mode & no_buffer) ? FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING : 0); + + m_file_handle = CreateFile_(m_path.c_str(), m.rw_mode + , (mode & lock_file) ? 0 : share_array[mode & rw_mask] + , 0, m.create_mode, flags, 0); + + if (m_file_handle == INVALID_HANDLE_VALUE) + { + ec.assign(GetLastError(), get_system_category()); + TORRENT_ASSERT(ec); + return false; + } + + // try to make the file sparse if supported + // only set this flag if the file is opened for writing + if ((mode & file::sparse) && (mode & rw_mask) != read_only) + { + DWORD temp; + bool use_overlapped = (m_open_mode & no_buffer) != 0; + overlapped_t ol; + BOOL ret = ::DeviceIoControl(m_file_handle, FSCTL_SET_SPARSE, 0, 0 + , 0, 0, &temp, use_overlapped ? &ol.ol : NULL); + error_code error; + if (use_overlapped && ret == FALSE && GetLastError() == ERROR_IO_PENDING) + ol.wait(m_file_handle, error); + } +#else // TORRENT_WINDOWS + + // rely on default umask to filter x and w permissions + // for group and others + int permissions = S_IRUSR | S_IWUSR + | S_IRGRP | S_IWGRP + | S_IROTH | S_IWOTH; + + if (mode & attribute_executable) + permissions |= S_IXGRP | S_IXOTH | S_IXUSR; +#ifdef O_BINARY + static const int mode_array[] = {O_RDONLY | O_BINARY, O_WRONLY | O_CREAT | O_BINARY, O_RDWR | O_CREAT | O_BINARY}; +#else + static const int mode_array[] = {O_RDONLY, O_WRONLY | O_CREAT, O_RDWR | O_CREAT}; +#endif +#ifdef O_DIRECT + static const int no_buffer_flag[] = {0, O_DIRECT}; +#else + static const int no_buffer_flag[] = {0, 0}; +#endif + +#ifdef O_NOATIME + static const int no_atime_flag[] = {0, O_NOATIME}; +#endif + + m_fd = ::open(convert_to_native(path).c_str() + , mode_array[mode & rw_mask] + | no_buffer_flag[(mode & no_buffer) >> 2] +#ifdef O_NOATIME + | no_atime_flag[(mode & no_atime) >> 4] +#endif + , permissions); + +#ifdef TORRENT_LINUX + // workaround for linux bug + // https://bugs.launchpad.net/ubuntu/+source/linux/+bug/269946 + if (m_fd == -1 && (mode & no_buffer) && errno == EINVAL) + { + mode &= ~no_buffer; + m_fd = ::open(path.c_str() + , mode_array[mode & rw_mask] +#ifdef O_NOATIME + | no_atime_flag[(mode & no_atime) >> 4] +#endif + + , permissions); + } +#endif + +#ifdef O_NOATIME + // O_NOATIME is not allowed for files we don't own + // so, if we get EPERM when we try to open with it + // try again without O_NOATIME + if (m_fd == -1 && (mode & no_atime) && errno == EPERM) + { + mode &= ~no_atime; + m_fd = ::open(path.c_str() + , mode_array[mode & rw_mask] + | no_buffer_flag[(mode & no_buffer) >> 2] + , permissions); + } +#endif + if (m_fd == -1) + { + ec.assign(errno, get_posix_category()); + TORRENT_ASSERT(ec); + return false; + } + + // The purpose of the lock_file flag is primarily to prevent other + // processes from corrupting files that are being used by libtorrent. + // the posix file locking mechanism does not prevent others from + // accessing files, unless they also attempt to lock the file. That's + // why the SETLK mechanism is not used here. + +#ifdef DIRECTIO_ON + // for solaris + if (mode & no_buffer) + { + int yes = 1; + directio(m_fd, DIRECTIO_ON); + } +#endif + +#ifdef F_NOCACHE + // for BSD/Mac + if (mode & no_buffer) + { + int yes = 1; + fcntl(m_fd, F_NOCACHE, &yes); + } +#endif + +#ifdef POSIX_FADV_RANDOM + if (mode & random_access) + { + // disable read-ahead + posix_fadvise(m_fd, 0, 0, POSIX_FADV_RANDOM); + } +#endif + +#endif + m_open_mode = mode; + + TORRENT_ASSERT(is_open()); + return true; + } + + bool file::is_open() const + { +#ifdef TORRENT_WINDOWS + return m_file_handle != INVALID_HANDLE_VALUE; +#else + return m_fd != -1; +#endif + } + + int file::pos_alignment() const + { + // on linux and windows, file offsets needs + // to be aligned to the disk sector size +#if defined TORRENT_LINUX + if (m_sector_size == 0) + { + struct statvfs fs; + if (fstatvfs(m_fd, &fs) == 0) + m_sector_size = fs.f_bsize; + else + m_sector_size = 4096; + } + return m_sector_size; +#elif defined TORRENT_WINDOWS + if (m_sector_size == 0) + { + DWORD sectors_per_cluster; + DWORD bytes_per_sector; + DWORD free_clusters; + DWORD total_clusters; +#if TORRENT_USE_WSTRING +#define GetDiskFreeSpace_ GetDiskFreeSpaceW + wchar_t backslash = L'\\'; +#else +#define GetDiskFreeSpace_ GetDiskFreeSpaceA + char backslash = '\\'; +#endif + if (GetDiskFreeSpace_(m_path.substr(0, m_path.find_first_of(backslash)+1).c_str() + , §ors_per_cluster, &bytes_per_sector + , &free_clusters, &total_clusters)) + { + m_sector_size = bytes_per_sector; + m_cluster_size = sectors_per_cluster * bytes_per_sector; + } + else + { + // make a conservative guess + m_sector_size = 512; + m_cluster_size = 4096; + } + } + return m_sector_size; +#else + return 1; +#endif + } + + int file::buf_alignment() const + { +#if defined TORRENT_WINDOWS + init_file(); + return m_page_size; +#else + return pos_alignment(); +#endif + } + + int file::size_alignment() const + { +#if defined TORRENT_WINDOWS + init_file(); + return m_page_size; +#else + return pos_alignment(); +#endif + } + +#ifdef TORRENT_WINDOWS + bool is_sparse(HANDLE file, bool overlapped) + { + LARGE_INTEGER file_size; + if (!GetFileSizeEx(file, &file_size)) + return false; + + overlapped_t ol; + if (ol.ol.hEvent == NULL) return false; + +#ifdef TORRENT_MINGW +typedef struct _FILE_ALLOCATED_RANGE_BUFFER { + LARGE_INTEGER FileOffset; + LARGE_INTEGER Length; +} FILE_ALLOCATED_RANGE_BUFFER, *PFILE_ALLOCATED_RANGE_BUFFER; +#define FSCTL_QUERY_ALLOCATED_RANGES ((0x9 << 16) | (1 << 14) | (51 << 2) | 3) +#endif + FILE_ALLOCATED_RANGE_BUFFER in; + in.FileOffset.QuadPart = 0; + in.Length.QuadPart = file_size.QuadPart; + + FILE_ALLOCATED_RANGE_BUFFER out[2]; + + DWORD returned_bytes = 0; + BOOL ret = DeviceIoControl(file, FSCTL_QUERY_ALLOCATED_RANGES, (void*)&in, sizeof(in) + , out, sizeof(out), &returned_bytes, overlapped ? &ol.ol : NULL); + + if (overlapped && ret == FALSE && GetLastError() == ERROR_IO_PENDING) + { + error_code ec; + returned_bytes = ol.wait(file, ec); + if (ec) return true; + } + else if (ret == FALSE) + { + int error = GetLastError(); + return true; + } + + // if we have more than one range in the file, we're sparse + if (returned_bytes != sizeof(FILE_ALLOCATED_RANGE_BUFFER)) { + return true; + } + + return (in.Length.QuadPart != out[0].Length.QuadPart); + } +#endif + + void file::close() + { +#if defined TORRENT_WINDOWS || defined TORRENT_LINUX + m_sector_size = 0; +#endif + +#ifdef TORRENT_WINDOWS + if (m_file_handle == INVALID_HANDLE_VALUE) return; + + // if this file is open for writing, has the sparse + // flag set, but there are no sparse regions, unset + // the flag + int rw_mode = m_open_mode & rw_mask; + bool use_overlapped = (m_open_mode & no_buffer) != 0; + if ((rw_mode != read_only) + && (m_open_mode & sparse) + && !is_sparse(m_file_handle, use_overlapped)) + { + overlapped_t ol; + // according to MSDN, clearing the sparse flag of a file only + // works on windows vista and later +#ifdef TORRENT_MINGW + typedef struct _FILE_SET_SPARSE_BUFFER { + BOOLEAN SetSparse; + } FILE_SET_SPARSE_BUFFER, *PFILE_SET_SPARSE_BUFFER; +#endif + DWORD temp; + FILE_SET_SPARSE_BUFFER b; + b.SetSparse = FALSE; + BOOL ret = ::DeviceIoControl(m_file_handle, FSCTL_SET_SPARSE, &b, sizeof(b) + , 0, 0, &temp, use_overlapped ? &ol.ol : NULL); + error_code ec; + if (use_overlapped && ret == FALSE && GetLastError() == ERROR_IO_PENDING) + { + ol.wait(m_file_handle, ec); + } + } + + CloseHandle(m_file_handle); + m_file_handle = INVALID_HANDLE_VALUE; + m_path.clear(); +#else + if (m_fd == -1) return; + ::close(m_fd); + m_fd = -1; +#endif + m_open_mode = 0; + } + + // defined in storage.cpp + int bufs_size(file::iovec_t const* bufs, int num_bufs); + +#if defined TORRENT_WINDOWS || defined TORRENT_LINUX || defined TORRENT_DEBUG + + int file::m_page_size = 0; + + void file::init_file() + { + if (m_page_size != 0) return; + + m_page_size = page_size(); + } + +#endif + + void file::hint_read(size_type file_offset, int len) + { +#if defined POSIX_FADV_WILLNEED + posix_fadvise(m_fd, file_offset, len, POSIX_FADV_WILLNEED); +#elif defined F_RDADVISE + radvisory r; + r.ra_offset = file_offset; + r.ra_count = len; + fcntl(m_fd, F_RDADVISE, &r); +#else + // TODO: is there any way to pre-fetch data from a file on windows? +#endif + } + + size_type file::readv(size_type file_offset, iovec_t const* bufs, int num_bufs, error_code& ec) + { +#ifdef TORRENT_WINDOWS + if (m_file_handle == INVALID_HANDLE_VALUE) + { + ec = error_code(ERROR_INVALID_HANDLE, get_system_category()); + return -1; + } +#else + if (m_fd == -1) + { + ec = error_code(EBADF, get_system_category()); + return -1; + } +#endif + TORRENT_ASSERT((m_open_mode & rw_mask) == read_only || (m_open_mode & rw_mask) == read_write); + TORRENT_ASSERT(bufs); + TORRENT_ASSERT(num_bufs > 0); + TORRENT_ASSERT(is_open()); + +#if defined TORRENT_WINDOWS || defined TORRENT_LINUX || defined TORRENT_DEBUG + // make sure m_page_size is initialized + init_file(); +#endif + +#ifdef TORRENT_DEBUG + if (m_open_mode & no_buffer) + { + bool eof = false; + int size = 0; + // when opened in no_buffer mode, the file_offset must + // be aligned to pos_alignment() + TORRENT_ASSERT((file_offset & (pos_alignment()-1)) == 0); + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + TORRENT_ASSERT((uintptr_t(i->iov_base) & (buf_alignment()-1)) == 0); + // every buffer must be a multiple of the page size + // except for the last one + TORRENT_ASSERT((i->iov_len & (size_alignment()-1)) == 0 || i == end-1); + if ((i->iov_len & (size_alignment()-1)) != 0) eof = true; + size += i->iov_len; + } + error_code code; + if (eof) + { + size_type fsize = get_size(code); + if (code) printf("get_size: %s\n", code.message().c_str()); + if (file_offset + size < fsize) + { + printf("offset: %d size: %d get_size: %d\n", int(file_offset), int(size), int(fsize)); + TORRENT_ASSERT(false); + } + } + } +#endif + +#ifdef TORRENT_WINDOWS + + DWORD ret = 0; + + // since the ReadFileScatter requires the file to be opened + // with no buffering, and no buffering requires page aligned + // buffers, open the file in non-buffered mode in case the + // buffer is not aligned. Most of the times the buffer should + // be aligned though + + if ((m_open_mode & no_buffer) == 0) + { + // this means the buffer base or the buffer size is not aligned + // to the page size. Use a regular file for this operation. + + LARGE_INTEGER offs; + offs.QuadPart = file_offset; + if (SetFilePointerEx(m_file_handle, offs, &offs, FILE_BEGIN) == FALSE) + { + ec.assign(GetLastError(), get_system_category()); + return -1; + } + + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + DWORD intermediate = 0; + if (ReadFile(m_file_handle, (char*)i->iov_base + , (DWORD)i->iov_len, &intermediate, 0) == FALSE) + { + ec.assign(GetLastError(), get_system_category()); + return -1; + } + ret += intermediate; + } + return ret; + } + + int size = bufs_size(bufs, num_bufs); + // number of pages for the read. round up + int num_pages = (size + m_page_size - 1) / m_page_size; + // allocate array of FILE_SEGMENT_ELEMENT for ReadFileScatter + FILE_SEGMENT_ELEMENT* segment_array = TORRENT_ALLOCA(FILE_SEGMENT_ELEMENT, num_pages + 1); +#ifdef __GNUC__ + // MingW seems to have issues with 64 bit wide pointers + // (PVOID64) and only assign the low 32 bits. Therefore, make + // sure the other 32 bits are cleared out + memset(segment_array, 0, (num_pages + 1) * sizeof(FILE_SEGMENT_ELEMENT)); +#endif + FILE_SEGMENT_ELEMENT* cur_seg = segment_array; + + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + for (int k = 0; k < int(i->iov_len); k += m_page_size) + { + cur_seg->Buffer = PtrToPtr64((((char*)i->iov_base) + k)); + ++cur_seg; + } + } + // terminate the array + cur_seg->Buffer = 0; + + OVERLAPPED ol; + memset(&ol, 0, sizeof(ol)); + ol.Internal = 0; + ol.InternalHigh = 0; + ol.OffsetHigh = DWORD(file_offset >> 32); + ol.Offset = DWORD(file_offset & 0xffffffff); + ol.hEvent = CreateEvent(0, true, false, 0); + if (ol.hEvent == NULL) + { + ec.assign(GetLastError(), get_system_category()); + return -1; + } + + ret += size; + size = num_pages * m_page_size; + if (ReadFileScatter(m_file_handle, segment_array, size, 0, &ol) == 0) + { + DWORD last_error = GetLastError(); + if (last_error != ERROR_IO_PENDING +#ifdef ERROR_CANT_WAIT + && last_error != ERROR_CANT_WAIT +#endif +) + { + ec.assign(last_error, get_system_category()); + CloseHandle(ol.hEvent); + return -1; + } + if (WaitForSingleObject(ol.hEvent, INFINITE) == WAIT_FAILED) + { + ec.assign(GetLastError(), get_system_category()); + CloseHandle(ol.hEvent); + return -1; + } + DWORD num_read; + if (GetOverlappedResult(m_file_handle, &ol, &num_read, false) == 0) + { + DWORD last_error = GetLastError(); + if (last_error != ERROR_HANDLE_EOF) + { +#ifdef ERROR_CANT_WAIT + TORRENT_ASSERT(last_error != ERROR_CANT_WAIT); +#endif + ec.assign(last_error, get_system_category()); + CloseHandle(ol.hEvent); + return -1; + } + } + if (num_read < ret) ret = num_read; + } + CloseHandle(ol.hEvent); + return ret; + +#else // TORRENT_WINDOWS + + size_type ret = lseek(m_fd, file_offset, SEEK_SET); + if (ret < 0) + { + ec.assign(errno, get_posix_category()); + return -1; + } +#if TORRENT_USE_READV + + ret = 0; + while (num_bufs > 0) + { + int nbufs = (std::min)(num_bufs, TORRENT_IOV_MAX); + int tmp_ret = 0; +#ifdef TORRENT_LINUX + bool aligned = false; + int size = 0; + // if we're not opened in no-buffer mode, we don't need alignment + if ((m_open_mode & no_buffer) == 0) aligned = true; + if (!aligned) + { + size = bufs_size(bufs, nbufs); + if ((size & (size_alignment()-1)) == 0) aligned = true; + } + if (aligned) +#endif // TORRENT_LINUX + { + tmp_ret = ::readv(m_fd, bufs, nbufs); + if (tmp_ret < 0) + { + ec.assign(errno, get_posix_category()); + return -1; + } + ret += tmp_ret; + } +#ifdef TORRENT_LINUX + else + { + file::iovec_t* temp_bufs = TORRENT_ALLOCA(file::iovec_t, nbufs); + memcpy(temp_bufs, bufs, sizeof(file::iovec_t) * nbufs); + iovec_t& last = temp_bufs[nbufs-1]; + last.iov_len = (last.iov_len & ~(size_alignment()-1)) + m_page_size; + tmp_ret = ::readv(m_fd, temp_bufs, nbufs); + if (tmp_ret < 0) + { + ec.assign(errno, get_posix_category()); + return -1; + } + ret += (std::min)(tmp_ret, size); + } +#endif // TORRENT_LINUX + + num_bufs -= nbufs; + bufs += nbufs; + } + + return ret; + +#else // TORRENT_USE_READV + + ret = 0; + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + int tmp = read(m_fd, i->iov_base, i->iov_len); + if (tmp < 0) + { + ec.assign(errno, get_posix_category()); + return -1; + } + ret += tmp; + if (tmp < i->iov_len) break; + } + return ret; + +#endif // TORRENT_USE_READV + +#endif // TORRENT_WINDOWS + } + + size_type file::writev(size_type file_offset, iovec_t const* bufs, int num_bufs, error_code& ec) + { +#ifdef TORRENT_WINDOWS + if (m_file_handle == INVALID_HANDLE_VALUE) + { + ec = error_code(ERROR_INVALID_HANDLE, get_system_category()); + return -1; + } +#else + if (m_fd == -1) + { + ec = error_code(EBADF, get_system_category()); + return -1; + } +#endif + TORRENT_ASSERT((m_open_mode & rw_mask) == write_only || (m_open_mode & rw_mask) == read_write); + TORRENT_ASSERT(bufs); + TORRENT_ASSERT(num_bufs > 0); + TORRENT_ASSERT(is_open()); + +#if defined TORRENT_WINDOWS || defined TORRENT_LINUX || defined TORRENT_DEBUG + // make sure m_page_size is initialized + init_file(); +#endif + +#ifdef TORRENT_DEBUG + if (m_open_mode & no_buffer) + { + bool eof = false; + int size = 0; + // when opened in no_buffer mode, the file_offset must + // be aligned to pos_alignment() + TORRENT_ASSERT((file_offset & (pos_alignment()-1)) == 0); + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + TORRENT_ASSERT((uintptr_t(i->iov_base) & (buf_alignment()-1)) == 0); + // every buffer must be a multiple of the page size + // except for the last one + TORRENT_ASSERT((i->iov_len & (size_alignment()-1)) == 0 || i == end-1); + if ((i->iov_len & (size_alignment()-1)) != 0) eof = true; + size += i->iov_len; + } + error_code code; + if (eof) TORRENT_ASSERT(file_offset + size >= get_size(code)); + } +#endif + +#ifdef TORRENT_WINDOWS + + DWORD ret = 0; + + // since the ReadFileScatter requires the file to be opened + // with no buffering, and no buffering requires page aligned + // buffers, open the file in non-buffered mode in case the + // buffer is not aligned. Most of the times the buffer should + // be aligned though + + if ((m_open_mode & no_buffer) == 0) + { + // this means the buffer base or the buffer size is not aligned + // to the page size. Use a regular file for this operation. + + LARGE_INTEGER offs; + offs.QuadPart = file_offset; + if (SetFilePointerEx(m_file_handle, offs, &offs, FILE_BEGIN) == FALSE) + { + ec.assign(GetLastError(), get_system_category()); + return -1; + } + + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + DWORD intermediate = 0; + if (WriteFile(m_file_handle, (char const*)i->iov_base + , (DWORD)i->iov_len, &intermediate, 0) == FALSE) + { + ec.assign(GetLastError(), get_system_category()); + return -1; + } + ret += intermediate; + } + return ret; + } + + int size = bufs_size(bufs, num_bufs); + // number of pages for the write. round up + int num_pages = (size + m_page_size - 1) / m_page_size; + // allocate array of FILE_SEGMENT_ELEMENT for WriteFileGather + FILE_SEGMENT_ELEMENT* segment_array = TORRENT_ALLOCA(FILE_SEGMENT_ELEMENT, num_pages + 1); +#ifdef __GNUC__ + // MingW seems to have issues with 64 bit wide pointers + // (PVOID64) and only assign the low 32 bits. Therefore, make + // sure the other 32 bits are cleared out + memset(segment_array, 0, (num_pages + 1) * sizeof(FILE_SEGMENT_ELEMENT)); +#endif + FILE_SEGMENT_ELEMENT* cur_seg = segment_array; + + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + for (int k = 0; k < int(i->iov_len); k += m_page_size) + { + cur_seg->Buffer = PtrToPtr64((((char*)i->iov_base) + k)); + ++cur_seg; + } + } + // terminate the array + cur_seg->Buffer = 0; + + OVERLAPPED ol; + memset(&ol, 0, sizeof(ol)); + ol.Internal = 0; + ol.InternalHigh = 0; + ol.OffsetHigh = DWORD(file_offset >> 32); + ol.Offset = DWORD(file_offset & 0xffffffff); + ol.hEvent = CreateEvent(0, true, false, 0); + if (ol.hEvent == NULL) + { + ec.assign(GetLastError(), get_system_category()); + return -1; + } + + ret += size; + size_type file_size = 0; + + if ((size & (m_page_size-1)) != 0) + { + // if size is not an even multiple, this must be the tail + // of the file. + + file_size = file_offset + size; + size = num_pages * m_page_size; + } + + if (WriteFileGather(m_file_handle, segment_array, size, 0, &ol) == 0) + { + DWORD last_error = GetLastError(); + if (last_error != ERROR_IO_PENDING +#ifdef ERROR_CANT_WAIT + && last_error != ERROR_CANT_WAIT +#endif + ) + { + TORRENT_ASSERT(last_error != ERROR_BAD_ARGUMENTS); + ec.assign(last_error, get_system_category()); + CloseHandle(ol.hEvent); + return -1; + } + if (WaitForSingleObject(ol.hEvent, INFINITE) == WAIT_FAILED) + { + ec.assign(GetLastError(), get_system_category()); + CloseHandle(ol.hEvent); + return -1; + } + DWORD num_written; + if (GetOverlappedResult(m_file_handle, &ol, &num_written, false) == 0) + { + DWORD last_error = GetLastError(); +#ifdef ERROR_CANT_WAIT + TORRENT_ASSERT(last_error != ERROR_CANT_WAIT); +#endif + ec.assign(last_error, get_system_category()); + CloseHandle(ol.hEvent); + return -1; + } + if (num_written < ret) ret = num_written; + } + CloseHandle(ol.hEvent); + if (file_size > 0) set_size(file_size, ec); + return ret; +#else + size_type ret = lseek(m_fd, file_offset, SEEK_SET); + if (ret < 0) + { + ec.assign(errno, get_posix_category()); + return -1; + } + +#if TORRENT_USE_WRITEV + + ret = 0; + while (num_bufs > 0) + { + int nbufs = (std::min)(num_bufs, TORRENT_IOV_MAX); + int tmp_ret = 0; +#ifdef TORRENT_LINUX + bool aligned = false; + int size = 0; + // if we're not opened in no-buffer mode, we don't need alignment + if ((m_open_mode & no_buffer) == 0) aligned = true; + if (!aligned) + { + size = bufs_size(bufs, nbufs); + if ((size & (size_alignment()-1)) == 0) aligned = true; + } + if (aligned) +#endif + { + tmp_ret = ::writev(m_fd, bufs, nbufs); + if (tmp_ret < 0) + { + ec.assign(errno, get_posix_category()); + return -1; + } + ret += tmp_ret; + } +#ifdef TORRENT_LINUX + else + { + file::iovec_t* temp_bufs = TORRENT_ALLOCA(file::iovec_t, nbufs); + memcpy(temp_bufs, bufs, sizeof(file::iovec_t) * nbufs); + iovec_t& last = temp_bufs[nbufs-1]; + last.iov_len = (last.iov_len & ~(size_alignment()-1)) + size_alignment(); + tmp_ret = ::writev(m_fd, temp_bufs, nbufs); + if (tmp_ret < 0) + { + ec.assign(errno, get_posix_category()); + return -1; + } + if (ftruncate(m_fd, file_offset + size) < 0) + { + ec.assign(errno, get_posix_category()); + return -1; + } + ret += (std::min)(tmp_ret, size); + } +#endif // TORRENT_LINUX + + num_bufs -= nbufs; + bufs += nbufs; + } + + return ret; + +#else // TORRENT_USE_WRITEV + + ret = 0; + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + int tmp = write(m_fd, i->iov_base, i->iov_len); + if (tmp < 0) + { + ec.assign(errno, get_posix_category()); + return -1; + } + ret += tmp; + if (tmp < i->iov_len) break; + } + return ret; + +#endif // TORRENT_USE_WRITEV + +#endif // TORRENT_WINDOWS + } + + size_type file::phys_offset(size_type offset) + { +#ifdef FIEMAP_EXTENT_UNKNOWN + // for documentation of this feature + // http://lwn.net/Articles/297696/ + struct + { + struct fiemap fiemap; + struct fiemap_extent extent; + } fm; + + memset(&fm, 0, sizeof(fm)); + fm.fiemap.fm_start = offset; + fm.fiemap.fm_length = size_alignment(); + // this sounds expensive + fm.fiemap.fm_flags = FIEMAP_FLAG_SYNC; + fm.fiemap.fm_extent_count = 1; + + if (ioctl(m_fd, FS_IOC_FIEMAP, &fm) == -1) + return 0; + + if (fm.fiemap.fm_mapped_extents != 1) + return 0; + + if (fm.fiemap.fm_extents[0].fe_flags & FIEMAP_EXTENT_UNKNOWN) + return 0; + + // the returned extent is not guaranteed to start + // at the requested offset, adjust for that in + // case they differ + TORRENT_ASSERT(offset >= fm.fiemap.fm_extents[0].fe_logical); + return fm.fiemap.fm_extents[0].fe_physical + (offset - fm.fiemap.fm_extents[0].fe_logical); + +#elif defined F_LOG2PHYS + // for documentation of this feature + // http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man2/fcntl.2.html + + log2phys l; + size_type ret = lseek(m_fd, offset, SEEK_SET); + if (ret < 0) return 0; + if (fcntl(m_fd, F_LOG2PHYS, &l) == -1) return 0; + return l.l2p_devoffset; +#elif defined TORRENT_WINDOWS + // for documentation of this feature + // http://msdn.microsoft.com/en-us/library/aa364572(VS.85).aspx + STARTING_VCN_INPUT_BUFFER in; + RETRIEVAL_POINTERS_BUFFER out; + DWORD out_bytes; + + // query cluster size + pos_alignment(); + in.StartingVcn.QuadPart = offset / m_cluster_size; + int cluster_offset = int(in.StartingVcn.QuadPart % m_cluster_size); + + if (DeviceIoControl(m_file_handle, FSCTL_GET_RETRIEVAL_POINTERS, &in + , sizeof(in), &out, sizeof(out), &out_bytes, 0) == 0) + { + DWORD error = GetLastError(); + TORRENT_ASSERT(error != ERROR_INVALID_PARAMETER); + + // insufficient buffer error is expected, but we're + // only interested in the first extent anyway + if (error != ERROR_MORE_DATA) return 0; + } + if (out_bytes < sizeof(out)) return 0; + if (out.ExtentCount == 0) return 0; + if (out.Extents[0].Lcn.QuadPart == (LONGLONG)-1) return 0; + TORRENT_ASSERT(in.StartingVcn.QuadPart >= out.StartingVcn.QuadPart); + return (out.Extents[0].Lcn.QuadPart + + (in.StartingVcn.QuadPart - out.StartingVcn.QuadPart)) + * m_cluster_size + cluster_offset; +#endif + return 0; + } + +#ifdef TORRENT_WINDOWS + bool get_manage_volume_privs() + { + typedef BOOL (WINAPI *OpenProcessToken_t)( + HANDLE ProcessHandle, + DWORD DesiredAccess, + PHANDLE TokenHandle); + + typedef BOOL (WINAPI *LookupPrivilegeValue_t)( + LPCSTR lpSystemName, + LPCSTR lpName, + PLUID lpLuid); + + typedef BOOL (WINAPI *AdjustTokenPrivileges_t)( + HANDLE TokenHandle, + BOOL DisableAllPrivileges, + PTOKEN_PRIVILEGES NewState, + DWORD BufferLength, + PTOKEN_PRIVILEGES PreviousState, + PDWORD ReturnLength); + + static OpenProcessToken_t pOpenProcessToken = NULL; + static LookupPrivilegeValue_t pLookupPrivilegeValue = NULL; + static AdjustTokenPrivileges_t pAdjustTokenPrivileges = NULL; + static bool failed_advapi = false; + + if (pOpenProcessToken == NULL && !failed_advapi) + { + HMODULE advapi = LoadLibraryA("advapi32"); + if (advapi == NULL) + { + failed_advapi = true; + return false; + } + pOpenProcessToken = (OpenProcessToken_t)GetProcAddress(advapi, "OpenProcessToken"); + pLookupPrivilegeValue = (LookupPrivilegeValue_t)GetProcAddress(advapi, "LookupPrivilegeValueA"); + pAdjustTokenPrivileges = (AdjustTokenPrivileges_t)GetProcAddress(advapi, "AdjustTokenPrivileges"); + if (pOpenProcessToken == NULL + || pLookupPrivilegeValue == NULL + || pAdjustTokenPrivileges == NULL) + { + failed_advapi = true; + return false; + } + } + + HANDLE token; + if (!pOpenProcessToken(GetCurrentProcess() + , TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) + return false; + + TOKEN_PRIVILEGES privs; + if (!pLookupPrivilegeValue(NULL, "SeManageVolumePrivilege" + , &privs.Privileges[0].Luid)) + { + CloseHandle(token); + return false; + } + + privs.PrivilegeCount = 1; + privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + bool ret = pAdjustTokenPrivileges(token, FALSE, &privs, 0, NULL, NULL) + && GetLastError() == ERROR_SUCCESS; + + CloseHandle(token); + + return ret; + } + + void set_file_valid_data(HANDLE f, boost::int64_t size) + { + typedef BOOL (WINAPI *SetFileValidData_t)(HANDLE, LONGLONG); + static SetFileValidData_t pSetFileValidData = NULL; + static bool failed_kernel32 = false; + + if (pSetFileValidData == NULL && !failed_kernel32) + { + HMODULE k32 = LoadLibraryA("kernel32"); + if (k32 == NULL) + { + failed_kernel32 = true; + return; + } + pSetFileValidData = (SetFileValidData_t)GetProcAddress(k32, "SetFileValidData"); + if (pSetFileValidData == NULL) + { + failed_kernel32 = true; + return; + } + } + + TORRENT_ASSERT(pSetFileValidData); + + // we don't necessarily expect to have enough + // privilege to do this, so ignore errors. + pSetFileValidData(f, size); + } +#endif + + bool file::set_size(size_type s, error_code& ec) + { + TORRENT_ASSERT(is_open()); + TORRENT_ASSERT(s >= 0); + +#ifdef TORRENT_WINDOWS + + if ((m_open_mode & no_buffer) && (s & (size_alignment()-1)) != 0) + { + // the file is opened in unbuffered mode, and the size is not + // aligned to the required cluster size. Use NtSetInformationFile + +#define FileEndOfFileInformation 20 +#ifndef NT_SUCCESS +#define NT_SUCCESS(x) (!((x) & 0x80000000)) +#endif + + // for NtSetInformationFile, see: + // http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/File/NtSetInformationFile.html + + typedef DWORD _NTSTATUS; + typedef _NTSTATUS (NTAPI * NtSetInformationFile_t)(HANDLE file, PULONG_PTR iosb, PVOID data, ULONG len, ULONG file_info_class); + + static NtSetInformationFile_t NtSetInformationFile = 0; + static bool failed_ntdll = false; + + if (NtSetInformationFile == 0 && !failed_ntdll) + { + HMODULE nt = LoadLibraryA("ntdll"); + if (nt) + { + NtSetInformationFile = (NtSetInformationFile_t)GetProcAddress(nt, "NtSetInformationFile"); + if (NtSetInformationFile == 0) failed_ntdll = true; + } + else failed_ntdll = true; + } + + if (!failed_ntdll && NtSetInformationFile) + { + ULONG_PTR Iosb[2]; + LARGE_INTEGER fsize; + fsize.QuadPart = s; + _NTSTATUS st = NtSetInformationFile(m_file_handle + , Iosb, &fsize, sizeof(fsize), FileEndOfFileInformation); + if (!NT_SUCCESS(st)) + { + ec.assign(INVALID_SET_FILE_POINTER, get_system_category()); + return false; + } + + if ((m_open_mode & sparse) == 0) + set_file_valid_data(m_file_handle, s); + + return true; + } + + // couldn't find ntdll or NtSetFileInformation function + // and the file is opened in unbuffered mode! There's + // nothing we can do! (short of re-opening the file, but + // that introduces all sorts of nasty race conditions) + return false; + } + + LARGE_INTEGER offs; + LARGE_INTEGER cur_size; + if (GetFileSizeEx(m_file_handle, &cur_size) == FALSE) + { + ec.assign(GetLastError(), get_system_category()); + return false; + } + offs.QuadPart = s; + // only set the file size if it's not already at + // the right size. We don't want to update the + // modification time if we don't have to + if (cur_size.QuadPart != s) + { + if (SetFilePointerEx(m_file_handle, offs, &offs, FILE_BEGIN) == FALSE) + { + ec.assign(GetLastError(), get_system_category()); + return false; + } + if (::SetEndOfFile(m_file_handle) == FALSE) + { + ec.assign(GetLastError(), get_system_category()); + return false; + } + } + + if ((m_open_mode & sparse) == 0) + { +#if TORRENT_USE_WSTRING + typedef DWORD (WINAPI *GetCompressedFileSize_t)(LPCWSTR lpFileName, LPDWORD lpFileSizeHigh); +#else + typedef DWORD (WINAPI *GetCompressedFileSize_t)(LPCSTR lpFileName, LPDWORD lpFileSizeHigh); +#endif + + static GetCompressedFileSize_t GetCompressedFileSize_ = NULL; + + static bool failed_kernel32 = false; + + if ((GetCompressedFileSize_ == NULL) && !failed_kernel32) + { + HMODULE kernel32 = LoadLibraryA("kernel32.dll"); + if (kernel32) + { +#if TORRENT_USE_WSTRING + GetCompressedFileSize_ = (GetCompressedFileSize_t)GetProcAddress(kernel32, "GetCompressedFileSizeW"); +#else + GetCompressedFileSize_ = (GetCompressedFileSize_t)GetProcAddress(kernel32, "GetCompressedFileSizeA"); +#endif + } + else + { + failed_kernel32 = true; + } + } + + offs.QuadPart = 0; + if (GetCompressedFileSize_) + { + // only allocate the space if the file + // is not fully allocated + DWORD high_dword = 0; + offs.LowPart = GetCompressedFileSize_(m_path.c_str(), &high_dword); + offs.HighPart = high_dword; + if (offs.LowPart == INVALID_FILE_SIZE) + { + ec.assign(GetLastError(), get_system_category()); + if (ec) return false; + } + } + + if (offs.QuadPart != s) + { + // if the user has permissions, avoid filling + // the file with zeroes, but just fill it with + // garbage instead + set_file_valid_data(m_file_handle, s); + } + } +#else // NON-WINDOWS + struct stat st; + if (fstat(m_fd, &st) != 0) + { + ec.assign(errno, get_posix_category()); + return false; + } + + // only truncate the file if it doesn't already + // have the right size. We don't want to update + if (st.st_size != s && ftruncate(m_fd, s) < 0) + { + ec.assign(errno, get_posix_category()); + return false; + } + + // if we're not in sparse mode, allocate the storage + // but only if the number of allocated blocks for the file + // is less than the file size. Otherwise we would just + // update the modification time of the file for no good + // reason. + if ((m_open_mode & sparse) == 0 + && st.st_blocks < (s + st.st_blksize - 1) / st.st_blksize) + { + // How do we know that the file is already allocated? + // if we always try to allocate the space, we'll update + // the modification time without actually changing the file + // but if we don't do anything if the file size is +#ifdef F_PREALLOCATE + fstore_t f = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, s, 0}; + if (fcntl(m_fd, F_PREALLOCATE, &f) < 0) + { + if (errno != ENOSPC) + { + ec.assign(errno, get_posix_category()); + return false; + } + // ok, let's try to allocate non contiguous space then + fstore_t f = {F_ALLOCATEALL, F_PEOFPOSMODE, 0, s, 0}; + if (fcntl(m_fd, F_PREALLOCATE, &f) < 0) + { + ec.assign(errno, get_posix_category()); + return false; + } + } +#endif // F_PREALLOCATE + +#ifdef F_ALLOCSP64 + flock64 fl64; + fl64.l_whence = SEEK_SET; + fl64.l_start = 0; + fl64.l_len = s; + if (fcntl(native_handle(), F_ALLOCSP64, &fl64) < 0) + { + ec.assign(errno, get_posix_category()); + return false; + } + +#endif // F_ALLOCSP64 + +#if defined TORRENT_LINUX || TORRENT_HAS_FALLOCATE + int ret; +#endif + +#if defined TORRENT_LINUX + ret = my_fallocate(m_fd, 0, 0, s); + // if we return 0, everything went fine + // the fallocate call succeeded + if (ret == 0) return true; + // otherwise, something went wrong. If the error + // is ENOSYS, just keep going and do it the old-fashioned + // way. If fallocate failed with some other error, it + // probably means the user should know about it, error out + // and report it. + if (errno != ENOSYS && errno != EOPNOTSUPP && errno != EINVAL) + { + ec.assign(errno, get_posix_category()); + return false; + } +#endif // TORRENT_LINUX + +#if TORRENT_HAS_FALLOCATE + // if fallocate failed, we have to use posix_fallocate + // which can be painfully slow + // if you get a compile error here, you might want to + // define TORRENT_HAS_FALLOCATE to 0. + ret = posix_fallocate(m_fd, 0, s); + // posix_allocate fails with EINVAL in case the underlying + // filesystem does bot support this operation + if (ret != 0 && ret != EINVAL) + { + ec.assign(ret, get_posix_category()); + return false; + } +#endif // TORRENT_HAS_FALLOCATE + } +#endif // TORRENT_WINDOWS + return true; + } + + size_type file::get_size(error_code& ec) const + { +#ifdef TORRENT_WINDOWS + LARGE_INTEGER file_size; + if (!GetFileSizeEx(m_file_handle, &file_size)) + { + ec.assign(GetLastError(), get_system_category()); + return -1; + } + return file_size.QuadPart; +#else + struct stat fs; + if (fstat(m_fd, &fs) != 0) + { + ec.assign(errno, get_posix_category()); + return -1; + } + return fs.st_size; +#endif + } + + size_type file::sparse_end(size_type start) const + { +#ifdef TORRENT_WINDOWS +#ifdef TORRENT_MINGW +typedef struct _FILE_ALLOCATED_RANGE_BUFFER { + LARGE_INTEGER FileOffset; + LARGE_INTEGER Length; +} FILE_ALLOCATED_RANGE_BUFFER, *PFILE_ALLOCATED_RANGE_BUFFER; +#define FSCTL_QUERY_ALLOCATED_RANGES ((0x9 << 16) | (1 << 14) | (51 << 2) | 3) +#endif + FILE_ALLOCATED_RANGE_BUFFER buffer; + DWORD bytes_returned = 0; + FILE_ALLOCATED_RANGE_BUFFER in; + error_code ec; + size_type file_size = get_size(ec); + if (ec) return start; + + if (m_open_mode & no_buffer) + { + boost::uint64_t mask = size_alignment()-1; + in.FileOffset.QuadPart = start & (~mask); + in.Length.QuadPart = ((file_size + mask) & ~mask) - in.FileOffset.QuadPart; + TORRENT_ASSERT((in.Length.QuadPart & mask) == 0); + } + else + { + in.FileOffset.QuadPart = start; + in.Length.QuadPart = file_size - start; + } + + if (!DeviceIoControl(m_file_handle, FSCTL_QUERY_ALLOCATED_RANGES + , &in, sizeof(FILE_ALLOCATED_RANGE_BUFFER) + , &buffer, sizeof(FILE_ALLOCATED_RANGE_BUFFER), &bytes_returned, 0)) + { + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return start; + } + + // if there are no allocated regions within the rest + // of the file, return the end of the file + if (bytes_returned == 0) return file_size; + + // assume that this range overlaps the start of the + // region we were interested in, and that start actually + // resides in an allocated region. + if (buffer.FileOffset.QuadPart < start) return start; + + // return the offset to the next allocated region + return buffer.FileOffset.QuadPart; + +#elif defined SEEK_DATA + // this is supported on solaris + size_type ret = lseek(m_fd, start, SEEK_DATA); + if (ret < 0) return start; + return start; +#else + return start; +#endif + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/file_pool.cpp b/apps/Launcher/ext/libtorrent/src/file_pool.cpp new file mode 100644 index 0000000000..a53f2e9374 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/file_pool.cpp @@ -0,0 +1,326 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include "libtorrent/assert.hpp" +#include "libtorrent/file_pool.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/file_storage.hpp" // for file_entry + +namespace libtorrent +{ + + file_pool::file_pool(int size) + : m_size(size) + , m_low_prio_io(true) +#if TORRENT_CLOSE_MAY_BLOCK + , m_stop_thread(false) + , m_closer_thread(boost::bind(&file_pool::closer_thread_fun, this)) +#endif + { + } + + file_pool::~file_pool() + { +#if TORRENT_CLOSE_MAY_BLOCK + mutex::scoped_lock l(m_closer_mutex); + m_stop_thread = true; + l.unlock(); + // wait for hte closer thread to finish closing all files + m_closer_thread.join(); +#endif + } + +#if TORRENT_CLOSE_MAY_BLOCK + void file_pool::closer_thread_fun() + { + for (;;) + { + mutex::scoped_lock l(m_closer_mutex); + if (m_stop_thread) + { + l.unlock(); + m_queued_for_close.clear(); + return; + } + + // find a file that doesn't have any other threads referencing + // it. Since only those files can be closed in this thead + std::vector >::iterator i = std::find_if( + m_queued_for_close.begin(), m_queued_for_close.end() + , boost::bind(&file::refcount, boost::bind(&boost::intrusive_ptr::get, _1)) == 1); + + if (i == m_queued_for_close.end()) + { + l.unlock(); + // none of the files are ready to be closed yet + // because they're still in use by other threads + // hold off for a while + sleep(100); + } + else + { + // ok, first pull the file out of the queue, release the mutex + // (since closing the file may block) and then close it. + boost::intrusive_ptr f = *i; + m_queued_for_close.erase(i); + l.unlock(); + f->close(); + } + } + } +#endif + +#ifdef TORRENT_WINDOWS + void set_low_priority(boost::intrusive_ptr const& f) + { + // file prio is only supported on vista and up + // so load the functions dynamically + typedef enum _FILE_INFO_BY_HANDLE_CLASS { + FileBasicInfo, + FileStandardInfo, + FileNameInfo, + FileRenameInfo, + FileDispositionInfo, + FileAllocationInfo, + FileEndOfFileInfo, + FileStreamInfo, + FileCompressionInfo, + FileAttributeTagInfo, + FileIdBothDirectoryInfo, + FileIdBothDirectoryRestartInfo, + FileIoPriorityHintInfo, + FileRemoteProtocolInfo, + MaximumFileInfoByHandleClass + } FILE_INFO_BY_HANDLE_CLASS, *PFILE_INFO_BY_HANDLE_CLASS; + + typedef enum _PRIORITY_HINT { + IoPriorityHintVeryLow = 0, + IoPriorityHintLow, + IoPriorityHintNormal, + MaximumIoPriorityHintType + } PRIORITY_HINT; + + typedef struct _FILE_IO_PRIORITY_HINT_INFO { + PRIORITY_HINT PriorityHint; + } FILE_IO_PRIORITY_HINT_INFO, *PFILE_IO_PRIORITY_HINT_INFO; + + typedef BOOL (WINAPI *SetFileInformationByHandle_t)(HANDLE hFile, FILE_INFO_BY_HANDLE_CLASS FileInformationClass, LPVOID lpFileInformation, DWORD dwBufferSize); + static SetFileInformationByHandle_t SetFileInformationByHandle = NULL; + + static bool failed_kernel_load = false; + + if (failed_kernel_load) return; + + if (SetFileInformationByHandle == NULL) + { + HMODULE kernel32 = LoadLibraryA("kernel32.dll"); + if (kernel32 == NULL) + { + failed_kernel_load = true; + return; + } + + SetFileInformationByHandle = (SetFileInformationByHandle_t)GetProcAddress(kernel32, "SetFileInformationByHandle"); + if (SetFileInformationByHandle == NULL) + { + failed_kernel_load = true; + return; + } + } + + TORRENT_ASSERT(SetFileInformationByHandle); + + FILE_IO_PRIORITY_HINT_INFO io_hint; + io_hint.PriorityHint = IoPriorityHintLow; + SetFileInformationByHandle(f->native_handle(), + FileIoPriorityHintInfo, &io_hint, sizeof(io_hint)); + } +#endif // TORRENT_WINDOWS + + boost::intrusive_ptr file_pool::open_file(void* st, std::string const& p + , int file_index, file_storage const& fs, int m, error_code& ec) + { + TORRENT_ASSERT(st != 0); + TORRENT_ASSERT(is_complete(p)); + TORRENT_ASSERT((m & file::rw_mask) == file::read_only + || (m & file::rw_mask) == file::read_write); + mutex::scoped_lock l(m_mutex); + file_set::iterator i = m_files.find(std::make_pair(st, file_index)); + if (i != m_files.end()) + { + lru_file_entry& e = i->second; + e.last_use = time_now(); + + if (e.key != st && ((e.mode & file::rw_mask) != file::read_only + || (m & file::rw_mask) != file::read_only)) + { + // this means that another instance of the storage + // is using the exact same file. +#if BOOST_VERSION >= 103500 + ec = errors::file_collision; +#endif + return boost::intrusive_ptr(); + } + + e.key = st; + // if we asked for a file in write mode, + // and the cached file is is not opened in + // write mode, re-open it + if ((((e.mode & file::rw_mask) != file::read_write) + && ((m & file::rw_mask) == file::read_write)) + || (e.mode & file::no_buffer) != (m & file::no_buffer) + || (e.mode & file::random_access) != (m & file::random_access)) + { + // close the file before we open it with + // the new read/write privilages + TORRENT_ASSERT(e.file_ptr->refcount() == 1); + +#if TORRENT_CLOSE_MAY_BLOCK + mutex::scoped_lock l(m_closer_mutex); + m_queued_for_close.push_back(e.file_ptr); + l.unlock(); + e.file_ptr = new file; +#else + e.file_ptr->close(); +#endif + std::string full_path = fs.file_path(file_index, p); + if (!e.file_ptr->open(full_path, m, ec)) + { + m_files.erase(i); + return boost::intrusive_ptr(); + } +#ifdef TORRENT_WINDOWS + if (m_low_prio_io) + set_low_priority(e.file_ptr); +#endif + TORRENT_ASSERT(e.file_ptr->is_open()); + e.mode = m; + } + TORRENT_ASSERT((e.mode & file::no_buffer) == (m & file::no_buffer)); + return e.file_ptr; + } + // the file is not in our cache + if ((int)m_files.size() >= m_size) + { + // the file cache is at its maximum size, close + // the least recently used (lru) file from it + remove_oldest(); + } + lru_file_entry e; + e.file_ptr.reset(new (std::nothrow)file); + if (!e.file_ptr) + { + ec = error_code(ENOMEM, get_posix_category()); + return e.file_ptr; + } + std::string full_path = fs.file_path(file_index, p); + if (!e.file_ptr->open(full_path, m, ec)) + return boost::intrusive_ptr(); +#ifdef TORRENT_WINDOWS + if (m_low_prio_io) + set_low_priority(e.file_ptr); +#endif + e.mode = m; + e.key = st; + m_files.insert(std::make_pair(std::make_pair(st, file_index), e)); + TORRENT_ASSERT(e.file_ptr->is_open()); + return e.file_ptr; + } + + void file_pool::remove_oldest() + { + file_set::iterator i = std::min_element(m_files.begin(), m_files.end() + , boost::bind(&lru_file_entry::last_use, boost::bind(&file_set::value_type::second, _1)) + < boost::bind(&lru_file_entry::last_use, boost::bind(&file_set::value_type::second, _2))); + if (i == m_files.end()) return; + +#if TORRENT_CLOSE_MAY_BLOCK + mutex::scoped_lock l_(m_closer_mutex); + m_queued_for_close.push_back(i->second.file_ptr); + l_.unlock(); +#endif + m_files.erase(i); + } + + void file_pool::release(void* st, int file_index) + { + mutex::scoped_lock l(m_mutex); + file_set::iterator i = m_files.find(std::make_pair(st, file_index)); + if (i == m_files.end()) return; + +#if TORRENT_CLOSE_MAY_BLOCK + mutex::scoped_lock l2(m_closer_mutex); + m_queued_for_close.push_back(i->second.file_ptr); + l2.unlock(); +#endif + m_files.erase(i); + } + + // closes files belonging to the specified + // storage. If 0 is passed, all files are closed + void file_pool::release(void* st) + { + mutex::scoped_lock l(m_mutex); + if (st == 0) + { + m_files.clear(); + return; + } + + for (file_set::iterator i = m_files.begin(); + i != m_files.end();) + { + if (i->second.key == st) + m_files.erase(i++); + else + ++i; + } + } + + void file_pool::resize(int size) + { + TORRENT_ASSERT(size > 0); + + if (size == m_size) return; + mutex::scoped_lock l(m_mutex); + m_size = size; + if (int(m_files.size()) <= m_size) return; + + // close the least recently used files + while (int(m_files.size()) > m_size) + remove_oldest(); + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/file_storage.cpp b/apps/Launcher/ext/libtorrent/src/file_storage.cpp new file mode 100644 index 0000000000..a70a7fb6d7 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/file_storage.cpp @@ -0,0 +1,831 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/file_storage.hpp" +#include "libtorrent/string_util.hpp" // for allocate_string_copy +#include "libtorrent/file.hpp" +#include "libtorrent/utf8.hpp" +#include +#include +#include + +namespace libtorrent +{ + file_storage::file_storage() + : m_total_size(0) + , m_num_pieces(0) + , m_piece_length(0) + {} + + file_storage::~file_storage() {} + + // even though this copy constructor and the copy assignment + // operator are identical to what the compiler would have + // generated, they are put here to explicitly make them part + // of libtorrent and properly exported by the .dll. + file_storage::file_storage(file_storage const& f) + : m_files(f.m_files) + , m_file_hashes(f.m_file_hashes) + , m_symlinks(f.m_symlinks) + , m_mtime(f.m_mtime) + , m_file_base(f.m_file_base) + , m_paths(f.m_paths) + , m_name(f.m_name) + , m_total_size(f.m_total_size) + , m_num_pieces(f.m_num_pieces) + , m_piece_length(f.m_piece_length) + { + } + + file_storage& file_storage::operator=(file_storage const& f) + { + m_files = f.m_files; + m_file_hashes = f.m_file_hashes; + m_symlinks = f.m_symlinks; + m_mtime = f.m_mtime; + m_file_base = f.m_file_base; + m_paths = f.m_paths; + m_name = f.m_name; + m_total_size = f.m_total_size; + m_num_pieces = f.m_num_pieces; + m_piece_length = f.m_piece_length; + return *this; + } + + void file_storage::reserve(int num_files) + { + m_files.reserve(num_files); + } + + int file_storage::piece_size(int index) const + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < num_pieces()); + if (index == num_pieces()-1) + { + size_type size_except_last = num_pieces() - 1; + size_except_last *= size_type(piece_length()); + size_type size = total_size() - size_except_last; + TORRENT_ASSERT(size > 0); + TORRENT_ASSERT(size <= piece_length()); + return int(size); + } + else + return piece_length(); + } + + void file_storage::update_path_index(internal_file_entry& e) + { + std::string fname = e.filename(); + if (is_complete(fname)) + { + e.path_index = -2; + return; + } + std::string parent = parent_path(fname); + + if (parent.empty()) + { + e.path_index = -1; + return; + } + + if (parent.size() >= m_name.size() + && parent.compare(0, m_name.size(), m_name) == 0 + && (parent.size() == m_name.size() +#ifdef TORRENT_WINDOWS + || parent[m_name.size()] == '\\' +#endif + || parent[m_name.size()] == '/' + )) + { + parent.erase(parent.begin(), parent.begin() + m_name.size() + + (m_name.size() == parent.size()?0:1)); + e.no_root_dir = false; + } + else + { + e.no_root_dir = true; + } + + // do we already have this path in the path list? + std::vector::reverse_iterator p + = std::find(m_paths.rbegin(), m_paths.rend(), parent); + + if (p == m_paths.rend()) + { + // no, we don't. add it + e.path_index = m_paths.size(); + m_paths.push_back(parent); + } + else + { + // yes we do. use it + e.path_index = p.base() - m_paths.begin() - 1; + } + e.set_name(filename(e.filename()).c_str()); + } + + file_entry::file_entry(): offset(0), size(0), file_base(0) + , mtime(0), pad_file(false), hidden_attribute(false) + , executable_attribute(false) + , symlink_attribute(false) + {} + + file_entry::~file_entry() {} + + internal_file_entry::~internal_file_entry() + { + if (name_len == name_is_owned) free((void*)name); + } + + internal_file_entry::internal_file_entry(internal_file_entry const& fe) + : offset(fe.offset) + , symlink_index(fe.symlink_index) + , no_root_dir(fe.no_root_dir) + , size(fe.size) + , name_len(fe.name_len) + , pad_file(fe.pad_file) + , hidden_attribute(fe.hidden_attribute) + , executable_attribute(fe.executable_attribute) + , symlink_attribute(fe.symlink_attribute) + , name(0) + , path_index(fe.path_index) + { + set_name(fe.filename().c_str()); + } + + internal_file_entry& internal_file_entry::operator=(internal_file_entry const& fe) + { + offset = fe.offset; + size = fe.size; + path_index = fe.path_index; + symlink_index = fe.symlink_index; + pad_file = fe.pad_file; + hidden_attribute = fe.hidden_attribute; + executable_attribute = fe.executable_attribute; + symlink_attribute = fe.symlink_attribute; + no_root_dir = fe.no_root_dir; + set_name(fe.filename().c_str()); + return *this; + } + + // if borrow_chars >= 0, don't take ownership over n, just + // point to it. It points to borrow_chars number of characters. + // if borrow_chars == -1, n is a null terminated string that + // should be copied + void internal_file_entry::set_name(char const* n, bool borrow_string, int string_len) + { + TORRENT_ASSERT(string_len >= 0); + + // we have limited space in the length field. truncate string + // if it's too long + if (string_len >= name_is_owned) string_len = name_is_owned - 1; + + // free the current string, before assigning the new one + if (name_len == name_is_owned) free((void*)name); + if (n == NULL) + { + TORRENT_ASSERT(borrow_string == false); + name = NULL; + } + else if (borrow_string) + { + name = n; + name_len = string_len; + } + else + { + name = allocate_string_copy(n); + name_len = name_is_owned; + } + } + + std::string internal_file_entry::filename() const + { + if (name_len != name_is_owned) return std::string(name, name_len); + return name ? name : ""; + } + +#if TORRENT_USE_WSTRING +#ifndef TORRENT_NO_DEPRECATE + void file_storage::set_name(std::wstring const& n) + { + std::string utf8; + wchar_utf8(n, utf8); + m_name = utf8; + } + + void file_storage::rename_file_deprecated(int index, std::wstring const& new_filename) + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + std::string utf8; + wchar_utf8(new_filename, utf8); + m_files[index].set_name(utf8.c_str()); + update_path_index(m_files[index]); + } + + void file_storage::add_file(std::wstring const& file, size_type size, int flags + , std::time_t mtime, std::string const& symlink_path) + { + std::string utf8; + wchar_utf8(file, utf8); + add_file(utf8, size, flags, mtime, symlink_path); + } + + void file_storage::rename_file(int index, std::wstring const& new_filename) + { + rename_file_deprecated(index, new_filename); + } +#endif // TORRENT_NO_DEPRECATE +#endif // TORRENT_USE_WSTRING + + void file_storage::rename_file(int index, std::string const& new_filename) + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + m_files[index].set_name(new_filename.c_str()); + update_path_index(m_files[index]); + } + + void file_storage::rename_file_borrow(int index, char const* new_filename, int len) + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + m_files[index].set_name(new_filename, true, len); + } + + namespace + { + bool compare_file_offset(internal_file_entry const& lhs, internal_file_entry const& rhs) + { + return lhs.offset < rhs.offset; + } + } + +#ifndef TORRENT_NO_DEPRECATE + file_storage::iterator file_storage::file_at_offset_deprecated(size_type offset) const + { + // find the file iterator and file offset + internal_file_entry target; + target.offset = offset; + TORRENT_ASSERT(!compare_file_offset(target, m_files.front())); + + std::vector::const_iterator file_iter = std::upper_bound( + begin_deprecated(), end_deprecated(), target, compare_file_offset); + + TORRENT_ASSERT(file_iter != begin_deprecated()); + --file_iter; + return file_iter; + } + + file_storage::iterator file_storage::file_at_offset(size_type offset) const + { + return file_at_offset_deprecated(offset); + } +#endif + + int file_storage::file_index_at_offset(size_type offset) const + { + // find the file iterator and file offset + internal_file_entry target; + target.offset = offset; + TORRENT_ASSERT(!compare_file_offset(target, m_files.front())); + + std::vector::const_iterator file_iter = std::upper_bound( + m_files.begin(), m_files.end(), target, compare_file_offset); + + TORRENT_ASSERT(file_iter != m_files.begin()); + --file_iter; + return file_iter - m_files.begin(); + } + + char const* file_storage::file_name_ptr(int index) const + { + return m_files[index].name; + } + + int file_storage::file_name_len(int index) const + { + if (m_files[index].name_len == internal_file_entry::name_is_owned) + return -1; + return m_files[index].name_len; + } + + std::vector file_storage::map_block(int piece, size_type offset + , int size) const + { + TORRENT_ASSERT_PRECOND(num_files() > 0); + std::vector ret; + + if (m_files.empty()) return ret; + + // find the file iterator and file offset + internal_file_entry target; + target.offset = piece * (size_type)m_piece_length + offset; + TORRENT_ASSERT_PRECOND(size_type(target.offset + size) <= m_total_size); + TORRENT_ASSERT(!compare_file_offset(target, m_files.front())); + + std::vector::const_iterator file_iter = std::upper_bound( + m_files.begin(), m_files.end(), target, compare_file_offset); + + TORRENT_ASSERT(file_iter != m_files.begin()); + --file_iter; + + size_type file_offset = target.offset - file_iter->offset; + for (; size > 0; file_offset -= file_iter->size, ++file_iter) + { + TORRENT_ASSERT(file_iter != m_files.end()); + if (file_offset < size_type(file_iter->size)) + { + file_slice f; + f.file_index = file_iter - m_files.begin(); + f.offset = file_offset + file_base(f.file_index); + f.size = (std::min)(boost::uint64_t(file_iter->size) - file_offset, boost::uint64_t(size)); + TORRENT_ASSERT(f.size <= size); + size -= int(f.size); + file_offset += f.size; + ret.push_back(f); + } + + TORRENT_ASSERT(size >= 0); + } + return ret; + } + + file_entry file_storage::at(int index) const + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + file_entry ret; + internal_file_entry const& ife = m_files[index]; + ret.path = file_path(index); + ret.offset = ife.offset; + ret.size = ife.size; + ret.file_base = file_base(index); + ret.mtime = mtime(index); + ret.pad_file = ife.pad_file; + ret.hidden_attribute = ife.hidden_attribute; + ret.executable_attribute = ife.executable_attribute; + ret.symlink_attribute = ife.symlink_attribute; + if (ife.symlink_index != internal_file_entry::not_a_symlink) + ret.symlink_path = symlink(index); + ret.filehash = hash(index); + return ret; + } + + peer_request file_storage::map_file(int file_index, size_type file_offset + , int size) const + { + TORRENT_ASSERT_PRECOND(file_index < num_files()); + TORRENT_ASSERT_PRECOND(file_index >= 0); + TORRENT_ASSERT(m_num_pieces >= 0); + + peer_request ret; + if (file_index < 0 || file_index >= num_files()) + { + ret.piece = m_num_pieces; + ret.start = 0; + ret.length = 0; + return ret; + } + + size_type offset = file_offset + this->file_offset(file_index); + + if (offset >= total_size()) + { + ret.piece = m_num_pieces; + ret.start = 0; + ret.length = 0; + } + else + { + ret.piece = int(offset / piece_length()); + ret.start = int(offset % piece_length()); + ret.length = size; + if (offset + size > total_size()) + ret.length = int(total_size() - offset); + } + return ret; + } + + void file_storage::add_file(std::string const& file, size_type size, int flags + , std::time_t mtime, std::string const& symlink_path) + { + TORRENT_ASSERT_PRECOND(!is_complete(file)); + TORRENT_ASSERT_PRECOND(size >= 0); + if (size < 0) size = 0; + if (!has_parent_path(file)) + { + // you have already added at least one file with a + // path to the file (branch_path), which means that + // all the other files need to be in the same top + // directory as the first file. + TORRENT_ASSERT_PRECOND(m_files.empty()); + m_name = file; + } + else + { + if (m_files.empty()) + m_name = split_path(file).c_str(); + } + TORRENT_ASSERT_PRECOND(m_name == split_path(file).c_str()); + m_files.push_back(internal_file_entry()); + internal_file_entry& e = m_files.back(); + e.set_name(file.c_str()); + e.size = size; + e.offset = m_total_size; + e.pad_file = (flags & pad_file) != 0; + e.hidden_attribute = (flags & attribute_hidden) != 0; + e.executable_attribute = (flags & attribute_executable) != 0; + if ((flags & attribute_symlink) && m_symlinks.size() < internal_file_entry::not_a_symlink - 1) + { + e.symlink_attribute = 1; + e.symlink_index = m_symlinks.size(); + m_symlinks.push_back(symlink_path); + } + else + e.symlink_attribute = 0; + + if (mtime) + { + if (m_mtime.size() < m_files.size()) m_mtime.resize(m_files.size()); + m_mtime[m_files.size() - 1] = mtime; + } + + update_path_index(e); + m_total_size += size; + } + + // TODO: 2 it would be nice if file_entry::filehash could be taken into + // account as well, and if the file_storage object could actually hold + // copies of filehash + void file_storage::add_file(file_entry const& ent, char const* filehash) + { + TORRENT_ASSERT_PRECOND(ent.size >= 0); + if (!has_parent_path(ent.path)) + { + // you have already added at least one file with a + // path to the file (branch_path), which means that + // all the other files need to be in the same top + // directory as the first file. + TORRENT_ASSERT_PRECOND(m_files.empty()); + m_name = ent.path; + } + else + { + if (m_files.empty()) + m_name = split_path(ent.path).c_str(); + } + internal_file_entry ife(ent); + int file_index = m_files.size(); + m_files.push_back(ife); + internal_file_entry& e = m_files.back(); + e.offset = m_total_size; + m_total_size += e.size; + if (filehash) + { + if (m_file_hashes.size() < m_files.size()) m_file_hashes.resize(m_files.size()); + m_file_hashes[m_files.size() - 1] = filehash; + } + if (!ent.symlink_path.empty() && m_symlinks.size() < internal_file_entry::not_a_symlink - 1) + { + e.symlink_index = m_symlinks.size(); + m_symlinks.push_back(ent.symlink_path); + } + if (ent.mtime) + { + if (m_mtime.size() < m_files.size()) m_mtime.resize(m_files.size()); + m_mtime[m_files.size() - 1] = ent.mtime; + } + if (ent.file_base) set_file_base(file_index, ent.file_base); + update_path_index(e); + } + + sha1_hash file_storage::hash(int index) const + { + if (index >= int(m_file_hashes.size())) return sha1_hash(0); + return sha1_hash(m_file_hashes[index]); + } + + std::string const& file_storage::symlink(int index) const + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + internal_file_entry const& fe = m_files[index]; + TORRENT_ASSERT(fe.symlink_index < int(m_symlinks.size())); + return m_symlinks[fe.symlink_index]; + } + + time_t file_storage::mtime(int index) const + { + if (index >= int(m_mtime.size())) return 0; + return m_mtime[index]; + } + + void file_storage::set_file_base(int index, size_type off) + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + if (int(m_file_base.size()) <= index) m_file_base.resize(index + 1, 0); + m_file_base[index] = off; + } + + size_type file_storage::file_base(int index) const + { + if (index >= int(m_file_base.size())) return 0; + return m_file_base[index]; + } + + std::string file_storage::file_path(int index, std::string const& save_path) const + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + internal_file_entry const& fe = m_files[index]; + + // -2 means this is an absolute path filename + if (fe.path_index == -2) return fe.filename(); + + // -1 means no path + if (fe.path_index == -1) return combine_path(save_path, fe.filename()); + + if (fe.no_root_dir) + return combine_path(save_path + , combine_path(m_paths[fe.path_index] + , fe.filename())); + + return combine_path(save_path + , combine_path(m_name + , combine_path(m_paths[fe.path_index] + , fe.filename()))); + } + + std::string file_storage::file_name(int index) const + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + internal_file_entry const& fe = m_files[index]; + return fe.filename(); + } + + size_type file_storage::file_size(int index) const + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + return m_files[index].size; + } + + bool file_storage::pad_file_at(int index) const + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + return m_files[index].pad_file; + } + + size_type file_storage::file_offset(int index) const + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + return m_files[index].offset; + } + + int file_storage::file_flags(int index) const + { + internal_file_entry const& fe = m_files[index]; + return (fe.pad_file ? flag_pad_file : 0) + | (fe.hidden_attribute ? flag_hidden : 0) + | (fe.executable_attribute ? flag_executable : 0) + | (fe.symlink_attribute ? flag_symlink : 0); + } + +#ifndef TORRENT_NO_DEPRECATE + sha1_hash file_storage::hash(internal_file_entry const& fe) const + { + int index = &fe - &m_files[0]; + if (index >= int(m_file_hashes.size())) return sha1_hash(0); + return sha1_hash(m_file_hashes[index]); + } + + std::string const& file_storage::symlink(internal_file_entry const& fe) const + { + TORRENT_ASSERT_PRECOND(fe.symlink_index < int(m_symlinks.size())); + return m_symlinks[fe.symlink_index]; + } + + time_t file_storage::mtime(internal_file_entry const& fe) const + { + int index = &fe - &m_files[0]; + if (index >= int(m_mtime.size())) return 0; + return m_mtime[index]; + } + + int file_storage::file_index(internal_file_entry const& fe) const + { + int index = &fe - &m_files[0]; + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + return index; + } + + void file_storage::set_file_base(internal_file_entry const& fe, size_type off) + { + int index = &fe - &m_files[0]; + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + if (int(m_file_base.size()) <= index) m_file_base.resize(index + 1, 0); + m_file_base[index] = off; + } + + size_type file_storage::file_base(internal_file_entry const& fe) const + { + int index = &fe - &m_files[0]; + if (index >= int(m_file_base.size())) return 0; + return m_file_base[index]; + } + + std::string file_storage::file_path(internal_file_entry const& fe + , std::string const& save_path) const + { + int index = &fe - &m_files[0]; + return file_path(index); + } + + std::string file_storage::file_name(internal_file_entry const& fe) const + { + return fe.filename(); + } + + size_type file_storage::file_size(internal_file_entry const& fe) const + { + return fe.size; + } + + bool file_storage::pad_file_at(internal_file_entry const& fe) const + { + return fe.pad_file; + } + + size_type file_storage::file_offset(internal_file_entry const& fe) const + { + return fe.offset; + } + + file_entry file_storage::at(file_storage::iterator i) const + { return at(i - m_files.begin()); } +#endif // TORRENT_NO_DEPRECATE + + bool compare_file_entry_size(internal_file_entry const& fe1, internal_file_entry const& fe2) + { return fe1.size < fe2.size; } + + void file_storage::reorder_file(int index, int dst) + { + TORRENT_ASSERT(index < int(m_files.size())); + TORRENT_ASSERT(dst < int(m_files.size())); + TORRENT_ASSERT(dst < index); + + std::iter_swap(m_files.begin() + index, m_files.begin() + dst); + if (!m_mtime.empty()) + { + TORRENT_ASSERT(m_mtime.size() == m_files.size()); + if (int(m_mtime.size()) < index) m_mtime.resize(index+1, 0); + std::iter_swap(m_mtime.begin() + dst, m_mtime.begin() + index); + } + if (!m_file_hashes.empty()) + { + TORRENT_ASSERT(m_file_hashes.size() == m_files.size()); + if (int(m_file_hashes.size()) < index) m_file_hashes.resize(index + 1, NULL); + std::iter_swap(m_file_hashes.begin() + dst, m_file_hashes.begin() + index); + } + if (!m_file_base.empty()) + { + TORRENT_ASSERT(m_file_base.size() == m_files.size()); + if (int(m_file_base.size()) < index) m_file_base.resize(index + 1, 0); + std::iter_swap(m_file_base.begin() + dst, m_file_base.begin() + index); + } + } + + void file_storage::optimize(int pad_file_limit, int alignment) + { + if (alignment == -1) + alignment = m_piece_length; + + size_type off = 0; + int padding_file = 0; + for (std::vector::iterator i = m_files.begin(); + i != m_files.end(); ++i) + { + if ((off % alignment) == 0) + { + // this file position is aligned, pick the largest + // available file to put here + std::vector::iterator best_match + = std::max_element(i, m_files.end() + , &compare_file_entry_size); + + if (best_match != i) + { + int index = best_match - m_files.begin(); + int cur_index = i - m_files.begin(); + reorder_file(index, cur_index); + i = m_files.begin() + cur_index; + } + } + else if (pad_file_limit >= 0 + && i->size > boost::uint32_t(pad_file_limit) + && i->pad_file == false) + { + // if we have pad files enabled, and this file is + // not piece-aligned and the file size exceeds the + // limit, and it's not a padding file itself. + // so add a padding file in front of it + int pad_size = alignment - (off % alignment); + + // find the largest file that fits in pad_size + std::vector::iterator best_match = m_files.end(); + + // if pad_file_limit is 0, it means all files are padded, there's + // no point in trying to find smaller files to use as filling + if (pad_file_limit > 0) + { + for (std::vector::iterator j = i+1; j < m_files.end(); ++j) + { + if (j->size > boost::uint32_t(pad_size)) continue; + if (best_match == m_files.end() || j->size > best_match->size) + best_match = j; + } + + if (best_match != m_files.end()) + { + // we found one + // We cannot have found i, because i->size > pad_file_limit + // which is forced to be no less than alignment. We only + // look for files <= pad_size, which never is greater than + // alignment + TORRENT_ASSERT(best_match != i); + int index = best_match - m_files.begin(); + int cur_index = i - m_files.begin(); + reorder_file(index, cur_index); + i = m_files.begin() + cur_index; + i->offset = off; + off += i->size; + continue; + } + } + + // we could not find a file that fits in pad_size + // add a padding file + // note that i will be set to point to the + // new pad file. Once we're done adding it, we need + // to increment i to point to the current file again + // first add the pad file to the end of the file list + // then swap it in place. This minimizes the amount + // of copying of internal_file_entry, which is somewhat + // expensive (until we have move semantics) + int cur_index = i - m_files.begin(); + int index = m_files.size(); + m_files.push_back(internal_file_entry()); + internal_file_entry& e = m_files.back(); + // i may have been invalidated, refresh it + i = m_files.begin() + cur_index; + e.size = pad_size; + e.offset = off; + char name[30]; + snprintf(name, sizeof(name), ".____padding_file/%d", padding_file); + std::string path = combine_path(m_name, name); + e.set_name(path.c_str()); + e.pad_file = true; + off += pad_size; + ++padding_file; + + if (!m_mtime.empty()) m_mtime.resize(index + 1, 0); + if (!m_file_hashes.empty()) m_file_hashes.resize(index + 1, NULL); + if (!m_file_base.empty()) m_file_base.resize(index + 1, 0); + + reorder_file(index, cur_index); + + TORRENT_ASSERT((off % alignment) == 0); + continue; + } + i->offset = off; + off += i->size; + } + m_total_size = off; + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/gzip.cpp b/apps/Launcher/ext/libtorrent/src/gzip.cpp new file mode 100644 index 0000000000..9af00606eb --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/gzip.cpp @@ -0,0 +1,268 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/assert.hpp" +#include "libtorrent/puff.hpp" +#include "libtorrent/gzip.hpp" + +#include +#include + +namespace +{ + enum + { + FTEXT = 0x01, + FHCRC = 0x02, + FEXTRA = 0x04, + FNAME = 0x08, + FCOMMENT = 0x10, + FRESERVED = 0xe0, + + GZIP_MAGIC0 = 0x1f, + GZIP_MAGIC1 = 0x8b + }; + +} + +namespace libtorrent +{ + struct gzip_error_category : boost::system::error_category + { + virtual const char* name() const BOOST_SYSTEM_NOEXCEPT; + virtual std::string message(int ev) const BOOST_SYSTEM_NOEXCEPT; + virtual boost::system::error_condition default_error_condition(int ev) const BOOST_SYSTEM_NOEXCEPT + { return boost::system::error_condition(ev, *this); } + }; + + const char* gzip_error_category::name() const BOOST_SYSTEM_NOEXCEPT + { + return "gzip error"; + } + + std::string gzip_error_category::message(int ev) const BOOST_SYSTEM_NOEXCEPT + { + static char const* msgs[] = + { + "no error", + "invalid gzip header", + "inflated data too large", + "available inflate data did not terminate", + "output space exhausted before completing inflate", + "invalid block type (type == 3)", + "stored block length did not match one's complement", + "dynamic block code description: too many length or distance codes", + "dynamic block code description: code lengths codes incomplete", + "dynamic block code description: repeat lengths with no first length", + "dynamic block code description: repeat more than specified lengths", + "dynamic block code description: invalid literal/length code lengths", + "dynamic block code description: invalid distance code lengths", + "invalid literal/length or distance code in fixed or dynamic block", + "distance is too far back in fixed or dynamic block", + "unknown gzip error", + }; + if (ev < 0 || ev >= int(sizeof(msgs)/sizeof(msgs[0]))) + return "Unknown error"; + return msgs[ev]; + } + + boost::system::error_category& get_gzip_category() + { + static gzip_error_category gzip_category; + return gzip_category; + } + + namespace gzip_errors + { + boost::system::error_code make_error_code(error_code_enum e) + { + return boost::system::error_code(e, get_gzip_category()); + } + } + + // returns -1 if gzip header is invalid or the header size in bytes + int gzip_header(const char* buf, int size) + { + TORRENT_ASSERT(buf != 0); + + const unsigned char* buffer = reinterpret_cast(buf); + const int total_size = size; + + // The zip header cannot be shorter than 10 bytes + if (size < 10 || buf == 0) return -1; + + // check the magic header of gzip + if ((buffer[0] != GZIP_MAGIC0) || (buffer[1] != GZIP_MAGIC1)) return -1; + + int method = buffer[2]; + int flags = buffer[3]; + + // check for reserved flag and make sure it's compressed with the correct metod + if (method != 8 || (flags & FRESERVED) != 0) return -1; + + // skip time, xflags, OS code + size -= 10; + buffer += 10; + + if (flags & FEXTRA) + { + int extra_len; + + if (size < 2) return -1; + + extra_len = (buffer[1] << 8) | buffer[0]; + + if (size < (extra_len+2)) return -1; + size -= (extra_len + 2); + buffer += (extra_len + 2); + } + + if (flags & FNAME) + { + while (size && *buffer) + { + --size; + ++buffer; + } + if (!size || *buffer) return -1; + + --size; + ++buffer; + } + + if (flags & FCOMMENT) + { + while (size && *buffer) + { + --size; + ++buffer; + } + if (!size || *buffer) return -1; + + --size; + ++buffer; + } + + if (flags & FHCRC) + { + if (size < 2) return -1; + + size -= 2; +// buffer += 2; + } + + return total_size - size; + } + + TORRENT_EXTRA_EXPORT void inflate_gzip( + char const* in + , int size + , std::vector& buffer + , int maximum_size + , error_code& ec) + { + ec.clear(); + TORRENT_ASSERT(maximum_size > 0); + + int header_len = gzip_header(in, size); + if (header_len < 0) + { + ec = gzip_errors::invalid_gzip_header; + return; + } + + // start off with 4 kilobytes and grow + // if needed + boost::uint32_t destlen = 4096; + int ret = 0; + boost::uint32_t srclen = size - header_len; + in += header_len; + + do + { + TORRENT_TRY { + buffer.resize(destlen); + } TORRENT_CATCH(std::exception& e) { + ec = errors::no_memory; + return; + } + + ret = puff((unsigned char*)&buffer[0], &destlen, (unsigned char*)in, &srclen); + + // if the destination buffer wasn't large enough, double its + // size and try again. Unless it's already at its max, in which + // case we fail + if (ret == 1) // 1: output space exhausted before completing inflate + { + if (destlen == boost::uint32_t(maximum_size)) + { + ec = gzip_errors::inflated_data_too_large; + return; + } + + destlen *= 2; + if (destlen > (unsigned int)maximum_size) + destlen = maximum_size; + } + } while (ret == 1); + + if (ret != 0) + { + switch (ret) + { + case 2: ec = gzip_errors::data_did_not_terminate; return; + case 1: ec = gzip_errors::space_exhausted; return; + case -1: ec = gzip_errors::invalid_block_type; return; + case -2: ec = gzip_errors::invalid_stored_block_length; return; + case -3: ec = gzip_errors::too_many_length_or_distance_codes; return; + case -4: ec = gzip_errors::code_lengths_codes_incomplete; return; + case -5: ec = gzip_errors::repeat_lengths_with_no_first_length; return; + case -6: ec = gzip_errors::repeat_more_than_specified_lengths; return; + case -7: ec = gzip_errors::invalid_literal_length_code_lengths; return; + case -8: ec = gzip_errors::invalid_distance_code_lengths; return; + case -9: ec = gzip_errors::invalid_literal_code_in_block; return; + case -10: ec = gzip_errors::distance_too_far_back_in_block; return; + default: ec = gzip_errors::unknown_gzip_error; return; + } + } + + if (destlen > buffer.size()) + { + ec = gzip_errors::unknown_gzip_error; + return; + } + + buffer.resize(destlen); + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/hasher.cpp b/apps/Launcher/ext/libtorrent/src/hasher.cpp new file mode 100644 index 0000000000..35d0167608 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/hasher.cpp @@ -0,0 +1,136 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/hasher.hpp" + +namespace libtorrent +{ + hasher::hasher() + { +#ifdef TORRENT_USE_GCRYPT + gcry_md_open(&m_context, GCRY_MD_SHA1, 0); +#elif TORRENT_USE_COMMONCRYPTO + CC_SHA1_Init(&m_context); +#elif defined TORRENT_USE_OPENSSL + SHA1_Init(&m_context); +#else + SHA1_init(&m_context); +#endif + } + + hasher::hasher(const char* data, int len) + { + TORRENT_ASSERT(data != 0); + TORRENT_ASSERT(len > 0); +#ifdef TORRENT_USE_GCRYPT + gcry_md_open(&m_context, GCRY_MD_SHA1, 0); + gcry_md_write(m_context, data, len); +#elif TORRENT_USE_COMMONCRYPTO + CC_SHA1_Init(&m_context); + CC_SHA1_Update(&m_context, reinterpret_cast(data), len); +#elif defined TORRENT_USE_OPENSSL + SHA1_Init(&m_context); + SHA1_Update(&m_context, reinterpret_cast(data), len); +#else + SHA1_init(&m_context); + SHA1_update(&m_context, reinterpret_cast(data), len); +#endif + } + +#ifdef TORRENT_USE_GCRYPT + hasher::hasher(hasher const& h) + { + gcry_md_copy(&m_context, h.m_context); + } + + hasher& hasher::operator=(hasher const& h) + { + gcry_md_close(m_context); + gcry_md_copy(&m_context, h.m_context); + return *this; + } +#endif + + hasher& hasher::update(const char* data, int len) + { + TORRENT_ASSERT(data != 0); + TORRENT_ASSERT(len > 0); +#ifdef TORRENT_USE_GCRYPT + gcry_md_write(m_context, data, len); +#elif TORRENT_USE_COMMONCRYPTO + CC_SHA1_Update(&m_context, reinterpret_cast(data), len); +#elif defined TORRENT_USE_OPENSSL + SHA1_Update(&m_context, reinterpret_cast(data), len); +#else + SHA1_update(&m_context, reinterpret_cast(data), len); +#endif + return *this; + } + + sha1_hash hasher::final() + { + sha1_hash digest; +#ifdef TORRENT_USE_GCRYPT + gcry_md_final(m_context); + digest.assign((const char*)gcry_md_read(m_context, 0)); +#elif TORRENT_USE_COMMONCRYPTO + CC_SHA1_Final(digest.begin(), &m_context); +#elif defined TORRENT_USE_OPENSSL + SHA1_Final(digest.begin(), &m_context); +#else + SHA1_final(digest.begin(), &m_context); +#endif + return digest; + } + + void hasher::reset() + { +#ifdef TORRENT_USE_GCRYPT + gcry_md_reset(m_context); +#elif TORRENT_USE_COMMONCRYPTO + CC_SHA1_Init(&m_context); +#elif defined TORRENT_USE_OPENSSL + SHA1_Init(&m_context); +#else + SHA1_init(&m_context); +#endif + } + +#ifdef TORRENT_USE_GCRYPT + hasher::~hasher() + { + gcry_md_close(m_context); + } +#endif + +} + diff --git a/apps/Launcher/ext/libtorrent/src/http_connection.cpp b/apps/Launcher/ext/libtorrent/src/http_connection.cpp new file mode 100644 index 0000000000..e91204e9e9 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/http_connection.cpp @@ -0,0 +1,932 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/http_connection.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/instantiate_connection.hpp" +#include "libtorrent/gzip.hpp" +#include "libtorrent/parse_url.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/connection_queue.hpp" +#include "libtorrent/socket_type.hpp" // for async_shutdown + +#if defined TORRENT_ASIO_DEBUGGING +#include "libtorrent/debug.hpp" +#endif + +#include +#include +#include + +namespace libtorrent { + +http_connection::http_connection(io_service& ios, connection_queue& cc + , http_handler const& handler + , bool bottled + , int max_bottled_buffer_size + , http_connect_handler const& ch + , http_filter_handler const& fh +#ifdef TORRENT_USE_OPENSSL + , boost::asio::ssl::context* ssl_ctx +#endif + ) + : m_sock(ios) +#if TORRENT_USE_I2P + , m_i2p_conn(0) +#endif + , m_read_pos(0) + , m_resolver(ios) + , m_handler(handler) + , m_connect_handler(ch) + , m_filter_handler(fh) + , m_timer(ios) + , m_last_receive(time_now()) + , m_start_time(time_now()) + , m_bottled(bottled) + , m_max_bottled_buffer_size(max_bottled_buffer_size) + , m_called(false) +#ifdef TORRENT_USE_OPENSSL + , m_ssl_ctx(ssl_ctx) + , m_own_ssl_context(false) +#endif + , m_rate_limit(0) + , m_download_quota(0) + , m_limiter_timer_active(false) + , m_limiter_timer(ios) + , m_redirects(5) + , m_connection_ticket(-1) + , m_cc(cc) + , m_ssl(false) + , m_priority(0) + , m_abort(false) +{ + TORRENT_ASSERT(!m_handler.empty()); +} + +http_connection::~http_connection() +{ + TORRENT_ASSERT(m_connection_ticket == -1); +#ifdef TORRENT_USE_OPENSSL + if (m_own_ssl_context) delete m_ssl_ctx; +#endif +} + +void http_connection::get(std::string const& url, time_duration timeout, int prio + , proxy_settings const* ps, int handle_redirects, std::string const& user_agent + , address const& bind_addr +#if TORRENT_USE_I2P + , i2p_connection* i2p_conn +#endif + ) +{ + m_user_agent = user_agent; + + std::string protocol; + std::string auth; + std::string hostname; + std::string path; + error_code ec; + int port; + + boost::tie(protocol, auth, hostname, port, path) + = parse_url_components(url, ec); + + int default_port = protocol == "https" ? 443 : 80; + if (port == -1) port = default_port; + + // keep ourselves alive even if the callback function + // deletes this object + boost::shared_ptr me(shared_from_this()); + + if (protocol != "http" +#ifdef TORRENT_USE_OPENSSL + && protocol != "https" +#endif + ) + { + error_code ec(errors::unsupported_url_protocol); + m_resolver.get_io_service().post(boost::bind(&http_connection::callback + , me, ec, (char*)0, 0)); + return; + } + + if (ec) + { + m_resolver.get_io_service().post(boost::bind(&http_connection::callback + , me, ec, (char*)0, 0)); + return; + } + + TORRENT_ASSERT(prio >= 0 && prio < 3); + + bool ssl = false; + if (protocol == "https") ssl = true; + + char request[4096]; + char* end = request + sizeof(request); + char* ptr = request; + +#define APPEND_FMT(fmt) ptr += snprintf(ptr, end - ptr, fmt) +#define APPEND_FMT1(fmt, arg) ptr += snprintf(ptr, end - ptr, fmt, arg) +#define APPEND_FMT2(fmt, arg1, arg2) ptr += snprintf(ptr, end - ptr, fmt, arg1, arg2) + + // exclude ssl here, because SSL assumes CONNECT support in the + // proxy and is handled at the lower layer + if (ps && (ps->type == proxy_settings::http + || ps->type == proxy_settings::http_pw) + && !ssl) + { + // if we're using an http proxy and not an ssl + // connection, just do a regular http proxy request + APPEND_FMT1("GET %s HTTP/1.1\r\n", url.c_str()); + if (ps->type == proxy_settings::http_pw) + APPEND_FMT1("Proxy-Authorization: Basic %s\r\n", base64encode( + ps->username + ":" + ps->password).c_str()); + + hostname = ps->hostname; + port = ps->port; + + APPEND_FMT1("Host: %s", hostname.c_str()); + if (port != default_port) APPEND_FMT1(":%d\r\n", port); + else APPEND_FMT("\r\n"); + } + else + { + APPEND_FMT2("GET %s HTTP/1.1\r\n" + "Host: %s", path.c_str(), hostname.c_str()); + if (port != default_port) APPEND_FMT1(":%d\r\n", port); + else APPEND_FMT("\r\n"); + } + +// APPEND_FMT("Accept: */*\r\n"); + + if (!m_user_agent.empty()) + APPEND_FMT1("User-Agent: %s\r\n", m_user_agent.c_str()); + + if (m_bottled) + APPEND_FMT("Accept-Encoding: gzip\r\n"); + + if (!auth.empty()) + APPEND_FMT1("Authorization: Basic %s\r\n", base64encode(auth).c_str()); + + APPEND_FMT("Connection: close\r\n\r\n"); + + sendbuffer.assign(request); + m_url = url; + start(hostname, to_string(port).elems, timeout, prio + , ps, ssl, handle_redirects, bind_addr +#if TORRENT_USE_I2P + , i2p_conn +#endif + ); +} + +void http_connection::start(std::string const& hostname, std::string const& port + , time_duration timeout, int prio, proxy_settings const* ps, bool ssl, int handle_redirects + , address const& bind_addr +#if TORRENT_USE_I2P + , i2p_connection* i2p_conn +#endif + ) +{ + TORRENT_ASSERT(prio >= 0 && prio < 3); + + m_redirects = handle_redirects; + if (ps) m_proxy = *ps; + + // keep ourselves alive even if the callback function + // deletes this object + boost::shared_ptr me(shared_from_this()); + + m_completion_timeout = timeout; + m_read_timeout = (std::max)(seconds(5), timeout / 5); + error_code ec; + m_timer.expires_from_now(m_completion_timeout, ec); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_timeout"); +#endif + m_timer.async_wait(boost::bind(&http_connection::on_timeout + , boost::weak_ptr(me), _1)); + m_called = false; + m_parser.reset(); + m_recvbuffer.clear(); + m_read_pos = 0; + m_priority = prio; + + if (ec) + { + m_resolver.get_io_service().post(boost::bind(&http_connection::callback + , me, ec, (char*)0, 0)); + return; + } + + if (m_sock.is_open() && m_hostname == hostname && m_port == port + && m_ssl == ssl && m_bind_addr == bind_addr) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_write"); +#endif + async_write(m_sock, asio::buffer(sendbuffer) + , boost::bind(&http_connection::on_write, me, _1)); + } + else + { + m_ssl = ssl; + m_bind_addr = bind_addr; + error_code ec; + if (m_sock.is_open()) m_sock.close(ec); + +#if TORRENT_USE_I2P + bool is_i2p = false; + char const* top_domain = strrchr(hostname.c_str(), '.'); + if (top_domain && strcmp(top_domain, ".i2p") == 0 && i2p_conn) + { + // this is an i2p name, we need to use the sam connection + // to do the name lookup + is_i2p = true; + m_i2p_conn = i2p_conn; + // quadruple the timeout for i2p destinations + // because i2p is sloooooow + m_completion_timeout *= 4; + m_read_timeout *= 4; + } +#endif + +#if TORRENT_USE_I2P + if (is_i2p && i2p_conn->proxy().type != proxy_settings::i2p_proxy) + { + m_resolver.get_io_service().post(boost::bind(&http_connection::callback + , me, error_code(errors::no_i2p_router, get_libtorrent_category()), (char*)0, 0)); + return; + } +#endif + + proxy_settings const* proxy = ps; +#if TORRENT_USE_I2P + if (is_i2p) proxy = &i2p_conn->proxy(); +#endif + + // in this case, the upper layer is assumed to have taken + // care of the proxying already. Don't instantiate the socket + // with this proxy + if (proxy && (proxy->type == proxy_settings::http + || proxy->type == proxy_settings::http_pw) + && !ssl) + { + proxy = 0; + } + proxy_settings null_proxy; + + void* userdata = 0; +#ifdef TORRENT_USE_OPENSSL + if (m_ssl) + { + if (m_ssl_ctx == 0) + { + m_ssl_ctx = new (std::nothrow) boost::asio::ssl::context( + m_resolver.get_io_service(), asio::ssl::context::sslv23_client); + if (m_ssl_ctx) + { + m_own_ssl_context = true; + error_code ec; + m_ssl_ctx->set_verify_mode(asio::ssl::context::verify_none, ec); + TORRENT_ASSERT(!ec); + } + } + userdata = m_ssl_ctx; + } +#endif + instantiate_connection(m_resolver.get_io_service() + , proxy ? *proxy : null_proxy, m_sock, userdata); + + if (m_bind_addr != address_v4::any()) + { + error_code ec; + m_sock.open(m_bind_addr.is_v4()?tcp::v4():tcp::v6(), ec); + m_sock.bind(tcp::endpoint(m_bind_addr, 0), ec); + if (ec) + { + m_resolver.get_io_service().post(boost::bind(&http_connection::callback + , me, ec, (char*)0, 0)); + return; + } + } + + setup_ssl_hostname(m_sock, hostname, ec); + if (ec) + { + m_resolver.get_io_service().post(boost::bind(&http_connection::callback + , me, ec, (char*)0, 0)); + return; + } + +#if TORRENT_USE_I2P + if (is_i2p) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_i2p_resolve"); +#endif + i2p_conn->async_name_lookup(hostname.c_str(), boost::bind(&http_connection::on_i2p_resolve + , me, _1, _2)); + } + else +#endif + if (ps && ps->proxy_hostnames + && (ps->type == proxy_settings::socks5 + || ps->type == proxy_settings::socks5_pw)) + { + m_hostname = hostname; + m_port = port; + m_endpoints.push_back(tcp::endpoint(address(), atoi(port.c_str()))); + queue_connect(); + } + else + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_resolve"); +#endif + m_endpoints.clear(); + tcp::resolver::query query(hostname, port); + m_resolver.async_resolve(query, boost::bind(&http_connection::on_resolve + , me, _1, _2)); + } + m_hostname = hostname; + m_port = port; + } +} + +void http_connection::on_connect_timeout() +{ + TORRENT_ASSERT(m_connection_ticket > -1); + + // keep ourselves alive even if the callback function + // deletes this object + boost::shared_ptr me(shared_from_this()); + + error_code ec; + m_sock.close(ec); +} + +void http_connection::on_timeout(boost::weak_ptr p + , error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("http_connection::on_timeout"); +#endif + boost::shared_ptr c = p.lock(); + if (!c) return; + + if (e == asio::error::operation_aborted) return; + + if (c->m_abort) return; + + ptime now = time_now_hires(); + + if (c->m_start_time + c->m_completion_timeout < now + || c->m_last_receive + c->m_read_timeout < now) + { + if (c->m_connection_ticket > -1 && !c->m_endpoints.empty()) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_timeout"); +#endif + error_code ec; + async_shutdown(c->m_sock, c); + c->m_timer.expires_at((std::min)( + c->m_last_receive + c->m_read_timeout + , c->m_start_time + c->m_completion_timeout), ec); + c->m_timer.async_wait(boost::bind(&http_connection::on_timeout, p, _1)); + } + else + { + c->callback(asio::error::timed_out); + c->close(true); + } + return; + } + + if (!c->m_sock.is_open()) return; +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_timeout"); +#endif + error_code ec; + c->m_timer.expires_at((std::min)( + c->m_last_receive + c->m_read_timeout + , c->m_start_time + c->m_completion_timeout), ec); + c->m_timer.async_wait(boost::bind(&http_connection::on_timeout, p, _1)); +} + +void http_connection::close(bool force) +{ + if (m_abort) return; + + error_code ec; + m_timer.cancel(ec); + m_resolver.cancel(); + m_limiter_timer.cancel(ec); + + if (force) + m_sock.close(ec); + else + async_shutdown(m_sock, shared_from_this()); + + m_hostname.clear(); + m_port.clear(); + m_handler.clear(); + m_abort = true; +} + +#if TORRENT_USE_I2P +void http_connection::on_i2p_resolve(error_code const& e + , char const* destination) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("http_connection::on_i2p_resolve"); +#endif + if (e) + { + callback(e); + close(); + return; + } + +#ifdef TORRENT_USE_OPENSSL + TORRENT_ASSERT(m_ssl == false); + TORRENT_ASSERT(m_sock.get()); + TORRENT_ASSERT(m_sock.get()->get()); + m_sock.get()->get()->set_destination(destination); + m_sock.get()->get()->set_command(i2p_stream::cmd_connect); + m_sock.get()->get()->set_session_id(m_i2p_conn->session_id()); +#else + m_sock.get()->set_destination(destination); + m_sock.get()->set_command(i2p_stream::cmd_connect); + m_sock.get()->set_session_id(m_i2p_conn->session_id()); +#endif +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_connect"); +#endif + m_sock.async_connect(tcp::endpoint(), boost::bind(&http_connection::on_connect + , shared_from_this(), _1)); +} +#endif + +void http_connection::on_resolve(error_code const& e + , tcp::resolver::iterator i) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("http_connection::on_resolve"); +#endif + if (e) + { + boost::shared_ptr me(shared_from_this()); + + callback(e); + close(); + return; + } + TORRENT_ASSERT(i != tcp::resolver::iterator()); + + std::transform(i, tcp::resolver::iterator(), std::back_inserter(m_endpoints) + , boost::bind(&tcp::resolver::iterator::value_type::endpoint, _1)); + + if (m_filter_handler) m_filter_handler(*this, m_endpoints); + if (m_endpoints.empty()) + { + close(); + return; + } + + // The following statement causes msvc to crash (ICE). Since it's not + // necessary in the vast majority of cases, just ignore the endpoint + // order for windows +#if !defined _MSC_VER || _MSC_VER > 1310 + // sort the endpoints so that the ones with the same IP version as our + // bound listen socket are first. So that when contacting a tracker, + // we'll talk to it from the same IP that we're listening on + if (m_bind_addr != address_v4::any()) + std::partition(m_endpoints.begin(), m_endpoints.end() + , boost::bind(&address::is_v4, boost::bind(&tcp::endpoint::address, _1)) + == m_bind_addr.is_v4()); +#endif + + queue_connect(); +} + +void http_connection::queue_connect() +{ + TORRENT_ASSERT(!m_endpoints.empty()); + tcp::endpoint target = m_endpoints.front(); + m_endpoints.pop_front(); + + m_cc.enqueue(boost::bind(&http_connection::connect, shared_from_this(), _1, target) + , boost::bind(&http_connection::on_connect_timeout, shared_from_this()) + , m_read_timeout, m_priority); +} + +void http_connection::connect(int ticket, tcp::endpoint target_address) +{ + if (ticket == -1) + { + close(); + return; + } + + m_connection_ticket = ticket; + if (m_proxy.proxy_hostnames + && (m_proxy.type == proxy_settings::socks5 + || m_proxy.type == proxy_settings::socks5_pw)) + { + // we're using a socks proxy and we're resolving + // hostnames through it +#ifdef TORRENT_USE_OPENSSL + if (m_ssl) + { + TORRENT_ASSERT(m_sock.get >()); + m_sock.get >()->next_layer().set_dst_name(m_hostname); + } + else +#endif + { + TORRENT_ASSERT(m_sock.get()); + m_sock.get()->set_dst_name(m_hostname); + } + } +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_connect"); +#endif + m_sock.async_connect(target_address, boost::bind(&http_connection::on_connect + , shared_from_this(), _1)); +} + +void http_connection::on_connect(error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("http_connection::on_connect"); +#endif + if (m_connection_ticket >= 0) + { + m_cc.done(m_connection_ticket); + m_connection_ticket = -1; + } + + m_last_receive = time_now_hires(); + m_start_time = m_last_receive; + if (!e) + { + if (m_connect_handler) m_connect_handler(*this); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_write"); +#endif + async_write(m_sock, asio::buffer(sendbuffer) + , boost::bind(&http_connection::on_write, shared_from_this(), _1)); + } + else if (!m_endpoints.empty() && !m_abort) + { + // The connection failed. Try the next endpoint in the list. + error_code ec; + m_sock.close(ec); + queue_connect(); + } + else + { + boost::shared_ptr me(shared_from_this()); + callback(e); + close(); + } +} + +void http_connection::callback(error_code e, char* data, int size) +{ + if (m_bottled && m_called) return; + + std::vector buf; + if (data && m_bottled && m_parser.header_finished()) + { + size = m_parser.collapse_chunk_headers((char*)data, size); + + std::string const& encoding = m_parser.header("content-encoding"); + if ((encoding == "gzip" || encoding == "x-gzip") && size > 0 && data) + { + error_code ec; + inflate_gzip(data, size, buf, m_max_bottled_buffer_size, ec); + + if (ec) + { + if (m_handler) m_handler(ec, m_parser, data, size, *this); + close(); + return; + } + size = int(buf.size()); + data = size == 0 ? 0 : &buf[0]; + } + + // if we completed the whole response, no need + // to tell the user that the connection was closed by + // the server or by us. Just clear any error + if (m_parser.finished()) e.clear(); + } + m_called = true; + error_code ec; + m_timer.cancel(ec); + if (m_handler) m_handler(e, m_parser, data, size, *this); +} + +void http_connection::on_write(error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("http_connection::on_write"); +#endif + + if (e == asio::error::operation_aborted) return; + + if (e) + { + boost::shared_ptr me(shared_from_this()); + callback(e); + close(); + return; + } + + if (m_abort) return; + + std::string().swap(sendbuffer); + m_recvbuffer.resize(4096); + + int amount_to_read = m_recvbuffer.size() - m_read_pos; + if (m_rate_limit > 0 && amount_to_read > m_download_quota) + { + amount_to_read = m_download_quota; + if (m_download_quota == 0) + { + if (!m_limiter_timer_active) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_assign_bandwidth"); +#endif + on_assign_bandwidth(error_code()); + } + return; + } + } +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_read"); +#endif + m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos + , amount_to_read) + , boost::bind(&http_connection::on_read + , shared_from_this(), _1, _2)); +} + +void http_connection::on_read(error_code const& e + , std::size_t bytes_transferred) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("http_connection::on_read"); +#endif + + if (m_rate_limit) + { + m_download_quota -= bytes_transferred; + TORRENT_ASSERT(m_download_quota >= 0); + } + + if (e == asio::error::operation_aborted) return; + + if (m_abort) return; + + // keep ourselves alive even if the callback function + // deletes this object + boost::shared_ptr me(shared_from_this()); + + // when using the asio SSL wrapper, it seems like + // we get the shut_down error instead of EOF + if (e == asio::error::eof || e == asio::error::shut_down) + { + error_code ec = asio::error::eof; + TORRENT_ASSERT(bytes_transferred == 0); + char* data = 0; + std::size_t size = 0; + if (m_bottled && m_parser.header_finished()) + { + data = &m_recvbuffer[0] + m_parser.body_start(); + size = m_parser.get_body().left(); + } + callback(ec, data, size); + close(); + return; + } + + if (e) + { + TORRENT_ASSERT(bytes_transferred == 0); + callback(e); + close(); + return; + } + + m_read_pos += bytes_transferred; + TORRENT_ASSERT(m_read_pos <= int(m_recvbuffer.size())); + + if (m_bottled || !m_parser.header_finished()) + { + libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0] + , &m_recvbuffer[0] + m_read_pos); + bool error = false; + m_parser.incoming(rcv_buf, error); + if (error) + { + // HTTP parse error + error_code ec = errors::http_parse_error; + callback(ec, 0, 0); + return; + } + + // having a nonempty path means we should handle redirects + if (m_redirects && m_parser.header_finished()) + { + int code = m_parser.status_code(); + + if (is_redirect(code)) + { + // attempt a redirect + std::string const& location = m_parser.header("location"); + if (location.empty()) + { + // missing location header + callback(error_code(errors::http_missing_location)); + close(); + return; + } + + error_code ec; + // it would be nice to gracefully shut down SSL here + // but then we'd have to do all the reconnect logic + // in its handler. For now, just kill the connection. +// async_shutdown(m_sock, shared_from_this()); + m_sock.close(ec); + + std::string url = resolve_redirect_location(m_url, location); + get(url, m_completion_timeout, m_priority, &m_proxy, m_redirects - 1 + , m_user_agent, m_bind_addr +#if TORRENT_USE_I2P + , m_i2p_conn +#endif + ); + return; + } + + m_redirects = 0; + } + + if (!m_bottled && m_parser.header_finished()) + { + if (m_read_pos > m_parser.body_start()) + callback(e, &m_recvbuffer[0] + m_parser.body_start() + , m_read_pos - m_parser.body_start()); + m_read_pos = 0; + m_last_receive = time_now_hires(); + } + else if (m_bottled && m_parser.finished()) + { + error_code ec; + m_timer.cancel(ec); + callback(e, &m_recvbuffer[0] + m_parser.body_start(), m_parser.get_body().left()); + } + } + else + { + TORRENT_ASSERT(!m_bottled); + callback(e, &m_recvbuffer[0], m_read_pos); + m_read_pos = 0; + m_last_receive = time_now_hires(); + } + + // if we've hit the limit, double the buffer size + if (int(m_recvbuffer.size()) == m_read_pos) + m_recvbuffer.resize((std::min)(m_read_pos * 2, m_max_bottled_buffer_size)); + + if (m_read_pos == m_max_bottled_buffer_size) + { + // if we've reached the size limit, terminate the connection and + // report the error + callback(error_code(boost::system::errc::file_too_large, generic_category())); + close(); + return; + } + int amount_to_read = m_recvbuffer.size() - m_read_pos; + if (m_rate_limit > 0 && amount_to_read > m_download_quota) + { + amount_to_read = m_download_quota; + if (m_download_quota == 0) + { + if (!m_limiter_timer_active) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_assign_bandwidth"); +#endif + on_assign_bandwidth(error_code()); + } + return; + } + } +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_read"); +#endif + m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos + , amount_to_read) + , boost::bind(&http_connection::on_read + , me, _1, _2)); +} + +void http_connection::on_assign_bandwidth(error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("http_connection::on_assign_bandwidth"); +#endif + if ((e == asio::error::operation_aborted + && m_limiter_timer_active) + || !m_sock.is_open()) + { + callback(asio::error::eof); + return; + } + m_limiter_timer_active = false; + if (e) return; + + if (m_download_quota > 0) return; + + m_download_quota = m_rate_limit / 4; + + int amount_to_read = m_recvbuffer.size() - m_read_pos; + if (amount_to_read > m_download_quota) + amount_to_read = m_download_quota; + + if (!m_sock.is_open()) return; + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_read"); +#endif + m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos + , amount_to_read) + , boost::bind(&http_connection::on_read + , shared_from_this(), _1, _2)); + + error_code ec; + m_limiter_timer_active = true; + m_limiter_timer.expires_from_now(milliseconds(250), ec); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_assign_bandwidth"); +#endif + m_limiter_timer.async_wait(boost::bind(&http_connection::on_assign_bandwidth + , shared_from_this(), _1)); +} + +void http_connection::rate_limit(int limit) +{ + if (!m_sock.is_open()) return; + + if (!m_limiter_timer_active) + { + error_code ec; + m_limiter_timer_active = true; + m_limiter_timer.expires_from_now(milliseconds(250), ec); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_assign_bandwidth"); +#endif + m_limiter_timer.async_wait(boost::bind(&http_connection::on_assign_bandwidth + , shared_from_this(), _1)); + } + m_rate_limit = limit; +} + +} + diff --git a/apps/Launcher/ext/libtorrent/src/http_parser.cpp b/apps/Launcher/ext/libtorrent/src/http_parser.cpp new file mode 100644 index 0000000000..84b7af4bf5 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/http_parser.cpp @@ -0,0 +1,542 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include + +#include "libtorrent/config.hpp" +#include "libtorrent/http_parser.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/parse_url.hpp" // for parse_url_components + +using namespace libtorrent; + +namespace libtorrent +{ + + bool is_ok_status(int http_status) + { + return http_status == 206 // partial content + || http_status == 200 // OK + || (http_status >= 300 // redirect + && http_status < 400); + } + + bool is_redirect(int http_status) + { + return http_status >= 300 + && http_status < 400; + } + + std::string resolve_redirect_location(std::string referrer + , std::string location) + { + if (location.empty()) return referrer; + + error_code ec; + using boost::tuples::ignore; + boost::tie(ignore, ignore, ignore, ignore, ignore) + = parse_url_components(location, ec); + + // if location is a full URL, just return it + if (!ec) return location; + + // otherwise it's likely to be just the path, or a relative path + std::string url = referrer; + + if (location[0] == '/') + { + // it's an absolute path. replace the path component of + // referrer with location + + // 8 is to skip the ur;l scheme://. We want the first slash + // that's part of the path. + std::size_t i = url.find_first_of('/', 8); + if (i == std::string::npos) + return location; + url.resize(i); + url += location; + } + else + { + // some web servers send out relative paths + // in the location header. + // remove the leaf filename + std::size_t i = url.find_last_of('/'); + if (i == std::string::npos) + return location; + + url.resize(i); + + if ((url.empty() || url[url.size()-1] != '/') + && (location.empty() || location[0] != '/')) + url += '/'; + url += location; + } + return url; + } + + http_parser::~http_parser() {} + + http_parser::http_parser(int flags) + : m_recv_pos(0) + , m_status_code(-1) + , m_content_length(-1) + , m_range_start(-1) + , m_range_end(-1) + , m_state(read_status) + , m_recv_buffer(0, 0) + , m_body_start_pos(0) + , m_connection_close(false) + , m_chunked_encoding(false) + , m_finished(false) + , m_cur_chunk_end(-1) + , m_chunk_header_size(0) + , m_partial_chunk_header(0) + , m_flags(flags) + {} + + boost::tuple http_parser::incoming( + buffer::const_interval recv_buffer, bool& error) + { + TORRENT_ASSERT(recv_buffer.left() >= m_recv_buffer.left()); + boost::tuple ret(0, 0); + int start_pos = m_recv_buffer.left(); + + // early exit if there's nothing new in the receive buffer + if (start_pos == recv_buffer.left()) return ret; + m_recv_buffer = recv_buffer; + + if (m_state == error_state) + { + error = true; + return ret; + } + + char const* pos = recv_buffer.begin + m_recv_pos; + +restart_response: + + if (m_state == read_status) + { + TORRENT_ASSERT(!m_finished); + char const* newline = std::find(pos, recv_buffer.end, '\n'); + // if we don't have a full line yet, wait. + if (newline == recv_buffer.end) + { + boost::get<1>(ret) += m_recv_buffer.left() - start_pos; + return ret; + } + + if (newline == pos) + { + m_state = error_state; + error = true; + return ret; + } + + char const* line_end = newline; + if (pos != line_end && *(line_end - 1) == '\r') --line_end; + + char const* line = pos; + ++newline; + int incoming = int(newline - pos); + m_recv_pos += incoming; + boost::get<1>(ret) += newline - (m_recv_buffer.begin + start_pos); + pos = newline; + + m_protocol = read_until(line, ' ', line_end); + if (m_protocol.substr(0, 5) == "HTTP/") + { + m_status_code = atoi(read_until(line, ' ', line_end).c_str()); + m_server_message = read_until(line, '\r', line_end); + + // HTTP 1.0 always closes the connection after + // each request + if (m_protocol == "HTTP/1.0") m_connection_close = true; + } + else + { + m_method = m_protocol; + std::transform(m_method.begin(), m_method.end(), m_method.begin(), &to_lower); + // the content length is assumed to be 0 for requests + m_content_length = 0; + m_protocol.clear(); + m_path = read_until(line, ' ', line_end); + m_protocol = read_until(line, ' ', line_end); + m_status_code = 0; + } + m_state = read_header; + start_pos = pos - recv_buffer.begin; + } + + if (m_state == read_header) + { + TORRENT_ASSERT(!m_finished); + char const* newline = std::find(pos, recv_buffer.end, '\n'); + std::string line; + + while (newline != recv_buffer.end && m_state == read_header) + { + // if the LF character is preceeded by a CR + // charachter, don't copy it into the line string. + char const* line_end = newline; + if (pos != line_end && *(line_end - 1) == '\r') --line_end; + line.assign(pos, line_end); + ++newline; + m_recv_pos += newline - pos; + pos = newline; + + std::string::size_type separator = line.find(':'); + if (separator == std::string::npos) + { + if (m_status_code == 100) + { + // for 100 Continue, we need to read another response header + // before reading the body + m_state = read_status; + goto restart_response; + } + // this means we got a blank line, + // the header is finished and the body + // starts. + m_state = read_body; + // if this is a request (not a response) + // we're done once we reach the end of the headers +// if (!m_method.empty()) m_finished = true; + // the HTTP header should always be < 2 GB + TORRENT_ASSERT(m_recv_pos < INT_MAX); + m_body_start_pos = int(m_recv_pos); + break; + } + + std::string name = line.substr(0, separator); + std::transform(name.begin(), name.end(), name.begin(), &to_lower); + ++separator; + // skip whitespace + while (separator < line.size() + && (line[separator] == ' ' || line[separator] == '\t')) + ++separator; + std::string value = line.substr(separator, std::string::npos); + m_header.insert(std::make_pair(name, value)); + + if (name == "content-length") + { + m_content_length = strtoll(value.c_str(), 0, 10); + } + else if (name == "connection") + { + m_connection_close = string_begins_no_case("close", value.c_str()); + } + else if (name == "content-range") + { + bool success = true; + char const* ptr = value.c_str(); + + // apparently some web servers do not send the "bytes" + // in their content-range. Don't treat it as an error + // if we can't find it, just assume the byte counters + // start immediately + if (string_begins_no_case("bytes ", ptr)) ptr += 6; + char* end; + m_range_start = strtoll(ptr, &end, 10); + if (end == ptr) success = false; + else if (*end != '-') success = false; + else + { + ptr = end + 1; + m_range_end = strtoll(ptr, &end, 10); + if (end == ptr) success = false; + } + + if (!success || m_range_end < m_range_start) + { + m_state = error_state; + error = true; + return ret; + } + // the http range is inclusive + m_content_length = m_range_end - m_range_start + 1; + } + else if (name == "transfer-encoding") + { + m_chunked_encoding = string_begins_no_case("chunked", value.c_str()); + } + + TORRENT_ASSERT(m_recv_pos <= recv_buffer.left()); + newline = std::find(pos, recv_buffer.end, '\n'); + } + boost::get<1>(ret) += newline - (m_recv_buffer.begin + start_pos); + } + + if (m_state == read_body) + { + int incoming = recv_buffer.end - pos; + + if (m_chunked_encoding && (m_flags & dont_parse_chunks) == 0) + { + if (m_cur_chunk_end == -1) + m_cur_chunk_end = m_body_start_pos; + + while (m_cur_chunk_end <= m_recv_pos + incoming && !m_finished && incoming > 0) + { + size_type payload = m_cur_chunk_end - m_recv_pos; + if (payload > 0) + { + TORRENT_ASSERT(payload < INT_MAX); + m_recv_pos += payload; + boost::get<0>(ret) += int(payload); + incoming -= int(payload); + } + buffer::const_interval buf(recv_buffer.begin + m_cur_chunk_end, recv_buffer.end); + size_type chunk_size; + int header_size; + if (parse_chunk_header(buf, &chunk_size, &header_size)) + { + if (chunk_size > 0) + { + std::pair chunk_range(m_cur_chunk_end + header_size + , m_cur_chunk_end + header_size + chunk_size); + m_chunked_ranges.push_back(chunk_range); + } + m_cur_chunk_end += header_size + chunk_size; + if (chunk_size == 0) + { + m_finished = true; + TORRENT_ASSERT(m_content_length < 0 || m_recv_pos - m_body_start_pos + - m_chunk_header_size == m_content_length); + } + header_size -= m_partial_chunk_header; + m_partial_chunk_header = 0; +// fprintf(stderr, "parse_chunk_header(%d, -> %d, -> %d) -> %d\n" +// " incoming = %d\n m_recv_pos = %d\n m_cur_chunk_end = %d\n" +// " content-length = %d\n" +// , buf.left(), int(chunk_size), header_size, 1, incoming, int(m_recv_pos) +// , m_cur_chunk_end, int(m_content_length)); + } + else + { + m_partial_chunk_header += incoming; + header_size = incoming; + +// fprintf(stderr, "parse_chunk_header(%d, -> %d, -> %d) -> %d\n" +// " incoming = %d\n m_recv_pos = %d\n m_cur_chunk_end = %d\n" +// " content-length = %d\n" +// , buf.left(), int(chunk_size), header_size, 0, incoming, int(m_recv_pos) +// , m_cur_chunk_end, int(m_content_length)); + } + m_chunk_header_size += header_size; + m_recv_pos += header_size; + boost::get<1>(ret) += header_size; + incoming -= header_size; + } + if (incoming > 0) + { + m_recv_pos += incoming; + boost::get<0>(ret) += incoming; +// incoming = 0; + } + } + else + { + size_type payload_received = m_recv_pos - m_body_start_pos + incoming; + if (payload_received > m_content_length + && m_content_length >= 0) + { + TORRENT_ASSERT(m_content_length - m_recv_pos + m_body_start_pos < INT_MAX); + incoming = int(m_content_length - m_recv_pos + m_body_start_pos); + } + + TORRENT_ASSERT(incoming >= 0); + m_recv_pos += incoming; + boost::get<0>(ret) += incoming; + } + + if (m_content_length >= 0 + && !m_chunked_encoding + && m_recv_pos - m_body_start_pos >= m_content_length) + { + m_finished = true; + } + } + return ret; + } + + bool http_parser::parse_chunk_header(buffer::const_interval buf + , size_type* chunk_size, int* header_size) + { + char const* pos = buf.begin; + + // ignore one optional new-line. This is since each chunk + // is terminated by a newline. we're likely to see one + // before the actual header. + + if (pos < buf.end && pos[0] == '\r') ++pos; + if (pos < buf.end && pos[0] == '\n') ++pos; + if (pos == buf.end) return false; + + char const* newline = std::find(pos, buf.end, '\n'); + if (newline == buf.end) return false; + ++newline; + + // the chunk header is a single line, a hex length of the + // chunk followed by an optional semi-colon with a comment + // in case the length is 0, the stream is terminated and + // there are extra tail headers, which is terminated by an + // empty line + + // first, read the chunk length + *chunk_size = strtoll(pos, 0, 16); + if (*chunk_size != 0) + { + *header_size = newline - buf.begin; + // the newline alone is two bytes + TORRENT_ASSERT(newline - buf.begin > 2); + return true; + } + + // this is the terminator of the stream. Also read headers + std::map tail_headers; + pos = newline; + newline = std::find(pos, buf.end, '\n'); + + std::string line; + while (newline != buf.end) + { + // if the LF character is preceeded by a CR + // charachter, don't copy it into the line string. + char const* line_end = newline; + if (pos != line_end && *(line_end - 1) == '\r') --line_end; + line.assign(pos, line_end); + ++newline; + pos = newline; + + std::string::size_type separator = line.find(':'); + if (separator == std::string::npos) + { + // this means we got a blank line, + // the header is finished and the body + // starts. + *header_size = newline - buf.begin; + + // the newline alone is two bytes + TORRENT_ASSERT(newline - buf.begin > 2); + + // we were successfull in parsing the headers. + // add them to the headers in the parser + for (std::map::const_iterator i = tail_headers.begin(); + i != tail_headers.end(); ++i) + m_header.insert(std::make_pair(i->first, i->second)); + + return true; + } + + std::string name = line.substr(0, separator); + std::transform(name.begin(), name.end(), name.begin(), &to_lower); + ++separator; + // skip whitespace + while (separator < line.size() + && (line[separator] == ' ' || line[separator] == '\t')) + ++separator; + std::string value = line.substr(separator, std::string::npos); + tail_headers.insert(std::make_pair(name, value)); +// fprintf(stderr, "tail_header: %s: %s\n", name.c_str(), value.c_str()); + + newline = std::find(pos, buf.end, '\n'); + } + return false; + } + + buffer::const_interval http_parser::get_body() const + { + TORRENT_ASSERT(m_state == read_body); + size_type last_byte = m_chunked_encoding && !m_chunked_ranges.empty() + ? (std::min)(m_chunked_ranges.back().second, m_recv_pos) + : m_content_length < 0 + ? m_recv_pos : (std::min)(m_body_start_pos + m_content_length, m_recv_pos); + + TORRENT_ASSERT(last_byte >= m_body_start_pos); + return buffer::const_interval(m_recv_buffer.begin + m_body_start_pos + , m_recv_buffer.begin + last_byte); + } + + void http_parser::reset() + { + m_method.clear(); + m_recv_pos = 0; + m_body_start_pos = 0; + m_status_code = -1; + m_content_length = -1; + m_range_start = -1; + m_range_end = -1; + m_finished = false; + m_state = read_status; + m_recv_buffer.begin = 0; + m_recv_buffer.end = 0; + m_header.clear(); + m_chunked_encoding = false; + m_chunked_ranges.clear(); + m_cur_chunk_end = -1; + m_chunk_header_size = 0; + m_partial_chunk_header = 0; + } + + int http_parser::collapse_chunk_headers(char* buffer, int size) const + { + if (!chunked_encoding()) return size; + + // go through all chunks and compact them + // since we're bottled, and the buffer is our after all + // it's OK to mutate it + char* write_ptr = (char*)buffer; + // the offsets in the array are from the start of the + // buffer, not start of the body, so subtract the size + // of the HTTP header from them + int offset = body_start(); + std::vector > const& c = chunks(); + for (std::vector >::const_iterator i = c.begin() + , end(c.end()); i != end; ++i) + { + TORRENT_ASSERT(i->second - i->first < INT_MAX); + TORRENT_ASSERT(i->second - offset <= size); + int len = int(i->second - i->first); + if (i->first - offset + len > size) len = size - int(i->first) + offset; + memmove(write_ptr, buffer + i->first - offset, len); + write_ptr += len; + } + size = write_ptr - buffer; + return size; + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/http_seed_connection.cpp b/apps/Launcher/ext/libtorrent/src/http_seed_connection.cpp new file mode 100644 index 0000000000..a39fbedd7d --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/http_seed_connection.cpp @@ -0,0 +1,461 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include + +#include "libtorrent/http_seed_connection.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/identify_client.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/version.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/parse_url.hpp" +#include "libtorrent/peer_info.hpp" + +using boost::shared_ptr; +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + http_seed_connection::http_seed_connection( + session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote + , web_seed_entry& web) + : web_connection_base(ses, t, s, remote, web) + , m_url(web.url) + , m_response_left(0) + , m_chunk_pos(0) + , m_partial_chunk_header(0) + { + INVARIANT_CHECK; + + if (!ses.settings().report_web_seed_downloads) + ignore_stats(true); + + shared_ptr tor = t.lock(); + TORRENT_ASSERT(tor); + int blocks_per_piece = tor->torrent_file().piece_length() / tor->block_size(); + + // multiply with the blocks per piece since that many requests are + // merged into one http request + max_out_request_queue(ses.settings().urlseed_pipeline_size + * blocks_per_piece); + + prefer_whole_pieces(1); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** http_seed_connection"); +#endif + } + + void http_seed_connection::disconnect(error_code const& ec, int error) + { + boost::shared_ptr t = associated_torrent().lock(); + peer_connection::disconnect(ec, error); + if (t) t->disconnect_web_seed(this); + } + + boost::optional + http_seed_connection::downloading_piece_progress() const + { + if (m_requests.empty()) + return boost::optional(); + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + piece_block_progress ret; + + peer_request const& pr = m_requests.front(); + ret.piece_index = pr.piece; + if (!m_parser.header_finished()) + { + ret.bytes_downloaded = 0; + } + else + { + int receive_buffer_size = receive_buffer().left() - m_parser.body_start(); + // TODO: 1 in chunked encoding mode, this assert won't hold. + // the chunk headers should be subtracted from the receive_buffer_size + TORRENT_ASSERT_VAL(receive_buffer_size <= t->block_size(), receive_buffer_size); + ret.bytes_downloaded = t->block_size() - receive_buffer_size; + } + // this is used to make sure that the block_index stays within + // bounds. If the entire piece is downloaded, the block_index + // would otherwise point to one past the end + int correction = ret.bytes_downloaded ? -1 : 0; + ret.block_index = (pr.start + ret.bytes_downloaded + correction) / t->block_size(); + ret.full_block_bytes = t->block_size(); + const int last_piece = t->torrent_file().num_pieces() - 1; + if (ret.piece_index == last_piece && ret.block_index + == t->torrent_file().piece_size(last_piece) / t->block_size()) + ret.full_block_bytes = t->torrent_file().piece_size(last_piece) % t->block_size(); + return ret; + } + + void http_seed_connection::write_request(peer_request const& r) + { + INVARIANT_CHECK; + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + TORRENT_ASSERT(t->valid_metadata()); + // http_seeds don't support requesting more than one piece + // at a time + TORRENT_ASSERT(r.length <= t->torrent_file().piece_size(r.piece)); + + std::string request; + request.reserve(400); + + int size = r.length; + const int block_size = t->block_size(); + const int piece_size = t->torrent_file().piece_length(); + peer_request pr; + while (size > 0) + { + int request_offset = r.start + r.length - size; + pr.start = request_offset % piece_size; + pr.length = (std::min)(block_size, size); + pr.piece = r.piece + request_offset / piece_size; + m_requests.push_back(pr); + size -= pr.length; + } + + proxy_settings const& ps = m_ses.proxy(); + bool using_proxy = (ps.type == proxy_settings::http + || ps.type == proxy_settings::http_pw) && !m_ssl; + + request += "GET "; + request += using_proxy ? m_url : m_path; + request += "?info_hash="; + request += escape_string((char const*)&t->torrent_file().info_hash()[0], 20); + request += "&piece="; + request += to_string(r.piece).elems; + + // if we're requesting less than an entire piece we need to + // add ranges + if (r.start > 0 || r.length != t->torrent_file().piece_size(r.piece)) + { + request += "&ranges="; + request += to_string(r.start).elems; + request += "-"; + // ranges are inclusive, just like HTTP + request += to_string(r.start + r.length - 1).elems; + } + + request += " HTTP/1.1\r\n"; + add_headers(request, ps, using_proxy); + request += "\r\n\r\n"; + m_first_request = false; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> %s", request.c_str()); +#endif + + send_buffer(request.c_str(), request.size(), message_type_request); + } + + // -------------------------- + // RECEIVE DATA + // -------------------------- + + void http_seed_connection::on_receive(error_code const& error + , std::size_t bytes_transferred) + { + INVARIANT_CHECK; + + if (error) + { + m_statistics.received_bytes(0, bytes_transferred); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** http_seed_connection error: %s", error.message().c_str()); +#endif + return; + } + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + for (;;) + { + buffer::const_interval recv_buffer = receive_buffer(); + + if (bytes_transferred == 0) break; + TORRENT_ASSERT(recv_buffer.left() > 0); + + TORRENT_ASSERT(!m_requests.empty()); + if (m_requests.empty()) + { + m_statistics.received_bytes(0, bytes_transferred); + disconnect(errors::http_error, 2); + return; + } + + peer_request front_request = m_requests.front(); + + bool header_finished = m_parser.header_finished(); + if (!header_finished) + { + bool parse_error = false; + int protocol = 0; + int payload = 0; + boost::tie(payload, protocol) = m_parser.incoming(recv_buffer, parse_error); + m_statistics.received_bytes(0, protocol); + bytes_transferred -= protocol; +#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS + if (payload > front_request.length) payload = front_request.length; +#endif + + if (parse_error) + { + m_statistics.received_bytes(0, bytes_transferred); + disconnect(errors::http_parse_error, 2); + return; + } + + TORRENT_ASSERT(recv_buffer.left() == 0 || *recv_buffer.begin == 'H'); + + TORRENT_ASSERT(recv_buffer.left() <= packet_size()); + + // this means the entire status line hasn't been received yet + if (m_parser.status_code() == -1) + { + TORRENT_ASSERT(payload == 0); + TORRENT_ASSERT(bytes_transferred == 0); + break; + } + + // if the status code is not one of the accepted ones, abort + if (!is_ok_status(m_parser.status_code())) + { + int retry_time = atoi(m_parser.header("retry-after").c_str()); + if (retry_time <= 0) retry_time = 5 * 60; + // temporarily unavailable, retry later + t->retry_web_seed(this, retry_time); + + std::string error_msg = to_string(m_parser.status_code()).elems + + (" " + m_parser.message()); + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(url_seed_alert(t->get_handle(), url() + , error_msg)); + } + m_statistics.received_bytes(0, bytes_transferred); + disconnect(error_code(m_parser.status_code(), get_http_category()), 1); + return; + } + if (!m_parser.header_finished()) + { + TORRENT_ASSERT(payload == 0); + TORRENT_ASSERT(bytes_transferred == 0); + break; + } + } + + // we just completed reading the header + if (!header_finished) + { + if (is_redirect(m_parser.status_code())) + { + // this means we got a redirection request + // look for the location header + std::string location = m_parser.header("location"); + m_statistics.received_bytes(0, bytes_transferred); + + if (location.empty()) + { + // we should not try this server again. + t->remove_web_seed(this); + disconnect(errors::missing_location, 2); + return; + } + + // add the redirected url and remove the current one + t->add_web_seed(location, web_seed_entry::http_seed); + t->remove_web_seed(this); + disconnect(errors::redirecting, 2); + return; + } + + std::string const& server_version = m_parser.header("server"); + if (!server_version.empty()) + { + m_server_string = "URL seed @ "; + m_server_string += m_host; + m_server_string += " ("; + m_server_string += server_version; + m_server_string += ")"; + } + + m_response_left = atol(m_parser.header("content-length").c_str()); + if (m_response_left == -1) + { + m_statistics.received_bytes(0, bytes_transferred); + // we should not try this server again. + t->remove_web_seed(this); + disconnect(errors::no_content_length, 2); + return; + } + if (m_response_left != front_request.length) + { + m_statistics.received_bytes(0, bytes_transferred); + // we should not try this server again. + t->remove_web_seed(this); + disconnect(errors::invalid_range, 2); + return; + } + m_body_start = m_parser.body_start(); + } + + recv_buffer.begin += m_body_start; + + // ========================= + // === CHUNKED ENCODING === + // ========================= + while (m_parser.chunked_encoding() + && m_chunk_pos >= 0 + && m_chunk_pos < recv_buffer.left()) + { + int header_size = 0; + size_type chunk_size = 0; + buffer::const_interval chunk_start = recv_buffer; + chunk_start.begin += m_chunk_pos; + TORRENT_ASSERT(chunk_start.begin[0] == '\r' || is_hex(chunk_start.begin, 1)); + bool ret = m_parser.parse_chunk_header(chunk_start, &chunk_size, &header_size); + if (!ret) + { + TORRENT_ASSERT(bytes_transferred >= size_t(chunk_start.left() - m_partial_chunk_header)); + bytes_transferred -= chunk_start.left() - m_partial_chunk_header; + m_statistics.received_bytes(0, chunk_start.left() - m_partial_chunk_header); + m_partial_chunk_header = chunk_start.left(); + if (bytes_transferred == 0) return; + break; + } + else + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** parsed chunk: %d header_size: %d", chunk_size, header_size); +#endif + TORRENT_ASSERT(bytes_transferred >= size_t(header_size - m_partial_chunk_header)); + bytes_transferred -= header_size - m_partial_chunk_header; + + m_statistics.received_bytes(0, header_size - m_partial_chunk_header); + m_partial_chunk_header = 0; + TORRENT_ASSERT(chunk_size != 0 || chunk_start.left() <= header_size || chunk_start.begin[header_size] == 'H'); + // cut out the chunk header from the receive buffer + TORRENT_ASSERT(m_chunk_pos + m_body_start < INT_MAX); + cut_receive_buffer(header_size, t->block_size() + 1024, int(m_chunk_pos + m_body_start)); + recv_buffer = receive_buffer(); + recv_buffer.begin += m_body_start; + m_chunk_pos += chunk_size; + if (chunk_size == 0) + { + TORRENT_ASSERT(receive_buffer().left() < m_chunk_pos + m_body_start + 1 + || receive_buffer()[int(m_chunk_pos + m_body_start)] == 'H' + || (m_parser.chunked_encoding() && receive_buffer()[int(m_chunk_pos + m_body_start)] == '\r')); + m_chunk_pos = -1; + } + } + } + + int payload = bytes_transferred; + if (payload > m_response_left) payload = int(m_response_left); + if (payload > front_request.length) payload = front_request.length; + m_statistics.received_bytes(payload, 0); + incoming_piece_fragment(payload); + m_response_left -= payload; + + if (m_parser.status_code() == 503) + { + if (!m_parser.finished()) return; + + int retry_time = atol(std::string(recv_buffer.begin, recv_buffer.end).c_str()); + if (retry_time <= 0) retry_time = 60; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** retrying in %d seconds", retry_time); +#endif + + m_statistics.received_bytes(0, bytes_transferred); + // temporarily unavailable, retry later + t->retry_web_seed(this, retry_time); + disconnect(error_code(m_parser.status_code(), get_http_category()), 1); + return; + } + + + // we only received the header, no data + if (recv_buffer.left() == 0) break; + + if (recv_buffer.left() < front_request.length) break; + + // if the response is chunked, we need to receive the last + // terminating chunk and the tail headers before we can proceed + if (m_parser.chunked_encoding() && m_chunk_pos >= 0) break; + + m_requests.pop_front(); + incoming_piece(front_request, recv_buffer.begin); + if (associated_torrent().expired()) return; + + int size_to_cut = m_body_start + front_request.length; + TORRENT_ASSERT(receive_buffer().left() < size_to_cut + 1 + || receive_buffer()[size_to_cut] == 'H' + || (m_parser.chunked_encoding() && receive_buffer()[size_to_cut] == '\r')); + + cut_receive_buffer(size_to_cut, t->block_size() + 1024); + if (m_response_left == 0) m_chunk_pos = 0; + else m_chunk_pos -= front_request.length; + bytes_transferred -= payload; + m_body_start = 0; + if (m_response_left > 0) continue; + TORRENT_ASSERT(m_response_left == 0); + m_parser.reset(); + } + } + + void http_seed_connection::get_specific_peer_info(peer_info& p) const + { + web_connection_base::get_specific_peer_info(p); + p.flags |= peer_info::local_connection; + p.connection_type = peer_info::http_seed; + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/http_stream.cpp b/apps/Launcher/ext/libtorrent/src/http_stream.cpp new file mode 100644 index 0000000000..0a84d84e19 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/http_stream.cpp @@ -0,0 +1,175 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/http_stream.hpp" +#include "libtorrent/escape_string.hpp" // for base64encode +#include "libtorrent/socket_io.hpp" + +namespace libtorrent +{ + + void http_stream::name_lookup(error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h) + { + if (e || i == tcp::resolver::iterator()) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + m_sock.async_connect(i->endpoint(), boost::bind( + &http_stream::connected, this, _1, h)); + } + + void http_stream::connected(error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + using namespace libtorrent::detail; + + if (m_no_connect) + { + std::vector().swap(m_buffer); + (*h)(e); + return; + } + + // send CONNECT + std::back_insert_iterator > p(m_buffer); + std::string endpoint; + if (!m_hostname.empty()) + { + endpoint = m_hostname + ':' + to_string(m_remote_endpoint.port()).elems; + } + else + { + endpoint = print_endpoint(m_remote_endpoint); + } + write_string("CONNECT " + endpoint + " HTTP/1.0\r\n", p); + if (!m_user.empty()) + { + write_string("Proxy-Authorization: Basic " + base64encode( + m_user + ":" + m_password) + "\r\n", p); + } + write_string("\r\n", p); + async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&http_stream::handshake1, this, _1, h)); + } + + void http_stream::handshake1(error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + // read one byte from the socket + m_buffer.resize(1); + async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&http_stream::handshake2, this, _1, h)); + } + + void http_stream::handshake2(error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + int read_pos = m_buffer.size(); + // look for \n\n and \r\n\r\n + // both of which means end of http response header + bool found_end = false; + if (m_buffer[read_pos - 1] == '\n' && read_pos > 2) + { + if (m_buffer[read_pos - 2] == '\n') + { + found_end = true; + } + else if (read_pos > 4 + && m_buffer[read_pos - 2] == '\r' + && m_buffer[read_pos - 3] == '\n' + && m_buffer[read_pos - 4] == '\r') + { + found_end = true; + } + } + + if (found_end) + { + m_buffer.push_back(0); + char* status = std::strchr(&m_buffer[0], ' '); + if (status == 0) + { + (*h)(asio::error::operation_not_supported); + error_code ec; + close(ec); + return; + } + + status++; + int code = std::atoi(status); + if (code != 200) + { + (*h)(asio::error::operation_not_supported); + error_code ec; + close(ec); + return; + } + + (*h)(e); + std::vector().swap(m_buffer); + return; + } + + // read another byte from the socket + m_buffer.resize(read_pos + 1); + async_read(m_sock, asio::buffer(&m_buffer[0] + read_pos, 1) + , boost::bind(&http_stream::handshake2, this, _1, h)); + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/http_tracker_connection.cpp b/apps/Launcher/ext/libtorrent/src/http_tracker_connection.cpp new file mode 100644 index 0000000000..70eb3ea58b --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/http_tracker_connection.cpp @@ -0,0 +1,544 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include + +#include "libtorrent/config.hpp" +#include "libtorrent/gzip.hpp" +#include "libtorrent/socket_io.hpp" + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/http_connection.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/broadcast_socket.hpp" // for is_local + +using namespace libtorrent; + +namespace libtorrent +{ +#if TORRENT_USE_I2P + // defined in torrent_info.cpp + bool is_i2p_url(std::string const& url); +#endif + + http_tracker_connection::http_tracker_connection( + io_service& ios + , connection_queue& cc + , tracker_manager& man + , tracker_request const& req + , boost::weak_ptr c + , aux::session_impl const& ses + , proxy_settings const& ps + , std::string const& auth +#if TORRENT_USE_I2P + , i2p_connection* i2p_conn +#endif + ) + : tracker_connection(man, req, ios, c) + , m_man(man) + , m_ses(ses) + , m_ps(ps) + , m_cc(cc) + , m_ios(ios) +#if TORRENT_USE_I2P + , m_i2p_conn(i2p_conn) +#endif + {} + + void http_tracker_connection::start() + { + // TODO: 0 support authentication (i.e. user name and password) in the URL + std::string url = tracker_req().url; + + if (tracker_req().kind == tracker_request::scrape_request) + { + // find and replace "announce" with "scrape" + // in request + + std::size_t pos = url.find("announce"); + if (pos == std::string::npos) + { + tracker_connection::fail(error_code(errors::scrape_not_available)); + return; + } + url.replace(pos, 8, "scrape"); + } + +#if TORRENT_USE_I2P + bool i2p = is_i2p_url(url); +#else + static const bool i2p = false; +#endif + + session_settings const& settings = m_ses.settings(); + + // if request-string already contains + // some parameters, append an ampersand instead + // of a question mark + size_t arguments_start = url.find('?'); + if (arguments_start != std::string::npos) + url += "&"; + else + url += "?"; + + if (tracker_req().kind == tracker_request::announce_request) + { + const char* event_string[] = {"completed", "started", "stopped", "paused"}; + + char str[1024]; + const bool stats = tracker_req().send_stats; + snprintf(str, sizeof(str) + , "info_hash=%s" + "&peer_id=%s" + "&port=%d" + "&uploaded=%" PRId64 + "&downloaded=%" PRId64 + "&left=%" PRId64 + "&corrupt=%" PRId64 + "&key=%X" + "%s%s" // event + "&numwant=%d" + "&compact=1" + "&no_peer_id=1" + , escape_string((const char*)&tracker_req().info_hash[0], 20).c_str() + , escape_string((const char*)&tracker_req().pid[0], 20).c_str() + // the i2p tracker seems to verify that the port is not 0, + // even though it ignores it otherwise + , i2p ? 1 : tracker_req().listen_port + , stats ? tracker_req().uploaded : 0 + , stats ? tracker_req().downloaded : 0 + , stats ? tracker_req().left : 0 + , stats ? tracker_req().corrupt : 0 + , tracker_req().key + , (tracker_req().event != tracker_request::none) ? "&event=" : "" + , (tracker_req().event != tracker_request::none) ? event_string[tracker_req().event - 1] : "" + , tracker_req().num_want); + url += str; +#ifndef TORRENT_DISABLE_ENCRYPTION + if (m_ses.get_pe_settings().in_enc_policy != pe_settings::disabled) + url += "&supportcrypto=1"; +#endif + if (stats && m_ses.settings().report_redundant_bytes) + { + url += "&redundant="; + url += to_string(tracker_req().redundant).elems; + } + if (!tracker_req().trackerid.empty()) + { + std::string id = tracker_req().trackerid; + url += "&trackerid="; + url += escape_string(id.c_str(), id.length()); + } + +#if TORRENT_USE_I2P + if (i2p) + { + url += "&ip="; + url += escape_string(m_i2p_conn->local_endpoint().c_str() + , m_i2p_conn->local_endpoint().size()); + url += ".i2p"; + } + else +#endif + if (!m_ses.settings().anonymous_mode) + { + if (!settings.announce_ip.empty()) + { + url += "&ip=" + escape_string( + settings.announce_ip.c_str(), settings.announce_ip.size()); + } + else if (m_ses.settings().announce_double_nat + && is_local(m_ses.listen_address())) + { + // only use the global external listen address here + // if it turned out to be on a local network + // since otherwise the tracker should use our + // source IP to determine our origin + url += "&ip=" + print_address(m_ses.listen_address()); + } + } + } + + m_tracker_connection.reset(new http_connection(m_ios, m_cc + , boost::bind(&http_tracker_connection::on_response, self(), _1, _2, _3, _4) + , true, settings.max_http_recv_buffer_size + , boost::bind(&http_tracker_connection::on_connect, self(), _1) + , boost::bind(&http_tracker_connection::on_filter, self(), _1, _2) +#ifdef TORRENT_USE_OPENSSL + , tracker_req().ssl_ctx +#endif + )); + + int timeout = tracker_req().event==tracker_request::stopped + ?settings.stop_tracker_timeout + :settings.tracker_completion_timeout; + + m_tracker_connection->get(url, seconds(timeout) + , tracker_req().event == tracker_request::stopped ? 2 : 1 + , &m_ps, 5, settings.anonymous_mode ? "" : settings.user_agent + , bind_interface() +#if TORRENT_USE_I2P + , m_i2p_conn +#endif + ); + + // the url + 100 estimated header size + sent_bytes(url.size() + 100); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + + boost::shared_ptr cb = requester(); + if (cb) + { + cb->debug_log("==> TRACKER_REQUEST [ url: %s ]", url.c_str()); + } +#endif + } + + void http_tracker_connection::close() + { + if (m_tracker_connection) + { + m_tracker_connection->close(); + m_tracker_connection.reset(); + } + tracker_connection::close(); + } + + void http_tracker_connection::on_filter(http_connection& c, std::list& endpoints) + { + if (tracker_req().apply_ip_filter == false) return; + + // remove endpoints that are filtered by the IP filter + for (std::list::iterator i = endpoints.begin(); + i != endpoints.end();) + { + if (m_ses.m_ip_filter.access(i->address()) == ip_filter::blocked) + i = endpoints.erase(i); + else + ++i; + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + boost::shared_ptr cb = requester(); + if (cb) + { + cb->debug_log("*** TRACKER_FILTER"); + } +#endif + if (endpoints.empty()) + fail(error_code(errors::banned_by_ip_filter)); + } + + void http_tracker_connection::on_connect(http_connection& c) + { + error_code ec; + tcp::endpoint ep = c.socket().remote_endpoint(ec); + m_tracker_ip = ep.address(); + boost::shared_ptr cb = requester(); + if (cb) cb->m_tracker_address = ep; + } + + void http_tracker_connection::on_response(error_code const& ec + , http_parser const& parser, char const* data, int size) + { + // keep this alive + boost::intrusive_ptr me(this); + + if (ec && ec != asio::error::eof) + { + fail(ec); + return; + } + + if (!parser.header_finished()) + { + fail(asio::error::eof); + return; + } + + if (parser.status_code() != 200) + { + fail(error_code(parser.status_code(), get_http_category()) + , parser.status_code(), parser.message().c_str()); + return; + } + + if (ec && ec != asio::error::eof) + { + fail(ec, parser.status_code()); + return; + } + + received_bytes(size + parser.body_start()); + + // handle tracker response + lazy_entry e; + error_code ecode; + int res = lazy_bdecode(data, data + size, e, ecode); + + if (res == 0 && e.type() == lazy_entry::dict_t) + { + parse(parser.status_code(), e); + } + else + { + fail(ecode, parser.status_code()); + } + close(); + } + + bool http_tracker_connection::extract_peer_info(lazy_entry const& info, peer_entry& ret) + { + // extract peer id (if any) + if (info.type() != lazy_entry::dict_t) + { + fail(error_code(errors::invalid_peer_dict)); + return false; + } + lazy_entry const* i = info.dict_find_string("peer id"); + if (i != 0 && i->string_length() == 20) + { + std::copy(i->string_ptr(), i->string_ptr()+20, ret.pid.begin()); + } + else + { + // if there's no peer_id, just initialize it to a bunch of zeroes + std::fill_n(ret.pid.begin(), 20, 0); + } + + // extract ip + i = info.dict_find_string("ip"); + if (i == 0) + { + fail(error_code(errors::invalid_tracker_response)); + return false; + } + ret.ip = i->string_value(); + + // extract port + i = info.dict_find_int("port"); + if (i == 0) + { + fail(error_code(errors::invalid_tracker_response)); + return false; + } + ret.port = (unsigned short)i->int_value(); + + return true; + } + + void http_tracker_connection::parse(int status_code, lazy_entry const& e) + { + boost::shared_ptr cb = requester(); + if (!cb) return; + + int interval = int(e.dict_find_int_value("interval", 0)); + int min_interval = int(e.dict_find_int_value("min interval", 30)); + + // if no interval is specified, default to 30 minutes + if (interval == 0) interval = 1800; + + std::string trackerid; + lazy_entry const* tracker_id = e.dict_find_string("tracker id"); + if (tracker_id) + trackerid = tracker_id->string_value(); + // parse the response + lazy_entry const* failure = e.dict_find_string("failure reason"); + if (failure) + { + fail(error_code(errors::tracker_failure), status_code + , failure->string_value().c_str(), interval, min_interval); + return; + } + + lazy_entry const* warning = e.dict_find_string("warning message"); + if (warning) + cb->tracker_warning(tracker_req(), warning->string_value()); + + std::vector peer_list; + + if (tracker_req().kind == tracker_request::scrape_request) + { + std::string ih = tracker_req().info_hash.to_string(); + + lazy_entry const* files = e.dict_find_dict("files"); + if (files == 0) + { + fail(error_code(errors::invalid_files_entry), -1, "" + , interval, min_interval); + return; + } + + lazy_entry const* scrape_data = files->dict_find_dict(ih); + if (scrape_data == 0) + { + fail(error_code(errors::invalid_hash_entry), -1, "" + , interval, min_interval); + return; + } + + int complete = int(scrape_data->dict_find_int_value("complete", -1)); + int incomplete = int(scrape_data->dict_find_int_value("incomplete", -1)); + int downloaded = int(scrape_data->dict_find_int_value("downloaded", -1)); + int downloaders = int(scrape_data->dict_find_int_value("downloaders", -1)); + cb->tracker_scrape_response(tracker_req(), complete + , incomplete, downloaded, downloaders); + return; + } + + lazy_entry const* peers_ent = e.dict_find("peers"); + if (peers_ent && peers_ent->type() == lazy_entry::string_t) + { + char const* peers = peers_ent->string_ptr(); + int len = peers_ent->string_length(); + for (int i = 0; i < len; i += 6) + { + if (len - i < 6) break; + + peer_entry p; + p.pid.clear(); + error_code ec; + p.ip = detail::read_v4_address(peers).to_string(ec); + p.port = detail::read_uint16(peers); + if (ec) continue; + peer_list.push_back(p); + } + } + else if (peers_ent && peers_ent->type() == lazy_entry::list_t) + { + int len = peers_ent->list_size(); + for (int i = 0; i < len; ++i) + { + peer_entry p; + if (!extract_peer_info(*peers_ent->list_at(i), p)) return; + peer_list.push_back(p); + } + } + else + { + peers_ent = 0; + } + +#if TORRENT_USE_IPV6 + lazy_entry const* ipv6_peers = e.dict_find_string("peers6"); + if (ipv6_peers) + { + char const* peers = ipv6_peers->string_ptr(); + int len = ipv6_peers->string_length(); + for (int i = 0; i < len; i += 18) + { + if (len - i < 18) break; + + peer_entry p; + p.pid.clear(); + error_code ec; + p.ip = detail::read_v6_address(peers).to_string(ec); + p.port = detail::read_uint16(peers); + if (ec) continue; + peer_list.push_back(p); + } + } + else + { + ipv6_peers = 0; + } +#else + lazy_entry const* ipv6_peers = 0; +#endif + + // if we didn't receive any peers. We don't care if we're stopping anyway + if (peers_ent == 0 && ipv6_peers == 0 + && tracker_req().event != tracker_request::stopped) + { + fail(error_code(errors::invalid_peers_entry), -1, "" + , interval, min_interval); + return; + } + + + // look for optional scrape info + address external_ip; + + lazy_entry const* ip_ent = e.dict_find_string("external ip"); + if (ip_ent) + { + char const* p = ip_ent->string_ptr(); + if (ip_ent->string_length() == int(address_v4::bytes_type().size())) + external_ip = detail::read_v4_address(p); +#if TORRENT_USE_IPV6 + else if (ip_ent->string_length() == int(address_v6::bytes_type().size())) + external_ip = detail::read_v6_address(p); +#endif + } + + int complete = int(e.dict_find_int_value("complete", -1)); + int incomplete = int(e.dict_find_int_value("incomplete", -1)); + int downloaded = int(e.dict_find_int_value("downloaded", -1)); + + std::list
ip_list; + if (m_tracker_connection) + { + error_code ec; + ip_list.push_back(m_tracker_connection->socket().remote_endpoint(ec).address()); + std::list const& epts = m_tracker_connection->endpoints(); + for (std::list::const_iterator i = epts.begin() + , end(epts.end()); i != end; ++i) + { + ip_list.push_back(i->address()); + } + } + + cb->tracker_response(tracker_req(), m_tracker_ip, ip_list, peer_list + , interval, min_interval, complete, incomplete, downloaded, external_ip, trackerid); + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/i2p_stream.cpp b/apps/Launcher/ext/libtorrent/src/i2p_stream.cpp new file mode 100644 index 0000000000..35c64a63f3 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/i2p_stream.cpp @@ -0,0 +1,524 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" +#include "libtorrent/i2p_stream.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/string_util.hpp" + +#if TORRENT_USE_I2P + +#include + +#if defined TORRENT_ASIO_DEBUGGING +#include "libtorrent/debug.hpp" +#endif + +namespace libtorrent +{ + + struct i2p_error_category : boost::system::error_category + { + virtual const char* name() const BOOST_SYSTEM_NOEXCEPT + { return "i2p error"; } + virtual std::string message(int ev) const BOOST_SYSTEM_NOEXCEPT + { + static char const* messages[] = + { + "no error", + "parse failed", + "cannot reach peer", + "i2p error", + "invalid key", + "invalid id", + "timeout", + "key not found", + "duplicated id" + }; + + if (ev < 0 || ev >= i2p_error::num_errors) return "unknown error"; + return messages[ev]; + } + virtual boost::system::error_condition default_error_condition( + int ev) const BOOST_SYSTEM_NOEXCEPT + { return boost::system::error_condition(ev, *this); } + }; + + + boost::system::error_category& get_i2p_category() + { + static i2p_error_category i2p_category; + return i2p_category; + } + + namespace i2p_error + { + boost::system::error_code make_error_code(i2p_error_code e) + { + return error_code(e, get_i2p_category()); + } + } + + i2p_connection::i2p_connection(io_service& ios) + : m_state(sam_idle) + , m_io_service(ios) + {} + + i2p_connection::~i2p_connection() + {} + + void i2p_connection::close(error_code& e) + { + if (m_sam_socket) m_sam_socket->close(e); + } + + void i2p_connection::open(proxy_settings const& s, i2p_stream::handler_type const& handler) + { + // we already seem to have a session to this SAM router + if (m_sam_router.hostname == s.hostname + && m_sam_router.port == s.port + && m_sam_socket + && (is_open() || m_state == sam_connecting)) return; + + m_sam_router = s; + m_sam_router.type = proxy_settings::i2p_proxy; + + if (m_sam_router.hostname.empty()) return; + + m_state = sam_connecting; + + char tmp[20]; + std::generate(tmp, tmp + sizeof(tmp), &std::rand); + m_session_id.resize(sizeof(tmp)*2); + to_hex(tmp, 20, &m_session_id[0]); + + m_sam_socket.reset(new i2p_stream(m_io_service)); + m_sam_socket->set_proxy(m_sam_router.hostname, m_sam_router.port); + m_sam_socket->set_command(i2p_stream::cmd_create_session); + m_sam_socket->set_session_id(m_session_id.c_str()); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("i2p_stream::on_sam_connect"); +#endif + m_sam_socket->async_connect(tcp::endpoint() + , boost::bind(&i2p_connection::on_sam_connect, this, _1, handler, m_sam_socket)); + } + + void i2p_connection::on_sam_connect(error_code const& ec, i2p_stream::handler_type const& h, boost::shared_ptr) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("i2p_stream::on_sam_connect"); +#endif + m_state = sam_idle; + + if (ec) + { + h(ec); + return; + } + + do_name_lookup("ME", boost::bind(&i2p_connection::set_local_endpoint, this, _1, _2, h)); + } + + void i2p_connection::set_local_endpoint(error_code const& ec, char const* dest + , i2p_stream::handler_type const& h) + { + if (!ec && dest != 0) + m_i2p_local_endpoint = dest; + else + m_i2p_local_endpoint.clear(); + + h(ec); + } + + void i2p_connection::async_name_lookup(char const* name + , i2p_connection::name_lookup_handler handler) + { + if (m_state == sam_idle && m_name_lookup.empty() && is_open()) + do_name_lookup(name, handler); + else + m_name_lookup.push_back(std::make_pair(std::string(name), handler)); + } + + void i2p_connection::do_name_lookup(std::string const& name + , name_lookup_handler const& handler) + { + TORRENT_ASSERT(m_state == sam_idle); + m_state = sam_name_lookup; + m_sam_socket->set_name_lookup(name.c_str()); + boost::shared_ptr h(new i2p_stream::handler_type( + boost::bind(&i2p_connection::on_name_lookup, this, _1, handler, m_sam_socket))); + m_sam_socket->send_name_lookup(h); + } + + void i2p_connection::on_name_lookup(error_code const& ec + , name_lookup_handler handler, boost::shared_ptr) + { + m_state = sam_idle; + + std::string name = m_sam_socket->name_lookup(); + if (!m_name_lookup.empty()) + { + std::pair& nl = m_name_lookup.front(); + do_name_lookup(nl.first, nl.second); + m_name_lookup.pop_front(); + } + + if (ec) + { + handler(ec, 0); + return; + } + + handler(ec, name.c_str()); + } + + i2p_stream::i2p_stream(io_service& io_service) + : proxy_base(io_service) + , m_id(0) + , m_command(cmd_create_session) + , m_state(0) + { +#if TORRENT_USE_ASSERTS + m_magic = 0x1337; +#endif + } + + i2p_stream::~i2p_stream() + { +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_magic == 0x1337); + m_magic = 0; +#endif + } + +// TODO: move this to proxy_base and use it in all proxies + bool i2p_stream::handle_error(error_code const& e, boost::shared_ptr const& h) + { + TORRENT_ASSERT(m_magic == 0x1337); + if (!e) return false; +// fprintf(stderr, "i2p error \"%s\"\n", e.message().c_str()); + (*h)(e); + return true; + } + + void i2p_stream::do_connect(error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h) + { + TORRENT_ASSERT(m_magic == 0x1337); + if (e || i == tcp::resolver::iterator()) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("i2p_stream::connected"); +#endif + m_sock.async_connect(i->endpoint(), boost::bind( + &i2p_stream::connected, this, _1, h)); + } + + void i2p_stream::connected(error_code const& e, boost::shared_ptr h) + { + TORRENT_ASSERT(m_magic == 0x1337); +#if defined TORRENT_ASIO_DEBUGGING + complete_async("i2p_stream::connected"); +#endif + if (handle_error(e, h)) return; + + // send hello command + m_state = read_hello_response; + static const char cmd[] = "HELLO VERSION MIN=3.0 MAX=3.0\n"; + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("i2p_stream::start_read_line"); +#endif + async_write(m_sock, asio::buffer(cmd, sizeof(cmd) - 1) + , boost::bind(&i2p_stream::start_read_line, this, _1, h)); +// fprintf(stderr, ">>> %s", cmd); + } + + void i2p_stream::start_read_line(error_code const& e, boost::shared_ptr h) + { + TORRENT_ASSERT(m_magic == 0x1337); +#if defined TORRENT_ASIO_DEBUGGING + complete_async("i2p_stream::start_read_line"); +#endif + if (handle_error(e, h)) return; + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("i2p_stream::read_line"); +#endif + m_buffer.resize(1); + async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&i2p_stream::read_line, this, _1, h)); + } + + void i2p_stream::read_line(error_code const& e, boost::shared_ptr h) + { + TORRENT_ASSERT(m_magic == 0x1337); +#if defined TORRENT_ASIO_DEBUGGING + complete_async("i2p_stream::read_line"); +#endif + if (handle_error(e, h)) return; + + int read_pos = m_buffer.size(); + + // look for \n which means end of the response + if (m_buffer[read_pos - 1] != '\n') + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("i2p_stream::read_line"); +#endif + // read another byte from the socket + m_buffer.resize(read_pos + 1); + async_read(m_sock, asio::buffer(&m_buffer[read_pos], 1) + , boost::bind(&i2p_stream::read_line, this, _1, h)); + return; + } + m_buffer[read_pos - 1] = 0; + + if (m_command == cmd_incoming) + { + // this is the line containing the destination + // of the incoming connection in an accept call + m_dest = &m_buffer[0]; + (*h)(e); + std::vector().swap(m_buffer); + return; + } + + error_code invalid_response(i2p_error::parse_failed + , get_i2p_category()); + + // null-terminate the string and parse it + m_buffer.push_back(0); + char* ptr = &m_buffer[0]; + char* next = ptr; + + char const* expect1 = 0; + char const* expect2 = 0; + + switch (m_state) + { + case read_hello_response: + expect1 = "HELLO"; + expect2 = "REPLY"; + break; + case read_connect_response: + case read_accept_response: + expect1 = "STREAM"; + expect2 = "STATUS"; + break; + case read_session_create_response: + expect1 = "SESSION"; + expect2 = "STATUS"; + break; + case read_name_lookup_response: + expect1 = "NAMING"; + expect2 = "REPLY"; + break; + } + +// fprintf(stderr, "<<< %s\n", &m_buffer[0]); + ptr = string_tokenize(next, ' ', &next); + if (ptr == 0 || expect1 == 0 || strcmp(expect1, ptr)) { handle_error(invalid_response, h); return; } + ptr = string_tokenize(next, ' ', &next); + if (ptr == 0 || expect2 == 0 || strcmp(expect2, ptr)) { handle_error(invalid_response, h); return; } + + int result = 0; +// char const* message = 0; +// float version = 3.0f; + + for(;;) + { + char* name = string_tokenize(next, '=', &next); + if (name == 0) break; +// fprintf(stderr, "name=\"%s\"\n", name); + char* ptr = string_tokenize(next, ' ', &next); + if (ptr == 0) { handle_error(invalid_response, h); return; } +// fprintf(stderr, "value=\"%s\"\n", ptr); + + if (strcmp("RESULT", name) == 0) + { + if (strcmp("OK", ptr) == 0) + result = i2p_error::no_error; + else if (strcmp("CANT_REACH_PEER", ptr) == 0) + result = i2p_error::cant_reach_peer; + else if (strcmp("I2P_ERROR", ptr) == 0) + result = i2p_error::i2p_error; + else if (strcmp("INVALID_KEY", ptr) == 0) + result = i2p_error::invalid_key; + else if (strcmp("INVALID_ID", ptr) == 0) + result = i2p_error::invalid_id; + else if (strcmp("TIMEOUT", ptr) == 0) + result = i2p_error::timeout; + else if (strcmp("KEY_NOT_FOUND", ptr) == 0) + result = i2p_error::key_not_found; + else if (strcmp("DUPLICATED_ID", ptr) == 0) + result = i2p_error::duplicated_id; + else + result = i2p_error::num_errors; // unknown error + } + else if (strcmp("MESSAGE", name) == 0) + { +// message = ptr; + } + else if (strcmp("VERSION", name) == 0) + { +// version = float(atof(ptr)); + } + else if (strcmp("VALUE", name) == 0) + { + m_name_lookup = ptr; + } + else if (strcmp("DESTINATION", name) == 0) + { + m_dest = ptr; + } + } + + if (result != i2p_error::no_error) + { + error_code ec(result, get_i2p_category()); + handle_error(ec, h); + return; + } + + switch (m_state) + { + case read_hello_response: + switch (m_command) + { + case cmd_create_session: + send_session_create(h); + break; + case cmd_accept: + send_accept(h); + break; + case cmd_connect: + send_connect(h); + break; + default: + (*h)(e); + std::vector().swap(m_buffer); + } + break; + case read_connect_response: + case read_session_create_response: + case read_name_lookup_response: + (*h)(e); + std::vector().swap(m_buffer); + break; + case read_accept_response: + // the SAM bridge is waiting for an incoming + // connection. + // wait for one more line containing + // the destination of the remote peer + m_command = cmd_incoming; + m_buffer.resize(1); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("i2p_stream::read_line"); +#endif + async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&i2p_stream::read_line, this, _1, h)); + break; + } + + return; + } + + void i2p_stream::send_connect(boost::shared_ptr h) + { + TORRENT_ASSERT(m_magic == 0x1337); + m_state = read_connect_response; + char cmd[1024]; + int size = snprintf(cmd, sizeof(cmd), "STREAM CONNECT ID=%s DESTINATION=%s\n" + , m_id, m_dest.c_str()); +// fprintf(stderr, ">>> %s", cmd); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("i2p_stream::start_read_line"); +#endif + async_write(m_sock, asio::buffer(cmd, size) + , boost::bind(&i2p_stream::start_read_line, this, _1, h)); + } + + void i2p_stream::send_accept(boost::shared_ptr h) + { + TORRENT_ASSERT(m_magic == 0x1337); + m_state = read_accept_response; + char cmd[400]; + int size = snprintf(cmd, sizeof(cmd), "STREAM ACCEPT ID=%s\n", m_id); +// fprintf(stderr, ">>> %s", cmd); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("i2p_stream::start_read_line"); +#endif + async_write(m_sock, asio::buffer(cmd, size) + , boost::bind(&i2p_stream::start_read_line, this, _1, h)); + } + + void i2p_stream::send_session_create(boost::shared_ptr h) + { + TORRENT_ASSERT(m_magic == 0x1337); + m_state = read_session_create_response; + char cmd[400]; + int size = snprintf(cmd, sizeof(cmd), "SESSION CREATE STYLE=STREAM ID=%s DESTINATION=TRANSIENT\n" + , m_id); +// fprintf(stderr, ">>> %s", cmd); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("i2p_stream::start_read_line"); +#endif + async_write(m_sock, asio::buffer(cmd, size) + , boost::bind(&i2p_stream::start_read_line, this, _1, h)); + } + + void i2p_stream::send_name_lookup(boost::shared_ptr h) + { + TORRENT_ASSERT(m_magic == 0x1337); + m_state = read_name_lookup_response; + char cmd[1024]; + int size = snprintf(cmd, sizeof(cmd), "NAMING LOOKUP NAME=%s\n", m_name_lookup.c_str()); +// fprintf(stderr, ">>> %s", cmd); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("i2p_stream::start_read_line"); +#endif + async_write(m_sock, asio::buffer(cmd, size) + , boost::bind(&i2p_stream::start_read_line, this, _1, h)); + } +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/identify_client.cpp b/apps/Launcher/ext/libtorrent/src/identify_client.cpp new file mode 100644 index 0000000000..a5583dfb50 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/identify_client.cpp @@ -0,0 +1,428 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/identify_client.hpp" +#include "libtorrent/fingerprint.hpp" +#include "libtorrent/string_util.hpp" + +namespace +{ + + using namespace libtorrent; + + int decode_digit(char c) + { + if (is_digit(c)) return c - '0'; + return unsigned(c) - 'A' + 10; + } + + // takes a peer id and returns a valid boost::optional + // object if the peer id matched the azureus style encoding + // the returned fingerprint contains information about the + // client's id + boost::optional parse_az_style(const peer_id& id) + { + fingerprint ret("..", 0, 0, 0, 0); + + if (id[0] != '-' || !is_print(id[1]) || (id[2] < '0') + || (id[3] < '0') || (id[4] < '0') + || (id[5] < '0') || (id[6] < '0') + || id[7] != '-') + return boost::optional(); + + ret.name[0] = id[1]; + ret.name[1] = id[2]; + ret.major_version = decode_digit(id[3]); + ret.minor_version = decode_digit(id[4]); + ret.revision_version = decode_digit(id[5]); + ret.tag_version = decode_digit(id[6]); + + return boost::optional(ret); + } + + // checks if a peer id can possibly contain a shadow-style + // identification + boost::optional parse_shadow_style(const peer_id& id) + { + fingerprint ret("..", 0, 0, 0, 0); + + if (!is_alpha(id[0]) && !is_digit(id[0])) + return boost::optional(); + + if (std::equal(id.begin()+4, id.begin()+6, "--")) + { + if ((id[1] < '0') || (id[2] < '0') + || (id[3] < '0')) + return boost::optional(); + ret.major_version = decode_digit(id[1]); + ret.minor_version = decode_digit(id[2]); + ret.revision_version = decode_digit(id[3]); + } + else + { + if (id[8] != 0 || id[1] > 127 || id[2] > 127 || id[3] > 127) + return boost::optional(); + ret.major_version = id[1]; + ret.minor_version = id[2]; + ret.revision_version = id[3]; + } + + ret.name[0] = id[0]; + ret.name[1] = 0; + + ret.tag_version = 0; + return boost::optional(ret); + } + + // checks if a peer id can possibly contain a mainline-style + // identification + boost::optional parse_mainline_style(const peer_id& id) + { + char ids[21]; + std::copy(id.begin(), id.end(), ids); + ids[20] = 0; + fingerprint ret("..", 0, 0, 0, 0); + ret.name[1] = 0; + ret.tag_version = 0; + if (sscanf(ids, "%c%d-%d-%d--", &ret.name[0], &ret.major_version, &ret.minor_version + , &ret.revision_version) != 4 + || !is_print(ret.name[0])) + return boost::optional(); + + return boost::optional(ret); + } + + struct map_entry + { + char const* id; + char const* name; + }; + + // only support BitTorrentSpecification + // must be ordered alphabetically + map_entry name_map[] = + { + {"A", "ABC"} + , {"AG", "Ares"} + , {"AR", "Arctic Torrent"} + , {"AT", "Artemis"} + , {"AV", "Avicora"} + , {"AX", "BitPump"} + , {"AZ", "Azureus"} + , {"A~", "Ares"} + , {"BB", "BitBuddy"} + , {"BC", "BitComet"} + , {"BE", "baretorrent"} + , {"BF", "Bitflu"} + , {"BG", "BTG"} + , {"BL", "BitBlinder"} + , {"BP", "BitTorrent Pro"} + , {"BR", "BitRocket"} + , {"BS", "BTSlave"} + , {"BT", "BitTorrent"} + , {"BU", "BigUp"} + , {"BW", "BitWombat"} + , {"BX", "BittorrentX"} + , {"CD", "Enhanced CTorrent"} + , {"CT", "CTorrent"} + , {"DE", "Deluge"} + , {"DP", "Propagate Data Client"} + , {"EB", "EBit"} + , {"ES", "electric sheep"} + , {"FC", "FileCroc"} + , {"FT", "FoxTorrent"} + , {"FX", "Freebox BitTorrent"} + , {"GS", "GSTorrent"} + , {"HK", "Hekate"} + , {"HL", "Halite"} + , {"HN", "Hydranode"} + , {"IL", "iLivid"} + , {"KG", "KGet"} + , {"KT", "KTorrent"} + , {"LC", "LeechCraft"} + , {"LH", "LH-ABC"} + , {"LK", "Linkage"} + , {"LP", "lphant"} + , {"LT", "libtorrent"} + , {"LW", "Limewire"} + , {"M", "Mainline"} + , {"ML", "MLDonkey"} + , {"MO", "Mono Torrent"} + , {"MP", "MooPolice"} + , {"MR", "Miro"} + , {"MT", "Moonlight Torrent"} + , {"NX", "Net Transport"} + , {"O", "Osprey Permaseed"} + , {"OS", "OneSwarm"} + , {"OT", "OmegaTorrent"} + , {"PD", "Pando"} + , {"Q", "BTQueue"} + , {"QD", "QQDownload"} + , {"QT", "Qt 4"} + , {"R", "Tribler"} + , {"RT", "Retriever"} + , {"RZ", "RezTorrent"} + , {"S", "Shadow"} + , {"SB", "Swiftbit"} + , {"SD", "Xunlei"} + , {"SK", "spark"} + , {"SN", "ShareNet"} + , {"SS", "SwarmScope"} + , {"ST", "SymTorrent"} + , {"SZ", "Shareaza"} + , {"S~", "Shareaza (beta)"} + , {"T", "BitTornado"} + , {"TB", "Torch"} + , {"TL", "Tribler"} + , {"TN", "Torrent.NET"} + , {"TR", "Transmission"} + , {"TS", "TorrentStorm"} + , {"TT", "TuoTu"} + , {"U", "UPnP"} + , {"UL", "uLeecher"} + , {"UM", "uTorrent Mac"} + , {"UT", "uTorrent"} + , {"VG", "Vagaa"} + , {"WT", "BitLet"} + , {"WY", "FireTorrent"} + , {"XF", "Xfplay"} + , {"XL", "Xunlei"} + , {"XS", "XSwifter"} + , {"XT", "XanTorrent"} + , {"XX", "Xtorrent"} + , {"ZT", "ZipTorrent"} + , {"lt", "rTorrent"} + , {"pX", "pHoeniX"} + , {"qB", "qBittorrent"} + , {"st", "SharkTorrent"} + }; + + struct generic_map_entry + { + int offset; + char const* id; + char const* name; + }; + // non-standard names + generic_map_entry generic_mappings[] = + { + {0, "Deadman Walking-", "Deadman"} + , {5, "Azureus", "Azureus 2.0.3.2"} + , {0, "DansClient", "XanTorrent"} + , {4, "btfans", "SimpleBT"} + , {0, "PRC.P---", "Bittorrent Plus! II"} + , {0, "P87.P---", "Bittorrent Plus!"} + , {0, "S587Plus", "Bittorrent Plus!"} + , {0, "martini", "Martini Man"} + , {0, "Plus---", "Bittorrent Plus"} + , {0, "turbobt", "TurboBT"} + , {0, "a00---0", "Swarmy"} + , {0, "a02---0", "Swarmy"} + , {0, "T00---0", "Teeweety"} + , {0, "BTDWV-", "Deadman Walking"} + , {2, "BS", "BitSpirit"} + , {0, "Pando-", "Pando"} + , {0, "LIME", "LimeWire"} + , {0, "btuga", "BTugaXP"} + , {0, "oernu", "BTugaXP"} + , {0, "Mbrst", "Burst!"} + , {0, "PEERAPP", "PeerApp"} + , {0, "Plus", "Plus!"} + , {0, "-Qt-", "Qt"} + , {0, "exbc", "BitComet"} + , {0, "DNA", "BitTorrent DNA"} + , {0, "-G3", "G3 Torrent"} + , {0, "-FG", "FlashGet"} + , {0, "-ML", "MLdonkey"} + , {0, "-MG", "Media Get"} + , {0, "XBT", "XBT"} + , {0, "OP", "Opera"} + , {2, "RS", "Rufus"} + , {0, "AZ2500BT", "BitTyrant"} + , {0, "btpd/", "BitTorrent Protocol Daemon"} + , {0, "TIX", "Tixati"} + , {0, "QVOD", "Qvod"} + }; + + bool compare_id(map_entry const& lhs, map_entry const& rhs) + { + return lhs.id[0] < rhs.id[0] + || ((lhs.id[0] == rhs.id[0]) && (lhs.id[1] < rhs.id[1])); + } + + std::string lookup(fingerprint const& f) + { + char identity[200]; + + const int size = sizeof(name_map)/sizeof(name_map[0]); + map_entry tmp = {f.name, ""}; + map_entry* i = + std::lower_bound(name_map, name_map + size + , tmp, &compare_id); + +#ifndef NDEBUG + for (int i = 1; i < size; ++i) + { + TORRENT_ASSERT(compare_id(name_map[i-1] + , name_map[i])); + } +#endif + + char temp[3]; + char const* name = 0; + if (i < name_map + size && std::equal(f.name, f.name + 2, i->id)) + { + name = i->name; + } + else + { + // if we don't have this client in the list + // just use the one or two letter code + memcpy(temp, f.name, 2); + temp[2] = 0; + name = temp; + } + + int num_chars = snprintf(identity, sizeof(identity), "%s %u.%u.%u", name + , f.major_version, f.minor_version, f.revision_version); + + if (f.tag_version != 0) + { + snprintf(identity + num_chars, sizeof(identity) - num_chars + , ".%u", f.tag_version); + } + + return identity; + } + + bool find_string(unsigned char const* id, char const* search) + { + return std::equal(search, search + std::strlen(search), id); + } +} + +namespace libtorrent +{ + + boost::optional client_fingerprint(peer_id const& p) + { + // look for azureus style id + boost::optional f; + f = parse_az_style(p); + if (f) return f; + + // look for shadow style id + f = parse_shadow_style(p); + if (f) return f; + + // look for mainline style id + f = parse_mainline_style(p); + if (f) return f; + return f; + } + + std::string identify_client(peer_id const& p) + { + peer_id::const_iterator PID = p.begin(); + boost::optional f; + + if (p.is_all_zeros()) return "Unknown"; + + // ---------------------- + // non standard encodings + // ---------------------- + + int num_generic_mappings = sizeof(generic_mappings) / sizeof(generic_mappings[0]); + + for (int i = 0; i < num_generic_mappings; ++i) + { + generic_map_entry const& e = generic_mappings[i]; + if (find_string(PID + e.offset, e.id)) return e.name; + } + + if (find_string(PID, "-BOW") && PID[7] == '-') + return "Bits on Wheels " + std::string((char const*)PID + 4, (char const*)PID + 7); + + + if (find_string(PID, "eX")) + { + std::string user((char const*)PID + 2, (char const*)PID + 14); + return std::string("eXeem ('") + user.c_str() + "')"; + } + + if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\x97")) + return "Experimental 3.2.1b2"; + + if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0")) + return "Experimental 3.1"; + + + // look for azureus style id + f = parse_az_style(p); + if (f) return lookup(*f); + + // look for shadow style id + f = parse_shadow_style(p); + if (f) return lookup(*f); + + // look for mainline style id + f = parse_mainline_style(p); + if (f) return lookup(*f); + + + if (std::equal(PID, PID + 12, "\0\0\0\0\0\0\0\0\0\0\0\0")) + return "Generic"; + + std::string unknown("Unknown ["); + for (peer_id::const_iterator i = p.begin(); i != p.end(); ++i) + { + unknown += is_print(char(*i))?*i:'.'; + } + unknown += "]"; + return unknown; + } + +} diff --git a/apps/Launcher/ext/libtorrent/src/instantiate_connection.cpp b/apps/Launcher/ext/libtorrent/src/instantiate_connection.cpp new file mode 100644 index 0000000000..18619e34eb --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/instantiate_connection.cpp @@ -0,0 +1,141 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/socket.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/socket_type.hpp" +#include "libtorrent/utp_socket_manager.hpp" +#include +#include + +namespace libtorrent +{ + TORRENT_EXPORT bool instantiate_connection(io_service& ios + , proxy_settings const& ps, socket_type& s + , void* ssl_context + , utp_socket_manager* sm + , bool peer_connection) + { + if (sm) + { + utp_stream* str; +#ifdef TORRENT_USE_OPENSSL + if (ssl_context) + { + s.instantiate >(ios, ssl_context); + str = &s.get >()->next_layer(); + } + else +#endif + { + s.instantiate(ios); + str = s.get(); + } + str->set_impl(sm->new_utp_socket(str)); + } +#if TORRENT_USE_I2P + else if (ps.type == proxy_settings::i2p_proxy) + { + // it doesn't make any sense to try ssl over i2p + TORRENT_ASSERT(ssl_context == 0); + s.instantiate(ios); + s.get()->set_proxy(ps.hostname, ps.port); + } +#endif + else if (ps.type == proxy_settings::none + || (peer_connection && !ps.proxy_peer_connections)) + { +#ifdef TORRENT_USE_OPENSSL + if (ssl_context) + { + s.instantiate >(ios, ssl_context); + } + else +#endif + { + s.instantiate(ios); + } + } + else if (ps.type == proxy_settings::http + || ps.type == proxy_settings::http_pw) + { + http_stream* str; +#ifdef TORRENT_USE_OPENSSL + if (ssl_context) + { + s.instantiate >(ios, ssl_context); + str = &s.get >()->next_layer(); + } + else +#endif + { + s.instantiate(ios); + str = s.get(); + } + + str->set_proxy(ps.hostname, ps.port); + if (ps.type == proxy_settings::http_pw) + str->set_username(ps.username, ps.password); + } + else if (ps.type == proxy_settings::socks5 + || ps.type == proxy_settings::socks5_pw + || ps.type == proxy_settings::socks4) + { + socks5_stream* str; +#ifdef TORRENT_USE_OPENSSL + if (ssl_context) + { + s.instantiate >(ios, ssl_context); + str = &s.get >()->next_layer(); + } + else +#endif + { + s.instantiate(ios); + str = s.get(); + } + str->set_proxy(ps.hostname, ps.port); + if (ps.type == proxy_settings::socks5_pw) + str->set_username(ps.username, ps.password); + if (ps.type == proxy_settings::socks4) + str->set_version(4); + } + else + { + TORRENT_ASSERT_VAL(false, ps.type); + return false; + } + return true; + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/ip_filter.cpp b/apps/Launcher/ext/libtorrent/src/ip_filter.cpp new file mode 100644 index 0000000000..c481fea9cb --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/ip_filter.cpp @@ -0,0 +1,98 @@ +/* + +Copyright (c) 2005-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/ip_filter.hpp" +#include + + +namespace libtorrent +{ + void ip_filter::add_rule(address first, address last, int flags) + { + if (first.is_v4()) + { + TORRENT_ASSERT(last.is_v4()); + m_filter4.add_rule(first.to_v4().to_bytes(), last.to_v4().to_bytes(), flags); + } +#if TORRENT_USE_IPV6 + else if (first.is_v6()) + { + TORRENT_ASSERT(last.is_v6()); + m_filter6.add_rule(first.to_v6().to_bytes(), last.to_v6().to_bytes(), flags); + } +#endif + else + TORRENT_ASSERT(false); + } + + int ip_filter::access(address const& addr) const + { + if (addr.is_v4()) + return m_filter4.access(addr.to_v4().to_bytes()); +#if TORRENT_USE_IPV6 + TORRENT_ASSERT(addr.is_v6()); + return m_filter6.access(addr.to_v6().to_bytes()); +#else + return 0; +#endif + } + + ip_filter::filter_tuple_t ip_filter::export_filter() const + { +#if TORRENT_USE_IPV6 + return boost::make_tuple(m_filter4.export_filter() + , m_filter6.export_filter()); +#else + return m_filter4.export_filter(); +#endif + } + + void port_filter::add_rule(boost::uint16_t first, boost::uint16_t last, int flags) + { + m_filter.add_rule(first, last, flags); + } + + int port_filter::access(boost::uint16_t port) const + { + return m_filter.access(port); + } +/* + void ip_filter::print() const + { + for (range_t::iterator i = m_access_list.begin(); i != m_access_list.end(); ++i) + { + std::cout << i->start.as_string() << " " << i->access << "\n"; + } + } +*/ +} + diff --git a/apps/Launcher/ext/libtorrent/src/ip_voter.cpp b/apps/Launcher/ext/libtorrent/src/ip_voter.cpp new file mode 100644 index 0000000000..7092f3848c --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/ip_voter.cpp @@ -0,0 +1,194 @@ +/* + +Copyright (c) 2013-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/ip_voter.hpp" +#include "libtorrent/broadcast_socket.hpp" // for is_any() etc. +#include "libtorrent/socket_io.hpp" // for hash_address +#include "libtorrent/random.hpp" // for random() +#include "libtorrent/time.hpp" // for time_now() + +#include + +namespace libtorrent +{ + ip_voter::ip_voter() + : m_total_votes(0) + , m_valid_external(false) + , m_last_rotate(time_now()) + { + } + + // returns true if our external IP changed + bool ip_voter::maybe_rotate() + { + ptime now = time_now(); + + // if we have more than or equal to 50 votes, + // we rotate. Also, if it's been more than 5 minutes + // and we have at least one vote, we also rotate. + // this is the inverse condition, since this is the case + // were we exit, without rotating + if (m_total_votes < 50 + && (now - m_last_rotate < minutes(5) || m_total_votes == 0) + && m_valid_external) + return false; + + // this shouldn't really happen if we have at least one + // vote. + if (m_external_addresses.empty()) return false; + + // if there's just one vote, go with that + std::vector::iterator i; + if (m_external_addresses.size() == 1) + { + // avoid flapping. We need more votes to change our mind on the + // external IP + if (m_external_addresses[0].num_votes < 2) return false; + } + else + { + // find the top two votes. + std::partial_sort(m_external_addresses.begin() + , m_external_addresses.begin() + 2, m_external_addresses.end()); + + // if we don't have enough of a majority voting for the winning + // IP, don't rotate. This avoids flapping + if (m_external_addresses[0].num_votes * 2 / 3 <= m_external_addresses[1].num_votes) + return false; + } + + i = m_external_addresses.begin(); + + bool ret = m_external_address != i->addr; + m_external_address = i->addr; + + m_external_address_voters.clear(); + m_total_votes = 0; + m_external_addresses.clear(); + m_last_rotate = now; + m_valid_external = true; + return ret; + } + + bool ip_voter::cast_vote(address const& ip + , int source_type, address const& source) + { + if (is_any(ip)) return false; + if (is_local(ip)) return false; + if (is_loopback(ip)) return false; + + // don't trust source that aren't connected to us + // on a different address family than the external + // IP they claim we have + if (ip.is_v4() != source.is_v4()) return false; + + // this is the key to use for the bloom filters + // it represents the identity of the voter + sha1_hash k; + hash_address(source, k); + + // do we already have an entry for this external IP? + std::vector::iterator i = std::find_if(m_external_addresses.begin() + , m_external_addresses.end(), boost::bind(&external_ip_t::addr, _1) == ip); + + if (i == m_external_addresses.end()) + { + // each IP only gets to add a new IP once + if (m_external_address_voters.find(k)) return maybe_rotate(); + + if (m_external_addresses.size() > 40) + { + if (random() % 100 < 50) + return maybe_rotate(); + + // use stable sort here to maintain the fifo-order + // of the entries with the same number of votes + // this will sort in ascending order, i.e. the lowest + // votes first. Also, the oldest are first, so this + // is a sort of weighted LRU. + std::stable_sort(m_external_addresses.begin(), m_external_addresses.end()); + + // erase the last element, since it is one of the + // ones with the fewest votes + m_external_addresses.erase(m_external_addresses.end() - 1); + } + m_external_addresses.push_back(external_ip_t()); + i = m_external_addresses.end() - 1; + i->addr = ip; + } + // add one more vote to this external IP + if (!i->add_vote(k, source_type)) return maybe_rotate(); + ++m_total_votes; + + if (m_valid_external) return maybe_rotate(); + + i = std::min_element(m_external_addresses.begin(), m_external_addresses.end()); + TORRENT_ASSERT(i != m_external_addresses.end()); + + if (i->addr == m_external_address) return maybe_rotate(); + + if (m_external_address != address_v4()) + { + // we have a temporary external address. As soon as we have + // more than 25 votes, consider deciding which one to settle for + return (m_total_votes >= 25) ? maybe_rotate() : false; + } + + m_external_address = i->addr; + + return true; + } + + bool ip_voter::external_ip_t::add_vote(sha1_hash const& k, int type) + { + sources |= type; + if (voters.find(k)) return false; + voters.set(k); + ++num_votes; + return true; + } + + bool external_ip::cast_vote(address const& ip, int source_type, address const& source) + { + return m_vote_group[ip.is_v6()].cast_vote(ip, source_type, source); + } + + address external_ip::external_address(address const& ip) const + { + address ext = m_vote_group[ip.is_v6()].external_address(); +#if TORRENT_USE_IPV6 + if (ip.is_v6() && ext == address_v4()) return address_v6(); +#endif + return ext; + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/dht_tracker.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/dht_tracker.cpp new file mode 100644 index 0000000000..1eb2588386 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/dht_tracker.cpp @@ -0,0 +1,766 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +#include +#endif + +#include +#include +#include +#include + +#include "libtorrent/kademlia/node.hpp" +#include "libtorrent/kademlia/node_id.hpp" +#include "libtorrent/kademlia/traversal_algorithm.hpp" +#include "libtorrent/kademlia/dht_tracker.hpp" +#include "libtorrent/kademlia/msg.hpp" + +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/socket_io.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/version.hpp" +#include "libtorrent/escape_string.hpp" + +using boost::ref; +using libtorrent::dht::node_impl; +using libtorrent::dht::node_id; +using libtorrent::dht::packet_t; +using libtorrent::dht::msg; +using namespace libtorrent::detail; + +enum +{ + key_refresh = 5 // generate a new write token key every 5 minutes +}; + +namespace +{ + const int tick_period = 1; // minutes +} + +namespace libtorrent { namespace dht +{ + + void incoming_error(entry& e, char const* msg); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + int g_az_message_input = 0; + int g_ut_message_input = 0; + int g_lt_message_input = 0; + int g_mp_message_input = 0; + int g_gr_message_input = 0; + int g_mo_message_input = 0; + int g_unknown_message_input = 0; + + int g_announces = 0; + int g_failed_announces = 0; +#endif + + void intrusive_ptr_add_ref(dht_tracker const* c) + { + TORRENT_ASSERT(c != 0); + TORRENT_ASSERT(c->m_refs >= 0); + ++c->m_refs; + } + + void intrusive_ptr_release(dht_tracker const* c) + { + TORRENT_ASSERT(c != 0); + TORRENT_ASSERT(c->m_refs > 0); + if (--c->m_refs == 0) + delete c; + } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + std::string parse_dht_client(lazy_entry const& e) + { + lazy_entry const* ver = e.dict_find_string("v"); + if (!ver) return "generic"; + std::string const& client = ver->string_value(); + if (client.size() < 2) + { + ++g_unknown_message_input; + return client; + } + else if (std::equal(client.begin(), client.begin() + 2, "Az")) + { + ++g_az_message_input; + return "Azureus"; + } + else if (std::equal(client.begin(), client.begin() + 2, "UT")) + { + ++g_ut_message_input; + return "uTorrent"; + } + else if (std::equal(client.begin(), client.begin() + 2, "LT")) + { + ++g_lt_message_input; + return "libtorrent"; + } + else if (std::equal(client.begin(), client.begin() + 2, "MP")) + { + ++g_mp_message_input; + return "MooPolice"; + } + else if (std::equal(client.begin(), client.begin() + 2, "GR")) + { + ++g_gr_message_input; + return "GetRight"; + } + else if (std::equal(client.begin(), client.begin() + 2, "MO")) + { + ++g_mo_message_input; + return "Mono Torrent"; + } + else + { + ++g_unknown_message_input; + return client; + } + } +#endif + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_DEFINE_LOG(dht_tracker) +#endif + + node_id extract_node_id(lazy_entry const* e) + { + if (e == 0 || e->type() != lazy_entry::dict_t) return (node_id::min)(); + lazy_entry const* nid = e->dict_find_string("node-id"); + if (nid == 0 || nid->string_length() != 20) return (node_id::min)(); + return node_id(node_id(nid->string_ptr())); + } + + node_id extract_node_id(entry const* e) + { + if (e == 0 || e->type() != entry::dictionary_t) return (node_id::min)(); + entry const* nid = e->find_key("node-id"); + if (nid == 0 || nid->type() != entry::string_t || nid->string().length() != 20) + return (node_id::min)(); + return node_id(node_id(nid->string().c_str())); + } + + // class that puts the networking and the kademlia node in a single + // unit and connecting them together. + dht_tracker::dht_tracker(libtorrent::aux::session_impl& ses, rate_limited_udp_socket& sock + , dht_settings const& settings, entry const* state) + : m_dht(&ses, this, settings, extract_node_id(state) + , ses.external_address().external_address(address_v4()), &ses) + , m_sock(sock) + , m_last_new_key(time_now() - minutes(key_refresh)) + , m_timer(sock.get_io_service()) + , m_connection_timer(sock.get_io_service()) + , m_refresh_timer(sock.get_io_service()) + , m_settings(settings) + , m_refresh_bucket(160) + , m_abort(false) + , m_host_resolver(sock.get_io_service()) + , m_sent_bytes(0) + , m_received_bytes(0) + , m_refs(0) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + m_counter = 0; + std::fill_n(m_replies_bytes_sent, 5, 0); + std::fill_n(m_queries_bytes_received, 5, 0); + std::fill_n(m_replies_sent, 5, 0); + std::fill_n(m_queries_received, 5, 0); + g_announces = 0; + g_failed_announces = 0; + m_total_message_input = 0; + m_total_in_bytes = 0; + m_total_out_bytes = 0; + m_queries_out_bytes = 0; + + // turns on and off individual components' logging + +// rpc_log().enable(false); +// node_log().enable(false); +// traversal_log().enable(false); +// dht_tracker_log.enable(false); + + TORRENT_LOG(dht_tracker) << "starting DHT tracker with node id: " << m_dht.nid(); +#endif + for (int i = 0; i < num_ban_nodes; ++i) + { + m_ban_nodes[i].count = 0; + m_ban_nodes[i].limit = min_time(); + } + } + + dht_tracker::~dht_tracker() {} + + // defined in node.cpp + extern void nop(); + + void dht_tracker::start(entry const& bootstrap + , find_data::nodes_callback const& f) + { + std::vector initial_nodes; + + if (bootstrap.type() == entry::dictionary_t) + { + TORRENT_TRY { + if (entry const* nodes = bootstrap.find_key("nodes")) + read_endpoint_list(nodes, initial_nodes); + } TORRENT_CATCH(std::exception&) {} + } + + error_code ec; + m_timer.expires_from_now(seconds(1), ec); + m_timer.async_wait(boost::bind(&dht_tracker::tick, self(), _1)); + + m_connection_timer.expires_from_now(seconds(1), ec); + m_connection_timer.async_wait( + boost::bind(&dht_tracker::connection_timeout, self(), _1)); + + m_refresh_timer.expires_from_now(seconds(5), ec); + m_refresh_timer.async_wait(boost::bind(&dht_tracker::refresh_timeout, self(), _1)); + m_dht.bootstrap(initial_nodes, f); + } + + void dht_tracker::stop() + { + m_abort = true; + error_code ec; + m_timer.cancel(ec); + m_connection_timer.cancel(ec); + m_refresh_timer.cancel(ec); + m_host_resolver.cancel(); + } + + void dht_tracker::dht_status(session_status& s) + { + m_dht.status(s); + } + + void dht_tracker::network_stats(int& sent, int& received) + { + sent = m_sent_bytes; + received = m_received_bytes; + m_sent_bytes = 0; + m_received_bytes = 0; + } + + void dht_tracker::connection_timeout(error_code const& e) + { + if (e || m_abort) return; + + time_duration d = m_dht.connection_timeout(); + error_code ec; + m_connection_timer.expires_from_now(d, ec); + m_connection_timer.async_wait(boost::bind(&dht_tracker::connection_timeout, self(), _1)); + } + + void dht_tracker::refresh_timeout(error_code const& e) + { + if (e || m_abort) return; + + m_dht.tick(); + error_code ec; + m_refresh_timer.expires_from_now(seconds(5), ec); + m_refresh_timer.async_wait( + boost::bind(&dht_tracker::refresh_timeout, self(), _1)); + } + + void dht_tracker::tick(error_code const& e) + { + if (e || m_abort) return; + + error_code ec; + m_timer.expires_from_now(minutes(tick_period), ec); + m_timer.async_wait(boost::bind(&dht_tracker::tick, self(), _1)); + + ptime now = time_now(); + if (now - m_last_new_key > minutes(key_refresh)) + { + m_last_new_key = now; + m_dht.new_write_key(); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " *** new write key"; +#endif + } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + static bool first = true; + + std::ofstream st("dht_routing_table_state.txt", std::ios_base::trunc); + m_dht.print_state(st); + + // count torrents + int torrents = m_dht.num_torrents(); + + // count peers + int peers = m_dht.num_peers(); + + std::ofstream pc("dht_stats.log", first ? std::ios_base::trunc : std::ios_base::app); + if (first) + { + first = false; + pc << "\n\n ***** starting log at " << time_now_string() << " *****\n\n" + << "minute:active nodes:passive nodes:confirmed nodes" + ":ping replies sent:ping queries recvd" + ":ping replies bytes sent:ping queries bytes recvd" + ":find_node replies sent:find_node queries recv" + ":find_node replies bytes sent:find_node queries bytes recv" + ":get_peers replies sent:get_peers queries recvd" + ":get_peers replies bytes sent:get_peers queries bytes recv" + ":announce_peer replies sent:announce_peer queries recvd" + ":announce_peer replies bytes sent:announce_peer queries bytes recv" + ":error replies sent:error queries recvd" + ":error replies bytes sent:error queries bytes recv" + ":num torrents:num peers:announces per min" + ":failed announces per min:total msgs per min" + ":az msgs per min:ut msgs per min:lt msgs per min:mp msgs per min" + ":gr msgs per min:mo msgs per min:bytes in per sec:bytes out per sec" + ":queries out bytes per sec\n\n"; + } + + int active; + int passive; + int confirmed; + boost::tie(active, passive, confirmed) = m_dht.size(); + pc << (m_counter * tick_period) + << "\t" << active + << "\t" << passive + << "\t" << confirmed; + for (int i = 0; i < 5; ++i) + pc << "\t" << (m_replies_sent[i] / float(tick_period)) + << "\t" << (m_queries_received[i] / float(tick_period)) + << "\t" << (m_replies_bytes_sent[i] / float(tick_period*60)) + << "\t" << (m_queries_bytes_received[i] / float(tick_period*60)); + + pc << "\t" << torrents + << "\t" << peers + << "\t" << g_announces / float(tick_period) + << "\t" << g_failed_announces / float(tick_period) + << "\t" << (m_total_message_input / float(tick_period)) + << "\t" << (g_az_message_input / float(tick_period)) + << "\t" << (g_ut_message_input / float(tick_period)) + << "\t" << (g_lt_message_input / float(tick_period)) + << "\t" << (g_mp_message_input / float(tick_period)) + << "\t" << (g_gr_message_input / float(tick_period)) + << "\t" << (g_mo_message_input / float(tick_period)) + << "\t" << (m_total_in_bytes / float(tick_period*60)) + << "\t" << (m_total_out_bytes / float(tick_period*60)) + << "\t" << (m_queries_out_bytes / float(tick_period*60)) + << std::endl; + ++m_counter; + std::fill_n(m_replies_bytes_sent, 5, 0); + std::fill_n(m_queries_bytes_received, 5, 0); + std::fill_n(m_replies_sent, 5, 0); + std::fill_n(m_queries_received, 5, 0); + g_announces = 0; + g_failed_announces = 0; + m_total_message_input = 0; + g_az_message_input = 0; + g_ut_message_input = 0; + g_lt_message_input = 0; + g_mp_message_input = 0; + g_gr_message_input = 0; + g_mo_message_input = 0; + g_unknown_message_input = 0; + m_total_in_bytes = 0; + m_total_out_bytes = 0; + m_queries_out_bytes = 0; +#endif + } + + void dht_tracker::announce(sha1_hash const& ih, int listen_port, int flags + , boost::function const&)> f) + { + m_dht.announce(ih, listen_port, flags, f); + } + + // these functions provide a slightly higher level + // interface to the get/put functionality in the DHT + bool get_immutable_item_callback(item& it, boost::function f) + { + // the reason to wrap here is to control the return value + // since it controls whether we re-put the content + TORRENT_ASSERT(!it.is_mutable()); + f(it); + return false; + } + + bool get_mutable_item_callback(item& it, boost::function f) + { + // the reason to wrap here is to control the return value + // since it controls whether we re-put the content + TORRENT_ASSERT(it.is_mutable()); + f(it); + return false; + } + + bool put_immutable_item_callback(item& it, boost::function f + , entry data) + { + TORRENT_ASSERT(!it.is_mutable()); + it.assign(data); + // TODO: ideally this function would be called when the + // put completes + f(); + return true; + } + + bool put_mutable_item_callback(item& it, boost::function cb) + { + cb(it); + return true; + } + + void dht_tracker::get_item(sha1_hash const& target + , boost::function cb) + { + m_dht.get_item(target, boost::bind(&get_immutable_item_callback, _1, cb)); + } + + // key is a 32-byte binary string, the public key to look up. + // the salt is optional + void dht_tracker::get_item(char const* key + , boost::function cb + , std::string salt) + { + m_dht.get_item(key, salt, boost::bind(&get_mutable_item_callback, _1, cb)); + } + + void dht_tracker::put_item(entry data + , boost::function cb) + { + std::string flat_data; + bencode(std::back_inserter(flat_data), data); + sha1_hash target = item_target_id( + std::pair(flat_data.c_str(), flat_data.size())); + + m_dht.get_item(target, boost::bind(&put_immutable_item_callback + , _1, cb, data)); + } + + void dht_tracker::put_item(char const* key + , boost::function cb, std::string salt) + { + m_dht.get_item(key, salt, boost::bind(&put_mutable_item_callback + , _1, cb)); + } + + // translate bittorrent kademlia message into the generice kademlia message + // used by the library + bool dht_tracker::incoming_packet(error_code const& ec + , udp::endpoint const& ep, char const* buf, int size) + { + if (ec) + { + if (ec == asio::error::connection_refused + || ec == asio::error::connection_reset + || ec == asio::error::connection_aborted +#ifdef WIN32 + || ec == error_code(ERROR_HOST_UNREACHABLE, get_system_category()) + || ec == error_code(ERROR_PORT_UNREACHABLE, get_system_category()) + || ec == error_code(ERROR_CONNECTION_REFUSED, get_system_category()) + || ec == error_code(ERROR_CONNECTION_ABORTED, get_system_category()) +#endif + ) + { + m_dht.unreachable(ep); + } + return false; + } + + if (size <= 20 || *buf != 'd' || buf[size-1] != 'e') return false; + + // account for IP and UDP overhead + m_received_bytes += size + (ep.address().is_v6() ? 48 : 28); + + if (m_settings.ignore_dark_internet && ep.address().is_v4()) + { + address_v4::bytes_type b = ep.address().to_v4().to_bytes(); + + // these are class A networks not available to the public + // if we receive messages from here, that seems suspicious + boost::uint8_t class_a[] = { 3, 6, 7, 9, 11, 19, 21, 22, 25 + , 26, 28, 29, 30, 33, 34, 45, 48, 51, 52, 56, 102, 104 }; + + int num = sizeof(class_a)/sizeof(class_a[0]); + if (std::find(class_a, class_a + num, b[0]) != class_a + num) + return true; + } + + node_ban_entry* match = 0; + node_ban_entry* min = m_ban_nodes; + ptime now = time_now(); + for (node_ban_entry* i = m_ban_nodes; i < m_ban_nodes + num_ban_nodes; ++i) + { + if (i->src == ep.address()) + { + match = i; + break; + } + if (i->count < min->count) min = i; + else if (i->count == min->count + && i->limit < min->limit) min = i; + } + + if (match) + { + ++match->count; + if (match->count >= 50) + { + if (now < match->limit) + { + // the first time we exceed the limit, ban it for 5 minutes + if (match->count == 50) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " BANNING NODE [ ip: " + << ep << " time: " << total_milliseconds((now - match->limit) + seconds(10)) / 1000.f + << " count: " << match->count << " ]"; +#endif + match->limit = now + minutes(5); + } + // we've received 50 messages in less than 10 seconds from + // this node. Ignore it until it's silent for 5 minutes + return true; + } + + // we got 50 messages from this peer, but it was in + // more than 10 seconds. Reset the counter and the timer + match->count = 0; + match->limit = now + seconds(10); + } + } + else + { + min->count = 1; + min->limit = now + seconds(10); + min->src = ep.address(); + } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + ++m_total_message_input; + m_total_in_bytes += size; +#endif + + using libtorrent::entry; + using libtorrent::bdecode; + + TORRENT_ASSERT(size > 0); + + lazy_entry e; + int pos; + error_code err; + int ret = lazy_bdecode(buf, buf + size, e, err, &pos, 10, 500); + if (ret != 0) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << "<== " << ep << " ERROR: " + << err.message() << " pos: " << pos; +#endif + return false; + } + + libtorrent::dht::msg m(e, ep); + + if (e.type() != lazy_entry::dict_t) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << "<== " << ep << " ERROR: not a dictionary: " + << print_entry(e, true); +#endif + // it's not a good idea to send invalid messages + // especially not in response to an invalid message +// entry r; +// libtorrent::dht::incoming_error(r, "message is not a dictionary"); +// send_packet(r, ep, 0); + return false; + } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + parse_dht_client(e); + TORRENT_LOG(dht_tracker) << "<== " << ep << " " << print_entry(e, true); + + if (e.dict_find_string_value("y") == "q") + { + std::string cmd = e.dict_find_string_value("q"); + int cmd_idx = -1; + if (cmd == "ping") cmd_idx = 0; + else if (cmd == "find_node") cmd_idx = 1; + else if (cmd == "get_peers") cmd_idx = 2; + else if (cmd == "announce_peeer") cmd_idx = 3; + if (cmd_idx >= 0) + { + ++m_queries_received[cmd_idx]; + m_queries_bytes_received[cmd_idx] += size; + } + } +#endif + + m_dht.incoming(m); + return true; + } + + void add_node_fun(void* userdata, node_entry const& e) + { + entry* n = (entry*)userdata; + std::string node; + std::back_insert_iterator out(node); + write_endpoint(e.ep(), out); + n->list().push_back(entry(node)); + } + + entry dht_tracker::state() const + { + entry ret(entry::dictionary_t); + { + entry nodes(entry::list_t); + m_dht.m_table.for_each_node(&add_node_fun, &add_node_fun, &nodes); + bucket_t cache; + m_dht.replacement_cache(cache); + for (bucket_t::iterator i(cache.begin()) + , end(cache.end()); i != end; ++i) + { + std::string node; + std::back_insert_iterator out(node); + write_endpoint(i->ep(), out); + nodes.list().push_back(entry(node)); + } + if (!nodes.list().empty()) + ret["nodes"] = nodes; + } + + ret["node-id"] = m_dht.nid().to_string(); + return ret; + } + + void dht_tracker::add_node(udp::endpoint node) + { + m_dht.add_node(node); + } + + void dht_tracker::add_node(std::pair const& node) + { + char port[7]; + snprintf(port, sizeof(port), "%d", node.second); + udp::resolver::query q(node.first, port); + m_host_resolver.async_resolve(q, + boost::bind(&dht_tracker::on_name_lookup, self(), _1, _2)); + } + + void dht_tracker::on_name_lookup(error_code const& e + , udp::resolver::iterator host) + { + if (e || host == udp::resolver::iterator()) return; + add_node(host->endpoint()); + } + + void dht_tracker::add_router_node(udp::endpoint const& node) + { + m_dht.add_router_node(node); + } + + bool dht_tracker::send_packet(libtorrent::entry& e, udp::endpoint const& addr, int send_flags) + { + using libtorrent::bencode; + using libtorrent::entry; + + static char const version_str[] = {'L', 'T' + , LIBTORRENT_VERSION_MAJOR, LIBTORRENT_VERSION_MINOR}; + e["v"] = std::string(version_str, version_str + 4); + + m_send_buf.clear(); + bencode(std::back_inserter(m_send_buf), e); + error_code ec; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + std::stringstream log_line; + lazy_entry print; + int ret = lazy_bdecode(&m_send_buf[0], &m_send_buf[0] + m_send_buf.size(), print, ec); + TORRENT_ASSERT(ret == 0); + log_line << print_entry(print, true); +#endif + + if (m_sock.send(addr, &m_send_buf[0], (int)m_send_buf.size(), ec, send_flags)) + { + if (ec) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << "==> " << addr << " DROPPED (" << ec.message() << ") " << log_line.str(); +#endif + return false; + } + + // account for IP and UDP overhead + m_sent_bytes += m_send_buf.size() + (addr.address().is_v6() ? 48 : 28); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + m_total_out_bytes += m_send_buf.size(); + + if (e["y"].string() == "r") + { +/* + // This doesn't work. r is a dictionary with return + // values. The query type isn't part of the response + std::string cmd = e["r"].string(); + int cmd_idx = -1; + if (cmd == "ping") cmd_idx = 0; + else if (cmd == "find_node") cmd_idx = 1; + else if (cmd == "get_peers") cmd_idx = 2; + else if (cmd == "announce_peeer") cmd_idx = 3; + if (cmd_idx >= 0) + { + ++m_replies_sent[cmd_idx]; + m_replies_bytes_sent[cmd_idx] += int(m_send_buf.size()); + } +*/ + } + else if (e["y"].string() == "q") + { + m_queries_out_bytes += m_send_buf.size(); + } + TORRENT_LOG(dht_tracker) << "==> " << addr << " " << log_line.str(); +#endif + return true; + } + else + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << "==> " << addr << " DROPPED " << log_line.str(); +#endif + return false; + } + } + +}} + diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/find_data.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/find_data.cpp new file mode 100644 index 0000000000..0106bc92c1 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/find_data.cpp @@ -0,0 +1,179 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace libtorrent { namespace dht +{ + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_DECLARE_LOG(traversal); +#endif + +using detail::read_endpoint_list; +using detail::read_v4_endpoint; +#if TORRENT_USE_IPV6 +using detail::read_v6_endpoint; +#endif + +void find_data_observer::reply(msg const& m) +{ + lazy_entry const* r = m.message.dict_find_dict("r"); + if (!r) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << m_algorithm.get() << "] missing response dict"; +#endif + return; + } + + lazy_entry const* id = r->dict_find_string("id"); + if (!id || id->string_length() != 20) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << m_algorithm.get() << "] invalid id in response"; +#endif + return; + } + lazy_entry const* token = r->dict_find_string("token"); + if (token) + { + static_cast(m_algorithm.get())->got_write_token( + node_id(id->string_ptr()), token->string_value()); + } + + traversal_observer::reply(m); + done(); +} + +void add_entry_fun(void* userdata, node_entry const& e) +{ + traversal_algorithm* f = (traversal_algorithm*)userdata; + f->add_entry(e.id, e.ep(), observer::flag_initial); +} + +find_data::find_data( + node_impl& node + , node_id target + , nodes_callback const& ncallback) + : traversal_algorithm(node, target) + , m_nodes_callback(ncallback) + , m_done(false) +{ +} + +void find_data::start() +{ + // if the user didn't add seed-nodes manually, grab a bunch of nodes from the + // routing table + if (m_results.empty()) + m_node.m_table.for_each_node(&add_entry_fun, 0, (traversal_algorithm*)this); + + traversal_algorithm::start(); +} + +void find_data::got_write_token(node_id const& n, std::string const& write_token) +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] adding write " + "token '" << to_hex(write_token) << "' under id '" << to_hex(n.to_string()) << "'"; +#endif + m_write_tokens[n] = write_token; +} + +observer_ptr find_data::new_observer(void* ptr + , udp::endpoint const& ep, node_id const& id) +{ + observer_ptr o(new (ptr) find_data_observer(this, ep, id)); +#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS + o->m_in_constructor = false; +#endif + return o; +} + +char const* find_data::name() const { return "find_data"; } + +void find_data::done() +{ + if (m_invoke_count != 0) return; + + m_done = true; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] " << name() << " DONE"; +#endif + + std::vector > results; + int num_results = m_node.m_table.bucket_size(); + for (std::vector::iterator i = m_results.begin() + , end(m_results.end()); i != end && num_results > 0; ++i) + { + observer_ptr const& o = *i; + if ((o->flags & observer::flag_alive) == 0) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] not alive: " + << o->target_ep(); +#endif + continue; + } + std::map::iterator j = m_write_tokens.find(o->id()); + if (j == m_write_tokens.end()) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] no write token: " + << o->target_ep(); +#endif + continue; + } + results.push_back(std::make_pair(node_entry(o->id(), o->target_ep()), j->second)); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] " + << o->target_ep(); +#endif + --num_results; + } + + if (m_nodes_callback) m_nodes_callback(results); + + traversal_algorithm::done(); +} + +} } // namespace libtorrent::dht + diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/get_item.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/get_item.cpp new file mode 100644 index 0000000000..c31ede6300 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/get_item.cpp @@ -0,0 +1,283 @@ +/* + +Copyright (c) 2013, Steven Siloti +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include + +#if TORRENT_USE_ASSERTS +#include +#endif + +namespace libtorrent { namespace dht +{ + +void get_item::got_data(lazy_entry const* v, + char const* pk, + boost::uint64_t seq, + char const* sig) +{ + // we received data! + + std::pair salt(m_salt.c_str(), m_salt.size()); + + sha1_hash incoming_target; + if (pk) + incoming_target = item_target_id(salt, pk); + else + incoming_target = item_target_id(v->data_section()); + + if (incoming_target != m_target) return; + + if (pk && sig) + { + // this is mutable data. If it passes the signature + // check, remember it. Just keep the version with + // the highest sequence number. + if (m_data.empty() || m_data.seq() < seq) + { + if (!m_data.assign(v, salt, seq, pk, sig)) + return; + } + } + else if (m_data.empty()) + { + // this is the first time we receive data, + // and it's immutable + + m_data.assign(v); + bool put_requested = m_data_callback(m_data); + + // if we intend to put, we need to keep going + // until we find the closest nodes, since those + // are the ones we're putting to + if (put_requested) + { +#if TORRENT_USE_ASSERTS + std::vector buffer; + bencode(std::back_inserter(buffer), m_data.value()); + TORRENT_ASSERT(m_target == hasher(&buffer[0], buffer.size()).final()); +#endif + + // this function is called when we're done, passing + // in all relevant nodes we received data from close + // to the target. + m_nodes_callback = boost::bind(&get_item::put, this, _1); + } + else + { + // There can only be one true immutable item with a given id + // Now that we've got it and the user doesn't want to do a put + // there's no point in continuing to query other nodes + abort(); + } + } +} + +get_item::get_item( + node_impl& node + , node_id target + , data_callback const& dcallback) + : find_data(node, target, nodes_callback()) + , m_data_callback(dcallback) +{ +} + +get_item::get_item( + node_impl& node + , char const* pk + , std::string const& salt + , data_callback const& dcallback) + : find_data(node, item_target_id( + std::make_pair(salt.c_str(), int(salt.size())), pk) + , nodes_callback()) + , m_data_callback(dcallback) + , m_data(pk, salt) +{ +} + +char const* get_item::name() const { return "get"; } + +observer_ptr get_item::new_observer(void* ptr + , udp::endpoint const& ep, node_id const& id) +{ + observer_ptr o(new (ptr) get_item_observer(this, ep, id)); +#if TORRENT_USE_ASSERTS + o->m_in_constructor = false; +#endif + return o; +} + +bool get_item::invoke(observer_ptr o) +{ + if (m_done) + { + m_invoke_count = -1; + return false; + } + + entry e; + e["y"] = "q"; + entry& a = e["a"]; + + e["q"] = "get"; + a["target"] = m_target.to_string(); + + return m_node.m_rpc.invoke(e, o->target_ep(), o); +} + +void get_item::done() +{ + if (m_data.is_mutable() || m_data.empty()) + { + // for mutable data, we only call the callback at the end, + // when we've heard from everyone, to be sure we got the + // latest version of the data (i.e. highest sequence number) + bool put_requested = m_data_callback(m_data); + if (put_requested) + { +#if TORRENT_USE_ASSERTS + if (m_data.is_mutable()) + { + TORRENT_ASSERT(m_target + == item_target_id(std::pair(m_data.salt().c_str() + , m_data.salt().size()) + , m_data.pk().data())); + } + else + { + std::vector buffer; + bencode(std::back_inserter(buffer), m_data.value()); + TORRENT_ASSERT(m_target == hasher(&buffer[0], buffer.size()).final()); + } +#endif + + // this function is called when we're done, passing + // in all relevant nodes we received data from close + // to the target. + m_nodes_callback = boost::bind(&get_item::put, this, _1); + } + } + find_data::done(); +} + +// this function sends a put message to the nodes +// closest to the target. Those nodes are passed in +// as the v argument +void get_item::put(std::vector > const& v) +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "sending put [ v: \"" << m_data.value() + << "\" seq: " << (m_data.is_mutable() ? m_data.seq() : -1) + << " nodes: " << v.size() << " ]" ; +#endif + + // create a dummy traversal_algorithm + boost::intrusive_ptr algo( + new traversal_algorithm(m_node, (node_id::min)())); + + // store on the first k nodes + for (std::vector >::const_iterator i = v.begin() + , end(v.end()); i != end; ++i) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << " put-distance: " << (160 - distance_exp(m_target, i->first.id)); +#endif + + void* ptr = m_node.m_rpc.allocate_observer(); + if (ptr == 0) return; + + // TODO: 3 we don't support CAS errors here! we need a custom observer + observer_ptr o(new (ptr) announce_observer(algo, i->first.ep(), i->first.id)); +#if TORRENT_USE_ASSERTS + o->m_in_constructor = false; +#endif + entry e; + e["y"] = "q"; + e["q"] = "put"; + entry& a = e["a"]; + a["v"] = m_data.value(); + a["token"] = i->second; + if (m_data.is_mutable()) + { + a["k"] = std::string(m_data.pk().data(), item_pk_len); + a["seq"] = m_data.seq(); + a["sig"] = std::string(m_data.sig().data(), item_sig_len); + if (!m_data.salt().empty()) + { + a["salt"] = m_data.salt(); + } + } + m_node.m_rpc.invoke(e, i->first.ep(), o); + } +} + +void get_item_observer::reply(msg const& m) +{ + char const* pk = NULL; + char const* sig = NULL; + boost::uint64_t seq = 0; + + lazy_entry const* r = m.message.dict_find_dict("r"); + if (!r) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << m_algorithm.get() << "] missing response dict"; +#endif + return; + } + + lazy_entry const* k = r->dict_find_string("k"); + if (k && k->string_length() == item_pk_len) + pk = k->string_ptr(); + + lazy_entry const* s = r->dict_find_string("sig"); + if (s && s->string_length() == item_sig_len) + sig = s->string_ptr(); + + lazy_entry const* q = r->dict_find_int("seq"); + if (q) + seq = q->int_value(); + else if (pk && sig) + return; + + lazy_entry const* v = r->dict_find("v"); + if (v) + { + static_cast(m_algorithm.get())->got_data(v, pk, seq, sig); + } + + find_data_observer::reply(m); +} + +} } // namespace libtorrent::dht diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/get_peers.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/get_peers.cpp new file mode 100644 index 0000000000..ec0a1beaf0 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/get_peers.cpp @@ -0,0 +1,313 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include + +namespace libtorrent { namespace dht +{ + +using detail::read_endpoint_list; +using detail::read_v4_endpoint; +#if TORRENT_USE_IPV6 +using detail::read_v6_endpoint; +#endif + +void get_peers_observer::reply(msg const& m) +{ + lazy_entry const* r = m.message.dict_find_dict("r"); + if (!r) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << m_algorithm.get() << "] missing response dict"; +#endif + return; + } + + // look for peers + lazy_entry const* n = r->dict_find_list("values"); + if (n) + { + std::vector peer_list; + if (n->list_size() == 1 && n->list_at(0)->type() == lazy_entry::string_t) + { + // assume it's mainline format + char const* peers = n->list_at(0)->string_ptr(); + char const* end = peers + n->list_at(0)->string_length(); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + lazy_entry const* id = r->dict_find_string("id"); + if (id && id->string_length() == 20) + { + TORRENT_LOG(traversal) + << "[" << m_algorithm.get() << "] PEERS" + << " invoke-count: " << m_algorithm->invoke_count() + << " branch-factor: " << m_algorithm->branch_factor() + << " addr: " << m.addr + << " id: " << node_id(id->string_ptr()) + << " distance: " << distance_exp(m_algorithm->target(), node_id(id->string_ptr())) + << " p: " << ((end - peers) / 6); + } +#endif + while (end - peers >= 6) + peer_list.push_back(read_v4_endpoint(peers)); + } + else + { + // assume it's uTorrent/libtorrent format + read_endpoint_list(n, peer_list); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + lazy_entry const* id = r->dict_find_string("id"); + if (id && id->string_length() == 20) + { + TORRENT_LOG(traversal) + << "[" << m_algorithm.get() << "] PEERS" + << " invoke-count: " << m_algorithm->invoke_count() + << " branch-factor: " << m_algorithm->branch_factor() + << " addr: " << m.addr + << " id: " << node_id(id->string_ptr()) + << " distance: " << distance_exp(m_algorithm->target(), node_id(id->string_ptr())) + << " p: " << n->list_size(); + } +#endif + } + static_cast(m_algorithm.get())->got_peers(peer_list); + } + + find_data_observer::reply(m); +} + +void get_peers::got_peers(std::vector const& peers) +{ + if (m_data_callback) m_data_callback(peers); +} + +get_peers::get_peers( + node_impl& node + , node_id target + , data_callback const& dcallback + , nodes_callback const& ncallback + , bool noseeds) + : find_data(node, target, ncallback) + , m_data_callback(dcallback) + , m_noseeds(noseeds) +{ +} + +char const* get_peers::name() const { return "get_peers"; } + +bool get_peers::invoke(observer_ptr o) +{ + if (m_done) + { + m_invoke_count = -1; + return false; + } + + entry e; + e["y"] = "q"; + entry& a = e["a"]; + + e["q"] = "get_peers"; + a["info_hash"] = m_target.to_string(); + if (m_noseeds) a["noseed"] = 1; + + return m_node.m_rpc.invoke(e, o->target_ep(), o); +} + +observer_ptr get_peers::new_observer(void* ptr + , udp::endpoint const& ep, node_id const& id) +{ + observer_ptr o(new (ptr) get_peers_observer(this, ep, id)); +#if TORRENT_USE_ASSERTS + o->m_in_constructor = false; +#endif + return o; +} + +obfuscated_get_peers::obfuscated_get_peers( + node_impl& node + , node_id info_hash + , data_callback const& dcallback + , nodes_callback const& ncallback + , bool noseeds) + : get_peers(node, info_hash, dcallback, ncallback, noseeds) + , m_obfuscated(true) +{ +} + +char const* obfuscated_get_peers::name() const +{ return !m_obfuscated ? get_peers::name() : "get_peers [obfuscated]"; } + +observer_ptr obfuscated_get_peers::new_observer(void* ptr + , udp::endpoint const& ep, node_id const& id) +{ + if (m_obfuscated) + { + observer_ptr o(new (ptr) obfuscated_get_peers_observer(this, ep, id)); +#if TORRENT_USE_ASSERTS + o->m_in_constructor = false; +#endif + return o; + } + else + { + observer_ptr o(new (ptr) get_peers_observer(this, ep, id)); +#if TORRENT_USE_ASSERTS + o->m_in_constructor = false; +#endif + return o; + } +} + +bool obfuscated_get_peers::invoke(observer_ptr o) +{ + if (!m_obfuscated) return get_peers::invoke(o); + + node_id id = o->id(); + int shared_prefix = 160 - distance_exp(id, m_target); + + // when we get close to the target zone in the DHT + // start using the correct info-hash, in order to + // start receiving peers + if (shared_prefix > m_node.m_table.depth() - 10) + { + m_obfuscated = false; + // clear the queried bits on all successful nodes in + // our node-list for this traversal algorithm, to + // allow the get_peers traversal to regress in case + // nodes further down end up being dead + for (std::vector::iterator i = m_results.begin() + , end(m_results.end()); i != end; ++i) + { + observer* o = i->get(); + // don't re-request from nodes that didn't respond + if (o->flags & observer::flag_failed) continue; + // don't interrupt with queries that are already in-flight + if ((o->flags & observer::flag_alive) == 0) continue; + o->flags &= ~(observer::flag_queried | observer::flag_alive); + } + return get_peers::invoke(o); + } + + entry e; + e["y"] = "q"; + e["q"] = "get_peers"; + entry& a = e["a"]; + + // This logic will obfuscate the target info-hash + // we're looking up, in order to preserve more privacy + // on the DHT. This is done by only including enough + // bits in the info-hash for the node we're querying to + // give a good answer, but not more. + + // now, obfuscate the bits past shared_prefix + 3 + node_id mask = generate_prefix_mask(shared_prefix + 3); + node_id obfuscated_target = generate_random_id() & ~mask; + obfuscated_target |= m_target & mask; + a["info_hash"] = obfuscated_target.to_string(); + + return m_node.m_rpc.invoke(e, o->target_ep(), o); +} + +void obfuscated_get_peers::done() +{ + if (!m_obfuscated) return get_peers::done(); + + // oops, we failed to switch over to the non-obfuscated + // mode early enough. do it now + + boost::intrusive_ptr ta(new get_peers(m_node, m_target + , m_data_callback + , m_nodes_callback + , m_noseeds)); + + // don't call these when the obfuscated_get_peers + // is done, we're passing them on to be called when + // ta completes. + m_data_callback.clear(); + m_nodes_callback.clear(); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << " [" << this << "]" + << " obfuscated get_peers phase 1 done, spawning get_peers [" << ta.get() << "]"; +#endif + + int num_added = 0; + for (std::vector::iterator i = m_results.begin() + , end(m_results.end()); i != end && num_added < 16; ++i) + { + observer_ptr o = *i; + + // only add nodes whose node ID we know and that + // we know are alive + if (o->flags & observer::flag_no_id) continue; + if ((o->flags & observer::flag_alive) == 0) continue; + + ta->add_entry(o->id(), o->target_ep(), observer::flag_initial); + ++num_added; + } + + ta->start(); + + get_peers::done(); +} + +void obfuscated_get_peers_observer::reply(msg const& m) +{ + lazy_entry const* r = m.message.dict_find_dict("r"); + if (!r) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << m_algorithm.get() + << "] missing response dict"; +#endif + return; + } + + lazy_entry const* id = r->dict_find_string("id"); + if (!id || id->string_length() != 20) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << m_algorithm.get() + << "] invalid id in response"; +#endif + return; + } + + traversal_observer::reply(m); + + done(); +} + +} } // namespace libtorrent::dht diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/item.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/item.cpp new file mode 100644 index 0000000000..d586008870 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/item.cpp @@ -0,0 +1,224 @@ +/* + +Copyright (c) 2013, Steven Siloti +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include + +#ifdef TORRENT_DEBUG +#include "libtorrent/lazy_entry.hpp" +#endif + +#ifdef TORRENT_USE_VALGRIND +#include +#endif + +namespace libtorrent { namespace dht +{ + +namespace +{ + enum { canonical_length = 1200 }; + int canonical_string(std::pair v, boost::uint64_t seq + , std::pair salt, char out[canonical_length]) + { + // v must be valid bencoding! +#ifdef TORRENT_DEBUG + lazy_entry e; + error_code ec; + TORRENT_ASSERT(lazy_bdecode(v.first, v.first + v.second, e, ec) == 0); +#endif + char* ptr = out; + + int left = canonical_length - (ptr - out); + if (salt.second > 0) + { + ptr += snprintf(ptr, left, "4:salt%d:", salt.second); + left = canonical_length - (ptr - out); + memcpy(ptr, salt.first, (std::min)(salt.second, left)); + ptr += (std::min)(salt.second, left); + left = canonical_length - (ptr - out); + } + ptr += snprintf(ptr, canonical_length - (ptr - out) + , "3:seqi%" PRId64 "e1:v", seq); + left = canonical_length - (ptr - out); + memcpy(ptr, v.first, (std::min)(v.second, left)); + ptr += (std::min)(v.second, left); + TORRENT_ASSERT((ptr - out) <= canonical_length); + return ptr - out; + } +} + +// calculate the target hash for an immutable item. +sha1_hash item_target_id(std::pair v) +{ + hasher h; + h.update(v.first, v.second); + return h.final(); +} + +// calculate the target hash for a mutable item. +sha1_hash item_target_id(std::pair salt + , char const* pk) +{ + hasher h; + h.update(pk, item_pk_len); + if (salt.second > 0) h.update(salt.first, salt.second); + return h.final(); +} + +bool verify_mutable_item( + std::pair v, + std::pair salt, + boost::uint64_t seq, + char const* pk, + char const* sig) +{ +#ifdef TORRENT_USE_VALGRIND + VALGRIND_CHECK_MEM_IS_DEFINED(v.first, v.second); + VALGRIND_CHECK_MEM_IS_DEFINED(pk, item_pk_len); + VALGRIND_CHECK_MEM_IS_DEFINED(sig, item_sig_len); +#endif + + char str[canonical_length]; + int len = canonical_string(v, seq, salt, str); + + return ed25519_verify((unsigned char const*)sig, + (unsigned char const*)str, + len, + (unsigned char const*)pk) == 1; +} + +// given the bencoded buffer ``v``, the salt (which is optional and may have +// a length of zero to be omitted), sequence number ``seq``, public key (32 +// bytes ed25519 key) ``pk`` and a secret/private key ``sk`` (64 bytes ed25519 +// key) a signature ``sig`` is produced. The ``sig`` pointer must point to +// at least 64 bytes of available space. This space is where the signature is +// written. +void sign_mutable_item( + std::pair v, + std::pair salt, + boost::uint64_t seq, + char const* pk, + char const* sk, + char* sig) +{ +#ifdef TORRENT_USE_VALGRIND + VALGRIND_CHECK_MEM_IS_DEFINED(v.first, v.second); + VALGRIND_CHECK_MEM_IS_DEFINED(sk, item_sk_len); + VALGRIND_CHECK_MEM_IS_DEFINED(pk, item_pk_len); +#endif + + char str[canonical_length]; + int len = canonical_string(v, seq, salt, str); + + ed25519_sign((unsigned char*)sig, + (unsigned char const*)str, + len, + (unsigned char const*)pk, + (unsigned char const*)sk + ); +} + +item::item(char const* pk, std::string const& salt) + : m_salt(salt) + , m_seq(0) + , m_mutable(true) +{ + memcpy(m_pk.data(), pk, item_pk_len); +} + +item::item(entry const& v + , std::pair salt + , boost::uint64_t seq, char const* pk, char const* sk) +{ + assign(v, salt, seq, pk, sk); +} + +void item::assign(entry const& v, std::pair salt + , boost::uint64_t seq, char const* pk, char const* sk) +{ + m_value = v; + if (pk && sk) + { + char buffer[1000]; + int bsize = bencode(buffer, v); + TORRENT_ASSERT(bsize <= 1000); + sign_mutable_item(std::make_pair(buffer, bsize) + , salt, seq, pk, sk, m_sig.c_array()); + m_salt.assign(salt.first, salt.second); + memcpy(m_pk.c_array(), pk, item_pk_len); + m_seq = seq; + m_mutable = true; + } + else + m_mutable = false; +} + +bool item::assign(lazy_entry const* v + , std::pair salt + , boost::uint64_t seq, char const* pk, char const* sig) +{ + TORRENT_ASSERT(v->data_section().second <= 1000); + if (pk && sig) + { + if (!verify_mutable_item(v->data_section(), salt, seq, pk, sig)) + return false; + memcpy(m_pk.c_array(), pk, item_pk_len); + memcpy(m_sig.c_array(), sig, item_sig_len); + if (salt.second > 0) + m_salt.assign(salt.first, salt.second); + else + m_salt.clear(); + m_seq = seq; + m_mutable = true; + } + else + m_mutable = false; + + m_value = *v; + return true; +} + +void item::assign(entry const& v, std::string salt, boost::uint64_t seq + , char const* pk, char const* sig) +{ + memcpy(m_pk.c_array(), pk, item_pk_len); + memcpy(m_sig.c_array(), sig, item_sig_len); + m_salt = salt; + m_seq = seq; + m_mutable = true; + m_value = v; +} + +} } // namespace libtorrent::dht diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/logging.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/logging.cpp new file mode 100644 index 0000000000..02522a7ca9 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/logging.cpp @@ -0,0 +1,55 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/kademlia/logging.hpp" +#include "libtorrent/time.hpp" + +namespace libtorrent { namespace dht +{ + log_event::log_event(log& log) + : log_(log) + { + if (log_.enabled()) + log_ << time_now_string() << " [" << log.id() << "] "; + } + + log_event::~log_event() + { + if (log_.enabled()) + { + log_ << "\n"; + log_.flush(); + } + } + +}} + diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/node.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/node.cpp new file mode 100644 index 0000000000..79fca13c1b --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/node.cpp @@ -0,0 +1,1291 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include + +#include "libtorrent/io.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/random.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/kademlia/node_id.hpp" +#include "libtorrent/kademlia/rpc_manager.hpp" +#include "libtorrent/kademlia/routing_table.hpp" +#include "libtorrent/kademlia/node.hpp" +#include "libtorrent/kademlia/dht_observer.hpp" + +#include "libtorrent/kademlia/refresh.hpp" +#include "libtorrent/kademlia/get_peers.hpp" +#include "libtorrent/kademlia/get_item.hpp" + +#ifdef TORRENT_USE_VALGRIND +#include +#endif + +namespace libtorrent { namespace dht +{ + +void incoming_error(entry& e, char const* msg, int error_code = 203); + +using detail::write_endpoint; + +// TODO: 2 make this configurable in dht_settings +enum { announce_interval = 30 }; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DEFINE_LOG(node) + +extern int g_failed_announces; +extern int g_announces; + +#endif + +// remove peers that have timed out +void purge_peers(std::set& peers) +{ + for (std::set::iterator i = peers.begin() + , end(peers.end()); i != end;) + { + // the peer has timed out + if (i->added + minutes(int(announce_interval * 1.5f)) < time_now()) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "peer timed out at: " << i->addr; +#endif + peers.erase(i++); + } + else + ++i; + } +} + +void nop() {} + +node_impl::node_impl(alert_dispatcher* alert_disp + , udp_socket_interface* sock + , dht_settings const& settings, node_id nid, address const& external_address + , dht_observer* observer) + : m_settings(settings) + , m_id(nid == (node_id::min)() || !verify_id(nid, external_address) ? generate_id(external_address) : nid) + , m_table(m_id, 8, settings) + , m_rpc(m_id, m_table, sock) + , m_observer(observer) + , m_last_tracker_tick(time_now()) + , m_last_self_refresh(min_time()) + , m_post_alert(alert_disp) + , m_sock(sock) +{ + m_secret[0] = random(); + m_secret[1] = std::rand(); +} + +bool node_impl::verify_token(std::string const& token, char const* info_hash + , udp::endpoint const& addr) +{ + if (token.length() != 4) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "token of incorrect length: " << token.length(); +#endif + return false; + } + + hasher h1; + error_code ec; + std::string address = addr.address().to_string(ec); + if (ec) return false; + h1.update(&address[0], address.length()); + h1.update((char*)&m_secret[0], sizeof(m_secret[0])); + h1.update((char*)info_hash, sha1_hash::size); + + sha1_hash h = h1.final(); + if (std::equal(token.begin(), token.end(), (char*)&h[0])) + return true; + + hasher h2; + h2.update(&address[0], address.length()); + h2.update((char*)&m_secret[1], sizeof(m_secret[1])); + h2.update((char*)info_hash, sha1_hash::size); + h = h2.final(); + if (std::equal(token.begin(), token.end(), (char*)&h[0])) + return true; + return false; +} + +std::string node_impl::generate_token(udp::endpoint const& addr, char const* info_hash) +{ + std::string token; + token.resize(4); + hasher h; + error_code ec; + std::string address = addr.address().to_string(ec); + TORRENT_ASSERT(!ec); + h.update(&address[0], address.length()); + h.update((char*)&m_secret[0], sizeof(m_secret[0])); + h.update(info_hash, sha1_hash::size); + + sha1_hash hash = h.final(); + std::copy(hash.begin(), hash.begin() + 4, (char*)&token[0]); + TORRENT_ASSERT(std::equal(token.begin(), token.end(), (char*)&hash[0])); + return token; +} + +void node_impl::bootstrap(std::vector const& nodes + , find_data::nodes_callback const& f) +{ + node_id target = m_id; + make_id_secret(target); + + boost::intrusive_ptr r(new dht::bootstrap(*this, target, f)); + m_last_self_refresh = time_now(); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + int count = 0; +#endif + + for (std::vector::const_iterator i = nodes.begin() + , end(nodes.end()); i != end; ++i) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + ++count; +#endif + r->add_entry(node_id(0), *i, observer::flag_initial); + } + + // make us start as far away from our node ID as possible + r->trim_seed_nodes(); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "bootstrapping with " << count << " nodes"; +#endif + r->start(); +} + +int node_impl::bucket_size(int bucket) +{ + return m_table.bucket_size(bucket); +} + +void node_impl::new_write_key() +{ + m_secret[1] = m_secret[0]; + m_secret[0] = random(); +} + +void node_impl::unreachable(udp::endpoint const& ep) +{ + m_rpc.unreachable(ep); +} + +void node_impl::incoming(msg const& m) +{ + // is this a reply? + lazy_entry const* y_ent = m.message.dict_find_string("y"); + if (!y_ent || y_ent->string_length() == 0) + { + // don't respond to this obviously broken messages. We don't + // want to open up a magnification opportunity +// entry e; +// incoming_error(e, "missing 'y' entry"); +// m_sock->send_packet(e, m.addr, 0); + return; + } + + char y = *(y_ent->string_ptr()); + + lazy_entry const* ext_ip = m.message.dict_find_string("ip"); + + // backwards compatibility + if (ext_ip == NULL) + { + lazy_entry const* r = m.message.dict_find_dict("r"); + if (r) + ext_ip = r->dict_find_string("ip"); + } + +#if TORRENT_USE_IPV6 + if (ext_ip && ext_ip->string_length() >= 16) + { + // this node claims we use the wrong node-ID! + address_v6::bytes_type b; + memcpy(&b[0], ext_ip->string_ptr(), 16); + if (m_observer) + m_observer->set_external_address(address_v6(b) + , m.addr.address()); + } else +#endif + if (ext_ip && ext_ip->string_length() >= 4) + { + address_v4::bytes_type b; + memcpy(&b[0], ext_ip->string_ptr(), 4); + if (m_observer) + m_observer->set_external_address(address_v4(b) + , m.addr.address()); + } + + switch (y) + { + case 'r': + { + node_id id; + m_rpc.incoming(m, &id, m_settings); + break; + } + case 'q': + { + TORRENT_ASSERT(m.message.dict_find_string_value("y") == "q"); + entry e; + incoming_request(m, e); + m_sock->send_packet(e, m.addr, 0); + break; + } + case 'e': + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + lazy_entry const* err = m.message.dict_find_list("e"); + if (err && err->list_size() >= 2) + { + TORRENT_LOG(node) << "INCOMING ERROR: " << err->list_string_value_at(1); + } +#endif + node_id id; + m_rpc.incoming(m, &id, m_settings); + break; + } + } +} + +namespace +{ + void announce_fun(std::vector > const& v + , node_impl& node, int listen_port, sha1_hash const& ih, int flags) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "sending announce_peer [ ih: " << ih + << " p: " << listen_port + << " nodes: " << v.size() << " ]" ; +#endif + + // create a dummy traversal_algorithm + boost::intrusive_ptr algo( + new traversal_algorithm(node, (node_id::min)())); + + // store on the first k nodes + for (std::vector >::const_iterator i = v.begin() + , end(v.end()); i != end; ++i) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << " announce-distance: " << (160 - distance_exp(ih, i->first.id)); +#endif + + void* ptr = node.m_rpc.allocate_observer(); + if (ptr == 0) return; + observer_ptr o(new (ptr) announce_observer(algo, i->first.ep(), i->first.id)); +#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS + o->m_in_constructor = false; +#endif + entry e; + e["y"] = "q"; + e["q"] = "announce_peer"; + entry& a = e["a"]; + a["info_hash"] = ih.to_string(); + a["port"] = listen_port; + a["token"] = i->second; + a["seed"] = (flags & node_impl::flag_seed) ? 1 : 0; + if (flags & node_impl::flag_implied_port) a["implied_port"] = 1; + node.m_rpc.invoke(e, i->first.ep(), o); + } + } +} + +void node_impl::add_router_node(udp::endpoint router) +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "adding router node: " << router; +#endif + m_table.add_router_node(router); +} + +void node_impl::add_node(udp::endpoint node) +{ + // ping the node, and if we get a reply, it + // will be added to the routing table + send_single_refresh(node, m_table.num_active_buckets()); +} + +void node_impl::announce(sha1_hash const& info_hash, int listen_port, int flags + , boost::function const&)> f) +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "announcing [ ih: " << info_hash << " p: " << listen_port << " ]" ; +#endif + // search for nodes with ids close to id or with peers + // for info-hash id. then send announce_peer to them. + + boost::intrusive_ptr ta; + if (m_settings.privacy_lookups) + { + ta.reset(new obfuscated_get_peers(*this, info_hash, f + , boost::bind(&announce_fun, _1, boost::ref(*this) + , listen_port, info_hash, flags), flags & node_impl::flag_seed)); + } + else + { + ta.reset(new get_peers(*this, info_hash, f + , boost::bind(&announce_fun, _1, boost::ref(*this) + , listen_port, info_hash, flags), flags & node_impl::flag_seed)); + } + + ta->start(); +} + +void node_impl::get_item(sha1_hash const& target + , boost::function f) +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "starting get for [ hash: " << target << " ]" ; +#endif + + boost::intrusive_ptr ta; + ta.reset(new dht::get_item(*this, target, f)); + ta->start(); +} + +void node_impl::get_item(char const* pk, std::string const& salt + , boost::function f) +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "starting get for [ key: " + << to_hex(std::string(pk, 32)) << " ]" ; +#endif + + boost::intrusive_ptr ta; + ta.reset(new dht::get_item(*this, pk, salt, f)); + ta->start(); +} + +struct ping_observer : observer +{ + ping_observer( + boost::intrusive_ptr const& algorithm + , udp::endpoint const& ep, node_id const& id) + : observer(algorithm, ep, id) + {} + + // parses out "nodes" + virtual void reply(msg const& m) + { + flags |= flag_done; + + lazy_entry const* r = m.message.dict_find_dict("r"); + if (!r) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << m_algorithm.get() + << "] missing response dict"; +#endif + return; + } + + // look for nodes + lazy_entry const* n = r->dict_find_string("nodes"); + if (n) + { + char const* nodes = n->string_ptr(); + char const* end = nodes + n->string_length(); + + while (end - nodes >= 26) + { + node_id id; + std::copy(nodes, nodes + 20, id.begin()); + nodes += 20; + m_algorithm.get()->node().m_table.heard_about(id + , detail::read_v4_endpoint(nodes)); + } + } + } +}; + + +void node_impl::tick() +{ + // every now and then we refresh our own ID, just to keep + // expanding the routing table buckets closer to us. + ptime now = time_now(); + if (m_last_self_refresh + minutes(10) < now) + { + node_id target = m_id; + make_id_secret(target); + boost::intrusive_ptr r(new dht::bootstrap(*this, target + , boost::bind(&nop))); + r->start(); + m_last_self_refresh = now; + return; + } + + node_entry const* ne = m_table.next_refresh(); + if (ne == NULL) return; + + // this shouldn't happen + TORRENT_ASSERT(m_id != ne->id); + if (ne->id == m_id) return; + + int bucket = 159 - distance_exp(m_id, ne->id); + TORRENT_ASSERT(bucket < 160); + send_single_refresh(ne->ep(), bucket, ne->id); +} + +void node_impl::send_single_refresh(udp::endpoint const& ep, int bucket + , node_id const& id) +{ + TORRENT_ASSERT(id != m_id); + void* ptr = m_rpc.allocate_observer(); + if (ptr == 0) return; + + TORRENT_ASSERT(bucket >= 0); + TORRENT_ASSERT(bucket <= 159); + + // generate a random node_id within the given bucket + // TODO: 2 it would be nice to have a bias towards node-id prefixes that + // are missing in the bucket + node_id mask = generate_prefix_mask(bucket + 1); + node_id target = generate_secret_id() & ~mask; + target |= m_id & mask; + + // create a dummy traversal_algorithm + // this is unfortunately necessary for the observer + // to free itself from the pool when it's being released + boost::intrusive_ptr algo( + new traversal_algorithm(*this, (node_id::min)())); + observer_ptr o(new (ptr) ping_observer(algo, ep, id)); +#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS + o->m_in_constructor = false; +#endif + entry e; + e["y"] = "q"; + entry& a = e["a"]; + + // use get_peers instead of find_node. We'll get nodes in the response + // either way. + e["q"] = "get_peers"; + a["info_hash"] = target.to_string(); + +// e["q"] = "find_node"; +// a["target"] = target.to_string(); + m_rpc.invoke(e, ep, o); +} + +time_duration node_impl::connection_timeout() +{ + time_duration d = m_rpc.tick(); + ptime now(time_now()); + if (now - m_last_tracker_tick < minutes(2)) return d; + m_last_tracker_tick = now; + + for (dht_immutable_table_t::iterator i = m_immutable_table.begin(); + i != m_immutable_table.end();) + { + if (i->second.last_seen + minutes(60) > now) + { + ++i; + continue; + } + free(i->second.value); + m_immutable_table.erase(i++); + } + + // look through all peers and see if any have timed out + for (table_t::iterator i = m_map.begin(), end(m_map.end()); i != end;) + { + torrent_entry& t = i->second; + node_id const& key = i->first; + ++i; + purge_peers(t.peers); + + // if there are no more peers, remove the entry altogether + if (t.peers.empty()) + { + table_t::iterator i = m_map.find(key); + if (i != m_map.end()) m_map.erase(i); + } + } + + return d; +} + +void node_impl::status(session_status& s) +{ + mutex_t::scoped_lock l(m_mutex); + + m_table.status(s); + s.dht_torrents = int(m_map.size()); + s.active_requests.clear(); + s.dht_total_allocations = m_rpc.num_allocated_observers(); + for (std::set::iterator i = m_running_requests.begin() + , end(m_running_requests.end()); i != end; ++i) + { + s.active_requests.push_back(dht_lookup()); + dht_lookup& l = s.active_requests.back(); + (*i)->status(l); + } +} + +void node_impl::lookup_peers(sha1_hash const& info_hash, entry& reply + , bool noseed, bool scrape) const +{ + if (m_post_alert) + { + alert* a = new dht_get_peers_alert(info_hash); + if (!m_post_alert->post_alert(a)) delete a; + } + + table_t::const_iterator i = m_map.lower_bound(info_hash); + if (i == m_map.end()) return; + if (i->first != info_hash) return; + + torrent_entry const& v = i->second; + + if (!v.name.empty()) reply["n"] = v.name; + + if (scrape) + { + bloom_filter<256> downloaders; + bloom_filter<256> seeds; + + for (std::set::const_iterator i = v.peers.begin() + , end(v.peers.end()); i != end; ++i) + { + sha1_hash iphash; + hash_address(i->addr.address(), iphash); + if (i->seed) seeds.set(iphash); + else downloaders.set(iphash); + } + + reply["BFpe"] = downloaders.to_string(); + reply["BFsd"] = seeds.to_string(); + } + else + { + int num = (std::min)((int)v.peers.size(), m_settings.max_peers_reply); + std::set::const_iterator iter = v.peers.begin(); + entry::list_type& pe = reply["values"].list(); + std::string endpoint; + + for (int t = 0, m = 0; m < num && iter != v.peers.end(); ++iter, ++t) + { + if ((random() / float(UINT_MAX + 1.f)) * (num - t) >= num - m) continue; + if (noseed && iter->seed) continue; + endpoint.resize(18); + std::string::iterator out = endpoint.begin(); + write_endpoint(iter->addr, out); + endpoint.resize(out - endpoint.begin()); + pe.push_back(entry(endpoint)); + + ++m; + } + } + return; +} + +namespace detail +{ + void TORRENT_EXTRA_EXPORT write_nodes_entry(entry& r, nodes_t const& nodes) + { + bool ipv6_nodes = false; + entry& n = r["nodes"]; + std::back_insert_iterator out(n.string()); + for (nodes_t::const_iterator i = nodes.begin() + , end(nodes.end()); i != end; ++i) + { + if (!i->addr().is_v4()) + { + ipv6_nodes = true; + continue; + } + std::copy(i->id.begin(), i->id.end(), out); + write_endpoint(udp::endpoint(i->addr(), i->port()), out); + } + + if (ipv6_nodes) + { + entry& p = r["nodes2"]; + std::string endpoint; + for (nodes_t::const_iterator i = nodes.begin() + , end(nodes.end()); i != end; ++i) + { + if (!i->addr().is_v6()) continue; + endpoint.resize(18 + 20); + std::string::iterator out = endpoint.begin(); + std::copy(i->id.begin(), i->id.end(), out); + out += 20; + write_endpoint(udp::endpoint(i->addr(), i->port()), out); + endpoint.resize(out - endpoint.begin()); + p.list().push_back(entry(endpoint)); + } + } + } +} +using detail::write_nodes_entry; + +// verifies that a message has all the required +// entries and returns them in ret +bool verify_message(lazy_entry const* msg, key_desc_t const desc[], lazy_entry const* ret[] + , int size , char* error, int error_size) +{ + // clear the return buffer + memset(ret, 0, sizeof(ret[0]) * size); + + // when parsing child nodes, this is the stack + // of lazy_entry pointers to return to + lazy_entry const* stack[5]; + int stack_ptr = -1; + + if (msg->type() != lazy_entry::dict_t) + { + snprintf(error, error_size, "not a dictionary"); + return false; + } + ++stack_ptr; + stack[stack_ptr] = msg; + for (int i = 0; i < size; ++i) + { + key_desc_t const& k = desc[i]; + +// fprintf(stderr, "looking for %s in %s\n", k.name, print_entry(*msg).c_str()); + + ret[i] = msg->dict_find(k.name); + // none_t means any type + if (ret[i] && ret[i]->type() != k.type && k.type != lazy_entry::none_t) ret[i] = 0; + if (ret[i] == 0 && (k.flags & key_desc_t::optional) == 0) + { + // the key was not found, and it's not an optional key + snprintf(error, error_size, "missing '%s' key", k.name); + return false; + } + + if (k.size > 0 + && ret[i] + && k.type == lazy_entry::string_t) + { + bool invalid = false; + if (k.flags & key_desc_t::size_divisible) + invalid = (ret[i]->string_length() % k.size) != 0; + else + invalid = ret[i]->string_length() != k.size; + + if (invalid) + { + // the string was not of the required size + ret[i] = 0; + if ((k.flags & key_desc_t::optional) == 0) + { + snprintf(error, error_size, "invalid value for '%s'", k.name); + return false; + } + } + } + if (k.flags & key_desc_t::parse_children) + { + TORRENT_ASSERT(k.type == lazy_entry::dict_t); + + if (ret[i]) + { + ++stack_ptr; + TORRENT_ASSERT(stack_ptr < int(sizeof(stack)/sizeof(stack[0]))); + msg = ret[i]; + stack[stack_ptr] = msg; + } + else + { + // skip all children + while (i < size && (desc[i].flags & key_desc_t::last_child) == 0) ++i; + // if this assert is hit, desc is incorrect + TORRENT_ASSERT(i < size); + } + } + else if (k.flags & key_desc_t::last_child) + { + TORRENT_ASSERT(stack_ptr > 0); + // this can happen if the specification passed + // in is unbalanced. i.e. contain more last_child + // nodes than parse_children + if (stack_ptr == 0) return false; + --stack_ptr; + msg = stack[stack_ptr]; + } + } + return true; +} + +void incoming_error(entry& e, char const* msg, int error_code) +{ + e["y"] = "e"; + entry::list_type& l = e["e"].list(); + l.push_back(entry(error_code)); + l.push_back(entry(msg)); +} + +// return true of the first argument is a better canidate for removal, i.e. +// less important to keep +struct immutable_item_comparator +{ + immutable_item_comparator(node_id const& our_id) : m_our_id(our_id) {} + + bool operator() (std::pair const& lhs + , std::pair const& rhs) const + { + int l_distance = distance_exp(lhs.first, m_our_id); + int r_distance = distance_exp(rhs.first, m_our_id); + + // this is a score taking the popularity (number of announcers) and the + // fit, in terms of distance from ideal storing node, into account. + // each additional 5 announcers is worth one extra bit in the distance. + // that is, an item with 10 announcers is allowed to be twice as far + // from another item with 5 announcers, from our node ID. Twice as far + // because it gets one more bit. + return lhs.second.num_announcers / 5 - l_distance < rhs.second.num_announcers / 5 - r_distance; + } + + node_id const& m_our_id; +}; + +// build response +void node_impl::incoming_request(msg const& m, entry& e) +{ + e = entry(entry::dictionary_t); + e["y"] = "r"; + e["t"] = m.message.dict_find_string_value("t"); + + key_desc_t top_desc[] = { + {"q", lazy_entry::string_t, 0, 0}, + {"ro", lazy_entry::int_t, 0, key_desc_t::optional}, + {"a", lazy_entry::dict_t, 0, key_desc_t::parse_children}, + {"id", lazy_entry::string_t, 20, key_desc_t::last_child}, + }; + + lazy_entry const* top_level[4]; + char error_string[200]; + if (!verify_message(&m.message, top_desc, top_level, 4, error_string, sizeof(error_string))) + { + incoming_error(e, error_string); + return; + } + + e["ip"] = endpoint_to_bytes(m.addr); + + char const* query = top_level[0]->string_cstr(); + + lazy_entry const* arg_ent = top_level[2]; + bool read_only = top_level[1] && top_level[1]->int_value() != 0; + node_id id(top_level[3]->string_ptr()); + + // if this nodes ID doesn't match its IP, tell it what + // its IP is with an error + // don't enforce this yet + if (m_settings.enforce_node_id && !verify_id(id, m.addr.address())) + { + incoming_error(e, "invalid node ID"); + return; + } + + if (!read_only) + m_table.heard_about(id, m.addr); + + entry& reply = e["r"]; + m_rpc.add_our_id(reply); + + // mirror back the other node's external port + reply["p"] = m.addr.port(); + + if (strcmp(query, "ping") == 0) + { + // we already have 't' and 'id' in the response + // no more left to add + } + else if (strcmp(query, "get_peers") == 0) + { + key_desc_t msg_desc[] = { + {"info_hash", lazy_entry::string_t, 20, 0}, + {"noseed", lazy_entry::int_t, 0, key_desc_t::optional}, + {"scrape", lazy_entry::int_t, 0, key_desc_t::optional}, + }; + + lazy_entry const* msg_keys[3]; + if (!verify_message(arg_ent, msg_desc, msg_keys, 3, error_string, sizeof(error_string))) + { + incoming_error(e, error_string); + return; + } + + reply["token"] = generate_token(m.addr, msg_keys[0]->string_ptr()); + + sha1_hash info_hash(msg_keys[0]->string_ptr()); + nodes_t n; + // always return nodes as well as peers + m_table.find_node(info_hash, n, 0); + write_nodes_entry(reply, n); + + bool noseed = false; + bool scrape = false; + if (msg_keys[1] && msg_keys[1]->int_value() != 0) noseed = true; + if (msg_keys[2] && msg_keys[2]->int_value() != 0) scrape = true; + lookup_peers(info_hash, reply, noseed, scrape); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + if (reply.find_key("values")) + { + TORRENT_LOG(node) << " values: " << reply["values"].list().size(); + } +#endif + } + else if (strcmp(query, "find_node") == 0) + { + key_desc_t msg_desc[] = { + {"target", lazy_entry::string_t, 20, 0}, + }; + + lazy_entry const* msg_keys[1]; + if (!verify_message(arg_ent, msg_desc, msg_keys, 1, error_string, sizeof(error_string))) + { + incoming_error(e, error_string); + return; + } + + sha1_hash target(msg_keys[0]->string_ptr()); + + // TODO: 2 find_node should write directly to the response entry + nodes_t n; + m_table.find_node(target, n, 0); + write_nodes_entry(reply, n); + } + else if (strcmp(query, "announce_peer") == 0) + { + key_desc_t msg_desc[] = { + {"info_hash", lazy_entry::string_t, 20, 0}, + {"port", lazy_entry::int_t, 0, 0}, + {"token", lazy_entry::string_t, 0, 0}, + {"n", lazy_entry::string_t, 0, key_desc_t::optional}, + {"seed", lazy_entry::int_t, 0, key_desc_t::optional}, + {"implied_port", lazy_entry::int_t, 0, key_desc_t::optional}, + }; + + lazy_entry const* msg_keys[6]; + if (!verify_message(arg_ent, msg_desc, msg_keys, 6, error_string, sizeof(error_string))) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + ++g_failed_announces; +#endif + incoming_error(e, error_string); + return; + } + + int port = int(msg_keys[1]->int_value()); + + // is the announcer asking to ignore the explicit + // listen port and instead use the source port of the packet? + if (msg_keys[5] && msg_keys[5]->int_value() != 0) + port = m.addr.port(); + + if (port < 0 || port >= 65536) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + ++g_failed_announces; +#endif + incoming_error(e, "invalid port"); + return; + } + + sha1_hash info_hash(msg_keys[0]->string_ptr()); + + if (m_post_alert) + { + alert* a = new dht_announce_alert(m.addr.address(), port, info_hash); + if (!m_post_alert->post_alert(a)) delete a; + } + + if (!verify_token(msg_keys[2]->string_value(), msg_keys[0]->string_ptr(), m.addr)) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + ++g_failed_announces; +#endif + incoming_error(e, "invalid token"); + return; + } + + // the token was correct. That means this + // node is not spoofing its address. So, let + // the table get a chance to add it. + m_table.node_seen(id, m.addr, 0xffff); + + if (!m_map.empty() && int(m_map.size()) >= m_settings.max_torrents) + { + // we need to remove some. Remove the ones with the + // fewest peers + int num_peers = m_map.begin()->second.peers.size(); + table_t::iterator candidate = m_map.begin(); + for (table_t::iterator i = m_map.begin() + , end(m_map.end()); i != end; ++i) + { + if (int(i->second.peers.size()) > num_peers) continue; + if (i->first == info_hash) continue; + num_peers = i->second.peers.size(); + candidate = i; + } + m_map.erase(candidate); + } + torrent_entry& v = m_map[info_hash]; + + // the peer announces a torrent name, and we don't have a name + // for this torrent. Store it. + if (msg_keys[3] && v.name.empty()) + { + std::string name = msg_keys[3]->string_value(); + if (name.size() > 50) name.resize(50); + v.name = name; + } + + peer_entry peer; + peer.addr = tcp::endpoint(m.addr.address(), port); + peer.added = time_now(); + peer.seed = msg_keys[4] && msg_keys[4]->int_value(); + std::set::iterator i = v.peers.find(peer); + if (i != v.peers.end()) v.peers.erase(i++); + v.peers.insert(i, peer); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + ++g_announces; +#endif + } + else if (strcmp(query, "put") == 0) + { + // the first 2 entries are for both mutable and + // immutable puts + const static key_desc_t msg_desc[] = { + {"token", lazy_entry::string_t, 0, 0}, + {"v", lazy_entry::none_t, 0, 0}, + {"seq", lazy_entry::int_t, 0, key_desc_t::optional}, + // public key + {"k", lazy_entry::string_t, item_pk_len, key_desc_t::optional}, + {"sig", lazy_entry::string_t, item_sig_len, key_desc_t::optional}, + {"cas", lazy_entry::int_t, 0, key_desc_t::optional}, + {"salt", lazy_entry::string_t, 0, key_desc_t::optional}, + }; + + // attempt to parse the message + lazy_entry const* msg_keys[7]; + if (!verify_message(arg_ent, msg_desc, msg_keys, 7, error_string, sizeof(error_string))) + { + incoming_error(e, error_string); + return; + } + + // is this a mutable put? + bool mutable_put = (msg_keys[2] && msg_keys[3] && msg_keys[4]); + + // public key (only set if it's a mutable put) + char const* pk = NULL; + if (msg_keys[3]) pk = msg_keys[3]->string_ptr(); + + // signature (only set if it's a mutable put) + char const* sig = NULL; + if (msg_keys[4]) sig = msg_keys[4]->string_ptr(); + + // pointer and length to the whole entry + std::pair buf = msg_keys[1]->data_section(); + if (buf.second > 1000 || buf.second <= 0) + { + incoming_error(e, "message too big", 205); + return; + } + + std::pair salt(static_cast(NULL), 0); + if (msg_keys[6]) + salt = std::pair( + msg_keys[6]->string_ptr(), msg_keys[6]->string_length()); + if (salt.second > 64) + { + incoming_error(e, "salt too big", 207); + return; + } + + sha1_hash target; + if (pk) + target = item_target_id(salt, pk); + else + target = item_target_id(buf); + +// fprintf(stderr, "%s PUT target: %s salt: %s key: %s\n" +// , mutable_put ? "mutable":"immutable" +// , to_hex(target.to_string()).c_str() +// , salt.second > 0 ? std::string(salt.first, salt.second).c_str() : "" +// , pk ? to_hex(std::string(pk, 32)).c_str() : ""); + + // verify the write-token. tokens are only valid to write to + // specific target hashes. it must match the one we got a "get" for + if (!verify_token(msg_keys[0]->string_value(), (char const*)&target[0], m.addr)) + { + incoming_error(e, "invalid token"); + return; + } + + dht_immutable_item* f = 0; + + if (!mutable_put) + { + dht_immutable_table_t::iterator i = m_immutable_table.find(target); + if (i == m_immutable_table.end()) + { + // make sure we don't add too many items + if (int(m_immutable_table.size()) >= m_settings.max_dht_items) + { + // delete the least important one (i.e. the one + // the fewest peers are announcing, and farthest + // from our node ID) + dht_immutable_table_t::iterator j = std::min_element(m_immutable_table.begin() + , m_immutable_table.end() + , immutable_item_comparator(m_id)); + + TORRENT_ASSERT(j != m_immutable_table.end()); + free(j->second.value); + m_immutable_table.erase(j); + } + dht_immutable_item to_add; + to_add.value = (char*)malloc(buf.second); + to_add.size = buf.second; + memcpy(to_add.value, buf.first, buf.second); + + boost::tie(i, boost::tuples::ignore) = m_immutable_table.insert( + std::make_pair(target, to_add)); + } + +// fprintf(stderr, "added immutable item (%d)\n", int(m_immutable_table.size())); + + f = &i->second; + } + else + { + // mutable put, we must verify the signature + +#ifdef TORRENT_USE_VALGRIND + VALGRIND_CHECK_MEM_IS_DEFINED(msg_keys[4]->string_ptr(), item_sig_len); + VALGRIND_CHECK_MEM_IS_DEFINED(pk, item_pk_len); +#endif + // msg_keys[4] is the signature, msg_keys[3] is the public key + if (!verify_mutable_item(buf, salt + , msg_keys[2]->int_value(), pk, sig)) + { + incoming_error(e, "invalid signature", 206); + return; + } + + dht_mutable_table_t::iterator i = m_mutable_table.find(target); + if (i == m_mutable_table.end()) + { + // this is the case where we don't have an item in this slot + // make sure we don't add too many items + if (int(m_mutable_table.size()) >= m_settings.max_dht_items) + { + // delete the least important one (i.e. the one + // the fewest peers are announcing) + dht_mutable_table_t::iterator j = std::min_element(m_mutable_table.begin() + , m_mutable_table.end() + , boost::bind(&dht_immutable_item::num_announcers + , boost::bind(&dht_mutable_table_t::value_type::second, _1))); + TORRENT_ASSERT(j != m_mutable_table.end()); + free(j->second.value); + free(j->second.salt); + m_mutable_table.erase(j); + } + dht_mutable_item to_add; + to_add.value = (char*)malloc(buf.second); + to_add.size = buf.second; + to_add.seq = msg_keys[2]->int_value(); + to_add.salt = NULL; + to_add.salt_size = 0; + if (salt.second > 0) + { + to_add.salt = (char*)malloc(salt.second); + to_add.salt_size = salt.second; + memcpy(to_add.salt, salt.first, salt.second); + } + memcpy(to_add.sig, sig, sizeof(to_add.sig)); + TORRENT_ASSERT(sizeof(to_add.sig) == msg_keys[4]->string_length()); + memcpy(to_add.value, buf.first, buf.second); + memcpy(&to_add.key, pk, sizeof(to_add.key)); + + boost::tie(i, boost::tuples::ignore) = m_mutable_table.insert( + std::make_pair(target, to_add)); + +// fprintf(stderr, "added mutable item (%d)\n", int(m_mutable_table.size())); + } + else + { + // this is the case where we already + dht_mutable_item* item = &i->second; + + // this is the "cas" field in the put message + // if it was specified, we MUST make sure the current sequence + // number matches the expected value before replacing it + // this is critical for avoiding race conditions when multiple + // writers are accessing the same slot + if (msg_keys[5] && item->seq != msg_keys[5]->int_value()) + { + incoming_error(e, "CAS mismatch", 301); + return; + } + + if (item->seq > boost::uint64_t(msg_keys[2]->int_value())) + { + incoming_error(e, "old sequence number", 302); + return; + } + + if (item->seq < boost::uint64_t(msg_keys[2]->int_value())) + { + if (item->size != buf.second) + { + free(item->value); + item->value = (char*)malloc(buf.second); + item->size = buf.second; + } + item->seq = msg_keys[2]->int_value(); + memcpy(item->sig, msg_keys[4]->string_ptr(), sizeof(item->sig)); + TORRENT_ASSERT(sizeof(item->sig) == msg_keys[4]->string_length()); + memcpy(item->value, buf.first, buf.second); + } + } + + f = &i->second; + } + + m_table.node_seen(id, m.addr, 0xffff); + + f->last_seen = time_now(); + + // maybe increase num_announcers if we haven't seen this IP before + sha1_hash iphash; + hash_address(m.addr.address(), iphash); + if (!f->ips.find(iphash)) + { + f->ips.set(iphash); + ++f->num_announcers; + } + } + else if (strcmp(query, "get") == 0) + { + key_desc_t msg_desc[] = { + {"seq", lazy_entry::int_t, 0, key_desc_t::optional}, + {"target", lazy_entry::string_t, 20, 0}, + }; + + // k is not used for now + + // attempt to parse the message + lazy_entry const* msg_keys[2]; + if (!verify_message(arg_ent, msg_desc, msg_keys, 2, error_string, sizeof(error_string))) + { + incoming_error(e, error_string); + return; + } + + sha1_hash target(msg_keys[1]->string_ptr()); + +// fprintf(stderr, "%s GET target: %s\n" +// , msg_keys[1] ? "mutable":"immutable" +// , to_hex(target.to_string()).c_str()); + + reply["token"] = generate_token(m.addr, msg_keys[1]->string_ptr()); + + nodes_t n; + // always return nodes as well as peers + m_table.find_node(target, n, 0); + write_nodes_entry(reply, n); + + dht_immutable_table_t::iterator i = m_immutable_table.end(); + + // if the get has a sequence number it must be for a mutable item + // so don't bother searching the immutable table + if (!msg_keys[0]) + i = m_immutable_table.find(target); + + if (i != m_immutable_table.end()) + { + dht_immutable_item const& f = i->second; + reply["v"] = bdecode(f.value, f.value + f.size); + } + else + { + dht_mutable_table_t::iterator i = m_mutable_table.find(target); + if (i != m_mutable_table.end()) + { + dht_mutable_item const& f = i->second; + reply["seq"] = f.seq; + if (!msg_keys[0] || boost::uint64_t(msg_keys[0]->int_value()) < f.seq) + { + reply["v"] = bdecode(f.value, f.value + f.size); + reply["sig"] = std::string(f.sig, f.sig + sizeof(f.sig)); + reply["k"] = std::string(f.key.bytes, f.key.bytes + sizeof(f.key.bytes)); + } + } + } + } + else + { + // if we don't recognize the message but there's a + // 'target' or 'info_hash' in the arguments, treat it + // as find_node to be future compatible + lazy_entry const* target_ent = arg_ent->dict_find_string("target"); + if (target_ent == 0 || target_ent->string_length() != 20) + { + target_ent = arg_ent->dict_find_string("info_hash"); + if (target_ent == 0 || target_ent->string_length() != 20) + { + incoming_error(e, "unknown message"); + return; + } + } + + sha1_hash target(target_ent->string_ptr()); + nodes_t n; + // always return nodes as well as peers + m_table.find_node(target, n, 0); + write_nodes_entry(reply, n); + return; + } +} + + +} } // namespace libtorrent::dht + diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/node_id.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/node_id.cpp new file mode 100644 index 0000000000..88b1f9af9f --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/node_id.cpp @@ -0,0 +1,231 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include + +#include "libtorrent/kademlia/node_id.hpp" +#include "libtorrent/kademlia/node_entry.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/broadcast_socket.hpp" // for is_local et.al +#include "libtorrent/socket_io.hpp" // for hash_address +#include "libtorrent/random.hpp" // for random +#include "libtorrent/hasher.hpp" // for hasher + +namespace libtorrent { namespace dht +{ + +// returns the distance between the two nodes +// using the kademlia XOR-metric +node_id distance(node_id const& n1, node_id const& n2) +{ + node_id ret; + node_id::iterator k = ret.begin(); + for (node_id::const_iterator i = n1.begin(), j = n2.begin() + , end(n1.end()); i != end; ++i, ++j, ++k) + { + *k = *i ^ *j; + } + return ret; +} + +// returns true if: distance(n1, ref) < distance(n2, ref) +bool compare_ref(node_id const& n1, node_id const& n2, node_id const& ref) +{ + for (node_id::const_iterator i = n1.begin(), j = n2.begin() + , k = ref.begin(), end(n1.end()); i != end; ++i, ++j, ++k) + { + boost::uint8_t lhs = (*i ^ *k); + boost::uint8_t rhs = (*j ^ *k); + if (lhs < rhs) return true; + if (lhs > rhs) return false; + } + return false; +} + +// returns n in: 2^n <= distance(n1, n2) < 2^(n+1) +// useful for finding out which bucket a node belongs to +int distance_exp(node_id const& n1, node_id const& n2) +{ + int byte = node_id::size - 1; + for (node_id::const_iterator i = n1.begin(), j = n2.begin() + , end(n1.end()); i != end; ++i, ++j, --byte) + { + TORRENT_ASSERT(byte >= 0); + boost::uint8_t t = *i ^ *j; + if (t == 0) continue; + // we have found the first non-zero byte + // return the bit-number of the first bit + // that differs + int bit = byte * 8; + for (int b = 7; b >= 0; --b) + if (t >= (1 << b)) return bit + b; + return bit; + } + + return 0; +} + +struct static_ { static_() { std::srand((unsigned int)std::time(0)); } } static__; + +node_id generate_id_impl(address const& ip_, boost::uint32_t r) +{ + boost::uint8_t* ip = 0; + + const static boost::uint8_t v4mask[] = { 0x03, 0x0f, 0x3f, 0xff }; + const static boost::uint8_t v6mask[] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff }; + boost::uint8_t const* mask = 0; + int num_octets = 0; + + address_v4::bytes_type b4; +#if TORRENT_USE_IPV6 + address_v6::bytes_type b6; + if (ip_.is_v6()) + { + b6 = ip_.to_v6().to_bytes(); + ip = &b6[0]; + num_octets = 8; + mask = v6mask; + } + else +#endif + { + b4 = ip_.to_v4().to_bytes(); + ip = &b4[0]; + num_octets = 4; + mask = v4mask; + } + + for (int i = 0; i < num_octets; ++i) + ip[i] &= mask[i]; + + ip[0] |= (r & 0x7) << 5; + + // this is the crc32c (Castagnoli) polynomial + // TODO: 2 this could be optimized if SSE 4.2 is + // available. It could also be optimized given + // that we have a fixed length + boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc; + crc.process_block(ip, ip + num_octets); + boost::uint32_t c = crc.checksum(); + node_id id; + + id[0] = (c >> 24) & 0xff; + id[1] = (c >> 16) & 0xff; + id[2] = ((c >> 8) & 0xf8) | (random() & 0x7); + + for (int i = 3; i < 19; ++i) id[i] = random() & 0xff; + id[19] = r & 0xff; + + return id; +} + +static boost::uint32_t secret = 0; + +void make_id_secret(node_id& in) +{ + if (secret == 0) secret = (random() % 0xfffffffe) + 1; + + boost::uint32_t rand = random(); + + // generate the last 4 bytes as a "signature" of the previous 4 bytes. This + // lets us verify whether a hash came from this function or not in the future. + hasher h((char*)&secret, 4); + h.update((char*)&rand, 4); + sha1_hash secret_hash = h.final(); + memcpy(&in[20-4], &secret_hash[0], 4); + memcpy(&in[20-8], &rand, 4); +} + +node_id generate_random_id() +{ + char r[20]; + for (int i = 0; i < 20; ++i) r[i] = random() & 0xff; + return hasher(r, 20).final(); +} + +node_id generate_secret_id() +{ + node_id ret = generate_random_id(); + make_id_secret(ret); + return ret; +} + +bool verify_secret_id(node_id const& nid) +{ + if (secret == 0) return false; + + hasher h((char*)&secret, 4); + h.update((char const*)&nid[20-8], 4); + sha1_hash secret_hash = h.final(); + return memcmp(&nid[20-4], &secret_hash[0], 4) == 0; +} + +// verifies whether a node-id matches the IP it's used from +// returns true if the node-id is OK coming from this source +// and false otherwise. +bool verify_id(node_id const& nid, address const& source_ip) +{ + // no need to verify local IPs, they would be incorrect anyway + if (is_local(source_ip)) return true; + + node_id h = generate_id_impl(source_ip, nid[19]); + return nid[0] == h[0] && nid[1] == h[1] && (nid[2] & 0xf8) == (h[2] & 0xf8); +} + +node_id generate_id(address const& ip) +{ + return generate_id_impl(ip, random()); +} + +bool matching_prefix(node_entry const& n, int mask, int prefix, int bucket_index) +{ + node_id id = n.id; + id <<= bucket_index + 1; + return (id[0] & mask) == prefix; +} + +node_id generate_prefix_mask(int bits) +{ + TORRENT_ASSERT(bits >= 0); + TORRENT_ASSERT(bits <= 160); + node_id mask(0); + int b = 0; + for (; b < bits - 7; b += 8) mask[b/8] |= 0xff; + if (bits < 160) mask[b/8] |= (0xff << (8 - (bits&7))) & 0xff; + return mask; +} + +} } // namespace libtorrent::dht + diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/refresh.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/refresh.cpp new file mode 100644 index 0000000000..dc0eb3b20d --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/refresh.cpp @@ -0,0 +1,110 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include + +#include + +namespace libtorrent { namespace dht +{ + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_DECLARE_LOG(traversal); +#endif + +observer_ptr bootstrap::new_observer(void* ptr + , udp::endpoint const& ep, node_id const& id) +{ + observer_ptr o(new (ptr) get_peers_observer(this, ep, id)); +#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS + o->m_in_constructor = false; +#endif + return o; +} + +bool bootstrap::invoke(observer_ptr o) +{ + entry e; + e["y"] = "q"; + entry& a = e["a"]; + + e["q"] = "get_peers"; + a["info_hash"] = target().to_string(); + +// e["q"] = "find_node"; +// a["target"] = target().to_string(); + return m_node.m_rpc.invoke(e, o->target_ep(), o); +} + +bootstrap::bootstrap( + node_impl& node + , node_id target + , done_callback const& callback) + : get_peers(node, target, get_peers::data_callback(), callback, false) +{ + // make it more resilient to nodes not responding. + // we don't want to terminate early when we're bootstrapping + m_num_target_nodes *= 2; +} + +char const* bootstrap::name() const { return "bootstrap"; } + +void bootstrap::trim_seed_nodes() +{ + // when we're bootstrapping, we want to start as far away from our ID as + // possible, to cover as much as possible of the ID space. So, remove all + // nodes except for the 32 that are farthest away from us + if (m_results.size() > 32) + m_results.erase(m_results.begin(), m_results.end() - 32); +} + +void bootstrap::done() +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "]" + << " bootstrap done, pinging remaining nodes"; +#endif + + for (std::vector::iterator i = m_results.begin() + , end(m_results.end()); i != end; ++i) + { + if ((*i)->flags & observer::flag_queried) continue; + // this will send a ping + m_node.add_node((*i)->target_ep()); + } + get_peers::done(); +} + +} } // namespace libtorrent::dht + diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/routing_table.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/routing_table.cpp new file mode 100644 index 0000000000..8df6cf31d4 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/routing_table.cpp @@ -0,0 +1,1146 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include // std::distance() +#include // std::copy, std::remove_copy_if +#include +#include +#include +#include + +#include "libtorrent/kademlia/routing_table.hpp" +#include "libtorrent/broadcast_socket.hpp" // for cidr_distance +#include "libtorrent/session_status.hpp" +#include "libtorrent/kademlia/node_id.hpp" +#include "libtorrent/time.hpp" + +#include "libtorrent/invariant_check.hpp" + +using boost::uint8_t; + +namespace libtorrent { namespace dht +{ + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DEFINE_LOG(table) +#endif + +template +void erase_one(T& container, K const& key) +{ + typename T::iterator i = container.find(key); + TORRENT_ASSERT(i != container.end()); + container.erase(i); +} + +routing_table::routing_table(node_id const& id, int bucket_size + , dht_settings const& settings) + : m_settings(settings) + , m_bucket_size(bucket_size) + , m_id(id) + , m_depth(0) + , m_last_self_refresh(min_time()) +{ + m_buckets.reserve(30); +} + +int routing_table::bucket_limit(int bucket) const +{ + if (!m_settings.extended_routing_table) return m_bucket_size; + + const static int size_exceptions[] = {16, 8, 4, 2}; + if (bucket < int(sizeof(size_exceptions)/sizeof(size_exceptions[0]))) + return m_bucket_size * size_exceptions[bucket]; + return m_bucket_size; +} + +void routing_table::status(session_status& s) const +{ + int ignore; + boost::tie(s.dht_nodes, s.dht_node_cache, ignore) = size(); + s.dht_global_nodes = num_global_nodes(); + + for (table_t::const_iterator i = m_buckets.begin() + , end(m_buckets.end()); i != end; ++i) + { + dht_routing_bucket b; + b.num_nodes = i->live_nodes.size(); + b.num_replacements = i->replacements.size(); +#ifndef TORRENT_NO_DEPRECATE + b.last_active = 0; +#endif + s.dht_routing_table.push_back(b); + } +} + +boost::tuple routing_table::size() const +{ + int nodes = 0; + int replacements = 0; + int confirmed = 0; + for (table_t::const_iterator i = m_buckets.begin() + , end(m_buckets.end()); i != end; ++i) + { + nodes += i->live_nodes.size(); + for (bucket_t::const_iterator k = i->live_nodes.begin() + , end(i->live_nodes.end()); k != end; ++k) + { + if (k->confirmed()) ++confirmed; + } + + replacements += i->replacements.size(); + } + return boost::make_tuple(nodes, replacements, confirmed); +} + +size_type routing_table::num_global_nodes() const +{ + int deepest_bucket = 0; + int deepest_size = 0; + for (table_t::const_iterator i = m_buckets.begin() + , end(m_buckets.end()); i != end; ++i) + { + deepest_size = i->live_nodes.size(); // + i->replacements.size(); + if (deepest_size < m_bucket_size) break; + // this bucket is full + ++deepest_bucket; + } + + if (deepest_bucket == 0) return 1 + deepest_size; + + if (deepest_size < m_bucket_size / 2) return (size_type(1) << deepest_bucket) * m_bucket_size; + else return (size_type(2) << deepest_bucket) * deepest_size; +} + +int routing_table::depth() const +{ + if (m_depth >= int(m_buckets.size())) + m_depth = m_buckets.size() - 1; + + if (m_depth < 0) return m_depth; + + // maybe the table is deeper now? + while (m_depth < int(m_buckets.size())-1 + && int(m_buckets[m_depth+1].live_nodes.size()) >= m_bucket_size / 2) + { + ++m_depth; + } + + // maybe the table is more shallow now? + while (m_depth > 0 + && int(m_buckets[m_depth-1].live_nodes.size()) < m_bucket_size / 2) + { + --m_depth; + } + + return m_depth; +} + +#if (defined TORRENT_DHT_VERBOSE_LOGGING || defined TORRENT_DEBUG) && TORRENT_USE_IOSTREAM + +void routing_table::print_state(std::ostream& os) const +{ + os << "kademlia routing table state\n" + << "bucket_size: " << m_bucket_size << "\n" + << "global node count: " << num_global_nodes() << "\n" + << "node_id: " << m_id << "\n\n"; + + os << "number of nodes per bucket:\n"; + + int idx = 0; + + for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); + i != end; ++i, ++idx) + { + os << std::setw(2) << idx << ": "; + for (int k = 0; k < int(i->live_nodes.size()); ++k) + os << "#"; + for (int k = 0; k < int(i->replacements.size()); ++k) + os << "-"; + os << "\n"; + } + + ptime now = time_now(); + + os << "\nnodes:"; + int bucket_index = 0; + for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); + i != end; ++i, ++bucket_index) + { + os << "\n=== BUCKET == " << bucket_index + << " == " << i->live_nodes.size() << "|" << i->replacements.size() + << " ===== \n"; + + int id_shift; + // the last bucket is special, since it hasn't been split yet, it + // includes that top bit as well + if (bucket_index + 1 == m_buckets.size()) + id_shift = bucket_index; + else + id_shift = bucket_index + 1; + + for (bucket_t::const_iterator j = i->live_nodes.begin() + , end(i->live_nodes.end()); j != end; ++j) + { + int bucket_size_limit = bucket_limit(bucket_index); + boost::uint32_t top_mask = bucket_size_limit - 1; + int mask_shift = 0; + TORRENT_ASSERT_VAL(bucket_size_limit > 0, bucket_size_limit); + while ((top_mask & 0x80) == 0) + { + top_mask <<= 1; + ++mask_shift; + } + top_mask = (0xff << mask_shift) & 0xff; + + node_id id = j->id; + id <<= id_shift; + + os << " prefx: " << std::setw(2) << std::hex << ((id[0] & top_mask) >> mask_shift) << std::dec + << " id: " << j->id; + if (j->rtt == 0xffff) + os << " rtt: "; + else + os << " rtt: " << std::setw(4) << j->rtt; + + os << " fail: " << j->fail_count() + << " ping: " << j->pinged() + << " dist: " << std::setw(3) << distance_exp(m_id, j->id); + + if (j->last_queried == min_time()) + os << " query: "; + else + os << " query: " << std::setw(3) << total_seconds(now - j->last_queried); + + os << " ip: " << j->ep() + << "\n"; + } + } + + os << "\nnode spread per bucket:\n"; + bucket_index = 0; + for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); + i != end; ++i, ++bucket_index) + { + int bucket_size_limit = bucket_limit(bucket_index); + + // mask out the first 3 bits, or more depending + // on the bucket_size_limit + // we have all the lower bits set in (bucket_size_limit-1) + // but we want the left-most bits to be set. Shift it + // until the MSB is set + boost::uint32_t top_mask = bucket_size_limit - 1; + int mask_shift = 0; + TORRENT_ASSERT_VAL(bucket_size_limit > 0, bucket_size_limit); + while ((top_mask & 0x80) == 0) + { + top_mask <<= 1; + ++mask_shift; + } + top_mask = (0xff << mask_shift) & 0xff; + bucket_size_limit = (top_mask >> mask_shift) + 1; + TORRENT_ASSERT_VAL(bucket_size_limit <= 256, bucket_size_limit); + bool sub_buckets[256]; + memset(sub_buckets, 0, sizeof(sub_buckets)); + + int id_shift; + // the last bucket is special, since it hasn't been split yet, it + // includes that top bit as well + if (bucket_index + 1 == m_buckets.size()) + id_shift = bucket_index; + else + id_shift = bucket_index + 1; + + for (bucket_t::const_iterator j = i->live_nodes.begin() + , end(i->live_nodes.end()); j != end; ++j) + { + node_id id = j->id; + id <<= id_shift; + int b = (id[0] & top_mask) >> mask_shift; + TORRENT_ASSERT(b >= 0 && b < int(sizeof(sub_buckets)/sizeof(sub_buckets[0]))); + sub_buckets[b] = true; + } + + os << std::dec << std::setw(2) << bucket_index << " mask: " << std::setw(2) + << std::hex << (top_mask >> mask_shift) << ": ["; + + for (int i = 0; i < bucket_size_limit; ++i) os << (sub_buckets[i] ? "X" : " "); + os << "]\n"; + } +} + +#endif + +node_entry const* routing_table::next_refresh() +{ + // find the node with the least recent 'last_queried' field. if it's too + // recent, return false. Otherwise return a random target ID that's close to + // a missing prefix for that bucket + + node_entry* candidate = NULL; + int bucket_idx = -1; + + // this will have a bias towards pinging nodes close to us first. + int idx = m_buckets.size() - 1; + for (table_t::reverse_iterator i = m_buckets.rbegin() + , end(m_buckets.rend()); i != end; ++i, --idx) + { + for (bucket_t::iterator j = i->live_nodes.begin() + , end(i->live_nodes.end()); j != end; ++j) + { + // this shouldn't happen + TORRENT_ASSERT(m_id != j->id); + if (j->id == m_id) continue; + + if (j->last_queried == min_time()) + { + bucket_idx = idx; + candidate = &*j; + goto out; + } + + if (candidate == NULL || j->last_queried < candidate->last_queried) + { + candidate = &*j; + bucket_idx = idx; + } + } + } +out: + + // make sure we don't pick the same node again next time we want to refresh + // the routing table + if (candidate) + candidate->last_queried = time_now(); + + return candidate; +} + +void routing_table::replacement_cache(bucket_t& nodes) const +{ + for (table_t::const_iterator i = m_buckets.begin() + , end(m_buckets.end()); i != end; ++i) + { + std::copy(i->replacements.begin(), i->replacements.end() + , std::back_inserter(nodes)); + } +} + +routing_table::table_t::iterator routing_table::find_bucket(node_id const& id) +{ +// TORRENT_ASSERT(id != m_id); + + int num_buckets = m_buckets.size(); + if (num_buckets == 0) + { + m_buckets.push_back(routing_table_node()); + ++num_buckets; + } + + int bucket_index = (std::min)(159 - distance_exp(m_id, id), num_buckets - 1); + TORRENT_ASSERT(bucket_index < int(m_buckets.size())); + TORRENT_ASSERT(bucket_index >= 0); + + table_t::iterator i = m_buckets.begin(); + std::advance(i, bucket_index); + return i; +} + +bool compare_ip_cidr(node_entry const& lhs, node_entry const& rhs) +{ + TORRENT_ASSERT(lhs.addr().is_v4() == rhs.addr().is_v4()); + // the number of bits in the IPs that may match. If + // more bits that this matches, something suspicious is + // going on and we shouldn't add the second one to our + // routing table + int cutoff = rhs.addr().is_v4() ? 8 : 64; + int dist = cidr_distance(lhs.addr(), rhs.addr()); + return dist <= cutoff; +} + +node_entry* routing_table::find_node(udp::endpoint const& ep + , routing_table::table_t::iterator* bucket) +{ + for (table_t::iterator i = m_buckets.begin() + , end(m_buckets.end()); i != end; ++i) + { + for (bucket_t::iterator j = i->replacements.begin(); + j != i->replacements.end(); ++j) + { + if (j->addr() != ep.address()) continue; + if (j->port() != ep.port()) continue; + *bucket = i; + return &*j; + } + for (bucket_t::iterator j = i->live_nodes.begin(); + j != i->live_nodes.end(); ++j) + { + if (j->addr() != ep.address()) continue; + if (j->port() != ep.port()) continue; + *bucket = i; + return &*j; + } + } + *bucket = m_buckets.end(); + return 0; +} + +void routing_table::remove_node(node_entry* n + , routing_table::table_t::iterator bucket) +{ + INVARIANT_CHECK; + + if (!bucket->replacements.empty() + && n >= &bucket->replacements[0] + && n < &bucket->replacements[0] + bucket->replacements.size()) + { + int idx = n - &bucket->replacements[0]; + TORRENT_ASSERT(m_ips.count(n->a) > 0); + erase_one(m_ips, n->a); + bucket->replacements.erase(bucket->replacements.begin() + idx); + } + + if (!bucket->live_nodes.empty() + && n >= &bucket->live_nodes[0] + && n < &bucket->live_nodes[0] + bucket->live_nodes.size()) + { + int idx = n - &bucket->live_nodes[0]; + TORRENT_ASSERT(m_ips.count(n->a) > 0); + erase_one(m_ips, n->a); + bucket->live_nodes.erase(bucket->live_nodes.begin() + idx); + } +} + +bool routing_table::add_node(node_entry e) +{ + add_node_status_t s = add_node_impl(e); + if (s == failed_to_add) return false; + if (s == node_added) return true; + + while (s == need_bucket_split) + { + split_bucket(); + + // if this assert triggers a lot in the wild, we should probably + // harden our resistence towards this attack. Perhaps by never + // splitting a bucket (and discard nodes) if the two buckets above it + // are empty or close to empty + TORRENT_ASSERT(m_buckets.size() <= 50); + if (m_buckets.size() > 50) + { + // this is a sanity check. In the wild, we shouldn't see routing + // tables deeper than 26 or 27. If we get this deep, there might + // be a bug in the bucket splitting logic, or there may be someone + // playing a prank on us, spoofing node IDs. + s = add_node_impl(e); + if (s == node_added) return true; + return false; + } + + // if the new bucket still has too many nodes in it, we need to keep + // splitting + if (m_buckets.back().live_nodes.size() > bucket_limit(m_buckets.size()-1)) + continue; + + s = add_node_impl(e); + if (s == failed_to_add) return false; + if (s == node_added) return true; + } + return false; +} + +routing_table::add_node_status_t routing_table::add_node_impl(node_entry e) +{ + INVARIANT_CHECK; + + // if we already have this (IP,port), don't do anything + if (m_router_nodes.find(e.ep()) != m_router_nodes.end()) + return failed_to_add; + + // don't add ourself + if (e.id == m_id) return failed_to_add; + + // do we already have this IP in the table? + if (m_ips.count(e.addr().to_v4().to_bytes()) > 0) + { + // this exact IP already exists in the table. It might be the case + // that the node changed IP. If pinged is true, and the port also + // matches then we assume it's in fact the same node, and just update + // the routing table + // pinged means that we have sent a message to the IP, port and received + // a response with a correct transaction ID, i.e. it is verified to not + // be the result of a poisoned routing table + + table_t::iterator existing_bucket; + node_entry* existing = find_node(e.ep(), &existing_bucket); + if (!e.pinged() || existing == 0) + { + // the new node is not pinged, or it's not an existing node + // we should ignore it, unless we allow duplicate IPs in our + // routing table + if (m_settings.restrict_routing_ips) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(table) << "ignoring node (duplicate IP): " + << e.id << " " << e.addr(); +#endif + return failed_to_add; + } + } + else if (existing && existing->id == e.id) + { + // if the node ID is the same, just update the failcount + // and be done with it + existing->timeout_count = 0; + existing->update_rtt(e.rtt); + existing->last_queried = e.last_queried; + return node_added; + } + else if (existing) + { + TORRENT_ASSERT(existing->id != e.id); + // this is the same IP and port, but with + // a new node ID. remove the old entry and + // replace it with this new ID + remove_node(existing, existing_bucket); + } + } + + table_t::iterator i = find_bucket(e.id); + bucket_t& b = i->live_nodes; + bucket_t& rb = i->replacements; + int bucket_index = std::distance(m_buckets.begin(), i); + int bucket_size_limit = bucket_limit(bucket_index); + + bucket_t::iterator j; + + // if the node already exists, we don't need it + j = std::find_if(b.begin(), b.end() + , boost::bind(&node_entry::id, _1) == e.id); + + if (j != b.end()) + { + // a new IP address just claimed this node-ID + // ignore it + if (j->addr() != e.addr() || j->port() != e.port()) + return failed_to_add; + + // we already have the node in our bucket + TORRENT_ASSERT(j->id == e.id && j->ep() == e.ep()); + j->timeout_count = 0; + j->update_rtt(e.rtt); +// TORRENT_LOG(table) << "updating node: " << i->id << " " << i->addr(); + return node_added; + } + + // if this node exists in the replacement bucket. update it and + // pull it out from there. We may add it back to the replacement + // bucket, but we may also replace a node in the main bucket, now + // that we have an updated RTT + j = std::find_if(rb.begin(), rb.end(), boost::bind(&node_entry::id, _1) == e.id); + if (j != rb.end()) + { + // a new IP address just claimed this node-ID + // ignore it + if (j->addr() != e.addr() || j->port() != e.port()) + return failed_to_add; + + TORRENT_ASSERT(j->id == e.id && j->ep() == e.ep()); + j->timeout_count = 0; + j->update_rtt(e.rtt); + e = *j; + erase_one(m_ips, j->addr().to_v4().to_bytes()); + rb.erase(j); + } + + if (m_settings.restrict_routing_ips) + { + // don't allow multiple entries from IPs very close to each other + j = std::find_if(b.begin(), b.end(), boost::bind(&compare_ip_cidr, _1, e)); + if (j != b.end()) + { + // we already have a node in this bucket with an IP very + // close to this one. We know that it's not the same, because + // it claims a different node-ID. Ignore this to avoid attacks +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(table) << "ignoring node: " << e.id << " " << e.addr() + << " existing node: " + << j->id << " " << j->addr(); +#endif + return failed_to_add; + } + + j = std::find_if(rb.begin(), rb.end(), boost::bind(&compare_ip_cidr, _1, e)); + if (j != rb.end()) + { + // same thing but for the replacement bucket +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(table) << "ignoring (replacement) node: " << e.id << " " << e.addr() + << " existing node: " + << j->id << " " << j->addr(); +#endif + return failed_to_add; + } + } + + // if there's room in the main bucket, just insert it + if (int(b.size()) < bucket_size_limit) + { + if (b.empty()) b.reserve(bucket_size_limit); + b.push_back(e); + m_ips.insert(e.addr().to_v4().to_bytes()); +// TORRENT_LOG(table) << "inserting node: " << e.id << " " << e.addr(); + return node_added; + } + + // if there is no room, we look for nodes that are not 'pinged', + // i.e. we haven't confirmed that they respond to messages. + // Then we look for nodes marked as stale + // in the k-bucket. If we find one, we can replace it. + // then we look for nodes with the same 3 bit prefix (or however + // many bits prefix the bucket size warrants). If there is no other + // node with this prefix, remove the duplicate with the highest RTT. + // as the last replacement strategy, if the node we found matching our + // bit prefix has higher RTT than the new node, replace it. + + // can we split the bucket? + // only nodes that haven't failed can split the bucket, and we can only + // split the last bucket + bool can_split = (boost::next(i) == m_buckets.end() && m_buckets.size() < 159) + && e.fail_count() == 0; + + if (e.pinged() && e.fail_count() == 0) + { + // if the node we're trying to insert is considered pinged, + // we may replace other nodes that aren't pinged + + j = std::find_if(b.begin(), b.end(), boost::bind(&node_entry::pinged, _1) == false); + + if (j != b.end() && !j->pinged()) + { + // j points to a node that has not been pinged. + // Replace it with this new one + erase_one(m_ips, j->addr().to_v4().to_bytes()); + *j = e; + m_ips.insert(e.addr().to_v4().to_bytes()); +// TORRENT_LOG(table) << "replacing unpinged node: " << e.id << " " << e.addr(); + return node_added; + } + + // A node is considered stale if it has failed at least one + // time. Here we choose the node that has failed most times. + // If we don't find one, place this node in the replacement- + // cache and replace any nodes that will fail in the future + // with nodes from that cache. + + j = std::max_element(b.begin(), b.end() + , boost::bind(&node_entry::fail_count, _1) + < boost::bind(&node_entry::fail_count, _2)); + TORRENT_ASSERT(j != b.end()); + + if (j->fail_count() > 0) + { + // i points to a node that has been marked + // as stale. Replace it with this new one + erase_one(m_ips, j->addr().to_v4().to_bytes()); + *j = e; + m_ips.insert(e.addr().to_v4().to_bytes()); +// TORRENT_LOG(table) << "replacing stale node: " << e.id << " " << e.addr(); + return node_added; + } + + // in order to provide as few lookups as possible before finding + // the data someone is looking for, make sure there is an affinity + // towards having a good spread of node IDs in each bucket + + boost::uint32_t mask = bucket_size_limit - 1; + int mask_shift = 0; + TORRENT_ASSERT_VAL(mask > 0, mask); + while ((mask & 0x80) == 0) + { + mask <<= 1; + ++mask_shift; + } + + // in case bucket_size_limit is not an even power of 2 + mask = (0xff << mask_shift) & 0xff; + + node_id id = e.id; + // the last bucket is special, since it hasn't been split yet, it + // includes that top bit as well + if (bucket_index + 1 == m_buckets.size()) + id <<= bucket_index; + else + id <<= bucket_index + 1; + + // pick out all nodes that have the same prefix as the new node + std::vector nodes; + bool force_replace = false; + for (j = b.begin(); j != b.end(); ++j) + { + if (!matching_prefix(*j, mask, id[0] & mask, bucket_index)) continue; + nodes.push_back(j); + } + + if (!nodes.empty()) + { + j = *std::max_element(nodes.begin(), nodes.end() + , boost::bind(&node_entry::rtt, boost::bind(&bucket_t::iterator::operator*, _1)) + < boost::bind(&node_entry::rtt, boost::bind(&bucket_t::iterator::operator*, _2))); + } + else + { + // there is no node in this prefix-slot, there may be some + // nodes sharing a prefix. Find all nodes that do not + // have a unique prefix + + // find node entries with duplicate prefixes in O(1) + std::vector prefix(1 << (8 - mask_shift), b.end()); + TORRENT_ASSERT(int(prefix.size()) >= bucket_size_limit); + + // the begin iterator from this object is used as a placeholder + // for an occupied slot whose node has already been added to the + // duplicate nodes list. + bucket_t placeholder; + + nodes.reserve(b.size()); + for (j = b.begin(); j != b.end(); ++j) + { + node_id id = j->id; + id <<= bucket_index + 1; + int this_prefix = (id[0] & mask) >> mask_shift; + TORRENT_ASSERT(this_prefix >= 0); + TORRENT_ASSERT(this_prefix < int(prefix.size())); + if (prefix[this_prefix] != b.end()) + { + // there's already a node with this prefix. Remember both + // duplicates. + nodes.push_back(j); + + if (prefix[this_prefix] != placeholder.begin()) + { + nodes.push_back(prefix[this_prefix]); + prefix[this_prefix] = placeholder.begin(); + } + } + } + + if (!nodes.empty()) + { + // from these nodes, pick the one with the highest RTT + // and replace it + + std::vector::iterator k = std::max_element(nodes.begin(), nodes.end() + , boost::bind(&node_entry::rtt, boost::bind(&bucket_t::iterator::operator*, _1)) + < boost::bind(&node_entry::rtt, boost::bind(&bucket_t::iterator::operator*, _2))); + + // in this case, we would really rather replace the node even if + // the new node has higher RTT, becase it fills a new prefix that we otherwise + // don't have. + force_replace = true; + j = *k; + } + else + { + j = std::max_element(b.begin(), b.end() + , boost::bind(&node_entry::rtt, _1) + < boost::bind(&node_entry::rtt, _2)); + } + } + + if (j != b.end() && (force_replace || j->rtt > e.rtt)) + { + erase_one(m_ips, j->addr().to_v4().to_bytes()); + *j = e; + m_ips.insert(e.addr().to_v4().to_bytes()); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(table) << "replacing node with higher RTT: " << e.id + << " " << e.addr(); +#endif + return node_added; + } + // in order to keep lookup times small, prefer nodes with low RTTs + + } + + // if we can't split, try to insert into the replacement bucket + + if (!can_split) + { + // if we don't have any identified stale nodes in + // the bucket, and the bucket is full, we have to + // cache this node and wait until some node fails + // and then replace it. + + j = std::find_if(rb.begin(), rb.end() + , boost::bind(&node_entry::id, _1) == e.id); + + // if the node is already in the replacement bucket + // just return. + if (j != rb.end()) + { + // if the IP address matches, it's the same node + // make sure it's marked as pinged + if (j->ep() == e.ep()) j->set_pinged(); + return node_added; + } + + if ((int)rb.size() >= m_bucket_size) + { + // if the replacement bucket is full, remove the oldest entry + // but prefer nodes that haven't been pinged, since they are + // less reliable than this one, that has been pinged + j = std::find_if(rb.begin(), rb.end(), boost::bind(&node_entry::pinged, _1) == false); + if (j == rb.end()) j = rb.begin(); + erase_one(m_ips, j->addr().to_v4().to_bytes()); + rb.erase(j); + } + + if (rb.empty()) rb.reserve(m_bucket_size); + rb.push_back(e); + m_ips.insert(e.addr().to_v4().to_bytes()); +// TORRENT_LOG(table) << "inserting node in replacement cache: " << e.id << " " << e.addr(); + return node_added; + } + + return need_bucket_split; +} + +void routing_table::split_bucket() +{ + INVARIANT_CHECK; + + int bucket_index = m_buckets.size()-1; + int bucket_size_limit = bucket_limit(bucket_index); + TORRENT_ASSERT(int(m_buckets.back().live_nodes.size()) >= bucket_size_limit); + + // this is the last bucket, and it's full already. Split + // it by adding another bucket + m_buckets.push_back(routing_table_node()); + bucket_t& new_bucket = m_buckets.back().live_nodes; + bucket_t& new_replacement_bucket = m_buckets.back().replacements; + + bucket_t& b = m_buckets[bucket_index].live_nodes; + bucket_t& rb = m_buckets[bucket_index].replacements; + + // move any node whose (160 - distane_exp(m_id, id)) >= (i - m_buckets.begin()) + // to the new bucket + int new_bucket_size = bucket_limit(bucket_index + 1); + for (bucket_t::iterator j = b.begin(); j != b.end();) + { + if (distance_exp(m_id, j->id) >= 159 - bucket_index) + { + ++j; + continue; + } + // this entry belongs in the new bucket + new_bucket.push_back(*j); + j = b.erase(j); + } + + if (b.size() > bucket_size_limit) + { + // TODO: 3 move the lowest priority nodes to the replacement bucket + for (bucket_t::iterator i = b.begin() + bucket_size_limit + , end(b.end()); i != end; ++i) + { + rb.push_back(*i); + } + + b.resize(bucket_size_limit); + } + + // split the replacement bucket as well. If the live bucket + // is not full anymore, also move the replacement entries + // into the main bucket + for (bucket_t::iterator j = rb.begin(); j != rb.end();) + { + if (distance_exp(m_id, j->id) >= 159 - bucket_index) + { + if (int(b.size()) >= bucket_size_limit) + { + ++j; + continue; + } + b.push_back(*j); + } + else + { + // this entry belongs in the new bucket + if (int(new_bucket.size()) < new_bucket_size) + new_bucket.push_back(*j); + else + new_replacement_bucket.push_back(*j); + } + j = rb.erase(j); + } +} + +void routing_table::for_each_node( + void (*fun1)(void*, node_entry const&) + , void (*fun2)(void*, node_entry const&) + , void* userdata) const +{ + for (table_t::const_iterator i = m_buckets.begin() + , end(m_buckets.end()); i != end; ++i) + { + if (fun1) + { + for (bucket_t::const_iterator j = i->live_nodes.begin() + , end(i->live_nodes.end()); j != end; ++j) + fun1(userdata, *j); + } + if (fun2) + { + for (bucket_t::const_iterator j = i->replacements.begin() + , end(i->replacements.end()); j != end; ++j) + fun2(userdata, *j); + } + } +} + +void routing_table::node_failed(node_id const& nid, udp::endpoint const& ep) +{ + INVARIANT_CHECK; + + // if messages to ourself fails, ignore it + if (nid == m_id) return; + + table_t::iterator i = find_bucket(nid); + bucket_t& b = i->live_nodes; + bucket_t& rb = i->replacements; + + bucket_t::iterator j = std::find_if(b.begin(), b.end() + , boost::bind(&node_entry::id, _1) == nid); + + if (j == b.end()) + { + j = std::find_if(rb.begin(), rb.end() + , boost::bind(&node_entry::id, _1) == nid); + + if (j == rb.end() + || j->ep() != ep) return; + + j->timed_out(); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(table) << " NODE FAILED" + " id: " << nid << + " ip: " << j->ep() << + " fails: " << j->fail_count() << + " pinged: " << j->pinged() << + " up-time: " << total_seconds(time_now() - j->first_seen); +#endif + return; + } + + // if the endpoint doesn't match, it's a different node + // claiming the same ID. The node we have in our routing + // table is not necessarily stale + if (j->ep() != ep) return; + + if (rb.empty()) + { + j->timed_out(); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(table) << " NODE FAILED" + " id: " << nid << + " ip: " << j->ep() << + " fails: " << j->fail_count() << + " pinged: " << j->pinged() << + " up-time: " << total_seconds(time_now() - j->first_seen); +#endif + + // if this node has failed too many times, or if this node + // has never responded at all, remove it + if (j->fail_count() >= m_settings.max_fail_count || !j->pinged()) + { + erase_one(m_ips, j->addr().to_v4().to_bytes()); + b.erase(j); + } + return; + } + + erase_one(m_ips, j->a); + b.erase(j); + + // sort by RTT first, to find the node with the lowest + // RTT that is pinged + std::sort(rb.begin(), rb.end() + , boost::bind(&node_entry::rtt, _1) < boost::bind(&node_entry::rtt, _2)); + + j = std::find_if(rb.begin(), rb.end(), boost::bind(&node_entry::pinged, _1)); + if (j == rb.end()) j = rb.begin(); + b.push_back(*j); + rb.erase(j); +} + +void routing_table::add_router_node(udp::endpoint router) +{ + m_router_nodes.insert(router); +} + +// we heard from this node, but we don't know if it was spoofed or not (i.e. +// pinged == false) +void routing_table::heard_about(node_id const& id, udp::endpoint const& ep) +{ + add_node(node_entry(id, ep)); +} + +// this function is called every time the node sees a sign of a node being +// alive. This node will either be inserted in the k-buckets or be moved to the +// top of its bucket. the return value indicates if the table needs a refresh. +// if true, the node should refresh the table (i.e. do a find_node on its own +// id) +bool routing_table::node_seen(node_id const& id, udp::endpoint ep, int rtt) +{ + return add_node(node_entry(id, ep, rtt, true)); +} + +// fills the vector with the k nodes from our buckets that +// are nearest to the given id. +void routing_table::find_node(node_id const& target + , std::vector& l, int options, int count) +{ + l.clear(); + if (count == 0) count = m_bucket_size; + + table_t::iterator i = find_bucket(target); + int bucket_index = std::distance(m_buckets.begin(), i); + int bucket_size_limit = bucket_limit(bucket_index); + + l.reserve(bucket_size_limit); + + table_t::iterator j = i; + + int unsorted_start_idx = 0; + for (; j != m_buckets.end() && int(l.size()) < count; ++j) + { + bucket_t& b = j->live_nodes; + if (options & include_failed) + { + copy(b.begin(), b.end() + , std::back_inserter(l)); + } + else + { + std::remove_copy_if(b.begin(), b.end() + , std::back_inserter(l) + , !boost::bind(&node_entry::confirmed, _1)); + } + + if (int(l.size()) == count) return; + + if (int(l.size()) > count) + { + // sort the nodes by how close they are to the target + std::sort(l.begin() + unsorted_start_idx, l.end(), boost::bind(&compare_ref + , boost::bind(&node_entry::id, _1) + , boost::bind(&node_entry::id, _2), target)); + + l.resize(count); + return; + } + unsorted_start_idx = int(l.size()); + } + + // if we still don't have enough nodes, copy nodes + // further away from us + + if (i == m_buckets.begin()) + return; + + j = i; + + unsorted_start_idx = int(l.size()); + do + { + --j; + bucket_t& b = j->live_nodes; + + if (options & include_failed) + { + std::copy(b.begin(), b.end(), std::back_inserter(l)); + } + else + { + std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l) + , !boost::bind(&node_entry::confirmed, _1)); + } + + if (int(l.size()) == count) return; + + if (int(l.size()) > count) + { + // sort the nodes by how close they are to the target + std::sort(l.begin() + unsorted_start_idx, l.end(), boost::bind(&compare_ref + , boost::bind(&node_entry::id, _1) + , boost::bind(&node_entry::id, _2), target)); + + l.resize(count); + return; + } + unsorted_start_idx = int(l.size()); + } + while (j != m_buckets.begin() && int(l.size()) < count); + + TORRENT_ASSERT(int(l.size()) <= count); +} + +#if TORRENT_USE_INVARIANT_CHECKS +void routing_table::check_invariant() const +{ + std::multiset all_ips; + + for (table_t::const_iterator i = m_buckets.begin() + , end(m_buckets.end()); i != end; ++i) + { + for (bucket_t::const_iterator j = i->replacements.begin(); + j != i->replacements.end(); ++j) + { + all_ips.insert(j->addr().to_v4().to_bytes()); + } + for (bucket_t::const_iterator j = i->live_nodes.begin(); + j != i->live_nodes.end(); ++j) + { + all_ips.insert(j->addr().to_v4().to_bytes()); + } + } + + TORRENT_ASSERT(all_ips == m_ips); +} +#endif + +} } // namespace libtorrent::dht + diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/rpc_manager.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/rpc_manager.cpp new file mode 100644 index 0000000000..4ab8144b78 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/rpc_manager.cpp @@ -0,0 +1,522 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/socket.hpp" + +#include + +#include +#include +#include +#include // for generate_random_id +#include +#include +#include +#include +#include +#include +#include +#include +#include // for dht_settings +#include +#include // time() + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +#include +#endif + +namespace libtorrent { namespace dht +{ + +namespace io = libtorrent::detail; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DEFINE_LOG(rpc) +#endif + +void intrusive_ptr_add_ref(observer const* o) +{ + TORRENT_ASSERT(o != 0); + TORRENT_ASSERT(o->m_refs >= 0); + ++o->m_refs; +} + +void intrusive_ptr_release(observer const* o) +{ + TORRENT_ASSERT(o != 0); + TORRENT_ASSERT(o->m_refs > 0); + if (--o->m_refs == 0) + { + boost::intrusive_ptr ta = o->m_algorithm; + (const_cast(o))->~observer(); + ta->free_observer(const_cast(o)); + } +} + +void observer::set_target(udp::endpoint const& ep) +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + // use high resolution timers for logging + m_sent = time_now_hires(); +#else + m_sent = time_now(); +#endif + + m_port = ep.port(); +#if TORRENT_USE_IPV6 + if (ep.address().is_v6()) + { + flags |= flag_ipv6_address; + m_addr.v6 = ep.address().to_v6().to_bytes(); + } + else +#endif + { + flags &= ~flag_ipv6_address; + m_addr.v4 = ep.address().to_v4().to_bytes(); + } +} + +address observer::target_addr() const +{ +#if TORRENT_USE_IPV6 + if (flags & flag_ipv6_address) + return address_v6(m_addr.v6); + else +#endif + return address_v4(m_addr.v4); +} + +udp::endpoint observer::target_ep() const +{ + return udp::endpoint(target_addr(), m_port); +} + +void observer::abort() +{ + if (flags & flag_done) return; + flags |= flag_done; + m_algorithm->failed(observer_ptr(this), traversal_algorithm::prevent_request); +} + +void observer::done() +{ + if (flags & flag_done) return; + flags |= flag_done; + m_algorithm->finished(observer_ptr(this)); +} + +void observer::short_timeout() +{ + if (flags & flag_short_timeout) return; + m_algorithm->failed(observer_ptr(this), traversal_algorithm::short_timeout); +} + +// this is called when no reply has been received within +// some timeout +void observer::timeout() +{ + if (flags & flag_done) return; + flags |= flag_done; + m_algorithm->failed(observer_ptr(this)); +} + +void observer::set_id(node_id const& id) +{ + if (m_id == id) return; + m_id = id; + if (m_algorithm) m_algorithm->resort_results(); +} + +enum { observer_size = max3< + sizeof(find_data_observer) + , sizeof(announce_observer) + , sizeof(null_observer) + >::value +}; + +rpc_manager::rpc_manager(node_id const& our_id + , routing_table& table, udp_socket_interface* sock) + : m_pool_allocator(observer_size, 10) + , m_sock(sock) + , m_table(table) + , m_timer(time_now()) + , m_our_id(our_id) + , m_allocated_observers(0) + , m_destructing(false) +{ + std::srand((unsigned int)time(0)); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "Constructing"; + +#define PRINT_OFFSETOF(x, y) TORRENT_LOG(rpc) << " +" << offsetof(x, y) << ": " #y + + TORRENT_LOG(rpc) << " observer: " << sizeof(observer); + PRINT_OFFSETOF(dht::observer, m_sent); + PRINT_OFFSETOF(dht::observer, m_refs); + PRINT_OFFSETOF(dht::observer, m_algorithm); + PRINT_OFFSETOF(dht::observer, m_id); + PRINT_OFFSETOF(dht::observer, m_addr); + PRINT_OFFSETOF(dht::observer, m_port); + PRINT_OFFSETOF(dht::observer, m_transaction_id); + PRINT_OFFSETOF(dht::observer, flags); + + TORRENT_LOG(rpc) << " announce_observer: " << sizeof(announce_observer); + TORRENT_LOG(rpc) << " null_observer: " << sizeof(null_observer); + TORRENT_LOG(rpc) << " find_data_observer: " << sizeof(find_data_observer); + +#undef PRINT_OFFSETOF +#endif + +} + +rpc_manager::~rpc_manager() +{ + TORRENT_ASSERT(!m_destructing); + m_destructing = true; +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "Destructing"; +#endif + + for (transactions_t::iterator i = m_transactions.begin() + , end(m_transactions.end()); i != end; ++i) + { + (*i)->abort(); + } +} + +void* rpc_manager::allocate_observer() +{ + m_pool_allocator.set_next_size(10); + void* ret = m_pool_allocator.malloc(); + if (ret) ++m_allocated_observers; + return ret; +} + +void rpc_manager::free_observer(void* ptr) +{ + if (!ptr) return; + --m_allocated_observers; + TORRENT_ASSERT(reinterpret_cast(ptr)->m_in_use == false); + m_pool_allocator.free(ptr); +} + +#if TORRENT_USE_ASSERTS +size_t rpc_manager::allocation_size() const +{ + return observer_size; +} +#endif +#if TORRENT_USE_INVARIANT_CHECKS +void rpc_manager::check_invariant() const +{ + for (transactions_t::const_iterator i = m_transactions.begin() + , end(m_transactions.end()); i != end; ++i) + { + TORRENT_ASSERT(*i); + } +} +#endif + +void rpc_manager::unreachable(udp::endpoint const& ep) +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << time_now_string() << " PORT_UNREACHABLE [ ip: " << ep << " ]"; +#endif + + for (transactions_t::iterator i = m_transactions.begin(); + i != m_transactions.end();) + { + TORRENT_ASSERT(*i); + observer_ptr const& o = *i; + if (o->target_ep() != ep) { ++i; continue; } + observer_ptr ptr = *i; + i = m_transactions.erase(i); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << " found transaction [ tid: " << ptr->transaction_id() << " ]"; +#endif + ptr->timeout(); + break; + } +} + +// defined in node.cpp +void incoming_error(entry& e, char const* msg, int error_code = 203); + +bool rpc_manager::incoming(msg const& m, node_id* id, libtorrent::dht_settings const& settings) +{ + INVARIANT_CHECK; + + if (m_destructing) return false; + + // we only deal with replies and errors, not queries + TORRENT_ASSERT(m.message.dict_find_string_value("y") == "r" + || m.message.dict_find_string_value("y") == "e"); + + // if we don't have the transaction id in our + // request list, ignore the packet + + std::string transaction_id = m.message.dict_find_string_value("t"); + if (transaction_id.empty()) return false; + + std::string::const_iterator i = transaction_id.begin(); + int tid = transaction_id.size() != 2 ? -1 : io::read_uint16(i); + + observer_ptr o; + + for (transactions_t::iterator i = m_transactions.begin() + , end(m_transactions.end()); i != end;) + { + TORRENT_ASSERT(*i); + if ((*i)->transaction_id() != tid + || m.addr.address() != (*i)->target_addr()) + { + ++i; + continue; + } + o = *i; + i = m_transactions.erase(i); + break; + } + + if (!o) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "Reply with unknown transaction id size: " + << transaction_id.size() << " from " << m.addr; +#endif + // this isn't necessarily because the other end is doing + // something wrong. This can also happen when we restart + // the node, and we prematurely abort all outstanding + // requests. Also, this opens up a potential magnification + // attack. +// entry e; +// incoming_error(e, "invalid transaction id"); +// m_sock->send_packet(e, m.addr, 0); + return false; + } + + ptime now = time_now_hires(); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + std::ofstream reply_stats("round_trip_ms.log", std::ios::app); + reply_stats << m.addr << "\t" << total_milliseconds(now - o->sent()) + << std::endl; +#endif + + lazy_entry const* ret_ent = m.message.dict_find_dict("r"); + if (ret_ent == 0) + { + // it may be an error + ret_ent = m.message.dict_find("e"); + o->timeout(); + if (ret_ent == NULL) + { + entry e; + incoming_error(e, "missing 'r' key"); + m_sock->send_packet(e, m.addr, 0); + } + return false; + } + + lazy_entry const* node_id_ent = ret_ent->dict_find_string("id"); + if (node_id_ent == 0 || node_id_ent->string_length() != 20) + { + o->timeout(); + entry e; + incoming_error(e, "missing 'id' key"); + m_sock->send_packet(e, m.addr, 0); + return false; + } + + node_id nid = node_id(node_id_ent->string_ptr()); + if (settings.enforce_node_id && !verify_id(nid, m.addr.address())) + { + o->timeout(); + entry e; + incoming_error(e, "invalid node ID"); + m_sock->send_packet(e, m.addr, 0); + return false; + } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "[" << o->m_algorithm.get() << "] Reply with transaction id: " + << tid << " from " << m.addr; +#endif + o->reply(m); + *id = nid; + + int rtt = int(total_milliseconds(now - o->sent())); + + // we found an observer for this reply, hence the node is not spoofing + // add it to the routing table + return m_table.node_seen(*id, m.addr, rtt); +} + +time_duration rpc_manager::tick() +{ + INVARIANT_CHECK; + + const static int short_timeout = 1; + const static int timeout = 15; + + // look for observers that have timed out + + if (m_transactions.empty()) return seconds(short_timeout); + + std::list timeouts; + + time_duration ret = seconds(short_timeout); + ptime now = time_now(); + +#if TORRENT_USE_ASSERTS + ptime last = min_time(); + for (transactions_t::iterator i = m_transactions.begin(); + i != m_transactions.end(); ++i) + { + TORRENT_ASSERT((*i)->sent() >= last); + last = (*i)->sent(); + } +#endif + + for (transactions_t::iterator i = m_transactions.begin(); + i != m_transactions.end();) + { + observer_ptr o = *i; + + // if we reach an observer that hasn't timed out + // break, because every observer after this one will + // also not have timed out yet + time_duration diff = now - o->sent(); + if (diff < seconds(timeout)) + { + ret = seconds(timeout) - diff; + break; + } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "[" << o->m_algorithm.get() << "] Timing out transaction id: " + << (*i)->transaction_id() << " from " << o->target_ep(); +#endif + i = m_transactions.erase(i); + timeouts.push_back(o); + } + + std::for_each(timeouts.begin(), timeouts.end(), boost::bind(&observer::timeout, _1)); + timeouts.clear(); + + for (transactions_t::iterator i = m_transactions.begin(); + i != m_transactions.end(); ++i) + { + observer_ptr o = *i; + + // if we reach an observer that hasn't timed out + // break, because every observer after this one will + // also not have timed out yet + time_duration diff = now - o->sent(); + if (diff < seconds(short_timeout)) + { + ret = seconds(short_timeout) - diff; + break; + } + + // don't call short_timeout() again if we've + // already called it once + if (o->has_short_timeout()) continue; + + timeouts.push_back(o); + } + + std::for_each(timeouts.begin(), timeouts.end(), boost::bind(&observer::short_timeout, _1)); + + return ret; +} + +void rpc_manager::add_our_id(entry& e) +{ + e["id"] = m_our_id.to_string(); +} + +bool rpc_manager::invoke(entry& e, udp::endpoint target_addr + , observer_ptr o) +{ + INVARIANT_CHECK; + + if (m_destructing) return false; + + e["y"] = "q"; + entry& a = e["a"]; + add_our_id(a); + + std::string transaction_id; + transaction_id.resize(2); + char* out = &transaction_id[0]; + int tid = (random() ^ (random() << 5)) & 0xffff; + io::write_uint16(tid, out); + e["t"] = transaction_id; + + o->set_target(target_addr); + o->set_transaction_id(tid); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "[" << o->m_algorithm.get() << "] invoking " + << e["q"].string() << " -> " << target_addr; +#endif + + if (m_sock->send_packet(e, target_addr, 1)) + { + m_transactions.push_back(o); +#if TORRENT_USE_ASSERTS + o->m_was_sent = true; +#endif + return true; + } + return false; +} + +observer::~observer() +{ + // if the message was sent, it must have been + // reported back to the traversal_algorithm as + // well. If it wasn't sent, it cannot have been + // reported back + TORRENT_ASSERT(m_was_sent == bool(flags & flag_done) || m_was_abandoned); + TORRENT_ASSERT(!m_in_constructor); +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_in_use); + m_in_use = false; +#endif +} + +} } // namespace libtorrent::dht + diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/traversal_algorithm.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/traversal_algorithm.cpp new file mode 100644 index 0000000000..10e65a12c6 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/traversal_algorithm.cpp @@ -0,0 +1,593 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/time.hpp" // for total_seconds + +#include +#include +#include +#include +#include +#include "libtorrent/broadcast_socket.hpp" // for cidr_distance +#include // for read_*_endpoint + +#include + +namespace libtorrent { namespace dht +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DEFINE_LOG(traversal) +#endif + +using detail::read_v4_endpoint; +#if TORRENT_USE_IPV6 +using detail::read_v6_endpoint; +#endif + +#if TORRENT_USE_ASSERTS +template +bool is_sorted(It b, It e, Cmp cmp) +{ + if (b == e) return true; + + typename std::iterator_traits::value_type v = *b; + ++b; + while (b != e) + { + if (cmp(*b, v)) return false; + v = *b; + ++b; + } + return true; +} +#endif + +observer_ptr traversal_algorithm::new_observer(void* ptr + , udp::endpoint const& ep, node_id const& id) +{ + observer_ptr o(new (ptr) null_observer(boost::intrusive_ptr(this), ep, id)); +#if TORRENT_USE_ASSERTS + o->m_in_constructor = false; +#endif + return o; +} + +traversal_algorithm::traversal_algorithm( + node_impl& node + , node_id target) + : m_ref_count(0) + , m_node(node) + , m_target(target) + , m_invoke_count(0) + , m_branch_factor(3) + , m_responses(0) + , m_timeouts(0) + , m_num_target_nodes(m_node.m_table.bucket_size()) +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] NEW" + " target: " << target + << " k: " << m_node.m_table.bucket_size() + ; +#endif +} + +// returns true of lhs and rhs are too close to each other to appear +// in the same DHT search under different node IDs +bool compare_ip_cidr(observer_ptr const& lhs, observer_ptr const& rhs) +{ + if (lhs->target_addr().is_v4() != rhs->target_addr().is_v4()) + return false; + // the number of bits in the IPs that may match. If + // more bits that this matches, something suspicious is + // going on and we shouldn't add the second one to our + // routing table + int cutoff = rhs->target_addr().is_v4() ? 4 : 64; + int dist = cidr_distance(lhs->target_addr(), rhs->target_addr()); + return dist <= cutoff; +} + +void traversal_algorithm::resort_results() +{ + std::sort( + m_results.begin() + , m_results.end() + , boost::bind( + compare_ref + , boost::bind(&observer::id, _1) + , boost::bind(&observer::id, _2) + , m_target + ) + ); +} + +void traversal_algorithm::add_entry(node_id const& id, udp::endpoint addr, unsigned char flags) +{ + TORRENT_ASSERT(m_node.m_rpc.allocation_size() >= sizeof(find_data_observer)); + void* ptr = m_node.m_rpc.allocate_observer(); + if (ptr == 0) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] failed to allocate memory for observer. aborting!"; +#endif + done(); + return; + } + observer_ptr o = new_observer(ptr, addr, id); + if (id.is_all_zeros()) + { + o->set_id(generate_random_id()); + o->flags |= observer::flag_no_id; + } + + o->flags |= flags; + + TORRENT_ASSERT(libtorrent::dht::is_sorted(m_results.begin(), m_results.end() + , boost::bind( + compare_ref + , boost::bind(&observer::id, _1) + , boost::bind(&observer::id, _2) + , m_target) + )); + + std::vector::iterator i = std::lower_bound( + m_results.begin() + , m_results.end() + , o + , boost::bind( + compare_ref + , boost::bind(&observer::id, _1) + , boost::bind(&observer::id, _2) + , m_target + ) + ); + + if (i == m_results.end() || (*i)->id() != id) + { + if (m_node.settings().restrict_search_ips + && !(flags & observer::flag_initial)) + { + // don't allow multiple entries from IPs very close to each other + std::vector::iterator j = std::find_if( + m_results.begin(), m_results.end(), boost::bind(&compare_ip_cidr, _1, o)); + + if (j != m_results.end()) + { + // we already have a node in this search with an IP very + // close to this one. We know that it's not the same, because + // it claims a different node-ID. Ignore this to avoid attacks +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] IGNORING result " + << "id: " << o->id() + << " address: " << o->target_addr() + << " existing node: " + << (*j)->id() << " " << (*j)->target_addr() + << " distance: " << distance_exp(m_target, o->id()) + << " type: " << name() + ; +#endif + return; + } + } + + TORRENT_ASSERT((o->flags & observer::flag_no_id) || std::find_if(m_results.begin(), m_results.end() + , boost::bind(&observer::id, _1) == id) == m_results.end()); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] ADD id: " << id + << " address: " << addr + << " distance: " << distance_exp(m_target, id) + << " invoke-count: " << m_invoke_count + << " type: " << name() + ; +#endif + i = m_results.insert(i, o); + + TORRENT_ASSERT(libtorrent::dht::is_sorted(m_results.begin(), m_results.end() + , boost::bind( + compare_ref + , boost::bind(&observer::id, _1) + , boost::bind(&observer::id, _2) + , m_target) + )); + } + + if (m_results.size() > 100) + { +#if TORRENT_USE_ASSERTS + for (int i = 100; i < int(m_results.size()); ++i) + m_results[i]->m_was_abandoned = true; +#endif + m_results.resize(100); + } +} + +void traversal_algorithm::start() +{ + // in case the routing table is empty, use the + // router nodes in the table + if (m_results.size() < 3) add_router_entries(); + init(); + bool is_done = add_requests(); + if (is_done) done(); +} + +void* traversal_algorithm::allocate_observer() +{ + return m_node.m_rpc.allocate_observer(); +} + +void traversal_algorithm::free_observer(void* ptr) +{ + m_node.m_rpc.free_observer(ptr); +} + +char const* traversal_algorithm::name() const +{ + return "traversal_algorithm"; +} + +void traversal_algorithm::traverse(node_id const& id, udp::endpoint addr) +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + if (id.is_all_zeros()) + { + TORRENT_LOG(traversal) << time_now_string() << "[" << this << "] WARNING node returned a list which included a node with id 0"; + } +#endif + + // let the routing table know this node may exist + m_node.m_table.heard_about(id, addr); + + add_entry(id, addr, 0); +} + +void traversal_algorithm::finished(observer_ptr o) +{ +#ifdef TORRENT_DEBUG + std::vector::iterator i = std::find( + m_results.begin(), m_results.end(), o); + + TORRENT_ASSERT(i != m_results.end() || m_results.size() == 100); +#endif + + // if this flag is set, it means we increased the + // branch factor for it, and we should restore it + if (o->flags & observer::flag_short_timeout) + { + TORRENT_ASSERT(m_branch_factor > 0); + --m_branch_factor; + } + + TORRENT_ASSERT(o->flags & observer::flag_queried); + o->flags |= observer::flag_alive; + + ++m_responses; + --m_invoke_count; + TORRENT_ASSERT(m_invoke_count >= 0); + bool is_done = add_requests(); + if (is_done) done(); +} + +// prevent request means that the total number of requests has +// overflown. This query failed because it was the oldest one. +// So, if this is true, don't make another request +void traversal_algorithm::failed(observer_ptr o, int flags) +{ + TORRENT_ASSERT(m_invoke_count >= 0); + + // don't tell the routing table about + // node ids that we just generated ourself + if ((o->flags & observer::flag_no_id) == 0) + m_node.m_table.node_failed(o->id(), o->target_ep()); + + if (m_results.empty()) return; + + TORRENT_ASSERT(o->flags & observer::flag_queried); + if (flags & short_timeout) + { + // short timeout means that it has been more than + // two seconds since we sent the request, and that + // we'll most likely not get a response. But, in case + // we do get a late response, keep the handler + // around for some more, but open up the slot + // by increasing the branch factor + if ((o->flags & observer::flag_short_timeout) == 0) + ++m_branch_factor; + o->flags |= observer::flag_short_timeout; +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] 1ST_TIMEOUT " + << " id: " << o->id() + << " distance: " << distance_exp(m_target, o->id()) + << " addr: " << o->target_ep() + << " branch-factor: " << m_branch_factor + << " invoke-count: " << m_invoke_count + << " type: " << name() + ; +#endif + } + else + { + o->flags |= observer::flag_failed; + // if this flag is set, it means we increased the + // branch factor for it, and we should restore it + if (o->flags & observer::flag_short_timeout) + --m_branch_factor; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] TIMEOUT " + << " id: " << o->id() + << " distance: " << distance_exp(m_target, o->id()) + << " addr: " << o->target_ep() + << " branch-factor: " << m_branch_factor + << " invoke-count: " << m_invoke_count + << " type: " << name() + ; +#endif + + ++m_timeouts; + --m_invoke_count; + TORRENT_ASSERT(m_invoke_count >= 0); + } + + if (flags & prevent_request) + { + --m_branch_factor; + if (m_branch_factor <= 0) m_branch_factor = 1; + } + bool is_done = add_requests(); + if (is_done) done(); +} + +void traversal_algorithm::done() +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + int results_target = m_num_target_nodes; + int closest_target = 160; + + for (std::vector::iterator i = m_results.begin() + , end(m_results.end()); i != end && results_target > 0; ++i) + { + boost::intrusive_ptr o = *i; + if (o->flags & observer::flag_alive) + { + TORRENT_ASSERT(o->flags & observer::flag_queried); + TORRENT_LOG(traversal) << "[" << this << "] " + << results_target + << " id: " << o->id() + << " distance: " << distance_exp(m_target, o->id()) + << " address: " << o->target_ep() + ; + --results_target; + int dist = distance_exp(m_target, o->id()); + if (dist < closest_target) closest_target = dist; + } + } + + TORRENT_LOG(traversal) << "[" << this << "] COMPLETED " + << "distance: " << closest_target + << " type: " << name() + ; + +#endif + // delete all our references to the observer objects so + // they will in turn release the traversal algorithm + m_results.clear(); +} + +bool traversal_algorithm::add_requests() +{ + int results_target = m_num_target_nodes; + + // this only counts outstanding requests at the top of the + // target list. This is <= m_invoke count. m_invoke_count + // is the total number of outstanding requests, including + // old ones that may be waiting on nodes much farther behind + // the current point we've reached in the search. + int outstanding = 0; + + // if we're doing aggressive lookups, we keep branch-factor + // outstanding requests _at the tops_ of the result list. Otherwise + // we just keep any branch-factor outstanding requests + bool agg = m_node.settings().aggressive_lookups; + + // Find the first node that hasn't already been queried. + // and make sure that the 'm_branch_factor' top nodes + // stay queried at all times (obviously ignoring failed nodes) + // and without surpassing the 'result_target' nodes (i.e. k=8) + // this is a slight variation of the original paper which instead + // limits the number of outstanding requests, this limits the + // number of good outstanding requests. It will use more traffic, + // but is intended to speed up lookups + for (std::vector::iterator i = m_results.begin() + , end(m_results.end()); i != end + && results_target > 0 + && (agg ? outstanding < m_branch_factor + : m_invoke_count < m_branch_factor); + ++i) + { + observer* o = i->get(); + if (o->flags & observer::flag_alive) + { + TORRENT_ASSERT(o->flags & observer::flag_queried); + --results_target; + continue; + } + if (o->flags & observer::flag_queried) + { + // if it's queried, not alive and not failed, it + // must be currently in flight + if ((o->flags & observer::flag_failed) == 0) + ++outstanding; + + continue; + } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] INVOKE " + << " nodes-left: " << (m_results.end() - i) + << " top-invoke-count: " << outstanding + << " invoke-count: " << m_invoke_count + << " branch-factor: " << m_branch_factor + << " distance: " << distance_exp(m_target, (*i)->id()) + << " type: " << name() + ; +#endif + + o->flags |= observer::flag_queried; + if (invoke(*i)) + { + TORRENT_ASSERT(m_invoke_count >= 0); + ++m_invoke_count; + ++outstanding; + } + else + { + o->flags |= observer::flag_failed; + } + } + + // this is the completion condition. If we found m_num_target_nodes + // (i.e. k=8) completed results, without finding any still + // outstanding requests, we're done. + // also, if invoke count is 0, it means we didn't even find 'k' + // working nodes, we still have to terminate though. + return (results_target == 0 && outstanding == 0) || m_invoke_count == 0; +} + +void traversal_algorithm::add_router_entries() +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] using router nodes to initiate traversal algorithm. " + << std::distance(m_node.m_table.router_begin(), m_node.m_table.router_end()) << " routers"; +#endif + for (routing_table::router_iterator i = m_node.m_table.router_begin() + , end(m_node.m_table.router_end()); i != end; ++i) + { + add_entry(node_id(0), *i, observer::flag_initial); + } +} + +void traversal_algorithm::init() +{ + m_branch_factor = m_node.branch_factor(); + m_node.add_traversal_algorithm(this); +} + +traversal_algorithm::~traversal_algorithm() +{ + m_node.remove_traversal_algorithm(this); +} + +void traversal_algorithm::status(dht_lookup& l) +{ + l.timeouts = m_timeouts; + l.responses = m_responses; + l.outstanding_requests = m_invoke_count; + l.branch_factor = m_branch_factor; + l.type = name(); + l.nodes_left = 0; + l.first_timeout = 0; + + int last_sent = INT_MAX; + ptime now = time_now(); + for (std::vector::iterator i = m_results.begin() + , end(m_results.end()); i != end; ++i) + { + observer& o = **i; + if (o.flags & observer::flag_queried) + { + last_sent = (std::min)(last_sent, int(total_seconds(now - o.sent()))); + if (o.has_short_timeout()) ++l.first_timeout; + continue; + } + ++l.nodes_left; + } + l.last_sent = last_sent; +} + +void traversal_observer::reply(msg const& m) +{ + lazy_entry const* r = m.message.dict_find_dict("r"); + if (!r) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << m_algorithm.get() + << "] missing response dict"; +#endif + return; + } + + // look for nodes + lazy_entry const* n = r->dict_find_string("nodes"); + if (n) + { + char const* nodes = n->string_ptr(); + char const* end = nodes + n->string_length(); + + while (end - nodes >= 26) + { + node_id id; + std::copy(nodes, nodes + 20, id.begin()); + nodes += 20; + m_algorithm->traverse(id, read_v4_endpoint(nodes)); + } + } + + lazy_entry const* id = r->dict_find_string("id"); + if (!id || id->string_length() != 20) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << m_algorithm.get() << "] invalid id in response"; +#endif + return; + } + + // in case we didn't know the id of this peer when we sent the message to + // it. For instance if it's a bootstrap node. + set_id(node_id(id->string_ptr())); +} + +void traversal_algorithm::abort() +{ + m_num_target_nodes = 0; + for (std::vector::iterator i = m_results.begin() + , end(m_results.end()); i != end; ++i) + { + observer& o = **i; + if (o.flags & observer::flag_queried) + o.flags |= observer::flag_done; + } + done(); +} + +} } // namespace libtorrent::dht + diff --git a/apps/Launcher/ext/libtorrent/src/lazy_bdecode.cpp b/apps/Launcher/ext/libtorrent/src/lazy_bdecode.cpp new file mode 100644 index 0000000000..3a468fc3ca --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/lazy_bdecode.cpp @@ -0,0 +1,706 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" +#include "libtorrent/lazy_entry.hpp" +#include +#include // for numeric_limits + +namespace +{ + const int lazy_entry_grow_factor = 150; // percent + const int lazy_entry_dict_init = 5; + const int lazy_entry_list_init = 5; +} + +namespace libtorrent +{ + + namespace + { + int fail(int* error_pos + , std::vector& stack + , char const* start + , char const* orig_start) + { + while (!stack.empty()) { + lazy_entry* top = stack.back(); + if (top->type() == lazy_entry::dict_t || top->type() == lazy_entry::list_t) + { + top->pop(); + break; + } + stack.pop_back(); + } + if (error_pos) *error_pos = start - orig_start; + return -1; + } + } + +#define TORRENT_FAIL_BDECODE(code) do { ec = make_error_code(code); return fail(error_pos, stack, start, orig_start); } while (false) + + namespace { bool numeric(char c) { return c >= '0' && c <= '9'; } } + + // fills in 'val' with what the string between start and the + // first occurance of the delimiter is interpreted as an int. + // return the pointer to the delimiter, or 0 if there is a + // parse error. val should be initialized to zero + char const* parse_int(char const* start, char const* end, char delimiter + , boost::int64_t& val, bdecode_errors::error_code_enum& ec) + { + while (start < end && *start != delimiter) + { + if (!numeric(*start)) + { + ec = bdecode_errors::expected_string; + return start; + } + if (val > (std::numeric_limits::max)() / 10) + { + ec = bdecode_errors::overflow; + return start; + } + val *= 10; + int digit = *start - '0'; + if (val > (std::numeric_limits::max)() - digit) + { + ec = bdecode_errors::overflow; + return start; + } + val += digit; + ++start; + } + if (*start != delimiter) + ec = bdecode_errors::expected_colon; + return start; + } + + char const* find_char(char const* start, char const* end, char delimiter) + { + while (start < end && *start != delimiter) ++start; + return start; + } + +#ifndef TORRENT_NO_DEPRECATE + int lazy_bdecode(char const* start, char const* end + , lazy_entry& ret, int depth_limit, int item_limit) + { + error_code ec; + int pos; + return lazy_bdecode(start, end, ret, ec, &pos, depth_limit, item_limit); + } +#endif + + // return 0 = success + int lazy_bdecode(char const* start, char const* end, lazy_entry& ret + , error_code& ec, int* error_pos, int depth_limit, int item_limit) + { + char const* const orig_start = start; + ret.clear(); + if (start == end) return 0; + + std::vector stack; + + stack.push_back(&ret); + while (start <= end) + { + if (stack.empty()) break; // done! + + lazy_entry* top = stack.back(); + + if (int(stack.size()) > depth_limit) TORRENT_FAIL_BDECODE(bdecode_errors::depth_exceeded); + if (start >= end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); + char t = *start; + ++start; + if (start >= end && t != 'e') TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); + + switch (top->type()) + { + case lazy_entry::dict_t: + { + if (t == 'e') + { + top->set_end(start); + stack.pop_back(); + continue; + } + if (!numeric(t)) TORRENT_FAIL_BDECODE(bdecode_errors::expected_string); + boost::int64_t len = t - '0'; + bdecode_errors::error_code_enum e = bdecode_errors::no_error; + start = parse_int(start, end, ':', len, e); + if (e) + TORRENT_FAIL_BDECODE(e); + + if (start + len + 1 > end) + TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); + + if (len < 0) + TORRENT_FAIL_BDECODE(bdecode_errors::overflow); + + ++start; + if (start == end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); + lazy_entry* ent = top->dict_append(start); + if (ent == 0) TORRENT_FAIL_BDECODE(boost::system::errc::not_enough_memory); + start += len; + if (start >= end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); + stack.push_back(ent); + t = *start; + ++start; + break; + } + case lazy_entry::list_t: + { + if (t == 'e') + { + top->set_end(start); + stack.pop_back(); + continue; + } + lazy_entry* ent = top->list_append(); + if (ent == 0) TORRENT_FAIL_BDECODE(boost::system::errc::not_enough_memory); + stack.push_back(ent); + break; + } + default: break; + } + + --item_limit; + if (item_limit <= 0) TORRENT_FAIL_BDECODE(bdecode_errors::limit_exceeded); + + top = stack.back(); + switch (t) + { + case 'd': + top->construct_dict(start - 1); + continue; + case 'l': + top->construct_list(start - 1); + continue; + case 'i': + { + char const* int_start = start; + start = find_char(start, end, 'e'); + top->construct_int(int_start, start - int_start); + if (start == end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); + TORRENT_ASSERT(*start == 'e'); + ++start; + stack.pop_back(); + continue; + } + default: + { + if (!numeric(t)) + TORRENT_FAIL_BDECODE(bdecode_errors::expected_value); + + boost::int64_t len = t - '0'; + bdecode_errors::error_code_enum e = bdecode_errors::no_error; + start = parse_int(start, end, ':', len, e); + if (e) + TORRENT_FAIL_BDECODE(e); + if (start + len + 1 > end) + TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); + if (len < 0) + TORRENT_FAIL_BDECODE(bdecode_errors::overflow); + + ++start; + top->construct_string(start, int(len)); + stack.pop_back(); + start += len; + continue; + } + } + return 0; + } + return 0; + } + + boost::int64_t lazy_entry::int_value() const + { + TORRENT_ASSERT(m_type == int_t); + boost::int64_t val = 0; + bool negative = false; + if (*m_data.start == '-') negative = true; + bdecode_errors::error_code_enum ec = bdecode_errors::no_error; + parse_int(m_data.start + negative + , m_data.start + m_size, 'e', val, ec); + if (ec) return 0; + if (negative) val = -val; + return val; + } + + lazy_entry* lazy_entry::dict_append(char const* name) + { + TORRENT_ASSERT(m_type == dict_t); + TORRENT_ASSERT(m_size <= m_capacity); + if (m_capacity == 0) + { + int capacity = lazy_entry_dict_init; + m_data.dict = new (std::nothrow) lazy_dict_entry[capacity]; + if (m_data.dict == 0) return 0; + m_capacity = capacity; + } + else if (m_size == m_capacity) + { + int capacity = m_capacity * lazy_entry_grow_factor / 100; + lazy_dict_entry* tmp = new (std::nothrow) lazy_dict_entry[capacity]; + if (tmp == 0) return 0; + std::memcpy(tmp, m_data.dict, sizeof(lazy_dict_entry) * m_size); + for (int i = 0; i < int(m_size); ++i) m_data.dict[i].val.release(); + delete[] m_data.dict; + m_data.dict = tmp; + m_capacity = capacity; + } + + TORRENT_ASSERT(m_size < m_capacity); + lazy_dict_entry& ret = m_data.dict[m_size++]; + ret.name = name; + return &ret.val; + } + + void lazy_entry::pop() + { + if (m_size > 0) --m_size; + } + + namespace + { + // the number of decimal digits needed + // to represent the given value + int num_digits(int val) + { + int ret = 1; + while (val >= 10) + { + ++ret; + val /= 10; + } + return ret; + } + } + + void lazy_entry::construct_string(char const* start, int length) + { + TORRENT_ASSERT(m_type == none_t); + m_type = string_t; + m_data.start = start; + m_size = length; + m_begin = start - 1 - num_digits(length); + m_len = start - m_begin + length; + } + + namespace + { + // str1 is null-terminated + // str2 is not, str2 is len2 chars + bool string_equal(char const* str1, char const* str2, int len2) + { + while (len2 > 0) + { + if (*str1 != *str2) return false; + if (*str1 == 0) return false; + ++str1; + ++str2; + --len2; + } + return *str1 == 0; + } + } + + std::pair lazy_entry::dict_at(int i) const + { + TORRENT_ASSERT(m_type == dict_t); + TORRENT_ASSERT(i < int(m_size)); + lazy_dict_entry const& e = m_data.dict[i]; + return std::make_pair(std::string(e.name, e.val.m_begin - e.name), &e.val); + } + + std::string lazy_entry::dict_find_string_value(char const* name) const + { + lazy_entry const* e = dict_find(name); + if (e == 0 || e->type() != lazy_entry::string_t) return std::string(); + return e->string_value(); + } + + pascal_string lazy_entry::dict_find_pstr(char const* name) const + { + lazy_entry const* e = dict_find(name); + if (e == 0 || e->type() != lazy_entry::string_t) return pascal_string(0, 0); + return e->string_pstr(); + } + + lazy_entry const* lazy_entry::dict_find_string(char const* name) const + { + lazy_entry const* e = dict_find(name); + if (e == 0 || e->type() != lazy_entry::string_t) return 0; + return e; + } + + lazy_entry const* lazy_entry::dict_find_int(char const* name) const + { + lazy_entry const* e = dict_find(name); + if (e == 0 || e->type() != lazy_entry::int_t) return 0; + return e; + } + + boost::int64_t lazy_entry::dict_find_int_value(char const* name, boost::int64_t default_val) const + { + lazy_entry const* e = dict_find(name); + if (e == 0 || e->type() != lazy_entry::int_t) return default_val; + return e->int_value(); + } + + lazy_entry const* lazy_entry::dict_find_dict(char const* name) const + { + lazy_entry const* e = dict_find(name); + if (e == 0 || e->type() != lazy_entry::dict_t) return 0; + return e; + } + + lazy_entry const* lazy_entry::dict_find_dict(std::string const& name) const + { + lazy_entry const* e = dict_find(name); + if (e == 0 || e->type() != lazy_entry::dict_t) return 0; + return e; + } + + lazy_entry const* lazy_entry::dict_find_list(char const* name) const + { + lazy_entry const* e = dict_find(name); + if (e == 0 || e->type() != lazy_entry::list_t) return 0; + return e; + } + + lazy_entry* lazy_entry::dict_find(char const* name) + { + TORRENT_ASSERT(m_type == dict_t); + for (int i = 0; i < int(m_size); ++i) + { + lazy_dict_entry& e = m_data.dict[i]; + if (string_equal(name, e.name, e.val.m_begin - e.name)) + return &e.val; + } + return 0; + } + + lazy_entry* lazy_entry::dict_find(std::string const& name) + { + TORRENT_ASSERT(m_type == dict_t); + for (int i = 0; i < int(m_size); ++i) + { + lazy_dict_entry& e = m_data.dict[i]; + if (name.size() != e.val.m_begin - e.name) continue; + if (std::equal(name.begin(), name.end(), e.name)) + return &e.val; + } + return 0; + } + + lazy_entry* lazy_entry::list_append() + { + TORRENT_ASSERT(m_type == list_t); + TORRENT_ASSERT(m_size <= m_capacity); + if (m_capacity == 0) + { + int capacity = lazy_entry_list_init; + m_data.list = new (std::nothrow) lazy_entry[capacity]; + if (m_data.list == 0) return 0; + m_capacity = capacity; + } + else if (m_size == m_capacity) + { + int capacity = m_capacity * lazy_entry_grow_factor / 100; + lazy_entry* tmp = new (std::nothrow) lazy_entry[capacity]; + if (tmp == 0) return 0; + std::memcpy(tmp, m_data.list, sizeof(lazy_entry) * m_size); + for (int i = 0; i < int(m_size); ++i) m_data.list[i].release(); + delete[] m_data.list; + m_data.list = tmp; + m_capacity = capacity; + } + + TORRENT_ASSERT(m_size < m_capacity); + return m_data.list + (m_size++); + } + + std::string lazy_entry::list_string_value_at(int i) const + { + lazy_entry const* e = list_at(i); + if (e == 0 || e->type() != lazy_entry::string_t) return std::string(); + return e->string_value(); + } + + pascal_string lazy_entry::list_pstr_at(int i) const + { + lazy_entry const* e = list_at(i); + if (e == 0 || e->type() != lazy_entry::string_t) return pascal_string(0, 0); + return e->string_pstr(); + } + + boost::int64_t lazy_entry::list_int_value_at(int i, boost::int64_t default_val) const + { + lazy_entry const* e = list_at(i); + if (e == 0 || e->type() != lazy_entry::int_t) return default_val; + return e->int_value(); + } + + void lazy_entry::clear() + { + switch (m_type) + { + case list_t: delete[] m_data.list; break; + case dict_t: delete[] m_data.dict; break; + default: break; + } + m_data.start = 0; + m_size = 0; + m_capacity = 0; + m_type = none_t; + } + + std::pair lazy_entry::data_section() const + { + typedef std::pair return_t; + return return_t(m_begin, m_len); + } + + int line_longer_than(lazy_entry const& e, int limit) + { + int line_len = 0; + switch (e.type()) + { + case lazy_entry::list_t: + line_len += 4; + if (line_len > limit) return -1; + for (int i = 0; i < e.list_size(); ++i) + { + int ret = line_longer_than(*e.list_at(i), limit - line_len); + if (ret == -1) return -1; + line_len += ret + 2; + } + break; + case lazy_entry::dict_t: + line_len += 4; + if (line_len > limit) return -1; + for (int i = 0; i < e.dict_size(); ++i) + { + line_len += 4 + e.dict_at(i).first.size(); + if (line_len > limit) return -1; + int ret = line_longer_than(*e.dict_at(i).second, limit - line_len); + if (ret == -1) return -1; + line_len += ret + 1; + } + break; + case lazy_entry::string_t: + line_len += 3 + e.string_length(); + break; + case lazy_entry::int_t: + { + boost::int64_t val = e.int_value(); + while (val > 0) + { + ++line_len; + val /= 10; + } + line_len += 2; + } + break; + case lazy_entry::none_t: + line_len += 4; + break; + } + + if (line_len > limit) return -1; + return line_len; + } + + void escape_string(std::string& ret, char const* str, int len) + { + for (int i = 0; i < len; ++i) + { + if (str[i] >= 32 && str[i] < 127) + { + ret += str[i]; + } + else + { + char tmp[5]; + snprintf(tmp, sizeof(tmp), "\\x%02x", (unsigned char)str[i]); + ret += tmp; + } + } + } + + void print_string(std::string& ret, char const* str, int len, bool single_line) + { + bool printable = true; + for (int i = 0; i < len; ++i) + { + char c = str[i]; + if (c >= 32 && c < 127) continue; + printable = false; + break; + } + ret += "'"; + if (printable) + { + if (single_line && len > 30) + { + ret.append(str, 14); + ret += "..."; + ret.append(str + len-14, 14); + } + else + ret.append(str, len); + ret += "'"; + return; + } + if (single_line && len > 20) + { + escape_string(ret, str, 9); + ret += "..."; + escape_string(ret, str + len - 9, 9); + } + else + { + escape_string(ret, str, len); + } + ret += "'"; + } + + std::string print_entry(lazy_entry const& e, bool single_line, int indent) + { + char indent_str[200]; + memset(indent_str, ' ', 200); + indent_str[0] = ','; + indent_str[1] = '\n'; + indent_str[199] = 0; + if (indent < 197 && indent >= 0) indent_str[indent+2] = 0; + std::string ret; + switch (e.type()) + { + case lazy_entry::none_t: return "none"; + case lazy_entry::int_t: + { + char str[100]; + snprintf(str, sizeof(str), "%" PRId64, e.int_value()); + return str; + } + case lazy_entry::string_t: + { + print_string(ret, e.string_ptr(), e.string_length(), single_line); + return ret; + } + case lazy_entry::list_t: + { + ret += '['; + bool one_liner = line_longer_than(e, 200) != -1 || single_line; + + if (!one_liner) ret += indent_str + 1; + for (int i = 0; i < e.list_size(); ++i) + { + if (i == 0 && one_liner) ret += " "; + ret += print_entry(*e.list_at(i), single_line, indent + 2); + if (i < e.list_size() - 1) ret += (one_liner?", ":indent_str); + else ret += (one_liner?" ":indent_str+1); + } + ret += "]"; + return ret; + } + case lazy_entry::dict_t: + { + ret += "{"; + bool one_liner = line_longer_than(e, 200) != -1 || single_line; + + if (!one_liner) ret += indent_str+1; + for (int i = 0; i < e.dict_size(); ++i) + { + if (i == 0 && one_liner) ret += " "; + std::pair ent = e.dict_at(i); + print_string(ret, ent.first.c_str(), ent.first.size(), true); + ret += ": "; + ret += print_entry(*ent.second, single_line, indent + 2); + if (i < e.dict_size() - 1) ret += (one_liner?", ":indent_str); + else ret += (one_liner?" ":indent_str+1); + } + ret += "}"; + return ret; + } + } + return ret; + } + + struct bdecode_error_category : boost::system::error_category + { + virtual const char* name() const BOOST_SYSTEM_NOEXCEPT; + virtual std::string message(int ev) const BOOST_SYSTEM_NOEXCEPT; + virtual boost::system::error_condition default_error_condition(int ev) const BOOST_SYSTEM_NOEXCEPT + { return boost::system::error_condition(ev, *this); } + }; + + const char* bdecode_error_category::name() const BOOST_SYSTEM_NOEXCEPT + { + return "bdecode error"; + } + + std::string bdecode_error_category::message(int ev) const BOOST_SYSTEM_NOEXCEPT + { + static char const* msgs[] = + { + "no error", + "expected string in bencoded string", + "expected colon in bencoded string", + "unexpected end of file in bencoded string", + "expected value (list, dict, int or string) in bencoded string", + "bencoded nesting depth exceeded", + "bencoded item count limit exceeded", + "integer overflow", + }; + if (ev < 0 || ev >= int(sizeof(msgs)/sizeof(msgs[0]))) + return "Unknown error"; + return msgs[ev]; + } + + boost::system::error_category& get_bdecode_category() + { + static bdecode_error_category bdecode_category; + return bdecode_category; + } + + namespace bdecode_errors + { + boost::system::error_code make_error_code(error_code_enum e) + { + return boost::system::error_code(e, get_bdecode_category()); + } + } +}; + diff --git a/apps/Launcher/ext/libtorrent/src/logger.cpp b/apps/Launcher/ext/libtorrent/src/logger.cpp new file mode 100644 index 0000000000..6c42ce8560 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/logger.cpp @@ -0,0 +1,237 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include + +#include "libtorrent/extensions/logger.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/peer_request.hpp" + +#if TORRENT_USE_IOSTREAM && !defined TORRENT_DISABLE_EXTENSIONS + +#include +#include "libtorrent/file.hpp" +#include "libtorrent/time.hpp" +#include "libtorrent/lazy_entry.hpp" +#include "libtorrent/peer_connection.hpp" + +namespace libtorrent { + + class peer_connection; + +namespace +{ + + struct logger_peer_plugin : peer_plugin + { + logger_peer_plugin(std::string const& filename) + { + error_code ec; + std::string dir = complete("libtorrent_ext_logs"); + if (!exists(dir)) create_directories(dir, ec); + m_file.open(combine_path(dir, filename).c_str(), std::ios_base::out); + m_file << "\n\n\n"; + log_timestamp(); + m_file << "*** starting log ***\n"; + } + + void log_timestamp() + { + m_file << time_now_string() << ": "; + } + + // can add entries to the extension handshake + virtual void add_handshake(entry&) {} + + // called when the extension handshake from the other end is received + virtual bool on_extension_handshake(lazy_entry const& h) + { + log_timestamp(); + m_file << "<== EXTENSION_HANDSHAKE\n" << print_entry(h); + return true; + } + + // returning true from any of the message handlers + // indicates that the plugin has handeled the message. + // it will break the plugin chain traversing and not let + // anyone else handle the message, including the default + // handler. + + virtual bool on_choke() + { + log_timestamp(); + m_file << "<== CHOKE\n"; + m_file.flush(); + return false; + } + + virtual bool on_unchoke() + { + log_timestamp(); + m_file << "<== UNCHOKE\n"; + m_file.flush(); + return false; + } + + virtual bool on_interested() + { + log_timestamp(); + m_file << "<== INTERESTED\n"; + m_file.flush(); + return false; + } + + virtual bool on_not_interested() + { + log_timestamp(); + m_file << "<== NOT_INTERESTED\n"; + m_file.flush(); + return false; + } + + virtual bool on_have(int index) + { + log_timestamp(); + m_file << "<== HAVE [" << index << "]\n"; + m_file.flush(); + return false; + } + + virtual bool on_bitfield(bitfield const& bitfield_) + { + log_timestamp(); + m_file << "<== BITFIELD\n"; + m_file.flush(); + return false; + } + + virtual bool on_request(peer_request const& r) + { + log_timestamp(); + m_file << "<== REQUEST [ piece: " << r.piece << " | s: " << r.start + << " | l: " << r.length << " ]\n"; + m_file.flush(); + return false; + } + + virtual bool on_piece(peer_request const& r, disk_buffer_holder& data) + { + log_timestamp(); + m_file << "<== PIECE [ piece: " << r.piece << " | s: " << r.start + << " | l: " << r.length << " ]\n"; + m_file.flush(); + return false; + } + + virtual bool on_cancel(peer_request const& r) + { + log_timestamp(); + m_file << "<== CANCEL [ piece: " << r.piece << " | s: " << r.start + << " | l: " << r.length << " ]\n"; + m_file.flush(); + return false; + } + + // called when an extended message is received. If returning true, + // the message is not processed by any other plugin and if false + // is returned the next plugin in the chain will receive it to + // be able to handle it + virtual bool on_extended(int length + , int msg, buffer::const_interval body) + { return false; } + + virtual bool on_unknown_message(int length, int msg + , buffer::const_interval body) + { + if (body.left() < length) return false; + log_timestamp(); + m_file << "<== UNKNOWN [ msg: " << msg + << " | l: " << length << " ]\n"; + m_file.flush(); + return false; + } + + virtual void on_piece_pass(int index) + { + log_timestamp(); + m_file << "*** HASH PASSED *** [ piece: " << index << " ]\n"; + m_file.flush(); + } + + virtual void on_piece_failed(int index) + { + log_timestamp(); + m_file << "*** HASH FAILED *** [ piece: " << index << " ]\n"; + m_file.flush(); + } + + private: + std::ofstream m_file; + }; + + struct logger_plugin : torrent_plugin + { + virtual boost::shared_ptr new_connection( + peer_connection* pc) + { + error_code ec; + return boost::shared_ptr(new logger_peer_plugin( + pc->remote().address().to_string(ec) + "_" + + to_string(pc->remote().port()).elems + ".log")); + } + }; + +} } + +namespace libtorrent +{ + + boost::shared_ptr create_logger_plugin(torrent*) + { + return boost::shared_ptr(new logger_plugin()); + } + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/lsd.cpp b/apps/Launcher/ext/libtorrent/src/lsd.cpp new file mode 100644 index 0000000000..86e6bc4c70 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/lsd.cpp @@ -0,0 +1,325 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/lsd.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/buffer.hpp" +#include "libtorrent/random.hpp" +#include "libtorrent/http_parser.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/socket_io.hpp" // for print_address + +#if defined TORRENT_ASIO_DEBUGGING +#include "libtorrent/debug.hpp" +#endif + +#include +#include +#if BOOST_VERSION < 103500 +#include +#include +#else +#include +#include +#endif +#include +#include + +using namespace libtorrent; + +namespace libtorrent +{ + // defined in broadcast_socket.cpp + address guess_local_address(io_service&); +} + +static error_code ec; + +lsd::lsd(io_service& ios, address const& listen_interface + , peer_callback_t const& cb) + : m_callback(cb) + , m_socket(udp::endpoint(address_v4::from_string("239.192.152.143", ec), 6771) + , boost::bind(&lsd::on_announce, self(), _1, _2, _3)) +#if TORRENT_USE_IPV6 + , m_socket6(udp::endpoint(address_v6::from_string("ff15::efc0:988f", ec), 6771) + , boost::bind(&lsd::on_announce, self(), _1, _2, _3)) +#endif + , m_broadcast_timer(ios) + , m_cookie(random()) + , m_disabled(false) +#if TORRENT_USE_IPV6 + , m_disabled6(false) +#endif +{ +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log = fopen("lsd.log", "w+"); + if (m_log == NULL) + { + fprintf(stderr, "failed to open 'lsd.log': (%d) %s" + , errno, strerror(errno)); + } +#endif + + error_code ec; + m_socket.open(ios, ec); + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (ec) + { + if (m_log) fprintf(m_log, "FAILED TO OPEN SOCKET: (%d) %s\n" + , ec.value(), ec.message().c_str()); + } +#endif + +#if TORRENT_USE_IPV6 + m_socket6.open(ios, ec); +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (ec) + { + if (m_log) fprintf(m_log, "FAILED TO OPEN SOCKET6: (%d) %s\n" + , ec.value(), ec.message().c_str()); + } +#endif +#endif +} + +lsd::~lsd() +{ +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (m_log) fclose(m_log); +#endif +} + +int render_lsd_packet(char* dst, int len, int listen_port + , char const* info_hash_hex, int m_cookie, char const* host) +{ + return snprintf(dst, len, + "BT-SEARCH * HTTP/1.1\r\n" + "Host: %s:6771\r\n" + "Port: %d\r\n" + "Infohash: %s\r\n" + "cookie: %x\r\n" + "\r\n\r\n", host, listen_port, info_hash_hex, m_cookie); +} + +void lsd::announce(sha1_hash const& ih, int listen_port, bool broadcast) +{ + announce_impl(ih, listen_port, broadcast, 0); +} + +void lsd::announce_impl(sha1_hash const& ih, int listen_port, bool broadcast + , int retry_count) +{ +#if TORRENT_USE_IPV6 + if (m_disabled && m_disabled6) return; +#else + if (m_disabled) return; +#endif + + char ih_hex[41]; + to_hex((char const*)&ih[0], 20, ih_hex); + char msg[200]; + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (m_log) fprintf(m_log, "%s ==> announce: ih: %s port: %u\n" + , time_now_string(), ih_hex, listen_port); +#endif + + error_code ec; + if (!m_disabled) + { + int msg_len = render_lsd_packet(msg, sizeof(msg), listen_port, ih_hex + , m_cookie, "239.192.152.143"); + m_socket.send(msg, msg_len, ec, broadcast ? broadcast_socket::broadcast : 0); + if (ec) + { + m_disabled = true; +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (m_log) fprintf(m_log, "%s failed to send message: (%d) %s" + , time_now_string(), ec.value(), ec.message().c_str()); +#endif + } + } + +#if TORRENT_USE_IPV6 + if (!m_disabled6) + { + int msg_len = render_lsd_packet(msg, sizeof(msg), listen_port, ih_hex + , m_cookie, "[ff15::efc0:988f]"); + m_socket6.send(msg, msg_len, ec, broadcast ? broadcast_socket::broadcast : 0); + if (ec) + { + m_disabled6 = true; +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (m_log) fprintf(m_log, "%s failed to send message6: (%d) %s" + , time_now_string(), ec.value(), ec.message().c_str()); +#endif + } + } +#endif + + ++retry_count; + if (retry_count >= 3) return; + +#if TORRENT_USE_IPV6 + if (m_disabled && m_disabled6) return; +#else + if (m_disabled) return; +#endif + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("lsd::resend_announce"); +#endif + m_broadcast_timer.expires_from_now(seconds(2 * retry_count), ec); + m_broadcast_timer.async_wait(boost::bind(&lsd::resend_announce, self(), _1 + , ih, listen_port, retry_count)); +} + +void lsd::resend_announce(error_code const& e, sha1_hash const& info_hash + , int listen_port, int retry_count) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("lsd::resend_announce"); +#endif + if (e) return; + + announce_impl(info_hash, listen_port, false, retry_count); +} + +void lsd::on_announce(udp::endpoint const& from, char* buffer + , std::size_t bytes_transferred) +{ + using namespace libtorrent::detail; + + http_parser p; + + bool error = false; + p.incoming(buffer::const_interval(buffer, buffer + bytes_transferred) + , error); + + if (!p.header_finished() || error) + { +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (m_log) fprintf(m_log, "%s <== announce: incomplete HTTP message\n", time_now_string()); +#endif + return; + } + + if (p.method() != "bt-search") + { +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (m_log) fprintf(m_log, "%s <== announce: invalid HTTP method: %s\n" + , time_now_string(), p.method().c_str()); +#endif + return; + } + + std::string const& port_str = p.header("port"); + if (port_str.empty()) + { +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (m_log) fprintf(m_log, "%s <== announce: invalid BT-SEARCH, missing port\n" + , time_now_string()); +#endif + return; + } + + int port = std::atoi(port_str.c_str()); + + typedef std::multimap headers_t; + headers_t const& headers = p.headers(); + + headers_t::const_iterator cookie_iter = headers.find("cookie"); + if (cookie_iter != headers.end()) + { + // we expect it to be hexadecimal + // if it isn't, it's not our cookie anyway + boost::int32_t cookie = strtol(cookie_iter->second.c_str(), NULL, 16); + if (cookie == m_cookie) + { +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (m_log) fprintf(m_log, "%s <== announce: ignoring packet (cookie matched our own): %x == %x\n" + , time_now_string(), cookie, m_cookie); +#endif + return; + } + } + + std::pair ihs + = headers.equal_range("infohash"); + + for (headers_t::const_iterator i = ihs.first; i != ihs.second; ++i) + { + std::string const& ih_str = i->second; + if (ih_str.size() != 40) + { +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (m_log) fprintf(m_log, "%s <== announce: invalid BT-SEARCH, invalid infohash: %s\n" + , time_now_string(), ih_str.c_str()); +#endif + continue; + } + + sha1_hash ih(0); + from_hex(ih_str.c_str(), 40, (char*)&ih[0]); + + if (!ih.is_all_zeros() && port != 0) + { +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (m_log) fprintf(m_log, "%s *** incoming local announce %s:%d ih: %s\n" + , time_now_string(), print_address(from.address()).c_str() + , port, ih_str.c_str()); +#endif + // we got an announce, pass it on through the callback + TORRENT_TRY { + m_callback(tcp::endpoint(from.address(), port), ih); + } TORRENT_CATCH(std::exception&) {} + } + } +} + +void lsd::close() +{ + m_socket.close(); +#if TORRENT_USE_IPV6 + m_socket6.close(); +#endif + error_code ec; + m_broadcast_timer.cancel(ec); + m_disabled = true; +#if TORRENT_USE_IPV6 + m_disabled6 = true; +#endif + m_callback.clear(); +} + diff --git a/apps/Launcher/ext/libtorrent/src/lt_trackers.cpp b/apps/Launcher/ext/libtorrent/src/lt_trackers.cpp new file mode 100644 index 0000000000..1f3d727fb9 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/lt_trackers.cpp @@ -0,0 +1,414 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include +#include +#include + +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/extensions/lt_trackers.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/parse_url.hpp" +#ifdef TORRENT_STATS +#include "libtorrent/aux_/session_impl.hpp" +#endif + +namespace libtorrent { namespace +{ + + bool send_tracker(announce_entry const& e) + { + // max_fails == 0 means that it's one + // of the trackers from the trackers + // from the torrent file + return e.fail_limit == 0 || e.verified; + } + + struct lt_tracker_plugin : torrent_plugin + { + lt_tracker_plugin(torrent& t) + : m_torrent(t) + , m_updates(0) + , m_2_minutes(110) + , m_num_trackers(0) + { + m_old_trackers = t.trackers(); + update_list_hash(); + } + + virtual boost::shared_ptr new_connection( + peer_connection* pc); + + virtual void tick() + { + if (m_2_minutes++ < 120) return; + m_2_minutes = 0; + + // build tracker diff + entry tex; + entry::list_type& added = tex["added"].list(); + std::vector const& trackers = m_torrent.trackers(); + for (std::vector::const_iterator i = trackers.begin() + , end(trackers.end()); i != end; ++i) + { + std::vector::const_iterator k = std::find_if( + m_old_trackers.begin(), m_old_trackers.end() + , boost::bind(&announce_entry::url, _1) == i->url); + if (k != m_old_trackers.end()) continue; + if (!send_tracker(*i)) continue; + m_old_trackers.push_back(*i); + ++m_updates; + added.push_back(i->url); + } + m_lt_trackers_msg.clear(); + bencode(std::back_inserter(m_lt_trackers_msg), tex); + if (m_updates > 0) update_list_hash(); + } + + void update_list_hash() + { + std::vector canonical_list; + std::transform(m_old_trackers.begin(), m_old_trackers.end(), back_inserter(canonical_list) + , boost::bind(&announce_entry::url, _1)); + std::sort(canonical_list.begin(), canonical_list.end()); + + hasher h; + std::for_each(canonical_list.begin(), canonical_list.end() + , boost::bind(&hasher::update, &h, _1)); + m_list_hash = h.final(); + } + + int num_updates() const { return m_updates; } + + std::vector const& get_lt_tex_msg() const { return m_lt_trackers_msg; } + + sha1_hash const& list_hash() const { return m_list_hash; } + + std::vector const& trackers() const { return m_old_trackers; } + + void increment_tracker_counter() { m_num_trackers++; } + int num_tex_trackers() const { return m_num_trackers; } + + private: + torrent& m_torrent; + std::vector m_old_trackers; + int m_updates; + int m_2_minutes; + std::vector m_lt_trackers_msg; + sha1_hash m_list_hash; + int m_num_trackers; + }; + + + struct lt_tracker_peer_plugin : peer_plugin + { + lt_tracker_peer_plugin(torrent& t, bt_peer_connection& pc, lt_tracker_plugin& tp) + : m_message_index(0) + , m_torrent(t) + , m_pc(pc) + , m_tp(tp) + , m_2_minutes(115) + , m_full_list(true) + {} + + // can add entries to the extension handshake + virtual void add_handshake(entry& h) + { + entry& messages = h["m"]; + messages["lt_tex"] = 19; + h["tr"] = m_tp.list_hash().to_string(); + } + + // called when the extension handshake from the other end is received + virtual bool on_extension_handshake(lazy_entry const& h) + { + m_message_index = 0; + if (h.type() != lazy_entry::dict_t) return false; + lazy_entry const* messages = h.dict_find("m"); + if (!messages || messages->type() != lazy_entry::dict_t) return false; + + int index = int(messages->dict_find_int_value("lt_tex", -1)); + if (index == -1) return false; + m_message_index = index; + + // if we have the same tracker list, don't bother sending the + // full list. Just send deltas + std::string tracker_list_hash = h.dict_find_string_value("tr"); + if (tracker_list_hash.size() == 20 + && sha1_hash(tracker_list_hash) == m_tp.list_hash()) + { + m_full_list = false; + } + return true; + } + + virtual bool on_extended(int length + , int extended_msg, buffer::const_interval body) + { + if (extended_msg != 19) return false; + if (m_message_index == 0) return false; + if (!m_pc.packet_finished()) return true; + + lazy_entry msg; + error_code ec; + int ret = lazy_bdecode(body.begin, body.end, msg, ec); + if (ret != 0 || msg.type() != lazy_entry::dict_t) + { + m_pc.disconnect(errors::invalid_lt_tracker_message, 2); + return true; + } + + lazy_entry const* added = msg.dict_find_list("added"); + + // invalid tex message + if (added == 0) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_pc.m_logger) << time_now_string() << " <== LT_TEX [ NOT A DICTIONARY ]\n"; +#endif + return true; + } + +#ifdef TORRENT_VERBOSE_LOGGING + std::stringstream log_line; +#endif + if (m_tp.num_tex_trackers() >= 50) + { +#ifdef TORRENT_VERBOSE_LOGGING + log_line << time_now_string() << " <== LT_TEX [ " + "we already have " << m_tp.num_tex_trackers() << " trackers " + "from tex, don't add any more"; + (*m_pc.m_logger) << log_line.str(); +#endif + return true; + } + +#ifdef TORRENT_VERBOSE_LOGGING + log_line << time_now_string() << " <== LT_TEX [ " + "added: "; +#endif + + for (int i = 0; i < added->list_size(); ++i) + { + announce_entry e(added->list_string_value_at(i)); + if (e.url.empty()) continue; + + // ignore urls with binary data in them + if (need_encoding(e.url.c_str(), e.url.size())) continue; + + // ignore invalid URLs + error_code ec; + std::string protocol; + std::string auth; + std::string hostname; + int port; + std::string path; + boost::tie(protocol, auth, hostname, port, path) + = parse_url_components(e.url, ec); + if (ec) continue; + + // ignore unknown protocols + if (protocol != "udp" && protocol != "http" && protocol != "https") + continue; + + // ignore invalid ports + if (port == 0) + continue; + + if (m_tp.num_tex_trackers() >= 50) + { +#ifdef TORRENT_VERBOSE_LOGGING + log_line << "**reached-limit** "; +#endif + break; + } + + e.fail_limit = 1; + e.send_stats = false; + e.source = announce_entry::source_tex; + if (m_torrent.add_tracker(e)) + m_tp.increment_tracker_counter(); + +#ifdef TORRENT_VERBOSE_LOGGING + log_line << e.url << " "; +#endif + } +#ifdef TORRENT_VERBOSE_LOGGING + log_line << "]\n"; + (*m_pc.m_logger) << log_line.str(); +#endif + return true; + } + + virtual void tick() + { + if (!m_message_index) return; // no handshake yet + if (++m_2_minutes <= 120) return; + m_2_minutes = 0; + + if (m_full_list) + { + if (send_full_tex_list()) + m_full_list = false; + } + else + { + send_lt_tex_diff(); + } + } + + private: + + void send_lt_tex_diff() + { + // if there's no change in out tracker set, don't send anything + if (m_tp.num_updates() == 0) return; + + if (!m_torrent.valid_metadata() || m_torrent.torrent_file().priv()) + return; + + std::vector const& tex_msg = m_tp.get_lt_tex_msg(); + + char msg[6]; + char* ptr = msg; + + detail::write_uint32(1 + 1 + tex_msg.size(), ptr); + detail::write_uint8(bt_peer_connection::msg_extended, ptr); + detail::write_uint8(m_message_index, ptr); + m_pc.send_buffer(msg, sizeof(msg)); + m_pc.send_buffer(&tex_msg[0], tex_msg.size()); + m_pc.setup_send(); + } + + bool send_full_tex_list() const + { + if (m_tp.trackers().empty()) return false; + + if (!m_torrent.valid_metadata() || m_torrent.torrent_file().priv()) + return false; + +#ifdef TORRENT_VERBOSE_LOGGING + std::stringstream log_line; + log_line << time_now_string() << " ==> LT_TEX [ " + "added: "; +#endif + entry tex; + entry::list_type& added = tex["added"].list(); + for (std::vector::const_iterator i = m_tp.trackers().begin() + , end(m_tp.trackers().end()); i != end; ++i) + { + if (!send_tracker(*i)) continue; + added.push_back(i->url); +#ifdef TORRENT_VERBOSE_LOGGING + log_line << i->url << " "; +#endif + } + std::vector tex_msg; + bencode(std::back_inserter(tex_msg), tex); + +#ifdef TORRENT_VERBOSE_LOGGING + log_line << "]\n"; + (*m_pc.m_logger) << log_line.str(); +#endif + + char msg[6]; + char* ptr = msg; + + detail::write_uint32(1 + 1 + tex_msg.size(), ptr); + detail::write_uint8(bt_peer_connection::msg_extended, ptr); + detail::write_uint8(m_message_index, ptr); + m_pc.send_buffer(msg, sizeof(msg)); + m_pc.send_buffer(&tex_msg[0], tex_msg.size()); + m_pc.setup_send(); + + return true; + } + + // this is the message index the remote peer uses + // for metadata extension messages. + int m_message_index; + + torrent& m_torrent; + bt_peer_connection& m_pc; + lt_tracker_plugin& m_tp; + + int m_2_minutes; + bool m_full_list; + }; + + boost::shared_ptr lt_tracker_plugin::new_connection( + peer_connection* pc) + { + if (pc->type() != peer_connection::bittorrent_connection) + return boost::shared_ptr(); + + if (m_torrent.valid_metadata() && m_torrent.torrent_file().priv()) + return boost::shared_ptr(); + + bt_peer_connection* c = static_cast(pc); + return boost::shared_ptr(new lt_tracker_peer_plugin(m_torrent, *c, *this)); + } + +} } + +namespace libtorrent +{ + + boost::shared_ptr TORRENT_EXPORT create_lt_trackers_plugin(torrent* t, void*) + { + if (t->valid_metadata() && t->torrent_file().priv()) return boost::shared_ptr(); + return boost::shared_ptr(new lt_tracker_plugin(*t)); + } + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/magnet_uri.cpp b/apps/Launcher/ext/libtorrent/src/magnet_uri.cpp new file mode 100644 index 0000000000..6b90aff2fe --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/magnet_uri.cpp @@ -0,0 +1,254 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/magnet_uri.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/error_code.hpp" + +#include + +namespace libtorrent +{ + std::string make_magnet_uri(torrent_handle const& handle) + { + if (!handle.is_valid()) return ""; + + std::string ret; + sha1_hash const& ih = handle.info_hash(); + ret += "magnet:?xt=urn:btih:"; + ret += to_hex(ih.to_string()); + + torrent_status st = handle.status(torrent_handle::query_name); + if (!st.name.empty()) + { + ret += "&dn="; + ret += escape_string(st.name.c_str(), st.name.length()); + } + + std::vector const& tr = handle.trackers(); + for (std::vector::const_iterator i = tr.begin(), end(tr.end()); i != end; ++i) + { + ret += "&tr="; + ret += escape_string(i->url.c_str(), i->url.length()); + } + + std::set seeds = handle.url_seeds(); + for (std::set::iterator i = seeds.begin() + , end(seeds.end()); i != end; ++i) + { + ret += "&ws="; + ret += escape_string(i->c_str(), i->length()); + } + + return ret; + } + + std::string make_magnet_uri(torrent_info const& info) + { + std::string ret; + sha1_hash const& ih = info.info_hash(); + ret += "magnet:?xt=urn:btih:"; + ret += to_hex(ih.to_string()); + + std::string const& name = info.name(); + + if (!name.empty()) + { + ret += "&dn="; + ret += escape_string(name.c_str(), name.length()); + } + + std::vector const& tr = info.trackers(); + + for (std::vector::const_iterator i = tr.begin(), end(tr.end()); i != end; ++i) + { + ret += "&tr="; + ret += escape_string(i->url.c_str(), i->url.length()); + } + + std::vector const& seeds = info.web_seeds(); + for (std::vector::const_iterator i = seeds.begin() + , end(seeds.end()); i != end; ++i) + { + if (i->type != web_seed_entry::url_seed) continue; + + ret += "&ws="; + ret += escape_string(i->url.c_str(), i->url.length()); + } + + return ret; + } + +#ifndef TORRENT_NO_DEPRECATE + + torrent_handle add_magnet_uri_deprecated(session& ses, std::string const& uri + , add_torrent_params p, error_code& ec) + { + parse_magnet_uri(uri, p, ec); + if (ec) return torrent_handle(); + return ses.add_torrent(p, ec); + } + + torrent_handle add_magnet_uri(session& ses, std::string const& uri + , add_torrent_params p, error_code& ec) + { + return add_magnet_uri_deprecated(ses, uri, p, ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + torrent_handle add_magnet_uri(session& ses, std::string const& uri + , std::string const& save_path + , storage_mode_t storage_mode + , bool paused + , storage_constructor_type sc + , void* userdata) + { + add_torrent_params params(sc); + params.storage_mode = storage_mode; + params.userdata = userdata; + params.save_path = save_path; + + if (paused) params.flags |= add_torrent_params::flag_paused; + else params.flags &= ~add_torrent_params::flag_paused; + + error_code ec; + std::string display_name = url_has_argument(uri, "dn"); + if (!display_name.empty()) params.name = unescape_string(display_name.c_str(), ec); + std::string tracker_string = url_has_argument(uri, "tr"); + if (!tracker_string.empty()) params.trackers.push_back(unescape_string(tracker_string.c_str(), ec)); + + std::string btih = url_has_argument(uri, "xt"); + if (btih.empty()) return torrent_handle(); + + if (btih.compare(0, 9, "urn:btih:") != 0) return torrent_handle(); + + if (btih.size() == 40 + 9) from_hex(&btih[9], 40, (char*)¶ms.info_hash[0]); + else params.info_hash.assign(base32decode(btih.substr(9))); + + return ses.add_torrent(params); + } + + torrent_handle add_magnet_uri(session& ses, std::string const& uri + , add_torrent_params p) + { + error_code ec; + torrent_handle ret = add_magnet_uri_deprecated(ses, uri, p, ec); + if (ec) throw libtorrent_exception(ec); + return ret; + } +#endif // BOOST_NO_EXCEPTIONS +#endif // TORRENT_NO_DEPRECATE + + void parse_magnet_uri(std::string const& uri, add_torrent_params& p, error_code& ec) + { + ec.clear(); + std::string name; + + error_code e; + std::string display_name = url_has_argument(uri, "dn"); + if (!display_name.empty()) name = unescape_string(display_name.c_str(), e); + + // parse trackers out of the magnet link + std::string::size_type pos = std::string::npos; + std::string url = url_has_argument(uri, "tr", &pos); + while (pos != std::string::npos) + { + error_code e; + url = unescape_string(url, e); + if (e) continue; + p.trackers.push_back(url); + pos = uri.find("&tr=", pos); + if (pos == std::string::npos) break; + pos += 4; + url = uri.substr(pos, uri.find('&', pos) - pos); + } + + // parse web seeds out of the magnet link + pos = std::string::npos; + url = url_has_argument(uri, "ws", &pos); + while (pos != std::string::npos) + { + error_code e; + url = unescape_string(url, e); + if (e) continue; + p.url_seeds.push_back(url); + pos = uri.find("&ws=", pos); + if (pos == std::string::npos) break; + pos += 4; + url = uri.substr(pos, uri.find('&', pos) - pos); + } + + std::string btih = url_has_argument(uri, "xt"); + if (btih.empty()) + { + ec = errors::missing_info_hash_in_uri; + return; + } + + if (btih.compare(0, 9, "urn:btih:") != 0) + { + ec = errors::missing_info_hash_in_uri; + return; + } + +#ifndef TORRENT_DISABLE_DHT + std::string::size_type node_pos = std::string::npos; + std::string node = url_has_argument(uri, "dht", &node_pos); + while (!node.empty()) + { + std::string::size_type divider = node.find_last_of(':'); + if (divider != std::string::npos) + { + int port = atoi(node.c_str()+divider+1); + if (port != 0) + p.dht_nodes.push_back(std::make_pair(node.substr(0, divider), port)); + } + + node_pos = uri.find("&dht=", node_pos); + if (node_pos == std::string::npos) break; + node_pos += 5; + node = uri.substr(node_pos, uri.find('&', node_pos) - node_pos); + } +#endif + + sha1_hash info_hash; + if (btih.size() == 40 + 9) from_hex(&btih[9], 40, (char*)&info_hash[0]); + else info_hash.assign(base32decode(btih.substr(9))); + + p.info_hash = info_hash; + if (!name.empty()) p.name = name; + } +} + + diff --git a/apps/Launcher/ext/libtorrent/src/metadata_transfer.cpp b/apps/Launcher/ext/libtorrent/src/metadata_transfer.cpp new file mode 100644 index 0000000000..aa19e3f456 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/metadata_transfer.cpp @@ -0,0 +1,582 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include +#include +#include // count + +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/extensions/metadata_transfer.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/buffer.hpp" + +namespace libtorrent { namespace +{ + int div_round_up(int numerator, int denominator) + { + return (numerator + denominator - 1) / denominator; + } + + std::pair req_to_offset(std::pair req, int total_size) + { + TORRENT_ASSERT(req.first >= 0); + TORRENT_ASSERT(req.second > 0); + TORRENT_ASSERT(req.second <= 256); + TORRENT_ASSERT(req.first + req.second <= 256); + + int start = div_round_up(req.first * total_size, 256); + int size = div_round_up((req.first + req.second) * total_size, 256) - start; + return std::make_pair(start, size); + } + + std::pair offset_to_req(std::pair offset, int total_size) + { + int start = offset.first * 256 / total_size; + int size = (offset.first + offset.second) * 256 / total_size - start; + + std::pair ret(start, size); + + TORRENT_ASSERT(start >= 0); + TORRENT_ASSERT(size > 0); + TORRENT_ASSERT(start <= 256); + TORRENT_ASSERT(start + size <= 256); + + // assert the identity of this function +#ifndef NDEBUG + std::pair identity = req_to_offset(ret, total_size); + TORRENT_ASSERT(offset == identity); +#endif + return ret; + } + + struct metadata_plugin : torrent_plugin + { + metadata_plugin(torrent& t) + : m_torrent(t) + , m_metadata_progress(0) + , m_metadata_size(0) + { + m_requested_metadata.resize(256, 0); + } + + virtual void on_files_checked() + { + // if the torrent is a seed, make a reference to + // the metadata from the torrent before it is deallocated + if (m_torrent.is_seed()) metadata(); + } + + virtual boost::shared_ptr new_connection( + peer_connection* pc); + + buffer::const_interval metadata() const + { + if (!m_metadata) + { + m_metadata = m_torrent.torrent_file().metadata(); + m_metadata_size = m_torrent.torrent_file().metadata_size(); + TORRENT_ASSERT(hasher(m_metadata.get(), m_metadata_size).final() + == m_torrent.torrent_file().info_hash()); + } + return buffer::const_interval(m_metadata.get(), m_metadata.get() + + m_metadata_size); + } + + bool received_metadata(char const* buf, int size, int offset, int total_size) + { + if (m_torrent.valid_metadata()) return false; + + if (!m_metadata || m_metadata_size < total_size) + { + m_metadata.reset(new char[total_size]); + m_metadata_size = total_size; + } + std::copy(buf, buf + size, &m_metadata[offset]); + + if (m_have_metadata.empty()) + m_have_metadata.resize(256, false); + + std::pair req = offset_to_req(std::make_pair(offset, size) + , total_size); + + TORRENT_ASSERT(req.first + req.second <= (int)m_have_metadata.size()); + + std::fill( + m_have_metadata.begin() + req.first + , m_have_metadata.begin() + req.first + req.second + , true); + + bool have_all = std::count( + m_have_metadata.begin() + , m_have_metadata.end() + , true) == 256; + + if (!have_all) return false; + + if (!m_torrent.set_metadata(&m_metadata[0], m_metadata_size)) + { + std::fill( + m_have_metadata.begin() + , m_have_metadata.begin() + req.first + req.second + , false); + m_metadata_progress = 0; + m_metadata_size = 0; + return false; + } + + // clear the storage for the bitfield + std::vector().swap(m_have_metadata); + std::vector().swap(m_requested_metadata); + + return true; + } + + // returns a range of the metadata that + // we should request. + std::pair metadata_request(); + + void cancel_metadata_request(std::pair req) + { + for (int i = req.first; i < req.first + req.second; ++i) + { + TORRENT_ASSERT(m_requested_metadata[i] > 0); + if (m_requested_metadata[i] > 0) + --m_requested_metadata[i]; + } + } + + // this is called from the peer_connection for + // each piece of metadata it receives + void metadata_progress(int total_size, int received) + { + m_metadata_progress += received; + m_metadata_size = total_size; + m_torrent.set_progress_ppm(boost::int64_t(m_metadata_progress) * 1000000 / m_metadata_size); + } + + void on_piece_pass(int) + { + // if we became a seed, copy the metadata from + // the torrent before it is deallocated + if (m_torrent.is_seed()) + metadata(); + } + + int metadata_size() const { return m_metadata_size; } + + private: + torrent& m_torrent; + + // this buffer is filled with the info-section of + // the metadata file while downloading it from + // peers, and while sending it. + // it is mutable because it's generated lazily + mutable boost::shared_array m_metadata; + + int m_metadata_progress; + mutable int m_metadata_size; + + // this is a bitfield of size 256, each bit represents + // a piece of the metadata. It is set to one if we + // have that piece. This vector may be empty + // (size 0) if we haven't received any metadata + // or if we already have all metadata + std::vector m_have_metadata; + // this vector keeps track of how many times each meatdata + // block has been requested + std::vector m_requested_metadata; + }; + + + struct metadata_peer_plugin : peer_plugin + { + metadata_peer_plugin(torrent& t, peer_connection& pc + , metadata_plugin& tp) + : m_waiting_metadata_request(false) + , m_message_index(0) + , m_metadata_progress(0) + , m_no_metadata(min_time()) + , m_metadata_request(min_time()) + , m_torrent(t) + , m_pc(pc) + , m_tp(tp) + {} + + virtual char const* type() const { return "LT_metadata"; } + + // can add entries to the extension handshake + virtual void add_handshake(entry& h) + { + entry& messages = h["m"]; + messages["LT_metadata"] = 14; + } + + // called when the extension handshake from the other end is received + virtual bool on_extension_handshake(lazy_entry const& h) + { + m_message_index = 0; + if (h.type() != lazy_entry::dict_t) return false; + lazy_entry const* messages = h.dict_find("m"); + if (!messages || messages->type() != lazy_entry::dict_t) return false; + + int index = int(messages->dict_find_int_value("LT_metadata", -1)); + if (index == -1) return false; + m_message_index = index; + return true; + } + + void write_metadata_request(std::pair req) + { + TORRENT_ASSERT(req.first >= 0); + TORRENT_ASSERT(req.second > 0); + TORRENT_ASSERT(req.first + req.second <= 256); + TORRENT_ASSERT(!m_pc.associated_torrent().expired()); + TORRENT_ASSERT(!m_pc.associated_torrent().lock()->valid_metadata()); + + int start = req.first; + int size = req.second; + + // abort if the peer doesn't support the metadata extension + if (m_message_index == 0) return; + +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("==> METADATA_REQUEST [ start: %d | size: %d ]\n" + , start, size); +#endif + + char msg[9]; + char* ptr = msg; + + detail::write_uint32(1 + 1 + 3, ptr); + detail::write_uint8(bt_peer_connection::msg_extended, ptr); + detail::write_uint8(m_message_index, ptr); + // means 'request data' + detail::write_uint8(0, ptr); + detail::write_uint8(start, ptr); + detail::write_uint8(size - 1, ptr); + m_pc.send_buffer(msg, sizeof(msg)); + m_pc.setup_send(); + } + + void write_metadata(std::pair req) + { + TORRENT_ASSERT(req.first >= 0); + TORRENT_ASSERT(req.second > 0); + TORRENT_ASSERT(req.second <= 256); + TORRENT_ASSERT(req.first + req.second <= 256); + TORRENT_ASSERT(!m_pc.associated_torrent().expired()); + + // abort if the peer doesn't support the metadata extension + if (m_message_index == 0) return; + + if (m_torrent.valid_metadata()) + { + std::pair offset + = req_to_offset(req, (int)m_tp.metadata().left()); + + char msg[15]; + char* ptr = msg; + +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("==> METADATA [ start: %d | total_size: %d | offset: %d | data_size: %d ]" + , req.first, req.second, offset.first, offset.second); +#endif + // yes, we have metadata, send it + detail::write_uint32(11 + offset.second, ptr); + detail::write_uint8(bt_peer_connection::msg_extended, ptr); + detail::write_uint8(m_message_index, ptr); + // means 'data packet' + detail::write_uint8(1, ptr); + detail::write_uint32((int)m_tp.metadata().left(), ptr); + detail::write_uint32(offset.first, ptr); + m_pc.send_buffer(msg, sizeof(msg)); + char const* metadata = m_tp.metadata().begin; + m_pc.append_const_send_buffer(metadata + offset.first, offset.second); + } + else + { +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("==> DONT HAVE METADATA\n"); +#endif + char msg[4+3]; + char* ptr = msg; + + // we don't have the metadata, reply with + // don't have-message + detail::write_uint32(1 + 2, ptr); + detail::write_uint8(bt_peer_connection::msg_extended, ptr); + detail::write_uint8(m_message_index, ptr); + // means 'have no data' + detail::write_uint8(2, ptr); + m_pc.send_buffer(msg, sizeof(msg)); + } + m_pc.setup_send(); + } + + virtual bool on_extended(int length + , int msg, buffer::const_interval body) + { + if (msg != 14) return false; + if (m_message_index == 0) return false; + + if (length > 500 * 1024) + { + m_pc.disconnect(errors::metadata_too_large, 2); + return true; + } + + if (body.left() < 1) return true; + int type = detail::read_uint8(body.begin); + + switch (type) + { + case 0: // request + { + if (body.left() < 2) return true; + int start = detail::read_uint8(body.begin); + int size = detail::read_uint8(body.begin) + 1; + +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("<== METADATA_REQUEST [ start: %d | size: %d ]\n" + , start, size); +#endif + + if (length != 3) + { + // invalid metadata request + m_pc.disconnect(errors::invalid_metadata_request, 2); + return true; + } + + write_metadata(std::make_pair(start, size)); + } + break; + case 1: // data + { + if (body.left() < 8) return true; + + int total_size = detail::read_int32(body.begin); + int offset = detail::read_int32(body.begin); + int data_size = length - 9; + +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("<== METADATA [ total_size: %d | offset: %d | data_size: %d ]" + ,total_size, offset, data_size); +#endif + + if (total_size > m_torrent.session().settings().max_metadata_size) + { + m_pc.disconnect(errors::metadata_too_large, 2); + return true; + } + if (total_size <= 0) + { + m_pc.disconnect(errors::invalid_metadata_size, 2); + return true; + } + if (offset > total_size || offset < 0) + { + m_pc.disconnect(errors::invalid_metadata_offset, 2); + return true; + } + if (offset + data_size > total_size) + { + m_pc.disconnect(errors::invalid_metadata_message, 2); + return true; + } + + m_tp.metadata_progress(total_size + , body.left() - m_metadata_progress); + m_metadata_progress = body.left(); + + if (body.left() < data_size) return true; + + m_waiting_metadata_request = false; + m_tp.received_metadata(body.begin, data_size + , offset, total_size); + m_metadata_progress = 0; + } + break; + case 2: // have no data + m_no_metadata = time_now(); + if (m_waiting_metadata_request) + m_tp.cancel_metadata_request(m_last_metadata_request); + m_waiting_metadata_request = false; +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("<== DONT HAVE METADATA\n"); +#endif + break; + default: + { + m_pc.disconnect(errors::invalid_metadata_message, 2); + } + } + return true; + } + + virtual void tick() + { + if (m_pc.is_disconnecting()) return; + + // if we don't have any metadata, and this peer + // supports the request metadata extension + // and we aren't currently waiting for a request + // reply. Then, send a request for some metadata. + if (!m_torrent.valid_metadata() + && m_message_index != 0 + && !m_waiting_metadata_request + && has_metadata()) + { + m_last_metadata_request = m_tp.metadata_request(); + write_metadata_request(m_last_metadata_request); + m_waiting_metadata_request = true; + m_metadata_request = time_now(); + } + } + + bool has_metadata() const + { + return time_now() - m_no_metadata > minutes(5); + } + + private: + + // this is set to true when we send a metadata + // request to this peer, and reset to false when + // we receive a reply to our request. + bool m_waiting_metadata_request; + + // this is the message index the remote peer uses + // for metadata extension messages. + int m_message_index; + + // the number of bytes of metadata we have received + // so far from this per, only counting the current + // request. Any previously finished requests + // that have been forwarded to the torrent object + // do not count. + int m_metadata_progress; + + // this is set to the current time each time we get a + // "I don't have metadata" message. + ptime m_no_metadata; + + // this is set to the time when we last sent + // a request for metadata to this peer + ptime m_metadata_request; + + // if we're waiting for a metadata request + // this was the request we sent + std::pair m_last_metadata_request; + + torrent& m_torrent; + peer_connection& m_pc; + metadata_plugin& m_tp; + }; + + boost::shared_ptr metadata_plugin::new_connection( + peer_connection* pc) + { + if (pc->type() != peer_connection::bittorrent_connection) + return boost::shared_ptr(); + + return boost::shared_ptr(new metadata_peer_plugin(m_torrent, *pc, *this)); + } + + std::pair metadata_plugin::metadata_request() + { + // the number of blocks to request + int num_blocks = 256 / 4; + TORRENT_ASSERT(num_blocks <= 128); + + int min_element = (std::numeric_limits::max)(); + int best_index = 0; + for (int i = 0; i < 256 - num_blocks + 1; ++i) + { + int min = *std::min_element(m_requested_metadata.begin() + i + , m_requested_metadata.begin() + i + num_blocks); + min += std::accumulate(m_requested_metadata.begin() + i + , m_requested_metadata.begin() + i + num_blocks, (int)0); + + if (min_element > min) + { + best_index = i; + min_element = min; + } + } + + std::pair ret(best_index, num_blocks); + for (int i = ret.first; i < ret.first + ret.second; ++i) + m_requested_metadata[i]++; + + TORRENT_ASSERT(ret.first >= 0); + TORRENT_ASSERT(ret.second > 0); + TORRENT_ASSERT(ret.second <= 256); + TORRENT_ASSERT(ret.first + ret.second <= 256); + + return ret; + } + +} } + +namespace libtorrent +{ + + boost::shared_ptr create_metadata_plugin(torrent* t, void*) + { + // don't add this extension if the torrent is private + if (t->valid_metadata() && t->torrent_file().priv()) return boost::shared_ptr(); + return boost::shared_ptr(new metadata_plugin(*t)); + } + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/mpi.c b/apps/Launcher/ext/libtorrent/src/mpi.c new file mode 100644 index 0000000000..e1c59ee715 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/mpi.c @@ -0,0 +1,9514 @@ +/* Start: bn_error.c */ +#include "libtorrent/tommath.h" +#ifdef BN_ERROR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +static const struct { + int code; + char *msg; +} msgs[] = { + { MP_OKAY, "Successful" }, + { MP_MEM, "Out of heap" }, + { MP_VAL, "Value out of range" } +}; + +/* return a char * string for a given code */ +char *mp_error_to_string(int code) +{ + int x; + + /* scan the lookup table for the given message */ + for (x = 0; x < (int)(sizeof(msgs) / sizeof(msgs[0])); x++) { + if (msgs[x].code == code) { + return msgs[x].msg; + } + } + + /* generic reply for invalid code */ + return "Invalid error code"; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_error.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_error.c */ + +/* Start: bn_fast_mp_invmod.c */ +#include "libtorrent/tommath.h" +#ifdef BN_FAST_MP_INVMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* computes the modular inverse via binary extended euclidean algorithm, + * that is c = 1/a mod b + * + * Based on slow invmod except this is optimized for the case where b is + * odd as per HAC Note 14.64 on pp. 610 + */ +int fast_mp_invmod (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int x, y, u, v, B, D; + int res, neg; + + /* 2. [modified] b must be odd */ + if (mp_iseven (b) == 1) { + return MP_VAL; + } + + /* init all our temps */ + if ((res = mp_init_multi(&x, &y, &u, &v, &B, &D, NULL)) != MP_OKAY) { + return res; + } + + /* x == modulus, y == value to invert */ + if ((res = mp_copy (b, &x)) != MP_OKAY) { + goto LBL_ERR; + } + + /* we need y = |a| */ + if ((res = mp_mod (a, b, &y)) != MP_OKAY) { + goto LBL_ERR; + } + + /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ + if ((res = mp_copy (&x, &u)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy (&y, &v)) != MP_OKAY) { + goto LBL_ERR; + } + mp_set (&D, 1); + +top: + /* 4. while u is even do */ + while (mp_iseven (&u) == 1) { + /* 4.1 u = u/2 */ + if ((res = mp_div_2 (&u, &u)) != MP_OKAY) { + goto LBL_ERR; + } + /* 4.2 if B is odd then */ + if (mp_isodd (&B) == 1) { + if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* B = B/2 */ + if ((res = mp_div_2 (&B, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 5. while v is even do */ + while (mp_iseven (&v) == 1) { + /* 5.1 v = v/2 */ + if ((res = mp_div_2 (&v, &v)) != MP_OKAY) { + goto LBL_ERR; + } + /* 5.2 if D is odd then */ + if (mp_isodd (&D) == 1) { + /* D = (D-x)/2 */ + if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* D = D/2 */ + if ((res = mp_div_2 (&D, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 6. if u >= v then */ + if (mp_cmp (&u, &v) != MP_LT) { + /* u = u - v, B = B - D */ + if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } else { + /* v - v - u, D = D - B */ + if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* if not zero goto step 4 */ + if (mp_iszero (&u) == 0) { + goto top; + } + + /* now a = C, b = D, gcd == g*v */ + + /* if v != 1 then there is no inverse */ + if (mp_cmp_d (&v, 1) != MP_EQ) { + res = MP_VAL; + goto LBL_ERR; + } + + /* b is now the inverse */ + neg = a->sign; + while (D.sign == MP_NEG) { + if ((res = mp_add (&D, b, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + mp_exch (&D, c); + c->sign = neg; + res = MP_OKAY; + +LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &B, &D, NULL); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_fast_mp_invmod.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_fast_mp_invmod.c */ + +/* Start: bn_fast_mp_montgomery_reduce.c */ +#include "libtorrent/tommath.h" +#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* computes xR**-1 == x (mod N) via Montgomery Reduction + * + * This is an optimized implementation of montgomery_reduce + * which uses the comba method to quickly calculate the columns of the + * reduction. + * + * Based on Algorithm 14.32 on pp.601 of HAC. +*/ +int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) +{ + int ix, res, olduse; + mp_word W[MP_WARRAY]; + + /* get old used count */ + olduse = x->used; + + /* grow a as required */ + if (x->alloc < n->used + 1) { + if ((res = mp_grow (x, n->used + 1)) != MP_OKAY) { + return res; + } + } + + /* first we have to get the digits of the input into + * an array of double precision words W[...] + */ + { + register mp_word *_W; + register mp_digit *tmpx; + + /* alias for the W[] array */ + _W = W; + + /* alias for the digits of x*/ + tmpx = x->dp; + + /* copy the digits of a into W[0..a->used-1] */ + for (ix = 0; ix < x->used; ix++) { + *_W++ = *tmpx++; + } + + /* zero the high words of W[a->used..m->used*2] */ + for (; ix < n->used * 2 + 1; ix++) { + *_W++ = 0; + } + } + + /* now we proceed to zero successive digits + * from the least significant upwards + */ + for (ix = 0; ix < n->used; ix++) { + /* mu = ai * m' mod b + * + * We avoid a double precision multiplication (which isn't required) + * by casting the value down to a mp_digit. Note this requires + * that W[ix-1] have the carry cleared (see after the inner loop) + */ + register mp_digit mu; + mu = (mp_digit) (((W[ix] & MP_MASK) * rho) & MP_MASK); + + /* a = a + mu * m * b**i + * + * This is computed in place and on the fly. The multiplication + * by b**i is handled by offseting which columns the results + * are added to. + * + * Note the comba method normally doesn't handle carries in the + * inner loop In this case we fix the carry from the previous + * column since the Montgomery reduction requires digits of the + * result (so far) [see above] to work. This is + * handled by fixing up one carry after the inner loop. The + * carry fixups are done in order so after these loops the + * first m->used words of W[] have the carries fixed + */ + { + register int iy; + register mp_digit *tmpn; + register mp_word *_W; + + /* alias for the digits of the modulus */ + tmpn = n->dp; + + /* Alias for the columns set by an offset of ix */ + _W = W + ix; + + /* inner loop */ + for (iy = 0; iy < n->used; iy++) { + *_W++ += ((mp_word)mu) * ((mp_word)*tmpn++); + } + } + + /* now fix carry for next digit, W[ix+1] */ + W[ix + 1] += W[ix] >> ((mp_word) DIGIT_BIT); + } + + /* now we have to propagate the carries and + * shift the words downward [all those least + * significant digits we zeroed]. + */ + { + register mp_digit *tmpx; + register mp_word *_W, *_W1; + + /* nox fix rest of carries */ + + /* alias for current word */ + _W1 = W + ix; + + /* alias for next word, where the carry goes */ + _W = W + ++ix; + + for (; ix <= n->used * 2 + 1; ix++) { + *_W++ += *_W1++ >> ((mp_word) DIGIT_BIT); + } + + /* copy out, A = A/b**n + * + * The result is A/b**n but instead of converting from an + * array of mp_word to mp_digit than calling mp_rshd + * we just copy them in the right order + */ + + /* alias for destination word */ + tmpx = x->dp; + + /* alias for shifted double precision result */ + _W = W + n->used; + + for (ix = 0; ix < n->used + 1; ix++) { + *tmpx++ = (mp_digit)(*_W++ & ((mp_word) MP_MASK)); + } + + /* zero oldused digits, if the input a was larger than + * m->used+1 we'll have to clear the digits + */ + for (; ix < olduse; ix++) { + *tmpx++ = 0; + } + } + + /* set the max used and clamp */ + x->used = n->used + 1; + mp_clamp (x); + + /* if A >= m then A = A - m */ + if (mp_cmp_mag (x, n) != MP_LT) { + return s_mp_sub (x, n, x); + } + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_fast_mp_montgomery_reduce.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_fast_mp_montgomery_reduce.c */ + +/* Start: bn_fast_s_mp_mul_digs.c */ +#include "libtorrent/tommath.h" +#ifdef BN_FAST_S_MP_MUL_DIGS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* Fast (comba) multiplier + * + * This is the fast column-array [comba] multiplier. It is + * designed to compute the columns of the product first + * then handle the carries afterwards. This has the effect + * of making the nested loops that compute the columns very + * simple and schedulable on super-scalar processors. + * + * This has been modified to produce a variable number of + * digits of output so if say only a half-product is required + * you don't have to compute the upper half (a feature + * required for fast Barrett reduction). + * + * Based on Algorithm 14.12 on pp.595 of HAC. + * + */ +int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY]; + register mp_word _W; + + /* grow the destination as required */ + if (c->alloc < digs) { + if ((res = mp_grow (c, digs)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + pa = MIN(digs, a->used + b->used); + + /* clear the carry */ + _W = 0; + for (ix = 0; ix < pa; ix++) { + int tx, ty; + int iy; + mp_digit *tmpx, *tmpy; + + /* get offsets into the two bignums */ + ty = MIN(b->used-1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = b->dp + ty; + + /* this is the number of times the loop will iterrate, essentially + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used-tx, ty+1); + + /* execute loop */ + for (iz = 0; iz < iy; ++iz) { + _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); + + } + + /* store term */ + W[ix] = (mp_digit)(_W & MP_MASK); + + /* make next carry */ + _W = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = c->used; + c->used = pa; + + { + register mp_digit *tmpc; + tmpc = c->dp; + for (ix = 0; ix < pa+1; ix++) { + /* now extract the previous digit [below the carry] */ + *tmpc++ = W[ix]; + } + + /* clear unused digits [that existed in the old copy of c] */ + for (; ix < olduse; ix++) { + *tmpc++ = 0; + } + } + mp_clamp (c); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_fast_s_mp_mul_digs.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_fast_s_mp_mul_digs.c */ + +/* Start: bn_fast_s_mp_mul_high_digs.c */ +#include "libtorrent/tommath.h" +#ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* this is a modified version of fast_s_mul_digs that only produces + * output digits *above* digs. See the comments for fast_s_mul_digs + * to see how it works. + * + * This is used in the Barrett reduction since for one of the multiplications + * only the higher digits were needed. This essentially halves the work. + * + * Based on Algorithm 14.12 on pp.595 of HAC. + */ +int fast_s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY]; + mp_word _W; + + /* grow the destination as required */ + pa = a->used + b->used; + if (c->alloc < pa) { + if ((res = mp_grow (c, pa)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + pa = a->used + b->used; + _W = 0; + for (ix = digs; ix < pa; ix++) { + int tx, ty, iy; + mp_digit *tmpx, *tmpy; + + /* get offsets into the two bignums */ + ty = MIN(b->used-1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = b->dp + ty; + + /* this is the number of times the loop will iterrate, essentially its + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used-tx, ty+1); + + /* execute loop */ + for (iz = 0; iz < iy; iz++) { + _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); + } + + /* store term */ + W[ix] = ((mp_digit)_W) & MP_MASK; + + /* make next carry */ + _W = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = c->used; + c->used = pa; + + { + register mp_digit *tmpc; + + tmpc = c->dp + digs; + for (ix = digs; ix <= pa; ix++) { + /* now extract the previous digit [below the carry] */ + *tmpc++ = W[ix]; + } + + /* clear unused digits [that existed in the old copy of c] */ + for (; ix < olduse; ix++) { + *tmpc++ = 0; + } + } + mp_clamp (c); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_fast_s_mp_mul_high_digs.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_fast_s_mp_mul_high_digs.c */ + +/* Start: bn_fast_s_mp_sqr.c */ +#include "libtorrent/tommath.h" +#ifdef BN_FAST_S_MP_SQR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* the jist of squaring... + * you do like mult except the offset of the tmpx [one that + * starts closer to zero] can't equal the offset of tmpy. + * So basically you set up iy like before then you min it with + * (ty-tx) so that it never happens. You double all those + * you add in the inner loop + +After that loop you do the squares and add them in. +*/ + +int fast_s_mp_sqr (mp_int * a, mp_int * b) +{ + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY], *tmpx; + mp_word W1; + + /* grow the destination as required */ + pa = a->used + a->used; + if (b->alloc < pa) { + if ((res = mp_grow (b, pa)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + W1 = 0; + for (ix = 0; ix < pa; ix++) { + int tx, ty, iy; + mp_word _W; + mp_digit *tmpy; + + /* clear counter */ + _W = 0; + + /* get offsets into the two bignums */ + ty = MIN(a->used-1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = a->dp + ty; + + /* this is the number of times the loop will iterrate, essentially + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used-tx, ty+1); + + /* now for squaring tx can never equal ty + * we halve the distance since they approach at a rate of 2x + * and we have to round because odd cases need to be executed + */ + iy = MIN(iy, (ty-tx+1)>>1); + + /* execute loop */ + for (iz = 0; iz < iy; iz++) { + _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); + } + + /* double the inner product and add carry */ + _W = _W + _W + W1; + + /* even columns have the square term in them */ + if ((ix&1) == 0) { + _W += ((mp_word)a->dp[ix>>1])*((mp_word)a->dp[ix>>1]); + } + + /* store it */ + W[ix] = (mp_digit)(_W & MP_MASK); + + /* make next carry */ + W1 = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = b->used; + b->used = a->used+a->used; + + { + mp_digit *tmpb; + tmpb = b->dp; + for (ix = 0; ix < pa; ix++) { + *tmpb++ = W[ix] & MP_MASK; + } + + /* clear unused digits [that existed in the old copy of c] */ + for (; ix < olduse; ix++) { + *tmpb++ = 0; + } + } + mp_clamp (b); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_fast_s_mp_sqr.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_fast_s_mp_sqr.c */ + +/* Start: bn_mp_2expt.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_2EXPT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* computes a = 2**b + * + * Simple algorithm which zeroes the int, grows it then just sets one bit + * as required. + */ +int +mp_2expt (mp_int * a, int b) +{ + int res; + + /* zero a as per default */ + mp_zero (a); + + /* grow a to accomodate the single bit */ + if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) { + return res; + } + + /* set the used count of where the bit will go */ + a->used = b / DIGIT_BIT + 1; + + /* put the single bit in its place */ + a->dp[b / DIGIT_BIT] = ((mp_digit)1) << (b % DIGIT_BIT); + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_2expt.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_2expt.c */ + +/* Start: bn_mp_abs.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_ABS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* b = |a| + * + * Simple function copies the input and fixes the sign to positive + */ +int +mp_abs (mp_int * a, mp_int * b) +{ + int res; + + /* copy a to b */ + if (a != b) { + if ((res = mp_copy (a, b)) != MP_OKAY) { + return res; + } + } + + /* force the sign of b to positive */ + b->sign = MP_ZPOS; + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_abs.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_abs.c */ + +/* Start: bn_mp_add.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_ADD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* high level addition (handles signs) */ +int mp_add (mp_int * a, mp_int * b, mp_int * c) +{ + int sa, sb, res; + + /* get sign of both inputs */ + sa = a->sign; + sb = b->sign; + + /* handle two cases, not four */ + if (sa == sb) { + /* both positive or both negative */ + /* add their magnitudes, copy the sign */ + c->sign = sa; + res = s_mp_add (a, b, c); + } else { + /* one positive, the other negative */ + /* subtract the one with the greater magnitude from */ + /* the one of the lesser magnitude. The result gets */ + /* the sign of the one with the greater magnitude. */ + if (mp_cmp_mag (a, b) == MP_LT) { + c->sign = sb; + res = s_mp_sub (b, a, c); + } else { + c->sign = sa; + res = s_mp_sub (a, b, c); + } + } + return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_add.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_add.c */ + +/* Start: bn_mp_add_d.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_ADD_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* single digit addition */ +int +mp_add_d (mp_int * a, mp_digit b, mp_int * c) +{ + int res, ix, oldused; + mp_digit *tmpa, *tmpc, mu; + + /* grow c as required */ + if (c->alloc < a->used + 1) { + if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* if a is negative and |a| >= b, call c = |a| - b */ + if (a->sign == MP_NEG && (a->used > 1 || a->dp[0] >= b)) { + /* temporarily fix sign of a */ + a->sign = MP_ZPOS; + + /* c = |a| - b */ + res = mp_sub_d(a, b, c); + + /* fix sign */ + a->sign = c->sign = MP_NEG; + + /* clamp */ + mp_clamp(c); + + return res; + } + + /* old number of used digits in c */ + oldused = c->used; + + /* sign always positive */ + c->sign = MP_ZPOS; + + /* source alias */ + tmpa = a->dp; + + /* destination alias */ + tmpc = c->dp; + + /* if a is positive */ + if (a->sign == MP_ZPOS) { + /* add digit, after this we're propagating + * the carry. + */ + *tmpc = *tmpa++ + b; + mu = *tmpc >> DIGIT_BIT; + *tmpc++ &= MP_MASK; + + /* now handle rest of the digits */ + for (ix = 1; ix < a->used; ix++) { + *tmpc = *tmpa++ + mu; + mu = *tmpc >> DIGIT_BIT; + *tmpc++ &= MP_MASK; + } + /* set final carry */ + ix++; + *tmpc++ = mu; + + /* setup size */ + c->used = a->used + 1; + } else { + /* a was negative and |a| < b */ + c->used = 1; + + /* the result is a single digit */ + if (a->used == 1) { + *tmpc++ = b - a->dp[0]; + } else { + *tmpc++ = b; + } + + /* setup count so the clearing of oldused + * can fall through correctly + */ + ix = 1; + } + + /* now zero to oldused */ + while (ix++ < oldused) { + *tmpc++ = 0; + } + mp_clamp(c); + + return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_add_d.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_add_d.c */ + +/* Start: bn_mp_addmod.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_ADDMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* d = a + b (mod c) */ +int +mp_addmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + int res; + mp_int t; + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_add (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + res = mp_mod (&t, c, d); + mp_clear (&t); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_addmod.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_addmod.c */ + +/* Start: bn_mp_and.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_AND_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* AND two ints together */ +int +mp_and (mp_int * a, mp_int * b, mp_int * c) +{ + int res, ix, px; + mp_int t, *x; + + if (a->used > b->used) { + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + px = b->used; + x = b; + } else { + if ((res = mp_init_copy (&t, b)) != MP_OKAY) { + return res; + } + px = a->used; + x = a; + } + + for (ix = 0; ix < px; ix++) { + t.dp[ix] &= x->dp[ix]; + } + + /* zero digits above the last from the smallest mp_int */ + for (; ix < t.used; ix++) { + t.dp[ix] = 0; + } + + mp_clamp (&t); + mp_exch (c, &t); + mp_clear (&t); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_and.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_and.c */ + +/* Start: bn_mp_clamp.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_CLAMP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* trim unused digits + * + * This is used to ensure that leading zero digits are + * trimed and the leading "used" digit will be non-zero + * Typically very fast. Also fixes the sign if there + * are no more leading digits + */ +void +mp_clamp (mp_int * a) +{ + /* decrease used while the most significant digit is + * zero. + */ + while (a->used > 0 && a->dp[a->used - 1] == 0) { + --(a->used); + } + + /* reset the sign flag if used == 0 */ + if (a->used == 0) { + a->sign = MP_ZPOS; + } +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_clamp.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_clamp.c */ + +/* Start: bn_mp_clear.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_CLEAR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* clear one (frees) */ +void +mp_clear (mp_int * a) +{ + int i; + + /* only do anything if a hasn't been freed previously */ + if (a->dp != NULL) { + /* first zero the digits */ + for (i = 0; i < a->used; i++) { + a->dp[i] = 0; + } + + /* free ram */ + XFREE(a->dp); + + /* reset members to make debugging easier */ + a->dp = NULL; + a->alloc = a->used = 0; + a->sign = MP_ZPOS; + } +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_clear.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_clear.c */ + +/* Start: bn_mp_clear_multi.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_CLEAR_MULTI_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ +#include + +void mp_clear_multi(mp_int *mp, ...) +{ + mp_int* next_mp = mp; + va_list args; + va_start(args, mp); + while (next_mp != NULL) { + mp_clear(next_mp); + next_mp = va_arg(args, mp_int*); + } + va_end(args); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_clear_multi.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_clear_multi.c */ + +/* Start: bn_mp_cmp.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_CMP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* compare two ints (signed)*/ +int +mp_cmp (mp_int * a, mp_int * b) +{ + /* compare based on sign */ + if (a->sign != b->sign) { + if (a->sign == MP_NEG) { + return MP_LT; + } else { + return MP_GT; + } + } + + /* compare digits */ + if (a->sign == MP_NEG) { + /* if negative compare opposite direction */ + return mp_cmp_mag(b, a); + } else { + return mp_cmp_mag(a, b); + } +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_cmp.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_cmp.c */ + +/* Start: bn_mp_cmp_d.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_CMP_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* compare a digit */ +int mp_cmp_d(mp_int * a, mp_digit b) +{ + /* compare based on sign */ + if (a->sign == MP_NEG) { + return MP_LT; + } + + /* compare based on magnitude */ + if (a->used > 1) { + return MP_GT; + } + + /* compare the only digit of a to b */ + if (a->dp[0] > b) { + return MP_GT; + } else if (a->dp[0] < b) { + return MP_LT; + } else { + return MP_EQ; + } +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_cmp_d.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_cmp_d.c */ + +/* Start: bn_mp_cmp_mag.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_CMP_MAG_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* compare maginitude of two ints (unsigned) */ +int mp_cmp_mag (mp_int * a, mp_int * b) +{ + int n; + mp_digit *tmpa, *tmpb; + + /* compare based on # of non-zero digits */ + if (a->used > b->used) { + return MP_GT; + } + + if (a->used < b->used) { + return MP_LT; + } + + /* alias for a */ + tmpa = a->dp + (a->used - 1); + + /* alias for b */ + tmpb = b->dp + (a->used - 1); + + /* compare based on digits */ + for (n = 0; n < a->used; ++n, --tmpa, --tmpb) { + if (*tmpa > *tmpb) { + return MP_GT; + } + + if (*tmpa < *tmpb) { + return MP_LT; + } + } + return MP_EQ; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_cmp_mag.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_cmp_mag.c */ + +/* Start: bn_mp_cnt_lsb.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_CNT_LSB_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +static const int lnz[16] = { + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 +}; + +/* Counts the number of lsbs which are zero before the first zero bit */ +int mp_cnt_lsb(mp_int *a) +{ + int x; + mp_digit q, qq; + + /* easy out */ + if (mp_iszero(a) == 1) { + return 0; + } + + /* scan lower digits until non-zero */ + for (x = 0; x < a->used && a->dp[x] == 0; x++); + q = a->dp[x]; + x *= DIGIT_BIT; + + /* now scan this digit until a 1 is found */ + if ((q & 1) == 0) { + do { + qq = q & 15; + x += lnz[qq]; + q >>= 4; + } while (qq == 0); + } + return x; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_cnt_lsb.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_cnt_lsb.c */ + +/* Start: bn_mp_copy.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_COPY_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* copy, b = a */ +int +mp_copy (mp_int * a, mp_int * b) +{ + int res, n; + + /* if dst == src do nothing */ + if (a == b) { + return MP_OKAY; + } + + /* grow dest */ + if (b->alloc < a->used) { + if ((res = mp_grow (b, a->used)) != MP_OKAY) { + return res; + } + } + + /* zero b and copy the parameters over */ + { + register mp_digit *tmpa, *tmpb; + + /* pointer aliases */ + + /* source */ + tmpa = a->dp; + + /* destination */ + tmpb = b->dp; + + /* copy all the digits */ + for (n = 0; n < a->used; n++) { + *tmpb++ = *tmpa++; + } + + /* clear high digits */ + for (; n < b->used; n++) { + *tmpb++ = 0; + } + } + + /* copy used count and sign */ + b->used = a->used; + b->sign = a->sign; + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_copy.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_copy.c */ + +/* Start: bn_mp_count_bits.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_COUNT_BITS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* returns the number of bits in an int */ +int +mp_count_bits (mp_int * a) +{ + int r; + mp_digit q; + + /* shortcut */ + if (a->used == 0) { + return 0; + } + + /* get number of digits and add that */ + r = (a->used - 1) * DIGIT_BIT; + + /* take the last digit and count the bits in it */ + q = a->dp[a->used - 1]; + while (q > ((mp_digit) 0)) { + ++r; + q >>= ((mp_digit) 1); + } + return r; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_count_bits.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_count_bits.c */ + +/* Start: bn_mp_div.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_DIV_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +#ifdef BN_MP_DIV_SMALL + +/* slower bit-bang division... also smaller */ +int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + mp_int ta, tb, tq, q; + int res, n, n2; + + /* is divisor zero ? */ + if (mp_iszero (b) == 1) { + return MP_VAL; + } + + /* if a < b then q=0, r = a */ + if (mp_cmp_mag (a, b) == MP_LT) { + if (d != NULL) { + res = mp_copy (a, d); + } else { + res = MP_OKAY; + } + if (c != NULL) { + mp_zero (c); + } + return res; + } + + /* init our temps */ + if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL) != MP_OKAY)) { + return res; + } + + + mp_set(&tq, 1); + n = mp_count_bits(a) - mp_count_bits(b); + if (((res = mp_abs(a, &ta)) != MP_OKAY) || + ((res = mp_abs(b, &tb)) != MP_OKAY) || + ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) || + ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) { + goto LBL_ERR; + } + + while (n-- >= 0) { + if (mp_cmp(&tb, &ta) != MP_GT) { + if (((res = mp_sub(&ta, &tb, &ta)) != MP_OKAY) || + ((res = mp_add(&q, &tq, &q)) != MP_OKAY)) { + goto LBL_ERR; + } + } + if (((res = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) || + ((res = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY)) { + goto LBL_ERR; + } + } + + /* now q == quotient and ta == remainder */ + n = a->sign; + n2 = (a->sign == b->sign ? MP_ZPOS : MP_NEG); + if (c != NULL) { + mp_exch(c, &q); + c->sign = (mp_iszero(c) == MP_YES) ? MP_ZPOS : n2; + } + if (d != NULL) { + mp_exch(d, &ta); + d->sign = (mp_iszero(d) == MP_YES) ? MP_ZPOS : n; + } +LBL_ERR: + mp_clear_multi(&ta, &tb, &tq, &q, NULL); + return res; +} + +#else + +/* integer signed division. + * c*b + d == a [e.g. a/b, c=quotient, d=remainder] + * HAC pp.598 Algorithm 14.20 + * + * Note that the description in HAC is horribly + * incomplete. For example, it doesn't consider + * the case where digits are removed from 'x' in + * the inner loop. It also doesn't consider the + * case that y has fewer than three digits, etc.. + * + * The overall algorithm is as described as + * 14.20 from HAC but fixed to treat these cases. +*/ +int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + mp_int q, x, y, t1, t2; + int res, n, t, i, norm, neg; + + /* is divisor zero ? */ + if (mp_iszero (b) == 1) { + return MP_VAL; + } + + /* if a < b then q=0, r = a */ + if (mp_cmp_mag (a, b) == MP_LT) { + if (d != NULL) { + res = mp_copy (a, d); + } else { + res = MP_OKAY; + } + if (c != NULL) { + mp_zero (c); + } + return res; + } + + if ((res = mp_init_size (&q, a->used + 2)) != MP_OKAY) { + return res; + } + q.used = a->used + 2; + + if ((res = mp_init (&t1)) != MP_OKAY) { + goto LBL_Q; + } + + if ((res = mp_init (&t2)) != MP_OKAY) { + goto LBL_T1; + } + + if ((res = mp_init_copy (&x, a)) != MP_OKAY) { + goto LBL_T2; + } + + if ((res = mp_init_copy (&y, b)) != MP_OKAY) { + goto LBL_X; + } + + /* fix the sign */ + neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + x.sign = y.sign = MP_ZPOS; + + /* normalize both x and y, ensure that y >= b/2, [b == 2**DIGIT_BIT] */ + norm = mp_count_bits(&y) % DIGIT_BIT; + if (norm < (int)(DIGIT_BIT-1)) { + norm = (DIGIT_BIT-1) - norm; + if ((res = mp_mul_2d (&x, norm, &x)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_mul_2d (&y, norm, &y)) != MP_OKAY) { + goto LBL_Y; + } + } else { + norm = 0; + } + + /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */ + n = x.used - 1; + t = y.used - 1; + + /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */ + if ((res = mp_lshd (&y, n - t)) != MP_OKAY) { /* y = y*b**{n-t} */ + goto LBL_Y; + } + + while (mp_cmp (&x, &y) != MP_LT) { + ++(q.dp[n - t]); + if ((res = mp_sub (&x, &y, &x)) != MP_OKAY) { + goto LBL_Y; + } + } + + /* reset y by shifting it back down */ + mp_rshd (&y, n - t); + + /* step 3. for i from n down to (t + 1) */ + for (i = n; i >= (t + 1); i--) { + if (i > x.used) { + continue; + } + + /* step 3.1 if xi == yt then set q{i-t-1} to b-1, + * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */ + if (x.dp[i] == y.dp[t]) { + q.dp[i - t - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1); + } else { + mp_word tmp; + tmp = ((mp_word) x.dp[i]) << ((mp_word) DIGIT_BIT); + tmp |= ((mp_word) x.dp[i - 1]); + tmp /= ((mp_word) y.dp[t]); + if (tmp > (mp_word) MP_MASK) + tmp = MP_MASK; + q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK)); + } + + /* while (q{i-t-1} * (yt * b + y{t-1})) > + xi * b**2 + xi-1 * b + xi-2 + + do q{i-t-1} -= 1; + */ + q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK; + do { + q.dp[i - t - 1] = (q.dp[i - t - 1] - 1) & MP_MASK; + + /* find left hand */ + mp_zero (&t1); + t1.dp[0] = (t - 1 < 0) ? 0 : y.dp[t - 1]; + t1.dp[1] = y.dp[t]; + t1.used = 2; + if ((res = mp_mul_d (&t1, q.dp[i - t - 1], &t1)) != MP_OKAY) { + goto LBL_Y; + } + + /* find right hand */ + t2.dp[0] = (i - 2 < 0) ? 0 : x.dp[i - 2]; + t2.dp[1] = (i - 1 < 0) ? 0 : x.dp[i - 1]; + t2.dp[2] = x.dp[i]; + t2.used = 3; + } while (mp_cmp_mag(&t1, &t2) == MP_GT); + + /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */ + if ((res = mp_mul_d (&y, q.dp[i - t - 1], &t1)) != MP_OKAY) { + goto LBL_Y; + } + + if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { + goto LBL_Y; + } + + if ((res = mp_sub (&x, &t1, &x)) != MP_OKAY) { + goto LBL_Y; + } + + /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */ + if (x.sign == MP_NEG) { + if ((res = mp_copy (&y, &t1)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_add (&x, &t1, &x)) != MP_OKAY) { + goto LBL_Y; + } + + q.dp[i - t - 1] = (q.dp[i - t - 1] - 1UL) & MP_MASK; + } + } + + /* now q is the quotient and x is the remainder + * [which we have to normalize] + */ + + /* get sign before writing to c */ + x.sign = x.used == 0 ? MP_ZPOS : a->sign; + + if (c != NULL) { + mp_clamp (&q); + mp_exch (&q, c); + c->sign = neg; + } + + if (d != NULL) { + mp_div_2d (&x, norm, &x, NULL); + mp_exch (&x, d); + } + + res = MP_OKAY; + +LBL_Y:mp_clear (&y); +LBL_X:mp_clear (&x); +LBL_T2:mp_clear (&t2); +LBL_T1:mp_clear (&t1); +LBL_Q:mp_clear (&q); + return res; +} + +#endif + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_div.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_div.c */ + +/* Start: bn_mp_div_2.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_DIV_2_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* b = a/2 */ +int mp_div_2(mp_int * a, mp_int * b) +{ + int x, res, oldused; + + /* copy */ + if (b->alloc < a->used) { + if ((res = mp_grow (b, a->used)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + { + register mp_digit r, rr, *tmpa, *tmpb; + + /* source alias */ + tmpa = a->dp + b->used - 1; + + /* dest alias */ + tmpb = b->dp + b->used - 1; + + /* carry */ + r = 0; + for (x = b->used - 1; x >= 0; x--) { + /* get the carry for the next iteration */ + rr = *tmpa & 1; + + /* shift the current digit, add in carry and store */ + *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1)); + + /* forward carry to next iteration */ + r = rr; + } + + /* zero excess digits */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + mp_clamp (b); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_div_2.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_div_2.c */ + +/* Start: bn_mp_div_2d.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_DIV_2D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* shift right by a certain bit count (store quotient in c, optional remainder in d) */ +int mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d) +{ + mp_digit D, r, rr; + int x, res; + mp_int t; + + + /* if the shift count is <= 0 then we do no work */ + if (b <= 0) { + res = mp_copy (a, c); + if (d != NULL) { + mp_zero (d); + } + return res; + } + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + /* get the remainder */ + if (d != NULL) { + if ((res = mp_mod_2d (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + } + + /* copy */ + if ((res = mp_copy (a, c)) != MP_OKAY) { + mp_clear (&t); + return res; + } + + /* shift by as many digits in the bit count */ + if (b >= (int)DIGIT_BIT) { + mp_rshd (c, b / DIGIT_BIT); + } + + /* shift any bit count < DIGIT_BIT */ + D = (mp_digit) (b % DIGIT_BIT); + if (D != 0) { + register mp_digit *tmpc, mask, shift; + + /* mask */ + mask = (((mp_digit)1) << D) - 1; + + /* shift for lsb */ + shift = DIGIT_BIT - D; + + /* alias */ + tmpc = c->dp + (c->used - 1); + + /* carry */ + r = 0; + for (x = c->used - 1; x >= 0; x--) { + /* get the lower bits of this word in a temp */ + rr = *tmpc & mask; + + /* shift the current word and mix in the carry bits from the previous word */ + *tmpc = (*tmpc >> D) | (r << shift); + --tmpc; + + /* set the carry to the carry bits of the current word found above */ + r = rr; + } + } + mp_clamp (c); + if (d != NULL) { + mp_exch (&t, d); + } + mp_clear (&t); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_div_2d.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_div_2d.c */ + +/* Start: bn_mp_div_3.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_DIV_3_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* divide by three (based on routine from MPI and the GMP manual) */ +int +mp_div_3 (mp_int * a, mp_int *c, mp_digit * d) +{ + mp_int q; + mp_word w, t; + mp_digit b; + int res, ix; + + /* b = 2**DIGIT_BIT / 3 */ + b = (((mp_word)1) << ((mp_word)DIGIT_BIT)) / ((mp_word)3); + + if ((res = mp_init_size(&q, a->used)) != MP_OKAY) { + return res; + } + + q.used = a->used; + q.sign = a->sign; + w = 0; + for (ix = a->used - 1; ix >= 0; ix--) { + w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]); + + if (w >= 3) { + /* multiply w by [1/3] */ + t = (w * ((mp_word)b)) >> ((mp_word)DIGIT_BIT); + + /* now subtract 3 * [w/3] from w, to get the remainder */ + w -= t+t+t; + + /* fixup the remainder as required since + * the optimization is not exact. + */ + while (w >= 3) { + t += 1; + w -= 3; + } + } else { + t = 0; + } + q.dp[ix] = (mp_digit)t; + } + + /* [optional] store the remainder */ + if (d != NULL) { + *d = (mp_digit)w; + } + + /* [optional] store the quotient */ + if (c != NULL) { + mp_clamp(&q); + mp_exch(&q, c); + } + mp_clear(&q); + + return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_div_3.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_div_3.c */ + +/* Start: bn_mp_div_d.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_DIV_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +static int s_is_power_of_two(mp_digit b, int *p) +{ + int x; + + for (x = 1; x < DIGIT_BIT; x++) { + if (b == (((mp_digit)1)<dp[0] & ((((mp_digit)1)<used)) != MP_OKAY) { + return res; + } + + q.used = a->used; + q.sign = a->sign; + w = 0; + for (ix = a->used - 1; ix >= 0; ix--) { + w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]); + + if (w >= b) { + t = (mp_digit)(w / b); + w -= ((mp_word)t) * ((mp_word)b); + } else { + t = 0; + } + q.dp[ix] = (mp_digit)t; + } + + if (d != NULL) { + *d = (mp_digit)w; + } + + if (c != NULL) { + mp_clamp(&q); + mp_exch(&q, c); + } + mp_clear(&q); + + return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_div_d.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_div_d.c */ + +/* Start: bn_mp_dr_is_modulus.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_DR_IS_MODULUS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* determines if a number is a valid DR modulus */ +int mp_dr_is_modulus(mp_int *a) +{ + int ix; + + /* must be at least two digits */ + if (a->used < 2) { + return 0; + } + + /* must be of the form b**k - a [a <= b] so all + * but the first digit must be equal to -1 (mod b). + */ + for (ix = 1; ix < a->used; ix++) { + if (a->dp[ix] != MP_MASK) { + return 0; + } + } + return 1; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_dr_is_modulus.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_dr_is_modulus.c */ + +/* Start: bn_mp_dr_reduce.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_DR_REDUCE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* reduce "x" in place modulo "n" using the Diminished Radix algorithm. + * + * Based on algorithm from the paper + * + * "Generating Efficient Primes for Discrete Log Cryptosystems" + * Chae Hoon Lim, Pil Joong Lee, + * POSTECH Information Research Laboratories + * + * The modulus must be of a special format [see manual] + * + * Has been modified to use algorithm 7.10 from the LTM book instead + * + * Input x must be in the range 0 <= x <= (n-1)**2 + */ +int +mp_dr_reduce (mp_int * x, mp_int * n, mp_digit k) +{ + int err, i, m; + mp_word r; + mp_digit mu, *tmpx1, *tmpx2; + + /* m = digits in modulus */ + m = n->used; + + /* ensure that "x" has at least 2m digits */ + if (x->alloc < m + m) { + if ((err = mp_grow (x, m + m)) != MP_OKAY) { + return err; + } + } + +/* top of loop, this is where the code resumes if + * another reduction pass is required. + */ +top: + /* aliases for digits */ + /* alias for lower half of x */ + tmpx1 = x->dp; + + /* alias for upper half of x, or x/B**m */ + tmpx2 = x->dp + m; + + /* set carry to zero */ + mu = 0; + + /* compute (x mod B**m) + k * [x/B**m] inline and inplace */ + for (i = 0; i < m; i++) { + r = ((mp_word)*tmpx2++) * ((mp_word)k) + *tmpx1 + mu; + *tmpx1++ = (mp_digit)(r & MP_MASK); + mu = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); + } + + /* set final carry */ + *tmpx1++ = mu; + + /* zero words above m */ + for (i = m + 1; i < x->used; i++) { + *tmpx1++ = 0; + } + + /* clamp, sub and return */ + mp_clamp (x); + + /* if x >= n then subtract and reduce again + * Each successive "recursion" makes the input smaller and smaller. + */ + if (mp_cmp_mag (x, n) != MP_LT) { + s_mp_sub(x, n, x); + goto top; + } + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_dr_reduce.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_dr_reduce.c */ + +/* Start: bn_mp_dr_setup.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_DR_SETUP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* determines the setup value */ +void mp_dr_setup(mp_int *a, mp_digit *d) +{ + /* the casts are required if DIGIT_BIT is one less than + * the number of bits in a mp_digit [e.g. DIGIT_BIT==31] + */ + *d = (mp_digit)((((mp_word)1) << ((mp_word)DIGIT_BIT)) - + ((mp_word)a->dp[0])); +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_dr_setup.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_dr_setup.c */ + +/* Start: bn_mp_exch.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_EXCH_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* swap the elements of two integers, for cases where you can't simply swap the + * mp_int pointers around + */ +void +mp_exch (mp_int * a, mp_int * b) +{ + mp_int t; + + t = *a; + *a = *b; + *b = t; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_exch.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_exch.c */ + +/* Start: bn_mp_expt_d.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_EXPT_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* calculate c = a**b using a square-multiply algorithm */ +int mp_expt_d (mp_int * a, mp_digit b, mp_int * c) +{ + int res, x; + mp_int g; + + if ((res = mp_init_copy (&g, a)) != MP_OKAY) { + return res; + } + + /* set initial result */ + mp_set (c, 1); + + for (x = 0; x < (int) DIGIT_BIT; x++) { + /* square */ + if ((res = mp_sqr (c, c)) != MP_OKAY) { + mp_clear (&g); + return res; + } + + /* if the bit is set multiply */ + if ((b & (mp_digit) (((mp_digit)1) << (DIGIT_BIT - 1))) != 0) { + if ((res = mp_mul (c, &g, c)) != MP_OKAY) { + mp_clear (&g); + return res; + } + } + + /* shift to next bit */ + b <<= 1; + } + + mp_clear (&g); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_expt_d.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_expt_d.c */ + +/* Start: bn_mp_exptmod.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_EXPTMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + + +/* this is a shell function that calls either the normal or Montgomery + * exptmod functions. Originally the call to the montgomery code was + * embedded in the normal function but that wasted alot of stack space + * for nothing (since 99% of the time the Montgomery code would be called) + */ +int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) +{ + int dr; + + /* modulus P must be positive */ + if (P->sign == MP_NEG) { + return MP_VAL; + } + + /* if exponent X is negative we have to recurse */ + if (X->sign == MP_NEG) { +#ifdef BN_MP_INVMOD_C + mp_int tmpG, tmpX; + int err; + + /* first compute 1/G mod P */ + if ((err = mp_init(&tmpG)) != MP_OKAY) { + return err; + } + if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + + /* now get |X| */ + if ((err = mp_init(&tmpX)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + if ((err = mp_abs(X, &tmpX)) != MP_OKAY) { + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; + } + + /* and now compute (1/G)**|X| instead of G**X [X < 0] */ + err = mp_exptmod(&tmpG, &tmpX, P, Y); + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; +#else + /* no invmod */ + return MP_VAL; +#endif + } + +/* modified diminished radix reduction */ +#if defined(BN_MP_REDUCE_IS_2K_L_C) && defined(BN_MP_REDUCE_2K_L_C) && defined(BN_S_MP_EXPTMOD_C) + if (mp_reduce_is_2k_l(P) == MP_YES) { + return s_mp_exptmod(G, X, P, Y, 1); + } +#endif + +#ifdef BN_MP_DR_IS_MODULUS_C + /* is it a DR modulus? */ + dr = mp_dr_is_modulus(P); +#else + /* default to no */ + dr = 0; +#endif + +#ifdef BN_MP_REDUCE_IS_2K_C + /* if not, is it a unrestricted DR modulus? */ + if (dr == 0) { + dr = mp_reduce_is_2k(P) << 1; + } +#endif + + /* if the modulus is odd or dr != 0 use the montgomery method */ +#ifdef BN_MP_EXPTMOD_FAST_C + if (mp_isodd (P) == 1 || dr != 0) { + return mp_exptmod_fast (G, X, P, Y, dr); + } else { +#endif +#ifdef BN_S_MP_EXPTMOD_C + /* otherwise use the generic Barrett reduction technique */ + return s_mp_exptmod (G, X, P, Y, 0); +#else + /* no exptmod for evens */ + return MP_VAL; +#endif +#ifdef BN_MP_EXPTMOD_FAST_C + } +#endif +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_exptmod.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_exptmod.c */ + +/* Start: bn_mp_exptmod_fast.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_EXPTMOD_FAST_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* computes Y == G**X mod P, HAC pp.616, Algorithm 14.85 + * + * Uses a left-to-right k-ary sliding window to compute the modular exponentiation. + * The value of k changes based on the size of the exponent. + * + * Uses Montgomery or Diminished Radix reduction [whichever appropriate] + */ + +#ifdef MP_LOW_MEM + #define TAB_SIZE 32 +#else + #define TAB_SIZE 256 +#endif + +int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) +{ + mp_int M[TAB_SIZE], res; + mp_digit buf, mp; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + + /* use a pointer to the reduction algorithm. This allows us to use + * one of many reduction algorithms without modding the guts of + * the code with if statements everywhere. + */ + int (*redux)(mp_int*,mp_int*,mp_digit); + + /* find window size */ + x = mp_count_bits (X); + if (x <= 7) { + winsize = 2; + } else if (x <= 36) { + winsize = 3; + } else if (x <= 140) { + winsize = 4; + } else if (x <= 450) { + winsize = 5; + } else if (x <= 1303) { + winsize = 6; + } else if (x <= 3529) { + winsize = 7; + } else { + winsize = 8; + } + +#ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } +#endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1<<(winsize-1); y < x; y++) { + mp_clear (&M[y]); + } + mp_clear(&M[1]); + return err; + } + } + + /* determine and setup reduction code */ + if (redmode == 0) { +#ifdef BN_MP_MONTGOMERY_SETUP_C + /* now setup montgomery */ + if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) { + goto LBL_M; + } +#else + err = MP_VAL; + goto LBL_M; +#endif + + /* automatically pick the comba one if available (saves quite a few calls/ifs) */ +#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C + if (((P->used * 2 + 1) < MP_WARRAY) && + P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + redux = fast_mp_montgomery_reduce; + } else +#endif + { +#ifdef BN_MP_MONTGOMERY_REDUCE_C + /* use slower baseline Montgomery method */ + redux = mp_montgomery_reduce; +#else + err = MP_VAL; + goto LBL_M; +#endif + } + } else if (redmode == 1) { +#if defined(BN_MP_DR_SETUP_C) && defined(BN_MP_DR_REDUCE_C) + /* setup DR reduction for moduli of the form B**k - b */ + mp_dr_setup(P, &mp); + redux = mp_dr_reduce; +#else + err = MP_VAL; + goto LBL_M; +#endif + } else { +#if defined(BN_MP_REDUCE_2K_SETUP_C) && defined(BN_MP_REDUCE_2K_C) + /* setup DR reduction for moduli of the form 2**k - b */ + if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) { + goto LBL_M; + } + redux = mp_reduce_2k; +#else + err = MP_VAL; + goto LBL_M; +#endif + } + + /* setup result */ + if ((err = mp_init (&res)) != MP_OKAY) { + goto LBL_M; + } + + /* create M table + * + + * + * The first half of the table is not computed though accept for M[0] and M[1] + */ + + if (redmode == 0) { +#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C + /* now we need R mod m */ + if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) { + goto LBL_RES; + } +#else + err = MP_VAL; + goto LBL_RES; +#endif + + /* now set M[1] to G * R mod m */ + if ((err = mp_mulmod (G, &res, P, &M[1])) != MP_OKAY) { + goto LBL_RES; + } + } else { + mp_set(&res, 1); + if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) { + goto LBL_RES; + } + } + + /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */ + if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_RES; + } + + for (x = 0; x < (winsize - 1); x++) { + if ((err = mp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* create upper table */ + for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { + if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&M[x], P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for (;;) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits so break */ + if (digidx == -1) { + break; + } + /* read next digit and reset bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int)DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)1; + + /* if the bit is zero and mode == 0 then we ignore it + * These represent the leading zero bits before the first 1 bit + * in the exponent. Technically this opt is not required but it + * does lower the # of trivial squaring/reductions used + */ + if (mode == 0 && y == 0) { + continue; + } + + /* if the bit is zero and mode == 1 then we square */ + if (mode == 1 && y == 0) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (y << (winsize - ++bitcpy)); + mode = 2; + + if (bitcpy == winsize) { + /* ok window is filled so square as required and multiply */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* then multiply */ + if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + + /* empty window and reset */ + bitcpy = 0; + bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then square/multiply */ + if (mode == 2 && bitcpy > 0) { + /* square then multiply if the bit is set */ + for (x = 0; x < bitcpy; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + + /* get next bit of the window */ + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + } + } + + if (redmode == 0) { + /* fixup result if Montgomery reduction is used + * recall that any value in a Montgomery system is + * actually multiplied by R mod n. So we have + * to reduce one more time to cancel out the factor + * of R. + */ + if ((err = redux(&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* swap res with Y */ + mp_exch (&res, Y); + err = MP_OKAY; +LBL_RES:mp_clear (&res); +LBL_M: + mp_clear(&M[1]); + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + mp_clear (&M[x]); + } + return err; +} +#endif + + +/* $Source: /cvs/libtom/libtommath/bn_mp_exptmod_fast.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_exptmod_fast.c */ + +/* Start: bn_mp_exteuclid.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_EXTEUCLID_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* Extended euclidean algorithm of (a, b) produces + a*u1 + b*u2 = u3 + */ +int mp_exteuclid(mp_int *a, mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3) +{ + mp_int u1,u2,u3,v1,v2,v3,t1,t2,t3,q,tmp; + int err; + + if ((err = mp_init_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL)) != MP_OKAY) { + return err; + } + + /* initialize, (u1,u2,u3) = (1,0,a) */ + mp_set(&u1, 1); + if ((err = mp_copy(a, &u3)) != MP_OKAY) { goto _ERR; } + + /* initialize, (v1,v2,v3) = (0,1,b) */ + mp_set(&v2, 1); + if ((err = mp_copy(b, &v3)) != MP_OKAY) { goto _ERR; } + + /* loop while v3 != 0 */ + while (mp_iszero(&v3) == MP_NO) { + /* q = u3/v3 */ + if ((err = mp_div(&u3, &v3, &q, NULL)) != MP_OKAY) { goto _ERR; } + + /* (t1,t2,t3) = (u1,u2,u3) - (v1,v2,v3)q */ + if ((err = mp_mul(&v1, &q, &tmp)) != MP_OKAY) { goto _ERR; } + if ((err = mp_sub(&u1, &tmp, &t1)) != MP_OKAY) { goto _ERR; } + if ((err = mp_mul(&v2, &q, &tmp)) != MP_OKAY) { goto _ERR; } + if ((err = mp_sub(&u2, &tmp, &t2)) != MP_OKAY) { goto _ERR; } + if ((err = mp_mul(&v3, &q, &tmp)) != MP_OKAY) { goto _ERR; } + if ((err = mp_sub(&u3, &tmp, &t3)) != MP_OKAY) { goto _ERR; } + + /* (u1,u2,u3) = (v1,v2,v3) */ + if ((err = mp_copy(&v1, &u1)) != MP_OKAY) { goto _ERR; } + if ((err = mp_copy(&v2, &u2)) != MP_OKAY) { goto _ERR; } + if ((err = mp_copy(&v3, &u3)) != MP_OKAY) { goto _ERR; } + + /* (v1,v2,v3) = (t1,t2,t3) */ + if ((err = mp_copy(&t1, &v1)) != MP_OKAY) { goto _ERR; } + if ((err = mp_copy(&t2, &v2)) != MP_OKAY) { goto _ERR; } + if ((err = mp_copy(&t3, &v3)) != MP_OKAY) { goto _ERR; } + } + + /* make sure U3 >= 0 */ + if (u3.sign == MP_NEG) { + mp_neg(&u1, &u1); + mp_neg(&u2, &u2); + mp_neg(&u3, &u3); + } + + /* copy result out */ + if (U1 != NULL) { mp_exch(U1, &u1); } + if (U2 != NULL) { mp_exch(U2, &u2); } + if (U3 != NULL) { mp_exch(U3, &u3); } + + err = MP_OKAY; +_ERR: mp_clear_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_exteuclid.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_exteuclid.c */ + +/* Start: bn_mp_fread.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_FREAD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* read a bigint from a file stream in ASCII */ +int mp_fread(mp_int *a, int radix, FILE *stream) +{ + int err, ch, neg, y; + + /* clear a */ + mp_zero(a); + + /* if first digit is - then set negative */ + ch = fgetc(stream); + if (ch == '-') { + neg = MP_NEG; + ch = fgetc(stream); + } else { + neg = MP_ZPOS; + } + + for (;;) { + /* find y in the radix map */ + for (y = 0; y < radix; y++) { + if (mp_s_rmap[y] == ch) { + break; + } + } + if (y == radix) { + break; + } + + /* shift up and add */ + if ((err = mp_mul_d(a, radix, a)) != MP_OKAY) { + return err; + } + if ((err = mp_add_d(a, y, a)) != MP_OKAY) { + return err; + } + + ch = fgetc(stream); + } + if (mp_cmp_d(a, 0) != MP_EQ) { + a->sign = neg; + } + + return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_fread.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_fread.c */ + +/* Start: bn_mp_fwrite.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_FWRITE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +int mp_fwrite(mp_int *a, int radix, FILE *stream) +{ + char *buf; + int err, len, x; + + if ((err = mp_radix_size(a, radix, &len)) != MP_OKAY) { + return err; + } + + buf = OPT_CAST(char) XMALLOC (len); + if (buf == NULL) { + return MP_MEM; + } + + if ((err = mp_toradix(a, buf, radix)) != MP_OKAY) { + XFREE (buf); + return err; + } + + for (x = 0; x < len; x++) { + if (fputc(buf[x], stream) == EOF) { + XFREE (buf); + return MP_VAL; + } + } + + XFREE (buf); + return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_fwrite.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_fwrite.c */ + +/* Start: bn_mp_gcd.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_GCD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* Greatest Common Divisor using the binary method */ +int mp_gcd (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int u, v; + int k, u_lsb, v_lsb, res; + + /* either zero than gcd is the largest */ + if (mp_iszero (a) == MP_YES) { + return mp_abs (b, c); + } + if (mp_iszero (b) == MP_YES) { + return mp_abs (a, c); + } + + /* get copies of a and b we can modify */ + if ((res = mp_init_copy (&u, a)) != MP_OKAY) { + return res; + } + + if ((res = mp_init_copy (&v, b)) != MP_OKAY) { + goto LBL_U; + } + + /* must be positive for the remainder of the algorithm */ + u.sign = v.sign = MP_ZPOS; + + /* B1. Find the common power of two for u and v */ + u_lsb = mp_cnt_lsb(&u); + v_lsb = mp_cnt_lsb(&v); + k = MIN(u_lsb, v_lsb); + + if (k > 0) { + /* divide the power of two out */ + if ((res = mp_div_2d(&u, k, &u, NULL)) != MP_OKAY) { + goto LBL_V; + } + + if ((res = mp_div_2d(&v, k, &v, NULL)) != MP_OKAY) { + goto LBL_V; + } + } + + /* divide any remaining factors of two out */ + if (u_lsb != k) { + if ((res = mp_div_2d(&u, u_lsb - k, &u, NULL)) != MP_OKAY) { + goto LBL_V; + } + } + + if (v_lsb != k) { + if ((res = mp_div_2d(&v, v_lsb - k, &v, NULL)) != MP_OKAY) { + goto LBL_V; + } + } + + while (mp_iszero(&v) == 0) { + /* make sure v is the largest */ + if (mp_cmp_mag(&u, &v) == MP_GT) { + /* swap u and v to make sure v is >= u */ + mp_exch(&u, &v); + } + + /* subtract smallest from largest */ + if ((res = s_mp_sub(&v, &u, &v)) != MP_OKAY) { + goto LBL_V; + } + + /* Divide out all factors of two */ + if ((res = mp_div_2d(&v, mp_cnt_lsb(&v), &v, NULL)) != MP_OKAY) { + goto LBL_V; + } + } + + /* multiply by 2**k which we divided out at the beginning */ + if ((res = mp_mul_2d (&u, k, c)) != MP_OKAY) { + goto LBL_V; + } + c->sign = MP_ZPOS; + res = MP_OKAY; +LBL_V:mp_clear (&u); +LBL_U:mp_clear (&v); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_gcd.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_gcd.c */ + +/* Start: bn_mp_get_int.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_GET_INT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* get the lower 32-bits of an mp_int */ +unsigned long mp_get_int(mp_int * a) +{ + int i; + unsigned long res; + + if (a->used == 0) { + return 0; + } + + /* get number of digits of the lsb we have to read */ + i = MIN(a->used,(int)((sizeof(unsigned long)*CHAR_BIT+DIGIT_BIT-1)/DIGIT_BIT))-1; + + /* get most significant digit of result */ + res = DIGIT(a,i); + + while (--i >= 0) { + res = (res << DIGIT_BIT) | DIGIT(a,i); + } + + /* force result to 32-bits always so it is consistent on non 32-bit platforms */ + return res & 0xFFFFFFFFUL; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_get_int.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_get_int.c */ + +/* Start: bn_mp_grow.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_GROW_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* grow as required */ +int mp_grow (mp_int * a, int size) +{ + int i; + mp_digit *tmp; + + /* if the alloc size is smaller alloc more ram */ + if (a->alloc < size) { + /* ensure there are always at least MP_PREC digits extra on top */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* reallocate the array a->dp + * + * We store the return in a temporary variable + * in case the operation failed we don't want + * to overwrite the dp member of a. + */ + tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * size); + if (tmp == NULL) { + /* reallocation failed but "a" is still valid [can be freed] */ + return MP_MEM; + } + + /* reallocation succeeded so set a->dp */ + a->dp = tmp; + + /* zero excess digits */ + i = a->alloc; + a->alloc = size; + for (; i < a->alloc; i++) { + a->dp[i] = 0; + } + } + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_grow.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_grow.c */ + +/* Start: bn_mp_init.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_INIT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* init a new mp_int */ +int mp_init (mp_int * a) +{ + int i; + + /* allocate memory required and clear it */ + a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * MP_PREC); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the digits to zero */ + for (i = 0; i < MP_PREC; i++) { + a->dp[i] = 0; + } + + /* set the used to zero, allocated digits to the default precision + * and sign to positive */ + a->used = 0; + a->alloc = MP_PREC; + a->sign = MP_ZPOS; + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_init.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_init.c */ + +/* Start: bn_mp_init_copy.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_INIT_COPY_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* creates "a" then copies b into it */ +int mp_init_copy (mp_int * a, mp_int * b) +{ + int res; + + if ((res = mp_init (a)) != MP_OKAY) { + return res; + } + return mp_copy (b, a); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_init_copy.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_init_copy.c */ + +/* Start: bn_mp_init_multi.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_INIT_MULTI_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ +#include + +int mp_init_multi(mp_int *mp, ...) +{ + mp_err res = MP_OKAY; /* Assume ok until proven otherwise */ + int n = 0; /* Number of ok inits */ + mp_int* cur_arg = mp; + va_list args; + + va_start(args, mp); /* init args to next argument from caller */ + while (cur_arg != NULL) { + if (mp_init(cur_arg) != MP_OKAY) { + /* Oops - error! Back-track and mp_clear what we already + succeeded in init-ing, then return error. + */ + va_list clean_args; + + /* end the current list */ + va_end(args); + + /* now start cleaning up */ + cur_arg = mp; + va_start(clean_args, mp); + while (n--) { + mp_clear(cur_arg); + cur_arg = va_arg(clean_args, mp_int*); + } + va_end(clean_args); + res = MP_MEM; + break; + } + n++; + cur_arg = va_arg(args, mp_int*); + } + va_end(args); + return res; /* Assumed ok, if error flagged above. */ +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_init_multi.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_init_multi.c */ + +/* Start: bn_mp_init_set.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_INIT_SET_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* initialize and set a digit */ +int mp_init_set (mp_int * a, mp_digit b) +{ + int err; + if ((err = mp_init(a)) != MP_OKAY) { + return err; + } + mp_set(a, b); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_init_set.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_init_set.c */ + +/* Start: bn_mp_init_set_int.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_INIT_SET_INT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* initialize and set a digit */ +int mp_init_set_int (mp_int * a, unsigned long b) +{ + int err; + if ((err = mp_init(a)) != MP_OKAY) { + return err; + } + return mp_set_int(a, b); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_init_set_int.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_init_set_int.c */ + +/* Start: bn_mp_init_size.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_INIT_SIZE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* init an mp_init for a given size */ +int mp_init_size (mp_int * a, int size) +{ + int x; + + /* pad size so there are always extra digits */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* alloc mem */ + a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * size); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the members */ + a->used = 0; + a->alloc = size; + a->sign = MP_ZPOS; + + /* zero the digits */ + for (x = 0; x < size; x++) { + a->dp[x] = 0; + } + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_init_size.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_init_size.c */ + +/* Start: bn_mp_invmod.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_INVMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* hac 14.61, pp608 */ +int mp_invmod (mp_int * a, mp_int * b, mp_int * c) +{ + /* b cannot be negative */ + if (b->sign == MP_NEG || mp_iszero(b) == 1) { + return MP_VAL; + } + +#ifdef BN_FAST_MP_INVMOD_C + /* if the modulus is odd we can use a faster routine instead */ + if (mp_isodd (b) == 1) { + return fast_mp_invmod (a, b, c); + } +#endif + +#ifdef BN_MP_INVMOD_SLOW_C + return mp_invmod_slow(a, b, c); +#endif + + return MP_VAL; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_invmod.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_invmod.c */ + +/* Start: bn_mp_invmod_slow.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_INVMOD_SLOW_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* hac 14.61, pp608 */ +int mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int x, y, u, v, A, B, C, D; + int res; + + /* b cannot be negative */ + if (b->sign == MP_NEG || mp_iszero(b) == 1) { + return MP_VAL; + } + + /* init temps */ + if ((res = mp_init_multi(&x, &y, &u, &v, + &A, &B, &C, &D, NULL)) != MP_OKAY) { + return res; + } + + /* x = a, y = b */ + if ((res = mp_mod(a, b, &x)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy (b, &y)) != MP_OKAY) { + goto LBL_ERR; + } + + /* 2. [modified] if x,y are both even then return an error! */ + if (mp_iseven (&x) == 1 && mp_iseven (&y) == 1) { + res = MP_VAL; + goto LBL_ERR; + } + + /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ + if ((res = mp_copy (&x, &u)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy (&y, &v)) != MP_OKAY) { + goto LBL_ERR; + } + mp_set (&A, 1); + mp_set (&D, 1); + +top: + /* 4. while u is even do */ + while (mp_iseven (&u) == 1) { + /* 4.1 u = u/2 */ + if ((res = mp_div_2 (&u, &u)) != MP_OKAY) { + goto LBL_ERR; + } + /* 4.2 if A or B is odd then */ + if (mp_isodd (&A) == 1 || mp_isodd (&B) == 1) { + /* A = (A+y)/2, B = (B-x)/2 */ + if ((res = mp_add (&A, &y, &A)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* A = A/2, B = B/2 */ + if ((res = mp_div_2 (&A, &A)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_div_2 (&B, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 5. while v is even do */ + while (mp_iseven (&v) == 1) { + /* 5.1 v = v/2 */ + if ((res = mp_div_2 (&v, &v)) != MP_OKAY) { + goto LBL_ERR; + } + /* 5.2 if C or D is odd then */ + if (mp_isodd (&C) == 1 || mp_isodd (&D) == 1) { + /* C = (C+y)/2, D = (D-x)/2 */ + if ((res = mp_add (&C, &y, &C)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* C = C/2, D = D/2 */ + if ((res = mp_div_2 (&C, &C)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_div_2 (&D, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 6. if u >= v then */ + if (mp_cmp (&u, &v) != MP_LT) { + /* u = u - v, A = A - C, B = B - D */ + if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&A, &C, &A)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } else { + /* v - v - u, C = C - A, D = D - B */ + if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&C, &A, &C)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* if not zero goto step 4 */ + if (mp_iszero (&u) == 0) + goto top; + + /* now a = C, b = D, gcd == g*v */ + + /* if v != 1 then there is no inverse */ + if (mp_cmp_d (&v, 1) != MP_EQ) { + res = MP_VAL; + goto LBL_ERR; + } + + /* if its too low */ + while (mp_cmp_d(&C, 0) == MP_LT) { + if ((res = mp_add(&C, b, &C)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* too big */ + while (mp_cmp_mag(&C, b) != MP_LT) { + if ((res = mp_sub(&C, b, &C)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* C is now the inverse */ + mp_exch (&C, c); + res = MP_OKAY; +LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &A, &B, &C, &D, NULL); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_invmod_slow.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_invmod_slow.c */ + +/* Start: bn_mp_is_square.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_IS_SQUARE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* Check if remainders are possible squares - fast exclude non-squares */ +static const char rem_128[128] = { + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1 +}; + +static const char rem_105[105] = { + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1 +}; + +/* Store non-zero to ret if arg is square, and zero if not */ +int mp_is_square(mp_int *arg,int *ret) +{ + int res; + mp_digit c; + mp_int t; + unsigned long r; + + /* Default to Non-square :) */ + *ret = MP_NO; + + if (arg->sign == MP_NEG) { + return MP_VAL; + } + + /* digits used? (TSD) */ + if (arg->used == 0) { + return MP_OKAY; + } + + /* First check mod 128 (suppose that DIGIT_BIT is at least 7) */ + if (rem_128[127 & DIGIT(arg,0)] == 1) { + return MP_OKAY; + } + + /* Next check mod 105 (3*5*7) */ + if ((res = mp_mod_d(arg,105,&c)) != MP_OKAY) { + return res; + } + if (rem_105[c] == 1) { + return MP_OKAY; + } + + + if ((res = mp_init_set_int(&t,11L*13L*17L*19L*23L*29L*31L)) != MP_OKAY) { + return res; + } + if ((res = mp_mod(arg,&t,&t)) != MP_OKAY) { + goto ERR; + } + r = mp_get_int(&t); + /* Check for other prime modules, note it's not an ERROR but we must + * free "t" so the easiest way is to goto ERR. We know that res + * is already equal to MP_OKAY from the mp_mod call + */ + if ( (1L<<(r%11)) & 0x5C4L ) goto ERR; + if ( (1L<<(r%13)) & 0x9E4L ) goto ERR; + if ( (1L<<(r%17)) & 0x5CE8L ) goto ERR; + if ( (1L<<(r%19)) & 0x4F50CL ) goto ERR; + if ( (1L<<(r%23)) & 0x7ACCA0L ) goto ERR; + if ( (1L<<(r%29)) & 0xC2EDD0CL ) goto ERR; + if ( (1L<<(r%31)) & 0x6DE2B848L ) goto ERR; + + /* Final check - is sqr(sqrt(arg)) == arg ? */ + if ((res = mp_sqrt(arg,&t)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sqr(&t,&t)) != MP_OKAY) { + goto ERR; + } + + *ret = (mp_cmp_mag(&t,arg) == MP_EQ) ? MP_YES : MP_NO; +ERR:mp_clear(&t); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_is_square.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_is_square.c */ + +/* Start: bn_mp_jacobi.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_JACOBI_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* computes the jacobi c = (a | n) (or Legendre if n is prime) + * HAC pp. 73 Algorithm 2.149 + */ +int mp_jacobi (mp_int * a, mp_int * p, int *c) +{ + mp_int a1, p1; + int k, s, r, res; + mp_digit residue; + + /* if p <= 0 return MP_VAL */ + if (mp_cmp_d(p, 0) != MP_GT) { + return MP_VAL; + } + + /* step 1. if a == 0, return 0 */ + if (mp_iszero (a) == 1) { + *c = 0; + return MP_OKAY; + } + + /* step 2. if a == 1, return 1 */ + if (mp_cmp_d (a, 1) == MP_EQ) { + *c = 1; + return MP_OKAY; + } + + /* default */ + s = 0; + + /* step 3. write a = a1 * 2**k */ + if ((res = mp_init_copy (&a1, a)) != MP_OKAY) { + return res; + } + + if ((res = mp_init (&p1)) != MP_OKAY) { + goto LBL_A1; + } + + /* divide out larger power of two */ + k = mp_cnt_lsb(&a1); + if ((res = mp_div_2d(&a1, k, &a1, NULL)) != MP_OKAY) { + goto LBL_P1; + } + + /* step 4. if e is even set s=1 */ + if ((k & 1) == 0) { + s = 1; + } else { + /* else set s=1 if p = 1/7 (mod 8) or s=-1 if p = 3/5 (mod 8) */ + residue = p->dp[0] & 7; + + if (residue == 1 || residue == 7) { + s = 1; + } else if (residue == 3 || residue == 5) { + s = -1; + } + } + + /* step 5. if p == 3 (mod 4) *and* a1 == 3 (mod 4) then s = -s */ + if ( ((p->dp[0] & 3) == 3) && ((a1.dp[0] & 3) == 3)) { + s = -s; + } + + /* if a1 == 1 we're done */ + if (mp_cmp_d (&a1, 1) == MP_EQ) { + *c = s; + } else { + /* n1 = n mod a1 */ + if ((res = mp_mod (p, &a1, &p1)) != MP_OKAY) { + goto LBL_P1; + } + if ((res = mp_jacobi (&p1, &a1, &r)) != MP_OKAY) { + goto LBL_P1; + } + *c = s * r; + } + + /* done */ + res = MP_OKAY; +LBL_P1:mp_clear (&p1); +LBL_A1:mp_clear (&a1); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_jacobi.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_jacobi.c */ + +/* Start: bn_mp_karatsuba_mul.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_KARATSUBA_MUL_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* c = |a| * |b| using Karatsuba Multiplication using + * three half size multiplications + * + * Let B represent the radix [e.g. 2**DIGIT_BIT] and + * let n represent half of the number of digits in + * the min(a,b) + * + * a = a1 * B**n + a0 + * b = b1 * B**n + b0 + * + * Then, a * b => + a1b1 * B**2n + ((a1 + a0)(b1 + b0) - (a0b0 + a1b1)) * B + a0b0 + * + * Note that a1b1 and a0b0 are used twice and only need to be + * computed once. So in total three half size (half # of + * digit) multiplications are performed, a0b0, a1b1 and + * (a1+b1)(a0+b0) + * + * Note that a multiplication of half the digits requires + * 1/4th the number of single precision multiplications so in + * total after one call 25% of the single precision multiplications + * are saved. Note also that the call to mp_mul can end up back + * in this function if the a0, a1, b0, or b1 are above the threshold. + * This is known as divide-and-conquer and leads to the famous + * O(N**lg(3)) or O(N**1.584) work which is asymptopically lower than + * the standard O(N**2) that the baseline/comba methods use. + * Generally though the overhead of this method doesn't pay off + * until a certain size (N ~ 80) is reached. + */ +int mp_karatsuba_mul (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int x0, x1, y0, y1, t1, x0y0, x1y1; + int B, err; + + /* default the return code to an error */ + err = MP_MEM; + + /* min # of digits */ + B = MIN (a->used, b->used); + + /* now divide in two */ + B = B >> 1; + + /* init copy all the temps */ + if (mp_init_size (&x0, B) != MP_OKAY) + goto ERR; + if (mp_init_size (&x1, a->used - B) != MP_OKAY) + goto X0; + if (mp_init_size (&y0, B) != MP_OKAY) + goto X1; + if (mp_init_size (&y1, b->used - B) != MP_OKAY) + goto Y0; + + /* init temps */ + if (mp_init_size (&t1, B * 2) != MP_OKAY) + goto Y1; + if (mp_init_size (&x0y0, B * 2) != MP_OKAY) + goto T1; + if (mp_init_size (&x1y1, B * 2) != MP_OKAY) + goto X0Y0; + + /* now shift the digits */ + x0.used = y0.used = B; + x1.used = a->used - B; + y1.used = b->used - B; + + { + register int x; + register mp_digit *tmpa, *tmpb, *tmpx, *tmpy; + + /* we copy the digits directly instead of using higher level functions + * since we also need to shift the digits + */ + tmpa = a->dp; + tmpb = b->dp; + + tmpx = x0.dp; + tmpy = y0.dp; + for (x = 0; x < B; x++) { + *tmpx++ = *tmpa++; + *tmpy++ = *tmpb++; + } + + tmpx = x1.dp; + for (x = B; x < a->used; x++) { + *tmpx++ = *tmpa++; + } + + tmpy = y1.dp; + for (x = B; x < b->used; x++) { + *tmpy++ = *tmpb++; + } + } + + /* only need to clamp the lower words since by definition the + * upper words x1/y1 must have a known number of digits + */ + mp_clamp (&x0); + mp_clamp (&y0); + + /* now calc the products x0y0 and x1y1 */ + /* after this x0 is no longer required, free temp [x0==t2]! */ + if (mp_mul (&x0, &y0, &x0y0) != MP_OKAY) + goto X1Y1; /* x0y0 = x0*y0 */ + if (mp_mul (&x1, &y1, &x1y1) != MP_OKAY) + goto X1Y1; /* x1y1 = x1*y1 */ + + /* now calc x1+x0 and y1+y0 */ + if (s_mp_add (&x1, &x0, &t1) != MP_OKAY) + goto X1Y1; /* t1 = x1 - x0 */ + if (s_mp_add (&y1, &y0, &x0) != MP_OKAY) + goto X1Y1; /* t2 = y1 - y0 */ + if (mp_mul (&t1, &x0, &t1) != MP_OKAY) + goto X1Y1; /* t1 = (x1 + x0) * (y1 + y0) */ + + /* add x0y0 */ + if (mp_add (&x0y0, &x1y1, &x0) != MP_OKAY) + goto X1Y1; /* t2 = x0y0 + x1y1 */ + if (s_mp_sub (&t1, &x0, &t1) != MP_OKAY) + goto X1Y1; /* t1 = (x1+x0)*(y1+y0) - (x1y1 + x0y0) */ + + /* shift by B */ + if (mp_lshd (&t1, B) != MP_OKAY) + goto X1Y1; /* t1 = (x0y0 + x1y1 - (x1-x0)*(y1-y0))<used; + + /* now divide in two */ + B = B >> 1; + + /* init copy all the temps */ + if (mp_init_size (&x0, B) != MP_OKAY) + goto ERR; + if (mp_init_size (&x1, a->used - B) != MP_OKAY) + goto X0; + + /* init temps */ + if (mp_init_size (&t1, a->used * 2) != MP_OKAY) + goto X1; + if (mp_init_size (&t2, a->used * 2) != MP_OKAY) + goto T1; + if (mp_init_size (&x0x0, B * 2) != MP_OKAY) + goto T2; + if (mp_init_size (&x1x1, (a->used - B) * 2) != MP_OKAY) + goto X0X0; + + { + register int x; + register mp_digit *dst, *src; + + src = a->dp; + + /* now shift the digits */ + dst = x0.dp; + for (x = 0; x < B; x++) { + *dst++ = *src++; + } + + dst = x1.dp; + for (x = B; x < a->used; x++) { + *dst++ = *src++; + } + } + + x0.used = B; + x1.used = a->used - B; + + mp_clamp (&x0); + + /* now calc the products x0*x0 and x1*x1 */ + if (mp_sqr (&x0, &x0x0) != MP_OKAY) + goto X1X1; /* x0x0 = x0*x0 */ + if (mp_sqr (&x1, &x1x1) != MP_OKAY) + goto X1X1; /* x1x1 = x1*x1 */ + + /* now calc (x1+x0)**2 */ + if (s_mp_add (&x1, &x0, &t1) != MP_OKAY) + goto X1X1; /* t1 = x1 - x0 */ + if (mp_sqr (&t1, &t1) != MP_OKAY) + goto X1X1; /* t1 = (x1 - x0) * (x1 - x0) */ + + /* add x0y0 */ + if (s_mp_add (&x0x0, &x1x1, &t2) != MP_OKAY) + goto X1X1; /* t2 = x0x0 + x1x1 */ + if (s_mp_sub (&t1, &t2, &t1) != MP_OKAY) + goto X1X1; /* t1 = (x1+x0)**2 - (x0x0 + x1x1) */ + + /* shift by B */ + if (mp_lshd (&t1, B) != MP_OKAY) + goto X1X1; /* t1 = (x0x0 + x1x1 - (x1-x0)*(x1-x0))<sign = MP_ZPOS; + +LBL_T: + mp_clear_multi (&t1, &t2, NULL); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_lcm.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_lcm.c */ + +/* Start: bn_mp_lshd.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_LSHD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* shift left a certain amount of digits */ +int mp_lshd (mp_int * a, int b) +{ + int x, res; + + /* if its less than zero return */ + if (b <= 0) { + return MP_OKAY; + } + + /* grow to fit the new digits */ + if (a->alloc < a->used + b) { + if ((res = mp_grow (a, a->used + b)) != MP_OKAY) { + return res; + } + } + + { + register mp_digit *top, *bottom; + + /* increment the used by the shift amount then copy upwards */ + a->used += b; + + /* top */ + top = a->dp + a->used - 1; + + /* base */ + bottom = a->dp + a->used - 1 - b; + + /* much like mp_rshd this is implemented using a sliding window + * except the window goes the otherway around. Copying from + * the bottom to the top. see bn_mp_rshd.c for more info. + */ + for (x = a->used - 1; x >= b; x--) { + *top-- = *bottom--; + } + + /* zero the lower digits */ + top = a->dp; + for (x = 0; x < b; x++) { + *top++ = 0; + } + } + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_lshd.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_lshd.c */ + +/* Start: bn_mp_mod.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* c = a mod b, 0 <= c < b */ +int +mp_mod (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int t; + int res; + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_div (a, b, NULL, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + + if (t.sign != b->sign) { + res = mp_add (b, &t, c); + } else { + res = MP_OKAY; + mp_exch (&t, c); + } + + mp_clear (&t); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mod.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_mod.c */ + +/* Start: bn_mp_mod_2d.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MOD_2D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* calc a value mod 2**b */ +int +mp_mod_2d (mp_int * a, int b, mp_int * c) +{ + int x, res; + + /* if b is <= 0 then zero the int */ + if (b <= 0) { + mp_zero (c); + return MP_OKAY; + } + + /* if the modulus is larger than the value than return */ + if (b >= (int) (a->used * DIGIT_BIT)) { + res = mp_copy (a, c); + return res; + } + + /* copy */ + if ((res = mp_copy (a, c)) != MP_OKAY) { + return res; + } + + /* zero digits above the last digit of the modulus */ + for (x = (b / DIGIT_BIT) + ((b % DIGIT_BIT) == 0 ? 0 : 1); x < c->used; x++) { + c->dp[x] = 0; + } + /* clear the digit that is not completely outside/inside the modulus */ + c->dp[b / DIGIT_BIT] &= + (mp_digit) ((((mp_digit) 1) << (((mp_digit) b) % DIGIT_BIT)) - ((mp_digit) 1)); + mp_clamp (c); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mod_2d.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_mod_2d.c */ + +/* Start: bn_mp_mod_d.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MOD_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +int +mp_mod_d (mp_int * a, mp_digit b, mp_digit * c) +{ + return mp_div_d(a, b, NULL, c); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mod_d.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_mod_d.c */ + +/* Start: bn_mp_montgomery_calc_normalization.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* + * shifts with subtractions when the result is greater than b. + * + * The method is slightly modified to shift B unconditionally upto just under + * the leading bit of b. This saves alot of multiple precision shifting. + */ +int mp_montgomery_calc_normalization (mp_int * a, mp_int * b) +{ + int x, bits, res; + + /* how many bits of last digit does b use */ + bits = mp_count_bits (b) % DIGIT_BIT; + + if (b->used > 1) { + if ((res = mp_2expt (a, (b->used - 1) * DIGIT_BIT + bits - 1)) != MP_OKAY) { + return res; + } + } else { + mp_set(a, 1); + bits = 1; + } + + + /* now compute C = A * B mod b */ + for (x = bits - 1; x < (int)DIGIT_BIT; x++) { + if ((res = mp_mul_2 (a, a)) != MP_OKAY) { + return res; + } + if (mp_cmp_mag (a, b) != MP_LT) { + if ((res = s_mp_sub (a, b, a)) != MP_OKAY) { + return res; + } + } + } + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_montgomery_calc_normalization.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_montgomery_calc_normalization.c */ + +/* Start: bn_mp_montgomery_reduce.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MONTGOMERY_REDUCE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* computes xR**-1 == x (mod N) via Montgomery Reduction */ +int +mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) +{ + int ix, res, digs; + mp_digit mu; + + /* can the fast reduction [comba] method be used? + * + * Note that unlike in mul you're safely allowed *less* + * than the available columns [255 per default] since carries + * are fixed up in the inner loop. + */ + digs = n->used * 2 + 1; + if ((digs < MP_WARRAY) && + n->used < + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + return fast_mp_montgomery_reduce (x, n, rho); + } + + /* grow the input as required */ + if (x->alloc < digs) { + if ((res = mp_grow (x, digs)) != MP_OKAY) { + return res; + } + } + x->used = digs; + + for (ix = 0; ix < n->used; ix++) { + /* mu = ai * rho mod b + * + * The value of rho must be precalculated via + * montgomery_setup() such that + * it equals -1/n0 mod b this allows the + * following inner loop to reduce the + * input one digit at a time + */ + mu = (mp_digit) (((mp_word)x->dp[ix]) * ((mp_word)rho) & MP_MASK); + + /* a = a + mu * m * b**i */ + { + register int iy; + register mp_digit *tmpn, *tmpx, u; + register mp_word r; + + /* alias for digits of the modulus */ + tmpn = n->dp; + + /* alias for the digits of x [the input] */ + tmpx = x->dp + ix; + + /* set the carry to zero */ + u = 0; + + /* Multiply and add in place */ + for (iy = 0; iy < n->used; iy++) { + /* compute product and sum */ + r = ((mp_word)mu) * ((mp_word)*tmpn++) + + ((mp_word) u) + ((mp_word) * tmpx); + + /* get carry */ + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + + /* fix digit */ + *tmpx++ = (mp_digit)(r & ((mp_word) MP_MASK)); + } + /* At this point the ix'th digit of x should be zero */ + + + /* propagate carries upwards as required*/ + while (u) { + *tmpx += u; + u = *tmpx >> DIGIT_BIT; + *tmpx++ &= MP_MASK; + } + } + } + + /* at this point the n.used'th least + * significant digits of x are all zero + * which means we can shift x to the + * right by n.used digits and the + * residue is unchanged. + */ + + /* x = x/b**n.used */ + mp_clamp(x); + mp_rshd (x, n->used); + + /* if x >= n then x = x - n */ + if (mp_cmp_mag (x, n) != MP_LT) { + return s_mp_sub (x, n, x); + } + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_montgomery_reduce.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_montgomery_reduce.c */ + +/* Start: bn_mp_montgomery_setup.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MONTGOMERY_SETUP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* setups the montgomery reduction stuff */ +int +mp_montgomery_setup (mp_int * n, mp_digit * rho) +{ + mp_digit x, b; + +/* fast inversion mod 2**k + * + * Based on the fact that + * + * XA = 1 (mod 2**n) => (X(2-XA)) A = 1 (mod 2**2n) + * => 2*X*A - X*X*A*A = 1 + * => 2*(1) - (1) = 1 + */ + b = n->dp[0]; + + if ((b & 1) == 0) { + return MP_VAL; + } + + x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */ + x *= 2 - b * x; /* here x*a==1 mod 2**8 */ +#if !defined(MP_8BIT) + x *= 2 - b * x; /* here x*a==1 mod 2**16 */ +#endif +#if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT)) + x *= 2 - b * x; /* here x*a==1 mod 2**32 */ +#endif +#ifdef MP_64BIT + x *= 2 - b * x; /* here x*a==1 mod 2**64 */ +#endif + + /* rho = -1/m mod b */ + *rho = (((mp_word)1 << ((mp_word) DIGIT_BIT)) - x) & MP_MASK; + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_montgomery_setup.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_montgomery_setup.c */ + +/* Start: bn_mp_mul.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MUL_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* high level multiplication (handles sign) */ +int mp_mul (mp_int * a, mp_int * b, mp_int * c) +{ + int res, neg; + neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + + /* use Toom-Cook? */ +#ifdef BN_MP_TOOM_MUL_C + if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) { + res = mp_toom_mul(a, b, c); + } else +#endif +#ifdef BN_MP_KARATSUBA_MUL_C + /* use Karatsuba? */ + if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) { + res = mp_karatsuba_mul (a, b, c); + } else +#endif + { + /* can we use the fast multiplier? + * + * The fast multiplier can be used if the output will + * have less than MP_WARRAY digits and the number of + * digits won't affect carry propagation + */ + int digs = a->used + b->used + 1; + +#ifdef BN_FAST_S_MP_MUL_DIGS_C + if ((digs < MP_WARRAY) && + MIN(a->used, b->used) <= + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + res = fast_s_mp_mul_digs (a, b, c, digs); + } else +#endif +#ifdef BN_S_MP_MUL_DIGS_C + res = s_mp_mul (a, b, c); /* uses s_mp_mul_digs */ +#else + res = MP_VAL; +#endif + + } + c->sign = (c->used > 0) ? neg : MP_ZPOS; + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mul.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_mul.c */ + +/* Start: bn_mp_mul_2.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MUL_2_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* b = a*2 */ +int mp_mul_2(mp_int * a, mp_int * b) +{ + int x, res, oldused; + + /* grow to accomodate result */ + if (b->alloc < a->used + 1) { + if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + + { + register mp_digit r, rr, *tmpa, *tmpb; + + /* alias for source */ + tmpa = a->dp; + + /* alias for dest */ + tmpb = b->dp; + + /* carry */ + r = 0; + for (x = 0; x < a->used; x++) { + + /* get what will be the *next* carry bit from the + * MSB of the current digit + */ + rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1)); + + /* now shift up this digit, add in the carry [from the previous] */ + *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK; + + /* copy the carry that would be from the source + * digit into the next iteration + */ + r = rr; + } + + /* new leading digit? */ + if (r != 0) { + /* add a MSB which is always 1 at this point */ + *tmpb = 1; + ++(b->used); + } + + /* now zero any excess digits on the destination + * that we didn't write to + */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mul_2.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_mul_2.c */ + +/* Start: bn_mp_mul_2d.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MUL_2D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* shift left by a certain bit count */ +int mp_mul_2d (mp_int * a, int b, mp_int * c) +{ + mp_digit d; + int res; + + /* copy */ + if (a != c) { + if ((res = mp_copy (a, c)) != MP_OKAY) { + return res; + } + } + + if (c->alloc < (int)(c->used + b/DIGIT_BIT + 1)) { + if ((res = mp_grow (c, c->used + b / DIGIT_BIT + 1)) != MP_OKAY) { + return res; + } + } + + /* shift by as many digits in the bit count */ + if (b >= (int)DIGIT_BIT) { + if ((res = mp_lshd (c, b / DIGIT_BIT)) != MP_OKAY) { + return res; + } + } + + /* shift any bit count < DIGIT_BIT */ + d = (mp_digit) (b % DIGIT_BIT); + if (d != 0) { + register mp_digit *tmpc, shift, mask, r, rr; + register int x; + + /* bitmask for carries */ + mask = (((mp_digit)1) << d) - 1; + + /* shift for msbs */ + shift = DIGIT_BIT - d; + + /* alias */ + tmpc = c->dp; + + /* carry */ + r = 0; + for (x = 0; x < c->used; x++) { + /* get the higher bits of the current word */ + rr = (*tmpc >> shift) & mask; + + /* shift the current word and OR in the carry */ + *tmpc = ((*tmpc << d) | r) & MP_MASK; + ++tmpc; + + /* set the carry to the carry bits of the current word */ + r = rr; + } + + /* set final carry */ + if (r != 0) { + c->dp[(c->used)++] = r; + } + } + mp_clamp (c); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mul_2d.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_mul_2d.c */ + +/* Start: bn_mp_mul_d.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MUL_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* multiply by a digit */ +int +mp_mul_d (mp_int * a, mp_digit b, mp_int * c) +{ + mp_digit u, *tmpa, *tmpc; + mp_word r; + int ix, res, olduse; + + /* make sure c is big enough to hold a*b */ + if (c->alloc < a->used + 1) { + if ((res = mp_grow (c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* get the original destinations used count */ + olduse = c->used; + + /* set the sign */ + c->sign = a->sign; + + /* alias for a->dp [source] */ + tmpa = a->dp; + + /* alias for c->dp [dest] */ + tmpc = c->dp; + + /* zero carry */ + u = 0; + + /* compute columns */ + for (ix = 0; ix < a->used; ix++) { + /* compute product and carry sum for this term */ + r = ((mp_word) u) + ((mp_word)*tmpa++) * ((mp_word)b); + + /* mask off higher bits to get a single digit */ + *tmpc++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* send carry into next iteration */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + + /* store final carry [if any] and increment ix offset */ + *tmpc++ = u; + ++ix; + + /* now zero digits above the top */ + while (ix++ < olduse) { + *tmpc++ = 0; + } + + /* set used count */ + c->used = a->used + 1; + mp_clamp(c); + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mul_d.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_mul_d.c */ + +/* Start: bn_mp_mulmod.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MULMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* d = a * b (mod c) */ +int mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + int res; + mp_int t; + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_mul (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + res = mp_mod (&t, c, d); + mp_clear (&t); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mulmod.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_mulmod.c */ + +/* Start: bn_mp_n_root.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_N_ROOT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* find the n'th root of an integer + * + * Result found such that (c)**b <= a and (c+1)**b > a + * + * This algorithm uses Newton's approximation + * x[i+1] = x[i] - f(x[i])/f'(x[i]) + * which will find the root in log(N) time where + * each step involves a fair bit. This is not meant to + * find huge roots [square and cube, etc]. + */ +int mp_n_root (mp_int * a, mp_digit b, mp_int * c) +{ + mp_int t1, t2, t3; + int res, neg; + + /* input must be positive if b is even */ + if ((b & 1) == 0 && a->sign == MP_NEG) { + return MP_VAL; + } + + if ((res = mp_init (&t1)) != MP_OKAY) { + return res; + } + + if ((res = mp_init (&t2)) != MP_OKAY) { + goto LBL_T1; + } + + if ((res = mp_init (&t3)) != MP_OKAY) { + goto LBL_T2; + } + + /* if a is negative fudge the sign but keep track */ + neg = a->sign; + a->sign = MP_ZPOS; + + /* t2 = 2 */ + mp_set (&t2, 2); + + do { + /* t1 = t2 */ + if ((res = mp_copy (&t2, &t1)) != MP_OKAY) { + goto LBL_T3; + } + + /* t2 = t1 - ((t1**b - a) / (b * t1**(b-1))) */ + + /* t3 = t1**(b-1) */ + if ((res = mp_expt_d (&t1, b - 1, &t3)) != MP_OKAY) { + goto LBL_T3; + } + + /* numerator */ + /* t2 = t1**b */ + if ((res = mp_mul (&t3, &t1, &t2)) != MP_OKAY) { + goto LBL_T3; + } + + /* t2 = t1**b - a */ + if ((res = mp_sub (&t2, a, &t2)) != MP_OKAY) { + goto LBL_T3; + } + + /* denominator */ + /* t3 = t1**(b-1) * b */ + if ((res = mp_mul_d (&t3, b, &t3)) != MP_OKAY) { + goto LBL_T3; + } + + /* t3 = (t1**b - a)/(b * t1**(b-1)) */ + if ((res = mp_div (&t2, &t3, &t3, NULL)) != MP_OKAY) { + goto LBL_T3; + } + + if ((res = mp_sub (&t1, &t3, &t2)) != MP_OKAY) { + goto LBL_T3; + } + } while (mp_cmp (&t1, &t2) != MP_EQ); + + /* result can be off by a few so check */ + for (;;) { + if ((res = mp_expt_d (&t1, b, &t2)) != MP_OKAY) { + goto LBL_T3; + } + + if (mp_cmp (&t2, a) == MP_GT) { + if ((res = mp_sub_d (&t1, 1, &t1)) != MP_OKAY) { + goto LBL_T3; + } + } else { + break; + } + } + + /* reset the sign of a first */ + a->sign = neg; + + /* set the result */ + mp_exch (&t1, c); + + /* set the sign of the result */ + c->sign = neg; + + res = MP_OKAY; + +LBL_T3:mp_clear (&t3); +LBL_T2:mp_clear (&t2); +LBL_T1:mp_clear (&t1); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_n_root.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_n_root.c */ + +/* Start: bn_mp_neg.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_NEG_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* b = -a */ +int mp_neg (mp_int * a, mp_int * b) +{ + int res; + if (a != b) { + if ((res = mp_copy (a, b)) != MP_OKAY) { + return res; + } + } + + if (mp_iszero(b) != MP_YES) { + b->sign = (a->sign == MP_ZPOS) ? MP_NEG : MP_ZPOS; + } else { + b->sign = MP_ZPOS; + } + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_neg.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_neg.c */ + +/* Start: bn_mp_or.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_OR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* OR two ints together */ +int mp_or (mp_int * a, mp_int * b, mp_int * c) +{ + int res, ix, px; + mp_int t, *x; + + if (a->used > b->used) { + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + px = b->used; + x = b; + } else { + if ((res = mp_init_copy (&t, b)) != MP_OKAY) { + return res; + } + px = a->used; + x = a; + } + + for (ix = 0; ix < px; ix++) { + t.dp[ix] |= x->dp[ix]; + } + mp_clamp (&t); + mp_exch (c, &t); + mp_clear (&t); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_or.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_or.c */ + +/* Start: bn_mp_prime_fermat.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_PRIME_FERMAT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* performs one Fermat test. + * + * If "a" were prime then b**a == b (mod a) since the order of + * the multiplicative sub-group would be phi(a) = a-1. That means + * it would be the same as b**(a mod (a-1)) == b**1 == b (mod a). + * + * Sets result to 1 if the congruence holds, or zero otherwise. + */ +int mp_prime_fermat (mp_int * a, mp_int * b, int *result) +{ + mp_int t; + int err; + + /* default to composite */ + *result = MP_NO; + + /* ensure b > 1 */ + if (mp_cmp_d(b, 1) != MP_GT) { + return MP_VAL; + } + + /* init t */ + if ((err = mp_init (&t)) != MP_OKAY) { + return err; + } + + /* compute t = b**a mod a */ + if ((err = mp_exptmod (b, a, a, &t)) != MP_OKAY) { + goto LBL_T; + } + + /* is it equal to b? */ + if (mp_cmp (&t, b) == MP_EQ) { + *result = MP_YES; + } + + err = MP_OKAY; +LBL_T:mp_clear (&t); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_prime_fermat.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_prime_fermat.c */ + +/* Start: bn_mp_prime_is_divisible.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_PRIME_IS_DIVISIBLE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* determines if an integers is divisible by one + * of the first PRIME_SIZE primes or not + * + * sets result to 0 if not, 1 if yes + */ +int mp_prime_is_divisible (mp_int * a, int *result) +{ + int err, ix; + mp_digit res; + + /* default to not */ + *result = MP_NO; + + for (ix = 0; ix < PRIME_SIZE; ix++) { + /* what is a mod LBL_prime_tab[ix] */ + if ((err = mp_mod_d (a, ltm_prime_tab[ix], &res)) != MP_OKAY) { + return err; + } + + /* is the residue zero? */ + if (res == 0) { + *result = MP_YES; + return MP_OKAY; + } + } + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_prime_is_divisible.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_prime_is_divisible.c */ + +/* Start: bn_mp_prime_is_prime.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_PRIME_IS_PRIME_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* performs a variable number of rounds of Miller-Rabin + * + * Probability of error after t rounds is no more than + + * + * Sets result to 1 if probably prime, 0 otherwise + */ +int mp_prime_is_prime (mp_int * a, int t, int *result) +{ + mp_int b; + int ix, err, res; + + /* default to no */ + *result = MP_NO; + + /* valid value of t? */ + if (t <= 0 || t > PRIME_SIZE) { + return MP_VAL; + } + + /* is the input equal to one of the primes in the table? */ + for (ix = 0; ix < PRIME_SIZE; ix++) { + if (mp_cmp_d(a, ltm_prime_tab[ix]) == MP_EQ) { + *result = 1; + return MP_OKAY; + } + } + + /* first perform trial division */ + if ((err = mp_prime_is_divisible (a, &res)) != MP_OKAY) { + return err; + } + + /* return if it was trivially divisible */ + if (res == MP_YES) { + return MP_OKAY; + } + + /* now perform the miller-rabin rounds */ + if ((err = mp_init (&b)) != MP_OKAY) { + return err; + } + + for (ix = 0; ix < t; ix++) { + /* set the prime */ + mp_set (&b, ltm_prime_tab[ix]); + + if ((err = mp_prime_miller_rabin (a, &b, &res)) != MP_OKAY) { + goto LBL_B; + } + + if (res == MP_NO) { + goto LBL_B; + } + } + + /* passed the test */ + *result = MP_YES; +LBL_B:mp_clear (&b); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_prime_is_prime.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_prime_is_prime.c */ + +/* Start: bn_mp_prime_miller_rabin.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_PRIME_MILLER_RABIN_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* Miller-Rabin test of "a" to the base of "b" as described in + * HAC pp. 139 Algorithm 4.24 + * + * Sets result to 0 if definitely composite or 1 if probably prime. + * Randomly the chance of error is no more than 1/4 and often + * very much lower. + */ +int mp_prime_miller_rabin (mp_int * a, mp_int * b, int *result) +{ + mp_int n1, y, r; + int s, j, err; + + /* default */ + *result = MP_NO; + + /* ensure b > 1 */ + if (mp_cmp_d(b, 1) != MP_GT) { + return MP_VAL; + } + + /* get n1 = a - 1 */ + if ((err = mp_init_copy (&n1, a)) != MP_OKAY) { + return err; + } + if ((err = mp_sub_d (&n1, 1, &n1)) != MP_OKAY) { + goto LBL_N1; + } + + /* set 2**s * r = n1 */ + if ((err = mp_init_copy (&r, &n1)) != MP_OKAY) { + goto LBL_N1; + } + + /* count the number of least significant bits + * which are zero + */ + s = mp_cnt_lsb(&r); + + /* now divide n - 1 by 2**s */ + if ((err = mp_div_2d (&r, s, &r, NULL)) != MP_OKAY) { + goto LBL_R; + } + + /* compute y = b**r mod a */ + if ((err = mp_init (&y)) != MP_OKAY) { + goto LBL_R; + } + if ((err = mp_exptmod (b, &r, a, &y)) != MP_OKAY) { + goto LBL_Y; + } + + /* if y != 1 and y != n1 do */ + if (mp_cmp_d (&y, 1) != MP_EQ && mp_cmp (&y, &n1) != MP_EQ) { + j = 1; + /* while j <= s-1 and y != n1 */ + while ((j <= (s - 1)) && mp_cmp (&y, &n1) != MP_EQ) { + if ((err = mp_sqrmod (&y, a, &y)) != MP_OKAY) { + goto LBL_Y; + } + + /* if y == 1 then composite */ + if (mp_cmp_d (&y, 1) == MP_EQ) { + goto LBL_Y; + } + + ++j; + } + + /* if y != n1 then composite */ + if (mp_cmp (&y, &n1) != MP_EQ) { + goto LBL_Y; + } + } + + /* probably prime now */ + *result = MP_YES; +LBL_Y:mp_clear (&y); +LBL_R:mp_clear (&r); +LBL_N1:mp_clear (&n1); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_prime_miller_rabin.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_prime_miller_rabin.c */ + +/* Start: bn_mp_prime_next_prime.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_PRIME_NEXT_PRIME_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* finds the next prime after the number "a" using "t" trials + * of Miller-Rabin. + * + * bbs_style = 1 means the prime must be congruent to 3 mod 4 + */ +int mp_prime_next_prime(mp_int *a, int t, int bbs_style) +{ + int err, res, x, y; + mp_digit res_tab[PRIME_SIZE], step, kstep; + mp_int b; + + /* ensure t is valid */ + if (t <= 0 || t > PRIME_SIZE) { + return MP_VAL; + } + + /* force positive */ + a->sign = MP_ZPOS; + + /* simple algo if a is less than the largest prime in the table */ + if (mp_cmp_d(a, ltm_prime_tab[PRIME_SIZE-1]) == MP_LT) { + /* find which prime it is bigger than */ + for (x = PRIME_SIZE - 2; x >= 0; x--) { + if (mp_cmp_d(a, ltm_prime_tab[x]) != MP_LT) { + if (bbs_style == 1) { + /* ok we found a prime smaller or + * equal [so the next is larger] + * + * however, the prime must be + * congruent to 3 mod 4 + */ + if ((ltm_prime_tab[x + 1] & 3) != 3) { + /* scan upwards for a prime congruent to 3 mod 4 */ + for (y = x + 1; y < PRIME_SIZE; y++) { + if ((ltm_prime_tab[y] & 3) == 3) { + mp_set(a, ltm_prime_tab[y]); + return MP_OKAY; + } + } + } + } else { + mp_set(a, ltm_prime_tab[x + 1]); + return MP_OKAY; + } + } + } + /* at this point a maybe 1 */ + if (mp_cmp_d(a, 1) == MP_EQ) { + mp_set(a, 2); + return MP_OKAY; + } + /* fall through to the sieve */ + } + + /* generate a prime congruent to 3 mod 4 or 1/3 mod 4? */ + if (bbs_style == 1) { + kstep = 4; + } else { + kstep = 2; + } + + /* at this point we will use a combination of a sieve and Miller-Rabin */ + + if (bbs_style == 1) { + /* if a mod 4 != 3 subtract the correct value to make it so */ + if ((a->dp[0] & 3) != 3) { + if ((err = mp_sub_d(a, (a->dp[0] & 3) + 1, a)) != MP_OKAY) { return err; }; + } + } else { + if (mp_iseven(a) == 1) { + /* force odd */ + if ((err = mp_sub_d(a, 1, a)) != MP_OKAY) { + return err; + } + } + } + + /* generate the restable */ + for (x = 1; x < PRIME_SIZE; x++) { + if ((err = mp_mod_d(a, ltm_prime_tab[x], res_tab + x)) != MP_OKAY) { + return err; + } + } + + /* init temp used for Miller-Rabin Testing */ + if ((err = mp_init(&b)) != MP_OKAY) { + return err; + } + + for (;;) { + /* skip to the next non-trivially divisible candidate */ + step = 0; + do { + /* y == 1 if any residue was zero [e.g. cannot be prime] */ + y = 0; + + /* increase step to next candidate */ + step += kstep; + + /* compute the new residue without using division */ + for (x = 1; x < PRIME_SIZE; x++) { + /* add the step to each residue */ + res_tab[x] += kstep; + + /* subtract the modulus [instead of using division] */ + if (res_tab[x] >= ltm_prime_tab[x]) { + res_tab[x] -= ltm_prime_tab[x]; + } + + /* set flag if zero */ + if (res_tab[x] == 0) { + y = 1; + } + } + } while (y == 1 && step < ((((mp_digit)1)<= ((((mp_digit)1)< size) { + return (x == 0) ? sizes[0].t : sizes[x - 1].t; + } + } + return sizes[x-1].t + 1; +} + + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_prime_rabin_miller_trials.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_prime_rabin_miller_trials.c */ + +/* Start: bn_mp_prime_random_ex.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_PRIME_RANDOM_EX_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* makes a truly random prime of a given size (bits), + * + * Flags are as follows: + * + * LTM_PRIME_BBS - make prime congruent to 3 mod 4 + * LTM_PRIME_SAFE - make sure (p-1)/2 is prime as well (implies LTM_PRIME_BBS) + * LTM_PRIME_2MSB_OFF - make the 2nd highest bit zero + * LTM_PRIME_2MSB_ON - make the 2nd highest bit one + * + * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can + * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself + * so it can be NULL + * + */ + +/* This is possibly the mother of all prime generation functions, muahahahahaha! */ +int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat) +{ + unsigned char *tmp, maskAND, maskOR_msb, maskOR_lsb; + int res, err, bsize, maskOR_msb_offset; + + /* sanity check the input */ + if (size <= 1 || t <= 0) { + return MP_VAL; + } + + /* LTM_PRIME_SAFE implies LTM_PRIME_BBS */ + if (flags & LTM_PRIME_SAFE) { + flags |= LTM_PRIME_BBS; + } + + /* calc the byte size */ + bsize = (size>>3) + ((size&7)?1:0); + + /* we need a buffer of bsize bytes */ + tmp = OPT_CAST(unsigned char) XMALLOC(bsize); + if (tmp == NULL) { + return MP_MEM; + } + + /* calc the maskAND value for the MSbyte*/ + maskAND = ((size&7) == 0) ? 0xFF : (0xFF >> (8 - (size & 7))); + + /* calc the maskOR_msb */ + maskOR_msb = 0; + maskOR_msb_offset = ((size & 7) == 1) ? 1 : 0; + if (flags & LTM_PRIME_2MSB_ON) { + maskOR_msb |= 0x80 >> ((9 - size) & 7); + } + + /* get the maskOR_lsb */ + maskOR_lsb = 1; + if (flags & LTM_PRIME_BBS) { + maskOR_lsb |= 3; + } + + do { + /* read the bytes */ + if (cb(tmp, bsize, dat) != bsize) { + err = MP_VAL; + goto error; + } + + /* work over the MSbyte */ + tmp[0] &= maskAND; + tmp[0] |= 1 << ((size - 1) & 7); + + /* mix in the maskORs */ + tmp[maskOR_msb_offset] |= maskOR_msb; + tmp[bsize-1] |= maskOR_lsb; + + /* read it in */ + if ((err = mp_read_unsigned_bin(a, tmp, bsize)) != MP_OKAY) { goto error; } + + /* is it prime? */ + if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) { goto error; } + if (res == MP_NO) { + continue; + } + + if (flags & LTM_PRIME_SAFE) { + /* see if (a-1)/2 is prime */ + if ((err = mp_sub_d(a, 1, a)) != MP_OKAY) { goto error; } + if ((err = mp_div_2(a, a)) != MP_OKAY) { goto error; } + + /* is it prime? */ + if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) { goto error; } + } + } while (res == MP_NO); + + if (flags & LTM_PRIME_SAFE) { + /* restore a to the original value */ + if ((err = mp_mul_2(a, a)) != MP_OKAY) { goto error; } + if ((err = mp_add_d(a, 1, a)) != MP_OKAY) { goto error; } + } + + err = MP_OKAY; +error: + XFREE(tmp); + return err; +} + + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_prime_random_ex.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_prime_random_ex.c */ + +/* Start: bn_mp_radix_size.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_RADIX_SIZE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* returns size of ASCII reprensentation */ +int mp_radix_size (mp_int * a, int radix, int *size) +{ + int res, digs; + mp_int t; + mp_digit d; + + *size = 0; + + /* special case for binary */ + if (radix == 2) { + *size = mp_count_bits (a) + (a->sign == MP_NEG ? 1 : 0) + 1; + return MP_OKAY; + } + + /* make sure the radix is in range */ + if (radix < 2 || radix > 64) { + return MP_VAL; + } + + if (mp_iszero(a) == MP_YES) { + *size = 2; + return MP_OKAY; + } + + /* digs is the digit count */ + digs = 0; + + /* if it's negative add one for the sign */ + if (a->sign == MP_NEG) { + ++digs; + } + + /* init a copy of the input */ + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + + /* force temp to positive */ + t.sign = MP_ZPOS; + + /* fetch out all of the digits */ + while (mp_iszero (&t) == MP_NO) { + if ((res = mp_div_d (&t, (mp_digit) radix, &t, &d)) != MP_OKAY) { + mp_clear (&t); + return res; + } + ++digs; + } + mp_clear (&t); + + /* return digs + 1, the 1 is for the NULL byte that would be required. */ + *size = digs + 1; + return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_radix_size.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_radix_size.c */ + +/* Start: bn_mp_radix_smap.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_RADIX_SMAP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* chars used in radix conversions */ +const char *mp_s_rmap = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_radix_smap.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_radix_smap.c */ + +/* Start: bn_mp_rand.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_RAND_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* makes a pseudo-random int of a given size */ +int +mp_rand (mp_int * a, int digits) +{ + int res; + mp_digit d; + + mp_zero (a); + if (digits <= 0) { + return MP_OKAY; + } + + /* first place a random non-zero digit */ + do { + d = ((mp_digit) abs (rand ())) & MP_MASK; + } while (d == 0); + + if ((res = mp_add_d (a, d, a)) != MP_OKAY) { + return res; + } + + while (--digits > 0) { + if ((res = mp_lshd (a, 1)) != MP_OKAY) { + return res; + } + + if ((res = mp_add_d (a, ((mp_digit) abs (rand ())), a)) != MP_OKAY) { + return res; + } + } + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_rand.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_rand.c */ + +/* Start: bn_mp_read_radix.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_READ_RADIX_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* read a string [ASCII] in a given radix */ +int mp_read_radix (mp_int * a, const char *str, int radix) +{ + int y, res, neg; + char ch; + + /* zero the digit bignum */ + mp_zero(a); + + /* make sure the radix is ok */ + if (radix < 2 || radix > 64) { + return MP_VAL; + } + + /* if the leading digit is a + * minus set the sign to negative. + */ + if (*str == '-') { + ++str; + neg = MP_NEG; + } else { + neg = MP_ZPOS; + } + + /* set the integer to the default of zero */ + mp_zero (a); + + /* process each digit of the string */ + while (*str) { + /* if the radix < 36 the conversion is case insensitive + * this allows numbers like 1AB and 1ab to represent the same value + * [e.g. in hex] + */ + ch = (char) ((radix < 36) ? toupper (*str) : *str); + for (y = 0; y < 64; y++) { + if (ch == mp_s_rmap[y]) { + break; + } + } + + /* if the char was found in the map + * and is less than the given radix add it + * to the number, otherwise exit the loop. + */ + if (y < radix) { + if ((res = mp_mul_d (a, (mp_digit) radix, a)) != MP_OKAY) { + return res; + } + if ((res = mp_add_d (a, (mp_digit) y, a)) != MP_OKAY) { + return res; + } + } else { + break; + } + ++str; + } + + /* set the sign only if a != 0 */ + if (mp_iszero(a) != 1) { + a->sign = neg; + } + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_read_radix.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_read_radix.c */ + +/* Start: bn_mp_read_signed_bin.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_READ_SIGNED_BIN_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* read signed bin, big endian, first byte is 0==positive or 1==negative */ +int mp_read_signed_bin (mp_int * a, const unsigned char *b, int c) +{ + int res; + + /* read magnitude */ + if ((res = mp_read_unsigned_bin (a, b + 1, c - 1)) != MP_OKAY) { + return res; + } + + /* first byte is 0 for positive, non-zero for negative */ + if (b[0] == 0) { + a->sign = MP_ZPOS; + } else { + a->sign = MP_NEG; + } + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_read_signed_bin.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_read_signed_bin.c */ + +/* Start: bn_mp_read_unsigned_bin.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_READ_UNSIGNED_BIN_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* reads a unsigned char array, assumes the msb is stored first [big endian] */ +int mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c) +{ + int res; + + /* make sure there are at least two digits */ + if (a->alloc < 2) { + if ((res = mp_grow(a, 2)) != MP_OKAY) { + return res; + } + } + + /* zero the int */ + mp_zero (a); + + /* read the bytes in */ + while (c-- > 0) { + if ((res = mp_mul_2d (a, 8, a)) != MP_OKAY) { + return res; + } + +#ifndef MP_8BIT + a->dp[0] |= *b++; + a->used += 1; +#else + a->dp[0] = (*b & MP_MASK); + a->dp[1] |= ((*b++ >> 7U) & 1); + a->used += 2; +#endif + } + mp_clamp (a); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_read_unsigned_bin.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_read_unsigned_bin.c */ + +/* Start: bn_mp_reduce.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_REDUCE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* reduces x mod m, assumes 0 < x < m**2, mu is + * precomputed via mp_reduce_setup. + * From HAC pp.604 Algorithm 14.42 + */ +int mp_reduce (mp_int * x, mp_int * m, mp_int * mu) +{ + mp_int q; + int res, um = m->used; + + /* q = x */ + if ((res = mp_init_copy (&q, x)) != MP_OKAY) { + return res; + } + + /* q1 = x / b**(k-1) */ + mp_rshd (&q, um - 1); + + /* according to HAC this optimization is ok */ + if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) { + if ((res = mp_mul (&q, mu, &q)) != MP_OKAY) { + goto CLEANUP; + } + } else { +#ifdef BN_S_MP_MUL_HIGH_DIGS_C + if ((res = s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { + goto CLEANUP; + } +#elif defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) + if ((res = fast_s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { + goto CLEANUP; + } +#else + { + res = MP_VAL; + goto CLEANUP; + } +#endif + } + + /* q3 = q2 / b**(k+1) */ + mp_rshd (&q, um + 1); + + /* x = x mod b**(k+1), quick (no division) */ + if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) { + goto CLEANUP; + } + + /* q = q * m mod b**(k+1), quick (no division) */ + if ((res = s_mp_mul_digs (&q, m, &q, um + 1)) != MP_OKAY) { + goto CLEANUP; + } + + /* x = x - q */ + if ((res = mp_sub (x, &q, x)) != MP_OKAY) { + goto CLEANUP; + } + + /* If x < 0, add b**(k+1) to it */ + if (mp_cmp_d (x, 0) == MP_LT) { + mp_set (&q, 1); + if ((res = mp_lshd (&q, um + 1)) != MP_OKAY) + goto CLEANUP; + if ((res = mp_add (x, &q, x)) != MP_OKAY) + goto CLEANUP; + } + + /* Back off if it's too big */ + while (mp_cmp (x, m) != MP_LT) { + if ((res = s_mp_sub (x, m, x)) != MP_OKAY) { + goto CLEANUP; + } + } + +CLEANUP: + mp_clear (&q); + + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_reduce.c */ + +/* Start: bn_mp_reduce_2k.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_REDUCE_2K_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* reduces a modulo n where n is of the form 2**p - d */ +int mp_reduce_2k(mp_int *a, mp_int *n, mp_digit d) +{ + mp_int q; + int p, res; + + if ((res = mp_init(&q)) != MP_OKAY) { + return res; + } + + p = mp_count_bits(n); +top: + /* q = a/2**p, a = a mod 2**p */ + if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { + goto ERR; + } + + if (d != 1) { + /* q = q * d */ + if ((res = mp_mul_d(&q, d, &q)) != MP_OKAY) { + goto ERR; + } + } + + /* a = a + q */ + if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { + goto ERR; + } + + if (mp_cmp_mag(a, n) != MP_LT) { + s_mp_sub(a, n, a); + goto top; + } + +ERR: + mp_clear(&q); + return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce_2k.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_reduce_2k.c */ + +/* Start: bn_mp_reduce_2k_l.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_REDUCE_2K_L_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* reduces a modulo n where n is of the form 2**p - d + This differs from reduce_2k since "d" can be larger + than a single digit. +*/ +int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d) +{ + mp_int q; + int p, res; + + if ((res = mp_init(&q)) != MP_OKAY) { + return res; + } + + p = mp_count_bits(n); +top: + /* q = a/2**p, a = a mod 2**p */ + if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { + goto ERR; + } + + /* q = q * d */ + if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { + goto ERR; + } + + /* a = a + q */ + if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { + goto ERR; + } + + if (mp_cmp_mag(a, n) != MP_LT) { + s_mp_sub(a, n, a); + goto top; + } + +ERR: + mp_clear(&q); + return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce_2k_l.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_reduce_2k_l.c */ + +/* Start: bn_mp_reduce_2k_setup.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_REDUCE_2K_SETUP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* determines the setup value */ +int mp_reduce_2k_setup(mp_int *a, mp_digit *d) +{ + int res, p; + mp_int tmp; + + if ((res = mp_init(&tmp)) != MP_OKAY) { + return res; + } + + p = mp_count_bits(a); + if ((res = mp_2expt(&tmp, p)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + + if ((res = s_mp_sub(&tmp, a, &tmp)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + + *d = tmp.dp[0]; + mp_clear(&tmp); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce_2k_setup.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_reduce_2k_setup.c */ + +/* Start: bn_mp_reduce_2k_setup_l.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_REDUCE_2K_SETUP_L_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* determines the setup value */ +int mp_reduce_2k_setup_l(mp_int *a, mp_int *d) +{ + int res; + mp_int tmp; + + if ((res = mp_init(&tmp)) != MP_OKAY) { + return res; + } + + if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) { + goto ERR; + } + + if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) { + goto ERR; + } + +ERR: + mp_clear(&tmp); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce_2k_setup_l.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_reduce_2k_setup_l.c */ + +/* Start: bn_mp_reduce_is_2k.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_REDUCE_IS_2K_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* determines if mp_reduce_2k can be used */ +int mp_reduce_is_2k(mp_int *a) +{ + int ix, iy, iw; + mp_digit iz; + + if (a->used == 0) { + return MP_NO; + } else if (a->used == 1) { + return MP_YES; + } else if (a->used > 1) { + iy = mp_count_bits(a); + iz = 1; + iw = 1; + + /* Test every bit from the second digit up, must be 1 */ + for (ix = DIGIT_BIT; ix < iy; ix++) { + if ((a->dp[iw] & iz) == 0) { + return MP_NO; + } + iz <<= 1; + if (iz > (mp_digit)MP_MASK) { + ++iw; + iz = 1; + } + } + } + return MP_YES; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce_is_2k.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_reduce_is_2k.c */ + +/* Start: bn_mp_reduce_is_2k_l.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_REDUCE_IS_2K_L_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* determines if reduce_2k_l can be used */ +int mp_reduce_is_2k_l(mp_int *a) +{ + int ix, iy; + + if (a->used == 0) { + return MP_NO; + } else if (a->used == 1) { + return MP_YES; + } else if (a->used > 1) { + /* if more than half of the digits are -1 we're sold */ + for (iy = ix = 0; ix < a->used; ix++) { + if (a->dp[ix] == MP_MASK) { + ++iy; + } + } + return (iy >= (a->used/2)) ? MP_YES : MP_NO; + + } + return MP_NO; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce_is_2k_l.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_reduce_is_2k_l.c */ + +/* Start: bn_mp_reduce_setup.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_REDUCE_SETUP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* pre-calculate the value required for Barrett reduction + * For a given modulus "b" it calulates the value required in "a" + */ +int mp_reduce_setup (mp_int * a, mp_int * b) +{ + int res; + + if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) { + return res; + } + return mp_div (a, b, a, NULL); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce_setup.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_reduce_setup.c */ + +/* Start: bn_mp_rshd.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_RSHD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* shift right a certain amount of digits */ +void mp_rshd (mp_int * a, int b) +{ + int x; + + /* if b <= 0 then ignore it */ + if (b <= 0) { + return; + } + + /* if b > used then simply zero it and return */ + if (a->used <= b) { + mp_zero (a); + return; + } + + { + register mp_digit *bottom, *top; + + /* shift the digits down */ + + /* bottom */ + bottom = a->dp; + + /* top [offset into digits] */ + top = a->dp + b; + + /* this is implemented as a sliding window where + * the window is b-digits long and digits from + * the top of the window are copied to the bottom + * + * e.g. + + b-2 | b-1 | b0 | b1 | b2 | ... | bb | ----> + /\ | ----> + \-------------------/ ----> + */ + for (x = 0; x < (a->used - b); x++) { + *bottom++ = *top++; + } + + /* zero the top digits */ + for (; x < a->used; x++) { + *bottom++ = 0; + } + } + + /* remove excess digits */ + a->used -= b; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_rshd.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_rshd.c */ + +/* Start: bn_mp_set.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_SET_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* set to a digit */ +void mp_set (mp_int * a, mp_digit b) +{ + mp_zero (a); + a->dp[0] = b & MP_MASK; + a->used = (a->dp[0] != 0) ? 1 : 0; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_set.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_set.c */ + +/* Start: bn_mp_set_int.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_SET_INT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* set a 32-bit const */ +int mp_set_int (mp_int * a, unsigned long b) +{ + int x, res; + + mp_zero (a); + + /* set four bits at a time */ + for (x = 0; x < 8; x++) { + /* shift the number up four bits */ + if ((res = mp_mul_2d (a, 4, a)) != MP_OKAY) { + return res; + } + + /* OR in the top four bits of the source */ + a->dp[0] |= (b >> 28) & 15; + + /* shift the source up to the next four bits */ + b <<= 4; + + /* ensure that digits are not clamped off */ + a->used += 1; + } + mp_clamp (a); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_set_int.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_set_int.c */ + +/* Start: bn_mp_shrink.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_SHRINK_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* shrink a bignum */ +int mp_shrink (mp_int * a) +{ + mp_digit *tmp; + if (a->alloc != a->used && a->used > 0) { + if ((tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * a->used)) == NULL) { + return MP_MEM; + } + a->dp = tmp; + a->alloc = a->used; + } + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_shrink.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_shrink.c */ + +/* Start: bn_mp_signed_bin_size.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_SIGNED_BIN_SIZE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* get the size for an signed equivalent */ +int mp_signed_bin_size (mp_int * a) +{ + return 1 + mp_unsigned_bin_size (a); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_signed_bin_size.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_signed_bin_size.c */ + +/* Start: bn_mp_sqr.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_SQR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* computes b = a*a */ +int +mp_sqr (mp_int * a, mp_int * b) +{ + int res; + +#ifdef BN_MP_TOOM_SQR_C + /* use Toom-Cook? */ + if (a->used >= TOOM_SQR_CUTOFF) { + res = mp_toom_sqr(a, b); + /* Karatsuba? */ + } else +#endif +#ifdef BN_MP_KARATSUBA_SQR_C +if (a->used >= KARATSUBA_SQR_CUTOFF) { + res = mp_karatsuba_sqr (a, b); + } else +#endif + { +#ifdef BN_FAST_S_MP_SQR_C + /* can we use the fast comba multiplier? */ + if ((a->used * 2 + 1) < MP_WARRAY && + a->used < + (1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) { + res = fast_s_mp_sqr (a, b); + } else +#endif +#ifdef BN_S_MP_SQR_C + res = s_mp_sqr (a, b); +#else + res = MP_VAL; +#endif + } + b->sign = MP_ZPOS; + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_sqr.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_sqr.c */ + +/* Start: bn_mp_sqrmod.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_SQRMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* c = a * a (mod b) */ +int +mp_sqrmod (mp_int * a, mp_int * b, mp_int * c) +{ + int res; + mp_int t; + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_sqr (a, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + res = mp_mod (&t, b, c); + mp_clear (&t); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_sqrmod.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_sqrmod.c */ + +/* Start: bn_mp_sqrt.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_SQRT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* this function is less generic than mp_n_root, simpler and faster */ +int mp_sqrt(mp_int *arg, mp_int *ret) +{ + int res; + mp_int t1,t2; + + /* must be positive */ + if (arg->sign == MP_NEG) { + return MP_VAL; + } + + /* easy out */ + if (mp_iszero(arg) == MP_YES) { + mp_zero(ret); + return MP_OKAY; + } + + if ((res = mp_init_copy(&t1, arg)) != MP_OKAY) { + return res; + } + + if ((res = mp_init(&t2)) != MP_OKAY) { + goto E2; + } + + /* First approx. (not very bad for large arg) */ + mp_rshd (&t1,t1.used/2); + + /* t1 > 0 */ + if ((res = mp_div(arg,&t1,&t2,NULL)) != MP_OKAY) { + goto E1; + } + if ((res = mp_add(&t1,&t2,&t1)) != MP_OKAY) { + goto E1; + } + if ((res = mp_div_2(&t1,&t1)) != MP_OKAY) { + goto E1; + } + /* And now t1 > sqrt(arg) */ + do { + if ((res = mp_div(arg,&t1,&t2,NULL)) != MP_OKAY) { + goto E1; + } + if ((res = mp_add(&t1,&t2,&t1)) != MP_OKAY) { + goto E1; + } + if ((res = mp_div_2(&t1,&t1)) != MP_OKAY) { + goto E1; + } + /* t1 >= sqrt(arg) >= t2 at this point */ + } while (mp_cmp_mag(&t1,&t2) == MP_GT); + + mp_exch(&t1,ret); + +E1: mp_clear(&t2); +E2: mp_clear(&t1); + return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_sqrt.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_sqrt.c */ + +/* Start: bn_mp_sub.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_SUB_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* high level subtraction (handles signs) */ +int +mp_sub (mp_int * a, mp_int * b, mp_int * c) +{ + int sa, sb, res; + + sa = a->sign; + sb = b->sign; + + if (sa != sb) { + /* subtract a negative from a positive, OR */ + /* subtract a positive from a negative. */ + /* In either case, ADD their magnitudes, */ + /* and use the sign of the first number. */ + c->sign = sa; + res = s_mp_add (a, b, c); + } else { + /* subtract a positive from a positive, OR */ + /* subtract a negative from a negative. */ + /* First, take the difference between their */ + /* magnitudes, then... */ + if (mp_cmp_mag (a, b) != MP_LT) { + /* Copy the sign from the first */ + c->sign = sa; + /* The first has a larger or equal magnitude */ + res = s_mp_sub (a, b, c); + } else { + /* The result has the *opposite* sign from */ + /* the first number. */ + c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS; + /* The second has a larger magnitude */ + res = s_mp_sub (b, a, c); + } + } + return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_sub.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_sub.c */ + +/* Start: bn_mp_sub_d.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_SUB_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* single digit subtraction */ +int +mp_sub_d (mp_int * a, mp_digit b, mp_int * c) +{ + mp_digit *tmpa, *tmpc, mu; + int res, ix, oldused; + + /* grow c as required */ + if (c->alloc < a->used + 1) { + if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* if a is negative just do an unsigned + * addition [with fudged signs] + */ + if (a->sign == MP_NEG) { + a->sign = MP_ZPOS; + res = mp_add_d(a, b, c); + a->sign = c->sign = MP_NEG; + + /* clamp */ + mp_clamp(c); + + return res; + } + + /* setup regs */ + oldused = c->used; + tmpa = a->dp; + tmpc = c->dp; + + /* if a <= b simply fix the single digit */ + if ((a->used == 1 && a->dp[0] <= b) || a->used == 0) { + if (a->used == 1) { + *tmpc++ = b - *tmpa; + } else { + *tmpc++ = b; + } + ix = 1; + + /* negative/1digit */ + c->sign = MP_NEG; + c->used = 1; + } else { + /* positive/size */ + c->sign = MP_ZPOS; + c->used = a->used; + + /* subtract first digit */ + *tmpc = *tmpa++ - b; + mu = *tmpc >> (sizeof(mp_digit) * CHAR_BIT - 1); + *tmpc++ &= MP_MASK; + + /* handle rest of the digits */ + for (ix = 1; ix < a->used; ix++) { + *tmpc = *tmpa++ - mu; + mu = *tmpc >> (sizeof(mp_digit) * CHAR_BIT - 1); + *tmpc++ &= MP_MASK; + } + } + + /* zero excess digits */ + while (ix++ < oldused) { + *tmpc++ = 0; + } + mp_clamp(c); + return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_sub_d.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_sub_d.c */ + +/* Start: bn_mp_submod.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_SUBMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* d = a - b (mod c) */ +int +mp_submod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + int res; + mp_int t; + + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_sub (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + res = mp_mod (&t, c, d); + mp_clear (&t); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_submod.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_submod.c */ + +/* Start: bn_mp_to_signed_bin.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_TO_SIGNED_BIN_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* store in signed [big endian] format */ +int mp_to_signed_bin (mp_int * a, unsigned char *b) +{ + int res; + + if ((res = mp_to_unsigned_bin (a, b + 1)) != MP_OKAY) { + return res; + } + b[0] = (unsigned char) ((a->sign == MP_ZPOS) ? 0 : 1); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_to_signed_bin.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_to_signed_bin.c */ + +/* Start: bn_mp_to_signed_bin_n.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_TO_SIGNED_BIN_N_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* store in signed [big endian] format */ +int mp_to_signed_bin_n (mp_int * a, unsigned char *b, unsigned long *outlen) +{ + if (*outlen < (unsigned long)mp_signed_bin_size(a)) { + return MP_VAL; + } + *outlen = mp_signed_bin_size(a); + return mp_to_signed_bin(a, b); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_to_signed_bin_n.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_to_signed_bin_n.c */ + +/* Start: bn_mp_to_unsigned_bin.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_TO_UNSIGNED_BIN_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* store in unsigned [big endian] format */ +int mp_to_unsigned_bin (mp_int * a, unsigned char *b) +{ + int x, res; + mp_int t; + + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + + x = 0; + while (mp_iszero (&t) == 0) { +#ifndef MP_8BIT + b[x++] = (unsigned char) (t.dp[0] & 255); +#else + b[x++] = (unsigned char) (t.dp[0] | ((t.dp[1] & 0x01) << 7)); +#endif + if ((res = mp_div_2d (&t, 8, &t, NULL)) != MP_OKAY) { + mp_clear (&t); + return res; + } + } + bn_reverse (b, x); + mp_clear (&t); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_to_unsigned_bin.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_to_unsigned_bin.c */ + +/* Start: bn_mp_to_unsigned_bin_n.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_TO_UNSIGNED_BIN_N_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* store in unsigned [big endian] format */ +int mp_to_unsigned_bin_n (mp_int * a, unsigned char *b, unsigned long *outlen) +{ + if (*outlen < (unsigned long)mp_unsigned_bin_size(a)) { + return MP_VAL; + } + *outlen = mp_unsigned_bin_size(a); + return mp_to_unsigned_bin(a, b); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_to_unsigned_bin_n.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_to_unsigned_bin_n.c */ + +/* Start: bn_mp_toom_mul.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_TOOM_MUL_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* multiplication using the Toom-Cook 3-way algorithm + * + * Much more complicated than Karatsuba but has a lower + * asymptotic running time of O(N**1.464). This algorithm is + * only particularly useful on VERY large inputs + * (we're talking 1000s of digits here...). +*/ +int mp_toom_mul(mp_int *a, mp_int *b, mp_int *c) +{ + mp_int w0, w1, w2, w3, w4, tmp1, tmp2, a0, a1, a2, b0, b1, b2; + int res, B; + + /* init temps */ + if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4, + &a0, &a1, &a2, &b0, &b1, + &b2, &tmp1, &tmp2, NULL)) != MP_OKAY) { + return res; + } + + /* B */ + B = MIN(a->used, b->used) / 3; + + /* a = a2 * B**2 + a1 * B + a0 */ + if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(a, &a1)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a1, B); + mp_mod_2d(&a1, DIGIT_BIT * B, &a1); + + if ((res = mp_copy(a, &a2)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a2, B*2); + + /* b = b2 * B**2 + b1 * B + b0 */ + if ((res = mp_mod_2d(b, DIGIT_BIT * B, &b0)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(b, &b1)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&b1, B); + mp_mod_2d(&b1, DIGIT_BIT * B, &b1); + + if ((res = mp_copy(b, &b2)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&b2, B*2); + + /* w0 = a0*b0 */ + if ((res = mp_mul(&a0, &b0, &w0)) != MP_OKAY) { + goto ERR; + } + + /* w4 = a2 * b2 */ + if ((res = mp_mul(&a2, &b2, &w4)) != MP_OKAY) { + goto ERR; + } + + /* w1 = (a2 + 2(a1 + 2a0))(b2 + 2(b1 + 2b0)) */ + if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul_2(&b0, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b2, &tmp2)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul(&tmp1, &tmp2, &w1)) != MP_OKAY) { + goto ERR; + } + + /* w3 = (a0 + 2(a1 + 2a2))(b0 + 2(b1 + 2b2)) */ + if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul_2(&b2, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul(&tmp1, &tmp2, &w3)) != MP_OKAY) { + goto ERR; + } + + + /* w2 = (a2 + a1 + a0)(b2 + b1 + b0) */ + if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&b2, &b1, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul(&tmp1, &tmp2, &w2)) != MP_OKAY) { + goto ERR; + } + + /* now solve the matrix + + 0 0 0 0 1 + 1 2 4 8 16 + 1 1 1 1 1 + 16 8 4 2 1 + 1 0 0 0 0 + + using 12 subtractions, 4 shifts, + 2 small divisions and 1 small multiplication + */ + + /* r1 - r4 */ + if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r0 */ + if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/2 */ + if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3/2 */ + if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) { + goto ERR; + } + /* r2 - r0 - r4 */ + if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1 - 8r0 */ + if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - 8r4 */ + if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) { + goto ERR; + } + /* 3r2 - r1 - r3 */ + if ((res = mp_mul_d(&w2, 3, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/3 */ + if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) { + goto ERR; + } + /* r3/3 */ + if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) { + goto ERR; + } + + /* at this point shift W[n] by B*n */ + if ((res = mp_lshd(&w1, 1*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w2, 2*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w3, 3*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w4, 4*B)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_add(&w0, &w1, c)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, c, c)) != MP_OKAY) { + goto ERR; + } + +ERR: + mp_clear_multi(&w0, &w1, &w2, &w3, &w4, + &a0, &a1, &a2, &b0, &b1, + &b2, &tmp1, &tmp2, NULL); + return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_toom_mul.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_toom_mul.c */ + +/* Start: bn_mp_toom_sqr.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_TOOM_SQR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* squaring using Toom-Cook 3-way algorithm */ +int +mp_toom_sqr(mp_int *a, mp_int *b) +{ + mp_int w0, w1, w2, w3, w4, tmp1, a0, a1, a2; + int res, B; + + /* init temps */ + if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL)) != MP_OKAY) { + return res; + } + + /* B */ + B = a->used / 3; + + /* a = a2 * B**2 + a1 * B + a0 */ + if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(a, &a1)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a1, B); + mp_mod_2d(&a1, DIGIT_BIT * B, &a1); + + if ((res = mp_copy(a, &a2)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a2, B*2); + + /* w0 = a0*a0 */ + if ((res = mp_sqr(&a0, &w0)) != MP_OKAY) { + goto ERR; + } + + /* w4 = a2 * a2 */ + if ((res = mp_sqr(&a2, &w4)) != MP_OKAY) { + goto ERR; + } + + /* w1 = (a2 + 2(a1 + 2a0))**2 */ + if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_sqr(&tmp1, &w1)) != MP_OKAY) { + goto ERR; + } + + /* w3 = (a0 + 2(a1 + 2a2))**2 */ + if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_sqr(&tmp1, &w3)) != MP_OKAY) { + goto ERR; + } + + + /* w2 = (a2 + a1 + a0)**2 */ + if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sqr(&tmp1, &w2)) != MP_OKAY) { + goto ERR; + } + + /* now solve the matrix + + 0 0 0 0 1 + 1 2 4 8 16 + 1 1 1 1 1 + 16 8 4 2 1 + 1 0 0 0 0 + + using 12 subtractions, 4 shifts, 2 small divisions and 1 small multiplication. + */ + + /* r1 - r4 */ + if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r0 */ + if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/2 */ + if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3/2 */ + if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) { + goto ERR; + } + /* r2 - r0 - r4 */ + if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1 - 8r0 */ + if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - 8r4 */ + if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) { + goto ERR; + } + /* 3r2 - r1 - r3 */ + if ((res = mp_mul_d(&w2, 3, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/3 */ + if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) { + goto ERR; + } + /* r3/3 */ + if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) { + goto ERR; + } + + /* at this point shift W[n] by B*n */ + if ((res = mp_lshd(&w1, 1*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w2, 2*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w3, 3*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w4, 4*B)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_add(&w0, &w1, b)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, b, b)) != MP_OKAY) { + goto ERR; + } + +ERR: + mp_clear_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL); + return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_toom_sqr.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_toom_sqr.c */ + +/* Start: bn_mp_toradix.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_TORADIX_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* stores a bignum as a ASCII string in a given radix (2..64) */ +int mp_toradix (mp_int * a, char *str, int radix) +{ + int res, digs; + mp_int t; + mp_digit d; + char *_s = str; + + /* check range of the radix */ + if (radix < 2 || radix > 64) { + return MP_VAL; + } + + /* quick out if its zero */ + if (mp_iszero(a) == 1) { + *str++ = '0'; + *str = '\0'; + return MP_OKAY; + } + + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + + /* if it is negative output a - */ + if (t.sign == MP_NEG) { + ++_s; + *str++ = '-'; + t.sign = MP_ZPOS; + } + + digs = 0; + while (mp_iszero (&t) == 0) { + if ((res = mp_div_d (&t, (mp_digit) radix, &t, &d)) != MP_OKAY) { + mp_clear (&t); + return res; + } + *str++ = mp_s_rmap[d]; + ++digs; + } + + /* reverse the digits of the string. In this case _s points + * to the first digit [exluding the sign] of the number] + */ + bn_reverse ((unsigned char *)_s, digs); + + /* append a NULL so the string is properly terminated */ + *str = '\0'; + + mp_clear (&t); + return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_toradix.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_toradix.c */ + +/* Start: bn_mp_toradix_n.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_TORADIX_N_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* stores a bignum as a ASCII string in a given radix (2..64) + * + * Stores upto maxlen-1 chars and always a NULL byte + */ +int mp_toradix_n(mp_int * a, char *str, int radix, int maxlen) +{ + int res, digs; + mp_int t; + mp_digit d; + char *_s = str; + + /* check range of the maxlen, radix */ + if (maxlen < 2 || radix < 2 || radix > 64) { + return MP_VAL; + } + + /* quick out if its zero */ + if (mp_iszero(a) == MP_YES) { + *str++ = '0'; + *str = '\0'; + return MP_OKAY; + } + + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + + /* if it is negative output a - */ + if (t.sign == MP_NEG) { + /* we have to reverse our digits later... but not the - sign!! */ + ++_s; + + /* store the flag and mark the number as positive */ + *str++ = '-'; + t.sign = MP_ZPOS; + + /* subtract a char */ + --maxlen; + } + + digs = 0; + while (mp_iszero (&t) == 0) { + if (--maxlen < 1) { + /* no more room */ + break; + } + if ((res = mp_div_d (&t, (mp_digit) radix, &t, &d)) != MP_OKAY) { + mp_clear (&t); + return res; + } + *str++ = mp_s_rmap[d]; + ++digs; + } + + /* reverse the digits of the string. In this case _s points + * to the first digit [exluding the sign] of the number + */ + bn_reverse ((unsigned char *)_s, digs); + + /* append a NULL so the string is properly terminated */ + *str = '\0'; + + mp_clear (&t); + return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_toradix_n.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_toradix_n.c */ + +/* Start: bn_mp_unsigned_bin_size.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_UNSIGNED_BIN_SIZE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* get the size for an unsigned equivalent */ +int mp_unsigned_bin_size (mp_int * a) +{ + int size = mp_count_bits (a); + return (size / 8 + ((size & 7) != 0 ? 1 : 0)); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_unsigned_bin_size.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_unsigned_bin_size.c */ + +/* Start: bn_mp_xor.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_XOR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* XOR two ints together */ +int +mp_xor (mp_int * a, mp_int * b, mp_int * c) +{ + int res, ix, px; + mp_int t, *x; + + if (a->used > b->used) { + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + px = b->used; + x = b; + } else { + if ((res = mp_init_copy (&t, b)) != MP_OKAY) { + return res; + } + px = a->used; + x = a; + } + + for (ix = 0; ix < px; ix++) { + t.dp[ix] ^= x->dp[ix]; + } + mp_clamp (&t); + mp_exch (c, &t); + mp_clear (&t); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_xor.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_xor.c */ + +/* Start: bn_mp_zero.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_ZERO_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* set to zero */ +void mp_zero (mp_int * a) +{ + int n; + mp_digit *tmp; + + a->sign = MP_ZPOS; + a->used = 0; + + tmp = a->dp; + for (n = 0; n < a->alloc; n++) { + *tmp++ = 0; + } +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_zero.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_zero.c */ + +/* Start: bn_prime_tab.c */ +#include "libtorrent/tommath.h" +#ifdef BN_PRIME_TAB_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ +const mp_digit ltm_prime_tab[] = { + 0x0002, 0x0003, 0x0005, 0x0007, 0x000B, 0x000D, 0x0011, 0x0013, + 0x0017, 0x001D, 0x001F, 0x0025, 0x0029, 0x002B, 0x002F, 0x0035, + 0x003B, 0x003D, 0x0043, 0x0047, 0x0049, 0x004F, 0x0053, 0x0059, + 0x0061, 0x0065, 0x0067, 0x006B, 0x006D, 0x0071, 0x007F, +#ifndef MP_8BIT + 0x0083, + 0x0089, 0x008B, 0x0095, 0x0097, 0x009D, 0x00A3, 0x00A7, 0x00AD, + 0x00B3, 0x00B5, 0x00BF, 0x00C1, 0x00C5, 0x00C7, 0x00D3, 0x00DF, + 0x00E3, 0x00E5, 0x00E9, 0x00EF, 0x00F1, 0x00FB, 0x0101, 0x0107, + 0x010D, 0x010F, 0x0115, 0x0119, 0x011B, 0x0125, 0x0133, 0x0137, + + 0x0139, 0x013D, 0x014B, 0x0151, 0x015B, 0x015D, 0x0161, 0x0167, + 0x016F, 0x0175, 0x017B, 0x017F, 0x0185, 0x018D, 0x0191, 0x0199, + 0x01A3, 0x01A5, 0x01AF, 0x01B1, 0x01B7, 0x01BB, 0x01C1, 0x01C9, + 0x01CD, 0x01CF, 0x01D3, 0x01DF, 0x01E7, 0x01EB, 0x01F3, 0x01F7, + 0x01FD, 0x0209, 0x020B, 0x021D, 0x0223, 0x022D, 0x0233, 0x0239, + 0x023B, 0x0241, 0x024B, 0x0251, 0x0257, 0x0259, 0x025F, 0x0265, + 0x0269, 0x026B, 0x0277, 0x0281, 0x0283, 0x0287, 0x028D, 0x0293, + 0x0295, 0x02A1, 0x02A5, 0x02AB, 0x02B3, 0x02BD, 0x02C5, 0x02CF, + + 0x02D7, 0x02DD, 0x02E3, 0x02E7, 0x02EF, 0x02F5, 0x02F9, 0x0301, + 0x0305, 0x0313, 0x031D, 0x0329, 0x032B, 0x0335, 0x0337, 0x033B, + 0x033D, 0x0347, 0x0355, 0x0359, 0x035B, 0x035F, 0x036D, 0x0371, + 0x0373, 0x0377, 0x038B, 0x038F, 0x0397, 0x03A1, 0x03A9, 0x03AD, + 0x03B3, 0x03B9, 0x03C7, 0x03CB, 0x03D1, 0x03D7, 0x03DF, 0x03E5, + 0x03F1, 0x03F5, 0x03FB, 0x03FD, 0x0407, 0x0409, 0x040F, 0x0419, + 0x041B, 0x0425, 0x0427, 0x042D, 0x043F, 0x0443, 0x0445, 0x0449, + 0x044F, 0x0455, 0x045D, 0x0463, 0x0469, 0x047F, 0x0481, 0x048B, + + 0x0493, 0x049D, 0x04A3, 0x04A9, 0x04B1, 0x04BD, 0x04C1, 0x04C7, + 0x04CD, 0x04CF, 0x04D5, 0x04E1, 0x04EB, 0x04FD, 0x04FF, 0x0503, + 0x0509, 0x050B, 0x0511, 0x0515, 0x0517, 0x051B, 0x0527, 0x0529, + 0x052F, 0x0551, 0x0557, 0x055D, 0x0565, 0x0577, 0x0581, 0x058F, + 0x0593, 0x0595, 0x0599, 0x059F, 0x05A7, 0x05AB, 0x05AD, 0x05B3, + 0x05BF, 0x05C9, 0x05CB, 0x05CF, 0x05D1, 0x05D5, 0x05DB, 0x05E7, + 0x05F3, 0x05FB, 0x0607, 0x060D, 0x0611, 0x0617, 0x061F, 0x0623, + 0x062B, 0x062F, 0x063D, 0x0641, 0x0647, 0x0649, 0x064D, 0x0653 +#endif +}; +#endif + +/* $Source: /cvs/libtom/libtommath/bn_prime_tab.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_prime_tab.c */ + +/* Start: bn_reverse.c */ +#include "libtorrent/tommath.h" +#ifdef BN_REVERSE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* reverse an array, used for radix code */ +void +bn_reverse (unsigned char *s, int len) +{ + int ix, iy; + unsigned char t; + + ix = 0; + iy = len - 1; + while (ix < iy) { + t = s[ix]; + s[ix] = s[iy]; + s[iy] = t; + ++ix; + --iy; + } +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_reverse.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_reverse.c */ + +/* Start: bn_s_mp_add.c */ +#include "libtorrent/tommath.h" +#ifdef BN_S_MP_ADD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* low level addition, based on HAC pp.594, Algorithm 14.7 */ +int +s_mp_add (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int *x; + int olduse, res, min, max; + + /* find sizes, we let |a| <= |b| which means we have to sort + * them. "x" will point to the input with the most digits + */ + if (a->used > b->used) { + min = b->used; + max = a->used; + x = a; + } else { + min = a->used; + max = b->used; + x = b; + } + + /* init result */ + if (c->alloc < max + 1) { + if ((res = mp_grow (c, max + 1)) != MP_OKAY) { + return res; + } + } + + /* get old used digit count and set new one */ + olduse = c->used; + c->used = max + 1; + + { + register mp_digit u, *tmpa, *tmpb, *tmpc; + register int i; + + /* alias for digit pointers */ + + /* first input */ + tmpa = a->dp; + + /* second input */ + tmpb = b->dp; + + /* destination */ + tmpc = c->dp; + + /* zero the carry */ + u = 0; + for (i = 0; i < min; i++) { + /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */ + *tmpc = *tmpa++ + *tmpb++ + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* now copy higher words if any, that is in A+B + * if A or B has more digits add those in + */ + if (min != max) { + for (; i < max; i++) { + /* T[i] = X[i] + U */ + *tmpc = x->dp[i] + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= MP_MASK; + } + } + + /* add carry */ + *tmpc++ = u; + + /* clear digits above oldused */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp (c); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_s_mp_add.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_s_mp_add.c */ + +/* Start: bn_s_mp_exptmod.c */ +#include "libtorrent/tommath.h" +#ifdef BN_S_MP_EXPTMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ +#ifdef MP_LOW_MEM + #define TAB_SIZE 32 +#else + #define TAB_SIZE 256 +#endif + +int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) +{ + mp_int M[TAB_SIZE], res, mu; + mp_digit buf; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + int (*redux)(mp_int*,mp_int*,mp_int*); + + /* find window size */ + x = mp_count_bits (X); + if (x <= 7) { + winsize = 2; + } else if (x <= 36) { + winsize = 3; + } else if (x <= 140) { + winsize = 4; + } else if (x <= 450) { + winsize = 5; + } else if (x <= 1303) { + winsize = 6; + } else if (x <= 3529) { + winsize = 7; + } else { + winsize = 8; + } + +#ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } +#endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1<<(winsize-1); y < x; y++) { + mp_clear (&M[y]); + } + mp_clear(&M[1]); + return err; + } + } + + /* create mu, used for Barrett reduction */ + if ((err = mp_init (&mu)) != MP_OKAY) { + goto LBL_M; + } + + if (redmode == 0) { + if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) { + goto LBL_MU; + } + redux = mp_reduce; + } else { + if ((err = mp_reduce_2k_setup_l (P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + redux = mp_reduce_2k_l; + } + + /* create M table + * + * The M table contains powers of the base, + * e.g. M[x] = G**x mod P + * + * The first half of the table is not + * computed though accept for M[0] and M[1] + */ + if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) { + goto LBL_MU; + } + + /* compute the value at M[1<<(winsize-1)] by squaring + * M[1] (winsize-1) times + */ + if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_MU; + } + + for (x = 0; x < (winsize - 1); x++) { + /* square it */ + if ((err = mp_sqr (&M[1 << (winsize - 1)], + &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_MU; + } + + /* reduce modulo P */ + if ((err = redux (&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + } + + /* create upper table, that is M[x] = M[x-1] * M[1] (mod P) + * for x = (2**(winsize - 1) + 1) to (2**winsize - 1) + */ + for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { + if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { + goto LBL_MU; + } + if ((err = redux (&M[x], P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + } + + /* setup result */ + if ((err = mp_init (&res)) != MP_OKAY) { + goto LBL_MU; + } + mp_set (&res, 1); + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for (;;) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits */ + if (digidx == -1) { + break; + } + /* read next digit and reset the bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int) DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)1; + + /* if the bit is zero and mode == 0 then we ignore it + * These represent the leading zero bits before the first 1 bit + * in the exponent. Technically this opt is not required but it + * does lower the # of trivial squaring/reductions used + */ + if (mode == 0 && y == 0) { + continue; + } + + /* if the bit is zero and mode == 1 then we square */ + if (mode == 1 && y == 0) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (y << (winsize - ++bitcpy)); + mode = 2; + + if (bitcpy == winsize) { + /* ok window is filled so square as required and multiply */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* then multiply */ + if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + + /* empty window and reset */ + bitcpy = 0; + bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then square/multiply */ + if (mode == 2 && bitcpy > 0) { + /* square then multiply if the bit is set */ + for (x = 0; x < bitcpy; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + } + } + } + + mp_exch (&res, Y); + err = MP_OKAY; +LBL_RES:mp_clear (&res); +LBL_MU:mp_clear (&mu); +LBL_M: + mp_clear(&M[1]); + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + mp_clear (&M[x]); + } + return err; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_s_mp_exptmod.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_s_mp_exptmod.c */ + +/* Start: bn_s_mp_mul_digs.c */ +#include "libtorrent/tommath.h" +#ifdef BN_S_MP_MUL_DIGS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* multiplies |a| * |b| and only computes upto digs digits of result + * HAC pp. 595, Algorithm 14.12 Modified so you can control how + * many digits of output are created. + */ +int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + mp_int t; + int res, pa, pb, ix, iy; + mp_digit u; + mp_word r; + mp_digit tmpx, *tmpt, *tmpy; + + /* can we use the fast multiplier? */ + if (((digs) < MP_WARRAY) && + MIN (a->used, b->used) < + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + return fast_s_mp_mul_digs (a, b, c, digs); + } + + if ((res = mp_init_size (&t, digs)) != MP_OKAY) { + return res; + } + t.used = digs; + + /* compute the digits of the product directly */ + pa = a->used; + for (ix = 0; ix < pa; ix++) { + /* set the carry to zero */ + u = 0; + + /* limit ourselves to making digs digits of output */ + pb = MIN (b->used, digs - ix); + + /* setup some aliases */ + /* copy of the digit from a used within the nested loop */ + tmpx = a->dp[ix]; + + /* an alias for the destination shifted ix places */ + tmpt = t.dp + ix; + + /* an alias for the digits of b */ + tmpy = b->dp; + + /* compute the columns of the output and propagate the carry */ + for (iy = 0; iy < pb; iy++) { + /* compute the column as a mp_word */ + r = ((mp_word)*tmpt) + + ((mp_word)tmpx) * ((mp_word)*tmpy++) + + ((mp_word) u); + + /* the new column is the lower part of the result */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get the carry word from the result */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + /* set carry if it is placed below digs */ + if (ix + iy < digs) { + *tmpt = u; + } + } + + mp_clamp (&t); + mp_exch (&t, c); + + mp_clear (&t); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_s_mp_mul_digs.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_s_mp_mul_digs.c */ + +/* Start: bn_s_mp_mul_high_digs.c */ +#include "libtorrent/tommath.h" +#ifdef BN_S_MP_MUL_HIGH_DIGS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* multiplies |a| * |b| and does not compute the lower digs digits + * [meant to get the higher part of the product] + */ +int +s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + mp_int t; + int res, pa, pb, ix, iy; + mp_digit u; + mp_word r; + mp_digit tmpx, *tmpt, *tmpy; + + /* can we use the fast multiplier? */ +#ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C + if (((a->used + b->used + 1) < MP_WARRAY) + && MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + return fast_s_mp_mul_high_digs (a, b, c, digs); + } +#endif + + if ((res = mp_init_size (&t, a->used + b->used + 1)) != MP_OKAY) { + return res; + } + t.used = a->used + b->used + 1; + + pa = a->used; + pb = b->used; + for (ix = 0; ix < pa; ix++) { + /* clear the carry */ + u = 0; + + /* left hand side of A[ix] * B[iy] */ + tmpx = a->dp[ix]; + + /* alias to the address of where the digits will be stored */ + tmpt = &(t.dp[digs]); + + /* alias for where to read the right hand side from */ + tmpy = b->dp + (digs - ix); + + for (iy = digs - ix; iy < pb; iy++) { + /* calculate the double precision result */ + r = ((mp_word)*tmpt) + + ((mp_word)tmpx) * ((mp_word)*tmpy++) + + ((mp_word) u); + + /* get the lower part */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* carry the carry */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + *tmpt = u; + } + mp_clamp (&t); + mp_exch (&t, c); + mp_clear (&t); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_s_mp_mul_high_digs.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_s_mp_mul_high_digs.c */ + +/* Start: bn_s_mp_sqr.c */ +#include "libtorrent/tommath.h" +#ifdef BN_S_MP_SQR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */ +int s_mp_sqr (mp_int * a, mp_int * b) +{ + mp_int t; + int res, ix, iy, pa; + mp_word r; + mp_digit u, tmpx, *tmpt; + + pa = a->used; + if ((res = mp_init_size (&t, 2*pa + 1)) != MP_OKAY) { + return res; + } + + /* default used is maximum possible size */ + t.used = 2*pa + 1; + + for (ix = 0; ix < pa; ix++) { + /* first calculate the digit at 2*ix */ + /* calculate double precision result */ + r = ((mp_word) t.dp[2*ix]) + + ((mp_word)a->dp[ix])*((mp_word)a->dp[ix]); + + /* store lower part in result */ + t.dp[ix+ix] = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get the carry */ + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + + /* left hand side of A[ix] * A[iy] */ + tmpx = a->dp[ix]; + + /* alias for where to store the results */ + tmpt = t.dp + (2*ix + 1); + + for (iy = ix + 1; iy < pa; iy++) { + /* first calculate the product */ + r = ((mp_word)tmpx) * ((mp_word)a->dp[iy]); + + /* now calculate the double precision result, note we use + * addition instead of *2 since it's easier to optimize + */ + r = ((mp_word) *tmpt) + r + r + ((mp_word) u); + + /* store lower part */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get carry */ + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + } + /* propagate upwards */ + while (u != ((mp_digit) 0)) { + r = ((mp_word) *tmpt) + ((mp_word) u); + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + } + } + + mp_clamp (&t); + mp_exch (&t, b); + mp_clear (&t); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_s_mp_sqr.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_s_mp_sqr.c */ + +/* Start: bn_s_mp_sub.c */ +#include "libtorrent/tommath.h" +#ifdef BN_S_MP_SUB_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */ +int +s_mp_sub (mp_int * a, mp_int * b, mp_int * c) +{ + int olduse, res, min, max; + + /* find sizes */ + min = b->used; + max = a->used; + + /* init result */ + if (c->alloc < max) { + if ((res = mp_grow (c, max)) != MP_OKAY) { + return res; + } + } + olduse = c->used; + c->used = max; + + { + register mp_digit u, *tmpa, *tmpb, *tmpc; + register int i; + + /* alias for digit pointers */ + tmpa = a->dp; + tmpb = b->dp; + tmpc = c->dp; + + /* set carry to zero */ + u = 0; + for (i = 0; i < min; i++) { + /* T[i] = A[i] - B[i] - U */ + *tmpc = *tmpa++ - *tmpb++ - u; + + /* U = carry bit of T[i] + * Note this saves performing an AND operation since + * if a carry does occur it will propagate all the way to the + * MSB. As a result a single shift is enough to get the carry + */ + u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* now copy higher words if any, e.g. if A has more digits than B */ + for (; i < max; i++) { + /* T[i] = A[i] - U */ + *tmpc = *tmpa++ - u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* clear digits above used (since we may not have grown result above) */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp (c); + return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_s_mp_sub.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_s_mp_sub.c */ + +/* Start: bncore.c */ +#include "libtorrent/tommath.h" +#ifdef BNCORE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* Known optimal configurations + + CPU /Compiler /MUL CUTOFF/SQR CUTOFF +------------------------------------------------------------- + Intel P4 Northwood /GCC v3.4.1 / 88/ 128/LTM 0.32 ;-) + AMD Athlon64 /GCC v3.4.4 / 80/ 120/LTM 0.35 + +*/ + +int KARATSUBA_MUL_CUTOFF = 80, /* Min. number of digits before Karatsuba multiplication is used. */ + KARATSUBA_SQR_CUTOFF = 120, /* Min. number of digits before Karatsuba squaring is used. */ + + TOOM_MUL_CUTOFF = 350, /* no optimal values of these are known yet so set em high */ + TOOM_SQR_CUTOFF = 400; +#endif + +/* $Source: /cvs/libtom/libtommath/bncore.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bncore.c */ + + +/* EOF */ diff --git a/apps/Launcher/ext/libtorrent/src/natpmp.cpp b/apps/Launcher/ext/libtorrent/src/natpmp.cpp new file mode 100644 index 0000000000..5f12f49f10 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/natpmp.cpp @@ -0,0 +1,702 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ +#include "libtorrent/config.hpp" +#if defined TORRENT_OS2 +#include +#endif + +#include +#include + +#include "libtorrent/natpmp.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/enum_net.hpp" +#include "libtorrent/socket_io.hpp" +#include "libtorrent/io_service.hpp" +//#include "libtorrent/random.hpp" + +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif + +//#define NATPMP_LOG + +#ifdef NATPMP_LOG +#include +#endif + +#if defined TORRENT_ASIO_DEBUGGING +#include "libtorrent/debug.hpp" +#endif + +using namespace libtorrent; + +natpmp::natpmp(io_service& ios, address const& listen_interface + , portmap_callback_t const& cb, log_callback_t const& lcb) + : m_callback(cb) + , m_log_callback(lcb) + , m_currently_mapping(-1) + , m_retry_count(0) + , m_socket(ios) + , m_send_timer(ios) + , m_refresh_timer(ios) + , m_next_refresh(-1) + , m_disabled(false) + , m_abort(false) +{ + // unfortunately async operations rely on the storage + // for this array not to be reallocated, by passing + // around pointers to its elements. so reserve size for now + m_mappings.reserve(10); + rebind(listen_interface); +} + +void natpmp::rebind(address const& listen_interface) +{ + mutex::scoped_lock l(m_mutex); + + error_code ec; + address gateway = get_default_gateway(m_socket.get_io_service(), ec); + if (ec) + { + char msg[200]; + snprintf(msg, sizeof(msg), "failed to find default route: %s" + , convert_from_native(ec.message()).c_str()); + log(msg, l); + disable(ec, l); + return; + } + + m_disabled = false; + + udp::endpoint nat_endpoint(gateway, 5351); + if (nat_endpoint == m_nat_endpoint) return; + m_nat_endpoint = nat_endpoint; + + char msg[200]; + snprintf(msg, sizeof(msg), "found router at: %s" + , print_address(m_nat_endpoint.address()).c_str()); + log(msg, l); + + m_socket.open(udp::v4(), ec); + if (ec) + { + disable(ec, l); + return; + } + m_socket.bind(udp::endpoint(address_v4::any(), 0), ec); + if (ec) + { + disable(ec, l); + return; + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("natpmp::on_reply"); +#endif + m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16) + , m_remote, boost::bind(&natpmp::on_reply, self(), _1, _2)); + send_get_ip_address_request(l); + + for (std::vector::iterator i = m_mappings.begin() + , end(m_mappings.end()); i != end; ++i) + { + if (i->protocol != none + || i->action != mapping_t::action_none) + continue; + i->action = mapping_t::action_add; + update_mapping(i - m_mappings.begin(), l); + } +} + +void natpmp::send_get_ip_address_request(mutex::scoped_lock& l) +{ + using namespace libtorrent::detail; + + char buf[2]; + char* out = buf; + write_uint8(0, out); // NAT-PMP version + write_uint8(0, out); // public IP address request opcode + log("==> get public IP address", l); + + error_code ec; + m_socket.send_to(asio::buffer(buf, sizeof(buf)), m_nat_endpoint, 0, ec); +} + +bool natpmp::get_mapping(int index, int& local_port, int& external_port, int& protocol) const +{ + mutex::scoped_lock l(m_mutex); + + TORRENT_ASSERT(index < int(m_mappings.size()) && index >= 0); + if (index >= int(m_mappings.size()) || index < 0) return false; + mapping_t const& m = m_mappings[index]; + if (m.protocol == none) return false; + local_port = m.local_port; + external_port = m.external_port; + protocol = m.protocol; + return true; +} + +void natpmp::log(char const* msg, mutex::scoped_lock& l) +{ + l.unlock(); + m_log_callback(msg); + l.lock(); +} + +void natpmp::disable(error_code const& ec, mutex::scoped_lock& l) +{ + m_disabled = true; + + for (std::vector::iterator i = m_mappings.begin() + , end(m_mappings.end()); i != end; ++i) + { + if (i->protocol == none) continue; + i->protocol = none; + int index = i - m_mappings.begin(); + l.unlock(); + m_callback(index, address(), 0, ec); + l.lock(); + } + close_impl(l); +} + +void natpmp::delete_mapping(int index) +{ + mutex::scoped_lock l(m_mutex); + + TORRENT_ASSERT(index < int(m_mappings.size()) && index >= 0); + if (index >= int(m_mappings.size()) || index < 0) return; + mapping_t& m = m_mappings[index]; + + if (m.protocol == none) return; + if (!m.map_sent) + { + m.action = mapping_t::action_none; + m.protocol = none; + return; + } + + m.action = mapping_t::action_delete; + update_mapping(index, l); +} + +int natpmp::add_mapping(protocol_type p, int external_port, int local_port) +{ + mutex::scoped_lock l(m_mutex); + + if (m_disabled) return -1; + + std::vector::iterator i = std::find_if(m_mappings.begin() + , m_mappings.end(), boost::bind(&mapping_t::protocol, _1) == int(none)); + if (i == m_mappings.end()) + { + m_mappings.push_back(mapping_t()); + i = m_mappings.end() - 1; + } + i->protocol = p; + i->external_port = external_port; + i->local_port = local_port; + i->action = mapping_t::action_add; + + int mapping_index = i - m_mappings.begin(); + +#ifdef NATPMP_LOG + ptime now = time_now(); + for (std::vector::iterator m = m_mappings.begin() + , end(m_mappings.end()); m != end; ++m) + { + std::cout << " ADD MAPPING: " << mapping_index << " [ " + "proto: " << (i->protocol == none ? "none" : i->protocol == tcp ? "tcp" : "udp") + << " port: " << i->external_port + << " local-port: " << i->local_port + << " action: " << (i->action == mapping_t::action_none ? "none" : i->action == mapping_t::action_add ? "add" : "delete") + << " ttl: " << total_seconds(i->expires - now) + << " ]" << std::endl; + } +#endif + + update_mapping(mapping_index, l); + return mapping_index; +} + +void natpmp::try_next_mapping(int i, mutex::scoped_lock& l) +{ +#ifdef NATPMP_LOG + ptime now = time_now(); + for (std::vector::iterator m = m_mappings.begin() + , end(m_mappings.end()); m != end; ++m) + { + std::cout << " " << (m - m_mappings.begin()) << " [ " + "proto: " << (m->protocol == none ? "none" : m->protocol == tcp ? "tcp" : "udp") + << " port: " << m->external_port + << " local-port: " << m->local_port + << " action: " << (m->action == mapping_t::action_none ? "none" : m->action == mapping_t::action_add ? "add" : "delete") + << " ttl: " << total_seconds(m->expires - now) + << " ]" << std::endl; + } +#endif + if (i < int(m_mappings.size()) - 1) + { + update_mapping(i + 1, l); + return; + } + + std::vector::iterator m = std::find_if( + m_mappings.begin(), m_mappings.end() + , boost::bind(&mapping_t::action, _1) != int(mapping_t::action_none)); + + if (m == m_mappings.end()) + { + if (m_abort) + { + error_code ec; + m_send_timer.cancel(ec); + m_socket.close(ec); + } +#ifdef NATPMP_LOG + std::cout << " done" << (m_abort?" shutting down":"") << std::endl; +#endif + return; + } + +#ifdef NATPMP_LOG + std::cout << " updating " << (m - m_mappings.begin()) << std::endl; +#endif + + update_mapping(m - m_mappings.begin(), l); +} + +void natpmp::update_mapping(int i, mutex::scoped_lock& l) +{ + if (i == int(m_mappings.size())) + { + if (m_abort) + { + error_code ec; + m_send_timer.cancel(ec); + m_socket.close(ec); + } +#ifdef NATPMP_LOG + std::cout << " done" << (m_abort?" shutting down":"") << std::endl; +#endif + return; + } + + natpmp::mapping_t& m = m_mappings[i]; + if (m.action == mapping_t::action_none + || m.protocol == none) + { + try_next_mapping(i, l); + return; + } + + if (m_currently_mapping == -1) + { + // the socket is not currently in use + // send out a mapping request + m_retry_count = 0; + send_map_request(i, l); + } +} + +void natpmp::send_map_request(int i, mutex::scoped_lock& l) +{ + using namespace libtorrent::detail; + + TORRENT_ASSERT(m_currently_mapping == -1 + || m_currently_mapping == i); + m_currently_mapping = i; + mapping_t& m = m_mappings[i]; + TORRENT_ASSERT(m.action != mapping_t::action_none); + char buf[12]; + char* out = buf; + write_uint8(0, out); // NAT-PMP version + write_uint8(m.protocol, out); // map "protocol" + write_uint16(0, out); // reserved + write_uint16(m.local_port, out); // private port + write_uint16(m.external_port, out); // requested public port + int ttl = m.action == mapping_t::action_add ? 3600 : 0; + write_uint32(ttl, out); // port mapping lifetime + + char msg[200]; + snprintf(msg, sizeof(msg), "==> port map [ mapping: %d action: %s" + " proto: %s local: %u external: %u ttl: %u ]" + , i, m.action == mapping_t::action_add ? "add" : "delete" + , m.protocol == udp ? "udp" : "tcp" + , m.local_port, m.external_port, ttl); + log(msg, l); + + error_code ec; + m_socket.send_to(asio::buffer(buf, sizeof(buf)), m_nat_endpoint, 0, ec); + m.map_sent = true; + m.outstanding_request = true; + if (m_abort) + { + // when we're shutting down, ignore the + // responses and just remove all mappings + // immediately + m_currently_mapping = -1; + m.action = mapping_t::action_none; + try_next_mapping(i, l); + } + else + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("natpmp::resend_request"); +#endif + // linear back-off instead of exponential + ++m_retry_count; + m_send_timer.expires_from_now(milliseconds(250 * m_retry_count), ec); + m_send_timer.async_wait(boost::bind(&natpmp::resend_request, self(), i, _1)); + } +} + +void natpmp::resend_request(int i, error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("natpmp::resend_request"); +#endif + if (e) return; + mutex::scoped_lock l(m_mutex); + if (m_currently_mapping != i) return; + + // if we're shutting down, don't retry, just move on + // to the next mapping + if (m_retry_count >= 9 || m_abort) + { + m_currently_mapping = -1; + m_mappings[i].action = mapping_t::action_none; + // try again in two hours + m_mappings[i].expires = time_now() + hours(2); + try_next_mapping(i, l); + return; + } + send_map_request(i, l); +} + +void natpmp::on_reply(error_code const& e + , std::size_t bytes_transferred) +{ + mutex::scoped_lock l(m_mutex); + +#if defined TORRENT_ASIO_DEBUGGING + complete_async("natpmp::on_reply"); +#endif + + using namespace libtorrent::detail; + if (e) + { + char msg[200]; + snprintf(msg, sizeof(msg), "error on receiving reply: %s" + , convert_from_native(e.message()).c_str()); + log(msg, l); + return; + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("natpmp::on_reply"); +#endif + // make a copy of the response packet buffer + // to avoid overwriting it in the next receive call + char msg_buf[16]; + memcpy(msg_buf, m_response_buffer, bytes_transferred); + + m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16) + , m_remote, boost::bind(&natpmp::on_reply, self(), _1, _2)); + + // simulate packet loss +/* + if ((random() % 2) == 0) + { + log(" simulating drop", l); + return; + } +*/ + if (m_remote != m_nat_endpoint) + { + char msg[200]; + snprintf(msg, sizeof(msg), "received packet from wrong IP: %s" + , print_endpoint(m_remote).c_str()); + log(msg, l); + return; + } + + error_code ec; + m_send_timer.cancel(ec); + + if (bytes_transferred < 12) + { + char msg[200]; + snprintf(msg, sizeof(msg), "received packet of invalid size: %d", int(bytes_transferred)); + log(msg, l); + return; + } + + char* in = msg_buf; + int version = read_uint8(in); + int cmd = read_uint8(in); + int result = read_uint16(in); + int time = read_uint32(in); + + if (cmd == 128) + { + // public IP request response + m_external_ip = read_v4_address(in); + + char msg[200]; + snprintf(msg, sizeof(msg), "<== public IP address [ %s ]", print_address(m_external_ip).c_str()); + log(msg, l); + return; + + } + + if (bytes_transferred < 16) + { + char msg[200]; + snprintf(msg, sizeof(msg), "received packet of invalid size: %d", int(bytes_transferred)); + log(msg, l); + return; + } + + int private_port = read_uint16(in); + int public_port = read_uint16(in); + int lifetime = read_uint32(in); + + (void)time; // to remove warning + + int protocol = (cmd - 128 == 1)?udp:tcp; + + char msg[200]; + int num_chars = snprintf(msg, sizeof(msg), "<== port map [" + " protocol: %s local: %u external: %u ttl: %u ]" + , (cmd - 128 == 1 ? "udp" : "tcp") + , private_port, public_port, lifetime); + + if (version != 0) + { + snprintf(msg + num_chars, sizeof(msg) - num_chars, "unexpected version: %u" + , version); + log(msg, l); + } + + mapping_t* m = 0; + int index = -1; + for (std::vector::iterator i = m_mappings.begin() + , end(m_mappings.end()); i != end; ++i) + { + if (private_port != i->local_port) continue; + if (protocol != i->protocol) continue; + if (!i->map_sent) continue; + if (!i->outstanding_request) continue; + m = &*i; + index = i - m_mappings.begin(); + break; + } + + if (m == 0) + { + snprintf(msg + num_chars, sizeof(msg) - num_chars, " not found in map table"); + log(msg, l); + return; + } + m->outstanding_request = false; + + log(msg, l); + + if (public_port == 0 || lifetime == 0) + { + // this means the mapping was + // successfully closed + m->protocol = none; + } + else + { + m->expires = time_now() + seconds(int(lifetime * 0.7f)); + m->external_port = public_port; + } + + if (result != 0) + { + int errors[] = + { + errors::unsupported_protocol_version, + errors::natpmp_not_authorized, + errors::network_failure, + errors::no_resources, + errors::unsupported_opcode, + }; + int ev = errors::no_error; + if (result >= 1 && result <= 5) ev = errors[result - 1]; + + m->expires = time_now() + hours(2); + l.unlock(); + m_callback(index, address(), 0, error_code(ev, get_libtorrent_category())); + l.lock(); + } + else if (m->action == mapping_t::action_add) + { + l.unlock(); + m_callback(index, m_external_ip, m->external_port, + error_code(errors::no_error, get_libtorrent_category())); + l.lock(); + } + + if (m_abort) return; + + m_currently_mapping = -1; + m->action = mapping_t::action_none; + m_send_timer.cancel(ec); + update_expiration_timer(l); + try_next_mapping(index, l); +} + +void natpmp::update_expiration_timer(mutex::scoped_lock& l) +{ + if (m_abort) return; + + ptime now = time_now() + milliseconds(100); +#ifdef NATPMP_LOG + std::cout << time_now_string() << " update_expiration_timer " << std::endl; + for (std::vector::iterator i = m_mappings.begin() + , end(m_mappings.end()); i != end; ++i) + { + std::cout << " " << (i - m_mappings.begin()) << " [ " + "proto: " << (i->protocol == none ? "none" : i->protocol == tcp ? "tcp" : "udp") + << " port: " << i->external_port + << " local-port: " << i->local_port + << " action: " << (i->action == mapping_t::action_none ? "none" : i->action == mapping_t::action_add ? "add" : "delete") + << " ttl: " << total_seconds(i->expires - now) + << " ]" << std::endl; + } +#endif + ptime min_expire = now + seconds(3600); + int min_index = -1; + for (std::vector::iterator i = m_mappings.begin() + , end(m_mappings.end()); i != end; ++i) + { + if (i->protocol == none + || i->action != mapping_t::action_none) continue; + int index = i - m_mappings.begin(); + if (i->expires < now) + { + char msg[200]; + snprintf(msg, sizeof(msg), "mapping %u expired", index); + log(msg, l); + i->action = mapping_t::action_add; + if (m_next_refresh == index) m_next_refresh = -1; + update_mapping(index, l); + } + else if (i->expires < min_expire) + { + min_expire = i->expires; + min_index = index; + } + } + + // this is already the mapping we're waiting for + if (m_next_refresh == min_index) return; + + if (min_index >= 0) + { +#ifdef NATPMP_LOG + std::cout << time_now_string() << " next expiration [" + " i: " << min_index + << " ttl: " << total_seconds(min_expire - time_now()) + << " ]" << std::endl; +#endif + error_code ec; + if (m_next_refresh >= 0) m_refresh_timer.cancel(ec); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("natpmp::mapping_expired"); +#endif + m_refresh_timer.expires_from_now(min_expire - now, ec); + m_refresh_timer.async_wait(boost::bind(&natpmp::mapping_expired, self(), _1, min_index)); + m_next_refresh = min_index; + } +} + +void natpmp::mapping_expired(error_code const& e, int i) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("natpmp::mapping_expired"); +#endif + if (e) return; + mutex::scoped_lock l(m_mutex); + char msg[200]; + snprintf(msg, sizeof(msg), "mapping %u expired", i); + log(msg, l); + m_mappings[i].action = mapping_t::action_add; + if (m_next_refresh == i) m_next_refresh = -1; + update_mapping(i, l); +} + +void natpmp::close() +{ + mutex::scoped_lock l(m_mutex); + close_impl(l); +} + +void natpmp::close_impl(mutex::scoped_lock& l) +{ + m_abort = true; + log("closing", l); +#ifdef NATPMP_LOG + std::cout << time_now_string() << " close" << std::endl; + ptime now = time_now(); +#endif + if (m_disabled) return; + for (std::vector::iterator i = m_mappings.begin() + , end(m_mappings.end()); i != end; ++i) + { +#ifdef NATPMP_LOG + std::cout << " " << (i - m_mappings.begin()) << " [ " + "proto: " << (i->protocol == none ? "none" : i->protocol == tcp ? "tcp" : "udp") + << " port: " << i->external_port + << " local-port: " << i->local_port + << " action: " << (i->action == mapping_t::action_none ? "none" : i->action == mapping_t::action_add ? "add" : "delete") + << " ttl: " << total_seconds(i->expires - now) + << " ]" << std::endl; +#endif + if (i->protocol == none) continue; + i->action = mapping_t::action_delete; + } + error_code ec; + m_refresh_timer.cancel(ec); + m_currently_mapping = -1; + update_mapping(0, l); +} + diff --git a/apps/Launcher/ext/libtorrent/src/packet_buffer.cpp b/apps/Launcher/ext/libtorrent/src/packet_buffer.cpp new file mode 100644 index 0000000000..b0178a611f --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/packet_buffer.cpp @@ -0,0 +1,215 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include // free and calloc +#include "libtorrent/packet_buffer.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/invariant_check.hpp" + +namespace libtorrent { + + bool compare_less_wrap(boost::uint32_t lhs, boost::uint32_t rhs + , boost::uint32_t mask); + + packet_buffer::packet_buffer() + : m_storage(0) + , m_capacity(0) + , m_size(0) + , m_first(0) + , m_last(0) + {} + +#if TORRENT_USE_INVARIANT_CHECKS + void packet_buffer::check_invariant() const + { + int count = 0; + for (int i = 0; i < int(m_capacity); ++i) + { + count += m_storage[i] ? 1 : 0; + } + TORRENT_ASSERT(count == int(m_size)); + } +#endif + + packet_buffer::~packet_buffer() + { + free(m_storage); + } + + void* packet_buffer::insert(index_type idx, void* value) + { + INVARIANT_CHECK; + + TORRENT_ASSERT_VAL(idx <= 0xffff, idx); + // you're not allowed to insert NULLs! + TORRENT_ASSERT(value); + + if (value == 0) return remove(idx); + + if (m_size != 0) + { + if (compare_less_wrap(idx, m_first, 0xffff)) + { + // Index comes before m_first. If we have room, we can simply + // adjust m_first backward. + + std::size_t free_space = 0; + + for (index_type i = (m_first - 1) & (m_capacity - 1); + i != (m_first & (m_capacity - 1)); i = (i - 1) & (m_capacity - 1)) + { + if (m_storage[i & (m_capacity - 1)]) + break; + ++free_space; + } + + if (((m_first - idx) & 0xffff) > free_space) + reserve(((m_first - idx) & 0xffff) + m_capacity - free_space); + + m_first = idx; + } + else if (idx >= m_first + m_capacity) + { + reserve(idx - m_first + 1); + } + else if (idx < m_first) + { + // We have wrapped. + if (idx >= ((m_first + m_capacity) & 0xffff) && m_capacity < 0xffff) + { + reserve(m_capacity + (idx + 1 - ((m_first + m_capacity) & 0xffff))); + } + } + if (compare_less_wrap(m_last, (idx + 1) & 0xffff, 0xffff)) + m_last = (idx + 1) & 0xffff; + } + else + { + m_first = idx; + m_last = (idx + 1) & 0xffff; + } + + if (m_capacity == 0) reserve(16); + + void* old_value = m_storage[idx & (m_capacity - 1)]; + m_storage[idx & (m_capacity - 1)] = value; + + if (m_size == 0) m_first = idx; + // if we're just replacing an old value, the number + // of elements in the buffer doesn't actually increase + if (old_value == 0) ++m_size; + + TORRENT_ASSERT_VAL(m_first <= 0xffff, m_first); + return old_value; + } + + void* packet_buffer::at(index_type idx) const + { + INVARIANT_CHECK; + if (idx >= m_first + m_capacity) + return 0; + + if (compare_less_wrap(idx, m_first, 0xffff)) + { + return 0; + } + + const int mask = (m_capacity - 1); + return m_storage[idx & mask]; + } + + void packet_buffer::reserve(std::size_t size) + { + INVARIANT_CHECK; + TORRENT_ASSERT_VAL(size <= 0xffff, size); + std::size_t new_size = m_capacity == 0 ? 16 : m_capacity; + + while (new_size < size) + new_size <<= 1; + + void** new_storage = (void**)malloc(sizeof(void*) * new_size); + + for (index_type i = 0; i < new_size; ++i) + new_storage[i] = 0; + + for (index_type i = m_first; i < (m_first + m_capacity); ++i) + new_storage[i & (new_size - 1)] = m_storage[i & (m_capacity - 1)]; + + free(m_storage); + + m_storage = new_storage; + m_capacity = new_size; + } + + void* packet_buffer::remove(index_type idx) + { + INVARIANT_CHECK; + // TODO: use compare_less_wrap for this comparison as well + if (idx >= m_first + m_capacity) + return 0; + + if (compare_less_wrap(idx, m_first, 0xffff)) + return 0; + + const int mask = (m_capacity - 1); + void* old_value = m_storage[idx & mask]; + m_storage[idx & mask] = 0; + + if (old_value) + { + --m_size; + if (m_size == 0) m_last = m_first; + } + + if (idx == m_first && m_size != 0) + { + ++m_first; + for (boost::uint32_t i = 0; i < m_capacity; ++i, ++m_first) + if (m_storage[m_first & mask]) break; + m_first &= 0xffff; + } + + if (((idx + 1) & 0xffff) == m_last && m_size != 0) + { + --m_last; + for (boost::uint32_t i = 0; i < m_capacity; ++i, --m_last) + if (m_storage[m_last & mask]) break; + ++m_last; + m_last &= 0xffff; + } + + TORRENT_ASSERT_VAL(m_first <= 0xffff, m_first); + return old_value; + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/parse_url.cpp b/apps/Launcher/ext/libtorrent/src/parse_url.cpp new file mode 100644 index 0000000000..06f2fb42b0 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/parse_url.cpp @@ -0,0 +1,135 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/parse_url.hpp" +#include + +namespace libtorrent +{ + + // returns protocol, auth, hostname, port, path + boost::tuple + parse_url_components(std::string url, error_code& ec) + { + std::string hostname; // hostname only + std::string auth; // user:pass + std::string protocol; // http or https for instance + int port = -1; + + std::string::iterator at; + std::string::iterator colon; + std::string::iterator port_pos; + + // PARSE URL + std::string::iterator start = url.begin(); + // remove white spaces in front of the url + while (start != url.end() && (*start == ' ' || *start == '\t')) + ++start; + std::string::iterator end + = std::find(url.begin(), url.end(), ':'); + protocol.assign(start, end); + + if (end == url.end()) + { + ec = errors::unsupported_url_protocol; + goto exit; + } + ++end; + if (end == url.end() || *end != '/') + { + ec = errors::unsupported_url_protocol; + goto exit; + } + ++end; + if (end == url.end() || *end != '/') + { + ec = errors::unsupported_url_protocol; + goto exit; + } + ++end; + start = end; + + at = std::find(start, url.end(), '@'); + colon = std::find(start, url.end(), ':'); + end = std::find(start, url.end(), '/'); + + if (at != url.end() + && colon != url.end() + && colon < at + && at < end) + { + auth.assign(start, at); + start = at; + ++start; + } + + // this is for IPv6 addresses + if (start != url.end() && *start == '[') + { + port_pos = std::find(start, url.end(), ']'); + if (port_pos == url.end()) + { + ec = errors::expected_close_bracket_in_address; + goto exit; + } + port_pos = std::find(port_pos, url.end(), ':'); + } + else + { + port_pos = std::find(start, url.end(), ':'); + } + + if (port_pos < end) + { + hostname.assign(start, port_pos); + ++port_pos; + for (std::string::iterator i = port_pos; i < end; ++i) + { + if (is_digit(*i)) continue; + ec = errors::invalid_port; + goto exit; + } + port = std::atoi(std::string(port_pos, end).c_str()); + } + else + { + hostname.assign(start, end); + } + + start = end; +exit: + return boost::make_tuple(protocol, auth, hostname, port + , std::string(start, url.end())); + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/pe_crypto.cpp b/apps/Launcher/ext/libtorrent/src/pe_crypto.cpp new file mode 100644 index 0000000000..d758b5a95d --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/pe_crypto.cpp @@ -0,0 +1,373 @@ +/* + +Copyright (c) 2007-2014, Un Shyam & Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISABLE_ENCRYPTION + +#include +#include + +#if defined TORRENT_USE_GCRYPT +#include +#elif defined TORRENT_USE_OPENSSL +#include +#include "libtorrent/random.hpp" +#elif defined TORRENT_USE_TOMMATH +extern "C" { +#include "libtorrent/tommath.h" +} +#include "libtorrent/random.hpp" +#endif + +#include "libtorrent/pe_crypto.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/assert.hpp" + +namespace libtorrent +{ + namespace + { + const unsigned char dh_prime[96] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, + 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, + 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, + 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, + 0xA6, 0x3A, 0x36, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x63 + }; + } + + + // Set the prime P and the generator, generate local public key + dh_key_exchange::dh_key_exchange() + { +#ifdef TORRENT_USE_GCRYPT + // create local key + gcry_randomize(m_dh_local_secret, sizeof(m_dh_local_secret), GCRY_STRONG_RANDOM); + + // build gcrypt big ints from the prime and the secret + gcry_mpi_t prime = 0; + gcry_mpi_t secret = 0; + gcry_mpi_t key = 0; + gcry_error_t e; + + e = gcry_mpi_scan(&prime, GCRYMPI_FMT_USG, dh_prime, sizeof(dh_prime), 0); + if (e) goto get_out; + e = gcry_mpi_scan(&secret, GCRYMPI_FMT_USG, m_dh_local_secret, sizeof(m_dh_local_secret), 0); + if (e) goto get_out; + + key = gcry_mpi_new(8); + + // generator is 2 + gcry_mpi_set_ui(key, 2); + // key = (2 ^ secret) % prime + gcry_mpi_powm(key, key, secret, prime); + + // key is now our local key + size_t written; + gcry_mpi_print(GCRYMPI_FMT_USG, (unsigned char*)m_dh_local_key + , sizeof(m_dh_local_key), &written, key); + if (written < 96) + { + memmove(m_dh_local_key + (sizeof(m_dh_local_key) - written), m_dh_local_key, written); + memset(m_dh_local_key, 0, sizeof(m_dh_local_key) - written); + } + +get_out: + if (key) gcry_mpi_release(key); + if (prime) gcry_mpi_release(prime); + if (secret) gcry_mpi_release(secret); + +#elif defined TORRENT_USE_OPENSSL + // create local key + for (int i = 0; i < sizeof(m_dh_local_secret); ++i) + m_dh_local_secret[i] = random() & 0xff; + + BIGNUM* prime = 0; + BIGNUM* secret = 0; + BIGNUM* key = 0; + BN_CTX* ctx = 0; + int size; + + prime = BN_bin2bn(dh_prime, sizeof(dh_prime), 0); + if (prime == 0) goto get_out; + secret = BN_bin2bn((unsigned char*)m_dh_local_secret, sizeof(m_dh_local_secret), 0); + if (secret == 0) goto get_out; + + key = BN_new(); + if (key == 0) goto get_out; + // generator is 2 + BN_set_word(key, 2); + + ctx = BN_CTX_new(); + if (ctx == 0) goto get_out; + BN_mod_exp(key, key, secret, prime, ctx); + BN_CTX_free(ctx); + + // print key to m_dh_local_key + size = BN_num_bytes(key); + memset(m_dh_local_key, 0, sizeof(m_dh_local_key) - size); + BN_bn2bin(key, (unsigned char*)m_dh_local_key + sizeof(m_dh_local_key) - size); + +get_out: + if (key) BN_free(key); + if (secret) BN_free(secret); + if (prime) BN_free(prime); +#elif defined TORRENT_USE_TOMMATH + // create local key + for (int i = 0; i < int(sizeof(m_dh_local_secret)); ++i) + m_dh_local_secret[i] = random() & 0xff; + + mp_int prime; + mp_int secret; + mp_int key; + int e; + int size; + + mp_init(&prime); + mp_init(&secret); + mp_init(&key); + + e = mp_read_unsigned_bin(&prime, dh_prime, sizeof(dh_prime)); + if (e) goto get_out; + e = mp_read_unsigned_bin(&secret, (unsigned char*)m_dh_local_secret, sizeof(m_dh_local_secret)); + if (e) goto get_out; + + // generator is 2 + mp_set_int(&key, 2); + // key = (2 ^ secret) % prime + e = mp_exptmod(&key, &secret, &prime, &key); + if (e) goto get_out; + + // key is now our local key + size = mp_unsigned_bin_size(&key); + memset(m_dh_local_key, 0, sizeof(m_dh_local_key) - size); + mp_to_unsigned_bin(&key, (unsigned char*)m_dh_local_key + sizeof(m_dh_local_key) - size); + +get_out: + mp_clear(&key); + mp_clear(&prime); + mp_clear(&secret); +#else +#error you must define which bigint library to use +#endif + } + + char const* dh_key_exchange::get_local_key() const + { + return m_dh_local_key; + } + + + // compute shared secret given remote public key + int dh_key_exchange::compute_secret(char const* remote_pubkey) + { + TORRENT_ASSERT(remote_pubkey); + int ret = 0; +#ifdef TORRENT_USE_GCRYPT + + gcry_mpi_t prime = 0; + gcry_mpi_t remote_key = 0; + gcry_mpi_t secret = 0; + size_t written; + gcry_error_t e; + + e = gcry_mpi_scan(&prime, GCRYMPI_FMT_USG, dh_prime, sizeof(dh_prime), 0); + if (e != 0) { ret = 1; goto get_out; } + e = gcry_mpi_scan(&remote_key, GCRYMPI_FMT_USG, remote_pubkey, 96, 0); + if (e != 0) { ret = 1; goto get_out; } + e = gcry_mpi_scan(&secret, GCRYMPI_FMT_USG, (unsigned char const*)m_dh_local_secret + , sizeof(m_dh_local_secret), 0); + if (e != 0) { ret = 1; goto get_out; } + + gcry_mpi_powm(remote_key, remote_key, secret, prime); + + // remote_key is now the shared secret + e = gcry_mpi_print(GCRYMPI_FMT_USG, (unsigned char*)m_dh_shared_secret + , sizeof(m_dh_shared_secret), &written, remote_key); + if (e != 0) { ret = 1; goto get_out; } + + if (written < 96) + { + memmove(m_dh_shared_secret, m_dh_shared_secret + + (sizeof(m_dh_shared_secret) - written), written); + memset(m_dh_shared_secret, 0, sizeof(m_dh_shared_secret) - written); + } + +get_out: + // TODO: 3 clean this up using destructors instead + if (prime) gcry_mpi_release(prime); + if (remote_key) gcry_mpi_release(remote_key); + if (secret) gcry_mpi_release(secret); + +#elif defined TORRENT_USE_OPENSSL + + BIGNUM* prime = 0; + BIGNUM* secret = 0; + BIGNUM* remote_key = 0; + BN_CTX* ctx = 0; + int size; + + prime = BN_bin2bn(dh_prime, sizeof(dh_prime), 0); + if (prime == 0) { ret = 1; goto get_out; } + secret = BN_bin2bn((unsigned char*)m_dh_local_secret, sizeof(m_dh_local_secret), 0); + if (secret == 0) { ret = 1; goto get_out; } + remote_key = BN_bin2bn((unsigned char*)remote_pubkey, 96, 0); + if (remote_key == 0) { ret = 1; goto get_out; } + + ctx = BN_CTX_new(); + if (ctx == 0) { ret = 1; goto get_out; } + BN_mod_exp(remote_key, remote_key, secret, prime, ctx); + BN_CTX_free(ctx); + + // remote_key is now the shared secret + size = BN_num_bytes(remote_key); + memset(m_dh_shared_secret, 0, sizeof(m_dh_shared_secret) - size); + BN_bn2bin(remote_key, (unsigned char*)m_dh_shared_secret + sizeof(m_dh_shared_secret) - size); + +get_out: + BN_free(remote_key); + BN_free(secret); + BN_free(prime); +#elif defined TORRENT_USE_TOMMATH + mp_int prime; + mp_int secret; + mp_int remote_key; + int size; + int e; + + mp_init(&prime); + mp_init(&secret); + mp_init(&remote_key); + + e = mp_read_unsigned_bin(&prime, dh_prime, sizeof(dh_prime)); + if (e) { ret = 1; goto get_out; } + e = mp_read_unsigned_bin(&secret, (unsigned char*)m_dh_local_secret, sizeof(m_dh_local_secret)); + if (e) { ret = 1; goto get_out; } + e = mp_read_unsigned_bin(&remote_key, (unsigned char*)remote_pubkey, 96); + if (e) { ret = 1; goto get_out; } + + e = mp_exptmod(&remote_key, &secret, &prime, &remote_key); + if (e) goto get_out; + + // remote_key is now the shared secret + size = mp_unsigned_bin_size(&remote_key); + memset(m_dh_shared_secret, 0, sizeof(m_dh_shared_secret) - size); + mp_to_unsigned_bin(&remote_key, (unsigned char*)m_dh_shared_secret + sizeof(m_dh_shared_secret) - size); + +get_out: + mp_clear(&remote_key); + mp_clear(&secret); + mp_clear(&prime); +#else +#error you must define which bigint library to use +#endif + + // calculate the xor mask for the obfuscated hash + hasher h; + h.update("req3", 4); + h.update(m_dh_shared_secret, sizeof(m_dh_shared_secret)); + m_xor_mask = h.final(); + return ret; + } + +} // namespace libtorrent + +#if !defined TORRENT_USE_OPENSSL && !defined TORRENT_USE_GCRYPT + +// All this code is based on libTomCrypt (http://www.libtomcrypt.com/) +// this library is public domain and has been specially +// tailored for libtorrent by Arvid Norberg + +void rc4_init(const unsigned char* in, unsigned long len, rc4 *state) +{ + unsigned char key[256], tmp, *s; + int keylen, x, y, j; + + TORRENT_ASSERT(state != 0); + TORRENT_ASSERT(len <= 256); + + state->x = 0; + while (len--) { + state->buf[state->x++] = *in++; + } + + /* extract the key */ + s = state->buf; + memcpy(key, s, 256); + keylen = state->x; + + /* make RC4 perm and shuffle */ + for (x = 0; x < 256; x++) { + s[x] = x; + } + + for (j = x = y = 0; x < 256; x++) { + y = (y + state->buf[x] + key[j++]) & 255; + if (j == keylen) { + j = 0; + } + tmp = s[x]; s[x] = s[y]; s[y] = tmp; + } + state->x = 0; + state->y = 0; +} + +unsigned long rc4_encrypt(unsigned char *out, unsigned long outlen, rc4 *state) +{ + unsigned char x, y, *s, tmp; + unsigned long n; + + TORRENT_ASSERT(out != 0); + TORRENT_ASSERT(state != 0); + + n = outlen; + x = state->x; + y = state->y; + s = state->buf; + while (outlen--) { + x = (x + 1) & 255; + y = (y + s[x]) & 255; + tmp = s[x]; s[x] = s[y]; s[y] = tmp; + tmp = (s[x] + s[y]) & 255; + *out++ ^= s[tmp]; + } + state->x = x; + state->y = y; + return n; +} + +#endif + +#endif // #ifndef TORRENT_DISABLE_ENCRYPTION + diff --git a/apps/Launcher/ext/libtorrent/src/peer_connection.cpp b/apps/Launcher/ext/libtorrent/src/peer_connection.cpp new file mode 100644 index 0000000000..67781ef797 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/peer_connection.cpp @@ -0,0 +1,6108 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING +#include // for va_start, va_end +#include // for vsnprintf +#endif + +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/identify_client.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/file.hpp" +#include "libtorrent/version.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/policy.hpp" +#include "libtorrent/socket_type.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/broadcast_socket.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/peer_info.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/error.hpp" +#include "libtorrent/kademlia/node_id.hpp" + +#ifdef TORRENT_DEBUG +#include +#endif + +#ifdef TORRENT_USE_OPENSSL +#include +#endif + +//#define TORRENT_CORRUPT_DATA + +using boost::shared_ptr; +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + int round_up8(int v) + { + return ((v & 7) == 0) ? v : v + (8 - (v & 7)); + } + +#if defined TORRENT_REQUEST_LOGGING + void write_request_log(FILE* f, sha1_hash const& ih + , peer_connection* p, peer_request const& r) + { + // the event format in the log is: + // uint64_t timestamp (microseconds) + // uint64_t info-hash prefix + // uint32_t peer identifier + // uint32_t piece + // uint32_t start offset + // uint32_t length + char event[32]; + char* ptr = event; + detail::write_uint64(total_microseconds((time_now_hires() - min_time())), ptr); + memcpy(ptr, &ih[0], 8); + ptr += 8; + detail::write_uint32(uintptr_t(p) & 0xffffffff, ptr); + detail::write_uint32(r.piece, ptr); + detail::write_uint32(r.start, ptr); + detail::write_uint32(r.length, ptr); + + int ret = fwrite(event, 1, sizeof(event), f); + if (ret != sizeof(event)) + { + fprintf(stderr, "ERROR writing to request log: (%d) %s\n" + , errno, strerror(errno)); + } + } +#endif + + // outbound connection + peer_connection::peer_connection( + session_impl& ses + , boost::weak_ptr tor + , shared_ptr s + , tcp::endpoint const& endp + , policy::peer* peerinfo + , bool outgoing) + : m_received_listen_port(false) + , m_have_all(false) + , m_peer_interested(false) + , m_peer_choked(true) + , m_interesting(false) + , m_choked(true) + , m_failed(false) + , m_disconnecting(false) + , m_bitfield_received(false) + , m_endgame_mode(false) + , m_sent_suggests(false) + , m_holepunch_mode(false) + , m_ignore_stats(false) + , m_corked(false) + , m_has_metadata(true) + , m_exceeded_limit(false) + , m_ses(ses) + , m_work(ses.m_io_service) + , m_last_piece(time_now()) + , m_last_request(time_now()) + , m_last_incoming_request(min_time()) + , m_last_unchoke(time_now()) + , m_last_unchoked(time_now()) + , m_last_choke(min_time()) + , m_last_receive(time_now()) + , m_last_sent(time_now()) + , m_requested(min_time()) + , m_remote_dl_update(time_now()) + , m_connect(time_now()) + , m_became_uninterested(time_now()) + , m_became_uninteresting(time_now()) + , m_downloaded_at_last_round(0) + , m_uploaded_at_last_round(0) + , m_uploaded_at_last_unchoke(0) + , m_disk_recv_buffer(ses, 0) + , m_socket(s) + , m_torrent(tor) + , m_peer_info(peerinfo) + , m_last_seen_complete(0) + , m_receiving_block(piece_block::invalid) + , m_remote(endp) + , m_timeout_extend(0) + , m_outstanding_bytes(0) + , m_extension_outstanding_bytes(0) + , m_queued_time_critical(0) + , m_num_pieces(0) + , m_timeout(m_ses.settings().peer_timeout) + , m_packet_size(0) + , m_soft_packet_size(0) + , m_recv_pos(0) + , m_disk_recv_buffer_size(0) + , m_reading_bytes(0) + , m_num_invalid_requests(0) + , m_priority(1) + , m_upload_limit(0) + , m_download_limit(0) + , m_speed(slow) + , m_connection_ticket(-1) + , m_remote_pieces_dled(0) + , m_remote_dl_rate(0) + , m_outstanding_writing_bytes(0) + , m_download_rate_peak(0) + , m_upload_rate_peak(0) + , m_max_out_request_queue(m_ses.settings().max_out_request_queue) + , m_rtt(0) + , m_desired_queue_size(2) + , m_prefer_whole_pieces(0) + , m_fast_reconnect(false) + , m_outgoing(outgoing) + , m_ignore_bandwidth_limits(false) + , m_ignore_unchoke_slots(false) + , m_connecting(outgoing) + , m_queued(outgoing) + , m_request_large_blocks(false) + , m_share_mode(false) + , m_upload_only(false) + , m_snubbed(false) + , m_no_download(false) +#if TORRENT_USE_ASSERTS + , m_in_constructor(true) + , m_disconnect_started(false) + , m_initialized(false) + , m_in_use(1337) + , m_received_in_piece(0) +#endif + { + m_superseed_piece[0] = -1; + m_superseed_piece[1] = -1; + boost::shared_ptr t = m_torrent.lock(); + // if t is NULL, we better not be connecting, since + // we can't decrement the connecting counter + TORRENT_ASSERT(t || !m_connecting); + if (m_connecting && t) t->inc_num_connecting(); + m_est_reciprocation_rate = m_ses.m_settings.default_est_reciprocation_rate; + +#if TORRENT_USE_I2P + if (peerinfo && peerinfo->is_i2p_addr) + { + // quadruple the timeout for i2p peers + m_timeout *= 4; + } +#endif + + m_channel_state[upload_channel] = peer_info::bw_idle; + m_channel_state[download_channel] = peer_info::bw_idle; + + m_quota[0] = 0; + m_quota[1] = 0; + + TORRENT_ASSERT(peerinfo == 0 || peerinfo->banned == false); +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + std::fill(m_country, m_country + 2, 0); +#ifndef TORRENT_DISABLE_GEO_IP + if (m_ses.has_country_db()) + { + char const *country = m_ses.country_for_ip(m_remote.address()); + if (country != 0) + { + m_country[0] = country[0]; + m_country[1] = country[1]; + } + } +#endif +#endif +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + error_code ec; + TORRENT_ASSERT(m_socket->remote_endpoint(ec) == m_remote || ec); + tcp::endpoint local_ep = m_socket->local_endpoint(ec); + std::string log_name = "[" + local_ep.address().to_string(ec) + "#" + + to_string(local_ep.port()).elems + "]-" + "[" + m_remote.address().to_string(ec) + "#" + + to_string(m_remote.port()).elems + "]"; + + if (t) log_name = combine_path(to_hex(t->info_hash().to_string()) + , log_name); + + m_logger = m_ses.create_log(log_name, m_ses.listen_port()); + peer_log("%s [ ep: %s type: %s seed: %d p: %p local: %s]" + , m_outgoing ? ">>> OUTGOING_CONNECTION" : "<<< INCOMING CONNECTION" + , print_endpoint(m_remote).c_str() + , m_socket->type_name() + , m_peer_info ? m_peer_info->seed : 0, m_peer_info + , print_endpoint(local_ep).c_str()); +#endif +#ifndef TORRENT_DISABLE_GEO_IP + m_inet_as_name = m_ses.as_name_for_ip(m_remote.address()); +#endif +#ifdef TORRENT_DEBUG + piece_failed = false; +#endif + std::fill(m_peer_id.begin(), m_peer_id.end(), 0); + } + +#ifdef TORRENT_DISK_STATS + void peer_connection::log_buffer_usage(char* buffer, int size, char const* label) + { + if (m_ses.m_disk_thread.is_disk_buffer(buffer)) + m_ses.m_disk_thread.rename_buffer(buffer, label); + + m_ses.m_buffer_usage_logger << log_time() << " append_send_buffer: " << size << std::endl; + m_ses.log_buffer_usage(); + } +#endif + + void peer_connection::increase_est_reciprocation_rate() + { + m_est_reciprocation_rate += m_est_reciprocation_rate + * m_ses.m_settings.increase_est_reciprocation_rate / 100; + } + + void peer_connection::decrease_est_reciprocation_rate() + { + m_est_reciprocation_rate -= m_est_reciprocation_rate + * m_ses.m_settings.decrease_est_reciprocation_rate / 100; + } + + bool peer_connection::bittyrant_unchoke_compare( + boost::intrusive_ptr const& p) const + { + TORRENT_ASSERT(p); + peer_connection const& rhs = *p; + + size_type d1, d2, u1, u2; + + // first compare how many bytes they've sent us + d1 = downloaded_in_last_round(); + d2 = rhs.downloaded_in_last_round(); + // divided by the number of bytes we've sent them + u1 = uploaded_in_last_round(); + u2 = rhs.uploaded_in_last_round(); + + boost::shared_ptr t1 = m_torrent.lock(); + TORRENT_ASSERT(t1); + boost::shared_ptr t2 = rhs.associated_torrent().lock(); + TORRENT_ASSERT(t2); + + // take torrent priority into account + d1 *= 1 + t1->priority(); + d2 *= 1 + t2->priority(); + + d1 = d1 * 1000 / (std::max)(size_type(1), u1); + d2 = d2 * 1000 / (std::max)(size_type(1), u2); + if (d1 > d2) return true; + if (d1 < d2) return false; + + // if both peers are still in their send quota or not in their send quota + // prioritize the one that has waited the longest to be unchoked + return m_last_unchoke < rhs.m_last_unchoke; + } + + // return true if 'this' peer should be preferred to be unchoke over p + bool peer_connection::unchoke_compare(boost::intrusive_ptr const& p) const + { + TORRENT_ASSERT(p); + peer_connection const& rhs = *p; + + // if one peer belongs to a higher priority torrent than the other one + // that one should be unchoked. + boost::shared_ptr t1 = m_torrent.lock(); + TORRENT_ASSERT(t1); + boost::shared_ptr t2 = rhs.associated_torrent().lock(); + TORRENT_ASSERT(t2); + + if (t1->priority() != t2->priority()) + return t1->priority() > t2->priority(); + + // compare how many bytes they've sent us + size_type c1; + size_type c2; + c1 = downloaded_in_last_round(); + c2 = rhs.downloaded_in_last_round(); + + if (c1 != c2) return c1 > c2; + + if (m_ses.settings().seed_choking_algorithm == session_settings::round_robin) + { + // the amount uploaded since unchoked (not just in the last round) + c1 = uploaded_since_unchoked(); + c2 = rhs.uploaded_since_unchoked(); + + // the way the round-robin unchoker works is that it, + // by default, prioritizes any peer that is already unchoked. + // this maintain the status quo across unchoke rounds. However, + // peers that are unchoked, but have sent more than one quota + // since they were unchoked, they get de-prioritized. + + int pieces = m_ses.settings().seeding_piece_quota; + // if a peer is already unchoked, and the number of bytes sent since it was unchoked + // is greater than the send quanta, then it's done with it' upload slot, and we + // can de-prioritize it + bool c1_quota_complete = !is_choked() && c1 > (std::max)(t1->torrent_file().piece_length() * pieces, 256 * 1024); + bool c2_quota_complete = !rhs.is_choked() && c2 > (std::max)(t2->torrent_file().piece_length() * pieces, 256 * 1024); + + // if c2 has completed a quanta, it shuold be de-prioritized + // and vice versa + if (c1_quota_complete < c2_quota_complete) return true; + if (c1_quota_complete > c2_quota_complete) return false; + + // if both peers have either completed a quanta, or not. + // keep unchoked peers prioritized over choked ones, to let + // peers keep working on uploading a full quanta + if (is_choked() < rhs.is_choked()) return true; + if (is_choked() > rhs.is_choked()) return false; + + // if the peers are still identical (say, they're both waiting to be unchoked) + // fall through and rely on the logic to prioritize peers who have waited + // the longest to be unchoked + } + else if (m_ses.settings().seed_choking_algorithm == session_settings::fastest_upload) + { + c1 = uploaded_in_last_round(); + c2 = rhs.uploaded_in_last_round(); + + // take torrent priority into account + c1 *= 1 + t1->priority(); + c2 *= 1 + t2->priority(); + + if (c1 > c2) return true; + if (c2 > c1) return false; + } + else if (m_ses.settings().seed_choking_algorithm == session_settings::anti_leech) + { + // the anti-leech seeding algorithm is based on the paper "Improving + // BitTorrent: A Simple Approach" from Chow et. al. and ranks peers based + // on how many pieces they have, prefering to unchoke peers that just + // started and peers that are close to completing. Like this: + // ^ + // | \ / | + // | \ / | + // | \ / | + // s | \ / | + // c | \ / | + // o | \ / | + // r | \ / | + // e | \ / | + // | \ / | + // | \ / | + // | \ / | + // | \ / | + // | V | + // +---------------------------+ + // 0% num have pieces 100% + int t1_total = t1->torrent_file().num_pieces(); + int t2_total = t2->torrent_file().num_pieces(); + int score1 = (num_have_pieces() < t1_total / 2 + ? t1_total - num_have_pieces() : num_have_pieces()) * 1000 / t1_total; + int score2 = (rhs.num_have_pieces() < t2_total / 2 + ? t2_total - rhs.num_have_pieces() : rhs.num_have_pieces()) * 1000 / t2_total; + if (score1 > score2) return true; + if (score2 > score1) return false; + } + + // prioritize the one that has waited the longest to be unchoked + // the round-robin unchoker relies on this logic. Don't change it + // without moving this into that unchoker logic + return m_last_unchoke < rhs.m_last_unchoke; + } + + bool peer_connection::upload_rate_compare(peer_connection const* p) const + { + size_type c1; + size_type c2; + + boost::shared_ptr t1 = m_torrent.lock(); + TORRENT_ASSERT(t1); + boost::shared_ptr t2 = p->associated_torrent().lock(); + TORRENT_ASSERT(t2); + + c1 = uploaded_in_last_round(); + c2 = p->uploaded_in_last_round(); + + // take torrent priority into account + c1 *= 1 + t1->priority(); + c2 *= 1 + t2->priority(); + + return c1 > c2; + } + + void peer_connection::reset_choke_counters() + { + m_downloaded_at_last_round= m_statistics.total_payload_download(); + m_uploaded_at_last_round = m_statistics.total_payload_upload(); + } + + void peer_connection::start() + { + TORRENT_ASSERT(m_peer_info == 0 || m_peer_info->connection == this); + boost::shared_ptr t = m_torrent.lock(); + + if (!m_outgoing) + { + tcp::socket::non_blocking_io ioc(true); + error_code ec; + m_socket->io_control(ioc, ec); + if (ec) + { + disconnect(ec); + return; + } + m_remote = m_socket->remote_endpoint(ec); + if (ec) + { + disconnect(ec); + return; + } + if (m_remote.address().is_v4() && m_ses.settings().peer_tos != 0) + { + m_socket->set_option(type_of_service(m_ses.settings().peer_tos), ec); +#if defined TORRENT_VERBOSE_LOGGING + peer_log(">>> SET_TOS[ tos: %d e: %s ]", m_ses.settings().peer_tos, ec.message().c_str()); +#endif + } +#if TORRENT_USE_IPV6 && defined IPV6_TCLASS + else if (m_remote.address().is_v6() && m_ses.settings().peer_tos != 0) + { + m_socket->set_option(traffic_class(m_ses.settings().peer_tos), ec); + } +#endif + } + + if (t && t->ready_for_connections()) + { + init(); + } + } + + void peer_connection::update_interest() + { + boost::shared_ptr t = m_torrent.lock(); + if (!t) return; + + // if m_have_piece is 0, it means the connections + // have not been initialized yet. The interested + // flag will be updated once they are. + if (m_have_piece.size() == 0) + { +#if defined TORRENT_VERBOSE_LOGGING + peer_log("*** UPDATE_INTEREST [ connections not initialized ]"); +#endif + return; + } + if (!t->ready_for_connections()) + { +#if defined TORRENT_VERBOSE_LOGGING + peer_log("*** UPDATE_INTEREST [ not ready for connections ]"); +#endif + return; + } + + bool interested = false; + if (!t->is_upload_only()) + { + piece_picker const& p = t->picker(); + int num_pieces = p.num_pieces(); + for (int j = 0; j != num_pieces; ++j) + { + if (!p.have_piece(j) + && t->piece_priority(j) > 0 + && m_have_piece[j]) + { + interested = true; +#if defined TORRENT_VERBOSE_LOGGING + peer_log("*** UPDATE_INTEREST [ interesting, piece: %d ]", j); +#endif + break; + } + } + } + +#if defined TORRENT_VERBOSE_LOGGING + if (!interested) + { + peer_log("*** UPDATE_INTEREST [ not interesting ]"); + } +#endif + + if (!interested) send_not_interested(); + else t->get_policy().peer_is_interesting(*this); + + TORRENT_ASSERT(in_handshake() || is_interesting() == interested); + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + void peer_connection::peer_log(char const* fmt, ...) const + { + if (!m_logger) return; + + va_list v; + va_start(v, fmt); + + char usr[400]; + vsnprintf(usr, sizeof(usr), fmt, v); + va_end(v); + char buf[450]; + snprintf(buf, sizeof(buf), "%s: %s\n", time_now_string(), usr); + (*m_logger) << buf; + } +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + void peer_connection::add_extension(boost::shared_ptr ext) + { + m_extensions.push_back(ext); + } + + peer_plugin const* peer_connection::find_plugin(char const* type) + { + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if (strcmp((*i)->type(), type) == 0) return (*i).get(); + } + return 0; + } +#endif + + void peer_connection::send_allowed_set() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + if (t->super_seeding()) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** SKIPPING ALLOWED SET BECAUSE OF SUPER SEEDING"); +#endif + return; + } + + if (upload_only()) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** SKIPPING ALLOWED SET BECAUSE PEER IS UPLOAD ONLY"); +#endif + return; + } + + int num_allowed_pieces = m_ses.settings().allowed_fast_set_size; + if (num_allowed_pieces == 0) return; + + int num_pieces = t->torrent_file().num_pieces(); + + if (num_allowed_pieces >= num_pieces) + { + // this is a special case where we have more allowed + // fast pieces than pieces in the torrent. Just send + // an allowed fast message for every single piece + for (int i = 0; i < num_pieces; ++i) + { + // there's no point in offering fast pieces + // that the peer already has + if (has_piece(i)) continue; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> ALLOWED_FAST [ %d ]", i); +#endif + write_allow_fast(i); + TORRENT_ASSERT(std::find(m_accept_fast.begin() + , m_accept_fast.end(), i) + == m_accept_fast.end()); + if (m_accept_fast.empty()) + { + m_accept_fast.reserve(10); + m_accept_fast_piece_cnt.reserve(10); + } + m_accept_fast.push_back(i); + m_accept_fast_piece_cnt.push_back(0); + } + return; + } + + std::string x; + address const& addr = m_remote.address(); + if (addr.is_v4()) + { + address_v4::bytes_type bytes = addr.to_v4().to_bytes(); + x.assign((char*)&bytes[0], bytes.size()); + } +#if TORRENT_USE_IPV6 + else + { + address_v6::bytes_type bytes = addr.to_v6().to_bytes(); + x.assign((char*)&bytes[0], bytes.size()); + } +#endif + x.append((char*)&t->torrent_file().info_hash()[0], 20); + + sha1_hash hash = hasher(x.c_str(), x.size()).final(); + for (;;) + { + char* p = (char*)&hash[0]; + for (int i = 0; i < 5; ++i) + { + int piece = detail::read_uint32(p) % num_pieces; + if (std::find(m_accept_fast.begin(), m_accept_fast.end(), piece) + == m_accept_fast.end()) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> ALLOWED_FAST [ %d ]", piece); +#endif + write_allow_fast(piece); + if (m_accept_fast.empty()) + { + m_accept_fast.reserve(10); + m_accept_fast_piece_cnt.reserve(10); + } + m_accept_fast.push_back(piece); + m_accept_fast_piece_cnt.push_back(0); + if (int(m_accept_fast.size()) >= num_allowed_pieces + || int(m_accept_fast.size()) == num_pieces) return; + } + } + hash = hasher((char*)&hash[0], 20).final(); + } + } + + void peer_connection::on_metadata_impl() + { + boost::shared_ptr t = associated_torrent().lock(); + m_have_piece.resize(t->torrent_file().num_pieces(), m_have_all); + m_num_pieces = m_have_piece.count(); + + // now that we know how many pieces there are + // remove any invalid allowed_fast and suggest pieces + // now that we know what the number of pieces are + for (std::vector::iterator i = m_allowed_fast.begin(); + i != m_allowed_fast.end();) + { + if (*i < m_num_pieces) + { + ++i; + continue; + } + i = m_allowed_fast.erase(i); + } + + for (std::vector::iterator i = m_suggested_pieces.begin(); + i != m_suggested_pieces.end();) + { + if (*i < m_num_pieces) + { + ++i; + continue; + } + i = m_suggested_pieces.erase(i); + } + on_metadata(); + if (m_disconnecting) return; + } + + void peer_connection::init() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + TORRENT_ASSERT(t->valid_metadata()); + TORRENT_ASSERT(t->ready_for_connections()); + + m_have_piece.resize(t->torrent_file().num_pieces(), m_have_all); + + if (m_have_all) m_num_pieces = t->torrent_file().num_pieces(); +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(!m_initialized); + m_initialized = true; +#endif + // now that we have a piece_picker, + // update it with this peer's pieces + + TORRENT_ASSERT(m_num_pieces == m_have_piece.count()); + + if (m_num_pieces == int(m_have_piece.size())) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** THIS IS A SEED [ p: %p ]", m_peer_info); +#endif + // if this is a web seed. we don't have a peer_info struct + t->get_policy().set_seed(m_peer_info, true); + m_upload_only = true; + + t->peer_has_all(this); + if (t->is_upload_only()) send_not_interested(); + else t->get_policy().peer_is_interesting(*this); + return; + } + + // if we're a seed, we don't keep track of piece availability + if (!t->is_seed()) + { + t->peer_has(m_have_piece, this); + bool interesting = false; + for (int i = 0; i < int(m_have_piece.size()); ++i) + { + if (m_have_piece[i]) + { + // if the peer has a piece and we don't, the peer is interesting + if (!t->have_piece(i) + && t->picker().piece_priority(i) != 0) + interesting = true; + } + } + if (interesting) t->get_policy().peer_is_interesting(*this); + else send_not_interested(); + } + else + { + update_interest(); + } + } + + peer_connection::~peer_connection() + { +// INVARIANT_CHECK; + TORRENT_ASSERT(!m_in_constructor); + TORRENT_ASSERT(m_disconnecting); + TORRENT_ASSERT(m_disconnect_started); + TORRENT_ASSERT(m_ses.is_network_thread()); + +#if TORRENT_USE_ASSERTS + m_in_use = 0; +#endif + + // defensive + + boost::shared_ptr t = m_torrent.lock(); + // if t is NULL, we better not be connecting, since + // we can't decrement the connecting counter + TORRENT_ASSERT(t || !m_connecting); + + // we should really have dealt with this already + TORRENT_ASSERT(!m_connecting); + if (m_connecting && t) + { + t->dec_num_connecting(); + m_connecting = false; + } + + m_disk_recv_buffer_size = 0; + +#ifndef TORRENT_DISABLE_EXTENSIONS + m_extensions.clear(); +#endif + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** CONNECTION CLOSED"); +#endif +// TORRENT_ASSERT(!m_ses.has_peer(this)); + TORRENT_ASSERT(m_request_queue.empty()); + TORRENT_ASSERT(m_download_queue.empty()); +#if TORRENT_USE_ASSERTS + for (aux::session_impl::torrent_map::const_iterator i = m_ses.m_torrents.begin() + , end(m_ses.m_torrents.end()); i != end; ++i) + TORRENT_ASSERT(!i->second->has_peer(this)); + if (m_peer_info) + TORRENT_ASSERT(m_peer_info->connection == 0); +#endif + } + + int peer_connection::picker_options() const + { + int ret = 0; + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + if (!t) return 0; + + if (t->num_time_critical_pieces() > 0) + { + ret |= piece_picker::time_critical_mode; + } + + if (t->is_sequential_download()) + { + ret |= piece_picker::sequential; + } + else if (t->num_have() < t->settings().initial_picker_threshold) + { + // if we have fewer pieces than a certain threshols + // don't pick rare pieces, just pick random ones, + // and prioritize finishing them + ret |= piece_picker::prioritize_partials; + } + else + { + ret |= piece_picker::rarest_first | piece_picker::speed_affinity; + } + + if (m_snubbed) + { + // snubbed peers should request + // the common pieces first, just to make + // it more likely for all snubbed peers to + // request blocks from the same piece + ret |= piece_picker::reverse; + } + + if (t->settings().prioritize_partial_pieces) + ret |= piece_picker::prioritize_partials; + + if (on_parole()) ret |= piece_picker::on_parole + | piece_picker::prioritize_partials; + + // only one of rarest_first, common_first and sequential can be set. + TORRENT_ASSERT((ret & piece_picker::rarest_first) ? 1 : 0 + + (ret & piece_picker::sequential) ? 1 : 0 <= 1); + return ret; + } + + void peer_connection::fast_reconnect(bool r) + { + if (!peer_info_struct() || peer_info_struct()->fast_reconnects > 1) + return; + m_fast_reconnect = r; + peer_info_struct()->last_connected = (boost::uint16_t)m_ses.session_time(); + int rewind = m_ses.settings().min_reconnect_time * m_ses.settings().max_failcount; + if (peer_info_struct()->last_connected < rewind) peer_info_struct()->last_connected = 0; + else peer_info_struct()->last_connected -= rewind; + + if (peer_info_struct()->fast_reconnects < 15) + ++peer_info_struct()->fast_reconnects; + } + + void peer_connection::announce_piece(int index) + { + // dont announce during handshake + if (in_handshake()) return; + + // remove suggested pieces once we have them + std::vector::iterator i = std::find( + m_suggested_pieces.begin(), m_suggested_pieces.end(), index); + if (i != m_suggested_pieces.end()) m_suggested_pieces.erase(i); + + // remove allowed fast pieces + i = std::find(m_allowed_fast.begin(), m_allowed_fast.end(), index); + if (i != m_allowed_fast.end()) m_allowed_fast.erase(i); + + if (has_piece(index)) + { + // if we got a piece that this peer has + // it might have been the last interesting + // piece this peer had. We might not be + // interested anymore + update_interest(); + if (is_disconnecting()) return; + + // optimization, don't send have messages + // to peers that already have the piece + if (!m_ses.settings().send_redundant_have) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> HAVE [ piece: %d ] SUPRESSED", index); +#endif + return; + } + } + + disconnect_if_redundant(); + if (is_disconnecting()) return; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> HAVE [ piece: %d ]", index); +#endif + write_have(index); +#if TORRENT_USE_ASSERTS + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); +#endif + } + + bool peer_connection::has_piece(int i) const + { + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + TORRENT_ASSERT(t->valid_metadata()); + TORRENT_ASSERT(i >= 0); + TORRENT_ASSERT(i < t->torrent_file().num_pieces()); + return m_have_piece[i]; + } + + std::vector const& peer_connection::request_queue() const + { + return m_request_queue; + } + + std::vector const& peer_connection::download_queue() const + { + return m_download_queue; + } + + std::vector const& peer_connection::upload_queue() const + { + return m_requests; + } + + time_duration peer_connection::download_queue_time(int extra_bytes) const + { + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + int rate = 0; + + // if we haven't received any data recently, the current download rate + // is not representative + if (time_now() - m_last_piece > seconds(30) && m_download_rate_peak > 0) + { + rate = m_download_rate_peak; + } + else if (time_now() - m_last_unchoked < seconds(5) + && m_statistics.total_payload_upload() < 2 * 0x4000) + { + // if we're have only been unchoked for a short period of time, + // we don't know what rate we can get from this peer. Instead of assuming + // the lowest possible rate, assume the average. + + // TODO: this should only be peers we're trying to download from + int peers_with_requests = m_ses.num_connections(); + // avoid division by 0 + if (peers_with_requests == 0) peers_with_requests = 1; + + rate = m_ses.m_stat.transfer_rate(stat::download_payload) / peers_with_requests; + } + else + { + // current download rate in bytes per seconds + rate = m_statistics.transfer_rate(stat::download_payload); + } + + // avoid division by zero + if (rate < 50) rate = 50; + + // average of current rate and peak +// rate = (rate + m_download_rate_peak) / 2; + + return milliseconds((m_outstanding_bytes + + m_queued_time_critical * t->block_size() * 1000) / rate); + } + + void peer_connection::add_stat(size_type downloaded, size_type uploaded) + { + m_statistics.add_stat(downloaded, uploaded); + } + + bitfield const& peer_connection::get_bitfield() const + { + return m_have_piece; + } + + void peer_connection::received_valid_data(int index) + { + // this fails because we haven't had time to disconnect + // seeds yet, and we might have just become one +// INVARIANT_CHECK; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->on_piece_pass(index); + } TORRENT_CATCH(std::exception&) {} + } +#endif + } + + bool peer_connection::received_invalid_data(int index, bool single_peer) + { + INVARIANT_CHECK; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->on_piece_failed(index); + } TORRENT_CATCH(std::exception&) {} + } +#endif + return true; + } + + // verifies a piece to see if it is valid (is within a valid range) + // and if it can correspond to a request generated by libtorrent. + bool peer_connection::verify_piece(const peer_request& p) const + { + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + TORRENT_ASSERT(t->valid_metadata()); + torrent_info const& ti = t->torrent_file(); + + return p.piece >= 0 + && p.piece < ti.num_pieces() + && p.start >= 0 + && p.start < ti.piece_length() + && t->to_req(piece_block(p.piece, p.start / t->block_size())) == p; + } + + void peer_connection::attach_to_torrent(sha1_hash const& ih, bool allow_encrypted) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(!m_disconnecting); + TORRENT_ASSERT(m_torrent.expired()); + boost::weak_ptr wpt = m_ses.find_torrent(ih); + boost::shared_ptr t = wpt.lock(); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + // now that we know which torrent this peer belongs + // to. Move the log file into its directory + + error_code ec; + std::string log_name = combine_path(to_hex(ih.to_string()) + , m_remote.address().to_string(ec) + "_" + + to_string(m_remote.port()).elems); + m_logger->move_log_file(m_ses.get_log_path(), log_name, m_ses.listen_port()); +#endif + + if (t && t->is_aborted()) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** the torrent has been aborted"); +#endif + t.reset(); + } + + if (!t) + { + // we couldn't find the torrent! +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** couldn't find a torrent with the given info_hash: %s torrents:", to_hex(ih.to_string()).c_str()); + session_impl::torrent_map const& torrents = m_ses.m_torrents; + for (session_impl::torrent_map::const_iterator i = torrents.begin() + , end(torrents.end()); i != end; ++i) + { + peer_log(" %s", to_hex(i->second->torrent_file().info_hash().to_string()).c_str()); + } +#endif + +#ifndef TORRENT_DISABLE_DHT + if (dht::verify_secret_id(ih)) + { + // this means the hash was generated from our generate_secret_id() + // as part of DHT traffic. The fact that we got an incoming + // connection on this info-hash, means the other end, making this + // connection fished it out of the DHT chatter. That's suspicious. + m_ses.m_ip_filter.add_rule(m_remote.address(), m_remote.address(), 0); + } +#endif + disconnect(errors::invalid_info_hash, 1); + return; + } + + if (t->is_paused() && (!t->is_auto_managed() + || !m_ses.m_settings.incoming_starts_queued_torrents)) + { + // paused torrents will not accept + // incoming connections unless they are auto managed + // and inconing_starts_queued_torrents is true + // torrents that have errors should always reject + // incoming peers +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("rejected connection to paused torrent"); +#endif + disconnect(errors::torrent_paused, 2); + return; + } + +#if TORRENT_USE_I2P + i2p_stream* i2ps = m_socket->get(); + if (!i2ps && t->torrent_file().is_i2p() && !m_ses.m_settings.allow_i2p_mixed) + { + // the torrent is an i2p torrent, the peer is a regular peer + // and we don't allow mixed mode. Disconnect the peer. +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("rejected regular connection to i2p torrent"); +#endif + disconnect(errors::peer_banned, 2); + return; + } +#endif // TORRENT_USE_I2P + + TORRENT_ASSERT(m_torrent.expired()); + + if (t->is_paused() + && m_ses.m_settings.incoming_starts_queued_torrents + && !m_ses.is_paused() + && !t->is_aborted() + && !m_ses.is_aborted()) + { + t->resume(); + } + + // check to make sure we don't have another connection with the same + // info_hash and peer_id. If we do. close this connection. + t->attach_peer(this); + if (m_disconnecting) return; + m_torrent = wpt; + + if (m_exceeded_limit) + { + // find a peer in some torrent (presumably the one with most peers) + // and disconnect the lowest ranking peer + boost::weak_ptr torr = m_ses.find_disconnect_candidate_torrent(); + boost::shared_ptr other_t = torr.lock(); + + if (other_t) + { + if (other_t->num_peers() <= t->num_peers()) + { + disconnect(errors::too_many_connections); + return; + } + // find the lowest ranking peer and disconnect that + peer_connection* p = other_t->find_lowest_ranking_peer(); + p->disconnect(errors::too_many_connections); + peer_disconnected_other(); + } + else + { + disconnect(errors::too_many_connections); + return; + } + } + + TORRENT_ASSERT(!m_torrent.expired()); + + // if the torrent isn't ready to accept + // connections yet, we'll have to wait with + // our initialization + if (t->ready_for_connections()) init(); + + TORRENT_ASSERT(!m_torrent.expired()); + + // assume the other end has no pieces + // if we don't have valid metadata yet, + // leave the vector unallocated + TORRENT_ASSERT(m_num_pieces == 0); + m_have_piece.clear_all(); + TORRENT_ASSERT(!m_torrent.expired()); + } + + boost::uint32_t peer_connection::peer_rank() const + { + return m_peer_info == NULL ? 0 + : m_peer_info->rank(m_ses.external_address(), m_ses.listen_port()); + } + + // message handlers + + // ----------------------------- + // --------- KEEPALIVE --------- + // ----------------------------- + + void peer_connection::incoming_keepalive() + { + INVARIANT_CHECK; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== KEEPALIVE"); +#endif + } + + // ----------------------------- + // ----------- CHOKE ----------- + // ----------------------------- + + void peer_connection::incoming_choke() + { + INVARIANT_CHECK; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_choke()) return; + } +#endif + if (is_disconnecting()) return; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== CHOKE"); +#endif + m_peer_choked = true; + set_endgame(false); + + clear_request_queue(); + } + + void peer_connection::clear_request_queue() + { + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + // clear the requests that haven't been sent yet + if (peer_info_struct() == 0 || !peer_info_struct()->on_parole) + { + // if the peer is not in parole mode, clear the queued + // up block requests + if (t->has_picker()) + { + piece_picker& p = t->picker(); + for (std::vector::const_iterator i = m_request_queue.begin() + , end(m_request_queue.end()); i != end; ++i) + { + p.abort_download(i->block, peer_info_struct()); + } + } + m_request_queue.clear(); + m_queued_time_critical = 0; + } + } + + bool match_request(peer_request const& r, piece_block const& b, int block_size) + { + if (int(b.piece_index) != r.piece) return false; + if (int(b.block_index) != r.start / block_size) return false; + if (r.start % block_size != 0) return false; + return true; + } + + // ----------------------------- + // -------- REJECT PIECE ------- + // ----------------------------- + + void peer_connection::incoming_reject_request(peer_request const& r) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== REJECT_PIECE [ piece: %d | s: %d | l: %d ]" + , r.piece, r.start, r.length); +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_reject(r)) return; + } +#endif + + if (is_disconnecting()) return; + + std::vector::iterator i = std::find_if( + m_download_queue.begin(), m_download_queue.end() + , boost::bind(match_request, boost::cref(r), boost::bind(&pending_block::block, _1) + , t->block_size())); + + if (i != m_download_queue.end()) + { + pending_block b = *i; + bool remove_from_picker = !i->timed_out && !i->not_wanted; + m_download_queue.erase(i); + TORRENT_ASSERT(m_outstanding_bytes >= r.length); + m_outstanding_bytes -= r.length; + if (m_outstanding_bytes < 0) m_outstanding_bytes = 0; + + // if the peer is in parole mode, keep the request + if (peer_info_struct() && peer_info_struct()->on_parole) + { + // we should only add it if the block is marked as + // busy in the piece-picker + if (remove_from_picker) + m_request_queue.insert(m_request_queue.begin(), b); + } + else if (!t->is_seed() && remove_from_picker) + { + piece_picker& p = t->picker(); + p.abort_download(b.block, peer_info_struct()); + } +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif + } +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + else + { + peer_log("*** PIECE NOT IN REQUEST QUEUE"); + } +#endif + if (has_peer_choked()) + { + // if we're choked and we got a rejection of + // a piece in the allowed fast set, remove it + // from the allow fast set. + std::vector::iterator i = std::find( + m_allowed_fast.begin(), m_allowed_fast.end(), r.piece); + if (i != m_allowed_fast.end()) m_allowed_fast.erase(i); + } + else + { + std::vector::iterator i = std::find(m_suggested_pieces.begin() + , m_suggested_pieces.end(), r.piece); + if (i != m_suggested_pieces.end()) + m_suggested_pieces.erase(i); + } + + if (m_request_queue.empty() && m_download_queue.size() < 2) + { +#ifdef TORRENT_STATS + ++m_ses.m_reject_piece_picks; +#endif + request_a_block(*t, *this); + send_block_requests(); + } + } + + // ----------------------------- + // ------- SUGGEST PIECE ------- + // ----------------------------- + + void peer_connection::incoming_suggest(int index) + { + INVARIANT_CHECK; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== SUGGEST_PIECE [ piece: %d ]", index); +#endif + boost::shared_ptr t = m_torrent.lock(); + if (!t) return; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_suggest(index)) return; + } +#endif + + if (is_disconnecting()) return; + if (index < 0) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("<== INVALID_SUGGEST_PIECE [ %d ]", index); +#endif + return; + } + + if (t->valid_metadata()) + { + if (index >= int(m_have_piece.size())) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("<== INVALID_SUGGEST [ %d | s: %d ]" + , index, int(m_have_piece.size())); +#endif + return; + } + + // if we already have the piece, we can + // ignore this message + if (t->have_piece(index)) + return; + } + + if (int(m_suggested_pieces.size()) > m_ses.m_settings.max_suggest_pieces) + m_suggested_pieces.erase(m_suggested_pieces.begin()); + + m_suggested_pieces.push_back(index); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("** SUGGEST_PIECE [ piece: %d added to set: %d ]", index, int(m_suggested_pieces.size())); +#endif + } + + // ----------------------------- + // ---------- UNCHOKE ---------- + // ----------------------------- + + void peer_connection::incoming_unchoke() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_unchoke()) return; + } +#endif + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== UNCHOKE"); +#endif + m_peer_choked = false; + m_last_unchoked = time_now(); + if (is_disconnecting()) return; + + if (is_interesting()) + { +#ifdef TORRENT_STATS + ++m_ses.m_unchoke_piece_picks; +#endif + request_a_block(*t, *this); + send_block_requests(); + } + } + + // ----------------------------- + // -------- INTERESTED --------- + // ----------------------------- + + void peer_connection::incoming_interested() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_interested()) return; + } +#endif + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== INTERESTED"); +#endif + m_peer_interested = true; + if (is_disconnecting()) return; + + // if the peer is ready to download stuff, it must have metadata + m_has_metadata = true; + + disconnect_if_redundant(); + if (is_disconnecting()) return; + + if (is_choked() && !t->graceful_pause()) + { + if (ignore_unchoke_slots()) + { + // if this peer is expempted from the choker + // just unchoke it immediately + send_unchoke(); + } + else if ((m_ses.num_uploads() < m_ses.settings().unchoke_slots_limit + || m_ses.settings().unchoke_slots_limit < 0)) + { + // if the peer is choked and we have upload slots left, + // then unchoke it. Another condition that has to be met + // is that the torrent doesn't keep track of the individual + // up/down ratio for each peer (ratio == 0) or (if it does + // keep track) this particular connection isn't a leecher. + // If the peer was choked because it was leeching, don't + // unchoke it again. + // The exception to this last condition is if we're a seed. + // In that case we don't care if people are leeching, they + // can't pay for their downloads anyway. + m_ses.unchoke_peer(*this); + } +#if defined TORRENT_VERBOSE_LOGGING + else + { + peer_log("DID NOT UNCHOKE [ the number of uploads (%d)" + "is more than or equal to the limit (%d) ]" + , m_ses.num_uploads(), m_ses.settings().unchoke_slots_limit); + } +#endif + } +#if defined TORRENT_VERBOSE_LOGGING + else if (t->graceful_pause()) + { + peer_log("DID NOT UNCHOKE [ graceful pause mode ]"); + } +#endif + } + + // ----------------------------- + // ------ NOT INTERESTED ------- + // ----------------------------- + + void peer_connection::incoming_not_interested() + { + INVARIANT_CHECK; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_not_interested()) return; + } +#endif + + m_became_uninterested = time_now(); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== NOT_INTERESTED"); +#endif + m_peer_interested = false; + if (is_disconnecting()) return; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + if (!is_choked()) + { + if (ignore_unchoke_slots()) + { + send_choke(); + } + else + { + if (m_peer_info && m_peer_info->optimistically_unchoked) + { + m_peer_info->optimistically_unchoked = false; + m_ses.m_optimistic_unchoke_time_scaler = 0; + } + m_ses.choke_peer(*this); + m_ses.m_unchoke_time_scaler = 0; + } + } + } + + // ----------------------------- + // ----------- HAVE ------------ + // ----------------------------- + + void peer_connection::incoming_have(int index) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_have(index)) return; + } +#endif + + if (is_disconnecting()) return; + + // if we haven't received a bitfield, it was + // probably omitted, which is the same as 'have_none' + if (!m_bitfield_received) incoming_have_none(); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== HAVE [ piece: %d ]", index); +#endif + + if (is_disconnecting()) return; + + if (!t->valid_metadata() && index >= int(m_have_piece.size())) + { + if (index < 131072) + { + // if we don't have metadata + // and we might not have received a bitfield + // extend the bitmask to fit the new + // have message + m_have_piece.resize(index + 1, false); + } + else + { + // unless the index > 64k, in which case + // we just ignore it + return; + } + } + + // if we got an invalid message, abort + if (index >= int(m_have_piece.size()) || index < 0) + { + disconnect(errors::invalid_have, 2); + return; + } + + if (t->super_seeding() && !m_ses.settings().strict_super_seeding) + { + // if we're superseeding and the peer just told + // us that it completed the piece we're superseeding + // to it, change the superseeding piece for this peer + // if the peer optimizes out redundant have messages + // this will be handled when the peer sends not-interested + // instead. + if (super_seeded_piece(index)) + { + superseed_piece(index, t->get_piece_to_super_seed(m_have_piece)); + } + } + + if (m_have_piece[index]) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log(" got redundant HAVE message for index: %d", index); +#endif + return; + } + + m_have_piece.set_bit(index); + ++m_num_pieces; + + // if the peer is downloading stuff, it must have metadata + m_has_metadata = true; + + // only update the piece_picker if + // we have the metadata and if + // we're not a seed (in which case + // we won't have a piece picker) + if (!t->valid_metadata()) return; + + t->peer_has(index, this); + + // this will disregard all have messages we get within + // the first two seconds. Since some clients implements + // lazy bitfields, these will not be reliable to use + // for an estimated peer download rate. + if (!peer_info_struct() + || m_ses.session_time() - peer_info_struct()->last_connected > 2) + { + // update bytes downloaded since last timer + ++m_remote_pieces_dled; + } + + // it's important to not disconnect before we have + // updated the piece picker, otherwise we will incorrectly + // decrement the piece count without first incrementing it + if (is_seed()) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** THIS IS A SEED [ p: %p ]", m_peer_info); +#endif + t->seen_complete(); + t->get_policy().set_seed(m_peer_info, true); + m_upload_only = true; + } + + // it's important to update whether we're intersted in this peer before + // calling disconnect_if_redundant, otherwise we may disconnect even if + // we are interested + if (!t->have_piece(index) + && !t->is_seed() + && !is_interesting() + && t->picker().piece_priority(index) != 0) + t->get_policy().peer_is_interesting(*this); + + disconnect_if_redundant(); + if (is_disconnecting()) return; + + // if we're super seeding, this might mean that somebody + // forwarded this piece. In which case we need to give + // a new piece to that peer + if (t->super_seeding() + && m_ses.settings().strict_super_seeding + && (!super_seeded_piece(index) || t->num_peers() == 1)) + { + for (torrent::peer_iterator i = t->begin() + , end(t->end()); i != end; ++i) + { + peer_connection* p = *i; + if (!p->super_seeded_piece(index)) continue; + if (!p->has_piece(index)) continue; + p->superseed_piece(index, t->get_piece_to_super_seed(p->get_bitfield())); + } + } + } + + // ----------------------------- + // -------- DONT HAVE ---------- + // ----------------------------- + + void peer_connection::incoming_dont_have(int index) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_dont_have(index)) return; + } +#endif + + if (is_disconnecting()) return; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== DONT_HAVE [ piece: %d ]", index); +#endif + + // if we got an invalid message, abort + if (index >= int(m_have_piece.size()) || index < 0) + { + disconnect(errors::invalid_dont_have, 2); + return; + } + + if (!m_have_piece[index]) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log(" got redundant DONT_HAVE message for index: %d", index); +#endif + return; + } + + bool was_seed = is_seed(); + m_have_piece.clear_bit(index); + TORRENT_ASSERT(m_num_pieces > 0); + --m_num_pieces; + + // only update the piece_picker if + // we have the metadata and if + // we're not a seed (in which case + // we won't have a piece picker) + if (!t->valid_metadata()) return; + + t->peer_lost(index, this); + + if (was_seed) + t->get_policy().set_seed(m_peer_info, false); + } + + // ----------------------------- + // --------- BITFIELD ---------- + // ----------------------------- + + void peer_connection::incoming_bitfield(bitfield const& bits) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_bitfield(bits)) return; + } +#endif + + if (is_disconnecting()) return; + +#ifdef TORRENT_VERBOSE_LOGGING + std::string bitfield_str; + bitfield_str.resize(bits.size()); + for (int i = 0; i < int(bits.size()); ++i) + bitfield_str[i] = bits[i] ? '1' : '0'; + peer_log("<== BITFIELD [ %s ]", bitfield_str.c_str()); +#endif + + // if we don't have the metedata, we cannot + // verify the bitfield size + if (t->valid_metadata() + && (bits.size() + 7) / 8 != (m_have_piece.size() + 7) / 8) + { + disconnect(errors::invalid_bitfield_size, 2); + return; + } + + if (m_bitfield_received) + { + // if we've already received a bitfield message + // we first need to count down all the pieces + // we believe the peer has first + t->peer_lost(m_have_piece, this); + } + + m_bitfield_received = true; + + // if we don't have metadata yet + // just remember the bitmask + // don't update the piecepicker + // (since it doesn't exist yet) + if (!t->ready_for_connections()) + { +#ifdef TORRENT_VERBOSE_LOGGING + if (m_num_pieces == int(bits.size())) + peer_log("*** THIS IS A SEED [ p: %p ]", m_peer_info); +#endif + m_have_piece = bits; + m_num_pieces = bits.count(); + t->get_policy().set_seed(m_peer_info, m_num_pieces == int(bits.size())); + return; + } + + TORRENT_ASSERT(t->valid_metadata()); + + int num_pieces = bits.count(); + if (num_pieces == int(m_have_piece.size())) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** THIS IS A SEED [ p: %p ]", m_peer_info); +#endif + // if this is a web seed. we don't have a peer_info struct + t->get_policy().set_seed(m_peer_info, true); + m_upload_only = true; + + m_have_piece.set_all(); + m_num_pieces = num_pieces; + t->peer_has_all(this); + + // this will cause us to send the INTERESTED message + if (!t->is_upload_only()) + t->get_policy().peer_is_interesting(*this); + + disconnect_if_redundant(); + + return; + } + + // let the torrent know which pieces the + // peer has + // if we're a seed, we don't keep track of piece availability + t->peer_has(bits, this); + + m_have_piece = bits; + m_num_pieces = num_pieces; + + update_interest(); + } + + void peer_connection::disconnect_if_redundant() + { + // we cannot disconnect in a constructor + TORRENT_ASSERT(m_in_constructor == false); + if (!m_ses.settings().close_redundant_connections) return; + + boost::shared_ptr t = m_torrent.lock(); + if (!t) return; + + // if we don't have the metadata yet, don't disconnect + // also, if the peer doesn't have metadata we shouldn't + // disconnect it, since it may want to request the + // metadata from us + if (!t->valid_metadata() || !has_metadata()) return; + + // don't close connections in share mode, we don't know if we need them + if (t->share_mode()) return; + + if (m_upload_only && t->is_upload_only()) + { + if (!can_disconnect(error_code(errors::upload_upload_connection, get_libtorrent_category()))) return; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** the peer is upload-only and our torrent is also upload-only"); +#endif + disconnect(errors::upload_upload_connection); + return; + } + + if (m_upload_only + && !m_interesting + && m_bitfield_received + && t->are_files_checked()) + { + if (!can_disconnect(error_code(errors::uninteresting_upload_peer, get_libtorrent_category()))) return; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** the peer is upload-only and we're not interested in it"); +#endif + disconnect(errors::uninteresting_upload_peer); + return; + } + } + + bool peer_connection::can_disconnect(error_code const& ec) const + { +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::const_iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if (!(*i)->can_disconnect(ec)) return false; + } +#endif + return true; + } + + // ----------------------------- + // ---------- REQUEST ---------- + // ----------------------------- + + void peer_connection::incoming_request(peer_request const& r) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + +#ifdef TORRENT_STATS + ++m_ses.m_piece_requests; +#endif + +#if defined TORRENT_VERBOSE_LOGGING + peer_log("<== REQUEST [ piece: %d s: %d l: %d ]" + , r.piece, r.start, r.length); +#endif + + if (t->super_seeding() + && !super_seeded_piece(r.piece)) + { +#ifdef TORRENT_STATS + ++m_ses.m_invalid_piece_requests; +#endif + ++m_num_invalid_requests; +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** INVALID_REQUEST [ piece not superseeded " + "i: %d t: %d n: %d h: %d ss1: %d ss2: %d ]" + , m_peer_interested + , int(t->torrent_file().piece_size(r.piece)) + , t->torrent_file().num_pieces() + , t->have_piece(r.piece) + , m_superseed_piece[0] + , m_superseed_piece[1]); +#endif + + write_reject_request(r); + + if (t->alerts().should_post()) + { + t->alerts().post_alert(invalid_request_alert( + t->get_handle(), m_remote, m_peer_id, r)); + } + return; + } + + // if we haven't received a bitfield, it was + // probably omitted, which is the same as 'have_none' + if (!m_bitfield_received) incoming_have_none(); + if (is_disconnecting()) return; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_request(r)) return; + } +#endif + if (is_disconnecting()) return; + + if (!t->valid_metadata()) + { +#ifdef TORRENT_STATS + ++m_ses.m_invalid_piece_requests; +#endif + // if we don't have valid metadata yet, + // we shouldn't get a request +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** INVALID_REQUEST [ we don't have metadata yet ]"); + peer_log("==> REJECT_PIECE [ piece: %d s: %d l: %d ]" + , r.piece , r.start , r.length); +#endif + write_reject_request(r); + return; + } + + if (int(m_requests.size()) > m_ses.settings().max_allowed_in_request_queue) + { +#ifdef TORRENT_STATS + ++m_ses.m_max_piece_requests; +#endif + // don't allow clients to abuse our + // memory consumption. + // ignore requests if the client + // is making too many of them. +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** INVALID_REQUEST [ incoming request queue full %d ]" + , int(m_requests.size())); + peer_log("==> REJECT_PIECE [ piece: %d s: %d l: %d ]" + , r.piece , r.start , r.length); +#endif + write_reject_request(r); + return; + } + + int fast_idx = -1; + std::vector::iterator fast_iter = std::find(m_accept_fast.begin() + , m_accept_fast.end(), r.piece); + if (fast_iter != m_accept_fast.end()) fast_idx = fast_iter - m_accept_fast.begin(); + + // make sure this request + // is legal and that the peer + // is not choked + if (r.piece >= 0 + && r.piece < t->torrent_file().num_pieces() + && t->have_piece(r.piece) + && r.start >= 0 + && r.start < t->torrent_file().piece_size(r.piece) + && r.length > 0 + && r.length + r.start <= t->torrent_file().piece_size(r.piece) + && m_peer_interested + && r.length <= t->block_size()) + { + // if we have choked the client + // ignore the request + const int blocks_per_piece = static_cast( + (t->torrent_file().piece_length() + t->block_size() - 1) / t->block_size()); + + // disconnect peers that downloads more than foo times an allowed + // fast piece + if (m_choked && fast_idx != -1 + && m_accept_fast_piece_cnt[fast_idx] >= 3 * blocks_per_piece + && can_disconnect(error_code(errors::too_many_requests_when_choked, get_libtorrent_category()))) + { + disconnect(errors::too_many_requests_when_choked); + return; + } + + if (m_choked && fast_idx == -1) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** REJECTING REQUEST [ peer choked and piece not in allowed fast set ]"); + peer_log(" ==> REJECT_PIECE [ piece: %d | s: %d | l: %d ]" + , r.piece, r.start, r.length); +#endif +#ifdef TORRENT_STATS + ++m_ses.m_choked_piece_requests; +#endif + write_reject_request(r); + + time_duration since_choked = time_now() - m_last_choke; + + // allow peers to send request up to 2 seconds after getting choked, + // the disconnect them + if (total_milliseconds(since_choked) > 2000 + && can_disconnect(error_code(errors::too_many_requests_when_choked, get_libtorrent_category()))) + { + disconnect(errors::too_many_requests_when_choked, 2); + return; + } + } + else + { + // increase the allowed fast set counter + if (fast_idx != -1) + ++m_accept_fast_piece_cnt[fast_idx]; + + m_requests.push_back(r); +#ifdef TORRENT_REQUEST_LOGGING + if (m_ses.m_request_log) + write_request_log(m_ses.m_request_log, t->info_hash(), this, r); +#endif + m_last_incoming_request = time_now(); + fill_send_buffer(); + } + } + else + { +#ifdef TORRENT_STATS + ++m_ses.m_invalid_piece_requests; +#endif +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** INVALID_REQUEST [ " + "i: %d t: %d n: %d h: %d block_limit: %d ]" + , m_peer_interested + , int(t->torrent_file().piece_size(r.piece)) + , t->torrent_file().num_pieces() + , t->have_piece(r.piece) + , t->block_size()); + + peer_log("==> REJECT_PIECE [ piece: %d | s: %d | l: %d ] invalid request" + , r.piece , r.start , r.length); +#endif + + write_reject_request(r); + ++m_num_invalid_requests; + + if (t->alerts().should_post()) + { + t->alerts().post_alert(invalid_request_alert( + t->get_handle(), m_remote, m_peer_id, r)); + } + } + } + + void peer_connection::incoming_piece_fragment(int bytes) + { + m_last_piece = time_now(); + TORRENT_ASSERT(m_outstanding_bytes >= bytes); + m_outstanding_bytes -= bytes; + if (m_outstanding_bytes < 0) m_outstanding_bytes = 0; + boost::shared_ptr t = associated_torrent().lock(); +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_received_in_piece + bytes <= t->block_size()); + m_received_in_piece += bytes; +#endif + + // progress of this torrent increased + t->state_updated(); + +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif + } + + void peer_connection::start_receive_piece(peer_request const& r) + { +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif +#if TORRENT_USE_ASSERTS + buffer::const_interval recv_buffer = receive_buffer(); + int recv_pos = recv_buffer.end - recv_buffer.begin; + TORRENT_ASSERT(recv_pos >= 9); +#endif + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + if (!verify_piece(r)) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** INVALID_PIECE [ piece: %d s: %d l: %d ]" + , r.piece, r.start, r.length); +#endif + disconnect(errors::invalid_piece, 2); + return; + } + + piece_block b(r.piece, r.start / t->block_size()); + m_receiving_block = b; + + bool in_req_queue = false; + for (std::vector::const_iterator i = m_download_queue.begin() + , end(m_download_queue.end()); i != end; ++i) + { + if (i->block != b) continue; + in_req_queue = true; + break; + } + + // if this is not in the request queue, we have to + // assume our outstanding bytes includes this piece too + // if we're disconnecting, we shouldn't add pieces + if (!in_req_queue && !m_disconnecting) + { + for (std::vector::iterator i = m_request_queue.begin() + , end(m_request_queue.end()); i != end; ++i) + { + if (i->block != b) continue; + in_req_queue = true; + if (i - m_request_queue.begin() < m_queued_time_critical) + --m_queued_time_critical; + m_request_queue.erase(i); + break; + } + + m_download_queue.insert(m_download_queue.begin(), b); + if (!in_req_queue) + { + if (t->alerts().should_post()) + { + t->alerts().post_alert(unwanted_block_alert(t->get_handle(), m_remote + , m_peer_id, b.block_index, b.piece_index)); + } +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** The block we just got was not in the request queue ***"); +#endif + TORRENT_ASSERT(m_download_queue.front().block == b); + m_download_queue.front().not_wanted = true; + } + m_outstanding_bytes += r.length; + } + } + +#ifdef TORRENT_DEBUG + struct check_postcondition + { + check_postcondition(boost::shared_ptr const& t_ + , bool init_check = true): t(t_) { if (init_check) check(); } + + ~check_postcondition() { check(); } + + void check() + { + if (!t->is_seed()) + { + const int blocks_per_piece = static_cast( + (t->torrent_file().piece_length() + t->block_size() - 1) / t->block_size()); + + std::vector const& dl_queue + = t->picker().get_download_queue(); + + for (std::vector::const_iterator i = + dl_queue.begin(); i != dl_queue.end(); ++i) + { + TORRENT_ASSERT(i->finished <= blocks_per_piece); + } + } + } + + shared_ptr t; + }; +#endif + + + // ----------------------------- + // ----------- PIECE ----------- + // ----------------------------- + + void peer_connection::incoming_piece(peer_request const& p, char const* data) + { + char* buffer = m_ses.allocate_disk_buffer("receive buffer"); + if (buffer == 0) + { + disconnect(errors::no_memory); + return; + } + disk_buffer_holder holder(m_ses, buffer); + std::memcpy(buffer, data, p.length); + incoming_piece(p, holder); + } + + void peer_connection::incoming_piece(peer_request const& p, disk_buffer_holder& data) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + TORRENT_ASSERT(!m_disk_recv_buffer); + TORRENT_ASSERT(m_disk_recv_buffer_size == 0); + + // we're not receiving any block right now + m_receiving_block = piece_block::invalid; + +#ifdef TORRENT_CORRUPT_DATA + // corrupt all pieces from certain peers + if (m_remote.address().is_v4() + && (m_remote.address().to_v4().to_ulong() & 0xf) == 0) + { + data.get()[0] = ~data.get()[0]; + } +#endif + + // if we haven't received a bitfield, it was + // probably omitted, which is the same as 'have_none' + if (!m_bitfield_received) incoming_have_none(); + if (is_disconnecting()) return; + + update_desired_queue_size(); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_piece(p, data)) + { +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_received_in_piece == p.length); + m_received_in_piece = 0; +#endif + return; + } + } +#endif + if (is_disconnecting()) return; + +#ifdef TORRENT_DEBUG + check_postcondition post_checker_(t); +#if TORRENT_USE_INVARIANT_CHECKS + t->check_invariant(); +#endif +#endif + +#ifdef TORRENT_VERBOSE_LOGGING + hasher h; + h.update(data.get(), p.length); + peer_log("<== PIECE [ piece: %d | s: %d | l: %d | ds: %d | qs: %d | q: %d | hash: %s ]" + , p.piece, p.start, p.length, statistics().download_rate() + , int(m_desired_queue_size), int(m_download_queue.size()) + , to_hex(h.final().to_string()).c_str()); +#endif + + if (p.length == 0) + { + if (t->alerts().should_post()) + { + t->alerts().post_alert(peer_error_alert(t->get_handle(), m_remote + , m_peer_id, errors::peer_sent_empty_piece)); + } + // This is used as a reject-request by bitcomet + incoming_reject_request(p); + return; + } + + // if we're already seeding, don't bother, + // just ignore it + if (t->is_seed()) + { +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_received_in_piece == p.length); + m_received_in_piece = 0; +#endif + if (!m_download_queue.empty()) m_download_queue.erase(m_download_queue.begin()); + t->add_redundant_bytes(p.length, torrent::piece_seed); + return; + } + + ptime now = time_now(); + + piece_picker& picker = t->picker(); + piece_manager& fs = t->filesystem(); + + piece_block block_finished(p.piece, p.start / t->block_size()); + TORRENT_ASSERT(verify_piece(p)); + + std::vector::iterator b + = std::find_if( + m_download_queue.begin() + , m_download_queue.end() + , has_block(block_finished)); + + if (b == m_download_queue.end()) + { + if (t->alerts().should_post()) + { + t->alerts().post_alert(unwanted_block_alert(t->get_handle(), m_remote + , m_peer_id, block_finished.block_index, block_finished.piece_index)); + } +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** The block we just got was not in the request queue ***"); +#endif +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT_VAL(m_received_in_piece == p.length, m_received_in_piece); + m_received_in_piece = 0; +#endif + t->add_redundant_bytes(p.length, torrent::piece_unknown); + + // the bytes of the piece we just completed have been deducted from + // m_outstanding_bytes as we received it, in incoming_piece_fragment. + // however, it now turns out the piece we received wasn't in the + // download queue, so we still have the same number of pieces in the + // download queue, which is why we need to add the bytes back. + m_outstanding_bytes += p.length; +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif + return; + } + +#if TORRENT_USE_ASSERTS + pending_block pending_b = *b; +#endif + + int block_index = b - m_download_queue.begin(); + TORRENT_ASSERT(m_download_queue[block_index] == pending_b); + for (int i = 0; i < block_index; ++i) + { + pending_block& qe = m_download_queue[i]; + TORRENT_ASSERT(m_download_queue[block_index] == pending_b); + TORRENT_ASSERT(i < block_index); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** SKIPPED_PIECE [ piece: %d b: %d dqs: %d ]" + , qe.block.piece_index, qe.block.block_index, int(m_desired_queue_size)); +#endif + + ++qe.skipped; + // if the number of times a block is skipped by out of order + // blocks exceeds the size of the outstanding queue, assume that + // the other end dropped the request. + if (m_ses.m_settings.drop_skipped_requests + && qe.skipped > m_desired_queue_size * 2) + { + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(request_dropped_alert(t->get_handle() + , remote(), pid(), qe.block.block_index, qe.block.piece_index)); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** DROPPED_PIECE [ piece: %d b: %d dqs: %d skip: %d ]" + , qe.block.piece_index, qe.block.block_index + , int(m_desired_queue_size), qe.skipped); +#endif + if (!qe.timed_out && !qe.not_wanted) + picker.abort_download(qe.block, peer_info_struct()); + + TORRENT_ASSERT(m_outstanding_bytes >= t->to_req(qe.block).length); + m_outstanding_bytes -= t->to_req(qe.block).length; + if (m_outstanding_bytes < 0) m_outstanding_bytes = 0; + TORRENT_ASSERT(m_download_queue[block_index] == pending_b); + m_download_queue.erase(m_download_queue.begin() + i); + --i; + --block_index; + TORRENT_ASSERT(m_download_queue[block_index] == pending_b); +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif + } + } + TORRENT_ASSERT(int(m_download_queue.size()) > block_index); + b = m_download_queue.begin() + block_index; + TORRENT_ASSERT(*b == pending_b); + +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT_VAL(m_received_in_piece == p.length, m_received_in_piece); + m_received_in_piece = 0; +#endif + // if the block we got is already finished, then ignore it + if (picker.is_downloaded(block_finished)) + { + torrent::wasted_reason_t reason; + if (b->timed_out) reason = torrent::piece_timed_out; + else if (b->not_wanted) reason = torrent::piece_cancelled; + else if (b->busy) reason = torrent::piece_end_game; + else reason = torrent::piece_unknown; + + t->add_redundant_bytes(p.length, reason); + + m_download_queue.erase(b); + m_timeout_extend = 0; + + if (!m_download_queue.empty()) + m_requested = now; + +#ifdef TORRENT_STATS + ++m_ses.m_incoming_redundant_piece_picks; +#endif + request_a_block(*t, *this); + send_block_requests(); + return; + } + + if (total_seconds(now - m_requested) + < m_ses.settings().request_timeout + && m_snubbed) + { + m_snubbed = false; + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(peer_unsnubbed_alert(t->get_handle() + , m_remote, m_peer_id)); + } + } + + if (t->is_deleted()) return; + + int write_queue_size = fs.async_write(p, data, boost::bind(&peer_connection::on_disk_write_complete + , self(), _1, _2, p, t)); + m_outstanding_writing_bytes += p.length; + m_download_queue.erase(b); + + if (write_queue_size / 16 / 1024 > m_ses.m_settings.cache_size / 2 + && m_ses.m_settings.cache_size > 5 + && (now - m_ses.m_last_disk_queue_performance_warning) > seconds(10) + && m_ses.m_alerts.should_post()) + { + m_ses.m_last_disk_queue_performance_warning = now; + t->alerts().post_alert(performance_alert(t->get_handle() + , performance_alert::too_high_disk_queue_limit)); + } + + if (!m_ses.can_write_to_disk() + && m_ses.settings().max_queued_disk_bytes + && t->alerts().should_post() + && (now - m_ses.m_last_disk_performance_warning) > seconds(10)) + { + m_ses.m_last_disk_performance_warning = now; + t->alerts().post_alert(performance_alert(t->get_handle() + , performance_alert::outstanding_disk_buffer_limit_reached)); + } + + if (!m_download_queue.empty()) + { + m_timeout_extend = (std::max)(m_timeout_extend + - m_ses.settings().request_timeout, 0); + m_requested += seconds(m_ses.settings().request_timeout); + if (m_requested > now) m_requested = now; + } + else + { + m_timeout_extend = 0; + } + + bool was_finished = picker.is_piece_finished(p.piece); + // did we request this block from any other peers? + bool multi = picker.num_peers(block_finished) > 1; + picker.mark_as_writing(block_finished, peer_info_struct()); + + TORRENT_ASSERT(picker.num_peers(block_finished) == 0); + // if we requested this block from other peers, cancel it now + if (multi) t->cancel_block(block_finished); + + TORRENT_ASSERT(picker.num_peers(block_finished) == 0); + +#if TORRENT_USE_INVARIANT_CHECKS \ + && defined TORRENT_EXPENSIVE_INVARIANT_CHECKS + t->check_invariant(); +#endif + +#if TORRENT_USE_ASSERTS + piece_picker::downloading_piece pi; + picker.piece_info(p.piece, pi); + int num_blocks = picker.blocks_in_piece(p.piece); + TORRENT_ASSERT(pi.writing + pi.finished + pi.requested <= num_blocks); + TORRENT_ASSERT(picker.is_piece_finished(p.piece) == (pi.writing + pi.finished == num_blocks)); +#endif + + // did we just finish the piece? + // this means all blocks are either written + // to disk or are in the disk write cache + if (picker.is_piece_finished(p.piece) && !was_finished) + { +#ifdef TORRENT_DEBUG + check_postcondition post_checker2_(t, false); +#endif + t->async_verify_piece(p.piece, boost::bind(&torrent::piece_finished, t + , p.piece, _1)); + } + + if (is_disconnecting()) return; + +#ifdef TORRENT_STATS + ++m_ses.m_incoming_piece_picks; +#endif + request_a_block(*t, *this); + send_block_requests(); + } + + void peer_connection::on_disk_write_complete(int ret, disk_io_job const& j + , peer_request p, boost::shared_ptr t) + { +#ifdef TORRENT_STATS + ++m_ses.m_num_messages[aux::session_impl::on_disk_write_counter]; +#endif + TORRENT_ASSERT(m_ses.is_network_thread()); + + // flush send buffer at the end of this scope + // TODO: 1 peers should really be corked/uncorked outside of + // all completed disk operations + cork _c(*this); + + INVARIANT_CHECK; + + m_outstanding_writing_bytes -= p.length; + TORRENT_ASSERT(m_outstanding_writing_bytes >= 0); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) +// (*m_ses.m_logger) << time_now_string() << " *** DISK_WRITE_COMPLETE [ p: " +// << p.piece << " o: " << p.start << " ]\n"; +#endif + + if (!t) + { + disconnect(j.error); + return; + } + + // in case the outstanding bytes just dropped down + // to allow to receive more data + setup_receive(read_async); + + piece_block block_finished(p.piece, p.start / t->block_size()); + + if (ret == -1) + { + // handle_disk_error may disconnect us + t->handle_disk_error(j, this); + return; + } + + if (!t->has_picker()) return; + + piece_picker& picker = t->picker(); + + TORRENT_ASSERT(p.piece == j.piece); + TORRENT_ASSERT(p.start == j.offset); + TORRENT_ASSERT(picker.num_peers(block_finished) == 0); + picker.mark_as_finished(block_finished, peer_info_struct()); + if (t->alerts().should_post()) + { + t->alerts().post_alert(block_finished_alert(t->get_handle(), + remote(), pid(), block_finished.block_index, block_finished.piece_index)); + } + + if (t->is_aborted()) return; + } + + // ----------------------------- + // ---------- CANCEL ----------- + // ----------------------------- + + void peer_connection::incoming_cancel(peer_request const& r) + { + INVARIANT_CHECK; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_cancel(r)) return; + } +#endif + if (is_disconnecting()) return; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== CANCEL [ piece: %d | s: %d | l: %d ]", r.piece, r.start, r.length); +#endif + + std::vector::iterator i + = std::find(m_requests.begin(), m_requests.end(), r); + + if (i != m_requests.end()) + { +#ifdef TORRENT_STATS + ++m_ses.m_cancelled_piece_requests; +#endif + m_requests.erase(i); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> REJECT_PIECE [ piece: %d s: %d l: %d ]" + , r.piece , r.start , r.length); +#endif + write_reject_request(r); + } + else + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** GOT CANCEL NOT IN THE QUEUE"); +#endif + } + } + + // ----------------------------- + // --------- DHT PORT ---------- + // ----------------------------- + + void peer_connection::incoming_dht_port(int listen_port) + { + INVARIANT_CHECK; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== DHT_PORT [ p: %d ]", listen_port); +#endif +#ifndef TORRENT_DISABLE_DHT + m_ses.add_dht_node(udp::endpoint( + m_remote.address(), listen_port)); +#endif + } + + // ----------------------------- + // --------- HAVE ALL ---------- + // ----------------------------- + + void peer_connection::incoming_have_all() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + // we cannot disconnect in a constructor, and + // this function may end up doing that + TORRENT_ASSERT(m_in_constructor == false); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== HAVE_ALL"); +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_have_all()) return; + } +#endif + if (is_disconnecting()) return; + + if (m_bitfield_received) + t->peer_lost(m_have_piece, this); + + m_have_all = true; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** THIS IS A SEED [ p: %p ]", m_peer_info); +#endif + + t->get_policy().set_seed(m_peer_info, true); + m_upload_only = true; + m_bitfield_received = true; + + // if we don't have metadata yet + // just remember the bitmask + // don't update the piecepicker + // (since it doesn't exist yet) + if (!t->ready_for_connections()) + { + // assume seeds are interesting when we + // don't even have the metadata + t->get_policy().peer_is_interesting(*this); + + disconnect_if_redundant(); + // TODO: this might need something more + // so that once we have the metadata + // we can construct a full bitfield + return; + } + + TORRENT_ASSERT(!m_have_piece.empty()); + m_have_piece.set_all(); + m_num_pieces = m_have_piece.size(); + + t->peer_has_all(this); + + // if we're finished, we're not interested + if (t->is_upload_only()) send_not_interested(); + else t->get_policy().peer_is_interesting(*this); + + disconnect_if_redundant(); + } + + // ----------------------------- + // --------- HAVE NONE --------- + // ----------------------------- + + void peer_connection::incoming_have_none() + { + INVARIANT_CHECK; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== HAVE_NONE"); +#endif + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_have_none()) return; + } +#endif + if (is_disconnecting()) return; + + if (m_bitfield_received) + t->peer_lost(m_have_piece, this); + + t->get_policy().set_seed(m_peer_info, false); + m_bitfield_received = true; + + m_have_piece.clear_all(); + m_num_pieces = 0; + + // if the peer is ready to download stuff, it must have metadata + m_has_metadata = true; + + // we're never interested in a peer that doesn't have anything + send_not_interested(); + + TORRENT_ASSERT(!m_have_piece.empty() || !t->ready_for_connections()); + disconnect_if_redundant(); + } + + // ----------------------------- + // ------- ALLOWED FAST -------- + // ----------------------------- + + void peer_connection::incoming_allowed_fast(int index) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== ALLOWED_FAST [ %d ]", index); +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_allowed_fast(index)) return; + } +#endif + if (is_disconnecting()) return; + if (index < 0) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("<== INVALID_ALLOWED_FAST [ %d ]", index); +#endif + return; + } + + if (t->valid_metadata()) + { + if (index >= int(m_have_piece.size())) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("<== INVALID_ALLOWED_FAST [ %d | s: %d ]" + , index, int(m_have_piece.size())); +#endif + return; + } + + // if we already have the piece, we can + // ignore this message + if (t->have_piece(index)) + return; + } + + // if we don't have the metadata, we'll verify + // this piece index later + m_allowed_fast.push_back(index); + + // if the peer has the piece and we want + // to download it, request it + if (int(m_have_piece.size()) > index + && m_have_piece[index] + && t->valid_metadata() + && t->has_picker() + && t->picker().piece_priority(index) > 0) + { + t->get_policy().peer_is_interesting(*this); + } + } + + std::vector const& peer_connection::allowed_fast() + { + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + // TODO: sort the allowed fast set in priority order + return m_allowed_fast; + } + + bool peer_connection::can_request_time_critical() const + { + if (has_peer_choked() || !is_interesting()) return false; + if ((int)m_download_queue.size() + (int)m_request_queue.size() + > m_desired_queue_size * 2) return false; + if (on_parole()) return false; + if (m_disconnecting) return false; + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + if (t->upload_mode()) return false; + + // ignore snubbed peers, since they're not likely to return pieces in a + // timely manner anyway + if (m_snubbed) return false; + return true; + } + + bool peer_connection::make_time_critical(piece_block const& block) + { + std::vector::iterator rit = std::find_if(m_request_queue.begin() + , m_request_queue.end(), has_block(block)); + if (rit == m_request_queue.end()) return false; +#if TORRENT_USE_ASSERTS + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + TORRENT_ASSERT(t->has_picker()); + TORRENT_ASSERT(t->picker().is_requested(block)); +#endif + // ignore it if it's already time critical + if (rit - m_request_queue.begin() < m_queued_time_critical) return false; + pending_block b = *rit; + m_request_queue.erase(rit); + m_request_queue.insert(m_request_queue.begin() + m_queued_time_critical, b); + ++m_queued_time_critical; + return true; + } + + bool peer_connection::add_request(piece_block const& block, int flags) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + TORRENT_ASSERT(!m_disconnecting); + TORRENT_ASSERT(t->valid_metadata()); + + TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); + TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); + TORRENT_ASSERT(int(block.piece_index) < t->torrent_file().num_pieces()); + TORRENT_ASSERT(int(block.block_index) < t->torrent_file().piece_size(block.piece_index)); + TORRENT_ASSERT(!t->picker().is_requested(block) || (t->picker().num_peers(block) > 0)); + TORRENT_ASSERT(!t->have_piece(block.piece_index)); + TORRENT_ASSERT(std::find_if(m_download_queue.begin(), m_download_queue.end() + , has_block(block)) == m_download_queue.end()); + TORRENT_ASSERT(std::find(m_request_queue.begin(), m_request_queue.end() + , block) == m_request_queue.end()); + + if (t->upload_mode()) return false; + if (m_disconnecting) return false; + + piece_picker::piece_state_t state; + peer_speed_t speed = peer_speed(); + char const* speedmsg = 0; + if (speed == fast) + { + speedmsg = "fast"; + state = piece_picker::fast; + } + else if (speed == medium) + { + speedmsg = "medium"; + state = piece_picker::medium; + } + else + { + speedmsg = "slow"; + state = piece_picker::slow; + } + + if ((flags & req_busy) && !(flags & req_time_critical)) + { + // this block is busy (i.e. it has been requested + // from another peer already). Only allow one busy + // request in the pipeline at the time + // this rule does not apply to time critical pieces, + // in which case we are allowed to pick more than one + // busy blocks + for (std::vector::const_iterator i = m_download_queue.begin() + , end(m_download_queue.end()); i != end; ++i) + { + if (i->busy) return false; + } + + for (std::vector::const_iterator i = m_request_queue.begin() + , end(m_request_queue.end()); i != end; ++i) + { + if (i->busy) return false; + } + } + + if (!t->picker().mark_as_downloading(block, peer_info_struct(), state)) + return false; + + if (t->alerts().should_post()) + { + t->alerts().post_alert(block_downloading_alert(t->get_handle(), + remote(), pid(), speedmsg, block.block_index, block.piece_index)); + } + + pending_block pb(block); + pb.busy = (flags & req_busy) ? true : false; + if (flags & req_time_critical) + { + m_request_queue.insert(m_request_queue.begin() + m_queued_time_critical + , pb); + ++m_queued_time_critical; + } + else + { + m_request_queue.push_back(pb); + } + return true; + } + + void peer_connection::cancel_all_requests() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + // this peer might be disconnecting + if (!t) return; + + TORRENT_ASSERT(t->valid_metadata()); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** CANCEL ALL REQUESTS"); +#endif + + while (!m_request_queue.empty()) + { + t->picker().abort_download(m_request_queue.back().block, peer_info_struct()); + m_request_queue.pop_back(); + } + m_queued_time_critical = 0; + + // make a local temporary copy of the download queue, since it + // may be modified when we call write_cancel (for peers that don't + // support the FAST extensions). + std::vector temp_copy = m_download_queue; + + for (std::vector::iterator i = temp_copy.begin() + , end(temp_copy.end()); i != end; ++i) + { + piece_block b = i->block; + + int block_offset = b.block_index * t->block_size(); + int block_size + = (std::min)(t->torrent_file().piece_size(b.piece_index)-block_offset, + t->block_size()); + TORRENT_ASSERT(block_size > 0); + TORRENT_ASSERT(block_size <= t->block_size()); + + // we can't cancel the piece if we've started receiving it + if (m_receiving_block == b) continue; + + peer_request r; + r.piece = b.piece_index; + r.start = block_offset; + r.length = block_size; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> CANCEL [ piece: %d s: %d l: %d b: %d ]" + , b.piece_index, block_offset, block_size, b.block_index); +#endif + write_cancel(r); + } + } + + void peer_connection::cancel_request(piece_block const& block, bool force) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + // this peer might be disconnecting + if (!t) return; + + TORRENT_ASSERT(t->valid_metadata()); + + TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); + TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); + TORRENT_ASSERT(int(block.piece_index) < t->torrent_file().num_pieces()); + TORRENT_ASSERT(int(block.block_index) < t->torrent_file().piece_size(block.piece_index)); + + // if all the peers that requested this block has been + // cancelled, then just ignore the cancel. + if (!t->picker().is_requested(block)) return; + + std::vector::iterator it + = std::find_if(m_download_queue.begin(), m_download_queue.end(), has_block(block)); + if (it == m_download_queue.end()) + { + std::vector::iterator rit = std::find_if(m_request_queue.begin() + , m_request_queue.end(), has_block(block)); + + // when a multi block is received, it is cancelled + // from all peers, so if this one hasn't requested + // the block, just ignore to cancel it. + if (rit == m_request_queue.end()) return; + + if (rit - m_request_queue.begin() < m_queued_time_critical) + --m_queued_time_critical; + + t->picker().abort_download(block, peer_info_struct()); + m_request_queue.erase(rit); + // since we found it in the request queue, it means it hasn't been + // sent yet, so we don't have to send a cancel. + return; + } + + int block_offset = block.block_index * t->block_size(); + int block_size + = (std::min)(t->torrent_file().piece_size(block.piece_index)-block_offset, + t->block_size()); + TORRENT_ASSERT(block_size > 0); + TORRENT_ASSERT(block_size <= t->block_size()); + + it->not_wanted = true; + + if (force) t->picker().abort_download(block, peer_info_struct()); + + if (m_outstanding_bytes < block_size) return; + + peer_request r; + r.piece = block.piece_index; + r.start = block_offset; + r.length = block_size; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> CANCEL [ piece: %d s: %d l: %d b: %d ]" + , block.piece_index, block_offset, block_size, block.block_index); +#endif + write_cancel(r); + } + + bool peer_connection::send_choke() + { + INVARIANT_CHECK; + + if (m_peer_info && m_peer_info->optimistically_unchoked) + m_peer_info->optimistically_unchoked = false; + + if (m_choked) return false; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> CHOKE"); +#endif + write_choke(); + m_choked = true; + + m_last_choke = time_now(); + m_num_invalid_requests = 0; + + // reject the requests we have in the queue + // except the allowed fast pieces + for (std::vector::iterator i = m_requests.begin(); + i != m_requests.end();) + { + if (std::find(m_accept_fast.begin(), m_accept_fast.end(), i->piece) + != m_accept_fast.end()) + { + ++i; + continue; + } + peer_request const& r = *i; +#ifdef TORRENT_STATS + ++m_ses.m_choked_piece_requests; +#endif +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> REJECT_PIECE [ piece: %d s: %d l: %d ] choking" + , r.piece , r.start , r.length); +#endif + write_reject_request(r); + i = m_requests.erase(i); + } + return true; + } + + bool peer_connection::send_unchoke() + { + INVARIANT_CHECK; + + if (!m_choked) return false; + boost::shared_ptr t = m_torrent.lock(); + if (!t->ready_for_connections()) return false; + + if (!m_sent_suggests) + { + std::vector ret; + t->get_suggested_pieces(ret); + for (std::vector::iterator i = ret.begin() + , end(ret.end()); i != end; ++i) + { + TORRENT_ASSERT(*i >= 0); + send_suggest(*i); + } + + m_sent_suggests = true; + } + + m_last_unchoke = time_now(); + write_unchoke(); + m_choked = false; + + m_uploaded_at_last_unchoke = m_statistics.total_payload_upload(); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> UNCHOKE"); +#endif + return true; + } + + void peer_connection::send_interested() + { + if (m_interesting) return; + boost::shared_ptr t = m_torrent.lock(); + if (!t->ready_for_connections()) return; + m_interesting = true; + write_interested(); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> INTERESTED"); +#endif + } + + void peer_connection::send_not_interested() + { + // we cannot disconnect in a constructor, and + // this function may end up doing that + TORRENT_ASSERT(m_in_constructor == false); + + if (!m_interesting) + { + disconnect_if_redundant(); + return; + } + + boost::shared_ptr t = m_torrent.lock(); + if (!t->ready_for_connections()) return; + m_interesting = false; + write_not_interested(); + + m_became_uninteresting = time_now(); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> NOT_INTERESTED"); +#endif + disconnect_if_redundant(); + } + + void peer_connection::send_suggest(int piece) + { + if (m_connecting) return; + if (in_handshake()) return; + + // don't suggest a piece that the peer already has + // don't suggest anything to a peer that isn't interested + if (has_piece(piece) + || !m_peer_interested) + return; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> SUGGEST [ %d ]", piece); +#endif + write_suggest(piece); + } + + void peer_connection::send_block_requests() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + if (m_disconnecting) return; + + if (t->graceful_pause() && m_outstanding_bytes == 0) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** GRACEFUL PAUSE [ NO MORE DOWNLOAD ]"); +#endif + disconnect(errors::torrent_paused); + return; + } + + // we can't download pieces in these states + if (t->state() == torrent_status::checking_files + || t->state() == torrent_status::checking_resume_data + || t->state() == torrent_status::downloading_metadata + || t->state() == torrent_status::allocating) + return; + + if ((int)m_download_queue.size() >= m_desired_queue_size + || t->upload_mode()) return; + + bool empty_download_queue = m_download_queue.empty(); + + while (!m_request_queue.empty() + && ((int)m_download_queue.size() < m_desired_queue_size + || m_queued_time_critical > 0)) + { + pending_block block = m_request_queue.front(); + + m_request_queue.erase(m_request_queue.begin()); + if (m_queued_time_critical) --m_queued_time_critical; + + // if we're a seed, we don't have a piece picker + // so we don't have to worry about invariants getting + // out of sync with it + if (!t->has_picker()) continue; + + // this can happen if a block times out, is re-requested and + // then arrives "unexpectedly" + if (t->picker().is_finished(block.block) + || t->picker().is_downloaded(block.block)) + { + t->picker().abort_download(block.block, peer_info_struct()); + continue; + } + + int block_offset = block.block.block_index * t->block_size(); + int block_size = (std::min)(t->torrent_file().piece_size( + block.block.piece_index) - block_offset, t->block_size()); + TORRENT_ASSERT(block_size > 0); + TORRENT_ASSERT(block_size <= t->block_size()); + + peer_request r; + r.piece = block.block.piece_index; + r.start = block_offset; + r.length = block_size; + + TORRENT_ASSERT(verify_piece(t->to_req(block.block))); + m_download_queue.push_back(block); + m_outstanding_bytes += block_size; +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif + +/* +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " *** REQUEST-QUEUE** [ " + "piece: " << block.piece_index << " | " + "block: " << block.block_index << " ]\n"; +#endif +*/ + // if we are requesting large blocks, merge the smaller + // blocks that are in the same piece into larger requests + if (m_request_large_blocks) + { + int blocks_per_piece = t->torrent_file().piece_length() / t->block_size(); + + while (!m_request_queue.empty()) + { + // check to see if this block is connected to the previous one + // if it is, merge them, otherwise, break this merge loop + pending_block const& front = m_request_queue.front(); + if (front.block.piece_index * blocks_per_piece + front.block.block_index + != block.block.piece_index * blocks_per_piece + block.block.block_index + 1) + break; + block = m_request_queue.front(); + m_request_queue.erase(m_request_queue.begin()); + TORRENT_ASSERT(verify_piece(t->to_req(block.block))); + m_download_queue.push_back(block); + if (m_queued_time_critical) --m_queued_time_critical; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** MERGING REQUEST [ piece: %d block: %d ]" + , block.block.piece_index, block.block.block_index); +#endif + + block_offset = block.block.block_index * t->block_size(); + block_size = (std::min)(t->torrent_file().piece_size( + block.block.piece_index) - block_offset, t->block_size()); + TORRENT_ASSERT(block_size > 0); + TORRENT_ASSERT(block_size <= t->block_size()); + + r.length += block_size; + m_outstanding_bytes += block_size; +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif + } + } + + // the verification will fail for coalesced blocks + TORRENT_ASSERT(verify_piece(r) || m_request_large_blocks); + +#ifndef TORRENT_DISABLE_EXTENSIONS + bool handled = false; + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((handled = (*i)->write_request(r))) break; + } + if (is_disconnecting()) return; + if (!handled) +#endif + { + write_request(r); + m_last_request = time_now(); + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> REQUEST [ piece: %d | s: %d | l: %d | ds: %d B/s | " + "dqs: %d rqs: %d blk: %s ]" + , r.piece, r.start, r.length, statistics().download_rate() + , int(m_desired_queue_size), int(m_download_queue.size()) + , m_request_large_blocks?"large":"single"); +#endif + } + m_last_piece = time_now(); + + if (!m_download_queue.empty() + && empty_download_queue) + { + // This means we just added a request to this connection + m_requested = time_now(); + } + } + + void peer_connection::on_timeout() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + connect_failed(errors::timed_out); + } + + void peer_connection::connect_failed(error_code const& e) + { + TORRENT_ASSERT(e); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("CONNECTION FAILED: %s", print_endpoint(m_remote).c_str()); +#endif +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + (*m_ses.m_logger) << time_now_string() << " CONNECTION FAILED: " << print_endpoint(m_remote) << "\n"; +#endif + +#ifdef TORRENT_STATS + ++m_ses.m_connect_timeouts; +#endif + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(!m_connecting || t); + if (m_connecting && t) + { + t->dec_num_connecting(); + m_connecting = false; + } + + if (m_connection_ticket != -1) + { + if (m_ses.m_half_open.done(m_connection_ticket)) + m_connection_ticket = -1; + } + + // a connection attempt using uTP just failed + // mark this peer as not supporting uTP + // we'll never try it again (unless we're trying holepunch) + if (is_utp(*m_socket) + && m_peer_info + && m_peer_info->supports_utp + && !m_holepunch_mode) + { + m_peer_info->supports_utp = false; + // reconnect immediately using TCP + policy::peer* pi = peer_info_struct(); + boost::shared_ptr t = m_torrent.lock(); + fast_reconnect(true); + disconnect(e, 0); + if (t && pi) t->connect_to_peer(pi, true); + return; + } + + if (m_holepunch_mode) + fast_reconnect(true); + +#ifndef TORRENT_DISABLE_EXTENSIONS + if ((!is_utp(*m_socket) + || !m_ses.m_settings.enable_outgoing_tcp) + && m_peer_info + && m_peer_info->supports_holepunch + && !m_holepunch_mode) + { + boost::shared_ptr t = m_torrent.lock(); + // see if we can try a holepunch + bt_peer_connection* p = t->find_introducer(remote()); + if (p) + p->write_holepunch_msg(bt_peer_connection::hp_rendezvous, remote(), 0); + } +#endif + + disconnect(e, 1); + return; + } + + // the error argument defaults to 0, which means deliberate disconnect + // 1 means unexpected disconnect/error + // 2 protocol error (client sent something invalid) + void peer_connection::disconnect(error_code const& ec, int error) + { +#if TORRENT_USE_ASSERTS + m_disconnect_started = true; +#endif + + if (m_disconnecting) return; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + switch (error) + { + case 0: + peer_log("*** CONNECTION CLOSED %s", ec.message().c_str()); + break; + case 1: + peer_log("*** CONNECTION FAILED %s", ec.message().c_str()); + break; + case 2: + peer_log("*** PEER ERROR %s", ec.message().c_str()); + break; + } +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + (*i)->on_disconnect(ec); + } +#endif + // for incoming connections, we get invalid argument errors + // when asking for the remote endpoint and the socket already + // closed, which is an edge case, but possible to happen when + // a peer makes a TCP and uTP connection in parallel. + // for outgoing connections however, why would we get this? + TORRENT_ASSERT(ec != error::invalid_argument || !m_outgoing); + +#ifdef TORRENT_STATS + ++m_ses.m_disconnected_peers; + if (error == 2) ++m_ses.m_error_peers; + if (ec == error::connection_reset) ++m_ses.m_connreset_peers; + else if (ec == error::eof) ++m_ses.m_eof_peers; + else if (ec == error::connection_refused) ++m_ses.m_connrefused_peers; + else if (ec == error::connection_aborted) ++m_ses.m_connaborted_peers; + else if (ec == error::no_permission) ++m_ses.m_perm_peers; + else if (ec == error::no_buffer_space) ++m_ses.m_buffer_peers; + else if (ec == error::host_unreachable) ++m_ses.m_unreachable_peers; + else if (ec == error::broken_pipe) ++m_ses.m_broken_pipe_peers; + else if (ec == error::address_in_use) ++m_ses.m_addrinuse_peers; + else if (ec == error::access_denied) ++m_ses.m_no_access_peers; + else if (ec == error::invalid_argument) ++m_ses.m_invalid_arg_peers; + else if (ec == error::operation_aborted) ++m_ses.m_aborted_peers; + else if (ec == error_code(errors::upload_upload_connection) + || ec == error_code(errors::uninteresting_upload_peer) + || ec == error_code(errors::torrent_aborted) + || ec == error_code(errors::self_connection) + || ec == error_code(errors::torrent_paused)) + ++m_ses.m_uninteresting_peers; + + if (ec == error_code(errors::timed_out) + || ec == error::timed_out) + ++m_ses.m_transport_timeout_peers; + + if (ec == error_code(errors::timed_out_inactivity) + || ec == error_code(errors::timed_out_no_request) + || ec == error_code(errors::timed_out_no_interest)) + ++m_ses.m_timeout_peers; + + if (ec == error_code(errors::no_memory)) + ++m_ses.m_no_memory_peers; + + if (ec == error_code(errors::too_many_connections)) + ++m_ses.m_too_many_peers; + + if (ec == error_code(errors::timed_out_no_handshake)) + ++m_ses.m_connect_timeouts; + + if (is_utp(*m_socket)) ++m_ses.m_error_utp_peers; + else ++m_ses.m_error_tcp_peers; + + if (m_outgoing) ++m_ses.m_error_outgoing_peers; + else ++m_ses.m_error_incoming_peers; + + +#ifndef TORRENT_DISABLE_ENCRYPTION + if (type() == bittorrent_connection) + { + bt_peer_connection* bt = static_cast(this); + if (bt->supports_encryption()) ++m_ses.m_error_encrypted_peers; + if (bt->rc4_encrypted() && bt->supports_encryption()) ++m_ses.m_error_rc4_peers; + } +#endif // TORRENT_DISABLE_ENCRYPTION +#endif + + // we cannot do this in a constructor + TORRENT_ASSERT(m_in_constructor == false); + if (error > 0) m_failed = true; + boost::intrusive_ptr me(this); + + INVARIANT_CHECK; + + if (m_channel_state[upload_channel] & peer_info::bw_disk) + { + m_ses.dec_disk_queue(upload_channel); + m_channel_state[upload_channel] &= ~peer_info::bw_disk; + } + if (m_channel_state[download_channel] & peer_info::bw_disk) + { + m_ses.dec_disk_queue(download_channel); + m_channel_state[download_channel] &= ~peer_info::bw_disk; + } + + boost::shared_ptr t = m_torrent.lock(); + if (m_connecting) + { + t->dec_num_connecting(); + m_connecting = false; + } + if (m_connection_ticket >= 0) + { + if (m_ses.m_half_open.done(m_connection_ticket)) + m_connection_ticket = -1; + } + + torrent_handle handle; + if (t) handle = t->get_handle(); + + if (ec == error::address_in_use + && m_ses.m_settings.outgoing_ports.first != 0) + { + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(performance_alert( + handle, performance_alert::too_few_outgoing_ports)); + } + + if (ec) + { + if ((error > 1 || ec.category() == get_socks_category()) + && m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + peer_error_alert(handle, remote(), pid(), ec)); + } + else if (error <= 1 && m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + peer_disconnected_alert(handle, remote(), pid(), ec)); + } + } + + if (t) + { + // make sure we keep all the stats! + if (!m_ignore_stats) + { + t->add_stats(statistics()); + + // report any partially received payload as redundant + boost::optional pbp = downloading_piece_progress(); + if (pbp + && pbp->bytes_downloaded > 0 + && pbp->bytes_downloaded < pbp->full_block_bytes) + { + t->add_redundant_bytes(pbp->bytes_downloaded, torrent::piece_closing); + } + } + + if (t->has_picker()) + { + piece_picker& picker = t->picker(); + + while (!m_download_queue.empty()) + { + pending_block& qe = m_download_queue.back(); + if (!qe.timed_out && !qe.not_wanted) + picker.abort_download(qe.block, peer_info_struct()); + m_outstanding_bytes -= t->to_req(qe.block).length; + if (m_outstanding_bytes < 0) m_outstanding_bytes = 0; + m_download_queue.pop_back(); + } + while (!m_request_queue.empty()) + { + picker.abort_download(m_request_queue.back().block, peer_info_struct()); + m_request_queue.pop_back(); + } + } + else + { + m_download_queue.clear(); + m_request_queue.clear(); + m_outstanding_bytes = 0; + } + m_queued_time_critical = 0; + +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif + t->remove_peer(this); + m_torrent.reset(); + } + else + { + TORRENT_ASSERT(m_download_queue.empty()); + TORRENT_ASSERT(m_request_queue.empty()); + } + +#if defined TORRENT_DEBUG && defined TORRENT_EXPENSIVE_INVARIANT_CHECKS + // since this connection doesn't have a torrent reference + // no torrent should have a reference to this connection either + for (aux::session_impl::torrent_map::const_iterator i = m_ses.m_torrents.begin() + , end(m_ses.m_torrents.end()); i != end; ++i) + TORRENT_ASSERT(!i->second->has_peer(this)); +#endif + + m_disconnecting = true; + error_code e; + + async_shutdown(*m_socket, m_socket); + + m_ses.close_connection(this, ec); + + // we should only disconnect while we still have + // at least one reference left to the connection + TORRENT_ASSERT(refcount() > 0); + } + + int peer_connection::get_upload_limit() const + { + return m_upload_limit; + } + + int peer_connection::get_download_limit() const + { + return m_download_limit; + } + + void peer_connection::set_upload_limit(int limit) + { + TORRENT_ASSERT(limit >= -1); + if (limit < 0) limit = 0; + if (limit < 10 && limit > 0) limit = 10; + m_upload_limit = limit; + m_bandwidth_channel[upload_channel].throttle(m_upload_limit); + } + + void peer_connection::set_download_limit(int limit) + { + TORRENT_ASSERT(limit >= -1); + if (limit < 0) limit = 0; + if (limit < 10 && limit > 0) limit = 10; + m_download_limit = limit; + m_bandwidth_channel[download_channel].throttle(m_download_limit); + } + + bool peer_connection::ignore_unchoke_slots() const + { + return m_ignore_unchoke_slots + || (m_ses.settings().ignore_limits_on_local_network + && on_local_network() + && m_ses.m_local_upload_channel.throttle() == 0); + } + + // defined in upnp.cpp + bool is_local(address const& a); + + bool peer_connection::on_local_network() const + { + if (libtorrent::is_local(m_remote.address()) + || is_loopback(m_remote.address())) return true; + return false; + } + + void peer_connection::get_peer_info(peer_info& p) const + { + TORRENT_ASSERT(!associated_torrent().expired()); + + ptime now = time_now(); + + p.download_rate_peak = m_download_rate_peak; + p.upload_rate_peak = m_upload_rate_peak; + p.rtt = m_rtt; + p.down_speed = statistics().download_rate(); + p.up_speed = statistics().upload_rate(); + p.payload_down_speed = statistics().download_payload_rate(); + p.payload_up_speed = statistics().upload_payload_rate(); + p.pid = pid(); + p.ip = remote(); + p.pending_disk_bytes = m_outstanding_writing_bytes; + p.send_quota = m_quota[upload_channel]; + p.receive_quota = m_quota[download_channel]; + p.num_pieces = m_num_pieces; + if (m_download_queue.empty()) p.request_timeout = -1; + else p.request_timeout = int(total_seconds(m_requested - now) + m_ses.settings().request_timeout + + m_timeout_extend); +#ifndef TORRENT_DISABLE_GEO_IP + p.inet_as_name = m_inet_as_name; +#endif + + p.download_queue_time = download_queue_time(); + p.queue_bytes = m_outstanding_bytes; + +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + p.country[0] = m_country[0]; + p.country[1] = m_country[1]; +#else + std::fill(p.country, p.country + 2, 0); +#endif + + p.total_download = statistics().total_payload_download(); + p.total_upload = statistics().total_payload_upload(); + + if (m_bandwidth_channel[upload_channel].throttle() == 0) + p.upload_limit = -1; + else + p.upload_limit = m_bandwidth_channel[upload_channel].throttle(); + + if (m_bandwidth_channel[download_channel].throttle() == 0) + p.download_limit = -1; + else + p.download_limit = m_bandwidth_channel[download_channel].throttle(); + + p.download_queue_length = int(download_queue().size() + m_request_queue.size()); + p.requests_in_buffer = int(m_requests_in_buffer.size() + m_request_queue.size()); + p.target_dl_queue_length = int(desired_queue_size()); + p.upload_queue_length = int(upload_queue().size()); + p.timed_out_requests = 0; + p.busy_requests = 0; + for (std::vector::const_iterator i = m_download_queue.begin() + , end(m_download_queue.end()); i != end; ++i) + { + if (i->timed_out) ++p.timed_out_requests; + if (i->busy) ++p.busy_requests; + } + + if (boost::optional ret = downloading_piece_progress()) + { + p.downloading_piece_index = ret->piece_index; + p.downloading_block_index = ret->block_index; + p.downloading_progress = ret->bytes_downloaded; + p.downloading_total = ret->full_block_bytes; + } + else + { + p.downloading_piece_index = -1; + p.downloading_block_index = -1; + p.downloading_progress = 0; + p.downloading_total = 0; + } + + p.pieces = get_bitfield(); + p.last_request = now - m_last_request; + p.last_active = now - (std::max)(m_last_sent, m_last_receive); + + // this will set the flags so that we can update them later + p.flags = 0; + get_specific_peer_info(p); + + p.flags |= is_seed() ? peer_info::seed : 0; + p.flags |= m_snubbed ? peer_info::snubbed : 0; + p.flags |= m_upload_only ? peer_info::upload_only : 0; + p.flags |= m_endgame_mode ? peer_info::endgame_mode : 0; + p.flags |= m_holepunch_mode ? peer_info::holepunched : 0; + if (peer_info_struct()) + { + policy::peer* pi = peer_info_struct(); + TORRENT_ASSERT(pi->in_use); + p.source = pi->source; + p.failcount = pi->failcount; + p.num_hashfails = pi->hashfails; + p.flags |= pi->on_parole ? peer_info::on_parole : 0; + p.flags |= pi->optimistically_unchoked ? peer_info::optimistic_unchoke : 0; +#ifndef TORRENT_DISABLE_GEO_IP + p.inet_as = pi->inet_as ? pi->inet_as->first : 0xffff; +#else + p.inet_as = 0xffff; +#endif + } + else + { + p.source = 0; + p.failcount = 0; + p.num_hashfails = 0; + p.inet_as = 0xffff; + } + + p.remote_dl_rate = m_remote_dl_rate; + p.send_buffer_size = m_send_buffer.capacity(); + p.used_send_buffer = m_send_buffer.size(); + p.receive_buffer_size = m_recv_buffer.capacity() + m_disk_recv_buffer_size; + p.used_receive_buffer = m_recv_pos; + p.write_state = m_channel_state[upload_channel]; + p.read_state = m_channel_state[download_channel]; + + // pieces may be empty if we don't have metadata yet + if (p.pieces.size() == 0) + { + p.progress = 0.f; + p.progress_ppm = 0; + } + else + { +#if TORRENT_NO_FPU + p.progress = 0.f; +#else + p.progress = (float)p.pieces.count() / (float)p.pieces.size(); +#endif + p.progress_ppm = boost::uint64_t(p.pieces.count()) * 1000000 / p.pieces.size(); + } + + p.estimated_reciprocation_rate = m_est_reciprocation_rate; + + error_code ec; + p.local_endpoint = get_socket()->local_endpoint(ec); + } + + // allocates a disk buffer of size 'disk_buffer_size' and replaces the + // end of the current receive buffer with it. i.e. the receive pos + // must be <= packet_size - disk_buffer_size + // the disk buffer can be accessed through release_disk_receive_buffer() + // when it is queried, the responsibility to free it is transferred + // to the caller + bool peer_connection::allocate_disk_receive_buffer(int disk_buffer_size) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_packet_size > 0); + TORRENT_ASSERT(m_recv_pos <= m_packet_size - disk_buffer_size); + TORRENT_ASSERT(!m_disk_recv_buffer); + TORRENT_ASSERT(disk_buffer_size <= 16 * 1024); + + if (disk_buffer_size == 0) return true; + + if (disk_buffer_size > 16 * 1024) + { + disconnect(errors::invalid_piece_size, 2); + return false; + } + + // first free the old buffer + m_disk_recv_buffer.reset(); + // then allocate a new one + + m_disk_recv_buffer.reset(m_ses.allocate_disk_buffer("receive buffer")); + if (!m_disk_recv_buffer) + { + disconnect(errors::no_memory); + return false; + } + m_disk_recv_buffer_size = disk_buffer_size; + return true; + } + + char* peer_connection::release_disk_receive_buffer() + { + m_disk_recv_buffer_size = 0; + return m_disk_recv_buffer.release(); + } + + // size = the packet size to remove from the receive buffer + // packet_size = the next packet size to receive in the buffer + void peer_connection::cut_receive_buffer(int size, int packet_size, int offset) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(packet_size > 0); + TORRENT_ASSERT(int(m_recv_buffer.size()) >= size); + TORRENT_ASSERT(int(m_recv_buffer.size()) >= m_recv_pos); + TORRENT_ASSERT(m_recv_pos >= size + offset); + TORRENT_ASSERT(offset >= 0); + TORRENT_ASSERT(size >= 0); + + if (size > 0) + { + if (m_recv_pos - size - offset > 0) + std::memmove(&m_recv_buffer[0] + offset, &m_recv_buffer[0] + offset + size, m_recv_pos - size - offset); + m_recv_pos -= size; + + // defensive. If this actually happens, we would have triggered + // an assert already (if asserts are enabled). + if (m_recv_pos < 0) m_recv_pos = 0; + } + +#ifdef TORRENT_DEBUG + std::fill(m_recv_buffer.begin() + m_recv_pos, m_recv_buffer.end(), 0); +#endif + + m_packet_size = packet_size; + } + + void peer_connection::superseed_piece(int replace_piece, int new_piece) + { + if (new_piece == -1) + { + if (m_superseed_piece[0] == -1) return; + m_superseed_piece[0] = -1; + m_superseed_piece[1] = -1; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** ending super seed mode"); +#endif + boost::shared_ptr t = m_torrent.lock(); + assert(t); + + for (int i = 0; i < int(m_have_piece.size()); ++i) + { + if (m_have_piece[i] || !t->have_piece(i)) continue; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> HAVE [ piece: %d] (ending super seed)", i); +#endif + write_have(i); + } + + return; + } + + assert(!has_piece(new_piece)); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> HAVE [ piece: %d ] (super seed)", new_piece); +#endif + write_have(new_piece); + + if (replace_piece >= 0) + { + // move the piece we're replacing to the tail + if (m_superseed_piece[0] == replace_piece) + std::swap(m_superseed_piece[0], m_superseed_piece[1]); + } + + m_superseed_piece[1] = m_superseed_piece[0]; + m_superseed_piece[0] = new_piece; + } + + void peer_connection::max_out_request_queue(int s) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** MAX OUT QUEUE SIZE [ %d -> %d ]" + , m_max_out_request_queue, s); +#endif + m_max_out_request_queue = s; + } + + int peer_connection::max_out_request_queue() const + { + return m_max_out_request_queue; + } + + void peer_connection::update_desired_queue_size() + { + if (m_snubbed) + { + m_desired_queue_size = 1; + return; + } + + int download_rate = statistics().download_payload_rate(); + + // calculate the desired download queue size + const int queue_time = m_ses.settings().request_queue_time; + // (if the latency is more than this, the download will stall) + // so, the queue size is queue_time * down_rate / 16 kiB + // (16 kB is the size of each request) + // the minimum number of requests is 2 and the maximum is 48 + // the block size doesn't have to be 16. So we first query the + // torrent for it + boost::shared_ptr t = m_torrent.lock(); + const int block_size = t->block_size(); + + TORRENT_ASSERT(block_size > 0); + + m_desired_queue_size = queue_time * download_rate / block_size; + + if (m_desired_queue_size > m_max_out_request_queue) + m_desired_queue_size = m_max_out_request_queue; + if (m_desired_queue_size < min_request_queue) + m_desired_queue_size = min_request_queue; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** UPDATE_QUEUE_SIZE [ dqs: %d max: %d dl: %d qt: %d snubbed: %d ]" + , m_desired_queue_size, m_max_out_request_queue + , download_rate, queue_time, int(m_snubbed)); +#endif + } + + void peer_connection::second_tick(int tick_interval_ms) + { + ptime now = time_now(); + boost::intrusive_ptr me(self()); + + // the invariant check must be run before me is destructed + // in case the peer got disconnected + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + + // drain the IP overhead from the bandwidth limiters + if (m_ses.m_settings.rate_limit_ip_overhead) + { + int download_overhead = m_statistics.download_ip_overhead(); + int upload_overhead = m_statistics.upload_ip_overhead(); + m_bandwidth_channel[download_channel].use_quota(download_overhead); + m_bandwidth_channel[upload_channel].use_quota(upload_overhead); + + bandwidth_channel* upc = 0; + bandwidth_channel* downc = 0; + if (m_ignore_bandwidth_limits) + { + upc = &m_ses.m_local_upload_channel; + downc = &m_ses.m_local_download_channel; + } + else + { + upc = &m_ses.m_upload_channel; + downc = &m_ses.m_download_channel; + } + + int up_limit = m_bandwidth_channel[upload_channel].throttle(); + int down_limit = m_bandwidth_channel[download_channel].throttle(); + + if (t) + { + if (!m_ignore_bandwidth_limits) + { + t->m_bandwidth_channel[download_channel].use_quota(download_overhead); + t->m_bandwidth_channel[upload_channel].use_quota(upload_overhead); + } + + if (down_limit > 0 + && download_overhead >= down_limit + && t->alerts().should_post()) + { + t->alerts().post_alert(performance_alert(t->get_handle() + , performance_alert::download_limit_too_low)); + } + + if (up_limit > 0 + && upload_overhead >= up_limit + && t->alerts().should_post()) + { + t->alerts().post_alert(performance_alert(t->get_handle() + , performance_alert::upload_limit_too_low)); + } + } + downc->use_quota(download_overhead); + upc->use_quota(upload_overhead); + } + + if (!t || m_disconnecting) + { + if (m_connection_ticket != -1) + { + if (m_ses.m_half_open.done(m_connection_ticket)) + m_connection_ticket = -1; + } + TORRENT_ASSERT(t || !m_connecting); + if (m_connecting && t) + { + t->dec_num_connecting(); + m_connecting = false; + } + disconnect(errors::torrent_aborted); + return; + } + + if (m_endgame_mode + && m_interesting + && m_download_queue.empty() + && m_request_queue.empty() + && total_seconds(now - m_last_request) >= 5) + { + // this happens when we're in strict end-game + // mode and the peer could not request any blocks + // because they were all taken but there were still + // unrequested blocks. Now, 5 seconds later, there + // might not be any unrequested blocks anymore, so + // we should try to pick another block to see + // if we can pick a busy one +#ifdef TORRENT_STATS + ++m_ses.m_end_game_piece_picks; +#endif + m_last_request = now; + request_a_block(*t, *this); + if (m_disconnecting) return; + send_block_requests(); + } + + if (t->super_seeding() + && !m_peer_interested + && m_became_uninterested + seconds(10) < now) + { + // maybe we need to try another piece, to see if the peer + // become interested in us then + superseed_piece(-1, t->get_piece_to_super_seed(m_have_piece)); + } + + on_tick(); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + (*i)->tick(); + } + if (is_disconnecting()) return; +#endif + + // if the peer hasn't said a thing for a certain + // time, it is considered to have timed out + time_duration d; + d = (std::min)(now - m_last_receive, now - m_last_sent); + + // if we can't read, it means we're blocked on the rate-limiter + // or the disk, not the peer itself. In this case, don't blame + // the peer and disconnect it + bool may_timeout = (m_channel_state[download_channel] & peer_info::bw_network) != 0; + + if (may_timeout && d > seconds(m_timeout) && !m_connecting + && can_disconnect(error_code(errors::timed_out_inactivity, get_libtorrent_category()))) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** LAST ACTIVITY [ %d seconds ago ] ***", int(total_seconds(d))); +#endif + disconnect(errors::timed_out_inactivity); + return; + } + + // do not stall waiting for a handshake + if (may_timeout + && !m_connecting + && in_handshake() + && d > seconds(m_ses.settings().handshake_timeout)) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** NO HANDSHAKE [ waited %d seconds ] ***", int(total_seconds(d))); +#endif + disconnect(errors::timed_out_no_handshake); + return; + } + + // disconnect peers that we unchoked, but + // they didn't send a request within 20 seconds. + // but only if we're a seed + d = now - (std::max)(m_last_unchoke, m_last_incoming_request); + if (may_timeout + && !m_connecting + && m_requests.empty() + && m_reading_bytes == 0 + && !m_choked + && m_peer_interested + && t && t->is_upload_only() + && d > seconds(20) + && can_disconnect(error_code(errors::timed_out_no_request, get_libtorrent_category()))) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** NO REQUEST [ waited %d seconds ] ***", int(total_seconds(d))); +#endif + disconnect(errors::timed_out_no_request); + return; + } + + // if the peer hasn't become interested and we haven't + // become interested in the peer for 10 minutes, it + // has also timed out. + time_duration d1; + time_duration d2; + d1 = now - m_became_uninterested; + d2 = now - m_became_uninteresting; + time_duration time_limit = seconds( + m_ses.settings().inactivity_timeout); + + // don't bother disconnect peers we haven't been interested + // in (and that hasn't been interested in us) for a while + // unless we have used up all our connection slots + if (may_timeout + && !m_interesting + && !m_peer_interested + && d1 > time_limit + && d2 > time_limit + && (m_ses.num_connections() >= m_ses.settings().connections_limit + || (t && t->num_peers() >= t->max_connections())) + && can_disconnect(error_code(errors::timed_out_no_interest, get_libtorrent_category()))) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** MUTUAL NO INTEREST [ t1: %d t2: %d ]" + , total_seconds(d1), total_seconds(d2)); +#endif + disconnect(errors::timed_out_no_interest); + return; + } + + if (may_timeout + && !m_download_queue.empty() + && m_quota[download_channel] > 0 + && now > m_requested + seconds(m_ses.settings().request_timeout + + m_timeout_extend)) + { + snub_peer(); + } + + // if we haven't sent something in too long, send a keep-alive + keep_alive(); + + m_ignore_bandwidth_limits = m_ses.settings().ignore_limits_on_local_network + && on_local_network(); + + m_statistics.second_tick(tick_interval_ms); + + if (m_statistics.upload_payload_rate() > m_upload_rate_peak) + { + m_upload_rate_peak = m_statistics.upload_payload_rate(); + } + if (m_statistics.download_payload_rate() > m_download_rate_peak) + { + m_download_rate_peak = m_statistics.download_payload_rate(); +#ifndef TORRENT_DISABLE_GEO_IP + if (peer_info_struct()) + { + std::pair* as_stats = peer_info_struct()->inet_as; + if (as_stats && as_stats->second < m_download_rate_peak) + as_stats->second = m_download_rate_peak; + } +#endif + } + if (is_disconnecting()) return; + + if (!t->ready_for_connections()) return; + + update_desired_queue_size(); + + if (m_desired_queue_size == m_max_out_request_queue + && t->alerts().should_post()) + { + t->alerts().post_alert(performance_alert(t->get_handle() + , performance_alert::outstanding_request_limit_reached)); + } + + int piece_timeout = m_ses.settings().piece_timeout; + int rate_limit = INT_MAX; + if (m_bandwidth_channel[download_channel].throttle() > 0) + rate_limit = (std::min)(m_bandwidth_channel[download_channel].throttle(), rate_limit); + if (t->bandwidth_throttle(download_channel) > 0) + rate_limit = (std::min)(t->bandwidth_throttle(download_channel) / t->num_peers(), rate_limit); + if (m_ses.m_download_channel.throttle() > 0) + rate_limit = (std::min)(m_ses.m_download_channel.throttle() + / m_ses.num_connections(), rate_limit); + + // rate_limit is an approximation of what this connection is + // allowed to download. If it is impossible to beat the piece + // timeout at this rate, adjust it to be realistic + + const int block_size = t->block_size(); + int rate_limit_timeout = rate_limit / block_size; + if (piece_timeout < rate_limit_timeout) piece_timeout = rate_limit_timeout; + + if (!m_download_queue.empty() + && m_quota[download_channel] > 0 + && now - m_last_piece > seconds(piece_timeout + m_timeout_extend)) + { + // this peer isn't sending the pieces we've + // requested (this has been observed by BitComet) + // in this case we'll clear our download queue and + // re-request the blocks. +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** PIECE_REQUEST TIMED OUT [ %d time: %d to: %d extend: %d ]" + , (int)m_download_queue.size(), total_seconds(now - m_last_piece) + , piece_timeout, m_timeout_extend); +#endif + + snub_peer(); + } + + // update once every minute + if (now - m_remote_dl_update >= seconds(60)) + { + boost::int64_t piece_size = t->torrent_file().piece_length(); + + if (m_remote_dl_rate > 0) + m_remote_dl_rate = int((m_remote_dl_rate * 2 / 3) + + ((boost::int64_t(m_remote_pieces_dled) * piece_size / 3) / 60)); + else + m_remote_dl_rate = int(boost::int64_t(m_remote_pieces_dled) + * piece_size / 60); + + m_remote_pieces_dled = 0; + m_remote_dl_update = now; + } + + fill_send_buffer(); + } + + void peer_connection::snub_peer() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + if (!m_snubbed) + { + m_snubbed = true; + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(peer_snubbed_alert(t->get_handle() + , m_remote, m_peer_id)); + } + } + m_desired_queue_size = 1; + + if (on_parole()) + { + m_timeout_extend += m_ses.settings().request_timeout; + return; + } + if (!t->has_picker()) return; + piece_picker& picker = t->picker(); + + // first, if we have any unsent requests, just + // wipe those out + while (!m_request_queue.empty()) + { + t->picker().abort_download(m_request_queue.back().block, peer_info_struct()); + m_request_queue.pop_back(); + } + m_queued_time_critical = 0; + + TORRENT_ASSERT(!m_download_queue.empty()); + + // request a new block before removing the previous + // one, in order to prevent it from + // picking the same block again, stalling the + // same piece indefinitely. + m_desired_queue_size = 2; +#ifdef TORRENT_STATS + ++m_ses.m_snubbed_piece_picks; +#endif + request_a_block(*t, *this); + + // the block we just picked (potentially) + // hasn't been put in m_download_queue yet. + // it's in m_request_queue and will be sent + // once send_block_requests() is called. + + m_desired_queue_size = 1; + + // time out the last request eligible + // block in the queue + int i = m_download_queue.size() - 1; + for (; i >= 0; --i) + { + if (!m_download_queue[i].timed_out + && !m_download_queue[i].not_wanted) + break; + } + + if (i >= 0) + { + pending_block& qe = m_download_queue[i]; + piece_block r = qe.block; + + // only time out a request if it blocks the piece + // from being completed (i.e. no free blocks to + // request from it) + piece_picker::downloading_piece p; + picker.piece_info(qe.block.piece_index, p); + int free_blocks = picker.blocks_in_piece(qe.block.piece_index) + - p.finished - p.writing - p.requested; + if (free_blocks > 0) + { + m_timeout_extend += m_ses.settings().request_timeout; + return; + } + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(block_timeout_alert(t->get_handle() + , remote(), pid(), qe.block.block_index, qe.block.piece_index)); + } + qe.timed_out = true; + picker.abort_download(r, peer_info_struct()); + } + + send_block_requests(); + } + + std::pair peer_connection::preferred_caching() const + { + int line_size = 0; + int expiry = 0; + if (m_ses.m_settings.guided_read_cache) + { + boost::shared_ptr t = m_torrent.lock(); + int upload_rate = m_statistics.upload_payload_rate(); + if (upload_rate == 0) upload_rate = 1; + + int num_uploads = m_ses.num_uploads(); + if (num_uploads == 0) num_uploads = 1; + + // assume half of the cache is write cache if we're downloading + // this torrent as well + int cache_size = m_ses.m_settings.cache_size / num_uploads; + if (!t->is_upload_only()) cache_size /= 2; + // cache_size is the amount of cache we have per peer. The + // cache line should not be greater than this + + // try to avoid locking caches for more than a couple of seconds + expiry = cache_size * 16 * 1024 / upload_rate; + if (expiry < 1) expiry = 1; + else if (expiry > 10) expiry = 10; + + line_size = cache_size; + } + return std::make_pair(line_size, expiry); + } + + void peer_connection::fill_send_buffer() + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + INVARIANT_CHECK; +#endif + + bool sent_a_piece = false; + boost::shared_ptr t = m_torrent.lock(); + if (!t || t->is_aborted()) return; + + // only add new piece-chunks if the send buffer is small enough + // otherwise there will be no end to how large it will be! + + boost::uint64_t upload_rate = int(m_statistics.upload_rate()); + + int buffer_size_watermark = int(upload_rate + * m_ses.settings().send_buffer_watermark_factor / 100); + + if (buffer_size_watermark < m_ses.settings().send_buffer_low_watermark) + { + buffer_size_watermark = m_ses.settings().send_buffer_low_watermark; + } + else if (buffer_size_watermark > m_ses.settings().send_buffer_watermark) + { + buffer_size_watermark = m_ses.settings().send_buffer_watermark; + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(">>> SEND_BUFFER_WATERMARK [ %d max: %d min: %d factor: %d ]" + , buffer_size_watermark, m_ses.settings().send_buffer_watermark + , m_ses.settings().send_buffer_low_watermark, m_ses.settings().send_buffer_watermark_factor); +#endif + + while (!m_requests.empty() + && (send_buffer_size() + m_reading_bytes < buffer_size_watermark)) + { + TORRENT_ASSERT(t->ready_for_connections()); + peer_request& r = m_requests.front(); + + TORRENT_ASSERT(r.piece >= 0); + TORRENT_ASSERT(r.piece < (int)m_have_piece.size()); + TORRENT_ASSERT(t->have_piece(r.piece)); + TORRENT_ASSERT(r.start + r.length <= t->torrent_file().piece_size(r.piece)); + TORRENT_ASSERT(r.length > 0 && r.start >= 0); + + std::pair cache = preferred_caching(); + + if (!t->seed_mode() || t->verified_piece(r.piece)) + { + t->filesystem().async_read(r, boost::bind(&peer_connection::on_disk_read_complete + , self(), _1, _2, r), cache.first, cache.second); + } + else + { + // this means we're in seed mode and we haven't yet + // verified this piece (r.piece) + t->filesystem().async_read_and_hash(r, boost::bind(&peer_connection::on_disk_read_complete + , self(), _1, _2, r), cache.second); + t->verified(r.piece); + } + + m_reading_bytes += r.length; + + m_requests.erase(m_requests.begin()); + sent_a_piece = true; + } + + if (t->share_mode() && sent_a_piece) + t->recalc_share_mode(); + } + + void peer_connection::on_disk_read_complete(int ret, disk_io_job const& j, peer_request r) + { + // flush send buffer at the end of this scope + // TODO: peers should really be corked/uncorked outside of + // all completed disk operations + cork _c(*this); + +#ifdef TORRENT_STATS + ++m_ses.m_num_messages[aux::session_impl::on_disk_read_counter]; +#endif + TORRENT_ASSERT(m_ses.is_network_thread()); + + m_reading_bytes -= r.length; + + disk_buffer_holder buffer(m_ses, j.buffer); +#if TORRENT_DISK_STATS + if (j.buffer) m_ses.m_disk_thread.rename_buffer(j.buffer, "received send buffer"); +#endif + + boost::shared_ptr t = m_torrent.lock(); + if (!t) + { + disconnect(j.error); + return; + } + + if (ret != r.length) + { + if (ret == -3) + { +#if defined TORRENT_VERBOSE_LOGGING + peer_log("==> REJECT_PIECE [ piece: %d s: %d l: %d ]" + , r.piece , r.start , r.length); +#endif + write_reject_request(r); + if (t->seed_mode()) t->leave_seed_mode(false); + } + else + { + // handle_disk_error may disconnect us + t->handle_disk_error(j, this); + } + return; + } + + if (t) + { + if (t->seed_mode() && t->all_verified()) + t->leave_seed_mode(true); + } + +#if defined TORRENT_VERBOSE_LOGGING + peer_log("==> PIECE [ piece: %d s: %d l: %d ]" + , r.piece, r.start, r.length); +#endif + +#if TORRENT_DISK_STATS + if (j.buffer) m_ses.m_disk_thread.rename_buffer(j.buffer, "dispatched send buffer"); +#endif + write_piece(r, buffer); + } + + void peer_connection::assign_bandwidth(int channel, int amount) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("%s ASSIGN BANDWIDHT [ bytes: %d ]" + , channel == upload_channel ? ">>>" : "<<<", amount); +#endif + + TORRENT_ASSERT(amount > 0 || is_disconnecting()); + m_quota[channel] += amount; + TORRENT_ASSERT(m_channel_state[channel] & peer_info::bw_limit); + m_channel_state[channel] &= ~peer_info::bw_limit; + +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif + + if (is_disconnecting()) return; + if (channel == upload_channel) + { + setup_send(); + } + else if (channel == download_channel) + { + setup_receive(); + } + } + + int peer_connection::request_upload_bandwidth( + bandwidth_channel* bwc1 + , bandwidth_channel* bwc2 + , bandwidth_channel* bwc3 + , bandwidth_channel* bwc4) + { + // we can only have one outstanding bandwidth request at a time + if (m_channel_state[upload_channel] & peer_info::bw_limit) return 0; + + int bytes = (std::max)(m_send_buffer.size() + , int(boost::int64_t(m_statistics.upload_rate()) * 2 + * m_ses.m_settings.tick_interval / 1000)); + + // we already have quota for the bytes we want to send + if (m_quota[upload_channel] >= bytes) return 0; + + // deduct the bytes we already have quota for + bytes -= m_quota[upload_channel]; + + shared_ptr t = m_torrent.lock(); + int priority; + if (t && m_ses.m_settings.choking_algorithm == session_settings::bittyrant_choker + && !t->upload_mode() && !t->is_upload_only()) + { + // when we use the bittyrant choker, the priority of a peer + // is decided based on the estimated reciprocation rate and + // the share it represents of the total upload rate capacity + // the torrent priority is taken into account when unchoking peers + int upload_capacity = m_ses.settings().upload_rate_limit; + if (upload_capacity == 0) + { + // we don't know at what rate we can upload. If we have a + // measurement of the peak, use that + 10kB/s, otherwise + // assume 20 kB/s + upload_capacity = (std::max)(20000, m_ses.m_peak_up_rate + 10000); + } + int estimated_reciprocation_rate = m_est_reciprocation_rate; + // we cannot send faster than our upload rate anyway + if (estimated_reciprocation_rate < upload_capacity) + estimated_reciprocation_rate = upload_capacity; + + priority = (boost::uint64_t(estimated_reciprocation_rate) << 14) / upload_capacity; + if (priority > 0xffff) priority = 0xffff; + } + else + { + priority = 1 + is_interesting() * 2 + m_requests_in_buffer.size(); + if (priority > 255) priority = 255; + priority += t ? t->priority() << 8 : 0; + } + TORRENT_ASSERT(priority <= 0xffff); + + // peers that we are not interested in are non-prioritized +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(">>> REQUEST_BANDWIDTH [ upload: %d prio: %d " + "channels: %p %p %p %p limits: %d %d %d %d ignore: %d ]" + , int(m_send_buffer.size()), priority + , bwc1, bwc2, bwc3, bwc4 + , (bwc1?bwc1->throttle():0) + , (bwc2?bwc2->throttle():0) + , (bwc3?bwc3->throttle():0) + , (bwc4?bwc4->throttle():0) + , m_ignore_bandwidth_limits); +#endif + + int ret = m_ses.m_upload_rate.request_bandwidth(self() + , bytes + , priority + , bwc1, bwc2, bwc3, bwc4); + + if (ret == 0) + { + m_channel_state[upload_channel] |= peer_info::bw_limit; + } + else + { + m_quota[upload_channel] += ret; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(">>> ASSIGN BANDWIDTH [ bytes: %d ]", ret); +#endif + } + return ret; + } + + int peer_connection::request_download_bandwidth( + bandwidth_channel* bwc1 + , bandwidth_channel* bwc2 + , bandwidth_channel* bwc3 + , bandwidth_channel* bwc4) + { + INVARIANT_CHECK; + // we can only have one outstanding bandwidth request at a time + if (m_channel_state[download_channel] & peer_info::bw_limit) return 0; + + int bytes = (std::max)((std::max)(m_outstanding_bytes, m_packet_size - m_recv_pos) + 30 + , int(boost::int64_t(m_statistics.download_rate()) * 2 + * m_ses.m_settings.tick_interval / 1000)); + + // we already have enough quota + if (m_quota[download_channel] >= bytes) return 0; + + // deduct the bytes we already have quota for + bytes -= m_quota[download_channel]; + + shared_ptr t = m_torrent.lock(); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<<< REQUEST_BANDWIDTH [ download: %d prio: %d " + "channels: %p %p %p %p limits: %d %d %d %d ignore: %d ]" + , int(m_download_queue.size() * 16 * 1024 + 30), m_priority + , bwc1, bwc2, bwc3, bwc4 + , (bwc1?bwc1->throttle():0) + , (bwc2?bwc2->throttle():0) + , (bwc3?bwc3->throttle():0) + , (bwc4?bwc4->throttle():0) + , m_ignore_bandwidth_limits); +#endif + + TORRENT_ASSERT(m_priority <= 255); + int priority = m_priority + (t ? (t->priority() << 8) : 0); + + TORRENT_ASSERT(m_outstanding_bytes >= 0); + TORRENT_ASSERT((m_channel_state[download_channel] & peer_info::bw_limit) == 0); + + int ret = m_ses.m_download_rate.request_bandwidth(self() + , bytes, priority, bwc1, bwc2, bwc3, bwc4); + if (ret == 0) + { + m_channel_state[download_channel] |= peer_info::bw_limit; + } + else + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<<< ASSIGN BANDWIDTH [ bytes: %d ]", ret); +#endif + m_quota[download_channel] += ret; + } + return ret; + } + + void peer_connection::uncork_socket() + { + if (!m_corked) return; + m_corked = false; + setup_send(); + } + + void peer_connection::setup_send() + { + if (m_disconnecting) return; + + shared_ptr t = m_torrent.lock(); + + // we may want to request more quota at this point + bool utp = m_socket->get() != 0; + bool ignore_limits = m_ignore_bandwidth_limits + || (!m_ses.m_settings.rate_limit_utp && utp); + if (!ignore_limits) + { + // in this case, we have data to send, but no + // bandwidth. So, we simply request bandwidth + // from the bandwidth manager + request_upload_bandwidth( + &m_ses.m_upload_channel + , t ? &t->m_bandwidth_channel[upload_channel] : 0 + , &m_bandwidth_channel[upload_channel] + , !utp ? &m_ses.m_tcp_upload_channel : 0); + } + else + { + // in this case, we're a local peer, and the settings + // are set to ignore rate limits for local peers. So, + // instead we rate limit ourself against the special + // global bandwidth channel for local peers, which defaults + // to unthrottled + request_upload_bandwidth(&m_ses.m_local_upload_channel + , &m_bandwidth_channel[upload_channel]); + } + + if (m_channel_state[upload_channel] & peer_info::bw_network) return; + + if (m_quota[upload_channel] == 0 + && !m_send_buffer.empty() + && !m_connecting) + { + return; + } + + int quota_left = m_quota[upload_channel]; + + if (m_send_buffer.empty() + && m_reading_bytes > 0 + && quota_left > 0) + { + if ((m_channel_state[upload_channel] & peer_info::bw_disk) == 0) + m_ses.inc_disk_queue(upload_channel); + m_channel_state[upload_channel] |= peer_info::bw_disk; + + if (!m_connecting + && !m_requests.empty() + && m_reading_bytes > m_ses.settings().send_buffer_watermark - 0x4000) + { + // we're stalled on the disk. We want to write and we can write + // but our send buffer is empty, waiting to be refilled from the disk + // this either means the disk is slower than the network connection + // or that our send buffer watermark is too small, because we can + // send it all before the disk gets back to us. That's why we only + // trigger this if we've also filled the allowed send buffer. The + // first request would not fill it all the way up because of the + // upload rate being virtually 0. If m_requests is empty, it doesn't + // matter anyway, because we don't have any more requests from the + // peer to hang on to the disk + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(performance_alert(t->get_handle() + , performance_alert::send_buffer_watermark_too_low)); + } + } + } + else + { + if (m_channel_state[upload_channel] & peer_info::bw_disk) + m_ses.dec_disk_queue(upload_channel); + m_channel_state[upload_channel] &= ~peer_info::bw_disk; + } + + if (!can_write()) + { +#ifdef TORRENT_VERBOSE_LOGGING + if (m_send_buffer.empty()) + { + peer_log(">>> SEND BUFFER DEPLETED [" + " quota: %d ignore: %s buf: %d connecting: %s disconnecting: %s pending_disk: %d ]" + , m_quota[upload_channel], m_ignore_bandwidth_limits?"yes":"no" + , int(m_send_buffer.size()), m_connecting?"yes":"no" + , m_disconnecting?"yes":"no", m_reading_bytes); + } + else + { + peer_log(">>> CANNOT WRITE [" + " quota: %d ignore: %s buf: %d connecting: %s disconnecting: %s pending_disk: %d ]" + , m_quota[upload_channel], m_ignore_bandwidth_limits?"yes":"no" + , int(m_send_buffer.size()), m_connecting?"yes":"no" + , m_disconnecting?"yes":"no", m_reading_bytes); + } +#endif + return; + } + + // send the actual buffer + int amount_to_send = m_send_buffer.size(); + if (amount_to_send > quota_left) + amount_to_send = quota_left; + + TORRENT_ASSERT(amount_to_send > 0); + + if (m_corked) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(">>> CORKED WRITE [ bytes: %d ]", amount_to_send); +#endif + return; + } + + TORRENT_ASSERT((m_channel_state[upload_channel] & peer_info::bw_network) == 0); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(">>> ASYNC_WRITE [ bytes: %d ]", amount_to_send); +#endif + std::list const& vec = m_send_buffer.build_iovec(amount_to_send); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("peer_connection::on_send_data"); +#endif + m_socket->async_write_some( + vec, make_write_handler(boost::bind( + &peer_connection::on_send_data, self(), _1, _2))); + + m_channel_state[upload_channel] |= peer_info::bw_network; + } + + void peer_connection::on_disk() + { + if ((m_channel_state[download_channel] & peer_info::bw_disk) == 0) return; + boost::intrusive_ptr me(this); + + m_ses.dec_disk_queue(download_channel); + m_channel_state[download_channel] &= ~peer_info::bw_disk; + setup_receive(read_async); + } + + void peer_connection::setup_receive(sync_t sync) + { + INVARIANT_CHECK; + + if (m_disconnecting) return; + + shared_ptr t = m_torrent.lock(); + + // we may want to request more quota at this point + bool utp = m_socket->get() != 0; + bool ignore_limits = m_ignore_bandwidth_limits + || (!m_ses.m_settings.rate_limit_utp && utp); + if (!ignore_limits) + { + // in this case, we have outstanding data to + // receive, but no bandwidth quota. So, we simply + // request bandwidth from the bandwidth manager + request_download_bandwidth( + &m_ses.m_download_channel + , t ? &t->m_bandwidth_channel[download_channel] : 0 + , &m_bandwidth_channel[download_channel] + , !utp ? &m_ses.m_tcp_download_channel : 0); + } + else + { + // in this case, we're a local peer, and the settings + // are set to ignore rate limits for local peers. So, + // instead we rate limit ourself against the special + // global bandwidth channel for local peers, which defaults + // to unthrottled + request_download_bandwidth(&m_ses.m_local_download_channel + , &m_bandwidth_channel[download_channel]); + } + + if (m_channel_state[download_channel] & peer_info::bw_network) return; + + if (m_quota[download_channel] == 0 + && !m_connecting) + { + return; + } + + if (!can_read(&m_channel_state[download_channel])) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<<< CANNOT READ [ quota: %d ignore: %s " + "can-write-to-disk: %s queue-limit: %d disconnecting: %s ]" + , m_quota[download_channel] + , (m_ignore_bandwidth_limits?"yes":"no") + , (m_ses.can_write_to_disk()?"yes":"no") + , m_ses.settings().max_queued_disk_bytes + , (m_disconnecting?"yes":"no")); +#endif + // if we block reading, waiting for the disk, we will wake up + // by the disk_io_thread posting a message every time it drops + // from being at or exceeding the limit down to below the limit + return; + } + error_code ec; + try_read(read_async, ec); + } + + size_t peer_connection::try_read(sync_t s, error_code& ec) + { + TORRENT_ASSERT(m_packet_size > 0); + int max_receive = m_packet_size - m_recv_pos; + TORRENT_ASSERT(max_receive >= 0); + + if (m_recv_pos >= m_soft_packet_size) m_soft_packet_size = 0; + if (m_soft_packet_size && max_receive > m_soft_packet_size - m_recv_pos) + max_receive = m_soft_packet_size - m_recv_pos; + int quota_left = m_quota[download_channel]; + if (max_receive > quota_left) + max_receive = quota_left; + + if (max_receive == 0) + { + ec = asio::error::would_block; + return 0; + } + + TORRENT_ASSERT(m_recv_pos >= 0); + TORRENT_ASSERT(m_packet_size > 0); + + if (!can_read()) + { + ec = asio::error::would_block; + return 0; + } + + int regular_buffer_size = m_packet_size - m_disk_recv_buffer_size; + + if (int(m_recv_buffer.size()) < regular_buffer_size) + m_recv_buffer.resize(round_up8(regular_buffer_size)); + + boost::array vec; + int num_bufs = 0; + if (!m_disk_recv_buffer || regular_buffer_size >= m_recv_pos + max_receive) + { + // only receive into regular buffer + TORRENT_ASSERT(m_recv_pos + max_receive <= int(m_recv_buffer.size())); + vec[0] = asio::buffer(&m_recv_buffer[m_recv_pos], max_receive); + num_bufs = 1; + } + else if (m_recv_pos >= regular_buffer_size) + { + // only receive into disk buffer + TORRENT_ASSERT(m_recv_pos - regular_buffer_size >= 0); + TORRENT_ASSERT(m_recv_pos - regular_buffer_size + max_receive <= m_disk_recv_buffer_size); + vec[0] = asio::buffer(m_disk_recv_buffer.get() + m_recv_pos - regular_buffer_size, max_receive); + num_bufs = 1; + } + else + { + // receive into both regular and disk buffer + TORRENT_ASSERT(max_receive + m_recv_pos > regular_buffer_size); + TORRENT_ASSERT(m_recv_pos < regular_buffer_size); + TORRENT_ASSERT(max_receive - regular_buffer_size + + m_recv_pos <= m_disk_recv_buffer_size); + + vec[0] = asio::buffer(&m_recv_buffer[m_recv_pos] + , regular_buffer_size - m_recv_pos); + vec[1] = asio::buffer(m_disk_recv_buffer.get() + , max_receive - regular_buffer_size + m_recv_pos); + num_bufs = 2; + } + + if (s == read_async) + { + TORRENT_ASSERT((m_channel_state[download_channel] & peer_info::bw_network) == 0); + m_channel_state[download_channel] |= peer_info::bw_network; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<<< ASYNC_READ [ max: %d bytes ]", max_receive); +#endif + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("peer_connection::on_receive_data"); +#endif + if (num_bufs == 1) + { + m_socket->async_read_some( + asio::mutable_buffers_1(vec[0]), make_read_handler( + boost::bind(&peer_connection::on_receive_data, self(), _1, _2))); + } + else + { + m_socket->async_read_some( + vec, make_read_handler( + boost::bind(&peer_connection::on_receive_data, self(), _1, _2))); + } + return 0; + } + + size_t ret = 0; + if (num_bufs == 1) + { + ret = m_socket->read_some(asio::mutable_buffers_1(vec[0]), ec); + } + else + { + ret = m_socket->read_some(vec, ec); + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<<< SYNC_READ [ max: %d ret: %d e: %s ]", max_receive, ret, ec ? ec.message().c_str() : ""); +#endif + return ret; + } + +#ifndef TORRENT_DISABLE_ENCRYPTION + + // returns the last 'bytes' from the receive buffer + std::pair peer_connection::wr_recv_buffers(int bytes) + { + TORRENT_ASSERT(bytes <= m_recv_pos); + + std::pair vec; + int regular_buffer_size = m_packet_size - m_disk_recv_buffer_size; + TORRENT_ASSERT(regular_buffer_size >= 0); + if (!m_disk_recv_buffer || regular_buffer_size >= m_recv_pos) + { + vec.first = buffer::interval(&m_recv_buffer[0] + + m_recv_pos - bytes, &m_recv_buffer[0] + m_recv_pos); + vec.second = buffer::interval(0,0); + } + else if (m_recv_pos - bytes >= regular_buffer_size) + { + vec.first = buffer::interval(m_disk_recv_buffer.get() + m_recv_pos + - regular_buffer_size - bytes, m_disk_recv_buffer.get() + m_recv_pos + - regular_buffer_size); + vec.second = buffer::interval(0,0); + } + else + { + TORRENT_ASSERT(m_recv_pos - bytes < regular_buffer_size); + TORRENT_ASSERT(m_recv_pos > regular_buffer_size); + vec.first = buffer::interval(&m_recv_buffer[0] + m_recv_pos - bytes + , &m_recv_buffer[0] + regular_buffer_size); + vec.second = buffer::interval(m_disk_recv_buffer.get() + , m_disk_recv_buffer.get() + m_recv_pos - regular_buffer_size); + } + TORRENT_ASSERT(vec.first.left() + vec.second.left() == bytes); + return vec; + } +#endif + + void peer_connection::reset_recv_buffer(int packet_size) + { + TORRENT_ASSERT(packet_size > 0); + if (m_recv_pos > m_packet_size) + { + cut_receive_buffer(m_packet_size, packet_size); + return; + } + m_recv_pos = 0; + m_packet_size = packet_size; + } + + void nop(char*) {} + + void peer_connection::append_const_send_buffer(char const* buffer, int size) + { + m_send_buffer.append_buffer((char*)buffer, size, size, &nop); +#if defined TORRENT_STATS && defined TORRENT_DISK_STATS + m_ses.m_buffer_usage_logger << log_time() << " append_const_send_buffer: " << size << std::endl; + m_ses.log_buffer_usage(); +#endif + } + + void peer_connection::send_buffer(char const* buf, int size, int flags + , void (*fun)(char*, int, void*), void* userdata) + { + if (flags == message_type_request) + m_requests_in_buffer.push_back(m_send_buffer.size() + size); + + int free_space = m_send_buffer.space_in_last_buffer(); + if (free_space > size) free_space = size; + if (free_space > 0) + { + char* dst = m_send_buffer.append(buf, free_space); + TORRENT_ASSERT(dst != 0); + if (fun) fun(dst, free_space, userdata); + size -= free_space; + buf += free_space; +#if defined TORRENT_STATS && defined TORRENT_DISK_STATS + m_ses.m_buffer_usage_logger << log_time() << " send_buffer: " + << free_space << std::endl; + m_ses.log_buffer_usage(); +#endif + } + if (size <= 0) return; + +#if defined TORRENT_STATS && defined TORRENT_DISK_STATS + m_ses.m_buffer_usage_logger << log_time() << " send_buffer_alloc: " << size << std::endl; + m_ses.log_buffer_usage(); +#endif + int i = 0; + while (size > 0) + { + char* chain_buf = m_ses.allocate_buffer(); + if (chain_buf == 0) + { + disconnect(errors::no_memory); + return; + } + + int buf_size = (std::min)(int(aux::session_impl::send_buffer_size), size); + memcpy(chain_buf, buf, buf_size); + if (fun) fun(chain_buf, buf_size, userdata); + buf += buf_size; + size -= buf_size; + m_send_buffer.append_buffer(chain_buf, aux::session_impl::send_buffer_size, buf_size + , boost::bind(&session_impl::free_buffer, boost::ref(m_ses), _1)); + ++i; + } + setup_send(); + } + + template + struct set_to_zero + { + set_to_zero(T& v, bool cond): m_val(v), m_cond(cond) {} + void fire() { if (!m_cond) return; m_cond = false; m_val = 0; } + ~set_to_zero() { if (m_cond) m_val = 0; } + private: + T& m_val; + bool m_cond; + }; + + // -------------------------- + // RECEIVE DATA + // -------------------------- + + void peer_connection::on_receive_data(const error_code& error + , std::size_t bytes_transferred) + { +#ifdef TORRENT_STATS + ++m_ses.m_num_messages[aux::session_impl::on_read_counter]; + int size = 8; + int index = 0; + while (bytes_transferred > size + 13) { size <<= 1; ++index; } + int num_max = sizeof(m_ses.m_recv_buffer_sizes)/sizeof(m_ses.m_recv_buffer_sizes[0]); + if (index >= num_max) index = num_max - 1; + ++m_ses.m_recv_buffer_sizes[index]; +#endif + TORRENT_ASSERT(m_ses.is_network_thread()); + + // keep ourselves alive in until this function exits in + // case we disconnect + // this needs to be created before the invariant check, + // to keep the object alive through the exit check + boost::intrusive_ptr me(self()); + + // flush the send buffer at the end of this function + cork _c(*this); + + INVARIANT_CHECK; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<<< ON_RECEIVE_DATA [ bytes: %d error: %s ]" + , bytes_transferred, error.message().c_str()); +#endif +#if defined TORRENT_ASIO_DEBUGGING + complete_async("peer_connection::on_receive_data"); +#endif + + // leave this bit set until we're done looping, reading from the socket. + // that way we don't trigger any async read calls until the end of this + // function. + TORRENT_ASSERT(m_channel_state[download_channel] & peer_info::bw_network); + + int bytes_in_loop = bytes_transferred; + + if (m_extension_outstanding_bytes > 0) + m_extension_outstanding_bytes -= (std::min)(m_extension_outstanding_bytes, int(bytes_transferred)); + + if (error) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** ERROR [ in peer_connection::on_receive_data error: %s ]" + , error.message().c_str()); +#endif + m_statistics.trancieve_ip_packet(bytes_in_loop, m_remote.address().is_v6()); + on_receive(error, bytes_transferred); + disconnect(error); + return; + } + + int num_loops = 0; + do + { + TORRENT_ASSERT(int(m_recv_pos + bytes_transferred) <= m_packet_size); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<<< read %d bytes", int(bytes_transferred)); +#endif + // correct the dl quota usage, if not all of the buffer was actually read + TORRENT_ASSERT(int(bytes_transferred) <= m_quota[download_channel]); + m_quota[download_channel] -= bytes_transferred; + + if (m_disconnecting) + { + m_statistics.trancieve_ip_packet(bytes_in_loop, m_remote.address().is_v6()); + return; + } + + TORRENT_ASSERT(m_packet_size > 0); + TORRENT_ASSERT(bytes_transferred > 0); + + m_last_receive = time_now(); + m_recv_pos += bytes_transferred; + TORRENT_ASSERT(m_recv_pos <= int(m_recv_buffer.size() + + m_disk_recv_buffer_size)); + +#if TORRENT_USE_ASSERTS + size_type cur_payload_dl = m_statistics.last_payload_downloaded(); + size_type cur_protocol_dl = m_statistics.last_protocol_downloaded(); +#endif + { + INVARIANT_CHECK; + on_receive(error, bytes_transferred); + } +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_statistics.last_payload_downloaded() - cur_payload_dl >= 0); + TORRENT_ASSERT(m_statistics.last_protocol_downloaded() - cur_protocol_dl >= 0); + size_type stats_diff = m_statistics.last_payload_downloaded() - cur_payload_dl + + m_statistics.last_protocol_downloaded() - cur_protocol_dl; + TORRENT_ASSERT(stats_diff == int(bytes_transferred)); +#endif + if (m_disconnecting) return; + + TORRENT_ASSERT(m_packet_size > 0); + + if (m_peer_choked + && m_recv_pos == 0 + && (m_recv_buffer.capacity() - m_packet_size) > 128) + { + // round up to an even 8 bytes since that's the RC4 blocksize + buffer(round_up8(m_packet_size)).swap(m_recv_buffer); + } + + if (m_recv_pos >= m_soft_packet_size) m_soft_packet_size = 0; + + if (num_loops > 20) break; + + error_code ec; + bytes_transferred = try_read(read_sync, ec); + TORRENT_ASSERT(bytes_transferred > 0 || ec); + if (ec && ec != asio::error::would_block) + { + m_statistics.trancieve_ip_packet(bytes_in_loop, m_remote.address().is_v6()); + disconnect(ec); + return; + } + if (ec == asio::error::would_block) break; + bytes_in_loop += bytes_transferred; + ++num_loops; + } + while (bytes_transferred > 0); + + if (is_seed()) + { + boost::shared_ptr t = m_torrent.lock(); + if (t) t->seen_complete(); + } + + m_statistics.trancieve_ip_packet(bytes_in_loop, m_remote.address().is_v6()); + + // allow reading from the socket again + TORRENT_ASSERT(m_channel_state[download_channel] & peer_info::bw_network); + m_channel_state[download_channel] &= ~peer_info::bw_network; + + setup_receive(read_async); + } + + bool peer_connection::can_write() const + { + // if we have requests or pending data to be sent or announcements to be made + // we want to send data + return !m_send_buffer.empty() + && m_quota[upload_channel] > 0 + && !m_connecting; + } + + bool peer_connection::can_read(boost::uint8_t* state) const + { + boost::shared_ptr t = m_torrent.lock(); + + bool bw_limit = m_quota[download_channel] > 0; + + if (!bw_limit) return false; + + bool disk = m_ses.settings().max_queued_disk_bytes == 0 + || m_ses.can_write_to_disk() + // don't block this peer because of disk saturation + // if we're not downloading any pieces from it + || m_outstanding_bytes == 0; + + if (!disk) + { + if (state) + { + if ((*state & peer_info::bw_disk) == 0) + m_ses.inc_disk_queue(download_channel); + *state |= peer_info::bw_disk; + } + return false; + } + + return !m_connecting && !m_disconnecting; + } + + void peer_connection::on_connect(int ticket) + { + TORRENT_ASSERT(m_ses.is_network_thread()); +#if TORRENT_USE_ASSERTS + // in case we disconnect here, we need to + // keep the connection alive until the + // exit invariant check is run + boost::intrusive_ptr me(self()); +#endif + INVARIANT_CHECK; + + error_code ec; +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + (*m_ses.m_logger) << time_now_string() << " ON_CONNECT: " << print_endpoint(m_remote) << "\n"; +#endif + + if (ticket == -1) + { + disconnect(asio::error::operation_aborted); + return; + } + + m_connection_ticket = ticket; + boost::shared_ptr t = m_torrent.lock(); + + m_queued = false; + + if (!t) + { + TORRENT_ASSERT(!m_connecting); + disconnect(errors::torrent_aborted); + return; + } + + TORRENT_ASSERT(m_connecting); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log(">>> OPEN [ protocol: %s ]", (m_remote.address().is_v4()?"IPv4":"IPv6")); +#endif + m_socket->open(m_remote.protocol(), ec); + if (ec) + { + disconnect(ec); + return; + } + + tcp::endpoint bind_interface = t->get_interface(); + + std::pair const& out_ports = m_ses.settings().outgoing_ports; + if (out_ports.first > 0 && out_ports.second >= out_ports.first) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log(">>> SET_REUSE_ADDRESS"); +#endif + m_socket->set_option(socket_acceptor::reuse_address(true), ec); + // ignore errors because the underlying socket may not + // be opened yet. This happens when we're routing through + // a proxy. In that case, we don't yet know the address of + // the proxy server, and more importantly, we don't know + // the address family of its address. This means we can't + // open the socket yet. The socks abstraction layer defers + // opening it. + ec.clear(); + bind_interface.port(m_ses.next_port()); + } + + // if we're not binding to a specific interface, bind + // to the same protocol family as the target endpoint + if (is_any(bind_interface.address())) + { +#if TORRENT_USE_IPV6 + if (m_remote.address().is_v6()) + bind_interface.address(address_v6::any()); + else +#endif + bind_interface.address(address_v4::any()); + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log(">>> BIND [ ep: %s ]", print_endpoint(bind_interface).c_str()); +#endif + m_socket->bind(bind_interface, ec); + if (ec) + { + disconnect(ec); + return; + } +#if defined TORRENT_VERBOSE_LOGGING + peer_log(">>> ASYNC_CONNECT [ dst: %s ]", print_endpoint(m_remote).c_str()); +#endif +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("peer_connection::on_connection_complete"); +#endif + m_socket->async_connect(m_remote + , boost::bind(&peer_connection::on_connection_complete, self(), _1)); + m_connect = time_now_hires(); + m_statistics.sent_syn(m_remote.address().is_v6()); + + if (t->alerts().should_post()) + { + t->alerts().post_alert(peer_connect_alert( + t->get_handle(), remote(), pid(), m_socket->type())); + } +#if defined TORRENT_VERBOSE_LOGGING + peer_log("*** LOCAL ENDPOINT[ e: %s ]", print_endpoint(m_socket->local_endpoint(ec)).c_str()); +#endif + } + + void peer_connection::on_connection_complete(error_code const& e) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("peer_connection::on_connection_complete"); +#endif + ptime completed = time_now_hires(); + + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + + m_rtt = boost::uint16_t(total_milliseconds(completed - m_connect)); + +#ifdef TORRENT_USE_OPENSSL + // add this RTT to the PRNG seed, to add more unpredictability + boost::uint64_t now = total_microseconds(completed - m_connect); + // assume 12 bits of entropy (i.e. about 8 milliseconds) + RAND_add(&now, 8, 1.5); +#endif + + if (m_disconnecting) return; + + error_code ec; + if (e) + { + connect_failed(e); + return; + } + + // if t is NULL, we better not be connecting, since + // we can't decrement the connecting counter + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t || !m_connecting); + if (m_connecting && t) + { + t->dec_num_connecting(); + m_connecting = false; + } + if (m_connection_ticket != -1) + { + if (m_ses.m_half_open.done(m_connection_ticket)) + m_connection_ticket = -1; + } + + if (m_disconnecting) return; + m_last_receive = time_now(); + + if (is_utp(*m_socket) && m_peer_info) + { + m_peer_info->confirmed_supports_utp = true; + m_peer_info->supports_utp = false; + } + + // this means the connection just succeeded + + m_statistics.received_synack(m_remote.address().is_v6()); + + TORRENT_ASSERT(m_socket); +#if defined TORRENT_VERBOSE_LOGGING + peer_log(">>> COMPLETED [ ep: %s rtt: %d ]", print_endpoint(m_remote).c_str(), m_rtt); +#endif + + // set the socket to non-blocking, so that we can + // read the entire buffer on each read event we get + tcp::socket::non_blocking_io ioc(true); +#if defined TORRENT_VERBOSE_LOGGING + peer_log("*** SET NON-BLOCKING"); +#endif + m_socket->io_control(ioc, ec); + if (ec) + { + disconnect(ec); + return; + } + + if (m_remote == m_socket->local_endpoint(ec)) + { + // if the remote endpoint is the same as the local endpoint, we're connected + // to ourselves + if (m_peer_info && t) t->get_policy().ban_peer(m_peer_info); + disconnect(errors::self_connection, 1); + return; + } + + if (m_remote.address().is_v4() && m_ses.settings().peer_tos != 0) + { + error_code ec; + m_socket->set_option(type_of_service(m_ses.settings().peer_tos), ec); +#if defined TORRENT_VERBOSE_LOGGING + peer_log(">>> SET_TOS[ tos: %d e: %s ]", m_ses.settings().peer_tos, ec.message().c_str()); +#endif + } +#if TORRENT_USE_IPV6 && defined IPV6_TCLASS + else if (m_remote.address().is_v6() && m_ses.settings().peer_tos != 0) + { + m_socket->set_option(traffic_class(m_ses.settings().peer_tos), ec); + } +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + (*i)->on_connected(); + } +#endif + + on_connected(); + setup_send(); + setup_receive(); + } + + // -------------------------- + // SEND DATA + // -------------------------- + + void peer_connection::on_send_data(error_code const& error + , std::size_t bytes_transferred) + { +#ifdef TORRENT_STATS + ++m_ses.m_num_messages[aux::session_impl::on_write_counter]; + int size = 8; + int index = 0; + while (bytes_transferred > size + 13) { size <<= 1; ++index; } + int num_max = sizeof(m_ses.m_send_buffer_sizes)/sizeof(m_ses.m_send_buffer_sizes[0]); + if (index >= num_max) index = num_max - 1; + ++m_ses.m_send_buffer_sizes[index]; +#endif + TORRENT_ASSERT(m_ses.is_network_thread()); + +#if defined TORRENT_VERBOSE_LOGGING + peer_log("*** ON_SEND_DATA [ bytes: %d error: %s ]" + , int(bytes_transferred), error.message().c_str()); +#endif + + INVARIANT_CHECK; + +#if defined TORRENT_ASIO_DEBUGGING + complete_async("peer_connection::on_send_data"); +#endif + // keep ourselves alive in until this function exits in + // case we disconnect + boost::intrusive_ptr me(self()); + + TORRENT_ASSERT(m_channel_state[upload_channel] & peer_info::bw_network); + + m_send_buffer.pop_front(bytes_transferred); + + for (std::vector::iterator i = m_requests_in_buffer.begin() + , end(m_requests_in_buffer.end()); i != end; ++i) + *i -= bytes_transferred; + + while (!m_requests_in_buffer.empty() + && m_requests_in_buffer.front() <= 0) + m_requests_in_buffer.erase(m_requests_in_buffer.begin()); + + m_channel_state[upload_channel] &= ~peer_info::bw_network; + + TORRENT_ASSERT(int(bytes_transferred) <= m_quota[upload_channel]); + m_quota[upload_channel] -= bytes_transferred; + + m_statistics.trancieve_ip_packet(bytes_transferred, m_remote.address().is_v6()); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(">>> wrote %d bytes", int(bytes_transferred)); +#endif + + if (error) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("**ERROR**: %s [in peer_connection::on_send_data]", error.message().c_str()); +#endif + disconnect(error); + return; + } + if (m_disconnecting) return; + + TORRENT_ASSERT(!m_connecting); + TORRENT_ASSERT(bytes_transferred > 0); + + m_last_sent = time_now(); + +#if TORRENT_USE_ASSERTS + size_type cur_payload_ul = m_statistics.last_payload_uploaded(); + size_type cur_protocol_ul = m_statistics.last_protocol_uploaded(); +#endif + on_sent(error, bytes_transferred); +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_statistics.last_payload_uploaded() - cur_payload_ul >= 0); + TORRENT_ASSERT(m_statistics.last_protocol_uploaded() - cur_protocol_ul >= 0); + size_type stats_diff = m_statistics.last_payload_uploaded() - cur_payload_ul + + m_statistics.last_protocol_uploaded() - cur_protocol_ul; + TORRENT_ASSERT(stats_diff == int(bytes_transferred)); +#endif + + fill_send_buffer(); + + setup_send(); + } + +#if TORRENT_USE_INVARIANT_CHECKS + struct peer_count_t + { + peer_count_t(): num_peers(0), num_peers_with_timeouts(0), num_peers_with_nowant(0), num_not_requested(0) {} + int num_peers; + int num_peers_with_timeouts; + int num_peers_with_nowant; + int num_not_requested; +// std::vector peers; + }; + + void peer_connection::check_invariant() const + { + TORRENT_ASSERT(m_in_use == 1337); + TORRENT_ASSERT(m_queued_time_critical <= int(m_request_queue.size())); + TORRENT_ASSERT(m_accept_fast.size() == m_accept_fast_piece_cnt.size()); + + TORRENT_ASSERT(bool(m_disk_recv_buffer) == (m_disk_recv_buffer_size > 0)); + + TORRENT_ASSERT(m_upload_limit >= 0); + TORRENT_ASSERT(m_download_limit >= 0); + + // It's not obvious why this invariant breaks when the peer disconnects + if (!m_disconnecting) + { + if (m_channel_state[upload_channel] & peer_info::bw_limit) + TORRENT_ASSERT(m_ses.m_upload_rate.is_queued(this)); + if (m_channel_state[download_channel] & peer_info::bw_limit) + TORRENT_ASSERT(m_ses.m_download_rate.is_queued(this)); + } + + boost::shared_ptr t = m_torrent.lock(); + + if (!m_disconnect_started && m_initialized) + { + // none of this matters if we're disconnecting anyway + if (t->is_finished()) + TORRENT_ASSERT(!is_interesting()); + if (is_seed()) + TORRENT_ASSERT(upload_only()); + } + + if (m_disconnecting) + { + TORRENT_ASSERT(m_download_queue.empty()); + TORRENT_ASSERT(m_request_queue.empty()); + TORRENT_ASSERT(!t); + TORRENT_ASSERT(m_disconnect_started); + } + else if (!m_in_constructor) + { + TORRENT_ASSERT(m_ses.has_peer((peer_connection*)this)); + } + + TORRENT_ASSERT(m_outstanding_bytes >= 0); + if (t && t->valid_metadata() && !m_disconnecting) + { + torrent_info const& ti = t->torrent_file(); + // if the piece is fully downloaded, we might have popped it from the + // download queue already + int outstanding_bytes = 0; +// bool in_download_queue = false; + int block_size = t->block_size(); + piece_block last_block(ti.num_pieces()-1 + , (ti.piece_size(ti.num_pieces()-1) + block_size - 1) / block_size); + for (std::vector::const_iterator i = m_download_queue.begin() + , end(m_download_queue.end()); i != end; ++i) + { + TORRENT_ASSERT(i->block.piece_index <= last_block.piece_index); + TORRENT_ASSERT(i->block.piece_index < last_block.piece_index + || i->block.block_index <= last_block.block_index); + if (m_received_in_piece && i == m_download_queue.begin()) + { +// in_download_queue = true; + // this assert is not correct since block may have different sizes + // and may not be returned in the order they were requested +// TORRENT_ASSERT(t->to_req(i->block).length >= m_received_in_piece); + outstanding_bytes += t->to_req(i->block).length - m_received_in_piece; + } + else + { + outstanding_bytes += t->to_req(i->block).length; + } + } + //if (p && p->bytes_downloaded < p->full_block_bytes) TORRENT_ASSERT(in_download_queue); + + TORRENT_ASSERT(m_outstanding_bytes == outstanding_bytes); + } + + std::set unique; + std::transform(m_download_queue.begin(), m_download_queue.end() + , std::inserter(unique, unique.begin()), boost::bind(&pending_block::block, _1)); + std::transform(m_request_queue.begin(), m_request_queue.end() + , std::inserter(unique, unique.begin()), boost::bind(&pending_block::block, _1)); + TORRENT_ASSERT(unique.size() == m_download_queue.size() + m_request_queue.size()); + if (m_peer_info) + { + TORRENT_ASSERT(m_peer_info->prev_amount_upload == 0); + TORRENT_ASSERT(m_peer_info->prev_amount_download == 0); + TORRENT_ASSERT(m_peer_info->connection == this + || m_peer_info->connection == 0); + + if (m_peer_info->optimistically_unchoked) + TORRENT_ASSERT(!is_choked()); + } + + TORRENT_ASSERT(m_have_piece.count() == m_num_pieces); + + if (!t) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + // since this connection doesn't have a torrent reference + // no torrent should have a reference to this connection either + for (aux::session_impl::torrent_map::const_iterator i = m_ses.m_torrents.begin() + , end(m_ses.m_torrents.end()); i != end; ++i) + TORRENT_ASSERT(!i->second->has_peer((peer_connection*)this)); +#endif + return; + } + + if (t->ready_for_connections() && m_initialized) + TORRENT_ASSERT(t->torrent_file().num_pieces() == int(m_have_piece.size())); + + // in share mode we don't close redundant connections + if (m_ses.settings().close_redundant_connections && !t->share_mode()) + { + bool ok_to_disconnect = + can_disconnect(error_code(errors::upload_upload_connection, get_libtorrent_category())) + || can_disconnect(error_code(errors::uninteresting_upload_peer, get_libtorrent_category())) + || can_disconnect(error_code(errors::too_many_requests_when_choked, get_libtorrent_category())) + || can_disconnect(error_code(errors::timed_out_no_interest, get_libtorrent_category())) + || can_disconnect(error_code(errors::timed_out_no_request, get_libtorrent_category())) + || can_disconnect(error_code(errors::timed_out_inactivity, get_libtorrent_category())); + + // make sure upload only peers are disconnected + if (t->is_upload_only() + && m_upload_only + && t->valid_metadata() + && has_metadata() + && ok_to_disconnect) + TORRENT_ASSERT(m_disconnect_started || t->graceful_pause() || t->has_error()); + + if (m_upload_only + && !m_interesting + && m_bitfield_received + && t->are_files_checked() + && t->valid_metadata() + && has_metadata() + && ok_to_disconnect) + TORRENT_ASSERT(m_disconnect_started); + } + + if (!m_disconnect_started && m_initialized && m_ses.settings().close_redundant_connections) + { + // none of this matters if we're disconnecting anyway + if (t->is_upload_only()) + TORRENT_ASSERT(!m_interesting || t->graceful_pause() || t->has_error()); + if (is_seed()) + TORRENT_ASSERT(m_upload_only); + } + +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + if (t->has_picker()) + { + std::map num_requests; + for (torrent::const_peer_iterator i = t->begin(); i != t->end(); ++i) + { + // make sure this peer is not a dangling pointer +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_ASSERT(m_ses.has_peer(*i)); +#endif + peer_connection const& p = *(*i); + for (std::vector::const_iterator i = p.request_queue().begin() + , end(p.request_queue().end()); i != end; ++i) + { + ++num_requests[i->block].num_peers; + ++num_requests[i->block].num_peers_with_timeouts; + ++num_requests[i->block].num_peers_with_nowant; + ++num_requests[i->block].num_not_requested; +// num_requests[i->block].peers.push_back(&p); + } + for (std::vector::const_iterator i = p.download_queue().begin() + , end(p.download_queue().end()); i != end; ++i) + { + if (!i->not_wanted && !i->timed_out) ++num_requests[i->block].num_peers; + if (i->timed_out) ++num_requests[i->block].num_peers_with_timeouts; + if (i->not_wanted) ++num_requests[i->block].num_peers_with_nowant; +// num_requests[i->block].peers.push_back(&p); + } + } + for (std::map::iterator i = num_requests.begin() + , end(num_requests.end()); i != end; ++i) + { + piece_block b = i->first; + peer_count_t const& pc = i->second; + int count = pc.num_peers; + int count_with_timeouts = pc.num_peers_with_timeouts; + int count_with_nowant = pc.num_peers_with_nowant; + (void)count_with_timeouts; + (void)count_with_nowant; + int picker_count = t->picker().num_peers(b); + if (!t->picker().is_downloaded(b)) + TORRENT_ASSERT(picker_count == count); + } + } + + if (m_peer_info && type() == bittorrent_connection) + { + policy::const_iterator i = t->get_policy().begin_peer(); + policy::const_iterator end = t->get_policy().end_peer(); + for (; i != end; ++i) + { + if (*i == m_peer_info) break; + } + TORRENT_ASSERT(i != end); + } +#endif +/* + if (t->has_picker() && !t->is_aborted()) + { + // make sure that pieces that have completed the download + // of all their blocks are in the disk io thread's queue + // to be checked. + const std::vector& dl_queue + = t->picker().get_download_queue(); + for (std::vector::const_iterator i = + dl_queue.begin(); i != dl_queue.end(); ++i) + { + const int blocks_per_piece = t->picker().blocks_in_piece(i->index); + + bool complete = true; + for (int j = 0; j < blocks_per_piece; ++j) + { + if (i->info[j].state == piece_picker::block_info::state_finished) + continue; + complete = false; + break; + } + TORRENT_ASSERT(complete); + } + } +*/ +// extremely expensive invariant check +/* + if (!t->is_seed()) + { + piece_picker& p = t->picker(); + const std::vector& dlq = p.get_download_queue(); + const int blocks_per_piece = static_cast( + t->torrent_file().piece_length() / t->block_size()); + + for (std::vector::const_iterator i = + dlq.begin(); i != dlq.end(); ++i) + { + for (int j = 0; j < blocks_per_piece; ++j) + { + if (std::find(m_request_queue.begin(), m_request_queue.end() + , piece_block(i->index, j)) != m_request_queue.end() + || + std::find(m_download_queue.begin(), m_download_queue.end() + , piece_block(i->index, j)) != m_download_queue.end()) + { + TORRENT_ASSERT(i->info[j].peer == m_remote); + } + else + { + TORRENT_ASSERT(i->info[j].peer != m_remote || i->info[j].finished); + } + } + } + } +*/ + } +#endif + + peer_connection::peer_speed_t peer_connection::peer_speed() + { + shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + int download_rate = int(statistics().download_payload_rate()); + int torrent_download_rate = int(t->statistics().download_payload_rate()); + + if (download_rate > 512 && download_rate > torrent_download_rate / 16) + m_speed = fast; + else if (download_rate > 4096 && download_rate > torrent_download_rate / 64) + m_speed = medium; + else if (download_rate < torrent_download_rate / 15 && m_speed == fast) + m_speed = medium; + else + m_speed = slow; + + return m_speed; + } + + void peer_connection::keep_alive() + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + INVARIANT_CHECK; +#endif + + time_duration d; + d = time_now() - m_last_sent; + if (total_seconds(d) < m_timeout / 2) return; + + if (m_connecting) return; + if (in_handshake()) return; + + // if the last send has not completed yet, do not send a keep + // alive + if (m_channel_state[upload_channel] & peer_info::bw_network) return; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> KEEPALIVE"); +#endif + + m_last_sent = time_now(); + write_keepalive(); + } + + bool peer_connection::is_seed() const + { + // if m_num_pieces == 0, we probably don't have the + // metadata yet. + boost::shared_ptr t = m_torrent.lock(); + return m_num_pieces == (int)m_have_piece.size() && m_num_pieces > 0 && t && t->valid_metadata(); + } + + void peer_connection::set_share_mode(bool u) + { + // if the peer is a seed, ignore share mode messages + if (is_seed()) return; + + m_share_mode = u; + } + + void peer_connection::set_upload_only(bool u) + { + // if the peer is a seed, don't allow setting + // upload_only to false + if (m_upload_only || is_seed()) return; + + m_upload_only = u; + boost::shared_ptr t = associated_torrent().lock(); + t->get_policy().set_seed(m_peer_info, u); + disconnect_if_redundant(); + } + +} diff --git a/apps/Launcher/ext/libtorrent/src/piece_picker.cpp b/apps/Launcher/ext/libtorrent/src/piece_picker.cpp new file mode 100644 index 0000000000..acc5852bf7 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/piece_picker.cpp @@ -0,0 +1,2731 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include + +#include +#include + +#include "libtorrent/piece_picker.hpp" +#include "libtorrent/bitfield.hpp" +#include "libtorrent/random.hpp" + +#if TORRENT_USE_ASSERTS +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/policy.hpp" // for policy::peer +#endif + +#ifdef TORRENT_USE_VALGRIND +#include +#endif + +#include "libtorrent/invariant_check.hpp" + +#define TORRENT_PIECE_PICKER_INVARIANT_CHECK INVARIANT_CHECK +//#define TORRENT_NO_EXPENSIVE_INVARIANT_CHECK +//#define TORRENT_PIECE_PICKER_INVARIANT_CHECK + +//#define TORRENT_PICKER_LOG + +namespace libtorrent +{ + + const piece_block piece_block::invalid(0x7FFFF, 0x1FFF); + + piece_picker::piece_picker() + : m_seeds(0) + , m_priority_boundries(1, int(m_pieces.size())) + , m_blocks_per_piece(0) + , m_blocks_in_last_piece(0) + , m_num_filtered(0) + , m_num_have_filtered(0) + , m_num_have(0) + , m_cursor(0) + , m_reverse_cursor(0) + , m_sparse_regions(1) + , m_num_pad_files(0) + , m_dirty(false) + { +#ifdef TORRENT_PICKER_LOG + std::cerr << "new piece_picker" << std::endl; +#endif +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif + } + + void piece_picker::init(int blocks_per_piece, int blocks_in_last_piece, int total_num_pieces) + { + TORRENT_ASSERT(blocks_per_piece > 0); + TORRENT_ASSERT(total_num_pieces > 0); + +#ifdef TORRENT_PICKER_LOG + std::cerr << "piece_picker::init()" << std::endl; +#endif + // allocate the piece_map to cover all pieces + // and make them invalid (as if we don't have a single piece) + m_piece_map.resize(total_num_pieces, piece_pos(0, 0)); + m_reverse_cursor = int(m_piece_map.size()); + m_cursor = 0; + + m_downloads.clear(); + m_block_info.clear(); + + m_num_filtered += m_num_have_filtered; + m_num_have_filtered = 0; + m_num_have = 0; + m_dirty = true; + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { + i->peer_count = 0; + i->downloading = 0; + i->index = 0; +#ifdef TORRENT_DEBUG_REFCOUNTS + i->have_peers.clear(); +#endif + } + + for (std::vector::iterator i = m_piece_map.begin() + m_cursor + , end(m_piece_map.end()); i != end && (i->have() || i->filtered()); + ++i, ++m_cursor); + for (std::vector::reverse_iterator i = m_piece_map.rend() + - m_reverse_cursor; m_reverse_cursor > 0 && (i->have() || i->filtered()); + ++i, --m_reverse_cursor); + + // the piece index is stored in 20 bits, which limits the allowed + // number of pieces somewhat + TORRENT_ASSERT(m_piece_map.size() < piece_pos::we_have_index); + + m_blocks_per_piece = blocks_per_piece; + m_blocks_in_last_piece = blocks_in_last_piece; + if (m_blocks_in_last_piece == 0) m_blocks_in_last_piece = blocks_per_piece; + + TORRENT_ASSERT(m_blocks_in_last_piece <= m_blocks_per_piece); + } + + void piece_picker::piece_info(int index, piece_picker::downloading_piece& st) const + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < int(m_piece_map.size())); + + if (m_piece_map[index].downloading) + { + std::vector::const_iterator piece = find_dl_piece(index); + TORRENT_ASSERT(piece != m_downloads.end()); + st = *piece; + return; + } + st.info = 0; + st.index = index; + st.writing = 0; + st.requested = 0; + if (m_piece_map[index].have()) + { + st.finished = blocks_in_piece(index); + return; + } + st.finished = 0; + } + + piece_picker::downloading_piece& piece_picker::add_download_piece(int piece) + { + TORRENT_ASSERT(piece >= 0); + TORRENT_ASSERT(piece < int(m_piece_map.size())); + int num_downloads = m_downloads.size(); + int block_index = num_downloads * m_blocks_per_piece; + if (int(m_block_info.size()) < block_index + m_blocks_per_piece) + { + block_info* base = 0; + if (!m_block_info.empty()) base = &m_block_info[0]; + m_block_info.resize(block_index + m_blocks_per_piece); + if (!m_downloads.empty() && &m_block_info[0] != base) + { + // this means the memory was reallocated, update the pointers + for (int i = 0; i < int(m_downloads.size()); ++i) + m_downloads[i].info = &m_block_info[m_downloads[i].info - base]; + } + } + downloading_piece ret; + ret.index = piece; + std::vector::iterator i = std::lower_bound(m_downloads.begin() + , m_downloads.end(), ret); + TORRENT_ASSERT(i == m_downloads.end() || i->index != piece); + ret.info = &m_block_info[block_index]; + TORRENT_ASSERT(ret.info >= &m_block_info[0]); + TORRENT_ASSERT(ret.info < &m_block_info[0] + m_block_info.size()); +#ifdef TORRENT_USE_VALGRIND + VALGRIND_CHECK_VALUE_IS_DEFINED(piece); + VALGRIND_CHECK_VALUE_IS_DEFINED(block_index); +#endif + for (int i = 0; i < m_blocks_per_piece; ++i) + { + ret.info[i].num_peers = 0; + ret.info[i].state = block_info::state_none; + ret.info[i].peer = 0; +#ifdef TORRENT_USE_VALGRIND + VALGRIND_CHECK_VALUE_IS_DEFINED(ret.info[i].peer); +#endif +#if TORRENT_USE_ASSERTS + ret.info[i].piece_index = piece; +#endif + } + +#ifdef TORRENT_USE_VALGRIND + VALGRIND_CHECK_VALUE_IS_DEFINED(ret.info); + VALGRIND_CHECK_VALUE_IS_DEFINED(ret.index); + VALGRIND_CHECK_VALUE_IS_DEFINED(ret.finished); + VALGRIND_CHECK_VALUE_IS_DEFINED(ret.writing); + VALGRIND_CHECK_VALUE_IS_DEFINED(ret.requested); + VALGRIND_CHECK_VALUE_IS_DEFINED(ret.state); +#endif + i = m_downloads.insert(i, ret); + return *i; + } + + void piece_picker::erase_download_piece(std::vector::iterator i) + { + std::vector::iterator other = std::find_if( + m_downloads.begin(), m_downloads.end() + , boost::bind(&downloading_piece::info, _1) + == &m_block_info[(m_downloads.size() - 1) * m_blocks_per_piece]); + TORRENT_ASSERT(other != m_downloads.end()); + + if (i != other) + { + std::copy(other->info, other->info + m_blocks_per_piece, i->info); + other->info = i->info; + } + m_piece_map[i->index].downloading = false; + m_downloads.erase(i); + } + +#if TORRENT_USE_INVARIANT_CHECKS + + void piece_picker::verify_pick(std::vector const& picked + , bitfield const& bits) const + { + TORRENT_ASSERT(bits.size() == m_piece_map.size()); + for (std::vector::const_iterator i = picked.begin() + , end(picked.end()); i != end; ++i) + { + TORRENT_ASSERT(i->piece_index >= 0); + TORRENT_ASSERT(i->piece_index < bits.size()); + TORRENT_ASSERT(bits[i->piece_index]); + TORRENT_ASSERT(!m_piece_map[i->piece_index].have()); + TORRENT_ASSERT(!m_piece_map[i->piece_index].filtered()); + } + } + + void piece_picker::verify_priority(int range_start, int range_end, int prio) const + { + TORRENT_ASSERT(range_start <= range_end); + TORRENT_ASSERT(range_end <= int(m_pieces.size())); + for (std::vector::const_iterator i = m_pieces.begin() + range_start + , end(m_pieces.begin() + range_end); i != end; ++i) + { + int index = *i; + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < int(m_piece_map.size())); + int p = m_piece_map[index].priority(this); + TORRENT_ASSERT(p == prio); + } + } + +#if defined TORRENT_PICKER_LOG + void piece_picker::print_pieces() const + { + for (std::vector::const_iterator i = m_priority_boundries.begin() + , end(m_priority_boundries.end()); i != end; ++i) + { + std::cerr << *i << " "; + } + std::cout << std::endl; + int index = 0; + std::vector::const_iterator j = m_priority_boundries.begin(); + for (std::vector::const_iterator i = m_pieces.begin() + , end(m_pieces.end()); i != end; ++i, ++index) + { + if (*i == -1) break; + while (j != m_priority_boundries.end() && *j <= index) + { + std::cerr << "| "; + ++j; + } + std::cerr << *i << "(" << m_piece_map[*i].index << ") "; + } + std::cerr << std::endl; + } +#endif // TORRENT_PIECE_PICKER +#endif // TORRENT_USE_INVARIANT_CHECKS + +#if TORRENT_USE_INVARIANT_CHECKS + void piece_picker::check_invariant(const torrent* t) const + { +#ifndef TORRENT_DEBUG_REFCOUNTS +#if TORRENT_COMPACT_PICKER + TORRENT_ASSERT(sizeof(piece_pos) == 4); +#else + TORRENT_ASSERT(sizeof(piece_pos) == 8); +#endif +#endif + TORRENT_ASSERT(m_num_have >= 0); + TORRENT_ASSERT(m_num_have_filtered >= 0); + TORRENT_ASSERT(m_num_filtered >= 0); + TORRENT_ASSERT(m_seeds >= 0); + + if (!m_downloads.empty()) + { + for (std::vector::const_iterator i = m_downloads.begin(); + i != m_downloads.end() - 1; ++i) + { + downloading_piece const& dp = *i; + downloading_piece const& next = *(i + 1); +// TORRENT_ASSERT(dp.finished + dp.writing >= next.finished + next.writing); + TORRENT_ASSERT(dp.index < next.index); + } + } + + if (t != 0) + TORRENT_ASSERT((int)m_piece_map.size() == t->torrent_file().num_pieces()); + + for (std::vector::const_iterator i = m_downloads.begin() + , end(m_downloads.end()); i != end; ++i) + { + bool blocks_requested = false; + int num_blocks = blocks_in_piece(i->index); + int num_requested = 0; + int num_finished = 0; + int num_writing = 0; + for (int k = 0; k < num_blocks; ++k) + { + TORRENT_ASSERT(i->info[k].piece_index == i->index); + TORRENT_ASSERT(i->info[k].peer == 0 || static_cast(i->info[k].peer)->in_use); + if (i->info[k].state == block_info::state_finished) + { + ++num_finished; + TORRENT_ASSERT(i->info[k].num_peers == 0); + } + else if (i->info[k].state == block_info::state_requested) + { + ++num_requested; + blocks_requested = true; + TORRENT_ASSERT(i->info[k].num_peers > 0); + } + else if (i->info[k].state == block_info::state_writing) + { + ++num_writing; + TORRENT_ASSERT(i->info[k].num_peers == 0); + } + } + TORRENT_ASSERT(blocks_requested == (i->state != none)); + TORRENT_ASSERT(num_requested == i->requested); + TORRENT_ASSERT(num_writing == i->writing); + TORRENT_ASSERT(num_finished == i->finished); + if (m_piece_map[i->index].full) + TORRENT_ASSERT(num_finished + num_writing + num_requested == num_blocks); + } + int num_pieces = int(m_piece_map.size()); + TORRENT_ASSERT(m_cursor >= 0); + TORRENT_ASSERT(m_cursor <= num_pieces); + TORRENT_ASSERT(m_reverse_cursor <= num_pieces); + TORRENT_ASSERT(m_reverse_cursor >= 0); + TORRENT_ASSERT(m_reverse_cursor > m_cursor + || (m_cursor == num_pieces && m_reverse_cursor == 0)); + +#ifdef TORRENT_NO_EXPENSIVE_INVARIANT_CHECK + return; +#endif + + if (!m_dirty) + { + TORRENT_ASSERT(!m_priority_boundries.empty()); + int prio = 0; + int start = 0; + for (std::vector::const_iterator i = m_priority_boundries.begin() + , end(m_priority_boundries.end()); i != end; ++i) + { + verify_priority(start, *i, prio); + ++prio; + start = *i; + } + TORRENT_ASSERT(m_priority_boundries.back() == int(m_pieces.size())); + } + int index = 0; + for (std::vector::const_iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end && (i->have() || i->filtered()); + ++i, ++index); + TORRENT_ASSERT(m_cursor == index); + index = num_pieces; + if (num_pieces > 0) + { + for (std::vector::reverse_iterator i = m_piece_map.rend() + - index; index > 0 && (i->have() || i->filtered()); ++i, --index); + TORRENT_ASSERT(index == num_pieces + || m_piece_map[index].have() + || m_piece_map[index].filtered()); + TORRENT_ASSERT(m_reverse_cursor == index); + } + else + { + TORRENT_ASSERT(m_reverse_cursor == 0); + } + + int num_filtered = 0; + int num_have_filtered = 0; + int num_have = 0; + for (std::vector::const_iterator i = m_piece_map.begin(); + i != m_piece_map.end(); ++i) + { + int index = static_cast(i - m_piece_map.begin()); + piece_pos const& p = *i; + + if (p.filtered()) + { + if (p.index != piece_pos::we_have_index) + ++num_filtered; + else + ++num_have_filtered; + } + +#ifdef TORRENT_DEBUG_REFCOUNTS + TORRENT_ASSERT(p.have_peers.size() == p.peer_count + m_seeds); +#endif + + if (p.index == piece_pos::we_have_index) + ++num_have; + +#if 0 + if (t != 0) + { + int actual_peer_count = 0; + for (torrent::const_peer_iterator peer = t->begin(); + peer != t->end(); ++peer) + { + if (peer->second->has_piece(index)) actual_peer_count++; + } + + TORRENT_ASSERT((int)i->peer_count == actual_peer_count); +/* + int num_downloaders = 0; + for (std::vector::const_iterator peer = t->begin(); + peer != t->end(); + ++peer) + { + const std::vector& queue = (*peer)->download_queue(); + if (std::find_if(queue.begin(), queue.end(), has_index(index)) == queue.end()) continue; + + ++num_downloaders; + } + + if (i->downloading) + { + TORRENT_ASSERT(num_downloaders == 1); + } + else + { + TORRENT_ASSERT(num_downloaders == 0); + } +*/ + } +#endif + + if (p.index == piece_pos::we_have_index) + { + TORRENT_ASSERT(t == 0 || t->have_piece(index)); + TORRENT_ASSERT(p.downloading == 0); + } + + if (t != 0) + TORRENT_ASSERT(!t->have_piece(index)); + + int prio = p.priority(this); + TORRENT_ASSERT(prio == -1 || p.downloading == (prio % piece_picker::prio_factor == 0)); + + if (!m_dirty) + { + TORRENT_ASSERT(prio < int(m_priority_boundries.size()) + || m_dirty); + if (prio >= 0) + { + TORRENT_ASSERT(p.index < m_pieces.size()); + TORRENT_ASSERT(m_pieces[p.index] == index); + } + else + { + TORRENT_ASSERT(prio == -1); + // make sure there's no entry + // with this index. (there shouldn't + // be since the priority is -1) + TORRENT_ASSERT(std::find(m_pieces.begin(), m_pieces.end(), index) + == m_pieces.end()); + } + } + + int count = std::count_if(m_downloads.begin(), m_downloads.end() + , has_index(index)); + if (i->downloading == 1) + { + TORRENT_ASSERT(count == 1); + } + else + { + TORRENT_ASSERT(count == 0); + } + } + TORRENT_ASSERT(num_have == m_num_have); + TORRENT_ASSERT(num_filtered == m_num_filtered); + TORRENT_ASSERT(num_have_filtered == m_num_have_filtered); + + if (!m_dirty) + { + for (std::vector::const_iterator i = m_pieces.begin() + , end(m_pieces.end()); i != end; ++i) + { + TORRENT_ASSERT(m_piece_map[*i].priority(this) >= 0); + } + } + } +#endif + + std::pair piece_picker::distributed_copies() const + { + TORRENT_ASSERT(m_seeds >= 0); + const int num_pieces = m_piece_map.size(); + + if (num_pieces == 0) return std::make_pair(1, 0); + int min_availability = piece_pos::max_peer_count; + // find the lowest availability count + // count the number of pieces that have that availability + // and also the number of pieces that have more than that. + int integer_part = 0; + int fraction_part = 0; + for (std::vector::const_iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { + int peer_count = int(i->peer_count); + // take ourself into account + if (i->have()) ++peer_count; + if (min_availability > peer_count) + { + min_availability = peer_count; + fraction_part += integer_part; + integer_part = 1; + } + else if (peer_count == min_availability) + { + ++integer_part; + } + else + { + TORRENT_ASSERT(peer_count > min_availability); + ++fraction_part; + } + } + TORRENT_ASSERT(integer_part + fraction_part == num_pieces); + return std::make_pair(min_availability + m_seeds, fraction_part * 1000 / num_pieces); + } + + void piece_picker::priority_range(int prio, int* start, int* end) + { + TORRENT_ASSERT(prio >= 0); + TORRENT_ASSERT(prio < int(m_priority_boundries.size()) + || m_dirty); + if (prio == 0) *start = 0; + else *start = m_priority_boundries[prio - 1]; + *end = m_priority_boundries[prio]; + TORRENT_ASSERT(*start <= *end); + } + + void piece_picker::add(int index) + { + TORRENT_ASSERT(!m_dirty); + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < int(m_piece_map.size())); + piece_pos& p = m_piece_map[index]; + TORRENT_ASSERT(!p.filtered()); + TORRENT_ASSERT(!p.have()); + + int priority = p.priority(this); + TORRENT_ASSERT(priority >= 0); + if (int(m_priority_boundries.size()) <= priority) + m_priority_boundries.resize(priority + 1, m_pieces.size()); + + TORRENT_ASSERT(int(m_priority_boundries.size()) >= priority); + + int range_start, range_end; + priority_range(priority, &range_start, &range_end); + int new_index; + if (range_end == range_start) new_index = range_start; + else new_index = random() % (range_end - range_start + 1) + range_start; + +#ifdef TORRENT_PICKER_LOG + std::cerr << "add " << index << " (" << priority << ")" << std::endl; + print_pieces(); +#endif + m_pieces.push_back(-1); + + for (;;) + { + TORRENT_ASSERT(new_index < int(m_pieces.size())); + int temp = m_pieces[new_index]; + m_pieces[new_index] = index; + m_piece_map[index].index = new_index; + index = temp; + do + { + temp = m_priority_boundries[priority]++; + ++priority; + } while (temp == new_index && priority < int(m_priority_boundries.size())); + new_index = temp; +#ifdef TORRENT_PICKER_LOG + print_pieces(); + std::cerr << " index: " << index + << " prio: " << priority + << " new_index: " << new_index + << std::endl; +#endif + if (priority >= int(m_priority_boundries.size())) break; + TORRENT_ASSERT(temp >= 0); + } + if (index != -1) + { + TORRENT_ASSERT(new_index == int(m_pieces.size() - 1)); + m_pieces[new_index] = index; + m_piece_map[index].index = new_index; + +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif +// shuffle(priority, new_index); +#ifdef TORRENT_PICKER_LOG +// print_pieces(); +#endif + } + } + + void piece_picker::remove(int priority, int elem_index) + { + TORRENT_ASSERT(!m_dirty); + TORRENT_ASSERT(priority >= 0); + TORRENT_ASSERT(elem_index < int(m_pieces.size())); + TORRENT_ASSERT(elem_index >= 0); + +#ifdef TORRENT_PICKER_LOG + std::cerr << "remove " << m_pieces[elem_index] << " (" << priority << ")" << std::endl; +#endif + int next_index = elem_index; + TORRENT_ASSERT(m_piece_map[m_pieces[elem_index]].priority(this) == -1); + for (;;) + { +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + TORRENT_ASSERT(elem_index < int(m_pieces.size())); + int temp; + do + { + temp = --m_priority_boundries[priority]; + ++priority; + } while (next_index == temp && priority < int(m_priority_boundries.size())); + if (next_index == temp) break; + next_index = temp; + + int piece = m_pieces[next_index]; + m_pieces[elem_index] = piece; + m_piece_map[piece].index = elem_index; + TORRENT_ASSERT(m_piece_map[piece].priority(this) == priority - 1); + TORRENT_ASSERT(elem_index < int(m_pieces.size() - 1)); + elem_index = next_index; + + if (priority == int(m_priority_boundries.size())) + break; + } + m_pieces.pop_back(); + TORRENT_ASSERT(next_index == int(m_pieces.size())); +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + } + + // will update the piece with the given properties (priority, elem_index) + // to place it at the correct position + void piece_picker::update(int priority, int elem_index) + { + TORRENT_ASSERT(!m_dirty); + TORRENT_ASSERT(priority >= 0); + TORRENT_ASSERT(elem_index >= 0); + + TORRENT_ASSERT(int(m_priority_boundries.size()) > priority); + + int index = m_pieces[elem_index]; + // update the piece_map + piece_pos& p = m_piece_map[index]; + TORRENT_ASSERT(int(p.index) == elem_index || p.have()); + + int new_priority = p.priority(this); + + if (new_priority == priority) return; + + if (new_priority == -1) + { + remove(priority, elem_index); + return; + } + + if (int(m_priority_boundries.size()) <= new_priority) + m_priority_boundries.resize(new_priority + 1, m_pieces.size()); + +#ifdef TORRENT_PICKER_LOG + std::cerr << "update " << index << " (" << priority << "->" << new_priority << ")" << std::endl; +#endif + if (priority > new_priority) + { + int new_index; + int temp = index; + for (;;) + { +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + --priority; + new_index = m_priority_boundries[priority]++; + TORRENT_ASSERT(new_index < int(m_pieces.size())); + if (temp != m_pieces[new_index]) + { + temp = m_pieces[new_index]; + m_pieces[elem_index] = temp; + m_piece_map[temp].index = elem_index; + TORRENT_ASSERT(elem_index < int(m_pieces.size())); + } + elem_index = new_index; + if (priority == new_priority) break; + } +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + m_pieces[elem_index] = index; + m_piece_map[index].index = elem_index; + TORRENT_ASSERT(elem_index < int(m_pieces.size())); +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + shuffle(priority, elem_index); +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + TORRENT_ASSERT(m_piece_map[index].priority(this) == priority); + } + else + { + int new_index; + int temp = index; + for (;;) + { +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + new_index = --m_priority_boundries[priority]; + TORRENT_ASSERT(new_index < int(m_pieces.size())); + if (temp != m_pieces[new_index]) + { + temp = m_pieces[new_index]; + m_pieces[elem_index] = temp; + m_piece_map[temp].index = elem_index; + TORRENT_ASSERT(elem_index < int(m_pieces.size())); + } + elem_index = new_index; + ++priority; + if (priority == new_priority) break; + } +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + m_pieces[elem_index] = index; + m_piece_map[index].index = elem_index; + TORRENT_ASSERT(elem_index < int(m_pieces.size())); +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + shuffle(priority, elem_index); +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + TORRENT_ASSERT(m_piece_map[index].priority(this) == priority); + } + } + + void piece_picker::shuffle(int priority, int elem_index) + { +#ifdef TORRENT_PICKER_LOG + std::cerr << "shuffle()" << std::endl; +#endif + + TORRENT_ASSERT(!m_dirty); + TORRENT_ASSERT(priority >= 0); + TORRENT_ASSERT(elem_index >= 0); + TORRENT_ASSERT(elem_index < int(m_pieces.size())); + TORRENT_ASSERT(m_piece_map[m_pieces[elem_index]].priority(this) == priority); + + int range_start, range_end; + priority_range(priority, &range_start, &range_end); + TORRENT_ASSERT(range_start < range_end); + int other_index = random() % (range_end - range_start) + range_start; + + if (other_index == elem_index) return; + + // swap other_index with elem_index + piece_pos& p1 = m_piece_map[m_pieces[other_index]]; + piece_pos& p2 = m_piece_map[m_pieces[elem_index]]; + + int temp = p1.index; + p1.index = p2.index; + p2.index = temp; + std::swap(m_pieces[other_index], m_pieces[elem_index]); + } +/* + void piece_picker::sort_piece(std::vector::iterator dp) + { + TORRENT_ASSERT(m_piece_map[dp->index].downloading); + int complete = dp->writing + dp->finished; + if (dp != m_downloads.begin()) + { + for (std::vector::iterator j(dp-1); + dp != m_downloads.begin(); --dp, --j) + { + TORRENT_ASSERT(j >= m_downloads.begin()); + if (j->finished + j->writing >= complete) break; + using std::swap; + swap(*j, *dp); + if (j == m_downloads.begin()) return; + } + } + + TORRENT_ASSERT(dp != m_downloads.end()); + for (std::vector::iterator j(dp+1); + dp != m_downloads.end() - 1; ++dp, ++j) + { + TORRENT_ASSERT(j < m_downloads.end()); + if (j->finished + j->writing <= complete) break; + using std::swap; + swap(*j, *dp); + if (j == m_downloads.end() - 1) return; + } + } +*/ + void piece_picker::restore_piece(int index) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < int(m_piece_map.size())); + + TORRENT_ASSERT(m_piece_map[index].downloading == 1); + + std::vector::iterator i = find_dl_piece(index); + + TORRENT_ASSERT(i != m_downloads.end()); + TORRENT_ASSERT(i->info >= &m_block_info[0] + && i->info < &m_block_info[0] + m_block_info.size()); +#ifdef TORRENT_DEBUG + int num_blocks = blocks_in_piece(i->index); + for (int k = 0; k < num_blocks; ++k) + { + TORRENT_ASSERT(i->info[k].piece_index == index); + TORRENT_ASSERT(i->info[k].state == block_info::state_finished); + TORRENT_ASSERT(i->info[k].num_peers == 0); + } +#endif + + piece_pos& p = m_piece_map[index]; + int prev_priority = p.priority(this); + erase_download_piece(i); + int new_priority = p.priority(this); + + if (new_priority == prev_priority) return; + if (m_dirty) return; + if (prev_priority == -1) add(index); + else update(prev_priority, p.index); + } + + void piece_picker::inc_refcount_all(const void* peer) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + + ++m_seeds; + if (m_seeds == 1) + { + // when m_seeds is increased from 0 to 1 + // we may have to add pieces that previously + // didn't have any peers + m_dirty = true; + } + +#ifdef TORRENT_DEBUG_REFCOUNTS + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { + TORRENT_ASSERT(i->have_peers.count(peer) == 0); + i->have_peers.insert(peer); + } +#endif + } + + void piece_picker::dec_refcount_all(const void* peer) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + + if (m_seeds > 0) + { + --m_seeds; + if (m_seeds == 0) + { + // when m_seeds is decreased from 1 to 0 + // we may have to remove pieces that previously + // didn't have any peers + m_dirty = true; + } +#ifdef TORRENT_DEBUG_REFCOUNTS + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { + TORRENT_ASSERT(i->have_peers.count(peer) == 1); + i->have_peers.erase(peer); + } +#endif + return; + } + TORRENT_ASSERT(m_seeds == 0); + + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { +#ifdef TORRENT_DEBUG_REFCOUNTS + TORRENT_ASSERT(i->have_peers.count(peer) == 1); + i->have_peers.erase(peer); +#endif + + TORRENT_ASSERT(i->peer_count > 0); + --i->peer_count; + } + + m_dirty = true; + } + + void piece_picker::inc_refcount(int index, const void* peer) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + + piece_pos& p = m_piece_map[index]; + +#ifdef TORRENT_DEBUG_REFCOUNTS + TORRENT_ASSERT(p.have_peers.count(peer) == 0); + p.have_peers.insert(peer); +#endif + + int prev_priority = p.priority(this); + ++p.peer_count; + if (m_dirty) return; + int new_priority = p.priority(this); + if (prev_priority == new_priority) return; + if (prev_priority == -1) + add(index); + else + update(prev_priority, p.index); + } + + // this function decrements the m_seeds counter + // and increments the peer counter on every piece + // instead. Sometimes of we connect to a seed that + // later sends us a dont-have message, we'll need to + // turn that m_seed into counts on the pieces since + // they can't be negative + void piece_picker::break_one_seed() + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + TORRENT_ASSERT(m_seeds > 0); + --m_seeds; + + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { + ++i->peer_count; + } + + m_dirty = true; + } + + void piece_picker::dec_refcount(int index, const void* peer) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + + piece_pos& p = m_piece_map[index]; + +#ifdef TORRENT_DEBUG_REFCOUNTS + TORRENT_ASSERT(p.have_peers.count(peer) == 1); + p.have_peers.erase(peer); +#endif + + if (p.peer_count == 0) + { + TORRENT_ASSERT(m_seeds > 0); + // this is the case where we have one or more + // seeds, and one of them saying: I don't have this + // piece anymore. we need to break up one of the seed + // counters into actual peer counters on the pieces + break_one_seed(); + } + + int prev_priority = p.priority(this); + TORRENT_ASSERT(p.peer_count > 0); + --p.peer_count; + if (m_dirty) return; + if (prev_priority >= 0) update(prev_priority, p.index); + } + + void piece_picker::inc_refcount(bitfield const& bitmask, const void* peer) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + TORRENT_ASSERT(bitmask.size() == m_piece_map.size()); + + int index = 0; + bool updated = false; + for (bitfield::const_iterator i = bitmask.begin() + , end(bitmask.end()); i != end; ++i, ++index) + { + if (*i) + { +#ifdef TORRENT_DEBUG_REFCOUNTS + TORRENT_ASSERT(m_piece_map[index].have_peers.count(peer) == 0); + m_piece_map[index].have_peers.insert(peer); +#endif + + ++m_piece_map[index].peer_count; + updated = true; + } + } + + if (updated) m_dirty = true; + } + + void piece_picker::dec_refcount(bitfield const& bitmask, const void* peer) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + TORRENT_ASSERT(bitmask.size() <= m_piece_map.size()); + + int index = 0; + bool updated = false; +#if TORRENT_USE_ASSERTS + bool seed_broken = false; +#endif + for (bitfield::const_iterator i = bitmask.begin() + , end(bitmask.end()); i != end; ++i, ++index) + { + if (*i) + { +#ifdef TORRENT_DEBUG_REFCOUNTS + TORRENT_ASSERT(m_piece_map[index].have_peers.count(peer) == 1); + m_piece_map[index].have_peers.erase(peer); +#endif + piece_pos& p = m_piece_map[index]; + + if (p.peer_count == 0) + { + TORRENT_ASSERT(!seed_broken); + TORRENT_ASSERT(m_seeds > 0); + // this is the case where we have one or more + // seeds, and one of them saying: I don't have this + // piece anymore. we need to break up one of the seed + // counters into actual peer counters on the pieces + break_one_seed(); +#if TORRENT_USE_ASSERTS + seed_broken = true; +#endif + } + + --p.peer_count; + updated = true; + } + } + + if (updated) m_dirty = true; + } + + void piece_picker::update_pieces() const + { + TORRENT_ASSERT(m_dirty); + if (m_priority_boundries.empty()) m_priority_boundries.resize(1, 0); +#ifdef TORRENT_PICKER_LOG + std::cerr << "update_pieces" << std::endl; +#endif + std::fill(m_priority_boundries.begin(), m_priority_boundries.end(), 0); + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { + int prio = i->priority(this); + if (prio == -1) continue; + if (prio >= int(m_priority_boundries.size())) + m_priority_boundries.resize(prio + 1, 0); + i->index = m_priority_boundries[prio]; + ++m_priority_boundries[prio]; + } + +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + + int index = 0; + for (std::vector::iterator i = m_priority_boundries.begin() + , end(m_priority_boundries.end()); i != end; ++i) + { + *i += index; + index = *i; + } + m_pieces.resize(index, 0); + +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + + index = 0; + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i, ++index) + { + piece_pos& p = *i; + int prio = p.priority(this); + if (prio == -1) continue; + int new_index = (prio == 0 ? 0 : m_priority_boundries[prio - 1]) + p.index; + m_pieces[new_index] = index; + } + + int start = 0; + for (std::vector::iterator i = m_priority_boundries.begin() + , end(m_priority_boundries.end()); i != end; ++i) + { + if (start == *i) continue; + std::random_shuffle(&m_pieces[0] + start, &m_pieces[0] + *i); + start = *i; + } + + index = 0; + for (std::vector::const_iterator i = m_pieces.begin() + , end(m_pieces.end()); i != end; ++i, ++index) + { + TORRENT_ASSERT(*i >= 0 && *i < int(m_piece_map.size())); + m_piece_map[*i].index = index; + } + + m_dirty = false; +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + } + + void piece_picker::we_dont_have(int index) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < (int)m_piece_map.size()); + + piece_pos& p = m_piece_map[index]; + TORRENT_ASSERT(p.downloading == 0); + +#ifdef TORRENT_PICKER_LOG + std::cerr << "piece_picker::we_dont_have(" << index << ")" << std::endl; +#endif + if (!p.have()) return; + + if (p.filtered()) + { + ++m_num_filtered; + --m_num_have_filtered; + } + else + { + // update cursors + if (index < m_cursor) + m_cursor = index; + if (index >= m_reverse_cursor) + m_reverse_cursor = index + 1; + if (m_reverse_cursor == m_cursor) + { + m_reverse_cursor = 0; + m_cursor = num_pieces(); + } + } + + --m_num_have; + p.set_not_have(); + + if (m_dirty) return; + if (p.priority(this) >= 0) add(index); + } + + // this is used to indicate that we succesfully have + // downloaded a piece, and that no further attempts + // to pick that piece should be made. The piece will + // be removed from the available piece list. + void piece_picker::we_have(int index) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < (int)m_piece_map.size()); + +#ifdef TORRENT_PICKER_LOG + std::cerr << "piece_picker::we_have(" << index << ")" << std::endl; +#endif + piece_pos& p = m_piece_map[index]; + int info_index = p.index; + int priority = p.priority(this); + TORRENT_ASSERT(priority < int(m_priority_boundries.size()) || m_dirty); + + if (p.downloading) + { + std::vector::iterator i + = find_dl_piece(index); + TORRENT_ASSERT(i != m_downloads.end()); + erase_download_piece(i); + } + + TORRENT_ASSERT(find_dl_piece(index) == m_downloads.end()); + + if (p.have()) return; + +// maintain sparse_regions + if (index == 0) + { + if (index == int(m_piece_map.size()) - 1 + || m_piece_map[index + 1].have()) + --m_sparse_regions; + } + else if (index == int(m_piece_map.size() - 1)) + { + if (index == 0 + || m_piece_map[index - 1].have()) + --m_sparse_regions; + } + else + { + bool have_before = m_piece_map[index-1].have(); + bool have_after = m_piece_map[index+1].have(); + if (have_after && have_before) --m_sparse_regions; + else if (!have_after && !have_before) ++m_sparse_regions; + } + + if (p.filtered()) + { + --m_num_filtered; + ++m_num_have_filtered; + } + ++m_num_have; + p.set_have(); + if (m_cursor == m_reverse_cursor - 1 && + m_cursor == index) + { + m_cursor = int(m_piece_map.size()); + m_reverse_cursor = 0; + TORRENT_ASSERT(num_pieces() > 0); + } + else if (m_cursor == index) + { + ++m_cursor; + for (std::vector::const_iterator i = m_piece_map.begin() + m_cursor + , end(m_piece_map.end()); i != end && (i->have() || i->filtered()); + ++i, ++m_cursor); + } + else if (m_reverse_cursor - 1 == index) + { + --m_reverse_cursor; + TORRENT_ASSERT(m_piece_map[m_reverse_cursor].have() + || m_piece_map[m_reverse_cursor].filtered()); + for (std::vector::const_iterator i = m_piece_map.begin() + + m_reverse_cursor - 1; m_reverse_cursor > 0 && (i->have() || i->filtered()); + --i, --m_reverse_cursor); + TORRENT_ASSERT(m_piece_map[m_reverse_cursor].have() + || m_piece_map[m_reverse_cursor].filtered()); + } + TORRENT_ASSERT(m_reverse_cursor > m_cursor + || (m_cursor == num_pieces() && m_reverse_cursor == 0)); + if (priority == -1) return; + if (m_dirty) return; + remove(priority, info_index); + TORRENT_ASSERT(p.priority(this) == -1); + } + + bool piece_picker::set_piece_priority(int index, int new_piece_priority) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + TORRENT_ASSERT(new_piece_priority >= 0); + TORRENT_ASSERT(new_piece_priority <= 7); + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < (int)m_piece_map.size()); + + piece_pos& p = m_piece_map[index]; + + // if the priority isn't changed, don't do anything + if (new_piece_priority == int(p.piece_priority)) return false; + + int prev_priority = p.priority(this); + TORRENT_ASSERT(m_dirty || prev_priority < int(m_priority_boundries.size())); + + bool ret = false; + if (new_piece_priority == piece_pos::filter_priority + && p.piece_priority != piece_pos::filter_priority) + { + // the piece just got filtered + if (p.have()) + { + ++m_num_have_filtered; + } + else + { + ++m_num_filtered; + + // update m_cursor + if (m_cursor == m_reverse_cursor - 1 && m_cursor == index) + { + m_cursor = int(m_piece_map.size()); + m_reverse_cursor = 0; + } + else if (m_cursor == index) + { + ++m_cursor; + while (m_cursor < int(m_piece_map.size()) + && (m_piece_map[m_cursor].have() + || m_piece_map[m_cursor].filtered())) + ++m_cursor; + } + else if (m_reverse_cursor == index + 1) + { + --m_reverse_cursor; + while (m_reverse_cursor > 0 + && (m_piece_map[m_reverse_cursor-1].have() + || m_piece_map[m_reverse_cursor-1].filtered())) + --m_reverse_cursor; + } + } + ret = true; + } + else if (new_piece_priority != piece_pos::filter_priority + && p.piece_priority == piece_pos::filter_priority) + { + // the piece just got unfiltered + if (p.have()) + { + --m_num_have_filtered; + } + else + { + --m_num_filtered; + // update cursors + if (index < m_cursor) + m_cursor = index; + if (index >= m_reverse_cursor) + m_reverse_cursor = index + 1; + if (m_reverse_cursor == m_cursor) + { + m_reverse_cursor = 0; + m_cursor = num_pieces(); + } + } + ret = true; + } + TORRENT_ASSERT(m_num_filtered >= 0); + TORRENT_ASSERT(m_num_have_filtered >= 0); + + p.piece_priority = new_piece_priority; + int new_priority = p.priority(this); + + if (prev_priority == new_priority) return ret; + + if (m_dirty) return ret; + if (prev_priority == -1) + { + add(index); + } + else + { + update(prev_priority, p.index); + } + return ret; + } + + int piece_picker::piece_priority(int index) const + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < (int)m_piece_map.size()); + + return m_piece_map[index].piece_priority; + } + + void piece_picker::piece_priorities(std::vector& pieces) const + { + pieces.resize(m_piece_map.size()); + std::vector::iterator j = pieces.begin(); + for (std::vector::const_iterator i = m_piece_map.begin(), + end(m_piece_map.end()); i != end; ++i, ++j) + { + *j = i->piece_priority; + } + } + + // ============ start deprecation ============== + + void piece_picker::filtered_pieces(std::vector& mask) const + { + mask.resize(m_piece_map.size()); + std::vector::iterator j = mask.begin(); + for (std::vector::const_iterator i = m_piece_map.begin(), + end(m_piece_map.end()); i != end; ++i, ++j) + { + *j = i->filtered(); + } + } + + // ============ end deprecation ============== + + namespace + { + int append_blocks(std::vector& dst, std::vector& src + , int num_blocks) + { + if (src.empty()) return num_blocks; + int to_copy; +// if (prefer_whole_pieces == 0) + to_copy = (std::min)(int(src.size()), num_blocks); +// else +// to_copy = int(src.size()); + + dst.insert(dst.end() + , src.begin(), src.begin() + to_copy); + src.clear(); + return num_blocks - to_copy; + } + } + + // pieces describes which pieces the peer we're requesting from + // has. + // interesting_blocks is an out parameter, and will be filled + // with (up to) num_blocks of interesting blocks that the peer has. + // prefer_whole_pieces can be set if this peer should download + // whole pieces rather than trying to download blocks from the + // same piece as other peers. + // the void* is the pointer to the policy::peer of the peer we're + // picking pieces from. This is used when downloading whole pieces, + // to only pick from the same piece the same peer is downloading + // from. state is supposed to be set to fast if the peer is downloading + // relatively fast, by some notion. Slow peers will prefer not + // to pick blocks from the same pieces as fast peers, and vice + // versa. Downloading pieces are marked as being fast, medium + // or slow once they're started. + + // options are: + // * rarest_first + // pick the rarest pieces first + // * reverse + // reverse the piece picking. Pick the most common + // pieces first or the last pieces (if picking sequential) + // * sequential + // download pieces in-order + // * on_parole + // the peer is on parole, only pick whole pieces which + // has only been downloaded and requested from the same + // peer + // * prioritize_partials + // pick blocks from downloading pieces first + // * speed_affinity + // have an affinity to pick pieces in the same speed + // category. + // * ignore_whole_pieces + // ignores the prefer_whole_pieces parameter (as if + // it was 0) + + // only one of rarest_first, sequential can be set + + void piece_picker::pick_pieces(bitfield const& pieces + , std::vector& interesting_blocks, int num_blocks + , int prefer_whole_pieces, void* peer, piece_state_t speed + , int options, std::vector const& suggested_pieces + , int num_peers) const + { + TORRENT_ASSERT(peer == 0 || static_cast(peer)->in_use); + + // prevent the number of partial pieces to grow indefinitely + // make this scale by the number of peers we have. For large + // scale clients, we would have more peers, and allow a higher + // threshold for the number of partials + if (int(m_downloads.size()) > m_num_pad_files + num_peers * 3 / 2) options |= prioritize_partials; + + if (options & ignore_whole_pieces) prefer_whole_pieces = 0; + + // only one of rarest_first and sequential can be set. + TORRENT_ASSERT(((options & rarest_first) ? 1 : 0) + + ((options & sequential) ? 1 : 0) <= 1); +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + TORRENT_ASSERT(num_blocks > 0); + TORRENT_ASSERT(pieces.size() == m_piece_map.size()); + + TORRENT_ASSERT(!m_priority_boundries.empty() + || m_dirty); + + // this will be filled with blocks that we should not request + // unless we can't find num_blocks among the other ones. + // blocks that belong to pieces with a mismatching speed + // category for instance, or if we prefer whole pieces, + // blocks belonging to a piece that others have + // downloaded to + std::vector backup_blocks; + std::vector backup_blocks2; + const std::vector empty_vector; + + // When prefer_whole_pieces is set (usually set when downloading from + // fast peers) the partial pieces will not be prioritized, but actually + // ignored as long as possible. All blocks found in downloading + // pieces are regarded as backup blocks + + if (options & prioritize_partials) + { + for (std::vector::const_iterator i = m_downloads.begin() + , end(m_downloads.end()); i != end; ++i) + { + // in time critical mode, only pick prio 7 pieces + if ((options & time_critical_mode) && piece_priority(i->index) != 7) + continue; + + if (!is_piece_free(i->index, pieces)) continue; + if (m_piece_map[i->index].full + && int(backup_blocks.size()) >= num_blocks + && int(backup_blocks2.size()) >= num_blocks) + continue; + + num_blocks = add_blocks_downloading(*i, pieces + , interesting_blocks, backup_blocks, backup_blocks2 + , num_blocks, prefer_whole_pieces, peer, speed, options); + if (num_blocks <= 0) return; + } + + num_blocks = append_blocks(interesting_blocks, backup_blocks + , num_blocks); + if (num_blocks <= 0) return; + + num_blocks = append_blocks(interesting_blocks, backup_blocks2 + , num_blocks); + if (num_blocks <= 0) return; + } + + if (!suggested_pieces.empty()) + { + for (std::vector::const_iterator i = suggested_pieces.begin(); + i != suggested_pieces.end(); ++i) + { + // in time critical mode, only pick prio 7 pieces + if ((options & time_critical_mode) && piece_priority(*i) != 7) + continue; + + if (!is_piece_free(*i, pieces)) continue; + num_blocks = add_blocks(*i, pieces + , interesting_blocks, backup_blocks + , backup_blocks2, num_blocks + , prefer_whole_pieces, peer, empty_vector + , speed, options); + if (num_blocks <= 0) return; + } + } + + if (options & sequential) + { + if (m_dirty) update_pieces(); + TORRENT_ASSERT(!m_dirty); + + for (std::vector::const_iterator i = m_pieces.begin(); + i != m_pieces.end() && piece_priority(*i) == 7; ++i) + { + if (!is_piece_free(*i, pieces)) continue; + num_blocks = add_blocks(*i, pieces + , interesting_blocks, backup_blocks + , backup_blocks2, num_blocks + , prefer_whole_pieces, peer, suggested_pieces + , speed, options); + if (num_blocks <= 0) return; + } + + // in time critical mode, only pick prio 7 pieces + if ((options & time_critical_mode) == 0) + { + if (options & reverse) + { + for (int i = m_reverse_cursor - 1; i >= m_cursor; --i) + { + if (!is_piece_free(i, pieces)) continue; + // we've already added prio 7 pieces + if (piece_priority(i) == 7) continue; + num_blocks = add_blocks(i, pieces + , interesting_blocks, backup_blocks + , backup_blocks2, num_blocks + , prefer_whole_pieces, peer, suggested_pieces + , speed, options); + if (num_blocks <= 0) return; + } + } + else + { + for (int i = m_cursor; i < m_reverse_cursor; ++i) + { + if (!is_piece_free(i, pieces)) continue; + // we've already added prio 7 pieces + if (piece_priority(i) == 7) continue; + num_blocks = add_blocks(i, pieces + , interesting_blocks, backup_blocks + , backup_blocks2, num_blocks + , prefer_whole_pieces, peer, suggested_pieces + , speed, options); + if (num_blocks <= 0) return; + } + } + } + } + else if (options & rarest_first) + { + if (m_dirty) update_pieces(); + TORRENT_ASSERT(!m_dirty); + + // in time critical mode, we're only allowed to pick prio 7 + // pieces. This is why reverse mode is disabled when we're in + // time-critical mode, because all prio 7 pieces are at the front + // of the list + if ((options & reverse) && (options & time_critical_mode) == 0) + { + // it's a bit complicated in order to always prioritize + // partial pieces, and respect priorities. Every chunk + // of 4 priority levels are traversed in forward order, but otherwise + // they are traversed in reverse order + // round up to an even 4 priority boundry, to make it simpler + // to do the akward reverse traversing +#define div_round_up(n, d) (((n) + (d) - 1) / (d)) + m_priority_boundries.resize(div_round_up(m_priority_boundries.size() + , prio_factor) * prio_factor, m_priority_boundries.back()); + for (int i = m_priority_boundries.size() - 1; i >= 0; --i) + { + int prio = (i / prio_factor) * prio_factor + + prio_factor - 1 - (i % prio_factor); + + TORRENT_ASSERT(prio >= 0); + TORRENT_ASSERT(prio < int(m_priority_boundries.size())); + int start = prio == 0 ? 0 : m_priority_boundries[prio - 1]; + for (int p = start; p < m_priority_boundries[prio]; ++p) + { + if (!is_piece_free(m_pieces[p], pieces)) continue; + num_blocks = add_blocks(m_pieces[p], pieces + , interesting_blocks, backup_blocks + , backup_blocks2, num_blocks + , prefer_whole_pieces, peer, suggested_pieces + , speed, options); + if (num_blocks <= 0) return; + } + } +#undef div_round_up + } + else + { + for (std::vector::const_iterator i = m_pieces.begin(); + i != m_pieces.end(); ++i) + { + // in time critical mode, only pick prio 7 pieces + // it's safe to break here because in this mode we + // pick pieces in priority order. Once we hit a lower priority + // piece, we won't encounter any more prio 7 ones + if ((options & time_critical_mode) && piece_priority(*i) != 7) + break; + + if (!is_piece_free(*i, pieces)) continue; + num_blocks = add_blocks(*i, pieces + , interesting_blocks, backup_blocks + , backup_blocks2, num_blocks + , prefer_whole_pieces, peer, suggested_pieces + , speed, options); + if (num_blocks <= 0) return; + } + } + } + else if (options & time_critical_mode) + { + // if we're in time-critical mode, we are only allowed to pick + // prio 7 pieces. + for (std::vector::const_iterator i = m_pieces.begin(); + i != m_pieces.end() && piece_priority(*i) == 7; ++i) + { + if (!is_piece_free(*i, pieces)) continue; + num_blocks = add_blocks(*i, pieces + , interesting_blocks, backup_blocks + , backup_blocks2, num_blocks + , prefer_whole_pieces, peer, suggested_pieces + , speed, options); + if (num_blocks <= 0) return; + } + } + else + { + + // we're not using rarest first (only for the first + // bucket, since that's where the currently downloading + // pieces are) + int start_piece = random() % m_piece_map.size(); + + int piece = start_piece; + while (num_blocks > 0) + { + bool done = false; + // skip pieces we can't pick, and suggested pieces + // since we've already picked those + while (!can_pick(piece, pieces) + || std::find(suggested_pieces.begin() + , suggested_pieces.end(), piece) + != suggested_pieces.end()) + { + ++piece; + if (piece == int(m_piece_map.size())) piece = 0; + // could not find any more pieces + if (piece == start_piece) { done = true; break; } + } + if (done) break; + + TORRENT_ASSERT(can_pick(piece, pieces)); + TORRENT_ASSERT(m_piece_map[piece].downloading == false); + + int start, end; + boost::tie(start, end) = expand_piece(piece, prefer_whole_pieces, pieces); + for (int k = start; k < end; ++k) + { + TORRENT_ASSERT(m_piece_map[k].downloading == false); + TORRENT_ASSERT(m_piece_map[k].priority(this) >= 0); + int num_blocks_in_piece = blocks_in_piece(k); + if (prefer_whole_pieces == 0 && num_blocks_in_piece > num_blocks) + num_blocks_in_piece = num_blocks; + for (int j = 0; j < num_blocks_in_piece; ++j) + { + TORRENT_ASSERT(is_piece_free(k, pieces)); + interesting_blocks.push_back(piece_block(k, j)); + --num_blocks; + } + } + piece = end; + if (piece == int(m_piece_map.size())) piece = 0; + // could not find any more pieces + if (piece == start_piece) break; + } + + } + + if (num_blocks <= 0) return; + + // we might have to re-pick some backup blocks + // from full pieces, since we skipped those the + // first pass over + for (std::vector::const_iterator i = m_downloads.begin() + , end(m_downloads.end()); i != end; ++i) + { + if (!pieces[i->index]) continue; + // we've already considered the non-full pieces + if (!m_piece_map[i->index].full) continue; + std::vector temp; + add_blocks_downloading(*i, pieces + , temp, backup_blocks, backup_blocks2 + , num_blocks, prefer_whole_pieces, peer, speed, options); + } + +#if TORRENT_USE_INVARIANT_CHECKS + verify_pick(interesting_blocks, pieces); + verify_pick(backup_blocks, pieces); + verify_pick(backup_blocks2, pieces); +#endif + + std::vector temp; + for (std::vector::const_iterator i = m_downloads.begin() + , end(m_downloads.end()); i != end; ++i) + { + if (!pieces[i->index]) continue; + if (piece_priority(i->index) == 0) continue; + + int num_blocks_in_piece = blocks_in_piece(i->index); + + // fill in with blocks requested from other peers + // as backups + bool done = false; + for (int j = 0; j < num_blocks_in_piece; ++j) + { + block_info const& info = i->info[j]; + TORRENT_ASSERT(info.peer == 0 || static_cast(info.peer)->in_use); + TORRENT_ASSERT(info.piece_index == i->index); + if (info.state != block_info::state_requested + || info.peer == peer) + continue; + temp.push_back(piece_block(i->index, j)); + done = true; + } + if (done) break; + } + + num_blocks = append_blocks(interesting_blocks, backup_blocks + , num_blocks); + if (num_blocks <= 0) return; + + num_blocks = append_blocks(interesting_blocks, backup_blocks2, num_blocks); + if (num_blocks <= 0) return; + + // don't double-pick anything if the peer is on parole + if (options & on_parole) return; + + // pick one random block from the first busy piece we encountered + // none of these blocks have more than one request to them + if (!temp.empty()) interesting_blocks.push_back(temp[random() % temp.size()]); + +#ifdef TORRENT_DEBUG +// make sure that we at this point have added requests to all unrequested blocks +// in all downloading pieces + + for (std::vector::const_iterator i = m_downloads.begin() + , end(m_downloads.end()); i != end; ++i) + { + if (!pieces[i->index]) continue; + if (piece_priority(i->index) == 0) continue; + + if ((options & time_critical_mode) && piece_priority(i->index) != 7) + continue; + + int num_blocks_in_piece = blocks_in_piece(i->index); + for (int j = 0; j < num_blocks_in_piece; ++j) + { + block_info const& info = i->info[j]; + TORRENT_ASSERT(info.piece_index == i->index); + if (info.state != block_info::state_none) continue; + std::vector::iterator k = std::find( + interesting_blocks.begin(), interesting_blocks.end() + , piece_block(i->index, j)); + if (k != interesting_blocks.end()) continue; + + fprintf(stderr, "interesting blocks:\n"); + for (k = interesting_blocks.begin(); k != interesting_blocks.end(); ++k) + fprintf(stderr, "(%d, %d)", k->piece_index, k->block_index); + fprintf(stderr, "\nnum_blocks: %d\n", num_blocks); + + for (std::vector::const_iterator l = m_downloads.begin() + , end(m_downloads.end()); l != end; ++l) + { + fprintf(stderr, "%d : ", l->index); + int num_blocks_in_piece = blocks_in_piece(l->index); + for (int m = 0; m < num_blocks_in_piece; ++m) + fprintf(stderr, "%d", l->info[m].state); + fprintf(stderr, "\n"); + } + + TORRENT_ASSERT(false); + } + } + + if (interesting_blocks.empty() && !(options & time_critical_mode)) + { +// print_pieces(); + for (int i = 0; i < num_pieces(); ++i) + { + if (!pieces[i]) continue; + if (m_piece_map[i].priority(this) <= 0) continue; + if (have_piece(i)) continue; + + std::vector::const_iterator k = find_dl_piece(i); + + TORRENT_ASSERT(k != m_downloads.end()); + if (k == m_downloads.end()) continue; + + // this assert is not valid for web_seeds + /* + int num_blocks_in_piece = blocks_in_piece(k->index); + for (int j = 0; j < num_blocks_in_piece; ++j) + { + block_info const& info = k->info[j]; + TORRENT_ASSERT(info.piece_index == k->index); + if (info.state == block_info::state_finished) continue; + TORRENT_ASSERT(info.peer != 0); + } + */ + } + } +#endif + + } + + int piece_picker::blocks_in_piece(int index) const + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < (int)m_piece_map.size() || m_piece_map.empty()); + if (index+1 == (int)m_piece_map.size()) + return m_blocks_in_last_piece; + else + return m_blocks_per_piece; + } + + bool piece_picker::is_piece_free(int piece, bitfield const& bitmask) const + { + TORRENT_ASSERT(piece >= 0 && piece < int(m_piece_map.size())); + return bitmask[piece] + && !m_piece_map[piece].have() + && !m_piece_map[piece].filtered(); + } + + bool piece_picker::can_pick(int piece, bitfield const& bitmask) const + { + TORRENT_ASSERT(piece >= 0 && piece < int(m_piece_map.size())); + return bitmask[piece] + && !m_piece_map[piece].have() + && !m_piece_map[piece].downloading + && !m_piece_map[piece].filtered(); + } + + void piece_picker::clear_peer(void* peer) + { + for (std::vector::iterator i = m_block_info.begin() + , end(m_block_info.end()); i != end; ++i) + { + TORRENT_ASSERT(i->peer == 0 || static_cast(i->peer)->in_use); + if (i->peer == peer) i->peer = 0; + } + } + + namespace + { + // the first bool is true if this is the only peer that has requested and downloaded + // blocks from this piece. + // the second bool is true if this is the only active peer that is requesting + // and downloading blocks from this piece. Active means having a connection. + boost::tuple requested_from(piece_picker::downloading_piece const& p + , int num_blocks_in_piece, void* peer) + { + bool exclusive = true; + bool exclusive_active = true; + for (int j = 0; j < num_blocks_in_piece; ++j) + { + piece_picker::block_info const& info = p.info[j]; + TORRENT_ASSERT(info.peer == 0 || static_cast(info.peer)->in_use); + TORRENT_ASSERT(info.piece_index == p.index); + if (info.state != piece_picker::block_info::state_none + && info.peer != peer) + { + exclusive = false; + if (info.state == piece_picker::block_info::state_requested + && info.peer != 0) + { + exclusive_active = false; + return boost::make_tuple(exclusive, exclusive_active); + } + } + } + return boost::make_tuple(exclusive, exclusive_active); + } + } + + int piece_picker::add_blocks(int piece + , bitfield const& pieces + , std::vector& interesting_blocks + , std::vector& backup_blocks + , std::vector& backup_blocks2 + , int num_blocks, int prefer_whole_pieces + , void* peer, std::vector const& ignore + , piece_state_t speed, int options) const + { + TORRENT_ASSERT(piece >= 0); + TORRENT_ASSERT(piece < (int)m_piece_map.size()); + TORRENT_ASSERT(is_piece_free(piece, pieces)); + +// std::cout << "add_blocks(" << piece << ")" << std::endl; +// std::cout << " num_blocks " << num_blocks << std::endl; + + // ignore pieces found in the ignore list + if (std::find(ignore.begin(), ignore.end(), piece) != ignore.end()) return num_blocks; + + TORRENT_ASSERT(m_piece_map[piece].priority(this) >= 0); + if (m_piece_map[piece].downloading) + { + if (m_piece_map[piece].full) return num_blocks; + + // if we're prioritizing partials, we've already + // looked through the downloading pieces + if (options & prioritize_partials) return num_blocks; + + std::vector::const_iterator i = find_dl_piece(piece); + TORRENT_ASSERT(i != m_downloads.end()); + +// std::cout << "add_blocks_downloading(" << piece << ")" << std::endl; + + return add_blocks_downloading(*i, pieces + , interesting_blocks, backup_blocks, backup_blocks2 + , num_blocks, prefer_whole_pieces, peer, speed, options); + } + + int num_blocks_in_piece = blocks_in_piece(piece); + + // pick a new piece + if (prefer_whole_pieces == 0) + { + if (num_blocks_in_piece > num_blocks) + num_blocks_in_piece = num_blocks; + TORRENT_ASSERT(is_piece_free(piece, pieces)); + for (int j = 0; j < num_blocks_in_piece; ++j) + interesting_blocks.push_back(piece_block(piece, j)); + num_blocks -= num_blocks_in_piece; + } + else + { + int start, end; + boost::tie(start, end) = expand_piece(piece, prefer_whole_pieces, pieces); + for (int k = start; k < end; ++k) + { + TORRENT_ASSERT(m_piece_map[k].priority(this) > 0); + num_blocks_in_piece = blocks_in_piece(k); + TORRENT_ASSERT(is_piece_free(k, pieces)); + for (int j = 0; j < num_blocks_in_piece; ++j) + { + interesting_blocks.push_back(piece_block(k, j)); + --num_blocks; + } + } + } +#if TORRENT_USE_INVARIANT_CHECKS + verify_pick(interesting_blocks, pieces); +#endif + if (num_blocks <= 0) return 0; + return num_blocks; + } + + int piece_picker::add_blocks_downloading(downloading_piece const& dp + , bitfield const& pieces + , std::vector& interesting_blocks + , std::vector& backup_blocks + , std::vector& backup_blocks2 + , int num_blocks, int prefer_whole_pieces + , void* peer, piece_state_t speed, int options) const + { + if (!pieces[dp.index]) return num_blocks; + if (m_piece_map[dp.index].filtered()) return num_blocks; + + int num_blocks_in_piece = blocks_in_piece(dp.index); + + // if all blocks have been requested (and we don't need any backup + // blocks), we might as well return immediately +/* if (int(backup_blocks2.size()) >= num_blocks + && int(backup_blocks.size()) >= num_blocks + && dp.requested + dp.writing + dp.finished == num_blocks_in_piece) + return num_blocks; +*/ + // is true if all the other pieces that are currently + // requested from this piece are from the same + // peer as 'peer'. + bool exclusive; + bool exclusive_active; + boost::tie(exclusive, exclusive_active) + = requested_from(dp, num_blocks_in_piece, peer); + + // peers on parole are only allowed to pick blocks from + // pieces that only they have downloaded/requested from + if ((options & on_parole) && !exclusive) return num_blocks; + + // we prefer whole blocks, but there are other peers + // downloading from this piece, add it as backups + if (prefer_whole_pieces > 0 && !exclusive_active) + { + if (int(backup_blocks2.size()) >= num_blocks) + return num_blocks; + + for (int j = 0; j < num_blocks_in_piece; ++j) + { + // ignore completed blocks and already requested blocks + block_info const& info = dp.info[j]; + TORRENT_ASSERT(info.piece_index == dp.index); + if (info.state != block_info::state_none) continue; + backup_blocks2.push_back(piece_block(dp.index, j)); + } + return num_blocks; + } + + for (int j = 0; j < num_blocks_in_piece; ++j) + { + // ignore completed blocks and already requested blocks + block_info const& info = dp.info[j]; + TORRENT_ASSERT(info.piece_index == dp.index); + if (info.state != block_info::state_none) continue; + + // if the piece is fast and the peer is slow, or vice versa, + // add the block as a backup. + // override this behavior if all the other blocks + // have been requested from the same peer or + // if the state of the piece is none (the + // piece will in that case change state). + if (dp.state != none && dp.state != speed + && !exclusive_active && (options & speed_affinity)) + { + if (abs(dp.state - speed) == 1) + { + // don't pick too many back-up blocks + if (int(backup_blocks.size()) >= num_blocks) return num_blocks; + backup_blocks.push_back(piece_block(dp.index, j)); + } + else + { + // don't pick too many back-up blocks + if (int(backup_blocks2.size()) >= num_blocks) return num_blocks; + backup_blocks2.push_back(piece_block(dp.index, j)); + } + continue; + } + + // this block is interesting (we don't have it + // yet). + interesting_blocks.push_back(piece_block(dp.index, j)); + // we have found a block that's free to download + num_blocks--; + // if we prefer whole pieces, continue picking from this + // piece even though we have num_blocks + if (prefer_whole_pieces > 0) continue; + TORRENT_ASSERT(num_blocks >= 0); + if (num_blocks <= 0) return num_blocks; + } + + TORRENT_ASSERT(num_blocks >= 0 || prefer_whole_pieces > 0); + + if (num_blocks <= 0) return 0; + if (options & on_parole) return num_blocks; + + if (int(backup_blocks.size()) >= num_blocks) return num_blocks; + +#if TORRENT_USE_INVARIANT_CHECKS + verify_pick(backup_blocks, pieces); +#endif + return num_blocks; + } + + std::pair piece_picker::expand_piece(int piece, int whole_pieces + , bitfield const& have) const + { + if (whole_pieces == 0) return std::make_pair(piece, piece + 1); + + int start = piece - 1; + int lower_limit = piece - whole_pieces; + if (lower_limit < -1) lower_limit = -1; + while (start > lower_limit + && can_pick(start, have)) + --start; + ++start; + TORRENT_ASSERT(start >= 0); + int end = piece + 1; + int upper_limit = start + whole_pieces; + if (upper_limit > int(m_piece_map.size())) upper_limit = int(m_piece_map.size()); + while (end < upper_limit + && can_pick(end, have)) + ++end; + return std::make_pair(start, end); + } + + bool piece_picker::is_piece_finished(int index) const + { + TORRENT_ASSERT(index < (int)m_piece_map.size()); + TORRENT_ASSERT(index >= 0); + + if (m_piece_map[index].downloading == 0) + { + TORRENT_ASSERT(find_dl_piece(index) == m_downloads.end()); + return false; + } + std::vector::const_iterator i = find_dl_piece(index); + TORRENT_ASSERT(i != m_downloads.end()); + TORRENT_ASSERT((int)i->finished <= m_blocks_per_piece); + int max_blocks = blocks_in_piece(index); + if (int(i->finished) + int(i->writing) < max_blocks) return false; + TORRENT_ASSERT(int(i->finished) + int(i->writing) == max_blocks); + +#ifdef TORRENT_DEBUG + for (int k = 0; k < max_blocks; ++k) + { + TORRENT_ASSERT(i->info[k].piece_index == index); + TORRENT_ASSERT(i->info[k].state == block_info::state_finished + || i->info[k].state == block_info::state_writing); + } +#endif + + return true; + } + + std::vector::iterator piece_picker::find_dl_piece(int index) + { +// return std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index)); + downloading_piece cmp; + cmp.index = index; + std::vector::iterator i = std::lower_bound( + m_downloads.begin(), m_downloads.end(), cmp); + if (i == m_downloads.end()) return i; + if (i->index == index) return i; + return m_downloads.end(); + } + + std::vector::const_iterator piece_picker::find_dl_piece(int index) const + { +// return std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index)); + downloading_piece cmp; + cmp.index = index; + std::vector::const_iterator i = std::lower_bound( + m_downloads.begin(), m_downloads.end(), cmp); + if (i == m_downloads.end()) return i; + if (i->index == index) return i; + return m_downloads.end(); + } + + void piece_picker::update_full(downloading_piece& dp) + { + int num_blocks = blocks_in_piece(dp.index); + m_piece_map[dp.index].full = dp.requested + dp.finished + dp.writing == num_blocks; + } + + bool piece_picker::is_requested(piece_block block) const + { +#ifdef TORRENT_USE_VALGRIND + VALGRIND_CHECK_VALUE_IS_DEFINED(block); +#endif + TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); + TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); + TORRENT_ASSERT(block.piece_index < m_piece_map.size()); + + if (m_piece_map[block.piece_index].downloading == 0) return false; + std::vector::const_iterator i = find_dl_piece(block.piece_index); + + TORRENT_ASSERT(i != m_downloads.end()); + TORRENT_ASSERT(i->info >= &m_block_info[0] + && i->info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(i->info[block.block_index].piece_index == block.piece_index); + return i->info[block.block_index].state == block_info::state_requested; + } + + bool piece_picker::is_downloaded(piece_block block) const + { +#ifdef TORRENT_USE_VALGRIND + VALGRIND_CHECK_VALUE_IS_DEFINED(block); +#endif + TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); + TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); + TORRENT_ASSERT(block.piece_index < m_piece_map.size()); + + if (m_piece_map[block.piece_index].index == piece_pos::we_have_index) return true; + if (m_piece_map[block.piece_index].downloading == 0) return false; + std::vector::const_iterator i = find_dl_piece(block.piece_index); + TORRENT_ASSERT(i != m_downloads.end()); + TORRENT_ASSERT(i->info >= &m_block_info[0] + && i->info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(i->info[block.block_index].piece_index == block.piece_index); + return i->info[block.block_index].state == block_info::state_finished + || i->info[block.block_index].state == block_info::state_writing; + } + + bool piece_picker::is_finished(piece_block block) const + { +#ifdef TORRENT_USE_VALGRIND + VALGRIND_CHECK_VALUE_IS_DEFINED(block); +#endif + TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); + TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); + TORRENT_ASSERT(block.piece_index < m_piece_map.size()); + + piece_pos const& p = m_piece_map[block.piece_index]; + if (p.index == piece_pos::we_have_index) return true; + if (p.downloading == 0) return false; + std::vector::const_iterator i = find_dl_piece(block.piece_index); + TORRENT_ASSERT(i != m_downloads.end()); + TORRENT_ASSERT(i->info >= &m_block_info[0] + && i->info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(i->info[block.block_index].piece_index == block.piece_index); + return i->info[block.block_index].state == block_info::state_finished; + } + + bool piece_picker::mark_as_downloading(piece_block block + , void* peer, piece_state_t state) + { + TORRENT_ASSERT(peer == 0 || static_cast(peer)->in_use); + TORRENT_ASSERT(state != piece_picker::none); + TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); + TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); + TORRENT_ASSERT(block.piece_index < m_piece_map.size()); + TORRENT_ASSERT(int(block.block_index) < blocks_in_piece(block.piece_index)); + TORRENT_ASSERT(!m_piece_map[block.piece_index].have()); + + piece_pos& p = m_piece_map[block.piece_index]; + if (p.downloading == 0) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + int prio = p.priority(this); + TORRENT_ASSERT(prio < int(m_priority_boundries.size()) + || m_dirty); + p.downloading = 1; + if (prio >= 0 && !m_dirty) update(prio, p.index); + + downloading_piece& dp = add_download_piece(block.piece_index); + dp.state = state; + block_info& info = dp.info[block.block_index]; + TORRENT_ASSERT(info.piece_index == block.piece_index); + info.state = block_info::state_requested; + info.peer = peer; + info.num_peers = 1; + ++dp.requested; + update_full(dp); + } + else + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + std::vector::iterator i = find_dl_piece(block.piece_index); + TORRENT_ASSERT(i != m_downloads.end()); + block_info& info = i->info[block.block_index]; + TORRENT_ASSERT(&info >= &m_block_info[0]); + TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(info.piece_index == block.piece_index); + if (info.state == block_info::state_writing + || info.state == block_info::state_finished) + return false; + TORRENT_ASSERT(info.state == block_info::state_none + || (info.state == block_info::state_requested + && (info.num_peers > 0))); + info.peer = peer; + if (info.state != block_info::state_requested) + { + info.state = block_info::state_requested; + ++i->requested; + update_full(*i); + } + ++info.num_peers; + if (i->state == none) i->state = state; + } + return true; + } + + int piece_picker::num_peers(piece_block block) const + { + TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); + TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); + TORRENT_ASSERT(block.piece_index < m_piece_map.size()); + TORRENT_ASSERT(int(block.block_index) < blocks_in_piece(block.piece_index)); + + piece_pos const& p = m_piece_map[block.piece_index]; + if (!p.downloading) return 0; + + std::vector::const_iterator i = find_dl_piece(block.piece_index); + TORRENT_ASSERT(i != m_downloads.end()); + + block_info const& info = i->info[block.block_index]; + TORRENT_ASSERT(&info >= &m_block_info[0]); + TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(info.piece_index == block.piece_index); + return info.num_peers; + } + + void piece_picker::get_availability(std::vector& avail) const + { + TORRENT_ASSERT(m_seeds >= 0); + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + avail.resize(m_piece_map.size()); + std::vector::iterator j = avail.begin(); + for (std::vector::const_iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i, ++j) + *j = i->peer_count + m_seeds; + } + + bool piece_picker::mark_as_writing(piece_block block, void* peer) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + + TORRENT_ASSERT(peer == 0 || static_cast(peer)->in_use); + + TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); + TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); + TORRENT_ASSERT(block.piece_index < m_piece_map.size()); + TORRENT_ASSERT(int(block.block_index) < blocks_in_piece(block.piece_index)); + + piece_pos& p = m_piece_map[block.piece_index]; + if (p.downloading == 0) + { + // if we already have this piece, just ignore this + if (have_piece(block.piece_index)) return false; + +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + int prio = p.priority(this); + TORRENT_ASSERT(prio < int(m_priority_boundries.size()) + || m_dirty); + p.downloading = 1; + // prio being -1 can happen if a block is requested before + // the piece priority was set to 0 + if (prio >= 0 && !m_dirty) update(prio, p.index); + + downloading_piece& dp = add_download_piece(block.piece_index); + dp.state = none; + block_info& info = dp.info[block.block_index]; + TORRENT_ASSERT(&info >= &m_block_info[0]); + TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(info.piece_index == block.piece_index); + info.state = block_info::state_writing; + info.peer = peer; + info.num_peers = 0; + dp.writing = 1; + update_full(dp); +// sort_piece(m_downloads.end()-1); + } + else + { + std::vector::iterator i = find_dl_piece(block.piece_index); + TORRENT_ASSERT(i != m_downloads.end()); + block_info& info = i->info[block.block_index]; + + TORRENT_ASSERT(&info >= &m_block_info[0]); + TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(info.piece_index == block.piece_index); + + info.peer = peer; + if (info.state == block_info::state_requested) --i->requested; + TORRENT_ASSERT(i->requested >= 0); + if (info.state == block_info::state_writing + || info.state == block_info::state_finished) + return false; + + ++i->writing; + info.state = block_info::state_writing; + TORRENT_ASSERT(info.piece_index == block.piece_index); + + // all other requests for this block should have been + // cancelled now + info.num_peers = 0; + + if (i->requested == 0) + { + // there are no blocks requested in this piece. + // remove the fast/slow state from it + i->state = none; + } +// sort_piece(i); + } + return true; + } + + void piece_picker::write_failed(piece_block block) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + std::vector::iterator i = find_dl_piece(block.piece_index); + TORRENT_ASSERT(i != m_downloads.end()); + if (i == m_downloads.end()) return; + + block_info& info = i->info[block.block_index]; + TORRENT_ASSERT(&info >= &m_block_info[0]); + TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(info.piece_index == block.piece_index); + TORRENT_ASSERT(info.state == block_info::state_writing); + TORRENT_ASSERT(info.num_peers == 0); + + TORRENT_ASSERT(i->writing > 0); + TORRENT_ASSERT(info.state == block_info::state_writing); + + if (info.state == block_info::state_finished) return; + if (info.state == block_info::state_writing) --i->writing; + + info.peer = 0; + + info.state = block_info::state_none; + + update_full(*i); + + if (i->finished + i->writing + i->requested == 0) + { + piece_pos& p = m_piece_map[block.piece_index]; + int prev_priority = p.priority(this); + erase_download_piece(i); + int new_priority = p.priority(this); + + if (m_dirty) return; + if (new_priority == prev_priority) return; + if (prev_priority == -1) add(block.piece_index); + else update(prev_priority, p.index); + } + else + { +// sort_piece(i); + } + } + + void piece_picker::mark_as_finished(piece_block block, void* peer) + { + TORRENT_ASSERT(peer == 0 || static_cast(peer)->in_use); + TORRENT_ASSERT(block.piece_index >= 0); + TORRENT_ASSERT(block.block_index >= 0); + TORRENT_ASSERT(block.piece_index < m_piece_map.size()); + TORRENT_ASSERT(int(block.block_index) < blocks_in_piece(block.piece_index)); + + piece_pos& p = m_piece_map[block.piece_index]; + + if (p.downloading == 0) + { + // if we already have this piece, just ignore this + if (have_piece(block.piece_index)) return; + +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + + TORRENT_ASSERT(peer == 0); + int prio = p.priority(this); + TORRENT_ASSERT(prio < int(m_priority_boundries.size()) + || m_dirty); + p.downloading = 1; + if (prio >= 0 && !m_dirty) update(prio, p.index); + + downloading_piece& dp = add_download_piece(block.piece_index); + dp.state = none; + block_info& info = dp.info[block.block_index]; + TORRENT_ASSERT(&info >= &m_block_info[0]); + TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(info.piece_index == block.piece_index); + info.peer = peer; + TORRENT_ASSERT(info.state == block_info::state_none); + TORRENT_ASSERT(info.num_peers == 0); + if (info.state != block_info::state_finished) + { + ++dp.finished; +// sort_piece(m_downloads.end() - 1); + } + info.state = block_info::state_finished; + } + else + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + + std::vector::iterator i = find_dl_piece(block.piece_index); + TORRENT_ASSERT(i != m_downloads.end()); + block_info& info = i->info[block.block_index]; + TORRENT_ASSERT(&info >= &m_block_info[0]); + TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(info.piece_index == block.piece_index); + + if (info.state == block_info::state_finished) return; + + TORRENT_ASSERT(info.num_peers == 0); + + // peers may have been disconnected in between mark_as_writing + // and mark_as_finished. When a peer disconnects, its m_peer_info + // pointer is set to NULL. If so, preserve the previous peer + // pointer, instead of forgetting who we downloaded this block from + if (info.state != block_info::state_writing || peer != 0) + info.peer = peer; + + TORRENT_ASSERT(info.state == block_info::state_writing + || peer == 0); + TORRENT_ASSERT(i->writing >= 0); + ++i->finished; + if (info.state == block_info::state_writing) + { + --i->writing; + info.state = block_info::state_finished; + } + else + { + TORRENT_ASSERT(info.state == block_info::state_none); + info.state = block_info::state_finished; +// sort_piece(i); + } + } + } + + void piece_picker::get_downloaders(std::vector& d, int index) const + { + TORRENT_ASSERT(index >= 0 && index <= (int)m_piece_map.size()); + std::vector::const_iterator i = find_dl_piece(index); + TORRENT_ASSERT(i != m_downloads.end()); + TORRENT_ASSERT(i->info >= &m_block_info[0] + && i->info < &m_block_info[0] + m_block_info.size()); + + d.clear(); + for (int j = 0, end(blocks_in_piece(index)); j != end; ++j) + { + TORRENT_ASSERT(i->info[j].peer == 0 || static_cast(i->info[j].peer)->in_use); + d.push_back(i->info[j].peer); + } + } + + void* piece_picker::get_downloader(piece_block block) const + { + std::vector::const_iterator i = find_dl_piece(block.piece_index); + + if (i == m_downloads.end()) return 0; + + TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); + + TORRENT_ASSERT(i->info[block.block_index].piece_index == block.piece_index); + if (i->info[block.block_index].state == block_info::state_none) + return 0; + + void* peer = i->info[block.block_index].peer; + TORRENT_ASSERT(peer == 0 || static_cast(peer)->in_use); + return peer; + } + + // this is called when a request is rejected or when + // a peer disconnects. The piece might be in any state + void piece_picker::abort_download(piece_block block, void* peer) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + + TORRENT_ASSERT(peer == 0 || static_cast(peer)->in_use); + + TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); + TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); + TORRENT_ASSERT(block.piece_index < m_piece_map.size()); + TORRENT_ASSERT(int(block.block_index) < blocks_in_piece(block.piece_index)); + + if (m_piece_map[block.piece_index].downloading == 0) + { + TORRENT_ASSERT(find_dl_piece(block.piece_index) == m_downloads.end()); + return; + } + + std::vector::iterator i = find_dl_piece(block.piece_index); + TORRENT_ASSERT(i != m_downloads.end()); + + block_info& info = i->info[block.block_index]; + TORRENT_ASSERT(&info >= &m_block_info[0]); + TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(info.peer == 0 || static_cast(info.peer)->in_use); + TORRENT_ASSERT(info.piece_index == block.piece_index); + + TORRENT_ASSERT(info.state != block_info::state_none); + + if (info.state == block_info::state_finished + || info.state == block_info::state_none + || info.state == block_info::state_writing) + return; + + if (info.state == block_info::state_requested) + { + TORRENT_ASSERT(info.num_peers > 0); + if (info.num_peers > 0) --info.num_peers; + if (info.peer == peer) info.peer = 0; + + TORRENT_ASSERT(int(block.block_index) < blocks_in_piece(block.piece_index)); + + // if there are other peers, leave the block requested + if (info.num_peers > 0) return; + + // clear the downloader of this block + info.peer = 0; + + // clear this block as being downloaded + info.state = block_info::state_none; + --i->requested; + update_full(*i); + } + + // if there are no other blocks in this piece + // that's being downloaded, remove it from the list + if (i->requested + i->finished + i->writing == 0) + { + piece_pos& p = m_piece_map[block.piece_index]; + int prev_prio = p.priority(this); + TORRENT_ASSERT(prev_prio < int(m_priority_boundries.size()) + || m_dirty); + erase_download_piece(i); + if (!m_dirty) + { + int prio = p.priority(this); + if (prev_prio == -1 && prio >= 0) add(block.piece_index); + else if (prev_prio >= 0) update(prev_prio, p.index); + } + + TORRENT_ASSERT(find_dl_piece(block.piece_index) == m_downloads.end()); + } + else if (i->requested == 0) + { + // there are no blocks requested in this piece. + // remove the fast/slow state from it + i->state = none; + } + } + + int piece_picker::unverified_blocks() const + { + int counter = 0; + for (std::vector::const_iterator i = m_downloads.begin(); + i != m_downloads.end(); ++i) + { + counter += (int)i->finished; + } + return counter; + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/policy.cpp b/apps/Launcher/ext/libtorrent/src/policy.cpp new file mode 100644 index 0000000000..396a9eb8a4 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/policy.cpp @@ -0,0 +1,2029 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/web_peer_connection.hpp" +#include "libtorrent/policy.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/time.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/piece_picker.hpp" +#include "libtorrent/broadcast_socket.hpp" +#include "libtorrent/peer_info.hpp" +#include "libtorrent/random.hpp" +#include "libtorrent/extensions.hpp" + +#ifdef TORRENT_DEBUG +#include "libtorrent/bt_peer_connection.hpp" +#endif + +namespace +{ + using namespace libtorrent; + + struct match_peer_endpoint + { + match_peer_endpoint(tcp::endpoint const& ep) + : m_ep(ep) + {} + + bool operator()(policy::peer const* p) const + { + TORRENT_ASSERT(p->in_use); + return p->address() == m_ep.address() && p->port == m_ep.port(); + } + + tcp::endpoint const& m_ep; + }; + +#if TORRENT_USE_ASSERTS + struct match_peer_connection + { + match_peer_connection(peer_connection const& c) : m_conn(c) {} + + bool operator()(policy::peer const* p) const + { + TORRENT_ASSERT(p->in_use); + return p->connection == &m_conn; + } + + peer_connection const& m_conn; + }; + + struct match_peer_connection_or_endpoint + { + match_peer_connection_or_endpoint(peer_connection const& c) : m_conn(c) {} + + bool operator()(policy::peer const* p) const + { + TORRENT_ASSERT(p->in_use); + return p->connection == &m_conn + || (p->ip() == m_conn.remote() + && p->connectable); + } + + peer_connection const& m_conn; + }; +#endif + +} + +namespace libtorrent +{ + + void apply_mask(boost::uint8_t* b, boost::uint8_t const* mask, int size) + { + for (int i = 0; i < size; ++i) + { + *b &= *mask; + ++b; + ++mask; + } + } + + // 1. if the IP addresses are identical, hash the ports in 16 bit network-order + // binary representation, ordered lowest first. + // 2. if the IPs are in the same /24, hash the IPs ordered, lowest first. + // 3. if the IPs are in the ame /16, mask the IPs by 0xffffff55, hash them + // ordered, lowest first. + // 4. if IPs are not in the same /16, mask the IPs by 0xffff5555, hash them + // ordered, lowest first. + // + // * for IPv6 peers, just use the first 64 bits and widen the masks. + // like this: 0xffff5555 -> 0xffffffff55555555 + // the lower 64 bits are always unmasked + // + // * for IPv6 addresses, compare /32 and /48 instead of /16 and /24 + // + // * the two IP addresses that are used to calculate the rank must + // always be of the same address family + // + // * all IP addresses are in network byte order when hashed + boost::uint32_t peer_priority(tcp::endpoint e1, tcp::endpoint e2) + { + TORRENT_ASSERT(e1.address().is_v4() == e2.address().is_v4()); + + using std::swap; + + // this is the crc32c (Castagnoli) polynomial + boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc; + + if (e1.address() == e2.address()) + { + if (e1.port() > e2.port()) + swap(e1, e2); + boost::uint16_t p[2]; + p[0] = htons(e1.port()); + p[1] = htons(e2.port()); + crc.process_bytes((char const*)&p[0], 4); + } +#if TORRENT_USE_IPV6 + else if (e1.address().is_v6()) + { + const static boost::uint8_t v6mask[][8] = { + { 0xff, 0xff, 0xff, 0xff, 0x55, 0x55, 0x55, 0x55 }, + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x55, 0x55 }, + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } + }; + + if (e2 < e1) swap(e1, e2); + address_v6::bytes_type b1 = e1.address().to_v6().to_bytes(); + address_v6::bytes_type b2 = e2.address().to_v6().to_bytes(); + int mask = memcmp(&b1[0], &b2[0], 4) ? 0 + : memcmp(&b1[0], &b2[0], 6) ? 1 : 2; + apply_mask(&b1[0], v6mask[mask], 8); + apply_mask(&b2[0], v6mask[mask], 8); + + crc.process_bytes((char const*)&b1[0], 16); + crc.process_bytes((char const*)&b2[0], 16); + } +#endif + else + { + const static boost::uint8_t v4mask[][4] = { + { 0xff, 0xff, 0x55, 0x55 }, + { 0xff, 0xff, 0xff, 0x55 }, + { 0xff, 0xff, 0xff, 0xff } + }; + + if (e2 < e1) swap(e1, e2); + address_v4::bytes_type b1 = e1.address().to_v4().to_bytes(); + address_v4::bytes_type b2 = e2.address().to_v4().to_bytes(); + int mask = memcmp(&b1[0], &b2[0], 2) ? 0 + : memcmp(&b1[0], &b2[0], 3) ? 1 : 2; + apply_mask(&b1[0], v4mask[mask], 4); + apply_mask(&b2[0], v4mask[mask], 4); + + crc.process_bytes((char const*)&b1[0], 4); + crc.process_bytes((char const*)&b2[0], 4); + } + + return crc.checksum(); + } + + // returns the rank of a peer's source. We have an affinity + // to connecting to peers with higher rank. This is to avoid + // problems when our peer list is diluted by stale peers from + // the resume data for instance + int source_rank(int source_bitmask) + { + int ret = 0; + if (source_bitmask & peer_info::tracker) ret |= 1 << 5; + if (source_bitmask & peer_info::lsd) ret |= 1 << 4; + if (source_bitmask & peer_info::dht) ret |= 1 << 3; + if (source_bitmask & peer_info::pex) ret |= 1 << 2; + return ret; + } + + // the case where ignore_peer is motivated is if two peers + // have only one piece that we don't have, and it's the + // same piece for both peers. Then they might get into an + // infinite loop, fighting to request the same blocks. + void request_a_block(torrent& t, peer_connection& c) + { + if (t.is_seed()) return; + if (c.no_download()) return; + if (t.upload_mode()) return; + if (c.is_disconnecting()) return; + + // don't request pieces before we have the metadata + if (!t.valid_metadata()) return; + + // don't request pieces before the peer is properly + // initialized after we have the metadata + if (!t.are_files_checked()) return; + + TORRENT_ASSERT(t.valid_metadata()); + TORRENT_ASSERT(c.peer_info_struct() != 0 || c.type() != peer_connection::bittorrent_connection); + + bool time_critical_mode = t.num_time_critical_pieces() > 0; + + int desired_queue_size = c.desired_queue_size(); + + // in time critical mode, only have 1 outstanding request at a time + // via normal requests + if (time_critical_mode) + desired_queue_size = (std::min)(1, desired_queue_size); + + int num_requests = desired_queue_size + - (int)c.download_queue().size() + - (int)c.request_queue().size(); + +#ifdef TORRENT_VERBOSE_LOGGING + c.peer_log("*** PIECE_PICKER [ req: %d engame: %d ]", num_requests, c.endgame()); +#endif + TORRENT_ASSERT(c.desired_queue_size() > 0); + // if our request queue is already full, we + // don't have to make any new requests yet + if (num_requests <= 0) return; + + piece_picker& p = t.picker(); + std::vector interesting_pieces; + interesting_pieces.reserve(100); + + int prefer_whole_pieces = c.prefer_whole_pieces(); + + if (prefer_whole_pieces == 0 && !time_critical_mode) + { + prefer_whole_pieces = c.statistics().download_payload_rate() + * t.settings().whole_pieces_threshold + > t.torrent_file().piece_length() ? 1 : 0; + } + + // if we prefer whole pieces, the piece picker will pick at least + // the number of blocks we want, but it will try to make the picked + // blocks be from whole pieces, possibly by returning more blocks + // than we requested. +#ifdef TORRENT_DEBUG + error_code ec; + TORRENT_ASSERT(c.remote() == c.get_socket()->remote_endpoint(ec) || ec); +#endif + + aux::session_impl& ses = t.session(); + + std::vector const& dq = c.download_queue(); + std::vector const& rq = c.request_queue(); + + std::vector const& suggested = c.suggested_pieces(); + bitfield const* bits = &c.get_bitfield(); + bitfield fast_mask; + + if (c.has_peer_choked()) + { + // if we are choked we can only pick pieces from the + // allowed fast set. The allowed fast set is sorted + // in ascending priority order + std::vector const& allowed_fast = c.allowed_fast(); + + // build a bitmask with only the allowed pieces in it + fast_mask.resize(c.get_bitfield().size(), false); + for (std::vector::const_iterator i = allowed_fast.begin() + , end(allowed_fast.end()); i != end; ++i) + if ((*bits)[*i]) fast_mask.set_bit(*i); + bits = &fast_mask; + } + + piece_picker::piece_state_t state; + peer_connection::peer_speed_t speed = c.peer_speed(); + if (speed == peer_connection::fast) state = piece_picker::fast; + else if (speed == peer_connection::medium) state = piece_picker::medium; + else state = piece_picker::slow; + + // picks the interesting pieces from this peer + // the integer is the number of pieces that + // should be guaranteed to be available for download + // (if num_requests is too big, too many pieces are + // picked and cpu-time is wasted) + // the last argument is if we should prefer whole pieces + // for this peer. If we're downloading one piece in 20 seconds + // then use this mode. + p.pick_pieces(*bits, interesting_pieces + , num_requests, prefer_whole_pieces, c.peer_info_struct() + , state, c.picker_options(), suggested, t.num_peers()); + +#ifdef TORRENT_VERBOSE_LOGGING + c.peer_log("*** PIECE_PICKER [ prefer_whole: %d picked: %d ]" + , prefer_whole_pieces, int(interesting_pieces.size())); +#endif + + // if the number of pieces we have + the number of pieces + // we're requesting from is less than the number of pieces + // in the torrent, there are still some unrequested pieces + // and we're not strictly speaking in end-game mode yet + // also, if we already have at least one outstanding + // request, we shouldn't pick any busy pieces either + // in time critical mode, it's OK to request busy blocks + bool dont_pick_busy_blocks = ((ses.m_settings.strict_end_game_mode + && p.num_downloading_pieces() < p.num_want_left()) + || dq.size() + rq.size() > 0) + && !time_critical_mode; + + // this is filled with an interesting piece + // that some other peer is currently downloading + piece_block busy_block = piece_block::invalid; + + for (std::vector::iterator i = interesting_pieces.begin(); + i != interesting_pieces.end(); ++i) + { +#ifdef TORRENT_STATS + ++ses.m_piece_picker_blocks; +#endif + + if (prefer_whole_pieces == 0 && num_requests <= 0) break; + + if (time_critical_mode && p.piece_priority(i->piece_index) != 7) + { + // assume the subsequent pieces are not prio 7 and + // be done + break; + } + + int num_block_requests = p.num_peers(*i); + if (num_block_requests > 0) + { + // have we picked enough pieces? + if (num_requests <= 0) break; + + // this block is busy. This means all the following blocks + // in the interesting_pieces list are busy as well, we might + // as well just exit the loop + if (dont_pick_busy_blocks) break; + + TORRENT_ASSERT(p.num_peers(*i) > 0); + busy_block = *i; + continue; + } + + TORRENT_ASSERT(p.num_peers(*i) == 0); + + // don't request pieces we already have in our request queue + // This happens when pieces time out or the peer sends us + // pieces we didn't request. Those aren't marked in the + // piece picker, but we still keep track of them in the + // download queue + if (std::find_if(dq.begin(), dq.end(), has_block(*i)) != dq.end() + || std::find_if(rq.begin(), rq.end(), has_block(*i)) != rq.end()) + { +#ifdef TORRENT_DEBUG + std::vector::const_iterator j + = std::find_if(dq.begin(), dq.end(), has_block(*i)); + if (j != dq.end()) TORRENT_ASSERT(j->timed_out || j->not_wanted); +#endif + continue; + } + + // ok, we found a piece that's not being downloaded + // by somebody else. request it from this peer + // and return + if (!c.add_request(*i, 0)) continue; + TORRENT_ASSERT(p.num_peers(*i) == 1); + TORRENT_ASSERT(p.is_requested(*i)); + num_requests--; + } + + // we have picked as many blocks as we should + // we're done! + if (num_requests <= 0) + { + // since we could pick as many blocks as we + // requested without having to resort to picking + // busy ones, we're not in end-game mode + c.set_endgame(false); + return; + } + + // we did not pick as many pieces as we wanted, because + // there aren't enough. This means we're in end-game mode + // as long as we have at least one request outstanding, + // we shouldn't pick another piece + // if we are attempting to download 'allowed' pieces + // and can't find any, that doesn't count as end-game + if (!c.has_peer_choked()) + c.set_endgame(true); + + // if we don't have any potential busy blocks to request + // or if we already have outstanding requests, don't + // pick a busy piece + if (busy_block == piece_block::invalid + || dq.size() + rq.size() > 0) + { + return; + } + +#ifdef TORRENT_STATS + ++ses.m_end_game_piece_picker_blocks; +#endif + +#ifdef TORRENT_DEBUG + piece_picker::downloading_piece st; + p.piece_info(busy_block.piece_index, st); + TORRENT_ASSERT(st.requested + st.finished + st.writing + == p.blocks_in_piece(busy_block.piece_index)); +#endif + TORRENT_ASSERT(p.is_requested(busy_block)); + TORRENT_ASSERT(!p.is_downloaded(busy_block)); + TORRENT_ASSERT(!p.is_finished(busy_block)); + TORRENT_ASSERT(p.num_peers(busy_block) > 0); + + c.add_request(busy_block, peer_connection::req_busy); + } + + policy::policy(torrent* t) + : m_torrent(t) + , m_locked_peer(NULL) + , m_round_robin(0) + , m_num_connect_candidates(0) + , m_num_seeds(0) + , m_finished(false) + { TORRENT_ASSERT(t); } + + void policy::clear_peer_prio() + { + for (peers_t::iterator i = m_peers.begin() + , end(m_peers.end()); i != end; ++i) + (*i)->peer_rank = 0; + } + + // disconnects and removes all peers that are now filtered + void policy::ip_filter_updated() + { + INVARIANT_CHECK; + + aux::session_impl& ses = m_torrent->session(); + if (!m_torrent->apply_ip_filter()) return; + + for (iterator i = m_peers.begin(); i != m_peers.end();) + { + if ((ses.m_ip_filter.access((*i)->address()) & ip_filter::blocked) == 0) + { + ++i; + continue; + } + + if (*i == m_locked_peer) + { + ++i; + continue; + } + + if (ses.m_alerts.should_post()) + ses.m_alerts.post_alert(peer_blocked_alert(m_torrent->get_handle() + , (*i)->address(), peer_blocked_alert::ip_filter)); + + int current = i - m_peers.begin(); + TORRENT_ASSERT(current >= 0); + TORRENT_ASSERT(m_peers.size() > 0); + TORRENT_ASSERT(i != m_peers.end()); + + if ((*i)->connection) + { + // disconnecting the peer here may also delete the + // peer_info_struct. If that is the case, just continue + int count = m_peers.size(); + peer_connection* p = (*i)->connection; + + p->disconnect(errors::banned_by_ip_filter); + // what *i refers to has changed, i.e. cur was deleted + if (int(m_peers.size()) < count) + { + i = m_peers.begin() + current; + continue; + } + TORRENT_ASSERT((*i)->connection == 0 + || (*i)->connection->peer_info_struct() == 0); + } + + erase_peer(i); + i = m_peers.begin() + current; + } + } + + void policy::erase_peer(policy::peer* p) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(p->in_use); + + std::pair range = find_peers(p->address()); + iterator iter = std::find_if(range.first, range.second, match_peer_endpoint(p->ip())); + if (iter == range.second) return; + erase_peer(iter); + } + + // any peer that is erased from m_peers will be + // erased through this function. This way we can make + // sure that any references to the peer are removed + // as well, such as in the piece picker. + void policy::erase_peer(iterator i) + { + INVARIANT_CHECK; + TORRENT_ASSERT(i != m_peers.end()); + TORRENT_ASSERT(m_locked_peer != *i); + + if (m_torrent->has_picker()) + m_torrent->picker().clear_peer(*i); + if ((*i)->seed) --m_num_seeds; + if (is_connect_candidate(**i, m_finished)) + { + TORRENT_ASSERT(m_num_connect_candidates > 0); + --m_num_connect_candidates; + } + TORRENT_ASSERT(m_num_connect_candidates < int(m_peers.size())); + if (m_round_robin > i - m_peers.begin()) --m_round_robin; + if (m_round_robin >= int(m_peers.size())) m_round_robin = 0; + +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT((*i)->in_use); + (*i)->in_use = false; +#endif + +#if TORRENT_USE_IPV6 + if ((*i)->is_v6_addr) + { + TORRENT_ASSERT(m_torrent->session().m_ipv6_peer_pool.is_from( + static_cast(*i))); + m_torrent->session().m_ipv6_peer_pool.destroy( + static_cast(*i)); + } + else +#endif +#if TORRENT_USE_I2P + if ((*i)->is_i2p_addr) + { + TORRENT_ASSERT(m_torrent->session().m_i2p_peer_pool.is_from( + static_cast(*i))); + m_torrent->session().m_i2p_peer_pool.destroy( + static_cast(*i)); + } + else +#endif + { + TORRENT_ASSERT(m_torrent->session().m_ipv4_peer_pool.is_from( + static_cast(*i))); + m_torrent->session().m_ipv4_peer_pool.destroy( + static_cast(*i)); + } + m_peers.erase(i); + } + + bool policy::should_erase_immediately(peer const& p) const + { + TORRENT_ASSERT(p.in_use); + if (&p == m_locked_peer) return false; + return p.source == peer_info::resume_data; + } + + bool policy::is_erase_candidate(peer const& pe, bool finished) const + { + TORRENT_ASSERT(pe.in_use); + if (&pe == m_locked_peer) return false; + if (pe.connection) return false; + if (is_connect_candidate(pe, finished)) return false; + + return (pe.failcount > 0) + || (pe.source == peer_info::resume_data); + } + + bool policy::is_force_erase_candidate(peer const& pe) const + { + TORRENT_ASSERT(pe.in_use); + if (&pe == m_locked_peer) return false; + return pe.connection == 0; + } + + void policy::erase_peers(int flags) + { + INVARIANT_CHECK; + + int max_peerlist_size = m_torrent->is_paused() + ? m_torrent->settings().max_paused_peerlist_size + : m_torrent->settings().max_peerlist_size; + + if (max_peerlist_size == 0 || m_peers.empty()) return; + + int erase_candidate = -1; + int force_erase_candidate = -1; + + TORRENT_ASSERT(m_finished == m_torrent->is_finished()); + + int round_robin = random() % m_peers.size(); + + int low_watermark = max_peerlist_size * 95 / 100; + if (low_watermark == max_peerlist_size) --low_watermark; + + for (int iterations = (std::min)(int(m_peers.size()), 300); + iterations > 0; --iterations) + { + if (int(m_peers.size()) < low_watermark) + break; + + if (round_robin == int(m_peers.size())) round_robin = 0; + + peer& pe = *m_peers[round_robin]; + TORRENT_ASSERT(pe.in_use); + int current = round_robin; + + if (is_erase_candidate(pe, m_finished) + && (erase_candidate == -1 + || !compare_peer_erase(*m_peers[erase_candidate], pe))) + { + if (should_erase_immediately(pe)) + { + if (erase_candidate > current) --erase_candidate; + if (force_erase_candidate > current) --force_erase_candidate; + TORRENT_ASSERT(current >= 0 && current < int(m_peers.size())); + erase_peer(m_peers.begin() + current); + continue; + } + else + { + erase_candidate = current; + } + } + if (is_force_erase_candidate(pe) + && (force_erase_candidate == -1 + || !compare_peer_erase(*m_peers[force_erase_candidate], pe))) + { + force_erase_candidate = current; + } + + ++round_robin; + } + + if (erase_candidate > -1) + { + TORRENT_ASSERT(erase_candidate >= 0 && erase_candidate < int(m_peers.size())); + erase_peer(m_peers.begin() + erase_candidate); + } + else if ((flags & force_erase) && force_erase_candidate > -1) + { + TORRENT_ASSERT(force_erase_candidate >= 0 && force_erase_candidate < int(m_peers.size())); + erase_peer(m_peers.begin() + force_erase_candidate); + } + } + + void policy::ban_peer(policy::peer* p) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(p->in_use); + + if (!m_torrent->settings().ban_web_seeds && p->web_seed) + return; + + if (is_connect_candidate(*p, m_finished)) + --m_num_connect_candidates; + +#ifdef TORRENT_STATS + aux::session_impl& ses = m_torrent->session(); + ++ses.m_num_banned_peers; +#endif + + p->banned = true; + TORRENT_ASSERT(!is_connect_candidate(*p, m_finished)); + } + + void policy::set_connection(policy::peer* p, peer_connection* c) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(p->in_use); + TORRENT_ASSERT(c); + + const bool was_conn_cand = is_connect_candidate(*p, m_finished); + p->connection = c; + if (was_conn_cand) --m_num_connect_candidates; + } + + void policy::set_failcount(policy::peer* p, int f) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(p->in_use); + const bool was_conn_cand = is_connect_candidate(*p, m_finished); + p->failcount = f; + if (was_conn_cand != is_connect_candidate(*p, m_finished)) + { + if (was_conn_cand) --m_num_connect_candidates; + else ++m_num_connect_candidates; + } + } + + bool policy::is_connect_candidate(peer const& p, bool finished) const + { + TORRENT_ASSERT(p.in_use); + if (p.connection + || p.banned + || p.web_seed + || !p.connectable + || (p.seed && finished) + || int(p.failcount) >= m_torrent->settings().max_failcount) + return false; + + aux::session_impl const& ses = m_torrent->session(); + if (ses.m_port_filter.access(p.port) & port_filter::blocked) + return false; + + // only apply this to peers we've only heard + // about from the DHT + if (ses.m_settings.no_connect_privileged_ports + && p.port < 1024 + && p.source == peer_info::dht) + return false; + + return true; + } + + policy::iterator policy::find_connect_candidate(int session_time) + { + INVARIANT_CHECK; + + int candidate = -1; + int erase_candidate = -1; + + TORRENT_ASSERT(m_finished == m_torrent->is_finished()); + + int min_reconnect_time = m_torrent->settings().min_reconnect_time; + external_ip const& external = m_torrent->session().external_address(); + int external_port = m_torrent->session().listen_port(); + + if (m_round_robin >= int(m_peers.size())) m_round_robin = 0; + +#ifndef TORRENT_DISABLE_DHT + bool pinged = false; +#endif + + int max_peerlist_size = m_torrent->is_paused() + ?m_torrent->settings().max_paused_peerlist_size + :m_torrent->settings().max_peerlist_size; + + for (int iterations = (std::min)(int(m_peers.size()), 300); + iterations > 0; --iterations) + { + if (m_round_robin >= int(m_peers.size())) m_round_robin = 0; + + peer& pe = *m_peers[m_round_robin]; + TORRENT_ASSERT(pe.in_use); + int current = m_round_robin; + +#ifndef TORRENT_DISABLE_DHT + // try to send a DHT ping to this peer + // as well, to figure out if it supports + // DHT (uTorrent and BitComet doesn't + // advertise support) + if (!pinged && !pe.added_to_dht) + { + udp::endpoint node(pe.address(), pe.port); + m_torrent->session().add_dht_node(node); + pe.added_to_dht = true; + pinged = true; + } +#endif + // if the number of peers is growing large + // we need to start weeding. + + if (int(m_peers.size()) >= max_peerlist_size * 0.95 + && max_peerlist_size > 0) + { + if (is_erase_candidate(pe, m_finished) + && (erase_candidate == -1 + || !compare_peer_erase(*m_peers[erase_candidate], pe))) + { + if (should_erase_immediately(pe)) + { + if (erase_candidate > current) --erase_candidate; + if (candidate > current) --candidate; + erase_peer(m_peers.begin() + current); + continue; + } + else + { + erase_candidate = current; + } + } + } + + ++m_round_robin; + + if (!is_connect_candidate(pe, m_finished)) continue; + + // compare peer returns true if lhs is better than rhs. In this + // case, it returns true if the current candidate is better than + // pe, which is the peer m_round_robin points to. If it is, just + // keep looking. + if (candidate != -1 + && compare_peer(*m_peers[candidate], pe, external, external_port)) continue; + + if (pe.last_connected + && session_time - pe.last_connected < + (int(pe.failcount) + 1) * min_reconnect_time) + continue; + + candidate = current; + } + + if (erase_candidate > -1) + { + if (candidate > erase_candidate) --candidate; + erase_peer(m_peers.begin() + erase_candidate); + } + +#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING + if (candidate != -1) + { + (*m_torrent->session().m_logger) << time_now_string() + << " *** FOUND CONNECTION CANDIDATE [" + " ip: " << m_peers[candidate]->ip() << + " d: " << cidr_distance(external.external_address(m_peers[candidate]->address()), m_peers[candidate]->address()) << + " rank: " << m_peers[candidate]->rank(external, external_port) << + " external: " << external.external_address(m_peers[candidate]->address()) << + " t: " << (session_time - m_peers[candidate]->last_connected) << + " ]\n"; + } +#endif + + if (candidate == -1) return m_peers.end(); + return m_peers.begin() + candidate; + } + + bool policy::new_connection(peer_connection& c, int session_time) + { + TORRENT_ASSERT(!c.is_outgoing()); + + INVARIANT_CHECK; + + // if the connection comes from the tracker, + // it's probably just a NAT-check. Ignore the + // num connections constraint then. + + // TODO: only allow _one_ connection to use this + // override at a time + error_code ec; + TORRENT_ASSERT(c.remote() == c.get_socket()->remote_endpoint(ec) || ec); + TORRENT_ASSERT(!m_torrent->is_paused()); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + if (c.remote().address() == m_torrent->current_tracker().address()) + { + m_torrent->debug_log("overriding connection limit for tracker NAT-check"); + } +#endif + + iterator iter; + peer* i = 0; + + bool found = false; + if (m_torrent->settings().allow_multiple_connections_per_ip) + { + tcp::endpoint remote = c.remote(); + std::pair range = find_peers(remote.address()); + iter = std::find_if(range.first, range.second, match_peer_endpoint(remote)); + + if (iter != range.second) + { + TORRENT_ASSERT((*iter)->in_use); + found = true; + } + } + else + { + iter = std::lower_bound( + m_peers.begin(), m_peers.end() + , c.remote().address(), peer_address_compare() + ); + + if (iter != m_peers.end() && (*iter)->address() == c.remote().address()) + { + TORRENT_ASSERT((*iter)->in_use); + found = true; + } + } + + // make sure the iterator we got is properly sorted relative + // to the connection's address +// TORRENT_ASSERT(m_peers.empty() +// || (iter == m_peers.end() && (*(iter-1))->address() < c.remote().address()) +// || (iter != m_peers.end() && c.remote().address() < (*iter)->address()) +// || (iter != m_peers.end() && iter != m_peers.begin() && (*(iter-1))->address() < c.remote().address())); + +#if !defined TORRENT_DISABLE_GEO_IP || TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING + aux::session_impl& ses = m_torrent->session(); +#endif + + if (found) + { + i = *iter; + TORRENT_ASSERT(i->in_use); + TORRENT_ASSERT(i->connection != &c); + TORRENT_ASSERT(i->address() == c.remote().address()); + +#ifdef TORRENT_VERBOSE_LOGGING + c.peer_log("*** DUPLICATE PEER [ this: \"%s\" that: \"%s\" ]" + , print_address(c.remote().address()).c_str() + , print_address(i->address()).c_str()); +#endif + if (i->banned) + { + c.disconnect(errors::peer_banned); + return false; + } + + if (i->connection != 0) + { + boost::shared_ptr other_socket + = i->connection->get_socket(); + boost::shared_ptr this_socket + = c.get_socket(); + + error_code ec1; + error_code ec2; + bool self_connection = + other_socket->remote_endpoint(ec2) == this_socket->local_endpoint(ec1) + || other_socket->local_endpoint(ec2) == this_socket->remote_endpoint(ec1); + + if (ec1) + { + c.disconnect(ec1); + return false; + } + + if (self_connection) + { + c.disconnect(errors::self_connection, 1); + i->connection->disconnect(errors::self_connection, 1); + TORRENT_ASSERT(i->connection == 0); + return false; + } + + TORRENT_ASSERT(i->connection != &c); + // the new connection is a local (outgoing) connection + // or the current one is already connected + if (ec2) + { + TORRENT_ASSERT(m_locked_peer == NULL); + m_locked_peer = i; + i->connection->disconnect(ec2); + TORRENT_ASSERT(i->connection == 0); + m_locked_peer = NULL; + } + else if (i->connection->is_outgoing() == c.is_outgoing()) + { + // if the other end connected to us both times, just drop + // the second one. Or if we made both connections. + c.disconnect(errors::duplicate_peer_id); + return false; + } + else + { + // at this point, we need to disconnect either + // i->connection or c. In order for both this client + // and the client on the other end to decide to + // disconnect the same one, we need a consistent rule to + // select which one. + + bool outgoing1 = c.is_outgoing(); + + // for this, we compare our endpoints (IP and port) + // and whoever has the lower IP,port should be the + // one keeping its outgoing connection. Since outgoing + // ports are selected at random by the OS, we need + // to be careful to only look at the target end of a + // connection for the endpoint. + + int our_port = outgoing1 ? other_socket->local_endpoint(ec1).port() : this_socket->local_endpoint(ec1).port(); + int other_port = outgoing1 ? this_socket->remote_endpoint(ec1).port() : other_socket->remote_endpoint(ec1).port(); + + if (our_port < other_port) + { +#ifdef TORRENT_VERBOSE_LOGGING + c.peer_log("*** DUPLICATE PEER RESOLUTION [ \"%d\" < \"%d\" ]", our_port, other_port); + i->connection->peer_log("*** DUPLICATE PEER RESOLUTION [ \"%d\" < \"%d\" ]", our_port, other_port); +#endif + + // we should keep our outgoing connection + if (!outgoing1) + { + c.disconnect(errors::duplicate_peer_id); + return false; + } + TORRENT_ASSERT(m_locked_peer == NULL); + m_locked_peer = i; + i->connection->disconnect(errors::duplicate_peer_id); + m_locked_peer = NULL; + } + else + { +#ifdef TORRENT_VERBOSE_LOGGING + c.peer_log("*** DUPLICATE PEER RESOLUTION [ \"%d\" >= \"%d\" ]", our_port, other_port); + i->connection->peer_log("*** DUPLICATE PEER RESOLUTION [ \"%d\" >= \"%d\" ]", our_port, other_port); +#endif + // they should keep their outgoing connection + if (outgoing1) + { + c.disconnect(errors::duplicate_peer_id); + return false; + } + TORRENT_ASSERT(m_locked_peer == NULL); + m_locked_peer = i; + i->connection->disconnect(errors::duplicate_peer_id); + m_locked_peer = NULL; + } + } + } + + if (is_connect_candidate(*i, m_finished)) + { + m_num_connect_candidates--; + TORRENT_ASSERT(m_num_connect_candidates >= 0); + if (m_num_connect_candidates < 0) m_num_connect_candidates = 0; + } + } + else + { + // we don't have any info about this peer. + // add a new entry + error_code ec; + TORRENT_ASSERT(c.remote() == c.get_socket()->remote_endpoint(ec) || ec); + + // max peerlist size 0 means infinite + int max_peerlist_size = m_torrent->is_paused() + ? m_torrent->settings().max_paused_peerlist_size + : m_torrent->settings().max_peerlist_size; + + if (max_peerlist_size + && int(m_peers.size()) >= max_peerlist_size) + { + // this may invalidate our iterator! + erase_peers(force_erase); + if (int(m_peers.size()) >= m_torrent->settings().max_peerlist_size) + { +#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING + (*m_torrent->session().m_logger) << time_now_string() + << " *** TOO MANY CONNECTIONS [" + " torrent: " << m_torrent->name() << + " torrent peers: " << m_torrent->num_peers() << + " torrent limit: " << m_torrent->max_connections() << + " global peers: " << ses.num_connections() << + " global limit: " << ses.settings().connections_limit << + " global list peers " << int(m_peers.size()) << + " global list limit: " << m_torrent->settings().max_peerlist_size << + " ]\n"; +#endif + c.disconnect(errors::too_many_connections); + return false; + } + // restore it + iter = std::lower_bound( + m_peers.begin(), m_peers.end() + , c.remote().address(), peer_address_compare() + ); + } + +#if TORRENT_USE_IPV6 + bool is_v6 = c.remote().address().is_v6(); +#endif + peer* p = +#if TORRENT_USE_IPV6 + is_v6 ? (peer*)m_torrent->session().m_ipv6_peer_pool.malloc() : +#endif + (peer*)m_torrent->session().m_ipv4_peer_pool.malloc(); + if (p == 0) return false; + +#if TORRENT_USE_IPV6 + if (is_v6) + m_torrent->session().m_ipv6_peer_pool.set_next_size(500); + else +#endif + m_torrent->session().m_ipv4_peer_pool.set_next_size(500); + +#if TORRENT_USE_IPV6 + if (is_v6) + new (p) ipv6_peer(c.remote(), false, 0); + else +#endif + new (p) ipv4_peer(c.remote(), false, 0); + +#if TORRENT_USE_ASSERTS + p->in_use = true; +#endif + + iter = m_peers.insert(iter, p); + + if (m_round_robin >= iter - m_peers.begin()) ++m_round_robin; + + i = *iter; +#ifndef TORRENT_DISABLE_GEO_IP + int as = ses.as_for_ip(c.remote().address()); +#ifdef TORRENT_DEBUG + i->inet_as_num = as; +#endif + i->inet_as = ses.lookup_as(as); +#endif + i->source = peer_info::incoming; + } + + TORRENT_ASSERT(i); + c.set_peer_info(i); + TORRENT_ASSERT(i->connection == 0); + c.add_stat(size_type(i->prev_amount_download) << 10, size_type(i->prev_amount_upload) << 10); + + // restore transfer rate limits + int rate_limit; + rate_limit = i->upload_rate_limit; + if (rate_limit) c.set_upload_limit(rate_limit); + rate_limit = i->download_rate_limit; + if (rate_limit) c.set_download_limit(rate_limit); + + i->prev_amount_download = 0; + i->prev_amount_upload = 0; + i->connection = &c; + TORRENT_ASSERT(i->connection); + if (!c.fast_reconnect()) + i->last_connected = session_time; + + // this cannot be a connect candidate anymore, since i->connection is set + TORRENT_ASSERT(!is_connect_candidate(*i, m_finished)); + TORRENT_ASSERT(has_connection(&c)); + m_torrent->state_updated(); + return true; + } + + bool policy::update_peer_port(int port, policy::peer* p, int src) + { + TORRENT_ASSERT(p != 0); + TORRENT_ASSERT(p->connection); + TORRENT_ASSERT(p->in_use); + + INVARIANT_CHECK; + + if (p->port == port) return true; + + if (m_torrent->settings().allow_multiple_connections_per_ip) + { + tcp::endpoint remote(p->address(), port); + std::pair range = find_peers(remote.address()); + iterator i = std::find_if(range.first, range.second + , match_peer_endpoint(remote)); + if (i != range.second) + { + policy::peer& pp = **i; + TORRENT_ASSERT(pp.in_use); + if (pp.connection) + { + bool was_conn_cand = is_connect_candidate(pp, m_finished); + // if we already have an entry with this + // new endpoint, disconnect this one + pp.connectable = true; + pp.source |= src; + if (!was_conn_cand && is_connect_candidate(pp, m_finished)) + ++m_num_connect_candidates; + // calling disconnect() on a peer, may actually end + // up "garbage collecting" its policy::peer entry + // as well, if it's considered useless (which this specific) + // case will, since it was an incoming peer that just disconnected + // and we allow multiple connections per IP. Because of that, + // we need to make sure we don't let it do that, by unlinking + // the peer_connection from the policy::peer first. + p->connection->set_peer_info(0); + TORRENT_ASSERT(m_locked_peer == NULL); + m_locked_peer = p; + p->connection->disconnect(errors::duplicate_peer_id); + m_locked_peer = NULL; + erase_peer(p); + return false; + } + erase_peer(i); + } + } +#ifdef TORRENT_DEBUG + else + { +#if TORRENT_USE_I2P + if (!p->is_i2p_addr) +#endif + { + std::pair range = find_peers(p->address()); + TORRENT_ASSERT(range.second - range.first == 1); + } + } +#endif + + bool was_conn_cand = is_connect_candidate(*p, m_finished); + p->port = port; + p->source |= src; + p->connectable = true; + + if (was_conn_cand != is_connect_candidate(*p, m_finished)) + { + m_num_connect_candidates += was_conn_cand ? -1 : 1; + TORRENT_ASSERT(m_num_connect_candidates >= 0); + if (m_num_connect_candidates < 0) m_num_connect_candidates = 0; + } + return true; + } + + // it's important that we don't dereference + // p here, since it is allowed to be a dangling + // pointer. see smart_ban.cpp + bool policy::has_peer(policy::peer const* p) const + { + TORRENT_ASSERT(p->in_use); + // find p in m_peers + for (const_iterator i = m_peers.begin() + , end(m_peers.end()); i != end; ++i) + { + if (*i == p) return true; + } + return false; + } + + void policy::set_seed(policy::peer* p, bool s) + { + if (p == 0) return; + TORRENT_ASSERT(p->in_use); + if (p->seed == s) return; + bool was_conn_cand = is_connect_candidate(*p, m_finished); + p->seed = s; + if (was_conn_cand && !is_connect_candidate(*p, m_finished)) + { + --m_num_connect_candidates; + TORRENT_ASSERT(m_num_connect_candidates >= 0); + if (m_num_connect_candidates < 0) m_num_connect_candidates = 0; + } + + if (p->web_seed) return; + if (s) ++m_num_seeds; + else --m_num_seeds; + TORRENT_ASSERT(m_num_seeds >= 0); + TORRENT_ASSERT(m_num_seeds <= int(m_peers.size())); + } + + bool policy::insert_peer(policy::peer* p, iterator iter, int flags) + { + TORRENT_ASSERT(p); + TORRENT_ASSERT(p->in_use); + + int max_peerlist_size = m_torrent->is_paused() + ?m_torrent->settings().max_paused_peerlist_size + :m_torrent->settings().max_peerlist_size; + + if (max_peerlist_size + && int(m_peers.size()) >= max_peerlist_size) + { + if (p->source == peer_info::resume_data) return false; + + erase_peers(); + if (int(m_peers.size()) >= max_peerlist_size) + return 0; + + // since some peers were removed, we need to + // update the iterator to make it valid again +#if TORRENT_USE_I2P + if (p->is_i2p_addr) + { + iter = std::lower_bound( + m_peers.begin(), m_peers.end() + , p->dest(), peer_address_compare()); + } + else +#endif + iter = std::lower_bound( + m_peers.begin(), m_peers.end() + , p->address(), peer_address_compare()); + } + + iter = m_peers.insert(iter, p); + + if (m_round_robin >= iter - m_peers.begin()) ++m_round_robin; + +#ifndef TORRENT_DISABLE_ENCRYPTION + if (flags & 0x01) p->pe_support = true; +#endif + if (flags & 0x02) + { + p->seed = true; + ++m_num_seeds; + } + if (flags & 0x04) + p->supports_utp = true; + if (flags & 0x08) + p->supports_holepunch = true; + +#ifndef TORRENT_DISABLE_GEO_IP + int as = m_torrent->session().as_for_ip(p->address()); +#ifdef TORRENT_DEBUG + p->inet_as_num = as; +#endif + p->inet_as = m_torrent->session().lookup_as(as); +#endif + if (is_connect_candidate(*p, m_finished)) + ++m_num_connect_candidates; + + m_torrent->state_updated(); + + return true; + } + + void policy::update_peer(policy::peer* p, int src, int flags + , tcp::endpoint const& remote, char const* destination) + { + bool was_conn_cand = is_connect_candidate(*p, m_finished); + + TORRENT_ASSERT(p->in_use); + p->connectable = true; + + TORRENT_ASSERT(p->address() == remote.address()); + p->port = remote.port(); + p->source |= src; + + // if this peer has failed before, decrease the + // counter to allow it another try, since somebody + // else is appearantly able to connect to it + // only trust this if it comes from the tracker + if (p->failcount > 0 && src == peer_info::tracker) + --p->failcount; + + // if we're connected to this peer + // we already know if it's a seed or not + // so we don't have to trust this source + if ((flags & 0x02) && !p->connection) + { + if (!p->seed) ++m_num_seeds; + p->seed = true; + } + if (flags & 0x04) + p->supports_utp = true; + if (flags & 0x08) + p->supports_holepunch = true; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + if (p->connection) + { + // this means we're already connected + // to this peer. don't connect to + // it again. + + error_code ec; + char hex_pid[41]; + to_hex((char*)&p->connection->pid()[0], 20, hex_pid); + char msg[200]; + snprintf(msg, 200, "already connected to peer: %s %s" + , print_endpoint(remote).c_str(), hex_pid); + m_torrent->debug_log(msg); + + TORRENT_ASSERT(p->connection->associated_torrent().lock().get() == m_torrent); + } +#endif + + if (was_conn_cand != is_connect_candidate(*p, m_finished)) + { + m_num_connect_candidates += was_conn_cand ? -1 : 1; + if (m_num_connect_candidates < 0) m_num_connect_candidates = 0; + } + } + +#if TORRENT_USE_I2P + policy::peer* policy::add_i2p_peer(char const* destination, int src, char flags) + { + INVARIANT_CHECK; + + bool found = false; + iterator iter = std::lower_bound( + m_peers.begin(), m_peers.end() + , destination, peer_address_compare() + ); + + if (iter != m_peers.end() && strcmp((*iter)->dest(), destination) == 0) + found = true; + + peer* p = 0; + + if (!found) + { + // we don't have any info about this peer. + // add a new entry + p = (peer*)m_torrent->session().m_i2p_peer_pool.malloc(); + if (p == 0) return 0; + m_torrent->session().m_i2p_peer_pool.set_next_size(500); + new (p) i2p_peer(destination, true, src); + +#if TORRENT_USE_ASSERTS + p->in_use = true; +#endif + + if (!insert_peer(p, iter, flags)) + { +#if TORRENT_USE_ASSERTS + p->in_use = false; +#endif + + m_torrent->session().m_i2p_peer_pool.destroy((i2p_peer*)p); + return 0; + } + } + else + { + p = *iter; + update_peer(p, src, flags, tcp::endpoint(), destination); + } + m_torrent->state_updated(); + return p; + } +#endif // TORRENT_USE_I2P + + policy::peer* policy::add_peer(tcp::endpoint const& remote, peer_id const& pid + , int src, char flags) + { + INVARIANT_CHECK; + + // just ignore the obviously invalid entries + if (remote.address() == address() || remote.port() == 0) + return 0; + +#if TORRENT_USE_IPV6 + // don't allow link-local IPv6 addresses since they + // can't be used like normal addresses, they require an interface + // and will just cause connect() to fail with EINVAL + if (remote.address().is_v6() && remote.address().to_v6().is_link_local()) + return 0; +#endif + + aux::session_impl& ses = m_torrent->session(); + + // if this is an i2p torrent, and we don't allow mixed mode + // no regular peers should ever be added! + if (!ses.m_settings.allow_i2p_mixed && m_torrent->torrent_file().is_i2p()) + { + if (ses.m_alerts.should_post()) + ses.m_alerts.post_alert(peer_blocked_alert(m_torrent->get_handle() + , remote.address(), peer_blocked_alert::ip_filter)); + return 0; + } + + port_filter const& pf = ses.m_port_filter; + if (pf.access(remote.port()) & port_filter::blocked) + { + if (ses.m_alerts.should_post()) + ses.m_alerts.post_alert(peer_blocked_alert(m_torrent->get_handle() + , remote.address(), peer_blocked_alert::port_filter)); +#ifndef TORRENT_DISABLE_EXTENSIONS + m_torrent->notify_extension_add_peer(remote, src, torrent_plugin::filtered); +#endif + return 0; + } + + if (ses.m_settings.no_connect_privileged_ports && remote.port() < 1024) + { + if (ses.m_alerts.should_post()) + ses.m_alerts.post_alert(peer_blocked_alert(m_torrent->get_handle() + , remote.address(), peer_blocked_alert::privileged_ports)); +#ifndef TORRENT_DISABLE_EXTENSIONS + m_torrent->notify_extension_add_peer(remote, src, torrent_plugin::filtered); +#endif + return 0; + } + + // if the IP is blocked, don't add it + if (m_torrent->apply_ip_filter() + && (ses.m_ip_filter.access(remote.address()) & ip_filter::blocked)) + { + if (ses.m_alerts.should_post()) + ses.m_alerts.post_alert(peer_blocked_alert(m_torrent->get_handle() + , remote.address(), peer_blocked_alert::ip_filter)); +#ifndef TORRENT_DISABLE_EXTENSIONS + m_torrent->notify_extension_add_peer(remote, src, torrent_plugin::filtered); +#endif + return 0; + } + + iterator iter; + peer* p = 0; + + bool found = false; + if (m_torrent->settings().allow_multiple_connections_per_ip) + { + std::pair range = find_peers(remote.address()); + iter = std::find_if(range.first, range.second, match_peer_endpoint(remote)); + if (iter != range.second) found = true; + } + else + { + iter = std::lower_bound( + m_peers.begin(), m_peers.end() + , remote.address(), peer_address_compare() + ); + + if (iter != m_peers.end() && (*iter)->address() == remote.address()) found = true; + } + + if (!found) + { + // we don't have any info about this peer. + // add a new entry + +#if TORRENT_USE_IPV6 + bool is_v6 = remote.address().is_v6(); +#endif + p = +#if TORRENT_USE_IPV6 + is_v6 ? (peer*)m_torrent->session().m_ipv6_peer_pool.malloc() : +#endif + (peer*)m_torrent->session().m_ipv4_peer_pool.malloc(); + if (p == 0) return 0; +#if TORRENT_USE_IPV6 + if (is_v6) + m_torrent->session().m_ipv6_peer_pool.set_next_size(500); + else +#endif + m_torrent->session().m_ipv4_peer_pool.set_next_size(500); + +#if TORRENT_USE_IPV6 + if (is_v6) + new (p) ipv6_peer(remote, true, src); + else +#endif + new (p) ipv4_peer(remote, true, src); + +#if TORRENT_USE_ASSERTS + p->in_use = true; +#endif + + if (!insert_peer(p, iter, flags)) + { +#if TORRENT_USE_ASSERTS + p->in_use = false; +#endif +#if TORRENT_USE_IPV6 + if (is_v6) m_torrent->session().m_ipv6_peer_pool.destroy((ipv6_peer*)p); + else +#endif + m_torrent->session().m_ipv4_peer_pool.destroy((ipv4_peer*)p); + return 0; + } +#ifndef TORRENT_DISABLE_EXTENSIONS + m_torrent->notify_extension_add_peer(remote, src, torrent_plugin::first_time); +#endif + } + else + { + p = *iter; + TORRENT_ASSERT(p->in_use); + update_peer(p, src, flags, remote, 0); +#ifndef TORRENT_DISABLE_EXTENSIONS + m_torrent->notify_extension_add_peer(remote, src, 0); +#endif + } + + return p; + } + + bool policy::connect_one_peer(int session_time) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_torrent->want_more_peers()); + + iterator i = find_connect_candidate(session_time); + if (i == m_peers.end()) return false; + peer& p = **i; + TORRENT_ASSERT(p.in_use); + + TORRENT_ASSERT(!p.banned); + TORRENT_ASSERT(!p.connection); + TORRENT_ASSERT(p.connectable); + + TORRENT_ASSERT(m_finished == m_torrent->is_finished()); + TORRENT_ASSERT(is_connect_candidate(p, m_finished)); + if (!m_torrent->connect_to_peer(&p)) + { + // failcount is a 5 bit value + const bool was_conn_cand = is_connect_candidate(p, m_finished); + if (p.failcount < 31) ++p.failcount; + if (was_conn_cand && !is_connect_candidate(p, m_finished)) + --m_num_connect_candidates; + return false; + } + TORRENT_ASSERT(p.connection); + TORRENT_ASSERT(!is_connect_candidate(p, m_finished)); + return true; + } + + // this is called whenever a peer connection is closed + void policy::connection_closed(const peer_connection& c, int session_time) + { + INVARIANT_CHECK; + + peer* p = c.peer_info_struct(); + + // if we couldn't find the connection in our list, just ignore it. + if (p == 0) return; + + TORRENT_ASSERT(p->in_use); + + // web seeds are special, they're not connected via the peer list + // so they're not kept in m_peers + TORRENT_ASSERT(p->web_seed + || std::find_if( + m_peers.begin() + , m_peers.end() + , match_peer_connection(c)) + != m_peers.end()); + + TORRENT_ASSERT(p->connection == &c); + TORRENT_ASSERT(!is_connect_candidate(*p, m_finished)); + + // save transfer rate limits + p->upload_rate_limit = c.upload_limit(); + p->download_rate_limit = c.download_limit(); + + p->connection = 0; + p->optimistically_unchoked = false; + + // if fast reconnect is true, we won't + // update the timestamp, and it will remain + // the time when we initiated the connection. + if (!c.fast_reconnect()) + p->last_connected = session_time; + + if (c.failed()) + { + // failcount is a 5 bit value + if (p->failcount < 31) ++p->failcount; + } + + if (is_connect_candidate(*p, m_finished)) + ++m_num_connect_candidates; + + // if we're already a seed, it's not as important + // to keep all the possibly stale peers + // if we're not a seed, but we have too many peers + // start weeding the ones we only know from resume + // data first + // at this point it may be tempting to erase peers + // from the peer list, but keep in mind that we might + // have gotten to this point through new_connection, just + // disconnecting an old peer, relying on this policy::peer + // to still exist when we get back there, to assign the new + // peer connection pointer to it. The peer list must + // be left intact. + + // if we allow multiple connections per IP, and this peer + // was incoming and it never advertised its listen + // port, we don't really know which peer it was. In order + // to avoid adding one entry for every single connection + // the peer makes to us, don't save this entry + if (m_torrent->settings().allow_multiple_connections_per_ip + && !p->connectable + && p != m_locked_peer) + { + erase_peer(p); + } + } + + void policy::peer_is_interesting(peer_connection& c) + { + INVARIANT_CHECK; + + // no peer should be interesting if we're finished + TORRENT_ASSERT(!m_torrent->is_finished()); + + if (c.in_handshake()) return; + c.send_interested(); + if (c.has_peer_choked() + && c.allowed_fast().empty()) + return; + request_a_block(*m_torrent, c); + c.send_block_requests(); + } + + void policy::recalculate_connect_candidates() + { + INVARIANT_CHECK; + + const bool is_finished = m_torrent->is_finished(); + if (is_finished == m_finished) return; + + m_num_connect_candidates = 0; + m_finished = is_finished; + for (const_iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + m_num_connect_candidates += is_connect_candidate(**i, m_finished); + } + } + +#if TORRENT_USE_ASSERTS + bool policy::has_connection(const peer_connection* c) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(c); + error_code ec; + if (c->remote() != c->get_socket()->remote_endpoint(ec) && !ec) + { + fprintf(stderr, "c->remote: %s\nc->get_socket()->remote_endpoint: %s\n" + , print_endpoint(c->remote()).c_str() + , print_endpoint(c->get_socket()->remote_endpoint(ec)).c_str()); + TORRENT_ASSERT(false); + } + + return std::find_if( + m_peers.begin() + , m_peers.end() + , match_peer_connection_or_endpoint(*c)) != m_peers.end(); + } +#endif + +#if TORRENT_USE_INVARIANT_CHECKS + void policy::check_invariant() const + { + TORRENT_ASSERT(m_num_connect_candidates >= 0); + TORRENT_ASSERT(m_num_connect_candidates <= int(m_peers.size())); + if (m_torrent->is_aborted()) return; + +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + int connected_peers = 0; + + int total_connections = 0; + int nonempty_connections = 0; + int connect_candidates = 0; + + std::set unique_test; + const_iterator prev = m_peers.end(); + for (const_iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + if (prev != m_peers.end()) ++prev; + if (i == m_peers.begin() + 1) prev = m_peers.begin(); + if (prev != m_peers.end()) + { + if (m_torrent->settings().allow_multiple_connections_per_ip) + TORRENT_ASSERT(!((*i)->address() < (*prev)->address())); + else + TORRENT_ASSERT((*prev)->address() < (*i)->address()); + } + peer const& p = **i; + TORRENT_ASSERT(p.in_use); + if (is_connect_candidate(p, m_finished)) ++connect_candidates; +#ifndef TORRENT_DISABLE_GEO_IP + TORRENT_ASSERT(p.inet_as == 0 || p.inet_as->first == p.inet_as_num); +#endif + if (!m_torrent->settings().allow_multiple_connections_per_ip) + { + std::pair range = find_peers(p.address()); + TORRENT_ASSERT(range.second - range.first == 1); + } + else + { + TORRENT_ASSERT(unique_test.count(p.ip()) == 0); + unique_test.insert(p.ip()); +// TORRENT_ASSERT(p.connection == 0 || p.ip() == p.connection->remote()); + } + ++total_connections; + if (!p.connection) + { + continue; + } + if (p.optimistically_unchoked) + { + TORRENT_ASSERT(p.connection); + TORRENT_ASSERT(!p.connection->is_choked()); + } + TORRENT_ASSERT(p.connection->peer_info_struct() == 0 + || p.connection->peer_info_struct() == &p); + ++nonempty_connections; + if (!p.connection->is_disconnecting()) + ++connected_peers; + } + + TORRENT_ASSERT(m_num_connect_candidates == connect_candidates); + + int num_torrent_peers = 0; + for (torrent::const_peer_iterator i = m_torrent->begin(); + i != m_torrent->end(); ++i) + { + if ((*i)->is_disconnecting()) continue; + // ignore web_peer_connections since they are not managed + // by the policy class + if ((*i)->type() != peer_connection::bittorrent_connection) continue; + ++num_torrent_peers; + } + + if (m_torrent->has_picker()) + { + piece_picker& p = m_torrent->picker(); + std::vector downloaders = p.get_download_queue(); + + std::set peer_set; + std::vector peers; + for (std::vector::iterator i = downloaders.begin() + , end(downloaders.end()); i != end; ++i) + { + p.get_downloaders(peers, i->index); + std::copy(peers.begin(), peers.end() + , std::insert_iterator >(peer_set, peer_set.begin())); + } + + for (std::set::iterator i = peer_set.begin() + , end(peer_set.end()); i != end; ++i) + { + policy::peer* p = static_cast(*i); + if (p == 0) continue; + TORRENT_ASSERT(p->in_use); + if (p->connection == 0) continue; + // web seeds are special, they're not connected via the peer list + // so they're not kept in m_peers + if (p->connection->type() != peer_connection::bittorrent_connection) continue; + TORRENT_ASSERT(std::find_if(m_peers.begin(), m_peers.end() + , match_peer_connection_or_endpoint(*p->connection)) != m_peers.end()); + } + } +#endif // TORRENT_EXPENSIVE_INVARIANT_CHECKS + + // this invariant is a bit complicated. + // the usual case should be that connected_peers + // == num_torrent_peers. But when there's an incoming + // connection, it will first be added to the policy + // and then be added to the torrent. + // When there's an outgoing connection, it will first + // be added to the torrent and then to the policy. + // that's why the two second cases are in there. +/* + TORRENT_ASSERT(connected_peers == num_torrent_peers + || (connected_peers == num_torrent_peers + 1 + && connected_peers > 0) + || (connected_peers + 1 == num_torrent_peers + && num_torrent_peers > 0)); +*/ + } +#endif // TORRENT_DEBUG + + policy::peer::peer(boost::uint16_t port, bool conn, int src) + : prev_amount_upload(0) + , prev_amount_download(0) + , connection(0) + , peer_rank(0) +#ifndef TORRENT_DISABLE_GEO_IP + , inet_as(0) +#endif + , last_optimistically_unchoked(0) + , last_connected(0) + , port(port) + , upload_rate_limit(0) + , download_rate_limit(0) + , hashfails(0) + , failcount(0) + , connectable(conn) + , optimistically_unchoked(false) + , seed(false) + , fast_reconnects(0) + , trust_points(0) + , source(src) +#ifndef TORRENT_DISABLE_ENCRYPTION + // assume no support in order to + // prefer opening non-encrypyed + // connections. If it fails, we'll + // retry with encryption + , pe_support(false) +#endif +#if TORRENT_USE_IPV6 + , is_v6_addr(false) +#endif +#if TORRENT_USE_I2P + , is_i2p_addr(false) +#endif + , on_parole(false) + , banned(false) +#ifndef TORRENT_DISABLE_DHT + , added_to_dht(false) +#endif + , supports_utp(true) // assume peers support utp + , confirmed_supports_utp(false) + , supports_holepunch(false) + , web_seed(false) +#if TORRENT_USE_ASSERTS + , in_use(false) +#endif + { + TORRENT_ASSERT((src & 0xff) == src); + } + + // TOOD: pass in both an IPv6 and IPv4 address here + boost::uint32_t policy::peer::rank(external_ip const& external, int external_port) const + { + if (peer_rank == 0) + peer_rank = peer_priority( + tcp::endpoint(external.external_address(this->address()), external_port) + , tcp::endpoint(this->address(), this->port)); + return peer_rank; + } + + size_type policy::peer::total_download() const + { + if (connection != 0) + { + TORRENT_ASSERT(prev_amount_download == 0); + return connection->statistics().total_payload_download(); + } + else + { + return size_type(prev_amount_download) << 10; + } + } + + size_type policy::peer::total_upload() const + { + if (connection != 0) + { + TORRENT_ASSERT(prev_amount_upload == 0); + return connection->statistics().total_payload_upload(); + } + else + { + return size_type(prev_amount_upload) << 10; + } + } + + // this returns true if lhs is a better erase candidate than rhs + bool policy::compare_peer_erase(policy::peer const& lhs, policy::peer const& rhs) const + { + TORRENT_ASSERT(lhs.connection == 0); + TORRENT_ASSERT(rhs.connection == 0); + + // primarily, prefer getting rid of peers we've already tried and failed + if (lhs.failcount != rhs.failcount) + return lhs.failcount > rhs.failcount; + + bool lhs_resume_data_source = lhs.source == peer_info::resume_data; + bool rhs_resume_data_source = rhs.source == peer_info::resume_data; + + // prefer to drop peers whose only source is resume data + if (lhs_resume_data_source != rhs_resume_data_source) + return lhs_resume_data_source > rhs_resume_data_source; + + if (lhs.connectable != rhs.connectable) + return lhs.connectable < rhs.connectable; + + return lhs.trust_points < rhs.trust_points; + } + + // this returns true if lhs is a better connect candidate than rhs + bool policy::compare_peer(policy::peer const& lhs, policy::peer const& rhs + , external_ip const& external, int external_port) const + { + // prefer peers with lower failcount + if (lhs.failcount != rhs.failcount) + return lhs.failcount < rhs.failcount; + + // Local peers should always be tried first + bool lhs_local = is_local(lhs.address()); + bool rhs_local = is_local(rhs.address()); + if (lhs_local != rhs_local) return lhs_local > rhs_local; + + if (lhs.last_connected != rhs.last_connected) + return lhs.last_connected < rhs.last_connected; + + int lhs_rank = source_rank(lhs.source); + int rhs_rank = source_rank(rhs.source); + if (lhs_rank != rhs_rank) return lhs_rank > rhs_rank; + +#ifndef TORRENT_DISABLE_GEO_IP + // don't bias fast peers when seeding + if (!m_finished && m_torrent->session().has_asnum_db()) + { + int lhs_as = lhs.inet_as ? lhs.inet_as->second : 0; + int rhs_as = rhs.inet_as ? rhs.inet_as->second : 0; + if (lhs_as != rhs_as) return lhs_as > rhs_as; + } +#endif + boost::uint32_t lhs_peer_rank = lhs.rank(external, external_port); + boost::uint32_t rhs_peer_rank = rhs.rank(external, external_port); + if (lhs_peer_rank > rhs_peer_rank) return true; + return false; + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/puff.cpp b/apps/Launcher/ext/libtorrent/src/puff.cpp new file mode 100644 index 0000000000..82347e7791 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/puff.cpp @@ -0,0 +1,842 @@ +/* + * puff.c + * Copyright (C) 2002, 2003 Mark Adler + * For conditions of distribution and use, see copyright notice in puff.h + * version 1.7, 3 Mar 2003 + * + * puff.c is a simple inflate written to be an unambiguous way to specify the + * deflate format. It is not written for speed but rather simplicity. As a + * side benefit, this code might actually be useful when small code is more + * important than speed, such as bootstrap applications. For typical deflate + * data, zlib's inflate() is about four times as fast as puff(). zlib's + * inflate compiles to around 20K on my machine, whereas puff.c compiles to + * around 4K on my machine (a PowerPC using GNU cc). If the faster decode() + * function here is used, then puff() is only twice as slow as zlib's + * inflate(). + * + * All dynamically allocated memory comes from the stack. The stack required + * is less than 2K bytes. This code is compatible with 16-bit int's and + * assumes that long's are at least 32 bits. puff.c uses the short data type, + * assumed to be 16 bits, for arrays in order to to conserve memory. The code + * works whether integers are stored big endian or little endian. + * + * In the comments below are "Format notes" that describe the inflate process + * and document some of the less obvious aspects of the format. This source + * code is meant to supplement RFC 1951, which formally describes the deflate + * format: + * + * http://www.zlib.org/rfc-deflate.html + */ + +/* + * Change history: + * + * 1.0 10 Feb 2002 - First version + * 1.1 17 Feb 2002 - Clarifications of some comments and notes + * - Update puff() dest and source pointers on negative + * errors to facilitate debugging deflators + * - Remove longest from struct huffman -- not needed + * - Simplify offs[] index in construct() + * - Add input size and checking, using longjmp() to + * maintain easy readability + * - Use short data type for large arrays + * - Use pointers instead of long to specify source and + * destination sizes to avoid arbitrary 4 GB limits + * 1.2 17 Mar 2002 - Add faster version of decode(), doubles speed (!), + * but leave simple version for readabilty + * - Make sure invalid distances detected if pointers + * are 16 bits + * - Fix fixed codes table error + * - Provide a scanning mode for determining size of + * uncompressed data + * 1.3 20 Mar 2002 - Go back to lengths for puff() parameters [Jean-loup] + * - Add a puff.h file for the interface + * - Add braces in puff() for else do [Jean-loup] + * - Use indexes instead of pointers for readability + * 1.4 31 Mar 2002 - Simplify construct() code set check + * - Fix some comments + * - Add FIXLCODES #define + * 1.5 6 Apr 2002 - Minor comment fixes + * 1.6 7 Aug 2002 - Minor format changes + * 1.7 3 Mar 2003 - Added test code for distribution + * - Added zlib-like license + */ + +/* +note by Arvid Norberg. +This file was turned into a .cpp file in order to +be able to take advantage of boost's cstdint.hpp file +All "short" has been replaced with boost::int16_t +and all "long" with boost::int32_t according to the +type width assuptions in the comment above. +*/ +#include /* for setjmp(), longjmp(), and jmp_buf */ +#include /* for types with size guarantees */ +#include "libtorrent/puff.hpp" /* prototype for puff() */ + +#define local static /* for local function definitions */ +#define NIL ((unsigned char *)0) /* for no output option */ + +/* + * Maximums for allocations and loops. It is not useful to change these -- + * they are fixed by the deflate format. + */ +#define MAXBITS 15 /* maximum bits in a code */ +#define MAXLCODES 286 /* maximum number of literal/length codes */ +#define MAXDCODES 30 /* maximum number of distance codes */ +#define MAXCODES (MAXLCODES+MAXDCODES) /* maximum codes lengths to read */ +#define FIXLCODES 288 /* number of fixed literal/length codes */ + +/* input and output state */ +struct state { + /* output state */ + unsigned char *out; /* output buffer */ + boost::uint32_t outlen; /* available space at out */ + boost::uint32_t outcnt; /* bytes written to out so far */ + + /* input state */ + unsigned char *in; /* input buffer */ + boost::uint32_t inlen; /* available input at in */ + boost::uint32_t incnt; /* bytes read so far */ + int bitbuf; /* bit buffer */ + int bitcnt; /* number of bits in bit buffer */ + + /* input limit error return state for bits() and decode() */ + jmp_buf env; +}; + +/* + * Return need bits from the input stream. This always leaves less than + * eight bits in the buffer. bits() works properly for need == 0. + * + * Format notes: + * + * - Bits are stored in bytes from the least significant bit to the most + * significant bit. Therefore bits are dropped from the bottom of the bit + * buffer, using shift right, and new bytes are appended to the top of the + * bit buffer, using shift left. + */ +local int bits(struct state *s, int need) +{ + boost::int32_t val; /* bit accumulator (can use up to 20 bits) */ + + /* load at least need bits into val */ + val = s->bitbuf; + while (s->bitcnt < need) { + if (s->incnt == s->inlen) longjmp(s->env, 1); /* out of input */ + val |= (boost::int32_t)(s->in[s->incnt++]) << s->bitcnt; /* load eight bits */ + s->bitcnt += 8; + } + + /* drop need bits and update buffer, always zero to seven bits left */ + s->bitbuf = (int)(val >> need); + s->bitcnt -= need; + + /* return need bits, zeroing the bits above that */ + return (int)(val & ((1L << need) - 1)); +} + +/* + * Process a stored block. + * + * Format notes: + * + * - After the two-bit stored block type (00), the stored block length and + * stored bytes are byte-aligned for fast copying. Therefore any leftover + * bits in the byte that has the last bit of the type, as many as seven, are + * discarded. The value of the discarded bits are not defined and should not + * be checked against any expectation. + * + * - The second inverted copy of the stored block length does not have to be + * checked, but it's probably a good idea to do so anyway. + * + * - A stored block can have zero length. This is sometimes used to byte-align + * subsets of the compressed data for random access or partial recovery. + */ +local int stored(struct state *s) +{ + unsigned len; /* length of stored block */ + + /* discard leftover bits from current byte (assumes s->bitcnt < 8) */ + s->bitbuf = 0; + s->bitcnt = 0; + + /* get length and check against its one's complement */ + if (s->incnt + 4 > s->inlen) return 2; /* not enough input */ + len = s->in[s->incnt++]; + len |= s->in[s->incnt++] << 8; + if (s->in[s->incnt++] != (~len & 0xff) || + s->in[s->incnt++] != ((~len >> 8) & 0xff)) + return -2; /* didn't match complement! */ + + /* copy len bytes from in to out */ + if (s->incnt + len > s->inlen) return 2; /* not enough input */ + if (s->out != NIL) { + if (s->outcnt + len > s->outlen) + return 1; /* not enough output space */ + while (len--) + s->out[s->outcnt++] = s->in[s->incnt++]; + } + else { /* just scanning */ + s->outcnt += len; + s->incnt += len; + } + + /* done with a valid stored block */ + return 0; +} + +/* + * Huffman code decoding tables. count[1..MAXBITS] is the number of symbols of + * each length, which for a canonical code are stepped through in order. + * symbol[] are the symbol values in canonical order, where the number of + * entries is the sum of the counts in count[]. The decoding process can be + * seen in the function decode() below. + */ +struct huffman { + boost::int16_t *count; /* number of symbols of each length */ + boost::int16_t *symbol; /* canonically ordered symbols */ +}; + +/* + * Decode a code from the stream s using huffman table h. Return the symbol or + * a negative value if there is an error. If all of the lengths are zero, i.e. + * an empty code, or if the code is incomplete and an invalid code is received, + * then -9 is returned after reading MAXBITS bits. + * + * Format notes: + * + * - The codes as stored in the compressed data are bit-reversed relative to + * a simple integer ordering of codes of the same lengths. Hence below the + * bits are pulled from the compressed data one at a time and used to + * build the code value reversed from what is in the stream in order to + * permit simple integer comparisons for decoding. A table-based decoding + * scheme (as used in zlib) does not need to do this reversal. + * + * - The first code for the shortest length is all zeros. Subsequent codes of + * the same length are simply integer increments of the previous code. When + * moving up a length, a zero bit is appended to the code. For a complete + * code, the last code of the longest length will be all ones. + * + * - Incomplete codes are handled by this decoder, since they are permitted + * in the deflate format. See the format notes for fixed() and dynamic(). + */ +#ifdef SLOW +local int decode(struct state *s, struct huffman *h) +{ + int len; /* current number of bits in code */ + int code; /* len bits being decoded */ + int first; /* first code of length len */ + int count; /* number of codes of length len */ + int index; /* index of first code of length len in symbol table */ + + code = first = index = 0; + for (len = 1; len <= MAXBITS; len++) { + code |= bits(s, 1); /* get next bit */ + count = h->count[len]; + if (code < first + count) /* if length len, return symbol */ + return h->symbol[index + (code - first)]; + index += count; /* else update for next length */ + first += count; + first <<= 1; + code <<= 1; + } + return -9; /* ran out of codes */ +} + +/* + * A faster version of decode() for real applications of this code. It's not + * as readable, but it makes puff() twice as fast. And it only makes the code + * a few percent larger. + */ +#else /* !SLOW */ +local int decode(struct state *s, struct huffman *h) +{ + int len; /* current number of bits in code */ + int code; /* len bits being decoded */ + int first; /* first code of length len */ + int count; /* number of codes of length len */ + int index; /* index of first code of length len in symbol table */ + int bitbuf; /* bits from stream */ + int left; /* bits left in next or left to process */ + boost::int16_t *next; /* next number of codes */ + + bitbuf = s->bitbuf; + left = s->bitcnt; + code = first = index = 0; + len = 1; + next = h->count + 1; + while (1) { + while (left--) { + code |= bitbuf & 1; + bitbuf >>= 1; + count = *next++; + if (code < first + count) { /* if length len, return symbol */ + s->bitbuf = bitbuf; + s->bitcnt = (s->bitcnt - len) & 7; + return h->symbol[index + (code - first)]; + } + index += count; /* else update for next length */ + first += count; + first <<= 1; + code <<= 1; + len++; + } + left = (MAXBITS+1) - len; + if (left == 0) break; + if (s->incnt == s->inlen) longjmp(s->env, 1); /* out of input */ + bitbuf = s->in[s->incnt++]; + if (left > 8) left = 8; + } + return -9; /* ran out of codes */ +} +#endif /* SLOW */ + +/* + * Given the list of code lengths length[0..n-1] representing a canonical + * Huffman code for n symbols, construct the tables required to decode those + * codes. Those tables are the number of codes of each length, and the symbols + * sorted by length, retaining their original order within each length. The + * return value is zero for a complete code set, negative for an over- + * subscribed code set, and positive for an incomplete code set. The tables + * can be used if the return value is zero or positive, but they cannot be used + * if the return value is negative. If the return value is zero, it is not + * possible for decode() using that table to return an error--any stream of + * enough bits will resolve to a symbol. If the return value is positive, then + * it is possible for decode() using that table to return an error for received + * codes past the end of the incomplete lengths. + * + * Not used by decode(), but used for error checking, h->count[0] is the number + * of the n symbols not in the code. So n - h->count[0] is the number of + * codes. This is useful for checking for incomplete codes that have more than + * one symbol, which is an error in a dynamic block. + * + * Assumption: for all i in 0..n-1, 0 <= length[i] <= MAXBITS + * This is assured by the construction of the length arrays in dynamic() and + * fixed() and is not verified by construct(). + * + * Format notes: + * + * - Permitted and expected examples of incomplete codes are one of the fixed + * codes and any code with a single symbol which in deflate is coded as one + * bit instead of zero bits. See the format notes for fixed() and dynamic(). + * + * - Within a given code length, the symbols are kept in ascending order for + * the code bits definition. + */ +local int construct(struct huffman *h, boost::int16_t *length, int n) +{ + int symbol; /* current symbol when stepping through length[] */ + int len; /* current length when stepping through h->count[] */ + int left; /* number of possible codes left of current length */ + boost::int16_t offs[MAXBITS+1]; /* offsets in symbol table for each length */ + + /* count number of codes of each length */ + for (len = 0; len <= MAXBITS; len++) + h->count[len] = 0; + for (symbol = 0; symbol < n; symbol++) + (h->count[length[symbol]])++; /* assumes lengths are within bounds */ + if (h->count[0] == n) /* no codes! */ + return 0; /* complete, but decode() will fail */ + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; /* one possible code of zero length */ + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; /* one more bit, double codes left */ + left -= h->count[len]; /* deduct count from possible codes */ + if (left < 0) return left; /* over-subscribed--return negative */ + } /* left > 0 means incomplete */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + h->count[len]; + + /* + * put symbols in table sorted by length, by symbol order within each + * length + */ + for (symbol = 0; symbol < n; symbol++) + if (length[symbol] != 0) + h->symbol[offs[length[symbol]]++] = symbol; + + /* return zero for complete set, positive for incomplete set */ + return left; +} + +/* + * Decode literal/length and distance codes until an end-of-block code. + * + * Format notes: + * + * - Compressed data that is after the block type if fixed or after the code + * description if dynamic is a combination of literals and length/distance + * pairs terminated by and end-of-block code. Literals are simply Huffman + * coded bytes. A length/distance pair is a coded length followed by a + * coded distance to represent a string that occurs earlier in the + * uncompressed data that occurs again at the current location. + * + * - Literals, lengths, and the end-of-block code are combined into a single + * code of up to 286 symbols. They are 256 literals (0..255), 29 length + * symbols (257..285), and the end-of-block symbol (256). + * + * - There are 256 possible lengths (3..258), and so 29 symbols are not enough + * to represent all of those. Lengths 3..10 and 258 are in fact represented + * by just a length symbol. Lengths 11..257 are represented as a symbol and + * some number of extra bits that are added as an integer to the base length + * of the length symbol. The number of extra bits is determined by the base + * length symbol. These are in the static arrays below, lens[] for the base + * lengths and lext[] for the corresponding number of extra bits. + * + * - The reason that 258 gets its own symbol is that the longest length is used + * often in highly redundant files. Note that 258 can also be coded as the + * base value 227 plus the maximum extra value of 31. While a good deflate + * should never do this, it is not an error, and should be decoded properly. + * + * - If a length is decoded, including its extra bits if any, then it is + * followed a distance code. There are up to 30 distance symbols. Again + * there are many more possible distances (1..32768), so extra bits are added + * to a base value represented by the symbol. The distances 1..4 get their + * own symbol, but the rest require extra bits. The base distances and + * corresponding number of extra bits are below in the static arrays dist[] + * and dext[]. + * + * - Literal bytes are simply written to the output. A length/distance pair is + * an instruction to copy previously uncompressed bytes to the output. The + * copy is from distance bytes back in the output stream, copying for length + * bytes. + * + * - Distances pointing before the beginning of the output data are not + * permitted. + * + * - Overlapped copies, where the length is greater than the distance, are + * allowed and common. For example, a distance of one and a length of 258 + * simply copies the last byte 258 times. A distance of four and a length of + * twelve copies the last four bytes three times. A simple forward copy + * ignoring whether the length is greater than the distance or not implements + * this correctly. You should not use memcpy() since its behavior is not + * defined for overlapped arrays. You should not use memmove() or bcopy() + * since though their behavior -is- defined for overlapping arrays, it is + * defined to do the wrong thing in this case. + */ +local int codes(struct state *s, + struct huffman *lencode, + struct huffman *distcode) +{ + int symbol; /* decoded symbol */ + int len; /* length for copy */ + unsigned dist; /* distance for copy */ + static const boost::int16_t lens[29] = { /* Size base for length codes 257..285 */ + 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}; + static const boost::int16_t lext[29] = { /* Extra bits for length codes 257..285 */ + 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}; + static const boost::int16_t dists[30] = { /* Offset base for distance codes 0..29 */ + 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}; + static const boost::int16_t dext[30] = { /* Extra bits for distance codes 0..29 */ + 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}; + + /* decode literals and length/distance pairs */ + do { + symbol = decode(s, lencode); + if (symbol < 0) return symbol; /* invalid symbol */ + if (symbol < 256) { /* literal: symbol is the byte */ + /* write out the literal */ + if (s->out != NIL) { + if (s->outcnt == s->outlen) return 1; + s->out[s->outcnt] = symbol; + } + s->outcnt++; + } + else if (symbol > 256) { /* length */ + /* get and compute length */ + symbol -= 257; + if (symbol >= 29) return -9; /* invalid fixed code */ + len = lens[symbol] + bits(s, lext[symbol]); + + /* get and check distance */ + symbol = decode(s, distcode); + if (symbol < 0) return symbol; /* invalid symbol */ + dist = dists[symbol] + bits(s, dext[symbol]); + if (dist > s->outcnt) + return -10; /* distance too far back */ + + /* copy length bytes from distance bytes back */ + if (s->out != NIL) { + if (s->outcnt + len > s->outlen) return 1; + while (len--) { + s->out[s->outcnt] = s->out[s->outcnt - dist]; + s->outcnt++; + } + } + else + s->outcnt += len; + } + } while (symbol != 256); /* end of block symbol */ + + /* done with a valid fixed or dynamic block */ + return 0; +} + +/* + * Process a fixed codes block. + * + * Format notes: + * + * - This block type can be useful for compressing small amounts of data for + * which the size of the code descriptions in a dynamic block exceeds the + * benefit of custom codes for that block. For fixed codes, no bits are + * spent on code descriptions. Instead the code lengths for literal/length + * codes and distance codes are fixed. The specific lengths for each symbol + * can be seen in the "for" loops below. + * + * - The literal/length code is complete, but has two symbols that are invalid + * and should result in an error if received. This cannot be implemented + * simply as an incomplete code since those two symbols are in the "middle" + * of the code. They are eight bits long and the longest literal/length\ + * code is nine bits. Therefore the code must be constructed with those + * symbols, and the invalid symbols must be detected after decoding. + * + * - The fixed distance codes also have two invalid symbols that should result + * in an error if received. Since all of the distance codes are the same + * length, this can be implemented as an incomplete code. Then the invalid + * codes are detected while decoding. + */ +local int fixed(struct state *s) +{ + static int virgin = 1; + static boost::int16_t lencnt[MAXBITS+1], lensym[FIXLCODES]; + static boost::int16_t distcnt[MAXBITS+1], distsym[MAXDCODES]; + static struct huffman lencode = {lencnt, lensym}; + static struct huffman distcode = {distcnt, distsym}; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + int symbol; + boost::int16_t lengths[FIXLCODES]; + + /* literal/length table */ + for (symbol = 0; symbol < 144; symbol++) + lengths[symbol] = 8; + for (; symbol < 256; symbol++) + lengths[symbol] = 9; + for (; symbol < 280; symbol++) + lengths[symbol] = 7; + for (; symbol < FIXLCODES; symbol++) + lengths[symbol] = 8; + construct(&lencode, lengths, FIXLCODES); + + /* distance table */ + for (symbol = 0; symbol < MAXDCODES; symbol++) + lengths[symbol] = 5; + construct(&distcode, lengths, MAXDCODES); + + /* do this just once */ + virgin = 0; + } + + /* decode data until end-of-block code */ + return codes(s, &lencode, &distcode); +} + +/* + * Process a dynamic codes block. + * + * Format notes: + * + * - A dynamic block starts with a description of the literal/length and + * distance codes for that block. New dynamic blocks allow the compressor to + * rapidly adapt to changing data with new codes optimized for that data. + * + * - The codes used by the deflate format are "canonical", which means that + * the actual bits of the codes are generated in an unambiguous way simply + * from the number of bits in each code. Therefore the code descriptions + * are simply a list of code lengths for each symbol. + * + * - The code lengths are stored in order for the symbols, so lengths are + * provided for each of the literal/length symbols, and for each of the + * distance symbols. + * + * - If a symbol is not used in the block, this is represented by a zero as + * as the code length. This does not mean a zero-length code, but rather + * that no code should be created for this symbol. There is no way in the + * deflate format to represent a zero-length code. + * + * - The maximum number of bits in a code is 15, so the possible lengths for + * any code are 1..15. + * + * - The fact that a length of zero is not permitted for a code has an + * interesting consequence. Normally if only one symbol is used for a given + * code, then in fact that code could be represented with zero bits. However + * in deflate, that code has to be at least one bit. So for example, if + * only a single distance base symbol appears in a block, then it will be + * represented by a single code of length one, in particular one 0 bit. This + * is an incomplete code, since if a 1 bit is received, it has no meaning, + * and should result in an error. So incomplete distance codes of one symbol + * should be permitted, and the receipt of invalid codes should be handled. + * + * - It is also possible to have a single literal/length code, but that code + * must be the end-of-block code, since every dynamic block has one. This + * is not the most efficient way to create an empty block (an empty fixed + * block is fewer bits), but it is allowed by the format. So incomplete + * literal/length codes of one symbol should also be permitted. + * + * - The list of up to 286 length/literal lengths and up to 30 distance lengths + * are themselves compressed using Huffman codes and run-length encoding. In + * the list of code lengths, a 0 symbol means no code, a 1..15 symbol means + * that length, and the symbols 16, 17, and 18 are run-length instructions. + * Each of 16, 17, and 18 are follwed by extra bits to define the length of + * the run. 16 copies the last length 3 to 6 times. 17 represents 3 to 10 + * zero lengths, and 18 represents 11 to 138 zero lengths. Unused symbols + * are common, hence the special coding for zero lengths. + * + * - The symbols for 0..18 are Huffman coded, and so that code must be + * described first. This is simply a sequence of up to 19 three-bit values + * representing no code (0) or the code length for that symbol (1..7). + * + * - A dynamic block starts with three fixed-size counts from which is computed + * the number of literal/length code lengths, the number of distance code + * lengths, and the number of code length code lengths (ok, you come up with + * a better name!) in the code descriptions. For the literal/length and + * distance codes, lengths after those provided are considered zero, i.e. no + * code. The code length code lengths are received in a permuted order (see + * the order[] array below) to make a short code length code length list more + * likely. As it turns out, very short and very long codes are less likely + * to be seen in a dynamic code description, hence what may appear initially + * to be a peculiar ordering. + * + * - Given the number of literal/length code lengths (nlen) and distance code + * lengths (ndist), then they are treated as one long list of nlen + ndist + * code lengths. Therefore run-length coding can and often does cross the + * boundary between the two sets of lengths. + * + * - So to summarize, the code description at the start of a dynamic block is + * three counts for the number of code lengths for the literal/length codes, + * the distance codes, and the code length codes. This is followed by the + * code length code lengths, three bits each. This is used to construct the + * code length code which is used to read the remainder of the lengths. Then + * the literal/length code lengths and distance lengths are read as a single + * set of lengths using the code length codes. Codes are constructed from + * the resulting two sets of lengths, and then finally you can start + * decoding actual compressed data in the block. + * + * - For reference, a "typical" size for the code description in a dynamic + * block is around 80 bytes. + */ +local int dynamic(struct state *s) +{ + int nlen, ndist, ncode; /* number of lengths in descriptor */ + int index; /* index of lengths[] */ + int err; /* construct() return value */ + boost::int16_t lengths[MAXCODES]; /* descriptor code lengths */ + boost::int16_t lencnt[MAXBITS+1], lensym[MAXLCODES]; /* lencode memory */ + boost::int16_t distcnt[MAXBITS+1], distsym[MAXDCODES]; /* distcode memory */ + struct huffman lencode = {lencnt, lensym}; /* length code */ + struct huffman distcode = {distcnt, distsym}; /* distance code */ + static const boost::int16_t order[19] = /* permutation of code length codes */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* get number of lengths in each table, check lengths */ + nlen = bits(s, 5) + 257; + ndist = bits(s, 5) + 1; + ncode = bits(s, 4) + 4; + if (nlen > MAXLCODES || ndist > MAXDCODES) + return -3; /* bad counts */ + + /* read code length code lengths (really), missing lengths are zero */ + for (index = 0; index < ncode; index++) + lengths[order[index]] = bits(s, 3); + for (; index < 19; index++) + lengths[order[index]] = 0; + + /* build huffman table for code lengths codes (use lencode temporarily) */ + err = construct(&lencode, lengths, 19); + if (err != 0) return -4; /* require complete code set here */ + + /* read length/literal and distance code length tables */ + index = 0; + while (index < nlen + ndist) { + int symbol; /* decoded value */ + int len; /* last length to repeat */ + + symbol = decode(s, &lencode); + if (symbol < 16) /* length in 0..15 */ + lengths[index++] = symbol; + else { /* repeat instruction */ + len = 0; /* assume repeating zeros */ + if (symbol == 16) { /* repeat last length 3..6 times */ + if (index == 0) return -5; /* no last length! */ + len = lengths[index - 1]; /* last length */ + symbol = 3 + bits(s, 2); + } + else if (symbol == 17) /* repeat zero 3..10 times */ + symbol = 3 + bits(s, 3); + else /* == 18, repeat zero 11..138 times */ + symbol = 11 + bits(s, 7); + if (index + symbol > nlen + ndist) + return -6; /* too many lengths! */ + while (symbol--) /* repeat last or zero symbol times */ + lengths[index++] = len; + } + } + + /* build huffman table for literal/length codes */ + err = construct(&lencode, lengths, nlen); + if (err < 0 || (err > 0 && nlen - lencode.count[0] != 1)) + return -7; /* only allow incomplete codes if just one code */ + + /* build huffman table for distance codes */ + err = construct(&distcode, lengths + nlen, ndist); + if (err < 0 || (err > 0 && ndist - distcode.count[0] != 1)) + return -8; /* only allow incomplete codes if just one code */ + + /* decode data until end-of-block code */ + return codes(s, &lencode, &distcode); +} + +/* + * Inflate source to dest. On return, destlen and sourcelen are updated to the + * size of the uncompressed data and the size of the deflate data respectively. + * On success, the return value of puff() is zero. If there is an error in the + * source data, i.e. it is not in the deflate format, then a negative value is + * returned. If there is not enough input available or there is not enough + * output space, then a positive error is returned. In that case, destlen and + * sourcelen are not updated to facilitate retrying from the beginning with the + * provision of more input data or more output space. In the case of invalid + * inflate data (a negative error), the dest and source pointers are updated to + * facilitate the debugging of deflators. + * + * puff() also has a mode to determine the size of the uncompressed output with + * no output written. For this dest must be (unsigned char *)0. In this case, + * the input value of *destlen is ignored, and on return *destlen is set to the + * size of the uncompressed output. + * + * The return codes are: + * + * 2: available inflate data did not terminate + * 1: output space exhausted before completing inflate + * 0: successful inflate + * -1: invalid block type (type == 3) + * -2: stored block length did not match one's complement + * -3: dynamic block code description: too many length or distance codes + * -4: dynamic block code description: code lengths codes incomplete + * -5: dynamic block code description: repeat lengths with no first length + * -6: dynamic block code description: repeat more than specified lengths + * -7: dynamic block code description: invalid literal/length code lengths + * -8: dynamic block code description: invalid distance code lengths + * -9: invalid literal/length or distance code in fixed or dynamic block + * -10: distance is too far back in fixed or dynamic block + * + * Format notes: + * + * - Three bits are read for each block to determine the kind of block and + * whether or not it is the last block. Then the block is decoded and the + * process repeated if it was not the last block. + * + * - The leftover bits in the last byte of the deflate data after the last + * block (if it was a fixed or dynamic block) are undefined and have no + * expected values to check. + */ +int puff(unsigned char *dest, /* pointer to destination pointer */ + boost::uint32_t *destlen, /* amount of output space */ + unsigned char *source, /* pointer to source data pointer */ + boost::uint32_t *sourcelen) /* amount of input available */ +{ + struct state s; /* input/output state */ + int last, type; /* block information */ + int err; /* return value */ + + /* initialize output state */ + s.out = dest; + s.outlen = *destlen; /* ignored if dest is NIL */ + s.outcnt = 0; + + /* initialize input state */ + s.in = source; + s.inlen = *sourcelen; + s.incnt = 0; + s.bitbuf = 0; + s.bitcnt = 0; + + /* return if bits() or decode() tries to read past available input */ + if (setjmp(s.env) != 0) /* if came back here via longjmp() */ + err = 2; /* then skip do-loop, return error */ + else { + /* process blocks until last block or error */ + do { + last = bits(&s, 1); /* one if last block */ + type = bits(&s, 2); /* block type 0..3 */ + err = type == 0 ? stored(&s) : + (type == 1 ? fixed(&s) : + (type == 2 ? dynamic(&s) : + -1)); /* type == 3, invalid */ + if (err != 0) break; /* return with error */ + } while (!last); + } + + /* update the lengths and return */ + if (err <= 0) { + *destlen = s.outcnt; + *sourcelen = s.incnt; + } + return err; +} + +#ifdef TEST +/* Example of how to use puff() */ +#include +#include +#include +#include + +local unsigned char *yank(char *name, boost::uint32_t *len) +{ + boost::uint32_t size; + unsigned char *buf; + FILE *in; + struct stat s; + + *len = 0; + if (stat(name, &s)) return NULL; + if ((s.st_mode & S_IFMT) != S_IFREG) return NULL; + size = (boost::uint32_t)(s.st_size); + if (size == 0 || (off_t)size != s.st_size) return NULL; + in = fopen(name, "r"); + if (in == NULL) return NULL; + buf = malloc(size); + if (buf != NULL && fread(buf, 1, size, in) != size) { + free(buf); + buf = NULL; + } + fclose(in); + *len = size; + return buf; +} + +int main(int argc, char **argv) +{ + int ret; + unsigned char *source; + boost::uint32_t len, sourcelen, destlen; + + if (argc < 2) return 2; + source = yank(argv[1], &len); + if (source == NULL) return 2; + sourcelen = len; + ret = puff(NIL, &destlen, source, &sourcelen); + if (ret) + printf("puff() failed with return code %d\n", ret); + else { + printf("puff() succeeded uncompressing %lu bytes\n", destlen); + if (sourcelen < len) printf("%lu compressed bytes unused\n", + len - sourcelen); + } + free(source); + return ret; +} +#endif diff --git a/apps/Launcher/ext/libtorrent/src/random.cpp b/apps/Launcher/ext/libtorrent/src/random.cpp new file mode 100644 index 0000000000..f8698441cb --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/random.cpp @@ -0,0 +1,72 @@ +/* + +Copyright (c) 2011-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" +#include "libtorrent/random.hpp" +#include "libtorrent/assert.hpp" + +namespace libtorrent +{ + + namespace + { + boost::uint32_t x = 123456789; +#ifdef TORRENT_USE_ASSERTS + bool seeded = false; +#endif + } + + void random_seed(boost::uint32_t v) + { + x = v; +#ifdef TORRENT_USE_ASSERTS + seeded = true; +#endif + } + + // this is an xorshift random number generator + // see: http://en.wikipedia.org/wiki/Xorshift + boost::uint32_t random() + { + TORRENT_ASSERT(seeded); + + static boost::uint32_t y = 362436069; + static boost::uint32_t z = 521288629; + static boost::uint32_t w = 88675123; + boost::uint32_t t; + + t = x ^ (x << 11); + x = y; y = z; z = w; + return w = w ^ (w >> 19) ^ (t ^ (t >> 8)); + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/rss.cpp b/apps/Launcher/ext/libtorrent/src/rss.cpp new file mode 100644 index 0000000000..700cacd6d6 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/rss.cpp @@ -0,0 +1,683 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/rss.hpp" +#include "libtorrent/xml_parse.hpp" +#include "libtorrent/http_parser.hpp" +#include "libtorrent/http_connection.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/settings.hpp" +#include "libtorrent/alert_types.hpp" // for rss_alert + +#include +#include +#include +#include + +namespace libtorrent { + +feed_item::feed_item(): size(-1) {} +feed_item::~feed_item() {} + +struct feed_state +{ + feed_state(feed& r) + : in_item(false) + , num_items(0) + , type(none) + , ret(r) + {} + + bool in_item; + int num_items; + std::string current_tag; + enum feed_type + { + none, atom, rss2 + } type; + feed_item current_item; + feed& ret; + + bool is_item(char const* tag) const + { + switch (type) + { + case atom: return string_equal_no_case(tag, "entry"); + case rss2: return string_equal_no_case(tag, "item"); + default: return false; + } + } + + bool is_title(char const* tag) const + { + switch (type) + { + case atom: + case rss2: return string_equal_no_case(tag, "title"); + default: return false; + } + } + + bool is_url(char const* tag) const + { + switch (type) + { + case atom: + case rss2: return string_equal_no_case(tag, "link"); + default: return false; + } + } + + bool is_desc(char const* tag) const + { + switch (type) + { + case atom: return string_equal_no_case(tag, "summary"); + case rss2: return string_equal_no_case(tag, "description") + || string_equal_no_case(tag, "media:text"); + default: return false; + } + } + + bool is_uuid(char const* tag) const + { + switch (type) + { + case atom: return string_equal_no_case(tag, "id"); + case rss2: return string_equal_no_case(tag, "guid"); + default: return false; + } + } + + bool is_comment(char const* tag) const + { + switch (type) + { + case atom: return false; + case rss2: return string_equal_no_case(tag, "comments"); + default: return false; + } + } + + bool is_category(char const* tag) const + { + switch (type) + { + case atom: return false; + case rss2: return string_equal_no_case(tag, "category"); + default: return false; + } + } + + bool is_size(char const* tag) const + { + return string_equal_no_case(tag, "size") + || string_equal_no_case(tag, "contentlength"); + } + + bool is_hash(char const* tag) const + { + return string_equal_no_case(tag, "hash") + || string_equal_no_case(tag, "media:hash"); + } + + bool is_ttl(char const* tag) const + { + return string_equal_no_case(tag, "ttl"); + } +}; + +void parse_feed(feed_state& f, int token, char const* name, char const* val) +{ + switch (token) + { + case xml_parse_error: + f.ret.m_error = errors::parse_failed; + return; + case xml_start_tag: + case xml_empty_tag: + { + f.current_tag = name; + if (f.type == feed_state::none) + { + if (string_equal_no_case(f.current_tag.c_str(), "feed")) + f.type = feed_state::atom; + else if (string_equal_no_case(f.current_tag.c_str(), "rss")) + f.type = feed_state::rss2; + } + if (f.is_item(name)) f.in_item = true; + return; + } + case xml_attribute: + { + if (!f.in_item) return; + if (f.is_url(f.current_tag.c_str()) + && f.type == feed_state::atom) + { + // atom feeds have items like this: + // + if (string_equal_no_case(name, "href")) + f.current_item.url = val; + else if (string_equal_no_case(name, "length")) + f.current_item.size = strtoll(val, 0, 10); + } + else if (f.type == feed_state::rss2 + && string_equal_no_case(f.current_tag.c_str(), "enclosure")) + { + // rss feeds have items like this: + // + if (string_equal_no_case(name, "url")) + f.current_item.url = val; + else if (string_equal_no_case(name, "length")) + f.current_item.size = strtoll(val, 0, 10); + } + else if (f.type == feed_state::rss2 + && string_equal_no_case(f.current_tag.c_str(), "media:content")) + { + // rss feeds sometimes have items like this: + // + if (string_equal_no_case(name, "url")) + f.current_item.url = val; + else if (string_equal_no_case(name, "filesize")) + f.current_item.size = strtoll(val, 0, 10); + } + return; + } + case xml_end_tag: + { + if (f.in_item && f.is_item(name)) + { + f.in_item = false; + if (!f.current_item.title.empty() + && !f.current_item.url.empty()) + { + f.ret.add_item(f.current_item); + ++f.num_items; + } + f.current_item = feed_item(); + } + f.current_tag = ""; + return; + } + case xml_string: + { + if (!f.in_item) + { + if (f.is_title(f.current_tag.c_str())) + f.ret.m_title = name; + else if (f.is_desc(f.current_tag.c_str())) + f.ret.m_description = name; + else if (f.is_ttl(f.current_tag.c_str())) + { + int tmp = atoi(name); + if (tmp > 0) f.ret.m_ttl = tmp; + } + return; + } + if (f.is_title(f.current_tag.c_str())) + f.current_item.title = name; + else if (f.is_desc(f.current_tag.c_str())) + f.current_item.description = name; + else if (f.is_uuid(f.current_tag.c_str())) + f.current_item.uuid = name; + else if (f.is_url(f.current_tag.c_str()) && f.type != feed_state::atom) + f.current_item.url = name; + else if (f.is_comment(f.current_tag.c_str())) + f.current_item.comment = name; + else if (f.is_category(f.current_tag.c_str())) + f.current_item.category = name; + else if (f.is_size(f.current_tag.c_str())) + f.current_item.size = strtoll(name, 0, 10); + else if (f.is_hash(f.current_tag.c_str()) && strlen(name) == 40) + { + if (!from_hex(name, 40, (char*)&f.current_item.info_hash[0])) + { + // hex parsing failed + f.current_item.info_hash.clear(); + } + } + return; + } + case xml_declaration_tag: return; + case xml_comment: return; + } +} + +torrent_handle add_feed_item(session& s, feed_item const& fi + , add_torrent_params const& tp, error_code& ec) +{ + add_torrent_params p = tp; + p.url = fi.url; + p.uuid = fi.uuid; + // #error figure out how to get the feed url in here +// p.source_feed_url = ???; + p.ti.reset(); + p.info_hash.clear(); + p.name = fi.title.c_str(); + return s.add_torrent(p, ec); +} + +#ifndef BOOST_NO_EXCEPTIONS +torrent_handle add_feed_item(session& s, feed_item const& fi + , add_torrent_params const& tp) +{ + error_code ec; + torrent_handle ret = add_feed_item(s, fi, tp, ec); + if (ec) throw libtorrent_exception(ec); + return ret; +} +#endif + +boost::shared_ptr new_feed(aux::session_impl& ses, feed_settings const& sett) +{ + return boost::shared_ptr(new feed(ses, sett)); +} + +feed::feed(aux::session_impl& ses, feed_settings const& sett) + : m_last_attempt(0) + , m_last_update(0) + , m_ttl(-1) + , m_failures(0) + , m_updating(false) + , m_settings(sett) + , m_ses(ses) +{ +} + +void feed::set_settings(feed_settings const& s) +{ + m_settings = s; +} + +void feed::get_settings(feed_settings* s) const +{ + *s = m_settings; +} + +feed_handle feed::my_handle() +{ + return feed_handle(boost::weak_ptr(shared_from_this())); +} + +void feed::on_feed(error_code const& ec + , http_parser const& parser, char const* data, int size) +{ + // enabling this assert makes the unit test a lot more difficult +// TORRENT_ASSERT(m_updating); + m_updating = false; + + if (ec && ec != asio::error::eof) + { + ++m_failures; + m_error = ec; + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(rss_alert(my_handle(), m_settings.url + , rss_alert::state_error, m_error)); + } + return; + } + + if (parser.status_code() != 200) + { + ++m_failures; + m_error = error_code(parser.status_code(), get_http_category()); + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(rss_alert(my_handle(), m_settings.url + , rss_alert::state_error, m_error)); + } + return; + } + + m_failures = 0; + + char* buf = const_cast(data); + + feed_state s(*this); + xml_parse(buf, buf + size, boost::bind(&parse_feed, boost::ref(s), _1, _2, _3)); + + time_t now = time(NULL); + + // keep history of the typical feed size times 5 + int max_history = (std::max)(s.num_items * 5, 100); + + // this is not very efficient, but that's probably OK for now + while (int(m_added.size()) > max_history) + { + // loop over all elements and find the one with the lowest timestamp + // i.e. it was added the longest ago, then remove it + std::map::iterator i = std::min_element( + m_added.begin(), m_added.end() + , boost::bind(&std::pair::second, _1) + < boost::bind(&std::pair::second, _2)); + m_added.erase(i); + } + + m_last_update = now; + + // report that we successfully updated the feed + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(rss_alert(my_handle(), m_settings.url + , rss_alert::state_updated, error_code())); + } + + // update m_ses.m_next_rss_update timestamps + // now that we have updated our timestamp + m_ses.update_rss_feeds(); +} + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#endif + +#define TORRENT_SETTING(t, x) {#x, offsetof(feed_settings,x), t}, + bencode_map_entry feed_settings_map[] = + { + TORRENT_SETTING(std_string, url) + TORRENT_SETTING(boolean, auto_download) + TORRENT_SETTING(boolean, auto_map_handles) + TORRENT_SETTING(integer, default_ttl) + }; +#undef TORRENT_SETTING + +#define TORRENT_SETTING(t, x) {#x, offsetof(feed_item,x), t}, + bencode_map_entry feed_item_map[] = + { + TORRENT_SETTING(std_string, url) + TORRENT_SETTING(std_string, uuid) + TORRENT_SETTING(std_string, title) + TORRENT_SETTING(std_string, description) + TORRENT_SETTING(std_string, comment) + TORRENT_SETTING(std_string, category) + TORRENT_SETTING(size_integer, size) + }; +#undef TORRENT_SETTING + +#define TORRENT_SETTING(t, x) {#x, offsetof(feed,x), t}, + bencode_map_entry feed_map[] = + { + TORRENT_SETTING(std_string, m_title) + TORRENT_SETTING(std_string, m_description) + TORRENT_SETTING(time_integer, m_last_attempt) + TORRENT_SETTING(time_integer, m_last_update) + }; +#undef TORRENT_SETTING + +#define TORRENT_SETTING(t, x) {#x, offsetof(add_torrent_params,x), t}, + bencode_map_entry add_torrent_map[] = + { + TORRENT_SETTING(std_string, save_path) + TORRENT_SETTING(size_integer, flags) + }; +#undef TORRENT_SETTING + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +void feed::load_state(lazy_entry const& rd) +{ + load_struct(rd, this, feed_map, sizeof(feed_map)/sizeof(feed_map[0])); + lazy_entry const* e = rd.dict_find_list("items"); + if (e) + { + m_items.reserve(e->list_size()); + for (int i = 0; i < e->list_size(); ++i) + { + if (e->list_at(i)->type() != lazy_entry::dict_t) continue; + m_items.push_back(feed_item()); + load_struct(*e->list_at(i), &m_items.back(), feed_item_map + , sizeof(feed_item_map)/sizeof(feed_item_map[0])); + + // don't load duplicates + if (m_urls.find(m_items.back().url) != m_urls.end()) + { + m_items.pop_back(); + continue; + } + m_urls.insert(m_items.back().url); + } + } + load_struct(rd, &m_settings, feed_settings_map + , sizeof(feed_settings_map)/sizeof(feed_settings_map[0])); + e = rd.dict_find_dict("add_params"); + if (e) + { + load_struct(*e, &m_settings.add_args, add_torrent_map + , sizeof(add_torrent_map)/sizeof(add_torrent_map[0])); + } + + e = rd.dict_find_list("history"); + if (e) + { + for (int i = 0; i < e->list_size(); ++i) + { + if (e->list_at(i)->type() != lazy_entry::list_t) continue; + + lazy_entry const* item = e->list_at(i); + + if (item->list_size() != 2 + || item->list_at(0)->type() != lazy_entry::string_t + || item->list_at(1)->type() != lazy_entry::int_t) + continue; + + m_added.insert(std::pair( + item->list_at(0)->string_value() + , item->list_at(1)->int_value())); + } + } +} + +void feed::save_state(entry& rd) const +{ + // feed properties + save_struct(rd, this, feed_map, sizeof(feed_map)/sizeof(feed_map[0])); + + // items + entry::list_type& items = rd["items"].list(); + for (std::vector::const_iterator i = m_items.begin() + , end(m_items.end()); i != end; ++i) + { + items.push_back(entry()); + entry& item = items.back(); + save_struct(item, &*i, feed_item_map, sizeof(feed_item_map)/sizeof(feed_item_map[0])); + } + + // settings + feed_settings sett_def; + save_struct(rd, &m_settings, feed_settings_map + , sizeof(feed_settings_map)/sizeof(feed_settings_map[0]), &sett_def); + entry& add = rd["add_params"]; + add_torrent_params add_def; + save_struct(add, &m_settings.add_args, add_torrent_map + , sizeof(add_torrent_map)/sizeof(add_torrent_map[0]), &add_def); + + entry::list_type& history = rd["history"].list(); + for (std::map::const_iterator i = m_added.begin() + , end(m_added.end()); i != end; ++i) + { + history.push_back(entry()); + entry::list_type& item = history.back().list(); + item.push_back(entry(i->first)); + item.push_back(entry(i->second)); + } +} + +void feed::add_item(feed_item const& item) +{ + // don't add duplicates + if (m_urls.find(item.url) != m_urls.end()) + return; + + m_urls.insert(item.url); + m_items.push_back(item); + + feed_item& i = m_items.back(); + + if (m_settings.auto_map_handles) + i.handle = torrent_handle(m_ses.find_torrent(i.uuid.empty() ? i.url : i.uuid)); + + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(rss_item_alert(my_handle(), i)); + + if (m_settings.auto_download) + { + if (!m_settings.auto_map_handles) + i.handle = torrent_handle(m_ses.find_torrent(i.uuid.empty() ? i.url : i.uuid)); + + // if we're already downloading this torrent + // move along to the next one + if (i.handle.is_valid()) return; + + // has this already been added? + if (m_added.find(i.url) != m_added.end()) return; + + // this means we should add this torrent to the session + add_torrent_params p = m_settings.add_args; + p.url = i.url; + p.uuid = i.uuid; + p.source_feed_url = m_settings.url; + p.ti.reset(); + p.info_hash.clear(); + p.name = i.title.c_str(); + + error_code e; + m_ses.add_torrent(p, e); + time_t now = time(NULL); + m_added.insert(make_pair(i.url, now)); + } +} + +// returns the number of seconds until trying again +int feed::update_feed() +{ + if (m_updating) return 60; + + m_last_attempt = time(0); + m_last_update = 0; + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(rss_alert(my_handle(), m_settings.url + , rss_alert::state_updating, error_code())); + } + + boost::shared_ptr feed( + new http_connection(m_ses.m_io_service, m_ses.m_half_open + , boost::bind(&feed::on_feed, shared_from_this() + , _1, _2, _3, _4))); + + m_updating = true; + feed->get(m_settings.url, seconds(30), 0, 0, 5, m_ses.m_settings.user_agent); + + return 60 + m_failures * m_failures * 60; +} + +void feed::get_feed_status(feed_status* ret) const +{ + ret->items = m_items; + ret->last_update = m_last_update; + ret->updating = m_updating; + ret->url = m_settings.url; + ret->title = m_title; + ret->description = m_description; + ret->error = m_error; + ret->ttl = m_ttl == -1 ? m_settings.default_ttl : m_ttl; + ret->next_update = next_update(time(0)); +} + +int feed::next_update(time_t now) const +{ + if (m_last_update == 0) return int(m_last_attempt + 60 * 5 - now); + int ttl = m_ttl == -1 ? m_settings.default_ttl : m_ttl; + TORRENT_ASSERT((m_last_update + ttl * 60) - now < INT_MAX); + return int((m_last_update + ttl * 60) - now); +} + +// defined in session.cpp +void fun_wrap(bool* done, condition_variable* e, mutex* m, boost::function f); + +#define TORRENT_ASYNC_CALL(x) \ + boost::shared_ptr f = m_feed_ptr.lock(); \ + if (!f) return; \ + aux::session_impl& ses = f->session(); \ + ses.m_io_service.post(boost::bind(&feed:: x, f)) + +#define TORRENT_ASYNC_CALL1(x, a1) \ + boost::shared_ptr f = m_feed_ptr.lock(); \ + if (!f) return; \ + aux::session_impl& ses = f->session(); \ + ses.m_io_service.post(boost::bind(&feed:: x, f, a1)) + +#define TORRENT_SYNC_CALL1(x, a1) \ + boost::shared_ptr f = m_feed_ptr.lock(); \ + if (f) { \ + bool done = false; \ + aux::session_impl& ses = f->session(); \ + mutex::scoped_lock l(ses.mut); \ + ses.m_io_service.post(boost::bind(&fun_wrap, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&feed:: x, f, a1)))); \ + f.reset(); \ + do { ses.cond.wait(l); } while(!done); } + +feed_handle::feed_handle(boost::weak_ptr const& p) + : m_feed_ptr(p) {} + +void feed_handle::update_feed() +{ + TORRENT_ASYNC_CALL(update_feed); +} + +feed_status feed_handle::get_feed_status() const +{ + feed_status ret; + TORRENT_SYNC_CALL1(get_feed_status, &ret); + return ret; +} + +void feed_handle::set_settings(feed_settings const& s) +{ + TORRENT_ASYNC_CALL1(set_settings, s); +} + +feed_settings feed_handle::settings() const +{ + feed_settings ret; + TORRENT_SYNC_CALL1(get_settings, &ret); + return ret; +} + +} + diff --git a/apps/Launcher/ext/libtorrent/src/session.cpp b/apps/Launcher/ext/libtorrent/src/session.cpp new file mode 100644 index 0000000000..c666349417 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/session.cpp @@ -0,0 +1,1417 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg, Magnus Jonsson +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/extensions/ut_pex.hpp" +#include "libtorrent/extensions/ut_metadata.hpp" +#include "libtorrent/extensions/smart_ban.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/fingerprint.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/file.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/ip_filter.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/kademlia/dht_tracker.hpp" +#include "libtorrent/natpmp.hpp" +#include "libtorrent/upnp.hpp" +#include "libtorrent/magnet_uri.hpp" + +using boost::shared_ptr; +using boost::weak_ptr; +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + + TORRENT_EXPORT void TORRENT_LINK_TEST_NAME() {} + + // this function returns a session_settings object + // which will optimize libtorrent for minimum memory + // usage, with no consideration of performance. + TORRENT_EXPORT session_settings min_memory_usage() + { + session_settings set; + + set.alert_queue_size = 100; + + set.max_allowed_in_request_queue = 100; + + // setting this to a low limit, means more + // peers are more likely to request from the + // same piece. Which means fewer partial + // pieces and fewer entries in the partial + // piece list + set.whole_pieces_threshold = 2; + set.use_parole_mode = false; + set.prioritize_partial_pieces = true; + + // connect to 5 peers per second + set.connection_speed = 5; + + // be extra nice on the hard drive when running + // on embedded devices. This might slow down + // torrent checking + set.file_checks_delay_per_block = 5; + + // only have 4 files open at a time + set.file_pool_size = 4; + + // we want to keep the peer list as small as possible + set.allow_multiple_connections_per_ip = false; + set.max_failcount = 2; + set.inactivity_timeout = 120; + + // whenever a peer has downloaded one block, write + // it to disk, and don't read anything from the + // socket until the disk write is complete + set.max_queued_disk_bytes = 1; + + // don't keep track of all upnp devices, keep + // the device list small + set.upnp_ignore_nonrouters = true; + + // never keep more than one 16kB block in + // the send buffer + set.send_buffer_watermark = 9; + + // don't use any disk cache + set.cache_size = 0; + set.cache_buffer_chunk_size = 1; + set.use_read_cache = false; + set.use_disk_read_ahead = false; + + set.close_redundant_connections = true; + + set.max_peerlist_size = 500; + set.max_paused_peerlist_size = 50; + + // udp trackers are cheaper to talk to + set.prefer_udp_trackers = true; + + set.max_rejects = 10; + + set.recv_socket_buffer_size = 16 * 1024; + set.send_socket_buffer_size = 16 * 1024; + + // use less memory when checking pieces + set.optimize_hashing_for_speed = false; + + // use less memory when reading and writing + // whole pieces + set.coalesce_reads = false; + set.coalesce_writes = false; + + // disallow the buffer size to grow for the uTP socket + set.utp_dynamic_sock_buf = false; + + // max 'bottled' http receive buffer/url torrent size + set.max_http_recv_buffer_size = 1024 * 1024; + + return set; + } + + TORRENT_EXPORT session_settings high_performance_seed() + { + session_settings set; + + // allow peers to request a lot of blocks at a time, + // to be more likely to saturate the bandwidth-delay- + // product. + set.max_out_request_queue = 1500; + set.max_allowed_in_request_queue = 2000; + + // don't throttle TCP, assume there is + // plenty of bandwidth + set.mixed_mode_algorithm = session_settings::prefer_tcp; + + // we will probably see a high rate of alerts, make it less + // likely to loose alerts + set.alert_queue_size = 50000; + + // allow 500 files open at a time + set.file_pool_size = 500; + + // don't update access time for each read/write + set.no_atime_storage = true; + + // as a seed box, we must accept multiple peers behind + // the same NAT + set.allow_multiple_connections_per_ip = true; + + // connect to 50 peers per second + set.connection_speed = 50; + + // allow 8000 peer connections + set.connections_limit = 8000; + + // allow lots of peers to try to connect simultaneously + set.listen_queue_size = 200; + + // unchoke many peers + set.unchoke_slots_limit = 500; + + // we need more DHT capacity to ping more peers + // candidates before trying to connect + set.dht_upload_rate_limit = 20000; + + // we're more interested in downloading than seeding + // only service a read job every 1000 write job (when + // disk is congested). Presumably on a big box, writes + // are extremely cheap and reads are relatively expensive + // so that's the main reason this ratio should be adjusted + set.read_job_every = 100; + + // use 1 GB of cache + set.cache_size = 32768 * 2; + set.use_read_cache = true; + set.cache_buffer_chunk_size = 128; + set.read_cache_line_size = 32; + set.write_cache_line_size = 32; + set.low_prio_disk = false; + // one hour expiration + set.cache_expiry = 60 * 60; + // this is expensive and could add significant + // delays when freeing a large number of buffers + set.lock_disk_cache = false; + + // the max number of bytes pending write before we throttle + // download rate + set.max_queued_disk_bytes = 10 * 1024 * 1024; + // flush write cache in a way to minimize the amount we need to + // read back once we want to hash-check the piece. i.e. try to + // flush all blocks in-order + set.disk_cache_algorithm = session_settings::avoid_readback; + + set.explicit_read_cache = false; + // prevent fast pieces to interfere with suggested pieces + // since we unchoke everyone, we don't need fast pieces anyway + set.allowed_fast_set_size = 0; + // suggest pieces in the read cache for higher cache hit rate + set.suggest_mode = session_settings::suggest_read_cache; + + set.close_redundant_connections = true; + + set.max_rejects = 10; + + set.recv_socket_buffer_size = 1024 * 1024; + set.send_socket_buffer_size = 1024 * 1024; + + set.optimize_hashing_for_speed = true; + + // don't let connections linger for too long + set.request_timeout = 10; + set.peer_timeout = 20; + set.inactivity_timeout = 20; + + set.active_limit = 2000; + set.active_tracker_limit = 2000; + set.active_dht_limit = 600; + set.active_seeds = 2000; + + set.choking_algorithm = session_settings::fixed_slots_choker; + + // in order to be able to deliver very high + // upload rates, this should be able to cover + // the bandwidth delay product. Assuming an RTT + // of 500 ms, and a send rate of 20 MB/s, the upper + // limit should be 10 MB + set.send_buffer_watermark = 3 * 1024 * 1024; + + // put 1.5 seconds worth of data in the send buffer + // this gives the disk I/O more heads-up on disk + // reads, and can maximize throughput + set.send_buffer_watermark_factor = 150; + + // always stuff at least 1 MiB down each peer + // pipe, to quickly ramp up send rates + set.send_buffer_low_watermark = 1 * 1024 * 1024; + + // don't retry peers if they fail once. Let them + // connect to us if they want to + set.max_failcount = 1; + + // allow the buffer size to grow for the uTP socket + set.utp_dynamic_sock_buf = true; + + // max 'bottled' http receive buffer/url torrent size + set.max_http_recv_buffer_size = 6 * 1024 * 1024; + + // the disk cache performs better with the pool allocator + set.use_disk_cache_pool = true; + + return set; + } + + // wrapper around a function that's executed in the network thread + // ans synchronized in the client thread + template + void fun_ret(R* ret, bool* done, condition_variable* e, mutex* m, boost::function f) + { + *ret = f(); + mutex::scoped_lock l(*m); + *done = true; + e->notify_all(); + } + + void fun_wrap(bool* done, condition_variable* e, mutex* m, boost::function f) + { + f(); + mutex::scoped_lock l(*m); + *done = true; + e->notify_all(); + } + +#define TORRENT_ASYNC_CALL(x) \ + m_impl->m_io_service.dispatch(boost::bind(&session_impl:: x, m_impl.get())) + +#define TORRENT_ASYNC_CALL1(x, a1) \ + m_impl->m_io_service.dispatch(boost::bind(&session_impl:: x, m_impl.get(), a1)) + +#define TORRENT_ASYNC_CALL2(x, a1, a2) \ + m_impl->m_io_service.dispatch(boost::bind(&session_impl:: x, m_impl.get(), a1, a2)) + +#define TORRENT_ASYNC_CALL3(x, a1, a2, a3) \ + m_impl->m_io_service.dispatch(boost::bind(&session_impl:: x, m_impl.get(), a1, a2, a3)) + +#define TORRENT_WAIT \ + mutex::scoped_lock l(m_impl->mut); \ + while (!done) { m_impl->cond.wait(l); }; + +#define TORRENT_SYNC_CALL(x) \ + bool done = false; \ + m_impl->m_io_service.dispatch(boost::bind(&fun_wrap, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get())))); \ + TORRENT_WAIT + +#define TORRENT_SYNC_CALL1(x, a1) \ + bool done = false; \ + m_impl->m_io_service.dispatch(boost::bind(&fun_wrap, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1)))); \ + TORRENT_WAIT + +#define TORRENT_SYNC_CALL2(x, a1, a2) \ + bool done = false; \ + m_impl->m_io_service.dispatch(boost::bind(&fun_wrap, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1, a2)))); \ + TORRENT_WAIT + +#define TORRENT_SYNC_CALL3(x, a1, a2, a3) \ + bool done = false; \ + m_impl->m_io_service.dispatch(boost::bind(&fun_wrap, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1, a2, a3)))); \ + TORRENT_WAIT + +#define TORRENT_SYNC_CALL4(x, a1, a2, a3, a4) \ + bool done = false; \ + m_impl->m_io_service.dispatch(boost::bind(&fun_wrap, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1, a2, a3, a4)))); \ + TORRENT_WAIT + +#define TORRENT_SYNC_CALL_RET(type, x) \ + bool done = false; \ + type r; \ + m_impl->m_io_service.dispatch(boost::bind(&fun_ret, &r, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get())))); \ + TORRENT_WAIT + +#define TORRENT_SYNC_CALL_RET1(type, x, a1) \ + bool done = false; \ + type r; \ + m_impl->m_io_service.dispatch(boost::bind(&fun_ret, &r, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1)))); \ + TORRENT_WAIT + +#define TORRENT_SYNC_CALL_RET2(type, x, a1, a2) \ + bool done = false; \ + type r; \ + m_impl->m_io_service.dispatch(boost::bind(&fun_ret, &r, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1, a2)))); \ + TORRENT_WAIT + +#define TORRENT_SYNC_CALL_RET3(type, x, a1, a2, a3) \ + bool done = false; \ + type r; \ + m_impl->m_io_service.dispatch(boost::bind(&fun_ret, &r, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1, a2, a3)))); \ + TORRENT_WAIT + +#ifndef TORRENT_CFG +#error TORRENT_CFG is not defined! +#endif + + // this is a dummy function that's exported and named based + // on the configuration. The session.hpp file will reference + // it and if the library and the client are built with different + // configurations this will give a link error + void TORRENT_EXPORT TORRENT_CFG() {} + +#if defined _MSC_VER && defined TORRENT_DEBUG + static void straight_to_debugger(unsigned int, _EXCEPTION_POINTERS*) + { throw; } +#endif + + void session::init(std::pair listen_range, char const* listen_interface + , fingerprint const& id, boost::uint32_t alert_mask) + { +#if defined _MSC_VER && defined TORRENT_DEBUG + // workaround for microsofts + // hardware exceptions that makes + // it hard to debug stuff + ::_set_se_translator(straight_to_debugger); +#endif + + m_impl.reset(new session_impl(listen_range, id, listen_interface, alert_mask)); + } + + void session::set_log_path(std::string const& p) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING \ + || defined TORRENT_ERROR_LOGGING + m_impl->set_log_path(p); +#endif + } + + void session::start(int flags) + { +#ifndef TORRENT_DISABLE_EXTENSIONS + if (flags & add_default_plugins) + { + add_extension(create_ut_pex_plugin); + add_extension(create_ut_metadata_plugin); + add_extension(create_smart_ban_plugin); + } +#endif + + m_impl->start_session(); + + if (flags & start_default_features) + { + start_upnp(); + start_natpmp(); +#ifndef TORRENT_DISABLE_DHT + start_dht(); +#endif + start_lsd(); + } + } + + session::~session() + { + TORRENT_ASSERT(m_impl); + // if there is at least one destruction-proxy + // abort the session and let the destructor + // of the proxy to syncronize + if (!m_impl.unique()) + { + TORRENT_ASYNC_CALL(abort); + } + } + + void session::save_state(entry& e, boost::uint32_t flags) const + { + TORRENT_SYNC_CALL2(save_state, &e, flags); + } + + void session::load_state(lazy_entry const& e) + { + // this needs to be synchronized since the lifespan + // of e is tied to the caller + TORRENT_SYNC_CALL1(load_state, &e); + } + + feed_handle session::add_feed(feed_settings const& feed) + { + // if you have auto-download enabled, you must specify a download directory! + TORRENT_ASSERT_PRECOND(!feed.auto_download || !feed.add_args.save_path.empty()); + TORRENT_SYNC_CALL_RET1(feed_handle, add_feed, feed); + return r; + } + + void session::remove_feed(feed_handle h) + { + TORRENT_ASYNC_CALL1(remove_feed, h); + } + + void session::get_feeds(std::vector& f) const + { + f.clear(); + TORRENT_SYNC_CALL1(get_feeds, &f); + } + + void session::add_extension(boost::function(torrent*, void*)> ext) + { +#ifndef TORRENT_DISABLE_EXTENSIONS + TORRENT_ASYNC_CALL1(add_extension, ext); +#endif + } + + void session::add_extension(boost::shared_ptr ext) + { +#ifndef TORRENT_DISABLE_EXTENSIONS + TORRENT_ASYNC_CALL1(add_ses_extension, ext); +#endif + } + + void session::load_asnum_db(char const* file) + { +#ifndef TORRENT_DISABLE_GEO_IP + TORRENT_ASYNC_CALL1(load_asnum_db, std::string(file)); +#endif + } + + void session::load_country_db(char const* file) + { +#ifndef TORRENT_DISABLE_GEO_IP + TORRENT_ASYNC_CALL1(load_country_db, std::string(file)); +#endif + } + + int session::as_for_ip(address const& addr) + { +#ifndef TORRENT_DISABLE_GEO_IP + return m_impl->as_for_ip(addr); +#else + return 0; +#endif + } + +#if TORRENT_USE_WSTRING +#ifndef TORRENT_NO_DEPRECATE + void session::load_asnum_db(wchar_t const* file) + { +#ifndef TORRENT_DISABLE_GEO_IP + TORRENT_ASYNC_CALL1(load_asnum_dbw, std::wstring(file)); +#endif + } + + void session::load_country_db(wchar_t const* file) + { +#ifndef TORRENT_DISABLE_GEO_IP + TORRENT_ASYNC_CALL1(load_country_dbw, std::wstring(file)); +#endif + } +#endif // TORRENT_NO_DEPRECATE +#endif // TORRENT_USE_WSTRING + +#ifndef TORRENT_NO_DEPRECATE + void session::load_state(entry const& ses_state) + { + if (ses_state.type() == entry::undefined_t) return; + std::vector buf; + bencode(std::back_inserter(buf), ses_state); + lazy_entry e; + error_code ec; +#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS || !defined BOOST_NO_EXCEPTIONS + int ret = +#endif + lazy_bdecode(&buf[0], &buf[0] + buf.size(), e, ec); + + TORRENT_ASSERT(ret == 0); +#ifndef BOOST_NO_EXCEPTIONS + if (ret != 0) throw libtorrent_exception(ec); +#endif + TORRENT_SYNC_CALL1(load_state, &e); + } + + entry session::state() const + { + entry ret; + TORRENT_SYNC_CALL2(save_state, &ret, 0xffffffff); + return ret; + } +#endif + + void session::set_ip_filter(ip_filter const& f) + { + TORRENT_ASYNC_CALL1(set_ip_filter, f); + } + + ip_filter session::get_ip_filter() const + { + TORRENT_SYNC_CALL_RET(ip_filter, get_ip_filter); + return r; + } + + void session::set_port_filter(port_filter const& f) + { + TORRENT_ASYNC_CALL1(set_port_filter, f); + } + + void session::set_peer_id(peer_id const& id) + { + TORRENT_ASYNC_CALL1(set_peer_id, id); + } + + peer_id session::id() const + { + TORRENT_SYNC_CALL_RET(peer_id, get_peer_id); + return r; + } + + io_service& session::get_io_service() + { + return m_impl->m_io_service; + } + + void session::set_key(int key) + { + TORRENT_ASYNC_CALL1(set_key, key); + } + + void session::get_torrent_status(std::vector* ret + , boost::function const& pred + , boost::uint32_t flags) const + { + TORRENT_SYNC_CALL3(get_torrent_status, ret, boost::ref(pred), flags); + } + + void session::refresh_torrent_status(std::vector* ret + , boost::uint32_t flags) const + { + TORRENT_SYNC_CALL2(refresh_torrent_status, ret, flags); + } + + void session::post_torrent_updates() + { + TORRENT_ASYNC_CALL(post_torrent_updates); + } + + std::vector session::get_torrents() const + { + TORRENT_SYNC_CALL_RET(std::vector, get_torrents); + return r; + } + + torrent_handle session::find_torrent(sha1_hash const& info_hash) const + { + TORRENT_SYNC_CALL_RET1(torrent_handle, find_torrent_handle, info_hash); + return r; + } + +#ifndef BOOST_NO_EXCEPTIONS + torrent_handle session::add_torrent(add_torrent_params const& params) + { + error_code ec; + TORRENT_SYNC_CALL_RET2(torrent_handle, add_torrent, params, boost::ref(ec)); + if (ec) throw libtorrent_exception(ec); + return r; + } +#endif + + torrent_handle session::add_torrent(add_torrent_params const& params, error_code& ec) + { + ec.clear(); + TORRENT_SYNC_CALL_RET2(torrent_handle, add_torrent, params, boost::ref(ec)); + return r; + } + + void session::async_add_torrent(add_torrent_params const& params) + { + add_torrent_params* p = new add_torrent_params(params); +#ifndef TORRENT_NO_DEPRECATE + if (params.tracker_url) + { + p->trackers.push_back(params.tracker_url); + p->tracker_url = NULL; + } +#endif + TORRENT_ASYNC_CALL1(async_add_torrent, p); + } + +#ifndef BOOST_NO_EXCEPTIONS +#ifndef TORRENT_NO_DEPRECATE + // if the torrent already exists, this will throw duplicate_torrent + torrent_handle session::add_torrent( + torrent_info const& ti + , std::string const& save_path + , entry const& resume_data + , storage_mode_t storage_mode + , bool paused + , storage_constructor_type sc) + { + boost::intrusive_ptr tip(new torrent_info(ti)); + add_torrent_params p(sc); + p.ti = tip; + p.save_path = save_path; + if (resume_data.type() != entry::undefined_t) + { + bencode(std::back_inserter(p.resume_data), resume_data); + } + p.storage_mode = storage_mode; + p.paused = paused; + return add_torrent(p); + } + + torrent_handle session::add_torrent( + boost::intrusive_ptr ti + , std::string const& save_path + , entry const& resume_data + , storage_mode_t storage_mode + , bool paused + , storage_constructor_type sc + , void* userdata) + { + add_torrent_params p(sc); + p.ti = ti; + p.save_path = save_path; + if (resume_data.type() != entry::undefined_t) + { + bencode(std::back_inserter(p.resume_data), resume_data); + } + p.storage_mode = storage_mode; + p.paused = paused; + p.userdata = userdata; + return add_torrent(p); + } + + torrent_handle session::add_torrent( + char const* tracker_url + , sha1_hash const& info_hash + , char const* name + , std::string const& save_path + , entry const& e + , storage_mode_t storage_mode + , bool paused + , storage_constructor_type sc + , void* userdata) + { + add_torrent_params p(sc); + p.tracker_url = tracker_url; + p.info_hash = info_hash; + p.save_path = save_path; + p.paused = paused; + p.userdata = userdata; + return add_torrent(p); + } +#endif // TORRENT_NO_DEPRECATE +#endif // BOOST_NO_EXCEPTIONS + + void session::remove_torrent(const torrent_handle& h, int options) + { + if (!h.is_valid()) +#ifdef BOOST_NO_EXCEPTIONS + return; +#else + throw_invalid_handle(); +#endif + TORRENT_ASYNC_CALL2(remove_torrent, h, options); + } + +#ifndef TORRENT_NO_DEPRECATE + bool session::listen_on( + std::pair const& port_range + , const char* net_interface, int flags) + { + error_code ec; + TORRENT_SYNC_CALL4(listen_on, port_range, boost::ref(ec), net_interface, flags); + return !!ec; + } +#endif + + void session::listen_on( + std::pair const& port_range + , error_code& ec + , const char* net_interface, int flags) + { + TORRENT_SYNC_CALL4(listen_on, port_range, boost::ref(ec), net_interface, flags); + } + + unsigned short session::listen_port() const + { + TORRENT_SYNC_CALL_RET(unsigned short, listen_port); + return r; + } + + unsigned short session::ssl_listen_port() const + { + TORRENT_SYNC_CALL_RET(unsigned short, ssl_listen_port); + return r; + } + + session_status session::status() const + { + TORRENT_SYNC_CALL_RET(session_status, status); + return r; + } + + void session::pause() + { + TORRENT_ASYNC_CALL(pause); + } + + void session::resume() + { + TORRENT_ASYNC_CALL(resume); + } + + bool session::is_paused() const + { + TORRENT_SYNC_CALL_RET(bool, is_paused); + return r; + } + + void session::get_cache_info(sha1_hash const& ih + , std::vector& ret) const + { + m_impl->m_disk_thread.get_cache_info(ih, ret); + } + + cache_status session::get_cache_status() const + { + return m_impl->m_disk_thread.status(); + } + + void session::start_dht() + { +#ifndef TORRENT_DISABLE_DHT + // the state is loaded in load_state() + TORRENT_ASYNC_CALL(start_dht); +#endif + } + + void session::stop_dht() + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL(stop_dht); +#endif + } + + void session::set_dht_settings(dht_settings const& settings) + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL1(set_dht_settings, settings); +#endif + } + +#ifndef TORRENT_NO_DEPRECATE + void session::start_dht(entry const& startup_state) + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL1(start_dht, startup_state); +#endif + } + + entry session::dht_state() const + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_SYNC_CALL_RET(entry, dht_state); + return r; +#else + return entry(); +#endif + } +#endif // TORRENT_NO_DEPRECATE + + void session::add_dht_node(std::pair const& node) + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL1(add_dht_node_name, node); +#endif + } + + void session::add_dht_router(std::pair const& node) + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL1(add_dht_router, node); +#endif + } + + bool session::is_dht_running() const + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_SYNC_CALL_RET(bool, is_dht_running); + return r; +#else + return false; +#endif + } + + void session::dht_get_item(sha1_hash const& target) + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL1(dht_get_immutable_item, target); +#endif + } + + void session::dht_get_item(boost::array key + , std::string salt) + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL2(dht_get_mutable_item, key, salt); +#endif + } + + sha1_hash session::dht_put_item(entry data) + { + std::vector buf; + bencode(std::back_inserter(buf), data); + sha1_hash ret = hasher(&buf[0], buf.size()).final(); + +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL2(dht_put_item, data, ret); +#endif + return ret; + } + + void session::dht_put_item(boost::array key + , boost::function& + , boost::uint64_t&, std::string const&)> cb + , std::string salt) + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL3(dht_put_mutable_item, key, cb, salt); +#endif + } + + void session::set_pe_settings(pe_settings const& settings) + { +#ifndef TORRENT_DISABLE_ENCRYPTION + TORRENT_ASYNC_CALL1(set_pe_settings, settings); +#endif + } + + pe_settings session::get_pe_settings() const + { +#ifndef TORRENT_DISABLE_ENCRYPTION + TORRENT_SYNC_CALL_RET(pe_settings, get_pe_settings); +#else + pe_settings r; +#endif + return r; + } + + bool session::is_listening() const + { + TORRENT_SYNC_CALL_RET(bool, is_listening); + return r; + } + + void session::set_settings(session_settings const& s) + { + TORRENT_ASYNC_CALL1(set_settings, s); + } + + session_settings session::settings() const + { + TORRENT_SYNC_CALL_RET(session_settings, settings); + return r; + } + + void session::set_proxy(proxy_settings const& s) + { + TORRENT_ASYNC_CALL1(set_proxy, s); + } + + proxy_settings session::proxy() const + { + TORRENT_SYNC_CALL_RET(proxy_settings, proxy); + return r; + } + +#ifndef TORRENT_NO_DEPRECATE + void session::set_peer_proxy(proxy_settings const& s) + { + TORRENT_ASYNC_CALL1(set_peer_proxy, s); + } + + void session::set_web_seed_proxy(proxy_settings const& s) + { + TORRENT_ASYNC_CALL1(set_web_seed_proxy, s); + } + + void session::set_tracker_proxy(proxy_settings const& s) + { + TORRENT_ASYNC_CALL1(set_tracker_proxy, s); + } + + proxy_settings session::peer_proxy() const + { + TORRENT_SYNC_CALL_RET(proxy_settings, peer_proxy); + return r; + } + + proxy_settings session::web_seed_proxy() const + { + TORRENT_SYNC_CALL_RET(proxy_settings, web_seed_proxy); + return r; + } + + proxy_settings session::tracker_proxy() const + { + TORRENT_SYNC_CALL_RET(proxy_settings, tracker_proxy); + return r; + } + + + void session::set_dht_proxy(proxy_settings const& s) + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL1(set_dht_proxy, s); +#endif + } + + proxy_settings session::dht_proxy() const + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_SYNC_CALL_RET(proxy_settings, dht_proxy); + return r; +#else + return proxy_settings(); +#endif + } +#endif // TORRENT_NO_DEPRECATE + + void session::set_i2p_proxy(proxy_settings const& s) + { +#if TORRENT_USE_I2P + TORRENT_ASYNC_CALL1(set_i2p_proxy, s); +#endif + } + + proxy_settings session::i2p_proxy() const + { +#if TORRENT_USE_I2P + TORRENT_SYNC_CALL_RET(proxy_settings, i2p_proxy); +#else + proxy_settings r; +#endif + return r; + } + +#ifdef TORRENT_STATS + void session::enable_stats_logging(bool s) + { + TORRENT_ASYNC_CALL1(enable_stats_logging, s); + } +#endif + +#ifndef TORRENT_NO_DEPRECATE + int session::max_uploads() const + { + TORRENT_SYNC_CALL_RET(int, max_uploads); + return r; + } + + void session::set_max_uploads(int limit) + { + TORRENT_ASYNC_CALL1(set_max_uploads, limit); + } + + int session::max_connections() const + { + TORRENT_SYNC_CALL_RET(int, max_connections); + return r; + } + + void session::set_max_connections(int limit) + { + TORRENT_ASYNC_CALL1(set_max_connections, limit); + } + + int session::max_half_open_connections() const + { + TORRENT_SYNC_CALL_RET(int, max_half_open_connections); + return r; + } + + void session::set_max_half_open_connections(int limit) + { + TORRENT_ASYNC_CALL1(set_max_half_open_connections, limit); + } + + int session::local_upload_rate_limit() const + { + TORRENT_SYNC_CALL_RET(int, local_upload_rate_limit); + return r; + } + + int session::local_download_rate_limit() const + { + TORRENT_SYNC_CALL_RET(int, local_download_rate_limit); + return r; + } + + int session::upload_rate_limit() const + { + TORRENT_SYNC_CALL_RET(int, upload_rate_limit); + return r; + } + + int session::download_rate_limit() const + { + TORRENT_SYNC_CALL_RET(int, download_rate_limit); + return r; + } + + void session::set_local_upload_rate_limit(int bytes_per_second) + { + TORRENT_ASYNC_CALL1(set_local_upload_rate_limit, bytes_per_second); + } + + void session::set_local_download_rate_limit(int bytes_per_second) + { + TORRENT_ASYNC_CALL1(set_local_download_rate_limit, bytes_per_second); + } + + void session::set_upload_rate_limit(int bytes_per_second) + { + TORRENT_ASYNC_CALL1(set_upload_rate_limit, bytes_per_second); + } + + void session::set_download_rate_limit(int bytes_per_second) + { + TORRENT_ASYNC_CALL1(set_download_rate_limit, bytes_per_second); + } + + int session::num_uploads() const + { + TORRENT_SYNC_CALL_RET(int, num_uploads); + return r; + } + + int session::num_connections() const + { + TORRENT_SYNC_CALL_RET(int, num_connections); + return r; + } +#endif // TORRENT_NO_DEPRECATE + + void session::set_alert_dispatch(boost::function)> const& fun) + { + TORRENT_ASYNC_CALL1(set_alert_dispatch, fun); + } + + std::auto_ptr session::pop_alert() + { + return m_impl->pop_alert(); + } + + void session::pop_alerts(std::deque* alerts) + { + for (std::deque::iterator i = alerts->begin() + , end(alerts->end()); i != end; ++i) + delete *i; + alerts->clear(); + m_impl->pop_alerts(alerts); + } + + alert const* session::wait_for_alert(time_duration max_wait) + { + return m_impl->wait_for_alert(max_wait); + } + + void session::set_alert_mask(boost::uint32_t m) + { + TORRENT_ASYNC_CALL1(set_alert_mask, m); + } + +#ifndef TORRENT_NO_DEPRECATE + size_t session::set_alert_queue_size_limit(size_t queue_size_limit_) + { + TORRENT_SYNC_CALL_RET1(size_t, set_alert_queue_size_limit, queue_size_limit_); + return r; + } + + void session::set_severity_level(alert::severity_t s) + { + int m = 0; + switch (s) + { + case alert::debug: m = alert::all_categories; break; + case alert::info: m = alert::all_categories & ~(alert::debug_notification + | alert::progress_notification | alert::dht_notification); break; + case alert::warning: m = alert::all_categories & ~(alert::debug_notification + | alert::status_notification | alert::progress_notification + | alert::dht_notification); break; + case alert::critical: m = alert::error_notification | alert::storage_notification; break; + case alert::fatal: m = alert::error_notification; break; + default: break; + } + + TORRENT_ASYNC_CALL1(set_alert_mask, m); + } +#endif + + void session::start_lsd() + { + TORRENT_ASYNC_CALL(start_lsd); + } + + void session::start_natpmp() + { + TORRENT_ASYNC_CALL(start_natpmp); + } + + void session::start_upnp() + { + TORRENT_ASYNC_CALL(start_upnp); + } + + int session::add_port_mapping(protocol_type t, int external_port, int local_port) + { + TORRENT_SYNC_CALL_RET3(int, add_port_mapping, int(t), external_port, local_port); + return r; + } + + void session::delete_port_mapping(int handle) + { + TORRENT_ASYNC_CALL1(delete_port_mapping, handle); + } + + void session::stop_lsd() + { + TORRENT_ASYNC_CALL(stop_lsd); + } + + void session::stop_natpmp() + { + TORRENT_ASYNC_CALL(stop_natpmp); + } + + void session::stop_upnp() + { + TORRENT_ASYNC_CALL(stop_upnp); + } + + connection_queue& session::get_connection_queue() + { + return m_impl->m_half_open; + } + + session_settings::session_settings(std::string const& user_agent_) + : version(LIBTORRENT_VERSION_NUM) + , user_agent(user_agent_) + , tracker_completion_timeout(30) + , tracker_receive_timeout(10) + , stop_tracker_timeout(5) + , tracker_maximum_response_length(1024*1024) + , piece_timeout(20) + , request_timeout(50) + , request_queue_time(3) + , max_allowed_in_request_queue(500) + , max_out_request_queue(500) + , whole_pieces_threshold(20) + , peer_timeout(120) + , urlseed_timeout(20) + , urlseed_pipeline_size(5) + , urlseed_wait_retry(30) + , file_pool_size(40) + , allow_multiple_connections_per_ip(false) + , max_failcount(3) + , min_reconnect_time(60) + , peer_connect_timeout(15) + , ignore_limits_on_local_network(true) + , connection_speed(6) + , send_redundant_have(true) + , lazy_bitfields(false) + , inactivity_timeout(600) + , unchoke_interval(15) + , optimistic_unchoke_interval(30) + , num_want(200) + , initial_picker_threshold(4) + , allowed_fast_set_size(10) + , suggest_mode(no_piece_suggestions) + , max_queued_disk_bytes(1024 * 1024) + , max_queued_disk_bytes_low_watermark(0) + , handshake_timeout(10) + , use_dht_as_fallback(false) + , free_torrent_hashes(true) + , upnp_ignore_nonrouters(false) + , send_buffer_low_watermark(512) + , send_buffer_watermark(500 * 1024) + , send_buffer_watermark_factor(50) +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 0.16 + , auto_upload_slots(true) + , auto_upload_slots_rate_based(true) +#endif + , choking_algorithm(fixed_slots_choker) + , seed_choking_algorithm(round_robin) + , use_parole_mode(true) + , cache_size(1024) + , cache_buffer_chunk_size(16) + , cache_expiry(300) + , use_read_cache(true) + , explicit_read_cache(0) + , explicit_cache_interval(30) + , disk_io_write_mode(0) + , disk_io_read_mode(0) + , coalesce_reads(false) + , coalesce_writes(false) + , outgoing_ports(0,0) + , peer_tos(0) + , active_downloads(3) + , active_seeds(5) + , active_dht_limit(88) // don't announce more than once every 40 seconds + , active_tracker_limit(1600) // don't announce to trackers more than once every 1.125 seconds + , active_lsd_limit(60) // don't announce to local network more than once every 5 seconds + , active_limit(15) + , auto_manage_prefer_seeds(false) + , dont_count_slow_torrents(true) + , auto_manage_interval(30) + , share_ratio_limit(2.f) + , seed_time_ratio_limit(7.f) + , seed_time_limit(24 * 60 * 60) // 24 hours + , peer_turnover_interval(300) + , peer_turnover(2 / 50.f) + , peer_turnover_cutoff(.9f) + , close_redundant_connections(true) + , auto_scrape_interval(1800) + , auto_scrape_min_interval(300) + , max_peerlist_size(4000) + , max_paused_peerlist_size(4000) + , min_announce_interval(5 * 60) + , prioritize_partial_pieces(false) + , auto_manage_startup(60) + , rate_limit_ip_overhead(true) + , announce_to_all_trackers(false) + , announce_to_all_tiers(false) + , prefer_udp_trackers(true) + , strict_super_seeding(false) + , seeding_piece_quota(20) +#ifdef TORRENT_WINDOWS + , max_sparse_regions(30000) +#else + , max_sparse_regions(0) +#endif + , lock_disk_cache(false) + , max_rejects(50) + , recv_socket_buffer_size(0) + , send_socket_buffer_size(0) + , optimize_hashing_for_speed(true) + , file_checks_delay_per_block(0) + , disk_cache_algorithm(avoid_readback) + , read_cache_line_size(32) + , write_cache_line_size(32) + , optimistic_disk_retry(10 * 60) + , disable_hash_checks(false) + , allow_reordered_disk_operations(true) + , allow_i2p_mixed(false) + , max_suggest_pieces(10) + , drop_skipped_requests(false) + , low_prio_disk(true) + , local_service_announce_interval(5 * 60) + , dht_announce_interval(15 * 60) + , udp_tracker_token_expiry(60) + , volatile_read_cache(false) + , guided_read_cache(false) + , default_cache_min_age(1) + , num_optimistic_unchoke_slots(0) + , no_atime_storage(true) + , default_est_reciprocation_rate(16000) + , increase_est_reciprocation_rate(20) + , decrease_est_reciprocation_rate(3) + , incoming_starts_queued_torrents(false) + , report_true_downloaded(false) + , strict_end_game_mode(true) + , broadcast_lsd(true) + , enable_outgoing_utp(true) + , enable_incoming_utp(true) + , enable_outgoing_tcp(true) + , enable_incoming_tcp(true) + , max_pex_peers(50) + , ignore_resume_timestamps(false) + , no_recheck_incomplete_resume(false) + , anonymous_mode(false) + , force_proxy(false) + , tick_interval(500) + , report_web_seed_downloads(true) + , share_mode_target(3) + , upload_rate_limit(0) + , download_rate_limit(0) + , local_upload_rate_limit(0) + , local_download_rate_limit(0) + , dht_upload_rate_limit(4000) + , unchoke_slots_limit(8) + , half_open_limit(0) + , connections_limit(200) + , connections_slack(10) + , utp_target_delay(100) // milliseconds + , utp_gain_factor(1500) // bytes per rtt + , utp_min_timeout(500) // milliseconds + , utp_syn_resends(2) + , utp_fin_resends(2) + , utp_num_resends(6) + , utp_connect_timeout(3000) // milliseconds +#ifndef TORRENT_NO_DEPRECATE + , utp_delayed_ack(0) // milliseconds +#endif + , utp_dynamic_sock_buf(false) // this doesn't seem quite reliable yet + , utp_loss_multiplier(50) // specified in percent + , mixed_mode_algorithm(peer_proportional) + , rate_limit_utp(true) + , listen_queue_size(5) + , announce_double_nat(false) + , torrent_connect_boost(10) + , seeding_outgoing_connections(true) + , no_connect_privileged_ports(true) + , alert_queue_size(6000) + , max_metadata_size(3*1024*1024) + , smooth_connects(true) + , always_send_user_agent(false) + , apply_ip_filter_to_trackers(true) + , read_job_every(10) + , use_disk_read_ahead(true) + , lock_files(false) + , ssl_listen(4433) + , tracker_backoff(250) + , ban_web_seeds(true) + , max_http_recv_buffer_size(4*1024*1024) + , support_share_mode(true) + , support_merkle_torrents(false) + , report_redundant_bytes(true) + , use_disk_cache_pool(false) + , inactive_down_rate(2048) + , inactive_up_rate(2048) + {} + + session_settings::~session_settings() {} +} + diff --git a/apps/Launcher/ext/libtorrent/src/session_impl.cpp b/apps/Launcher/ext/libtorrent/src/session_impl.cpp new file mode 100644 index 0000000000..cc22d6aefd --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/session_impl.cpp @@ -0,0 +1,6658 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg, Magnus Jonsson +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include + +#ifdef TORRENT_USE_VALGRIND +#include +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/fingerprint.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/file.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/ip_filter.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#ifndef TORRENT_DISABLE_DHT +#include "libtorrent/kademlia/dht_tracker.hpp" +#endif +#include "libtorrent/enum_net.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/utf8.hpp" +#include "libtorrent/upnp.hpp" +#include "libtorrent/natpmp.hpp" +#include "libtorrent/lsd.hpp" +#include "libtorrent/instantiate_connection.hpp" +#include "libtorrent/peer_info.hpp" +#include "libtorrent/settings.hpp" +#include "libtorrent/build_config.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/random.hpp" +#include "libtorrent/magnet_uri.hpp" + +#if defined TORRENT_STATS && defined __MACH__ +#include +#endif + +#ifndef TORRENT_WINDOWS +#include +#endif + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + +// for logging stat layout +#include "libtorrent/stat.hpp" + +// for logging the size of DHT structures +#ifndef TORRENT_DISABLE_DHT +#include +#include +#include +#include +#include +#endif // TORRENT_DISABLE_DHT + +#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/udp_tracker_connection.hpp" + +#include "libtorrent/debug.hpp" + +#if TORRENT_USE_IOSTREAM +namespace libtorrent { +std::ofstream logger::log_file; +std::string logger::open_filename; +mutex logger::file_mutex; +} +#endif // TORRENT_USE_IOSTREAM + +#endif + +#ifdef TORRENT_USE_GCRYPT + +extern "C" { +GCRY_THREAD_OPTION_PTHREAD_IMPL; +} + +namespace +{ + // libgcrypt requires this to initialize the library + struct gcrypt_setup + { + gcrypt_setup() + { + gcry_check_version(0); + gcry_error_t e = gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); + if (e != 0) fprintf(stderr, "libcrypt ERROR: %s\n", gcry_strerror(e)); + e = gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); + if (e != 0) fprintf(stderr, "initialization finished error: %s\n", gcry_strerror(e)); + } + } gcrypt_global_constructor; +} + +#endif // TORRENT_USE_GCRYPT + +#ifdef TORRENT_USE_OPENSSL + +#include +#include + +namespace +{ + // openssl requires this to clean up internal + // structures it allocates + struct openssl_cleanup + { + ~openssl_cleanup() { CRYPTO_cleanup_all_ex_data(); } + } openssl_global_destructor; +} + +#endif // TORRENT_USE_OPENSSL + +#ifdef TORRENT_WINDOWS +// for ERROR_SEM_TIMEOUT +#include +#endif + +using boost::shared_ptr; +using boost::weak_ptr; +using libtorrent::aux::session_impl; + +#ifdef BOOST_NO_EXCEPTIONS +namespace boost { + void throw_exception(std::exception const& e) { ::abort(); } +} +#endif + +namespace libtorrent { + +#if defined TORRENT_ASIO_DEBUGGING + std::map _async_ops; + int _async_ops_nthreads = 0; + mutex _async_ops_mutex; +#endif + +namespace detail +{ + + std::string generate_auth_string(std::string const& user + , std::string const& passwd) + { + if (user.empty()) return std::string(); + return user + ":" + passwd; + } +} + +namespace aux { + +#ifdef TORRENT_STATS + void get_vm_stats(vm_statistics_data_t* vm_stat) + { + memset(vm_stat, 0, sizeof(*vm_stat)); +#if defined __MACH__ + mach_port_t host_port = mach_host_self(); + mach_msg_type_number_t host_count = HOST_VM_INFO_COUNT; + kern_return_t error = host_statistics(host_port, HOST_VM_INFO, + (host_info_t)vm_stat, &host_count); + TORRENT_ASSERT_VAL(error == KERN_SUCCESS, error); +#elif defined TORRENT_LINUX + char buffer[4096]; + char string[1024]; + boost::uint32_t value; + FILE* f = fopen("/proc/vmstat", "r"); + int ret = 0; + while ((ret = fscanf(f, "%s %u\n", string, &value)) != EOF) + { + if (ret != 2) continue; + if (strcmp(string, "nr_active_anon") == 0) vm_stat->active_count += value; + else if (strcmp(string, "nr_active_file") == 0) vm_stat->active_count += value; + else if (strcmp(string, "nr_inactive_anon") == 0) vm_stat->inactive_count += value; + else if (strcmp(string, "nr_inactive_file") == 0) vm_stat->inactive_count += value; + else if (strcmp(string, "nr_free_pages") == 0) vm_stat->free_count = value; + else if (strcmp(string, "nr_unevictable") == 0) vm_stat->wire_count = value; + else if (strcmp(string, "pswpin") == 0) vm_stat->pageins = value; + else if (strcmp(string, "pswpout") == 0) vm_stat->pageouts = value; + else if (strcmp(string, "pgfault") == 0) vm_stat->faults = value; + } + fclose(f); +#endif +// TOOD: windows? + } + + void get_thread_cpu_usage(thread_cpu_usage* tu) + { +#if defined __MACH__ + task_thread_times_info t_info; + mach_msg_type_number_t t_info_count = TASK_THREAD_TIMES_INFO_COUNT; + task_info(mach_task_self(), TASK_THREAD_TIMES_INFO, (task_info_t)&t_info, &t_info_count); + + tu->user_time = min_time() + + seconds(t_info.user_time.seconds) + + microsec(t_info.user_time.microseconds); + tu->system_time = min_time() + + seconds(t_info.system_time.seconds) + + microsec(t_info.system_time.microseconds); +#elif defined TORRENT_LINUX + struct rusage ru; + getrusage(RUSAGE_THREAD, &ru); + tu->user_time = min_time() + + seconds(ru.ru_utime.tv_sec) + + microsec(ru.ru_utime.tv_usec); + tu->system_time = min_time() + + seconds(ru.ru_stime.tv_sec) + + microsec(ru.ru_stime.tv_usec); +#elif defined TORRENT_WINDOWS + FILETIME system_time; + FILETIME user_time; + FILETIME creation_time; + FILETIME exit_time; + GetThreadTimes(GetCurrentThread(), &creation_time, &exit_time, &user_time, &system_time); + + boost::uint64_t utime = (boost::uint64_t(user_time.dwHighDateTime) << 32) + + user_time.dwLowDateTime; + boost::uint64_t stime = (boost::uint64_t(system_time.dwHighDateTime) << 32) + + system_time.dwLowDateTime; + + tu->user_time = min_time() + microsec(utime / 10); + tu->system_time = min_time() + microsec(stime / 10); +#endif + } +#endif //TORRENT_STATS + + struct seed_random_generator + { + seed_random_generator() + { + random_seed((unsigned int)((total_microseconds( + time_now_hires() - min_time())) & 0xffffffff)); + } + }; + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#endif + +#define TORRENT_SETTING(t, x) {#x, offsetof(session_settings,x), t}, + + bencode_map_entry session_settings_map[] = + { + TORRENT_SETTING(std_string, user_agent) + TORRENT_SETTING(integer, tracker_completion_timeout) + TORRENT_SETTING(integer, tracker_receive_timeout) + TORRENT_SETTING(integer, stop_tracker_timeout) + TORRENT_SETTING(integer, tracker_maximum_response_length) + TORRENT_SETTING(integer, piece_timeout) + TORRENT_SETTING(integer, request_timeout) + TORRENT_SETTING(integer, request_queue_time) + TORRENT_SETTING(integer, max_allowed_in_request_queue) + TORRENT_SETTING(integer, max_out_request_queue) + TORRENT_SETTING(integer, whole_pieces_threshold) + TORRENT_SETTING(integer, peer_timeout) + TORRENT_SETTING(integer, urlseed_timeout) + TORRENT_SETTING(integer, urlseed_pipeline_size) + TORRENT_SETTING(integer, urlseed_wait_retry) + TORRENT_SETTING(integer, file_pool_size) + TORRENT_SETTING(boolean, allow_multiple_connections_per_ip) + TORRENT_SETTING(integer, max_failcount) + TORRENT_SETTING(integer, min_reconnect_time) + TORRENT_SETTING(integer, peer_connect_timeout) + TORRENT_SETTING(boolean, ignore_limits_on_local_network) + TORRENT_SETTING(integer, connection_speed) + TORRENT_SETTING(boolean, send_redundant_have) + TORRENT_SETTING(boolean, lazy_bitfields) + TORRENT_SETTING(integer, inactivity_timeout) + TORRENT_SETTING(integer, unchoke_interval) + TORRENT_SETTING(integer, optimistic_unchoke_interval) + TORRENT_SETTING(std_string, announce_ip) + TORRENT_SETTING(integer, num_want) + TORRENT_SETTING(integer, initial_picker_threshold) + TORRENT_SETTING(integer, allowed_fast_set_size) + TORRENT_SETTING(integer, suggest_mode) + TORRENT_SETTING(integer, max_queued_disk_bytes) + TORRENT_SETTING(integer, max_queued_disk_bytes_low_watermark) + TORRENT_SETTING(integer, handshake_timeout) +#ifndef TORRENT_DISABLE_DHT + TORRENT_SETTING(boolean, use_dht_as_fallback) +#endif + TORRENT_SETTING(boolean, free_torrent_hashes) + TORRENT_SETTING(boolean, upnp_ignore_nonrouters) + TORRENT_SETTING(integer, send_buffer_low_watermark) + TORRENT_SETTING(integer, send_buffer_watermark) + TORRENT_SETTING(integer, send_buffer_watermark_factor) +#ifndef TORRENT_NO_DEPRECATE + TORRENT_SETTING(boolean, auto_upload_slots) + TORRENT_SETTING(boolean, auto_upload_slots_rate_based) +#endif + TORRENT_SETTING(integer, choking_algorithm) + TORRENT_SETTING(integer, seed_choking_algorithm) + TORRENT_SETTING(boolean, use_parole_mode) + TORRENT_SETTING(integer, cache_size) + TORRENT_SETTING(integer, cache_buffer_chunk_size) + TORRENT_SETTING(integer, cache_expiry) + TORRENT_SETTING(boolean, use_read_cache) + TORRENT_SETTING(boolean, explicit_read_cache) + TORRENT_SETTING(integer, disk_io_write_mode) + TORRENT_SETTING(integer, disk_io_read_mode) + TORRENT_SETTING(boolean, coalesce_reads) + TORRENT_SETTING(boolean, coalesce_writes) + TORRENT_SETTING(character, peer_tos) + TORRENT_SETTING(integer, active_downloads) + TORRENT_SETTING(integer, active_seeds) + TORRENT_SETTING(integer, active_dht_limit) + TORRENT_SETTING(integer, active_tracker_limit) + TORRENT_SETTING(integer, active_lsd_limit) + TORRENT_SETTING(integer, active_limit) + TORRENT_SETTING(boolean, auto_manage_prefer_seeds) + TORRENT_SETTING(boolean, dont_count_slow_torrents) + TORRENT_SETTING(integer, auto_manage_interval) + TORRENT_SETTING(floating_point, share_ratio_limit) + TORRENT_SETTING(floating_point, seed_time_ratio_limit) + TORRENT_SETTING(integer, seed_time_limit) + TORRENT_SETTING(floating_point, peer_turnover) + TORRENT_SETTING(floating_point, peer_turnover_cutoff) + TORRENT_SETTING(boolean, close_redundant_connections) + TORRENT_SETTING(integer, auto_scrape_interval) + TORRENT_SETTING(integer, auto_scrape_min_interval) + TORRENT_SETTING(integer, max_peerlist_size) + TORRENT_SETTING(integer, max_paused_peerlist_size) + TORRENT_SETTING(integer, min_announce_interval) + TORRENT_SETTING(boolean, prioritize_partial_pieces) + TORRENT_SETTING(integer, auto_manage_startup) + TORRENT_SETTING(boolean, rate_limit_ip_overhead) + TORRENT_SETTING(boolean, announce_to_all_trackers) + TORRENT_SETTING(boolean, announce_to_all_tiers) + TORRENT_SETTING(boolean, prefer_udp_trackers) + TORRENT_SETTING(boolean, strict_super_seeding) + TORRENT_SETTING(integer, seeding_piece_quota) + TORRENT_SETTING(integer, max_sparse_regions) +#ifndef TORRENT_DISABLE_MLOCK + TORRENT_SETTING(boolean, lock_disk_cache) +#endif + TORRENT_SETTING(integer, max_rejects) + TORRENT_SETTING(integer, recv_socket_buffer_size) + TORRENT_SETTING(integer, send_socket_buffer_size) + TORRENT_SETTING(boolean, optimize_hashing_for_speed) + TORRENT_SETTING(integer, file_checks_delay_per_block) + TORRENT_SETTING(integer, disk_cache_algorithm) + TORRENT_SETTING(integer, read_cache_line_size) + TORRENT_SETTING(integer, write_cache_line_size) + TORRENT_SETTING(integer, optimistic_disk_retry) + TORRENT_SETTING(boolean, disable_hash_checks) + TORRENT_SETTING(boolean, allow_reordered_disk_operations) + TORRENT_SETTING(boolean, allow_i2p_mixed) + TORRENT_SETTING(integer, max_suggest_pieces) + TORRENT_SETTING(boolean, drop_skipped_requests) + TORRENT_SETTING(boolean, low_prio_disk) + TORRENT_SETTING(integer, local_service_announce_interval) + TORRENT_SETTING(integer, dht_announce_interval) + TORRENT_SETTING(integer, udp_tracker_token_expiry) + TORRENT_SETTING(boolean, volatile_read_cache) + TORRENT_SETTING(boolean, guided_read_cache) + TORRENT_SETTING(integer, default_cache_min_age) + TORRENT_SETTING(integer, num_optimistic_unchoke_slots) + TORRENT_SETTING(boolean, no_atime_storage) + TORRENT_SETTING(integer, default_est_reciprocation_rate) + TORRENT_SETTING(integer, increase_est_reciprocation_rate) + TORRENT_SETTING(integer, decrease_est_reciprocation_rate) + TORRENT_SETTING(boolean, incoming_starts_queued_torrents) + TORRENT_SETTING(boolean, report_true_downloaded) + TORRENT_SETTING(boolean, strict_end_game_mode) + TORRENT_SETTING(boolean, broadcast_lsd) + TORRENT_SETTING(boolean, enable_outgoing_utp) + TORRENT_SETTING(boolean, enable_incoming_utp) + TORRENT_SETTING(boolean, enable_outgoing_tcp) + TORRENT_SETTING(boolean, enable_incoming_tcp) + TORRENT_SETTING(integer, max_pex_peers) + TORRENT_SETTING(boolean, ignore_resume_timestamps) + TORRENT_SETTING(boolean, no_recheck_incomplete_resume) + TORRENT_SETTING(boolean, anonymous_mode) + TORRENT_SETTING(boolean, force_proxy) + TORRENT_SETTING(integer, tick_interval) + TORRENT_SETTING(boolean, report_web_seed_downloads) + TORRENT_SETTING(integer, share_mode_target) + TORRENT_SETTING(integer, upload_rate_limit) + TORRENT_SETTING(integer, download_rate_limit) + TORRENT_SETTING(integer, local_upload_rate_limit) + TORRENT_SETTING(integer, local_download_rate_limit) + TORRENT_SETTING(integer, dht_upload_rate_limit) + TORRENT_SETTING(integer, unchoke_slots_limit) + TORRENT_SETTING(integer, half_open_limit) + TORRENT_SETTING(integer, connections_limit) + TORRENT_SETTING(integer, utp_target_delay) + TORRENT_SETTING(integer, utp_gain_factor) + TORRENT_SETTING(integer, utp_syn_resends) + TORRENT_SETTING(integer, utp_fin_resends) + TORRENT_SETTING(integer, utp_num_resends) + TORRENT_SETTING(integer, utp_connect_timeout) +#ifndef TORRENT_NO_DEPRECATE + TORRENT_SETTING(integer, utp_delayed_ack) +#endif + TORRENT_SETTING(boolean, utp_dynamic_sock_buf) + TORRENT_SETTING(integer, mixed_mode_algorithm) + TORRENT_SETTING(boolean, rate_limit_utp) + TORRENT_SETTING(integer, listen_queue_size) + TORRENT_SETTING(boolean, announce_double_nat) + TORRENT_SETTING(integer, torrent_connect_boost) + TORRENT_SETTING(boolean, seeding_outgoing_connections) + TORRENT_SETTING(boolean, no_connect_privileged_ports) + TORRENT_SETTING(integer, alert_queue_size) + TORRENT_SETTING(integer, max_metadata_size) + TORRENT_SETTING(boolean, smooth_connects) + TORRENT_SETTING(boolean, always_send_user_agent) + TORRENT_SETTING(boolean, apply_ip_filter_to_trackers) + TORRENT_SETTING(integer, read_job_every) + TORRENT_SETTING(boolean, use_disk_read_ahead) + TORRENT_SETTING(boolean, lock_files) + TORRENT_SETTING(integer, ssl_listen) + TORRENT_SETTING(integer, tracker_backoff) + TORRENT_SETTING(boolean, ban_web_seeds) + TORRENT_SETTING(integer, max_http_recv_buffer_size) + }; + +#undef TORRENT_SETTING +#define TORRENT_SETTING(t, x) {#x, offsetof(proxy_settings,x), t}, + + bencode_map_entry proxy_settings_map[] = + { + TORRENT_SETTING(std_string, hostname) + TORRENT_SETTING(integer16, port) + TORRENT_SETTING(std_string, username) + TORRENT_SETTING(std_string, password) + TORRENT_SETTING(character, type) + TORRENT_SETTING(boolean, proxy_hostnames) + TORRENT_SETTING(boolean, proxy_peer_connections) + }; +#undef TORRENT_SETTING + +#ifndef TORRENT_DISABLE_DHT +#define TORRENT_SETTING(t, x) {#x, offsetof(dht_settings,x), t}, + bencode_map_entry dht_settings_map[] = + { + TORRENT_SETTING(integer, max_peers_reply) + TORRENT_SETTING(integer, search_branching) +#ifndef TORRENT_NO_DEPRECATE + TORRENT_SETTING(integer, service_port) +#endif + TORRENT_SETTING(integer, max_fail_count) + TORRENT_SETTING(integer, max_torrents) + TORRENT_SETTING(integer, max_dht_items) + TORRENT_SETTING(integer, max_torrent_search_reply) + TORRENT_SETTING(boolean, restrict_routing_ips) + TORRENT_SETTING(boolean, restrict_search_ips) + TORRENT_SETTING(boolean, extended_routing_table) + }; +#undef TORRENT_SETTING +#endif + +#ifndef TORRENT_DISABLE_ENCRYPTION +#define TORRENT_SETTING(t, x) {#x, offsetof(pe_settings,x), t}, + bencode_map_entry pe_settings_map[] = + { + TORRENT_SETTING(character, out_enc_policy) + TORRENT_SETTING(character, in_enc_policy) + TORRENT_SETTING(character, allowed_enc_level) + TORRENT_SETTING(boolean, prefer_rc4) + }; +#undef TORRENT_SETTING +#endif + + struct session_category + { + char const* name; + bencode_map_entry const* map; + int num_entries; + int flag; + int offset; + int default_offset; + }; + + // the names in here need to match the names in session_impl + // to make the macro simpler + struct all_default_values + { + session_settings m_settings; + proxy_settings m_proxy; +#ifndef TORRENT_DISABLE_ENCRYPTION + pe_settings m_pe_settings; +#endif +#ifndef TORRENT_DISABLE_DHT + dht_settings m_dht_settings; +#endif + }; + +#define lenof(x) sizeof(x)/sizeof(x[0]) +#define TORRENT_CATEGORY(name, flag, member, map) \ + { name, map, lenof(map), session:: flag , offsetof(session_impl, member), offsetof(all_default_values, member) }, + + session_category all_settings[] = + { + TORRENT_CATEGORY("settings", save_settings, m_settings, session_settings_map) +#ifndef TORRENT_DISABLE_DHT + TORRENT_CATEGORY("dht", save_dht_settings, m_dht_settings, dht_settings_map) +#endif + TORRENT_CATEGORY("proxy", save_proxy, m_proxy, proxy_settings_map) +#if TORRENT_USE_I2P +// TORRENT_CATEGORY("i2p", save_i2p_proxy, m_i2p_proxy, proxy_settings_map) +#endif +#ifndef TORRENT_DISABLE_ENCRYPTION + TORRENT_CATEGORY("encryption", save_encryption_settings, m_pe_settings, pe_settings_map) +#endif + }; + + std::pair settings_map() + { + return std::make_pair(session_settings_map, lenof(session_settings_map)); + } +#undef lenof + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +#ifdef TORRENT_STATS + int session_impl::logging_allocator::allocations = 0; + int session_impl::logging_allocator::allocated_bytes = 0; +#endif + +#if defined TORRENT_USE_OPENSSL && BOOST_VERSION >= 104700 && OPENSSL_VERSION_NUMBER >= 0x90812f + // when running bittorrent over SSL, the SNI (server name indication) + // extension is used to know which torrent the incoming connection is + // trying to connect to. The 40 first bytes in the name is expected to + // be the hex encoded info-hash + int servername_callback(SSL *s, int *ad, void *arg) + { + session_impl* ses = (session_impl*)arg; + const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); + + if (!servername || strlen(servername) < 40) + return SSL_TLSEXT_ERR_ALERT_FATAL; + + sha1_hash info_hash; + bool valid = from_hex(servername, 40, (char*)&info_hash[0]); + + // the server name is not a valid hex-encoded info-hash + if (!valid) + return SSL_TLSEXT_ERR_ALERT_FATAL; + + // see if there is a torrent with this info-hash + boost::shared_ptr t = ses->find_torrent(info_hash).lock(); + + // if there isn't, fail + if (!t) return SSL_TLSEXT_ERR_ALERT_FATAL; + + // if the torrent we found isn't an SSL torrent, also fail. + if (!t->is_ssl_torrent()) return SSL_TLSEXT_ERR_ALERT_FATAL; + + // if the torrent doesn't have an SSL context and should not allow + // incoming SSL connections + if (!t->ssl_ctx()) return SSL_TLSEXT_ERR_ALERT_FATAL; + + // use this torrent's certificate + SSL_CTX *torrent_context = t->ssl_ctx()->native_handle(); + + SSL_set_SSL_CTX(s, torrent_context); + SSL_set_verify(s, SSL_CTX_get_verify_mode(torrent_context), SSL_CTX_get_verify_callback(torrent_context)); + + return SSL_TLSEXT_ERR_OK; + } +#endif + + session_impl::session_impl( + std::pair listen_port_range + , fingerprint const& cl_fprint + , char const* listen_interface + , boost::uint32_t alert_mask + ) + : m_ipv4_peer_pool(500) +#if TORRENT_USE_IPV6 + , m_ipv6_peer_pool(500) +#endif +#ifndef TORRENT_DISABLE_POOL_ALLOCATOR + , m_send_buffers(send_buffer_size) +#endif + , m_files(40) + , m_io_service() +#ifdef TORRENT_USE_OPENSSL + , m_ssl_ctx(m_io_service, asio::ssl::context::sslv23) +#endif + , m_alerts(m_settings.alert_queue_size, alert_mask) + , m_disk_thread(m_io_service, boost::bind(&session_impl::on_disk_queue, this), m_files) + , m_half_open(m_io_service) + , m_download_rate(peer_connection::download_channel) +#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT + , m_upload_rate(peer_connection::upload_channel, true) +#else + , m_upload_rate(peer_connection::upload_channel) +#endif + , m_tracker_manager(*this, m_proxy) + , m_num_active_downloading(0) + , m_num_active_finished(0) + , m_key(0) + , m_listen_port_retries(listen_port_range.second - listen_port_range.first) +#if TORRENT_USE_I2P + , m_i2p_conn(m_io_service) +#endif + , m_allowed_upload_slots(8) + , m_num_unchoked(0) + , m_unchoke_time_scaler(0) + , m_auto_manage_time_scaler(0) + , m_optimistic_unchoke_time_scaler(0) + , m_disconnect_time_scaler(90) + , m_auto_scrape_time_scaler(180) + , m_next_explicit_cache_torrent(0) + , m_cache_rotation_timer(0) + , m_peak_up_rate(0) + , m_peak_down_rate(0) + , m_created(time_now_hires()) + , m_last_tick(m_created) + , m_last_second_tick(m_created - milliseconds(900)) + , m_last_disk_performance_warning(min_time()) + , m_last_disk_queue_performance_warning(min_time()) + , m_last_choke(m_created) + , m_next_rss_update(min_time()) +#ifndef TORRENT_DISABLE_DHT + , m_dht_announce_timer(m_io_service) + , m_dht_interval_update_torrents(0) +#endif + , m_external_udp_port(0) + , m_udp_socket(m_io_service, m_half_open) + // TODO: 4 in order to support SSL over uTP, the utp_socket manager either + // needs to be able to receive packets on multiple ports, or we need to + // peek into the first few bytes the payload stream of a socket to determine + // whether or not it's an SSL connection. (The former is simpler but won't + // do as well with NATs) + , m_utp_socket_manager(m_settings, m_udp_socket + , boost::bind(&session_impl::incoming_connection, this, _1)) + , m_boost_connections(0) + , m_timer(m_io_service) + , m_lsd_announce_timer(m_io_service) + , m_host_resolver(m_io_service) + , m_current_connect_attempts(0) + , m_tick_residual(0) + , m_non_filtered_torrents(0) +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + , m_logpath(".") +#endif +#ifndef TORRENT_DISABLE_GEO_IP + , m_asnum_db(0) + , m_country_db(0) +#endif + , m_total_failed_bytes(0) + , m_total_redundant_bytes(0) + , m_pending_auto_manage(false) + , m_need_auto_manage(false) + , m_abort(false) + , m_paused(false) + , m_incoming_connection(false) +#if TORRENT_USE_ASSERTS && defined BOOST_HAS_PTHREADS + , m_network_thread(0) +#endif + { +#if TORRENT_USE_ASSERTS + m_posting_torrent_updates = false; +#endif + + memset(m_redundant_bytes, 0, sizeof(m_redundant_bytes)); + m_udp_socket.set_rate_limit(m_settings.dht_upload_rate_limit); + + m_udp_socket.subscribe(&m_utp_socket_manager); + m_udp_socket.subscribe(this); + m_udp_socket.subscribe(&m_tracker_manager); + + m_disk_queues[0] = 0; + m_disk_queues[1] = 0; + +#ifdef TORRENT_REQUEST_LOGGING + char log_filename[200]; +#ifdef TORRENT_WINDOWS + const int pid = GetCurrentProcessId(); +#else + const int pid = getpid(); +#endif + snprintf(log_filename, sizeof(log_filename), "requests-%d.log", pid); + m_request_log = fopen(log_filename, "w+"); + if (m_request_log == 0) + { + fprintf(stderr, "failed to open request log file: (%d) %s\n", errno, strerror(errno)); + } +#endif + + error_code ec; + if (!listen_interface) listen_interface = "0.0.0.0"; + m_listen_interface = tcp::endpoint(address::from_string(listen_interface, ec), listen_port_range.first); + TORRENT_ASSERT_VAL(!ec, ec); + + // ---- generate a peer id ---- + static seed_random_generator seeder; + + std::string print = cl_fprint.to_string(); + TORRENT_ASSERT_VAL(print.length() <= 20, print.length()); + + // the client's fingerprint + std::copy( + print.begin() + , print.begin() + print.length() + , m_peer_id.begin()); + + url_random((char*)&m_peer_id[print.length()], (char*)&m_peer_id[0] + 20); + + update_rate_settings(); + update_connections_limit(); + update_unchoke_limit(); + } + + void session_impl::start_session() + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + m_logger = create_log("main_session", listen_port(), false); + session_log("log created"); +#endif + + error_code ec; +#ifdef TORRENT_USE_OPENSSL + m_ssl_ctx.set_verify_mode(asio::ssl::context::verify_none, ec); +#if BOOST_VERSION >= 104700 +#if OPENSSL_VERSION_NUMBER >= 0x90812f + SSL_CTX_set_tlsext_servername_callback(m_ssl_ctx.native_handle(), servername_callback); + SSL_CTX_set_tlsext_servername_arg(m_ssl_ctx.native_handle(), this); +#endif // OPENSSL_VERSION_NUMBER +#endif // BOOST_VERSION +#endif + +#ifndef TORRENT_DISABLE_DHT + m_next_dht_torrent = m_torrents.begin(); +#endif + m_next_lsd_torrent = m_torrents.begin(); + m_next_connect_torrent = m_torrents.begin(); + m_next_disk_peer = m_connections.begin(); + + m_tcp_mapping[0] = -1; + m_tcp_mapping[1] = -1; + m_udp_mapping[0] = -1; + m_udp_mapping[1] = -1; +#ifdef TORRENT_USE_OPENSSL + m_ssl_mapping[0] = -1; + m_ssl_mapping[1] = -1; +#endif +#ifdef WIN32 + // windows XP has a limit on the number of + // simultaneous half-open TCP connections + // here's a table: + + // windows version half-open connections limit + // --------------------- --------------------------- + // XP sp1 and earlier infinite + // earlier than vista 8 + // vista sp1 and earlier 5 + // vista sp2 and later infinite + + // windows release version number + // ----------------------------------- -------------- + // Windows 7 6.1 + // Windows Server 2008 R2 6.1 + // Windows Server 2008 6.0 + // Windows Vista 6.0 + // Windows Server 2003 R2 5.2 + // Windows Home Server 5.2 + // Windows Server 2003 5.2 + // Windows XP Professional x64 Edition 5.2 + // Windows XP 5.1 + // Windows 2000 5.0 + + OSVERSIONINFOEX osv; + memset(&osv, 0, sizeof(osv)); + osv.dwOSVersionInfoSize = sizeof(osv); + GetVersionEx((OSVERSIONINFO*)&osv); + + // the low two bytes of windows_version is the actual + // version. + boost::uint32_t windows_version + = ((osv.dwMajorVersion & 0xff) << 16) + | ((osv.dwMinorVersion & 0xff) << 8) + | (osv.wServicePackMajor & 0xff); + + // this is the format of windows_version + // xx xx xx + // | | | + // | | + service pack version + // | + minor version + // + major version + + // the least significant byte is the major version + // and the most significant one is the minor version + if (windows_version >= 0x060100) + { + // windows 7 and up doesn't have a half-open limit + m_half_open.limit(0); + } + else if (windows_version >= 0x060002) + { + // on vista SP 2 and up, there's no limit + m_half_open.limit(0); + } + else if (windows_version >= 0x060000) + { + // on vista the limit is 5 (in home edition) + m_half_open.limit(4); + } + else if (windows_version >= 0x050102) + { + // on XP SP2 the limit is 10 + m_half_open.limit(9); + } + else + { + // before XP SP2, there was no limit + m_half_open.limit(0); + } + m_settings.half_open_limit = m_half_open.limit(); +#endif + + m_bandwidth_channel[peer_connection::download_channel] = &m_download_channel; + m_bandwidth_channel[peer_connection::upload_channel] = &m_upload_channel; + +#ifdef TORRENT_UPNP_LOGGING + m_upnp_log.open("upnp.log", std::ios::in | std::ios::out | std::ios::trunc); +#endif + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + + char tmp[300]; + snprintf(tmp, sizeof(tmp), "libtorrent configuration: %s\n" + "libtorrent version: %s\n" + "libtorrent revision: %s\n\n" + , TORRENT_CFG_STRING + , LIBTORRENT_VERSION + , LIBTORRENT_REVISION); + (*m_logger) << tmp; + +#endif // TORRENT_VERBOSE_LOGGING + +#ifdef TORRENT_STATS + + m_stats_logger = 0; + m_log_seq = 0; + m_stats_logging_enabled = true; + + memset(&m_last_cache_status, 0, sizeof(m_last_cache_status)); + get_vm_stats(&m_last_vm_stat); + + m_last_failed = 0; + m_last_redundant = 0; + m_last_uploaded = 0; + m_last_downloaded = 0; + get_thread_cpu_usage(&m_network_thread_cpu_usage); + + reset_stat_counters(); + rotate_stats_log(); +#endif +#ifdef TORRENT_DISK_STATS + m_buffer_usage_logger.open("buffer_stats.log", std::ios::trunc); + m_buffer_allocations = 0; +#endif + +#if defined TORRENT_BSD || defined TORRENT_LINUX + // ---- auto-cap open files ---- + + struct rlimit rl; + if (getrlimit(RLIMIT_NOFILE, &rl) == 0) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log(" max number of open files: %d", rl.rlim_cur); +#endif + + // deduct some margin for epoll/kqueue, log files, + // futexes, shared objects etc. + rl.rlim_cur -= 20; + + // 80% of the available file descriptors should go + m_settings.connections_limit = (std::min)(m_settings.connections_limit + , int(rl.rlim_cur * 8 / 10)); + // 20% goes towards regular files + m_files.resize((std::min)(m_files.size_limit(), int(rl.rlim_cur * 2 / 10))); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + (*m_logger) << time_now_string() << " max connections: " << m_settings.connections_limit << "\n"; + (*m_logger) << time_now_string() << " max files: " << m_files.size_limit() << "\n"; +#endif + } +#endif // TORRENT_BSD || TORRENT_LINUX + + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log(" generated peer ID: %s", m_peer_id.to_string().c_str()); +#endif + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log(" spawning network thread"); +#endif + m_thread.reset(new thread(boost::bind(&session_impl::main_thread, this))); + } + +#ifdef TORRENT_STATS + void session_impl::rotate_stats_log() + { + if (m_stats_logger) + { + ++m_log_seq; + fclose(m_stats_logger); + } + + // make these cumulative for easier reading of graphs + // reset them every time the log is rotated though, + // to make them cumulative per one-hour graph + m_error_peers = 0; + m_disconnected_peers = 0; + m_eof_peers = 0; + m_connreset_peers = 0; + m_connrefused_peers = 0; + m_connaborted_peers = 0; + m_perm_peers = 0; + m_buffer_peers = 0; + m_unreachable_peers = 0; + m_broken_pipe_peers = 0; + m_addrinuse_peers = 0; + m_no_access_peers = 0; + m_invalid_arg_peers = 0; + m_aborted_peers = 0; + m_error_incoming_peers = 0; + m_error_outgoing_peers = 0; + m_error_rc4_peers = 0; + m_error_encrypted_peers = 0; + m_error_tcp_peers = 0; + m_error_utp_peers = 0; + m_connect_timeouts = 0; + m_uninteresting_peers = 0; + m_transport_timeout_peers = 0; + m_timeout_peers = 0; + m_no_memory_peers = 0; + m_too_many_peers = 0; + + error_code ec; + char filename[100]; + create_directory("session_stats", ec); +#ifdef TORRENT_WINDOWS + const int pid = GetCurrentProcessId(); +#else + const int pid = getpid(); +#endif + snprintf(filename, sizeof(filename), "session_stats/%d.%04d.log", pid, m_log_seq); + m_stats_logger = fopen(filename, "w+"); + if (m_stats_logger == 0) + { + fprintf(stderr, "Failed to create session stats log file \"%s\": (%d) %s\n" + , filename, errno, strerror(errno)); + return; + } + m_last_log_rotation = time_now(); + + fputs("second:uploaded bytes:downloaded bytes:downloading torrents:seeding torrents" + ":peers:connecting peers:disk block buffers:num list peers" + ":peer allocations:peer storage bytes" + ":checking torrents" + ":stopped torrents" + ":upload-only torrents" + ":queued seed torrents" + ":queued download torrents" + ":peers bw-up:peers bw-down:peers disk-up:peers disk-down" + ":upload rate:download rate:disk write queued bytes" + ":peers down 0:peers down 0-2:peers down 2-5:peers down 5-10:peers down 10-50" + ":peers down 50-100:peers down 100-" + ":peers up 0:peers up 0-2:peers up 2-5:peers up 5-10:peers up 10-50:peers up 50-100" + ":peers up 100-:error peers" + ":peers down interesting:peers down unchoked:peers down requests" + ":peers up interested:peers up unchoked:peers up requests" + ":peer disconnects:peers eof:peers connection reset" + ":outstanding requests:outstanding end-game requests" + ":outstanding writing blocks" + ":end game piece picker blocks" + ":piece picker blocks" + ":piece picks" + ":reject piece picks" + ":unchoke piece picks" + ":incoming redundant piece picks" + ":incoming piece picks" + ":end game piece picks" + ":snubbed piece picks" + ":connect timeouts" + ":uninteresting peers disconnect" + ":timeout peers" + ":% failed payload bytes" + ":% wasted payload bytes" + ":% protocol bytes" + ":disk read time" + ":disk write time" + ":disk queue time" + ":disk queue size" + ":disk queued bytes" + ":read cache hits" + ":disk block read" + ":disk block written" + ":failed bytes" + ":redundant bytes" + ":error torrents" + ":read disk cache size" + ":disk cache size" + ":disk buffer allocations" + ":disk hash time" + ":disk job time" + ":disk sort time" + ":connection attempts" + ":banned peers" + ":banned for hash failure" + ":cache size" + ":max connections" + ":connect candidates" + ":disk queue limit" + ":disk queue low watermark" + ":% read time" + ":% write time" + ":% hash time" + ":% sort time" + ":disk read back" + ":% read back" + ":disk read queue size" + ":tick interval" + ":tick residual" + ":max unchoked" + ":read job queue size limit" + ":smooth upload rate" + ":smooth download rate" + ":num end-game peers" + ":TCP up rate" + ":TCP down rate" + ":TCP up limit" + ":TCP down limit" + ":uTP up rate" + ":uTP down rate" + ":uTP peak send delay" + ":uTP avg send delay" + ":uTP peak recv delay" + ":uTP avg recv delay" + ":read ops/s" + ":write ops/s" + ":active resident pages" + ":inactive resident pages" + ":pinned resident pages" + ":free pages" + ":pageins" + ":pageouts" + ":page faults" + ":smooth read ops/s" + ":smooth write ops/s" + ":pending reading bytes" + ":read_counter" + ":write_counter" + ":tick_counter" + ":lsd_counter" + ":lsd_peer_counter" + ":udp_counter" + ":accept_counter" + ":disk_queue_counter" + ":disk_read_counter" + ":disk_write_counter" + ":up 8:up 16:up 32:up 64:up 128:up 256:up 512:up 1024:up 2048:up 4096:up 8192:up 16384:up 32768:up 65536:up 131072:up 262144:up 524288:up 1048576" + ":down 8:down 16:down 32:down 64:down 128:down 256:down 512:down 1024:down 2048:down 4096:down 8192:down 16384:down 32768:down 65536:down 131072:down 262144:down 524288:down 1048576" + ":network thread system time" + ":network thread user+system time" + + ":redundant timed-out" + ":redundant cancelled" + ":redundant unknown" + ":redundant seed" + ":redundant end-game" + ":redundant closing" + ":no memory peer errors" + ":too many peers" + ":transport timeout peers" + ":uTP idle" + ":uTP syn-sent" + ":uTP connected" + ":uTP fin-sent" + ":uTP close-wait" + + ":tcp peers" + ":utp peers" + + ":connection refused peers" + ":connection aborted peers" + ":permission denied peers" + ":no buffer peers" + ":host unreachable peers" + ":broken pipe peers" + ":address in use peers" + ":access denied peers" + ":invalid argument peers" + ":operation aborted peers" + + ":error incoming peers" + ":error outgoing peers" + ":error rc4 peers" + ":error encrypted peers" + ":error tcp peers" + ":error utp peers" + + ":total peers" + ":pending incoming block requests" + ":average pending incoming block requests" + + ":torrents want more peers" + ":average peers per limit" + + ":piece requests" + ":max piece requests" + ":invalid piece requests" + ":choked piece requests" + ":cancelled piece requests" + ":piece rejects" + + ":peers up send buffer" + + ":packet_loss" + ":timeout" + ":packets_in" + ":packets_out" + ":fast_retransmit" + ":packet_resend" + ":samples_above_target" + ":samples_below_target" + ":payload_pkts_in" + ":payload_pkts_out" + ":invalid_pkts_in" + ":redundant_pkts_in" + + "\n\n", m_stats_logger); + } +#endif + + void session_impl::trigger_auto_manage() + { + if (m_pending_auto_manage || m_abort) return; + + m_pending_auto_manage = true; + m_need_auto_manage = true; + m_io_service.post(boost::bind(&session_impl::on_trigger_auto_manage, this)); + } + + void session_impl::on_trigger_auto_manage() + { + INVARIANT_CHECK; + + assert(m_pending_auto_manage); + m_pending_auto_manage = false; + if (!m_need_auto_manage) return; + recalculate_auto_managed_torrents(); + } + + void session_impl::update_dht_announce_interval() + { +#ifndef TORRENT_DISABLE_DHT + if (!m_dht) return; + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_dht_announce"); +#endif + m_dht_interval_update_torrents = m_torrents.size(); + error_code ec; + int delay = (std::max)(m_settings.dht_announce_interval + / (std::max)(int(m_torrents.size()), 1), 1); + m_dht_announce_timer.expires_from_now(seconds(delay), ec); + m_dht_announce_timer.async_wait( + boost::bind(&session_impl::on_dht_announce, this, _1)); + TORRENT_ASSERT(!ec); +#endif + } + + void session_impl::init() + { +#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING + session_log(" *** session thread init"); +#endif + + // this is where we should set up all async operations. This + // is called from within the network thread as opposed to the + // constructor which is called from the main thread + +#if defined TORRENT_ASIO_DEBUGGING + async_inc_threads(); + add_outstanding_async("session_impl::on_tick"); +#endif + error_code ec; + m_io_service.post(boost::bind(&session_impl::on_tick, this, ec)); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_lsd_announce"); +#endif + int delay = (std::max)(m_settings.local_service_announce_interval + / (std::max)(int(m_torrents.size()), 1), 1); + m_lsd_announce_timer.expires_from_now(seconds(delay), ec); + m_lsd_announce_timer.async_wait( + boost::bind(&session_impl::on_lsd_announce, this, _1)); + TORRENT_ASSERT(!ec); + +#ifndef TORRENT_DISABLE_DHT + update_dht_announce_interval(); +#endif + +#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING + session_log(" open listen port"); +#endif + // no reuse_address and allow system defined port + open_listen_port(0, ec); +#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING + session_log(" done starting session"); +#endif + } + + void session_impl::save_state(entry* eptr, boost::uint32_t flags) const + { + TORRENT_ASSERT(is_network_thread()); + + entry& e = *eptr; + + all_default_values def; + + for (int i = 0; i < int(sizeof(all_settings)/sizeof(all_settings[0])); ++i) + { + session_category const& c = all_settings[i]; + if ((flags & c.flag) == 0) continue; + save_struct(e[c.name], reinterpret_cast(this) + c.offset + , c.map, c.num_entries, reinterpret_cast(&def) + c.default_offset); + } +#ifndef TORRENT_DISABLE_DHT + if (m_dht && (flags & session::save_dht_state)) + { + e["dht state"] = m_dht->state(); + } +#endif + +#if TORRENT_USE_I2P + if (flags & session::save_i2p_proxy) + { + save_struct(e["i2p"], &i2p_proxy(), proxy_settings_map + , sizeof(proxy_settings_map)/sizeof(proxy_settings_map[0]) + , &def.m_proxy); + } +#endif +#ifndef TORRENT_DISABLE_GEO_IP + if (flags & session::save_as_map) + { + entry::dictionary_type& as_map = e["AS map"].dict(); + char buf[10]; + for (std::map::const_iterator i = m_as_peak.begin() + , end(m_as_peak.end()); i != end; ++i) + { + if (i->second == 0) continue; + sprintf(buf, "%05d", i->first); + as_map[buf] = i->second; + } + } +#endif + + if (flags & session::save_feeds) + { + entry::list_type& feeds = e["feeds"].list(); + for (std::vector >::const_iterator i = + m_feeds.begin(), end(m_feeds.end()); i != end; ++i) + { + feeds.push_back(entry()); + (*i)->save_state(feeds.back()); + } + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (ses_extension_list_t::const_iterator i = m_ses_extensions.begin() + , end(m_ses_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->save_state(*eptr); + } TORRENT_CATCH(std::exception&) {} + } +#endif + } + + void session_impl::set_proxy(proxy_settings const& s) + { + TORRENT_ASSERT(is_network_thread()); + + m_proxy = s; + // in case we just set a socks proxy, we might have to + // open the socks incoming connection + if (!m_socks_listen_socket) open_new_incoming_socks_connection(); + m_udp_socket.set_proxy_settings(m_proxy); + } + + void session_impl::load_state(lazy_entry const* e) + { + TORRENT_ASSERT(is_network_thread()); + + lazy_entry const* settings; + + if (e->type() != lazy_entry::dict_t) return; + + for (int i = 0; i < int(sizeof(all_settings)/sizeof(all_settings[0])); ++i) + { + session_category const& c = all_settings[i]; + settings = e->dict_find_dict(c.name); + if (!settings) continue; + load_struct(*settings, reinterpret_cast(this) + c.offset, c.map, c.num_entries); + } + + update_rate_settings(); + update_connections_limit(); + update_unchoke_limit(); + m_alerts.set_alert_queue_size_limit(m_settings.alert_queue_size); + + // in case we just set a socks proxy, we might have to + // open the socks incoming connection + if (!m_socks_listen_socket) open_new_incoming_socks_connection(); + m_udp_socket.set_proxy_settings(m_proxy); + +#ifndef TORRENT_DISABLE_DHT + settings = e->dict_find_dict("dht state"); + if (settings) + { + m_dht_state = *settings; + } +#endif + +#if TORRENT_USE_I2P + settings = e->dict_find_dict("i2p"); + if (settings) + { + proxy_settings s; + load_struct(*settings, &s, proxy_settings_map + , sizeof(proxy_settings_map)/sizeof(proxy_settings_map[0])); + set_i2p_proxy(s); + } +#endif +#ifndef TORRENT_DISABLE_GEO_IP + settings = e->dict_find_dict("AS map"); + if (settings) + { + for (int i = 0; i < settings->dict_size(); ++i) + { + std::pair item = settings->dict_at(i); + int as_num = atoi(item.first.c_str()); + if (item.second->type() != lazy_entry::int_t || item.second->int_value() == 0) continue; + int& peak = m_as_peak[as_num]; + if (peak < item.second->int_value()) peak = item.second->int_value(); + } + } +#endif + + if (m_settings.connection_speed < 0) m_settings.connection_speed = 200; + + update_disk_thread_settings(); + + settings = e->dict_find_list("feeds"); + if (settings) + { + m_feeds.reserve(settings->list_size()); + for (int i = 0; i < settings->list_size(); ++i) + { + if (settings->list_at(i)->type() != lazy_entry::dict_t) continue; + boost::shared_ptr f(new_feed(*this, feed_settings())); + f->load_state(*settings->list_at(i)); + f->update_feed(); + m_feeds.push_back(f); + } + update_rss_feeds(); + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (ses_extension_list_t::iterator i = m_ses_extensions.begin() + , end(m_ses_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->load_state(*e); + } TORRENT_CATCH(std::exception&) {} + } +#endif + } + +#ifndef TORRENT_DISABLE_GEO_IP + namespace + { + struct free_ptr + { + void* ptr_; + free_ptr(void* p): ptr_(p) {} + ~free_ptr() { free(ptr_); } + }; + } + + char const* session_impl::country_for_ip(address const& a) + { + TORRENT_ASSERT(is_network_thread()); + + if (!a.is_v4() || m_country_db == 0) return 0; + return GeoIP_country_code_by_ipnum(m_country_db, a.to_v4().to_ulong()); + } + + int session_impl::as_for_ip(address const& a) + { + TORRENT_ASSERT(is_network_thread()); + + if (!a.is_v4() || m_asnum_db == 0) return 0; + char* name = GeoIP_name_by_ipnum(m_asnum_db, a.to_v4().to_ulong()); + if (name == 0) return 0; + free_ptr p(name); + // GeoIP returns the name as AS??? where ? is the AS-number + return atoi(name + 2); + } + + std::string session_impl::as_name_for_ip(address const& a) + { + TORRENT_ASSERT(is_network_thread()); + + if (!a.is_v4() || m_asnum_db == 0) return std::string(); + char* name = GeoIP_name_by_ipnum(m_asnum_db, a.to_v4().to_ulong()); + if (name == 0) return std::string(); + free_ptr p(name); + char* tmp = std::strchr(name, ' '); + if (tmp == 0) return std::string(); + return tmp + 1; + } + + std::pair* session_impl::lookup_as(int as) + { + TORRENT_ASSERT(is_network_thread()); + + std::map::iterator i = m_as_peak.lower_bound(as); + + if (i == m_as_peak.end() || i->first != as) + { + // we don't have any data for this AS, insert a new entry + i = m_as_peak.insert(i, std::pair(as, 0)); + } + return &(*i); + } + + void session_impl::load_asnum_db(std::string file) + { + TORRENT_ASSERT(is_network_thread()); + + if (m_asnum_db) GeoIP_delete(m_asnum_db); + m_asnum_db = GeoIP_open(file.c_str(), GEOIP_STANDARD); +// return m_asnum_db; + } + +#if TORRENT_USE_WSTRING +#ifndef TORRENT_NO_DEPRECATE + void session_impl::load_asnum_dbw(std::wstring file) + { + TORRENT_ASSERT(is_network_thread()); + + if (m_asnum_db) GeoIP_delete(m_asnum_db); + std::string utf8; + wchar_utf8(file, utf8); + m_asnum_db = GeoIP_open(utf8.c_str(), GEOIP_STANDARD); +// return m_asnum_db; + } + + void session_impl::load_country_dbw(std::wstring file) + { + TORRENT_ASSERT(is_network_thread()); + + if (m_country_db) GeoIP_delete(m_country_db); + std::string utf8; + wchar_utf8(file, utf8); + m_country_db = GeoIP_open(utf8.c_str(), GEOIP_STANDARD); +// return m_country_db; + } +#endif // TORRENT_NO_DEPRECATE +#endif // TORRENT_USE_WSTRING + + void session_impl::load_country_db(std::string file) + { + TORRENT_ASSERT(is_network_thread()); + + if (m_country_db) GeoIP_delete(m_country_db); + m_country_db = GeoIP_open(file.c_str(), GEOIP_STANDARD); +// return m_country_db; + } + +#endif // TORRENT_DISABLE_GEO_IP + +#ifndef TORRENT_DISABLE_EXTENSIONS + + typedef boost::function(torrent*, void*)> ext_function_t; + + struct session_plugin_wrapper : plugin + { + session_plugin_wrapper(ext_function_t const& f) : m_f(f) {} + + virtual boost::shared_ptr new_torrent(torrent* t, void* user) + { return m_f(t, user); } + ext_function_t m_f; + }; + + void session_impl::add_extension(ext_function_t ext) + { + TORRENT_ASSERT(is_network_thread()); + TORRENT_ASSERT_VAL(ext, ext); + + boost::shared_ptr p(new session_plugin_wrapper(ext)); + + m_ses_extensions.push_back(p); + } + + void session_impl::add_ses_extension(boost::shared_ptr ext) + { + TORRENT_ASSERT(is_network_thread()); + TORRENT_ASSERT_VAL(ext, ext); + + m_ses_extensions.push_back(ext); + m_alerts.add_extension(ext); + ext->added(this); + } +#endif + + feed_handle session_impl::add_feed(feed_settings const& sett) + { + TORRENT_ASSERT(is_network_thread()); + + // look for duplicates. If we already have a feed with this + // URL, return a handle to the existing one + for (std::vector >::const_iterator i + = m_feeds.begin(), end(m_feeds.end()); i != end; ++i) + { + if (sett.url != (*i)->m_settings.url) continue; + return feed_handle(*i); + } + + boost::shared_ptr f(new_feed(*this, sett)); + m_feeds.push_back(f); + update_rss_feeds(); + return feed_handle(f); + } + + void session_impl::remove_feed(feed_handle h) + { + TORRENT_ASSERT(is_network_thread()); + + boost::shared_ptr f = h.m_feed_ptr.lock(); + if (!f) return; + + std::vector >::iterator i + = std::find(m_feeds.begin(), m_feeds.end(), f); + + if (i == m_feeds.end()) return; + + m_feeds.erase(i); + } + + void session_impl::get_feeds(std::vector* ret) const + { + TORRENT_ASSERT(is_network_thread()); + + ret->clear(); + ret->reserve(m_feeds.size()); + for (std::vector >::const_iterator i = m_feeds.begin() + , end(m_feeds.end()); i != end; ++i) + ret->push_back(feed_handle(*i)); + } + + void session_impl::pause() + { + TORRENT_ASSERT(is_network_thread()); + + if (m_paused) return; +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + session_log(" *** session paused ***"); +#endif + m_paused = true; + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + torrent& t = *i->second; + t.do_pause(); + } + } + + void session_impl::resume() + { + TORRENT_ASSERT(is_network_thread()); + + if (!m_paused) return; + m_paused = false; + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + torrent& t = *i->second; + t.do_resume(); + if (t.should_check_files()) t.queue_torrent_check(); + } + } + + void session_impl::abort() + { + TORRENT_ASSERT(is_network_thread()); + + if (m_abort) return; +#if defined TORRENT_LOGGING + session_log(" *** ABORT CALLED ***"); +#endif + // abort the main thread + m_abort = true; + error_code ec; +#if TORRENT_USE_I2P + m_i2p_conn.close(ec); +#endif + m_queued_for_checking.clear(); + stop_lsd(); + stop_upnp(); + stop_natpmp(); +#ifndef TORRENT_DISABLE_DHT + stop_dht(); + m_dht_announce_timer.cancel(ec); +#endif + m_timer.cancel(ec); + m_lsd_announce_timer.cancel(ec); + + for (std::set >::iterator i = m_incoming_sockets.begin() + , end(m_incoming_sockets.end()); i != end; ++i) + { + (*i)->close(ec); + TORRENT_ASSERT(!ec); + } + m_incoming_sockets.clear(); + + // close the listen sockets + for (std::list::iterator i = m_listen_sockets.begin() + , end(m_listen_sockets.end()); i != end; ++i) + { + i->sock->close(ec); + TORRENT_ASSERT(!ec); + } + m_listen_sockets.clear(); + if (m_socks_listen_socket && m_socks_listen_socket->is_open()) + { + m_socks_listen_socket->close(ec); + TORRENT_ASSERT(!ec); + } + m_socks_listen_socket.reset(); + +#if TORRENT_USE_I2P + if (m_i2p_listen_socket && m_i2p_listen_socket->is_open()) + { + m_i2p_listen_socket->close(ec); + TORRENT_ASSERT(!ec); + } + m_i2p_listen_socket.reset(); +#endif + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" aborting all torrents (%d)", m_torrents.size()); +#endif + // abort all torrents + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + i->second->abort(); + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" aborting all tracker requests"); +#endif + m_tracker_manager.abort_all_requests(); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" sending event=stopped to trackers"); +#endif + for (torrent_map::iterator i = m_torrents.begin(); + i != m_torrents.end(); ++i) + { + torrent& t = *i->second; + t.abort(); + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" aborting all connections (%d)", m_connections.size()); +#endif + m_half_open.close(); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" connection queue: %d", m_half_open.size()); +#endif + + // abort all connections + while (!m_connections.empty()) + { +#if TORRENT_USE_ASSERTS + int conn = m_connections.size(); +#endif + (*m_connections.begin())->disconnect(errors::stopping_torrent); + TORRENT_ASSERT_VAL(conn == int(m_connections.size()) + 1, conn); + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" connection queue: %d", m_half_open.size()); +#endif + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" shutting down connection queue"); +#endif + + m_download_rate.close(); + m_upload_rate.close(); + + // #error closing the udp socket here means that + // the uTP connections cannot be closed gracefully + m_udp_socket.close(); + m_external_udp_port = 0; + + m_undead_peers.clear(); + +#ifndef TORRENT_DISABLE_GEO_IP + if (m_asnum_db) GeoIP_delete(m_asnum_db); + if (m_country_db) GeoIP_delete(m_country_db); + m_asnum_db = 0; + m_country_db = 0; +#endif + + m_disk_thread.abort(); + } + + void session_impl::set_port_filter(port_filter const& f) + { + m_port_filter = f; + // TODO: recalculate all connect candidates for all torrents + } + + void session_impl::set_ip_filter(ip_filter const& f) + { + INVARIANT_CHECK; + + m_ip_filter = f; + + // Close connections whose endpoint is filtered + // by the new ip-filter + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + i->second->ip_filter_updated(); + } + + ip_filter const& session_impl::get_ip_filter() const + { + return m_ip_filter; + } + + void session_impl::update_disk_thread_settings() + { + disk_io_job j; + j.buffer = (char*)new session_settings(m_settings); + j.action = disk_io_job::update_settings; + m_disk_thread.add_job(j); + } + + template + void static set_socket_buffer_size(Socket& s, session_settings const& sett, error_code& ec) + { + if (sett.send_socket_buffer_size) + { + stream_socket::send_buffer_size prev_option; + s.get_option(prev_option, ec); + if (!ec) + { + stream_socket::send_buffer_size option( + sett.send_socket_buffer_size); + s.set_option(option, ec); + if (ec) + { + // restore previous value + s.set_option(prev_option, ec); + return; + } + } + } + if (sett.recv_socket_buffer_size) + { + stream_socket::receive_buffer_size prev_option; + s.get_option(prev_option, ec); + if (!ec) + { + stream_socket::receive_buffer_size option( + sett.recv_socket_buffer_size); + s.set_option(option, ec); + if (ec) + { + // restore previous value + s.set_option(prev_option, ec); + return; + } + } + } + } + + void session_impl::set_settings(session_settings const& s) + { + INVARIANT_CHECK; + TORRENT_ASSERT(is_network_thread()); + + TORRENT_ASSERT_VAL(s.file_pool_size > 0, s.file_pool_size); + + // less than 5 seconds unchoke interval is insane + TORRENT_ASSERT_VAL(s.unchoke_interval >= 5, s.unchoke_interval); + + // if disk io thread settings were changed + // post a notification to that thread + bool update_disk_io_thread = false; + if (m_settings.cache_size != s.cache_size + || m_settings.cache_expiry != s.cache_expiry + || m_settings.optimize_hashing_for_speed != s.optimize_hashing_for_speed + || m_settings.file_checks_delay_per_block != s.file_checks_delay_per_block + || m_settings.disk_cache_algorithm != s.disk_cache_algorithm + || m_settings.read_cache_line_size != s.read_cache_line_size + || m_settings.write_cache_line_size != s.write_cache_line_size + || m_settings.coalesce_writes != s.coalesce_writes + || m_settings.coalesce_reads != s.coalesce_reads + || m_settings.max_queued_disk_bytes != s.max_queued_disk_bytes + || m_settings.max_queued_disk_bytes_low_watermark != s.max_queued_disk_bytes_low_watermark + || m_settings.disable_hash_checks != s.disable_hash_checks + || m_settings.explicit_read_cache != s.explicit_read_cache +#ifndef TORRENT_DISABLE_MLOCK + || m_settings.lock_disk_cache != s.lock_disk_cache +#endif + || m_settings.use_read_cache != s.use_read_cache + || m_settings.disk_io_write_mode != s.disk_io_write_mode + || m_settings.disk_io_read_mode != s.disk_io_read_mode + || m_settings.allow_reordered_disk_operations != s.allow_reordered_disk_operations + || m_settings.file_pool_size != s.file_pool_size + || m_settings.volatile_read_cache != s.volatile_read_cache + || m_settings.no_atime_storage!= s.no_atime_storage + || m_settings.ignore_resume_timestamps != s.ignore_resume_timestamps + || m_settings.no_recheck_incomplete_resume != s.no_recheck_incomplete_resume + || m_settings.low_prio_disk != s.low_prio_disk + || m_settings.lock_files != s.lock_files + || m_settings.use_disk_cache_pool != s.use_disk_cache_pool) + update_disk_io_thread = true; + + bool connections_limit_changed = m_settings.connections_limit != s.connections_limit; + bool unchoke_limit_changed = m_settings.unchoke_slots_limit != s.unchoke_slots_limit; + +#ifndef TORRENT_NO_DEPRECATE + // support deprecated choker settings + if (s.choking_algorithm == session_settings::rate_based_choker) + { + if (s.auto_upload_slots && !s.auto_upload_slots_rate_based) + m_settings.choking_algorithm = session_settings::auto_expand_choker; + else if (!s.auto_upload_slots) + m_settings.choking_algorithm = session_settings::fixed_slots_choker; + } +#endif + + // safety check + if (m_settings.volatile_read_cache + && (m_settings.suggest_mode == session_settings::suggest_read_cache + || m_settings.explicit_read_cache)) + { + // If you hit this assert, you're trying to set your cache to be + // volatile and to suggest pieces out of it (or to make the cache + // explicit) at the same time this is a bad configuration, don't do it + TORRENT_ASSERT_PRECOND(false); + m_settings.volatile_read_cache = false; + } + + if (m_settings.choking_algorithm != s.choking_algorithm) + { + // trigger recalculation of the unchoked peers + m_unchoke_time_scaler = 0; + } + +#ifndef TORRENT_DISABLE_DHT + if (m_settings.dht_announce_interval != s.dht_announce_interval) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_dht_announce"); +#endif + error_code ec; + int delay = (std::max)(s.dht_announce_interval + / (std::max)(int(m_torrents.size()), 1), 1); + m_dht_announce_timer.expires_from_now(seconds(delay), ec); + m_dht_announce_timer.async_wait( + boost::bind(&session_impl::on_dht_announce, this, _1)); + } +#endif + + if (m_settings.local_service_announce_interval != s.local_service_announce_interval) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_lsd_announce"); +#endif + error_code ec; + int delay = (std::max)(s.local_service_announce_interval + / (std::max)(int(m_torrents.size()), 1), 1); + m_lsd_announce_timer.expires_from_now(seconds(delay), ec); + m_lsd_announce_timer.async_wait( + boost::bind(&session_impl::on_lsd_announce, this, _1)); + } + + // if queuing settings were changed, recalculate + // queued torrents sooner + if ((m_settings.active_downloads != s.active_downloads + || m_settings.active_seeds != s.active_seeds + || m_settings.active_limit != s.active_limit)) + m_auto_manage_time_scaler = 2; + + if (m_settings.report_web_seed_downloads != s.report_web_seed_downloads) + { + // if this flag changed, update all web seed connections + for (connection_map::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + int type = (*i)->type(); + if (type == peer_connection::url_seed_connection + || type == peer_connection::http_seed_connection) + (*i)->ignore_stats(!s.report_web_seed_downloads); + } + } + + if (m_settings.alert_queue_size != s.alert_queue_size) + m_alerts.set_alert_queue_size_limit(s.alert_queue_size); + + if (m_settings.dht_upload_rate_limit != s.dht_upload_rate_limit) + m_udp_socket.set_rate_limit(s.dht_upload_rate_limit); + + if (m_settings.peer_tos != s.peer_tos && s.peer_tos != 0) + { + error_code ec; + +#if TORRENT_USE_IPV6 && defined IPV6_TCLASS + if (m_udp_socket.local_endpoint(ec).address().is_v6()) + m_udp_socket.set_option(traffic_class(s.peer_tos), ec); + else +#endif + m_udp_socket.set_option(type_of_service(s.peer_tos), ec); + +#if defined TORRENT_VERBOSE_LOGGING + (*m_logger) << ">>> SET_TOS[ udp_socket tos: " << s.peer_tos << " e: " << ec.message() << " ]\n"; +#endif + } + + { + error_code ec; + set_socket_buffer_size(m_udp_socket, m_settings, ec); + if (ec) + { + if (m_alerts.should_post()) + m_alerts.post_alert(udp_error_alert(udp::endpoint(), ec)); + } + } + + bool reopen_listen_port = false; + if (m_settings.ssl_listen != s.ssl_listen) + reopen_listen_port = true; + + m_settings = s; + + if (m_settings.cache_buffer_chunk_size <= 0) + m_settings.cache_buffer_chunk_size = 1; + + update_rate_settings(); + + if (connections_limit_changed) update_connections_limit(); + if (unchoke_limit_changed) update_unchoke_limit(); + + bool anonymous_mode = (m_settings.anonymous_mode != s.anonymous_mode && s.anonymous_mode); + if (anonymous_mode) + { + m_settings.user_agent.clear(); + url_random((char*)&m_peer_id[0], (char*)&m_peer_id[0] + 20); + } + + bool force_proxy = (m_settings.force_proxy != s.force_proxy && s.force_proxy); + + m_udp_socket.set_force_proxy(s.force_proxy); + + // in force_proxy mode, we don't want to accept any incoming + // connections, except through a proxy. + if (force_proxy) + { + stop_lsd(); + stop_upnp(); + stop_natpmp(); +#ifndef TORRENT_DISABLE_DHT + stop_dht(); +#endif + // close the listen sockets + error_code ec; + for (std::list::iterator i = m_listen_sockets.begin() + , end(m_listen_sockets.end()); i != end; ++i) + i->sock->close(ec); + m_listen_sockets.clear(); + } + if (m_settings.connection_speed < 0) m_settings.connection_speed = 200; + + if (update_disk_io_thread) + update_disk_thread_settings(); + + if (m_settings.num_optimistic_unchoke_slots >= m_allowed_upload_slots / 2) + { + if (m_alerts.should_post()) + m_alerts.post_alert(performance_alert(torrent_handle() + , performance_alert::too_many_optimistic_unchoke_slots)); + } + + if (s.choking_algorithm == session_settings::fixed_slots_choker) + m_allowed_upload_slots = m_settings.unchoke_slots_limit; + else if (s.choking_algorithm == session_settings::auto_expand_choker + && m_allowed_upload_slots < m_settings.unchoke_slots_limit) + m_allowed_upload_slots = m_settings.unchoke_slots_limit; + if (m_allowed_upload_slots < 0) + m_allowed_upload_slots = (std::numeric_limits::max)(); + + // replace all occurances of '\n' with ' '. + std::string::iterator i = m_settings.user_agent.begin(); + while ((i = std::find(i, m_settings.user_agent.end(), '\n')) + != m_settings.user_agent.end()) + *i = ' '; + + if (reopen_listen_port) + { + error_code ec; + open_listen_port(0, ec); + } + } + + tcp::endpoint session_impl::get_ipv6_interface() const + { + return m_ipv6_interface; + } + + tcp::endpoint session_impl::get_ipv4_interface() const + { + return m_ipv4_interface; + } + + void session_impl::setup_listener(listen_socket_t* s, tcp::endpoint ep + , int& retries, bool v6_only, int flags, error_code& ec) + { + int last_op = 0; + listen_failed_alert::socket_type_t sock_type = s->ssl ? listen_failed_alert::tcp_ssl : listen_failed_alert::tcp; + s->sock.reset(new socket_acceptor(m_io_service)); + s->sock->open(ep.protocol(), ec); + last_op = listen_failed_alert::open; + if (ec) + { + if (m_alerts.should_post()) + m_alerts.post_alert(listen_failed_alert(ep, last_op, ec, sock_type)); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log("failed to open socket: %s: %s" + , print_endpoint(ep).c_str(), ec.message().c_str()); +#endif + return; + } + + // SO_REUSEADDR on windows is a bit special. It actually allows + // two active sockets to bind to the same port. That means we + // may end up binding to the same socket as some other random + // application. Don't do it! +#ifndef TORRENT_WINDOWS + error_code err; // ignore errors here + s->sock->set_option(socket_acceptor::reuse_address(true), err); +#endif + +#if TORRENT_USE_IPV6 + if (ep.protocol() == tcp::v6()) + { + error_code err; // ignore errors here +#ifdef IPV6_V6ONLY + s->sock->set_option(v6only(v6_only), err); +#endif +#ifdef TORRENT_WINDOWS + +#ifndef PROTECTION_LEVEL_UNRESTRICTED +#define PROTECTION_LEVEL_UNRESTRICTED 10 +#endif + // enable Teredo on windows + s->sock->set_option(v6_protection_level(PROTECTION_LEVEL_UNRESTRICTED), err); +#endif + } +#endif + s->sock->bind(ep, ec); + while (ec && retries > 0) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log("failed to bind to interface \"%s\": %s" + , print_endpoint(ep).c_str(), ec.message().c_str()); +#endif + ec.clear(); + TORRENT_ASSERT_VAL(!ec, ec); + --retries; + ep.port(ep.port() + 1); + s->sock->bind(ep, ec); + last_op = listen_failed_alert::bind; + } + if (ec && !(flags & session::listen_no_system_port)) + { + // instead of giving up, trying + // let the OS pick a port + ep.port(0); + ec = error_code(); + s->sock->bind(ep, ec); + last_op = listen_failed_alert::bind; + } + if (ec) + { + // not even that worked, give up + if (m_alerts.should_post()) + m_alerts.post_alert(listen_failed_alert(ep, last_op, ec, sock_type)); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log("cannot bind to interface \"%s\": %s" + , print_endpoint(ep).c_str(), ec.message().c_str()); +#endif + return; + } + s->external_port = s->sock->local_endpoint(ec).port(); + TORRENT_ASSERT(s->external_port == ep.port() || ep.port() == 0); + last_op = listen_failed_alert::get_peer_name; + if (!ec) + { + s->sock->listen(m_settings.listen_queue_size, ec); + last_op = listen_failed_alert::listen; + } + if (ec) + { + if (m_alerts.should_post()) + m_alerts.post_alert(listen_failed_alert(ep, last_op, ec, sock_type)); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log("cannot listen on interface \"%s\": %s" + , print_endpoint(ep).c_str(), ec.message().c_str()); +#endif + return; + } + + // if we asked the system to listen on port 0, which + // socket did it end up choosing? + if (ep.port() == 0) + { + ep.port(s->sock->local_endpoint(ec).port()); + last_op = listen_failed_alert::get_peer_name; + if (ec) + { + if (m_alerts.should_post()) + m_alerts.post_alert(listen_failed_alert(ep, last_op, ec, sock_type)); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + char msg[200]; + snprintf(msg, 200, "failed to get peer name \"%s\": %s" + , print_endpoint(ep).c_str(), ec.message().c_str()); + (*m_logger) << time_now_string() << msg << "\n"; +#endif + } + } + + if (m_alerts.should_post()) + m_alerts.post_alert(listen_succeeded_alert(ep, s->ssl ? listen_succeeded_alert::tcp_ssl : listen_succeeded_alert::tcp)); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log(" listening on: %s external port: %d" + , print_endpoint(ep).c_str(), s->external_port); +#endif + } + + void session_impl::open_listen_port(int flags, error_code& ec) + { + TORRENT_ASSERT(is_network_thread()); + + TORRENT_ASSERT(!m_abort); +retry: + + // close the open listen sockets + // close the listen sockets + for (std::list::iterator i = m_listen_sockets.begin() + , end(m_listen_sockets.end()); i != end; ++i) + i->sock->close(ec); + m_listen_sockets.clear(); + m_incoming_connection = false; + ec.clear(); + + if (m_abort) return; + + m_ipv6_interface = tcp::endpoint(); + m_ipv4_interface = tcp::endpoint(); + +#ifdef TORRENT_USE_OPENSSL + tcp::endpoint ssl_interface = m_listen_interface; + ssl_interface.port(m_settings.ssl_listen); +#endif + + if (is_any(m_listen_interface.address())) + { + // this means we should open two listen sockets + // one for IPv4 and one for IPv6 + + listen_socket_t s; + setup_listener(&s, tcp::endpoint(address_v4::any(), m_listen_interface.port()) + , m_listen_port_retries, false, flags, ec); + + if (s.sock) + { + // update the listen_interface member with the + // actual port we ended up listening on, so that the other + // sockets can be bound to the same one + m_listen_interface.port(s.external_port); + + TORRENT_ASSERT(!m_abort); + m_listen_sockets.push_back(s); + } + +#ifdef TORRENT_USE_OPENSSL + if (m_settings.ssl_listen) + { + listen_socket_t s; + s.ssl = true; + int retries = 10; + setup_listener(&s, ssl_interface, retries, false, flags, ec); + + if (s.sock) + { + TORRENT_ASSERT(!m_abort); + m_listen_sockets.push_back(s); + } + } +#endif + +#if TORRENT_USE_IPV6 + // only try to open the IPv6 port if IPv6 is installed + if (supports_ipv6()) + { + setup_listener(&s, tcp::endpoint(address_v6::any(), m_listen_interface.port()) + , m_listen_port_retries, true, flags, ec); + + if (s.sock) + { + TORRENT_ASSERT(!m_abort); + m_listen_sockets.push_back(s); + } + +#ifdef TORRENT_USE_OPENSSL + if (m_settings.ssl_listen) + { + listen_socket_t s; + s.ssl = true; + int retries = 10; + setup_listener(&s, tcp::endpoint(address_v6::any(), ssl_interface.port()) + , retries, false, flags, ec); + + if (s.sock) + { + TORRENT_ASSERT(!m_abort); + m_listen_sockets.push_back(s); + } + } +#endif // TORRENT_USE_OPENSSL + } +#endif // TORRENT_USE_IPV6 + + // set our main IPv4 and IPv6 interfaces + // used to send to the tracker + std::vector ifs = enum_net_interfaces(m_io_service, ec); + for (std::vector::const_iterator i = ifs.begin() + , end(ifs.end()); i != end; ++i) + { + address const& addr = i->interface_address; + if (addr.is_v6() && !is_local(addr) && !is_loopback(addr)) + m_ipv6_interface = tcp::endpoint(addr, m_listen_interface.port()); + else if (addr.is_v4() && !is_local(addr) && !is_loopback(addr)) + m_ipv4_interface = tcp::endpoint(addr, m_listen_interface.port()); + } + } + else + { + // we should only open a single listen socket, that + // binds to the given interface + + listen_socket_t s; + setup_listener(&s, m_listen_interface, m_listen_port_retries, false, flags, ec); + + if (s.sock) + { + TORRENT_ASSERT(!m_abort); + m_listen_sockets.push_back(s); + + if (m_listen_interface.address().is_v6()) + m_ipv6_interface = m_listen_interface; + else + m_ipv4_interface = m_listen_interface; + } + +#ifdef TORRENT_USE_OPENSSL + if (m_settings.ssl_listen) + { + listen_socket_t s; + s.ssl = true; + int retries = 10; + setup_listener(&s, ssl_interface, retries, false, flags, ec); + + if (s.sock) + { + TORRENT_ASSERT(!m_abort); + m_listen_sockets.push_back(s); + } + } +#endif + } + + if (m_listen_sockets.empty() && ec) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + char msg[200]; + snprintf(msg, sizeof(msg), "cannot bind TCP listen socket to interface \"%s\": %s" + , print_endpoint(m_listen_interface).c_str(), ec.message().c_str()); + (*m_logger) << msg << "\n"; +#endif + if (m_listen_port_retries > 0) + { + m_listen_interface.port(m_listen_interface.port() + 1); + --m_listen_port_retries; + goto retry; + } + if (m_alerts.should_post()) + m_alerts.post_alert(listen_failed_alert(m_listen_interface + , listen_failed_alert::bind, ec, listen_failed_alert::udp)); + return; + } + + m_udp_socket.bind(udp::endpoint(m_listen_interface.address(), m_listen_interface.port()), ec); + if (ec) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log("cannot bind to UDP interface \"%s\": %s" + , print_endpoint(m_listen_interface).c_str(), ec.message().c_str()); +#endif + if (m_listen_port_retries > 0) + { + m_listen_interface.port(m_listen_interface.port() + 1); + --m_listen_port_retries; + goto retry; + } + if (m_alerts.should_post()) + m_alerts.post_alert(listen_failed_alert(m_listen_interface + , listen_failed_alert::bind, ec, listen_failed_alert::udp)); + return; + } + else + { + m_external_udp_port = m_udp_socket.local_port(); + maybe_update_udp_mapping(0, m_listen_interface.port(), m_listen_interface.port()); + maybe_update_udp_mapping(1, m_listen_interface.port(), m_listen_interface.port()); + if (m_alerts.should_post()) + m_alerts.post_alert(listen_succeeded_alert(m_listen_interface, listen_succeeded_alert::udp)); + } + + if (m_settings.peer_tos != 0) { + +#if TORRENT_USE_IPV6 && defined IPV6_TCLASS + if (m_udp_socket.local_endpoint(ec).address().is_v6()) + m_udp_socket.set_option(traffic_class(m_settings.peer_tos), ec); + else +#endif + m_udp_socket.set_option(type_of_service(m_settings.peer_tos), ec); + +#if defined TORRENT_VERBOSE_LOGGING + (*m_logger) << ">>> SET_TOS[ udp_socket tos: " << m_settings.peer_tos << " e: " << ec.message() << " ]\n"; +#endif + } + ec.clear(); + + set_socket_buffer_size(m_udp_socket, m_settings, ec); + if (ec) + { + if (m_alerts.should_post()) + m_alerts.post_alert(udp_error_alert(udp::endpoint(), ec)); + } + + // initiate accepting on the listen sockets + for (std::list::iterator i = m_listen_sockets.begin() + , end(m_listen_sockets.end()); i != end; ++i) + async_accept(i->sock, i->ssl); + + open_new_incoming_socks_connection(); +#if TORRENT_USE_I2P + open_new_incoming_i2p_connection(); +#endif + + if (!m_listen_sockets.empty()) + { + tcp::endpoint local = m_listen_sockets.front().sock->local_endpoint(ec); + if (!ec) remap_tcp_ports(3, local.port(), ssl_listen_port()); + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + m_logger = create_log("main_session", listen_port(), false); +#endif + } + + void session_impl::remap_tcp_ports(boost::uint32_t mask, int tcp_port, int ssl_port) + { + if ((mask & 1) && m_natpmp.get()) + { + if (m_tcp_mapping[0] != -1) m_natpmp->delete_mapping(m_tcp_mapping[0]); + m_tcp_mapping[0] = m_natpmp->add_mapping(natpmp::tcp, tcp_port, tcp_port); +#ifdef TORRENT_USE_OPENSSL + if (m_ssl_mapping[0] != -1) m_natpmp->delete_mapping(m_ssl_mapping[0]); + if (ssl_port > 0) m_ssl_mapping[0] = m_natpmp->add_mapping(natpmp::tcp + , ssl_port, ssl_port); +#endif + } + if ((mask & 2) && m_upnp.get()) + { + if (m_tcp_mapping[1] != -1) m_upnp->delete_mapping(m_tcp_mapping[1]); + m_tcp_mapping[1] = m_upnp->add_mapping(upnp::tcp, tcp_port, tcp_port); +#ifdef TORRENT_USE_OPENSSL + if (m_ssl_mapping[1] != -1) m_upnp->delete_mapping(m_ssl_mapping[1]); + if (ssl_port > 0) m_ssl_mapping[1] = m_upnp->add_mapping(upnp::tcp + , ssl_port, ssl_port); +#endif + } + } + + void session_impl::open_new_incoming_socks_connection() + { + if (m_proxy.type != proxy_settings::socks5 + && m_proxy.type != proxy_settings::socks5_pw + && m_proxy.type != proxy_settings::socks4) + return; + + if (m_socks_listen_socket) return; + + m_socks_listen_socket = boost::shared_ptr(new socket_type(m_io_service)); + bool ret = instantiate_connection(m_io_service, m_proxy + , *m_socks_listen_socket); + TORRENT_ASSERT_VAL(ret, ret); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_socks_accept"); +#endif + socks5_stream& s = *m_socks_listen_socket->get(); + s.set_command(2); // 2 means BIND (as opposed to CONNECT) + m_socks_listen_port = m_listen_interface.port(); + if (m_socks_listen_port == 0) m_socks_listen_port = 2000 + random() % 60000; + s.async_connect(tcp::endpoint(address_v4::any(), m_socks_listen_port) + , boost::bind(&session_impl::on_socks_accept, this, m_socks_listen_socket, _1)); + } + +#if TORRENT_USE_I2P + void session_impl::set_i2p_proxy(proxy_settings const& s) + { + // we need this socket to be open before we + // can make name lookups for trackers for instance. + // pause the session now and resume it once we've + // established the i2p SAM connection + if (s.hostname.empty()) + { + error_code ec; + m_i2p_conn.close(ec); + return; + } + m_i2p_conn.open(s, boost::bind(&session_impl::on_i2p_open, this, _1)); + } + + void session_impl::on_i2p_open(error_code const& ec) + { + if (ec) + { + if (m_alerts.should_post()) + m_alerts.post_alert(i2p_alert(ec)); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + char msg[200]; + snprintf(msg, sizeof(msg), "i2p open failed (%d) %s", ec.value(), ec.message().c_str()); + (*m_logger) << msg << "\n"; +#endif + } + // now that we have our i2p connection established + // it's OK to start torrents and use this socket to + // do i2p name lookups + + open_new_incoming_i2p_connection(); + } + + void session_impl::open_new_incoming_i2p_connection() + { + if (!m_i2p_conn.is_open()) return; + + if (m_i2p_listen_socket) return; + + m_i2p_listen_socket = boost::shared_ptr(new socket_type(m_io_service)); + bool ret = instantiate_connection(m_io_service, m_i2p_conn.proxy() + , *m_i2p_listen_socket); + TORRENT_ASSERT_VAL(ret, ret); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_i2p_accept"); +#endif + i2p_stream& s = *m_i2p_listen_socket->get(); + s.set_command(i2p_stream::cmd_accept); + s.set_session_id(m_i2p_conn.session_id()); + s.async_connect(tcp::endpoint(address_v4::any(), m_listen_interface.port()) + , boost::bind(&session_impl::on_i2p_accept, this, m_i2p_listen_socket, _1)); + } + + void session_impl::on_i2p_accept(boost::shared_ptr const& s + , error_code const& e) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("session_impl::on_i2p_accept"); +#endif + m_i2p_listen_socket.reset(); + if (e == asio::error::operation_aborted) return; + if (e) + { + if (m_alerts.should_post()) + m_alerts.post_alert(listen_failed_alert(tcp::endpoint( + address_v4::any(), m_listen_interface.port()), listen_failed_alert::accept + , e, listen_failed_alert::i2p)); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log("cannot bind to port %d: %s" + , m_listen_interface.port(), e.message().c_str()); +#endif + return; + } + open_new_incoming_i2p_connection(); + incoming_connection(s); + } +#endif + + bool session_impl::incoming_packet(error_code const& ec + , udp::endpoint const& ep, char const* buf, int size) + { +#ifdef TORRENT_STATS + ++m_num_messages[on_udp_counter]; +#endif + + if (ec) + { + // don't bubble up operation aborted errors to the user + if (ec != asio::error::operation_aborted + && m_alerts.should_post()) + m_alerts.post_alert(udp_error_alert(ep, ec)); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log("UDP socket error: (%d) %s", ec.value(), ec.message().c_str()); +#endif + } + return false; + } + + void session_impl::async_accept(boost::shared_ptr const& listener, bool ssl) + { + TORRENT_ASSERT(!m_abort); + shared_ptr c(new socket_type(m_io_service)); + stream_socket* str = 0; + +#ifdef TORRENT_USE_OPENSSL + if (ssl) + { + // accept connections initializing the SSL connection to + // use the generic m_ssl_ctx context. However, since it has + // the servername callback set on it, we will switch away from + // this context into a specific torrent once we start handshaking + c->instantiate >(m_io_service, &m_ssl_ctx); + str = &c->get >()->next_layer(); + } + else +#endif + { + c->instantiate(m_io_service); + str = c->get(); + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_accept_connection"); +#endif + listener->async_accept(*str + , boost::bind(&session_impl::on_accept_connection, this, c + , boost::weak_ptr(listener), _1, ssl)); + } + + void session_impl::on_accept_connection(shared_ptr const& s + , weak_ptr listen_socket, error_code const& e, bool ssl) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("session_impl::on_accept_connection"); +#endif +#ifdef TORRENT_STATS + ++m_num_messages[on_accept_counter]; +#endif + TORRENT_ASSERT(is_network_thread()); + boost::shared_ptr listener = listen_socket.lock(); + if (!listener) return; + + if (e == asio::error::operation_aborted) return; + + if (m_abort) return; + + error_code ec; + if (e) + { + tcp::endpoint ep = listener->local_endpoint(ec); +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log("error accepting connection on '%s': %s" + , print_endpoint(ep).c_str(), e.message().c_str()); +#endif +#ifdef TORRENT_WINDOWS + // Windows sometimes generates this error. It seems to be + // non-fatal and we have to do another async_accept. + if (e.value() == ERROR_SEM_TIMEOUT) + { + async_accept(listener, ssl); + return; + } +#endif +#ifdef TORRENT_BSD + // Leopard sometimes generates an "invalid argument" error. It seems to be + // non-fatal and we have to do another async_accept. + if (e.value() == EINVAL) + { + async_accept(listener, ssl); + return; + } +#endif + if (e == boost::system::errc::too_many_files_open) + { + // if we failed to accept an incoming connection + // because we have too many files open, try again + // and lower the number of file descriptors used + // elsewere. + if (m_settings.connections_limit > 10) + { + // now, disconnect a random peer + torrent_map::iterator i = std::max_element(m_torrents.begin() + , m_torrents.end(), boost::bind(&torrent::num_peers + , boost::bind(&torrent_map::value_type::second, _1))); + + if (m_alerts.should_post()) + m_alerts.post_alert(performance_alert( + torrent_handle(), performance_alert::too_few_file_descriptors)); + + if (i != m_torrents.end()) + { + i->second->disconnect_peers(1, e); + } + + m_settings.connections_limit = m_connections.size(); + } + // try again, but still alert the user of the problem + async_accept(listener, ssl); + } + if (m_alerts.should_post()) + m_alerts.post_alert(listen_failed_alert(ep, listen_failed_alert::accept, e + , ssl ? listen_failed_alert::tcp_ssl : listen_failed_alert::tcp)); + return; + } + async_accept(listener, ssl); + +#ifdef TORRENT_USE_OPENSSL + if (ssl) + { + // for SSL connections, incoming_connection() is called + // after the handshake is done +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::ssl_handshake"); +#endif + s->get >()->async_accept_handshake( + boost::bind(&session_impl::ssl_handshake, this, _1, s)); + m_incoming_sockets.insert(s); + } + else +#endif + { + incoming_connection(s); + } + } + +#ifdef TORRENT_USE_OPENSSL + + // to test SSL connections, one can use this openssl command template: + // + // openssl s_client -cert .pem -key .pem + // -CAfile .pem -debug -connect 127.0.0.1:4433 -tls1 + // -servername + + void session_impl::ssl_handshake(error_code const& ec, boost::shared_ptr s) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("session_impl::ssl_handshake"); +#endif + m_incoming_sockets.erase(s); + + error_code e; + tcp::endpoint endp = s->remote_endpoint(e); + if (e) return; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" *** peer SSL handshake done [ ip: %s ec: %s socket: %s ]" + , print_endpoint(endp).c_str(), ec.message().c_str(), s->type_name()); +#endif + + if (ec) + { + if (m_alerts.should_post()) + { + m_alerts.post_alert(peer_error_alert(torrent_handle(), endp + , peer_id(), ec)); + } + return; + } + + incoming_connection(s); + } + +#endif // TORRENT_USE_OPENSSL + + void session_impl::incoming_connection(boost::shared_ptr const& s) + { + TORRENT_ASSERT(is_network_thread()); + +#ifdef TORRENT_USE_OPENSSL + // add the current time to the PRNG, to add more unpredictability + boost::uint64_t now = total_microseconds(time_now_hires() - min_time()); + // assume 12 bits of entropy (i.e. about 8 milliseconds) + RAND_add(&now, 8, 1.5); +#endif + + if (m_paused) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" <== INCOMING CONNECTION [ ignored, paused ]"); +#endif + return; + } + + error_code ec; + // we got a connection request! + tcp::endpoint endp = s->remote_endpoint(ec); + + if (ec) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" <== INCOMING CONNECTION FAILED, could " + "not retrieve remote endpoint " + , print_endpoint(endp).c_str(), ec.message().c_str()); +#endif + return; + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" <== INCOMING CONNECTION %s type: %s" + , print_endpoint(endp).c_str(), s->type_name()); +#endif + + if (m_alerts.should_post()) + { + m_alerts.post_alert(incoming_connection_alert(s->type(), endp)); + } + + if (!m_settings.enable_incoming_utp + && s->get()) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" rejected uTP connection"); +#endif + if (m_alerts.should_post()) + m_alerts.post_alert(peer_blocked_alert(torrent_handle() + , endp.address(), peer_blocked_alert::utp_disabled)); + return; + } + + if (!m_settings.enable_incoming_tcp + && s->get()) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" rejected TCP connection"); +#endif + if (m_alerts.should_post()) + m_alerts.post_alert(peer_blocked_alert(torrent_handle() + , endp.address(), peer_blocked_alert::tcp_disabled)); + return; + } + + // local addresses do not count, since it's likely + // coming from our own client through local service discovery + // and it does not reflect whether or not a router is open + // for incoming connections or not. + if (!is_local(endp.address())) + m_incoming_connection = true; + + // this filter is ignored if a single torrent + // is set to ignore the filter, since this peer might be + // for that torrent + if (m_non_filtered_torrents == 0 + && (m_ip_filter.access(endp.address()) & ip_filter::blocked)) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log("filtered blocked ip"); +#endif + if (m_alerts.should_post()) + m_alerts.post_alert(peer_blocked_alert(torrent_handle() + , endp.address(), peer_blocked_alert::ip_filter)); + return; + } + + // check if we have any active torrents + // if we don't reject the connection + if (m_torrents.empty()) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" There are no torrents, disconnect"); +#endif + return; + } + + // don't allow more connections than the max setting + bool reject = false; + if (m_settings.ignore_limits_on_local_network && is_local(endp.address())) + reject = m_settings.connections_limit < INT_MAX / 12 + && num_connections() >= m_settings.connections_limit * 12 / 10; + else + reject = num_connections() >= m_settings.connections_limit + m_settings.connections_slack; + + if (reject) + { + if (m_alerts.should_post()) + { + m_alerts.post_alert( + peer_disconnected_alert(torrent_handle(), endp, peer_id() + , error_code(errors::too_many_connections, get_libtorrent_category()))); + } +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log("number of connections limit exceeded (conns: %d" + ", limit: %d slack: %d), connection rejected\n" + , num_connections(), m_settings.connections_limit, m_settings.connections_slack); +#endif + return; + } + + // if we don't have any active torrents, there's no + // point in accepting this connection. If, however, + // the setting to start up queued torrents when they + // get an incoming connection is enabled, we cannot + // perform this check. + if (!m_settings.incoming_starts_queued_torrents) + { + bool has_active_torrent = false; + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + if (i->second->allows_peers()) + { + has_active_torrent = true; + break; + } + } + if (!has_active_torrent) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" There are no _active_ torrents, disconnect"); +#endif + return; + } + } + + setup_socket_buffers(*s); + + boost::intrusive_ptr c( + new bt_peer_connection(*this, s, endp, 0, get_peer_id())); +#if TORRENT_USE_ASSERTS + c->m_in_constructor = false; +#endif + + if (!c->is_disconnecting()) + { + // in case we've exceeded the limit, let this peer know that + // as soon as it's received the handshake, it needs to either + // disconnect or pick another peer to disconnect + if (num_connections() >= m_settings.connections_limit) + c->peer_exceeds_limit(); + + TORRENT_ASSERT(!c->m_in_constructor); + m_connections.insert(c); + c->start(); + // update the next disk peer round-robin cursor + if (m_next_disk_peer == m_connections.end()) m_next_disk_peer = m_connections.begin(); + } + } + + void session_impl::setup_socket_buffers(socket_type& s) + { + error_code ec; + set_socket_buffer_size(s, m_settings, ec); + } + + void session_impl::on_socks_accept(boost::shared_ptr const& s + , error_code const& e) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("session_impl::on_socks_accept"); +#endif + m_socks_listen_socket.reset(); + if (e == asio::error::operation_aborted) return; + if (e) + { + if (m_alerts.should_post()) + m_alerts.post_alert(listen_failed_alert(tcp::endpoint( + address_v4::any(), m_listen_interface.port()), listen_failed_alert::accept, e + , listen_failed_alert::socks5)); + return; + } + open_new_incoming_socks_connection(); + incoming_connection(s); + } + + void session_impl::close_connection(peer_connection const* p + , error_code const& ec) + { + TORRENT_ASSERT(is_network_thread()); + + // someone else is holding a reference, it's important that + // it's destructed from the network thread. Make sure the + // last reference is held by the network thread. + if (p->refcount() != 1) + m_undead_peers.push_back((peer_connection*)p); + +// too expensive +// INVARIANT_CHECK; + +#ifdef TORRENT_DEBUG +// for (aux::session_impl::torrent_map::const_iterator i = m_torrents.begin() +// , end(m_torrents.end()); i != end; ++i) +// TORRENT_ASSERT(!i->second->has_peer((peer_connection*)p)); +#endif + +#if defined(TORRENT_LOGGING) + session_log(" CLOSING CONNECTION %s : %s" + , print_endpoint(p->remote()).c_str(), ec.message().c_str()); +#endif + + TORRENT_ASSERT(p->is_disconnecting()); + + if (!p->is_choked() && !p->ignore_unchoke_slots()) --m_num_unchoked; + TORRENT_ASSERT(p->refcount() > 0); + + boost::intrusive_ptr sp((peer_connection*)p); + connection_map::iterator i = m_connections.find(sp); + // make sure the next disk peer round-robin cursor stays valid + if (m_next_disk_peer == i) ++m_next_disk_peer; + if (i != m_connections.end()) m_connections.erase(i); + if (m_next_disk_peer == m_connections.end()) m_next_disk_peer = m_connections.begin(); + } + + // implements alert_dispatcher + bool session_impl::post_alert(alert* a) + { + if (!m_alerts.should_post(a)) return false; + m_alerts.post_alert_ptr(a); + return true; + } + + void session_impl::set_peer_id(peer_id const& id) + { + m_peer_id = id; + } + + void session_impl::set_key(int key) + { + m_key = key; + } + + void session_impl::unchoke_peer(peer_connection& c) + { + TORRENT_ASSERT(!c.ignore_unchoke_slots()); + torrent* t = c.associated_torrent().lock().get(); + TORRENT_ASSERT(t); + if (t->unchoke_peer(c)) + ++m_num_unchoked; + } + + void session_impl::choke_peer(peer_connection& c) + { + TORRENT_ASSERT(!c.ignore_unchoke_slots()); + torrent* t = c.associated_torrent().lock().get(); + TORRENT_ASSERT(t); + if (t->choke_peer(c)) + --m_num_unchoked; + } + + int session_impl::next_port() + { + std::pair const& out_ports = m_settings.outgoing_ports; + if (m_next_port < out_ports.first || m_next_port > out_ports.second) + m_next_port = out_ports.first; + + int port = m_next_port; + ++m_next_port; + if (m_next_port > out_ports.second) m_next_port = out_ports.first; +#if defined TORRENT_LOGGING + session_log(" *** BINDING OUTGOING CONNECTION [ port: %d ]", port); +#endif + return port; + } + + // this function is called from the disk-io thread + // when the disk queue is low enough to post new + // write jobs to it. It will go through all peer + // connections that are blocked on the disk and + // wake them up + void session_impl::on_disk_queue() + { +#ifdef TORRENT_STATS + ++m_num_messages[on_disk_queue_counter]; +#endif + TORRENT_ASSERT(is_network_thread()); + + // just to play it safe + if (m_next_disk_peer == m_connections.end()) m_next_disk_peer = m_connections.begin(); + + // never loop more times than there are connections + // keep in mind that connections may disconnect + // while we're looping, that's why this is a reliable + // way of limiting it + int limit = m_connections.size(); + + while (m_next_disk_peer != m_connections.end() && limit > 0 && can_write_to_disk()) + { + --limit; + peer_connection* p = m_next_disk_peer->get(); + ++m_next_disk_peer; + if (m_next_disk_peer == m_connections.end()) m_next_disk_peer = m_connections.begin(); + if ((p->m_channel_state[peer_connection::download_channel] + & peer_info::bw_disk) == 0) continue; + p->on_disk(); + } + + } + + // used to cache the current time + // every 100 ms. This is cheaper + // than a system call and can be + // used where more accurate time + // is not necessary + extern ptime g_current_time; + + initialize_timer::initialize_timer() + { + g_current_time = time_now_hires(); + } + + void session_impl::on_tick(error_code const& e) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("session_impl::on_tick"); +#endif +#ifdef TORRENT_STATS + ++m_num_messages[on_tick_counter]; +#endif + + TORRENT_ASSERT(is_network_thread()); + + ptime now = time_now_hires(); + aux::g_current_time = now; +// too expensive +// INVARIANT_CHECK; + + // we have to keep ticking the utp socket manager + // until they're all closed + if (m_abort && m_utp_socket_manager.num_sockets() == 0) + { +#if defined TORRENT_ASIO_DEBUGGING + fprintf(stderr, "uTP sockets left: %d\n", m_utp_socket_manager.num_sockets()); +#endif + return; + } + + if (e == asio::error::operation_aborted) return; + + if (e) + { +#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING + session_log("*** TICK TIMER FAILED %s", e.message().c_str()); +#endif + ::abort(); + return; + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_tick"); +#endif + error_code ec; + m_timer.expires_at(now + milliseconds(m_settings.tick_interval), ec); + m_timer.async_wait(bind(&session_impl::on_tick, this, _1)); + + m_download_rate.update_quotas(now - m_last_tick); + m_upload_rate.update_quotas(now - m_last_tick); + + m_last_tick = now; + + m_utp_socket_manager.tick(now); + + // only tick the following once per second + if (now - m_last_second_tick < seconds(1)) return; + +#ifndef TORRENT_DISABLE_DHT + if (m_dht_interval_update_torrents < 40 + && m_dht_interval_update_torrents != int(m_torrents.size())) + update_dht_announce_interval(); +#endif + + // remove undead peers that only have this list as their reference keeping them alive + std::vector >::iterator i = std::remove_if( + m_undead_peers.begin(), m_undead_peers.end() + , boost::bind(&peer_connection::refcount, _1) == 1); + m_undead_peers.erase(i, m_undead_peers.end()); + + int tick_interval_ms = int(total_milliseconds(now - m_last_second_tick)); + m_last_second_tick = now; + m_tick_residual += tick_interval_ms - 1000; + + boost::int64_t session_time = total_seconds(now - m_created); + if (session_time > 65000) + { + // we're getting close to the point where our timestamps + // in policy::peer are wrapping. We need to step all counters back + // four hours. This means that any timestamp that refers to a time + // more than 18.2 - 4 = 14.2 hours ago, will be incremented to refer to + // 14.2 hours ago. + + m_created += hours(4); + + const int four_hours = 60 * 60 * 4; + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + policy& p = i->second->get_policy(); + for (policy::iterator j = p.begin_peer() + , end(p.end_peer()); j != end; ++j) + { + policy::peer* pe = *j; + + if (pe->last_optimistically_unchoked < four_hours) + pe->last_optimistically_unchoked = 0; + else + pe->last_optimistically_unchoked -= four_hours; + + if (pe->last_connected < four_hours) + pe->last_connected = 0; + else + pe->last_connected -= four_hours; + } + } + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (ses_extension_list_t::const_iterator i = m_ses_extensions.begin() + , end(m_ses_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->on_tick(); + } TORRENT_CATCH(std::exception&) {} + } +#endif + + // don't do any of the following while we're shutting down + if (m_abort) return; + + // -------------------------------------------------------------- + // RSS feeds + // -------------------------------------------------------------- + if (now > m_next_rss_update) + update_rss_feeds(); + + switch (m_settings.mixed_mode_algorithm) + { + case session_settings::prefer_tcp: + m_tcp_upload_channel.throttle(0); + m_tcp_download_channel.throttle(0); + break; + case session_settings::peer_proportional: + { + int num_peers[2][2] = {{0, 0}, {0, 0}}; + for (connection_map::iterator i = m_connections.begin() + , end(m_connections.end());i != end; ++i) + { + peer_connection& p = *(*i); + if (p.in_handshake()) continue; + int protocol = 0; + if (is_utp(*p.get_socket())) protocol = 1; + + if (p.download_queue().size() + p.request_queue().size() > 0) + ++num_peers[protocol][peer_connection::download_channel]; + if (p.upload_queue().size() > 0) + ++num_peers[protocol][peer_connection::upload_channel]; + } + + bandwidth_channel* tcp_channel[] = { &m_tcp_upload_channel, &m_tcp_download_channel }; + int stat_rate[] = {m_stat.upload_rate(), m_stat.download_rate() }; + // never throttle below this + int lower_limit[] = {5000, 30000}; + + for (int i = 0; i < 2; ++i) + { + // if there are no uploading uTP peers, don't throttle TCP up + if (num_peers[1][i] == 0) + { + tcp_channel[i]->throttle(0); + } + else + { + if (num_peers[0][i] == 0) num_peers[0][i] = 1; + int total_peers = num_peers[0][i] + num_peers[1][i]; + // this are 64 bits since it's multiplied by the number + // of peers, which otherwise might overflow an int + boost::uint64_t rate = stat_rate[i]; + tcp_channel[i]->throttle((std::max)(int(rate * num_peers[0][i] / total_peers), lower_limit[i])); + } + } + } + break; + } + + // -------------------------------------------------------------- + // auto managed torrent + // -------------------------------------------------------------- + if (!m_paused) m_auto_manage_time_scaler--; + if (m_auto_manage_time_scaler < 0) + { + m_auto_manage_time_scaler = settings().auto_manage_interval; + recalculate_auto_managed_torrents(); + } + + // -------------------------------------------------------------- + // check for incoming connections that might have timed out + // -------------------------------------------------------------- + + for (connection_map::iterator i = m_connections.begin(); + i != m_connections.end();) + { + peer_connection* p = (*i).get(); + ++i; + // ignore connections that already have a torrent, since they + // are ticked through the torrents' second_tick + if (!p->associated_torrent().expired()) continue; + // TODO: have a separate list for these connections, instead of having to loop through all of them + if (m_last_tick - p->connected_time() > seconds(m_settings.handshake_timeout)) + p->disconnect(errors::timed_out); + } + + // -------------------------------------------------------------- + // second_tick every torrent + // -------------------------------------------------------------- + + int congested_torrents = 0; + int uncongested_torrents = 0; + + // count the number of seeding torrents vs. downloading + // torrents we are running + int num_seeds = 0; + int num_downloads = 0; + + // count the number of peers of downloading torrents + int num_downloads_peers = 0; + + torrent_map::iterator least_recently_scraped = m_torrents.end(); + int num_paused_auto_managed = 0; + + int num_checking = 0; + int num_queued = 0; + +#if TORRENT_DEBUG_STREAMING > 0 + printf("\033[2J\033[0;0H"); +#endif + + for (torrent_map::iterator i = m_torrents.begin(); + i != m_torrents.end();) + { + torrent& t = *i->second; + TORRENT_ASSERT(!t.is_aborted()); + if (t.statistics().upload_rate() * 11 / 10 > t.upload_limit()) + ++congested_torrents; + else + ++uncongested_torrents; + + if (t.state() == torrent_status::checking_files) ++num_checking; + else if (t.state() == torrent_status::queued_for_checking && !t.is_paused()) ++num_queued; + + if (t.is_auto_managed() && t.is_paused() && !t.has_error()) + { + ++num_paused_auto_managed; + if (least_recently_scraped == m_torrents.end() + || least_recently_scraped->second->seconds_since_last_scrape() + < t.seconds_since_last_scrape()) + { + least_recently_scraped = i; + } + } + + if (t.is_finished()) + { + ++num_seeds; + } + else + { + ++num_downloads; + num_downloads_peers += t.num_peers(); + } + + ++i; + t.second_tick(m_stat, tick_interval_ms); + } + + // some people claim that there sometimes can be cases where + // there is no torrent being checked, but there are torrents + // waiting to be checked. I have never seen this, and I can't + // see a way for it to happen. But, if it does, start one of + // the queued torrents + if (num_checking == 0 && num_queued > 0 && !m_paused) + { + TORRENT_ASSERT(false); + check_queue_t::iterator i = std::min_element(m_queued_for_checking.begin() + , m_queued_for_checking.end(), boost::bind(&torrent::queue_position, _1) + < boost::bind(&torrent::queue_position, _2)); + if (i != m_queued_for_checking.end()) + { + (*i)->start_checking(); + } + } + +#ifndef TORRENT_DISABLE_DHT + if (m_dht) + { + int dht_down; + int dht_up; + m_dht->network_stats(dht_up, dht_down); + m_stat.sent_dht_bytes(dht_up); + m_stat.received_dht_bytes(dht_down); + } +#endif + + if (m_settings.rate_limit_ip_overhead) + { + m_download_channel.use_quota( +#ifndef TORRENT_DISABLE_DHT + m_stat.download_dht() + +#endif + m_stat.download_tracker()); + + m_upload_channel.use_quota( +#ifndef TORRENT_DISABLE_DHT + m_stat.upload_dht() + +#endif + m_stat.upload_tracker()); + + int up_limit = m_upload_channel.throttle(); + int down_limit = m_download_channel.throttle(); + + if (down_limit > 0 + && m_stat.download_ip_overhead() >= down_limit + && m_alerts.should_post()) + { + m_alerts.post_alert(performance_alert(torrent_handle() + , performance_alert::download_limit_too_low)); + } + + if (up_limit > 0 + && m_stat.upload_ip_overhead() >= up_limit + && m_alerts.should_post()) + { + m_alerts.post_alert(performance_alert(torrent_handle() + , performance_alert::upload_limit_too_low)); + } + } + + m_peak_up_rate = (std::max)(m_stat.upload_rate(), m_peak_up_rate); + m_peak_down_rate = (std::max)(m_stat.download_rate(), m_peak_down_rate); + + m_stat.second_tick(tick_interval_ms); + + TORRENT_ASSERT(least_recently_scraped == m_torrents.end() + || (least_recently_scraped->second->is_paused() + && least_recently_scraped->second->is_auto_managed())); + +#ifdef TORRENT_STATS + + if (m_stats_logging_enabled) + { + print_log_line(tick_interval_ms, now); + } +#endif + + // -------------------------------------------------------------- + // scrape paused torrents that are auto managed + // (unless the session is paused) + // -------------------------------------------------------------- + if (!is_paused()) + { + --m_auto_scrape_time_scaler; + if (m_auto_scrape_time_scaler <= 0) + { + m_auto_scrape_time_scaler = m_settings.auto_scrape_interval + / (std::max)(1, num_paused_auto_managed); + if (m_auto_scrape_time_scaler < m_settings.auto_scrape_min_interval) + m_auto_scrape_time_scaler = m_settings.auto_scrape_min_interval; + + if (least_recently_scraped != m_torrents.end()) + { + least_recently_scraped->second->scrape_tracker(); + } + } + } + + // -------------------------------------------------------------- + // refresh explicit disk read cache + // -------------------------------------------------------------- + --m_cache_rotation_timer; + if (m_settings.explicit_read_cache + && m_cache_rotation_timer <= 0) + { + m_cache_rotation_timer = m_settings.explicit_cache_interval; + + torrent_map::iterator least_recently_refreshed = m_torrents.begin(); + if (m_next_explicit_cache_torrent >= int(m_torrents.size())) + m_next_explicit_cache_torrent = 0; + + std::advance(least_recently_refreshed, m_next_explicit_cache_torrent); + + // how many blocks does this torrent get? + int cache_size = (std::max)(0, m_settings.cache_size * 9 / 10); + + if (m_connections.empty()) + { + // if we don't have any connections at all, split the + // cache evenly across all torrents + cache_size = cache_size / (std::max)(int(m_torrents.size()), 1); + } + else + { + cache_size = cache_size * least_recently_refreshed->second->num_peers() + / m_connections.size(); + } + + if (least_recently_refreshed != m_torrents.end()) + least_recently_refreshed->second->refresh_explicit_cache(cache_size); + ++m_next_explicit_cache_torrent; + } + + // -------------------------------------------------------------- + // connect new peers + // -------------------------------------------------------------- + + try_connect_more_peers(num_downloads, num_downloads_peers); + + // -------------------------------------------------------------- + // unchoke set calculations + // -------------------------------------------------------------- + m_unchoke_time_scaler--; + if (m_unchoke_time_scaler <= 0 && !m_connections.empty()) + { + m_unchoke_time_scaler = settings().unchoke_interval; + recalculate_unchoke_slots(congested_torrents + , uncongested_torrents); + } + + // -------------------------------------------------------------- + // optimistic unchoke calculation + // -------------------------------------------------------------- + m_optimistic_unchoke_time_scaler--; + if (m_optimistic_unchoke_time_scaler <= 0) + { + m_optimistic_unchoke_time_scaler + = settings().optimistic_unchoke_interval; + recalculate_optimistic_unchoke_slots(); + } + + // -------------------------------------------------------------- + // disconnect peers when we have too many + // -------------------------------------------------------------- + --m_disconnect_time_scaler; + if (m_disconnect_time_scaler <= 0) + { + m_disconnect_time_scaler = m_settings.peer_turnover_interval; + + // if the connections_limit is too low, the disconnect + // logic is disabled, since it is too disruptive + if (m_settings.connections_limit > 5) + { + if (num_connections() >= m_settings.connections_limit * m_settings.peer_turnover_cutoff + && !m_torrents.empty()) + { + // every 90 seconds, disconnect the worst peers + // if we have reached the connection limit + torrent_map::iterator i = std::max_element(m_torrents.begin(), m_torrents.end() + , boost::bind(&torrent::num_peers, boost::bind(&torrent_map::value_type::second, _1)) + < boost::bind(&torrent::num_peers, boost::bind(&torrent_map::value_type::second, _2))); + + TORRENT_ASSERT(i != m_torrents.end()); + int peers_to_disconnect = (std::min)((std::max)( + int(i->second->num_peers() * m_settings.peer_turnover), 1) + , i->second->get_policy().num_connect_candidates()); + i->second->disconnect_peers(peers_to_disconnect + , error_code(errors::optimistic_disconnect, get_libtorrent_category())); + } + else + { + // if we haven't reached the global max. see if any torrent + // has reached its local limit + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + boost::shared_ptr t = i->second; + + // ths disconnect logic is disabled for torrents with + // too low connection limit + if (t->num_peers() < t->max_connections() * m_settings.peer_turnover_cutoff + || t->max_connections() < 6) + continue; + + int peers_to_disconnect = (std::min)((std::max)(int(i->second->num_peers() + * m_settings.peer_turnover), 1) + , i->second->get_policy().num_connect_candidates()); + t->disconnect_peers(peers_to_disconnect + , error_code(errors::optimistic_disconnect, get_libtorrent_category())); + } + } + } + } + + while (m_tick_residual >= 1000) m_tick_residual -= 1000; +// m_peer_pool.release_memory(); + } + +#ifdef TORRENT_STATS + + void session_impl::enable_stats_logging(bool s) + { + if (m_stats_logging_enabled == s) return; + + m_stats_logging_enabled = s; + + reset_stat_counters(); + if (!s) + { + if (m_stats_logger) fclose(m_stats_logger); + m_stats_logger = 0; + } + else + { + rotate_stats_log(); + get_thread_cpu_usage(&m_network_thread_cpu_usage); + } + } + + void session_impl::reset_stat_counters() + { + m_end_game_piece_picker_blocks = 0; + m_piece_picker_blocks = 0; + m_piece_picks = 0; + m_reject_piece_picks = 0; + m_unchoke_piece_picks = 0; + m_incoming_redundant_piece_picks = 0; + m_incoming_piece_picks = 0; + m_end_game_piece_picks = 0; + m_snubbed_piece_picks = 0; + m_connection_attempts = 0; + m_num_banned_peers = 0; + m_banned_for_hash_failure = 0; + + m_piece_requests = 0; + m_max_piece_requests = 0; + m_invalid_piece_requests = 0; + m_choked_piece_requests = 0; + m_cancelled_piece_requests = 0; + m_piece_rejects = 0; + + memset(m_num_messages, 0, sizeof(m_num_messages)); + memset(m_send_buffer_sizes, 0, sizeof(m_send_buffer_sizes)); + memset(m_recv_buffer_sizes, 0, sizeof(m_recv_buffer_sizes)); + } + + void session_impl::print_log_line(int tick_interval_ms, ptime now) + { + int connect_candidates = 0; + + int checking_torrents = 0; + int stopped_torrents = 0; + int upload_only_torrents = 0; + int downloading_torrents = 0; + int seeding_torrents = 0; + int queued_seed_torrents = 0; + int queued_download_torrents = 0; + int error_torrents = 0; + + int num_peers = 0; + int peer_dl_rate_buckets[7]; + int peer_ul_rate_buckets[7]; + memset(peer_dl_rate_buckets, 0, sizeof(peer_dl_rate_buckets)); + memset(peer_ul_rate_buckets, 0, sizeof(peer_ul_rate_buckets)); + int outstanding_requests = 0; + int outstanding_end_game_requests = 0; + int outstanding_write_blocks = 0; + + int peers_up_interested = 0; + int peers_down_interesting = 0; + int peers_up_requests = 0; + int peers_down_requests = 0; + int peers_up_send_buffer = 0; + + // number of torrents that want more peers + int num_want_more_peers = 0; + + // number of peers among torrents with a peer limit + int num_limited_peers = 0; + // sum of limits of all torrents with a peer limit + boost::uint64_t total_peers_limit = 0; + + std::vector dq; + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + torrent* t = i->second.get(); + int connection_slots = (std::max)(t->max_connections() - t->num_peers(), 0); + int candidates = t->get_policy().num_connect_candidates(); + connect_candidates += (std::min)(candidates, connection_slots); + num_peers += t->get_policy().num_peers(); + + if (t->want_more_peers()) ++num_want_more_peers; + if (t->max_connections() > 0) + { + num_limited_peers += t->num_peers(); + total_peers_limit += t->max_connections(); + } + + if (t->has_error()) + ++error_torrents; + else + { + if (t->is_paused()) + { + if (!t->is_auto_managed()) + ++stopped_torrents; + else + { + if (t->is_seed()) + ++queued_seed_torrents; + else + ++queued_download_torrents; + } + } + else + { + if (i->second->state() == torrent_status::checking_files + || i->second->state() == torrent_status::queued_for_checking) + ++checking_torrents; + else if (i->second->is_seed()) + ++seeding_torrents; + else if (i->second->is_upload_only()) + ++upload_only_torrents; + else + ++downloading_torrents; + } + } + + dq.clear(); + i->second->get_download_queue(&dq); + for (std::vector::iterator j = dq.begin() + , end(dq.end()); j != end; ++j) + { + for (int k = 0; k < j->blocks_in_piece; ++k) + { + block_info& bi = j->blocks[k]; + if (bi.state == block_info::requested) + { + ++outstanding_requests; + if (bi.num_peers > 1) ++outstanding_end_game_requests; + } + else if (bi.state == block_info::writing) + ++outstanding_write_blocks; + } + } + } + int tcp_up_rate = 0; + int tcp_down_rate = 0; + int utp_up_rate = 0; + int utp_down_rate = 0; + int utp_peak_send_delay = 0; + int utp_peak_recv_delay = 0; + boost::uint64_t utp_send_delay_sum = 0; + boost::uint64_t utp_recv_delay_sum = 0; + int num_utp_peers = 0; + int num_tcp_peers = 0; + int utp_num_delay_sockets = 0; + int utp_num_recv_delay_sockets = 0; + int num_complete_connections = 0; + int num_half_open = 0; + int peers_down_unchoked = 0; + int peers_up_unchoked = 0; + int num_end_game_peers = 0; + int reading_bytes = 0; + int pending_incoming_reqs = 0; + + for (connection_map::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + peer_connection* p = i->get(); + if (p->is_connecting()) + { + ++num_half_open; + continue; + } + + ++num_complete_connections; + if (!p->is_choked()) ++peers_up_unchoked; + if (!p->has_peer_choked()) ++peers_down_unchoked; + if (!p->download_queue().empty()) ++peers_down_requests; + if (p->is_peer_interested()) ++peers_up_interested; + if (p->is_interesting()) ++peers_down_interesting; + if (p->send_buffer_size() > 100 || !p->upload_queue().empty() || p->num_reading_bytes() > 0) + ++peers_up_requests; + if (p->endgame()) ++num_end_game_peers; + reading_bytes += p->num_reading_bytes(); + + pending_incoming_reqs += int(p->upload_queue().size()); + + int dl_bucket = 0; + int dl_rate = p->statistics().download_payload_rate(); + if (dl_rate == 0) dl_bucket = 0; + else if (dl_rate < 2000) dl_bucket = 1; + else if (dl_rate < 5000) dl_bucket = 2; + else if (dl_rate < 10000) dl_bucket = 3; + else if (dl_rate < 50000) dl_bucket = 4; + else if (dl_rate < 100000) dl_bucket = 5; + else dl_bucket = 6; + + int ul_rate = p->statistics().upload_payload_rate(); + int ul_bucket = 0; + if (ul_rate == 0) ul_bucket = 0; + else if (ul_rate < 2000) ul_bucket = 1; + else if (ul_rate < 5000) ul_bucket = 2; + else if (ul_rate < 10000) ul_bucket = 3; + else if (ul_rate < 50000) ul_bucket = 4; + else if (ul_rate < 100000) ul_bucket = 5; + else ul_bucket = 6; + + ++peer_dl_rate_buckets[dl_bucket]; + ++peer_ul_rate_buckets[ul_bucket]; + + boost::uint64_t upload_rate = int(p->statistics().upload_rate()); + int buffer_size_watermark = upload_rate + * m_settings.send_buffer_watermark_factor / 100; + if (buffer_size_watermark < m_settings.send_buffer_low_watermark) + buffer_size_watermark = m_settings.send_buffer_low_watermark; + else if (buffer_size_watermark > m_settings.send_buffer_watermark) + buffer_size_watermark = m_settings.send_buffer_watermark; + if (p->send_buffer_size() + p->num_reading_bytes() >= buffer_size_watermark) + ++peers_up_send_buffer; + + utp_stream* utp_socket = p->get_socket()->get(); +#ifdef TORRENT_USE_OPENSSL + if (!utp_socket) + { + ssl_stream* ssl_str = p->get_socket()->get >(); + if (ssl_str) utp_socket = &ssl_str->next_layer(); + } +#endif + if (utp_socket) + { + utp_up_rate += ul_rate; + utp_down_rate += dl_rate; + int send_delay = utp_socket->send_delay(); + int recv_delay = utp_socket->recv_delay(); + utp_peak_send_delay = (std::max)(utp_peak_send_delay, send_delay); + utp_peak_recv_delay = (std::max)(utp_peak_recv_delay, recv_delay); + if (send_delay > 0) + { + utp_send_delay_sum += send_delay; + ++utp_num_delay_sockets; + } + if (recv_delay > 0) + { + utp_recv_delay_sum += recv_delay; + ++utp_num_recv_delay_sockets; + } + ++num_utp_peers; + } + else + { + tcp_up_rate += ul_rate; + tcp_down_rate += dl_rate; + ++num_tcp_peers; + } + + } + + int low_watermark = m_settings.max_queued_disk_bytes_low_watermark == 0 + || m_settings.max_queued_disk_bytes_low_watermark >= m_settings.max_queued_disk_bytes + ? size_type(m_settings.max_queued_disk_bytes) * 7 / 8 + : m_settings.max_queued_disk_bytes_low_watermark; + + if (now - m_last_log_rotation > hours(1)) + rotate_stats_log(); + + // system memory stats + vm_statistics_data_t vm_stat; + get_vm_stats(&vm_stat); + thread_cpu_usage cur_cpu_usage; + get_thread_cpu_usage(&cur_cpu_usage); + + if (m_stats_logger) + { + cache_status cs = m_disk_thread.status(); + session_status sst = status(); + + m_read_ops.add_sample((cs.reads - m_last_cache_status.reads) * 1000.0 / float(tick_interval_ms)); + m_write_ops.add_sample((cs.writes - m_last_cache_status.writes) * 1000.0 / float(tick_interval_ms)); + + int total_job_time = cs.cumulative_job_time == 0 ? 1 : cs.cumulative_job_time; + +#ifdef TORRENT_USE_VALGRIND +#define STAT_LOGL(type, val) VALGRIND_CHECK_VALUE_IS_DEFINED(val); fprintf(m_stats_logger, "%" #type "\t", val) +#else +#define STAT_LOGL(type, val) fprintf(m_stats_logger, "%" #type "\t", val) +#endif +#define STAT_LOG(type, val) fprintf(m_stats_logger, "%" #type "\t", val) + + STAT_LOG(f, total_milliseconds(now - m_last_log_rotation) / 1000.f); + size_type uploaded = m_stat.total_upload() - m_last_uploaded; + STAT_LOG(d, int(uploaded)); + size_type downloaded = m_stat.total_download() - m_last_downloaded; + STAT_LOG(d, int(downloaded)); + STAT_LOGL(d, downloading_torrents); + STAT_LOGL(d, seeding_torrents); + STAT_LOGL(d, num_complete_connections); + STAT_LOGL(d, num_half_open); + STAT_LOG(d, m_disk_thread.disk_allocations()); + STAT_LOGL(d, num_peers); + STAT_LOGL(d, logging_allocator::allocations); + STAT_LOGL(d, logging_allocator::allocated_bytes); + STAT_LOGL(d, checking_torrents); + STAT_LOGL(d, stopped_torrents); + STAT_LOGL(d, upload_only_torrents); + STAT_LOGL(d, queued_seed_torrents); + STAT_LOGL(d, queued_download_torrents); + STAT_LOG(d, m_upload_rate.queue_size()); + STAT_LOG(d, m_download_rate.queue_size()); + STAT_LOGL(d, m_disk_queues[peer_connection::upload_channel]); + STAT_LOGL(d, m_disk_queues[peer_connection::download_channel]); + STAT_LOG(d, m_stat.upload_rate()); + STAT_LOG(d, m_stat.download_rate()); + STAT_LOG(d, int(m_disk_thread.queue_buffer_size())); + STAT_LOGL(d, peer_dl_rate_buckets[0]); + STAT_LOGL(d, peer_dl_rate_buckets[1]); + STAT_LOGL(d, peer_dl_rate_buckets[2]); + STAT_LOGL(d, peer_dl_rate_buckets[3]); + STAT_LOGL(d, peer_dl_rate_buckets[4]); + STAT_LOGL(d, peer_dl_rate_buckets[5]); + STAT_LOGL(d, peer_dl_rate_buckets[6]); + STAT_LOGL(d, peer_ul_rate_buckets[0]); + STAT_LOGL(d, peer_ul_rate_buckets[1]); + STAT_LOGL(d, peer_ul_rate_buckets[2]); + STAT_LOGL(d, peer_ul_rate_buckets[3]); + STAT_LOGL(d, peer_ul_rate_buckets[4]); + STAT_LOGL(d, peer_ul_rate_buckets[5]); + STAT_LOGL(d, peer_ul_rate_buckets[6]); + STAT_LOGL(d, m_error_peers); + STAT_LOGL(d, peers_down_interesting); + STAT_LOGL(d, peers_down_unchoked); + STAT_LOGL(d, peers_down_requests); + STAT_LOGL(d, peers_up_interested); + STAT_LOGL(d, peers_up_unchoked); + STAT_LOGL(d, peers_up_requests); + STAT_LOGL(d, m_disconnected_peers); + STAT_LOGL(d, m_eof_peers); + STAT_LOGL(d, m_connreset_peers); + STAT_LOGL(d, outstanding_requests); + STAT_LOGL(d, outstanding_end_game_requests); + STAT_LOGL(d, outstanding_write_blocks); + STAT_LOGL(d, m_end_game_piece_picker_blocks); + STAT_LOGL(d, m_piece_picker_blocks); + STAT_LOGL(d, m_piece_picks); + STAT_LOGL(d, m_reject_piece_picks); + STAT_LOGL(d, m_unchoke_piece_picks); + STAT_LOGL(d, m_incoming_redundant_piece_picks); + STAT_LOGL(d, m_incoming_piece_picks); + STAT_LOGL(d, m_end_game_piece_picks); + STAT_LOGL(d, m_snubbed_piece_picks); + STAT_LOGL(d, m_connect_timeouts); + STAT_LOGL(d, m_uninteresting_peers); + STAT_LOGL(d, m_timeout_peers); + STAT_LOG(f, (float(m_total_failed_bytes) * 100.f / (m_stat.total_payload_download() == 0 ? 1 : m_stat.total_payload_download()))); + STAT_LOG(f, (float(m_total_redundant_bytes) * 100.f / (m_stat.total_payload_download() == 0 ? 1 : m_stat.total_payload_download()))); + STAT_LOG(f, (float(m_stat.total_protocol_download()) * 100.f / (m_stat.total_download() == 0 ? 1 : m_stat.total_download()))); + STAT_LOG(f, float(cs.average_read_time) / 1000000.f); + STAT_LOG(f, float(cs.average_write_time) / 1000000.f); + STAT_LOG(f, float(cs.average_queue_time) / 1000000.f); + STAT_LOG(d, int(cs.job_queue_length)); + STAT_LOG(d, int(cs.queued_bytes)); + STAT_LOG(d, int(cs.blocks_read_hit - m_last_cache_status.blocks_read_hit)); + STAT_LOG(d, int(cs.blocks_read - m_last_cache_status.blocks_read)); + STAT_LOG(d, int(cs.blocks_written - m_last_cache_status.blocks_written)); + STAT_LOG(d, int(m_total_failed_bytes - m_last_failed)); + STAT_LOG(d, int(m_total_redundant_bytes - m_last_redundant)); + STAT_LOGL(d, error_torrents); + STAT_LOGL(d, cs.read_cache_size); + STAT_LOGL(d, cs.cache_size); + STAT_LOGL(d, cs.total_used_buffers); + STAT_LOG(f, float(cs.average_hash_time) / 1000000.f); + STAT_LOG(f, float(cs.average_job_time) / 1000000.f); + STAT_LOG(f, float(cs.average_sort_time) / 1000000.f); + STAT_LOGL(d, m_connection_attempts); + STAT_LOGL(d, m_num_banned_peers); + STAT_LOGL(d, m_banned_for_hash_failure); + STAT_LOGL(d, m_settings.cache_size); + STAT_LOGL(d, m_settings.connections_limit); + STAT_LOGL(d, connect_candidates); + STAT_LOG(d, int(m_settings.max_queued_disk_bytes)); + STAT_LOGL(d, low_watermark); + STAT_LOG(f, float(cs.cumulative_read_time * 100.f / total_job_time)); + STAT_LOG(f, float(cs.cumulative_write_time * 100.f / total_job_time)); + STAT_LOG(f, float(cs.cumulative_hash_time * 100.f / total_job_time)); + STAT_LOG(f, float(cs.cumulative_sort_time * 100.f / total_job_time)); + STAT_LOG(d, int(cs.total_read_back - m_last_cache_status.total_read_back)); + STAT_LOG(f, float(cs.total_read_back * 100.f / (cs.blocks_written == 0 ? 1: cs.blocks_written))); + STAT_LOGL(d, cs.read_queue_size); + STAT_LOG(f, float(tick_interval_ms) / 1000.f); + STAT_LOG(f, float(m_tick_residual) / 1000.f); + STAT_LOGL(d, m_allowed_upload_slots); + STAT_LOG(d, m_settings.unchoke_slots_limit * 2); + STAT_LOG(d, m_stat.low_pass_upload_rate()); + STAT_LOG(d, m_stat.low_pass_download_rate()); + STAT_LOGL(d, num_end_game_peers); + STAT_LOGL(d, tcp_up_rate); + STAT_LOGL(d, tcp_down_rate); + STAT_LOG(d, int(m_tcp_upload_channel.throttle())); + STAT_LOG(d, int(m_tcp_download_channel.throttle())); + STAT_LOGL(d, utp_up_rate); + STAT_LOGL(d, utp_down_rate); + STAT_LOG(f, float(utp_peak_send_delay) / 1000000.f); + STAT_LOG(f, float(utp_num_delay_sockets ? float(utp_send_delay_sum) / float(utp_num_delay_sockets) : 0) / 1000000.f); + STAT_LOG(f, float(utp_peak_recv_delay) / 1000000.f); + STAT_LOG(f, float(utp_num_recv_delay_sockets ? float(utp_recv_delay_sum) / float(utp_num_recv_delay_sockets) : 0) / 1000000.f); + STAT_LOG(f, float(cs.reads - m_last_cache_status.reads) * 1000.0 / float(tick_interval_ms)); + STAT_LOG(f, float(cs.writes - m_last_cache_status.writes) * 1000.0 / float(tick_interval_ms)); + + STAT_LOG(d, int(vm_stat.active_count)); + STAT_LOG(d, int(vm_stat.inactive_count)); + STAT_LOG(d, int(vm_stat.wire_count)); + STAT_LOG(d, int(vm_stat.free_count)); + STAT_LOG(d, int(vm_stat.pageins - m_last_vm_stat.pageins)); + STAT_LOG(d, int(vm_stat.pageouts - m_last_vm_stat.pageouts)); + STAT_LOG(d, int(vm_stat.faults - m_last_vm_stat.faults)); + + STAT_LOG(d, m_read_ops.mean()); + STAT_LOG(d, m_write_ops.mean()); + + STAT_LOGL(d, reading_bytes); + + for (int i = 0; i < max_messages; ++i) + { + STAT_LOGL(d, m_num_messages[i]); + } + int num_max = sizeof(m_send_buffer_sizes)/sizeof(m_send_buffer_sizes[0]); + for (int i = 0; i < num_max; ++i) + { + STAT_LOGL(d, m_send_buffer_sizes[i]); + } + num_max = sizeof(m_recv_buffer_sizes)/sizeof(m_recv_buffer_sizes[0]); + for (int i = 0; i < num_max; ++i) + { + STAT_LOGL(d, m_recv_buffer_sizes[i]); + } + + STAT_LOG(f, total_microseconds(cur_cpu_usage.user_time + - m_network_thread_cpu_usage.user_time) / double(tick_interval_ms * 10)); + STAT_LOG(f, (total_microseconds(cur_cpu_usage.system_time + - m_network_thread_cpu_usage.system_time) + + total_microseconds(cur_cpu_usage.user_time + - m_network_thread_cpu_usage.user_time)) + / double(tick_interval_ms * 10)); + + for (int i = 0; i < torrent::waste_reason_max; ++i) + { + STAT_LOG(f, (m_redundant_bytes[i] * 100.) / double(m_total_redundant_bytes == 0 ? 1 : m_total_redundant_bytes)); + } + + STAT_LOGL(d, m_no_memory_peers); + STAT_LOGL(d, m_too_many_peers); + STAT_LOGL(d, m_transport_timeout_peers); + + STAT_LOGL(d, sst.utp_stats.num_idle); + STAT_LOGL(d, sst.utp_stats.num_syn_sent); + STAT_LOGL(d, sst.utp_stats.num_connected); + STAT_LOGL(d, sst.utp_stats.num_fin_sent); + STAT_LOGL(d, sst.utp_stats.num_close_wait); + + STAT_LOGL(d, num_tcp_peers); + STAT_LOGL(d, num_utp_peers); + + STAT_LOGL(d, m_connrefused_peers); + STAT_LOGL(d, m_connaborted_peers); + STAT_LOGL(d, m_perm_peers); + STAT_LOGL(d, m_buffer_peers); + STAT_LOGL(d, m_unreachable_peers); + STAT_LOGL(d, m_broken_pipe_peers); + STAT_LOGL(d, m_addrinuse_peers); + STAT_LOGL(d, m_no_access_peers); + STAT_LOGL(d, m_invalid_arg_peers); + STAT_LOGL(d, m_aborted_peers); + + STAT_LOGL(d, m_error_incoming_peers); + STAT_LOGL(d, m_error_outgoing_peers); + STAT_LOGL(d, m_error_rc4_peers); + STAT_LOGL(d, m_error_encrypted_peers); + STAT_LOGL(d, m_error_tcp_peers); + STAT_LOGL(d, m_error_utp_peers); + + STAT_LOG(d, int(m_connections.size())); + STAT_LOGL(d, pending_incoming_reqs); + STAT_LOG(f, num_complete_connections == 0 ? 0.f : (float(pending_incoming_reqs) / num_complete_connections)); + + STAT_LOGL(d, num_want_more_peers); + STAT_LOG(f, total_peers_limit == 0 ? 0 : float(num_limited_peers) / total_peers_limit); + + STAT_LOGL(d, m_piece_requests); + STAT_LOGL(d, m_max_piece_requests); + STAT_LOGL(d, m_invalid_piece_requests); + STAT_LOGL(d, m_choked_piece_requests); + STAT_LOGL(d, m_cancelled_piece_requests); + STAT_LOGL(d, m_piece_rejects); + + STAT_LOGL(d, peers_up_send_buffer); + + STAT_LOG(d, int(sst.utp_stats.packet_loss)); + STAT_LOG(d, int(sst.utp_stats.timeout)); + STAT_LOG(d, int(sst.utp_stats.packets_in)); + STAT_LOG(d, int(sst.utp_stats.packets_out)); + STAT_LOG(d, int(sst.utp_stats.fast_retransmit)); + STAT_LOG(d, int(sst.utp_stats.packet_resend)); + STAT_LOG(d, int(sst.utp_stats.samples_above_target)); + STAT_LOG(d, int(sst.utp_stats.samples_below_target)); + STAT_LOG(d, int(sst.utp_stats.payload_pkts_in)); + STAT_LOG(d, int(sst.utp_stats.payload_pkts_out)); + STAT_LOG(d, int(sst.utp_stats.invalid_pkts_in)); + STAT_LOG(d, int(sst.utp_stats.redundant_pkts_in)); + + fprintf(m_stats_logger, "\n"); + +#undef STAT_LOG +#undef STAT_LOGL + + m_last_cache_status = cs; + m_last_vm_stat = vm_stat; + m_network_thread_cpu_usage = cur_cpu_usage; + m_last_failed = m_total_failed_bytes; + m_last_redundant = m_total_redundant_bytes; + m_last_uploaded = m_stat.total_upload(); + m_last_downloaded = m_stat.total_download(); + } + + reset_stat_counters(); + } +#endif // TORRENT_STATS + + void session_impl::update_rss_feeds() + { + time_t now_posix = time(0); + ptime min_update = max_time(); + ptime now = time_now(); + for (std::vector >::iterator i + = m_feeds.begin(), end(m_feeds.end()); i != end; ++i) + { + feed& f = **i; + int delta = f.next_update(now_posix); + if (delta <= 0) + delta = f.update_feed(); + TORRENT_ASSERT(delta >= 0); + ptime next_update = now + seconds(delta); + if (next_update < min_update) min_update = next_update; + } + m_next_rss_update = min_update; + } + + void session_impl::prioritize_connections(boost::weak_ptr t) + { + m_prio_torrents.push_back(std::make_pair(t, 10)); + } + +#ifndef TORRENT_DISABLE_DHT + + void session_impl::add_dht_node(udp::endpoint n) + { + TORRENT_ASSERT(is_network_thread()); + + if (m_dht) m_dht->add_node(n); + } + + void session_impl::prioritize_dht(boost::weak_ptr t) + { + TORRENT_ASSERT(m_dht); + m_dht_torrents.push_back(t); + // trigger a DHT announce right away if we just added a new torrent and + // there's no back-log. in the timer handler, as long as there are more + // high priority torrents to be announced to the DHT, it will keep the + // timer interval short until all torrents have been announced. + if (m_dht_torrents.size() == 1) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_dht_announce"); +#endif + error_code ec; + m_dht_announce_timer.expires_from_now(seconds(0), ec); + m_dht_announce_timer.async_wait( + bind(&session_impl::on_dht_announce, this, _1)); + } + } + + void session_impl::on_dht_announce(error_code const& e) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("session_impl::on_dht_announce"); +#endif + TORRENT_ASSERT(is_network_thread()); + if (e) return; + + if (m_abort) return; + + TORRENT_ASSERT(m_dht); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_dht_announce"); +#endif + // announce to DHT every 15 minutes + int delay = (std::max)(m_settings.dht_announce_interval + / (std::max)(int(m_torrents.size()), 1), 1); + + if (!m_dht_torrents.empty()) + { + // we have prioritized torrents that need + // an initial DHT announce. Don't wait too long + // until we announce those. + delay = (std::min)(4, delay); + } + + error_code ec; + m_dht_announce_timer.expires_from_now(seconds(delay), ec); + m_dht_announce_timer.async_wait( + bind(&session_impl::on_dht_announce, this, _1)); + + if (!m_dht_torrents.empty()) + { + boost::shared_ptr t; + do + { + t = m_dht_torrents.front().lock(); + m_dht_torrents.pop_front(); + } + while (!t && !m_dht_torrents.empty()); + if (t) + { + t->dht_announce(); + return; + } + } + if (m_torrents.empty()) return; + + if (m_next_dht_torrent == m_torrents.end()) + m_next_dht_torrent = m_torrents.begin(); + m_next_dht_torrent->second->dht_announce(); + ++m_next_dht_torrent; + if (m_next_dht_torrent == m_torrents.end()) + m_next_dht_torrent = m_torrents.begin(); + } +#endif + + void session_impl::on_lsd_announce(error_code const& e) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("session_impl::on_lsd_announce"); +#endif +#ifdef TORRENT_STATS + ++m_num_messages[on_lsd_counter]; +#endif + TORRENT_ASSERT(is_network_thread()); + if (e) return; + + if (m_abort) return; + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_lsd_announce"); +#endif + // announce on local network every 5 minutes + int delay = (std::max)(m_settings.local_service_announce_interval + / (std::max)(int(m_torrents.size()), 1), 1); + error_code ec; + m_lsd_announce_timer.expires_from_now(seconds(delay), ec); + m_lsd_announce_timer.async_wait( + bind(&session_impl::on_lsd_announce, this, _1)); + + if (m_torrents.empty()) return; + + if (m_next_lsd_torrent == m_torrents.end()) + m_next_lsd_torrent = m_torrents.begin(); + m_next_lsd_torrent->second->lsd_announce(); + ++m_next_lsd_torrent; + if (m_next_lsd_torrent == m_torrents.end()) + m_next_lsd_torrent = m_torrents.begin(); + } + + void session_impl::auto_manage_torrents(std::vector& list + , int& dht_limit, int& tracker_limit, int& lsd_limit + , int& hard_limit, int type_limit) + { + for (std::vector::iterator i = list.begin() + , end(list.end()); i != end; ++i) + { + torrent* t = *i; + + if ((t->state() == torrent_status::checking_files + || t->state() == torrent_status::queued_for_checking)) + continue; + + --dht_limit; + --lsd_limit; + --tracker_limit; + t->set_announce_to_dht(dht_limit >= 0); + t->set_announce_to_trackers(tracker_limit >= 0); + t->set_announce_to_lsd(lsd_limit >= 0); + + if (!t->is_paused() && t->is_inactive() + && hard_limit > 0) + { + // the hard limit takes inactive torrents into account, but the + // download and seed limits don't. + continue; + } + + if (type_limit > 0 && hard_limit > 0) + { + --hard_limit; + --type_limit; +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + t->log_to_all_peers("AUTO MANAGER STARTING TORRENT"); +#endif + t->set_allow_peers(true); + } + else + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + t->log_to_all_peers("AUTO MANAGER PAUSING TORRENT"); +#endif + // use graceful pause for auto-managed torrents + t->set_allow_peers(false, true); + } + } + } + + void session_impl::recalculate_auto_managed_torrents() + { + INVARIANT_CHECK; + + m_need_auto_manage = false; + + // these vectors are filled with auto managed torrents + std::vector downloaders; + downloaders.reserve(m_torrents.size()); + std::vector seeds; + seeds.reserve(m_torrents.size()); + + // these counters are set to the number of torrents + // of each kind we're allowed to have active + int num_downloaders = settings().active_downloads; + int num_seeds = settings().active_seeds; + int dht_limit = settings().active_dht_limit; + int tracker_limit = settings().active_tracker_limit; + int lsd_limit = settings().active_lsd_limit; + int hard_limit = settings().active_limit; + + if (num_downloaders == -1) + num_downloaders = (std::numeric_limits::max)(); + if (num_seeds == -1) + num_seeds = (std::numeric_limits::max)(); + if (hard_limit == -1) + hard_limit = (std::numeric_limits::max)(); + if (dht_limit == -1) + dht_limit = (std::numeric_limits::max)(); + if (lsd_limit == -1) + lsd_limit = (std::numeric_limits::max)(); + if (tracker_limit == -1) + tracker_limit = (std::numeric_limits::max)(); + + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + torrent* t = i->second.get(); + TORRENT_ASSERT(t); + + // checking torrents are not subject to auto-management + if (t->state() == torrent_status::checking_files + || t->state() == torrent_status::queued_for_checking) + { + if (t->is_auto_managed() && t->is_paused()) t->resume(); + continue; + } + if (t->is_auto_managed() && !t->has_error()) + { + TORRENT_ASSERT(t->m_resume_data_loaded || !t->valid_metadata()); + // this torrent is auto managed, add it to + // the list (depending on if it's a seed or not) + if (t->is_finished()) + seeds.push_back(t); + else + downloaders.push_back(t); + } + else if (!t->is_paused()) + { + TORRENT_ASSERT(t->m_resume_data_loaded || !t->valid_metadata()); + --hard_limit; + } + } + + bool handled_by_extension = false; + +#ifndef TORRENT_DISABLE_EXTENSIONS + // TODO: 0 allow extensions to sort torrents for queuing +#endif + + if (!handled_by_extension) + { + std::sort(downloaders.begin(), downloaders.end() + , boost::bind(&torrent::sequence_number, _1) < boost::bind(&torrent::sequence_number, _2)); + + std::sort(seeds.begin(), seeds.end() + , boost::bind(&torrent::seed_rank, _1, boost::ref(m_settings)) + > boost::bind(&torrent::seed_rank, _2, boost::ref(m_settings))); + } + + if (settings().auto_manage_prefer_seeds) + { + auto_manage_torrents(seeds, dht_limit, tracker_limit, lsd_limit + , hard_limit, num_seeds); + auto_manage_torrents(downloaders, dht_limit, tracker_limit, lsd_limit + , hard_limit, num_downloaders); + } + else + { + auto_manage_torrents(downloaders, dht_limit, tracker_limit, lsd_limit + , hard_limit, num_downloaders); + auto_manage_torrents(seeds, dht_limit, tracker_limit, lsd_limit + , hard_limit, num_seeds); + } + } + + void session_impl::recalculate_optimistic_unchoke_slots() + { + TORRENT_ASSERT(is_network_thread()); + if (m_allowed_upload_slots == 0) return; + + std::vector opt_unchoke; + + for (connection_map::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + peer_connection* p = i->get(); + TORRENT_ASSERT(p); + policy::peer* pi = p->peer_info_struct(); + if (!pi) continue; + if (pi->web_seed) continue; + torrent* t = p->associated_torrent().lock().get(); + if (!t) continue; + if (t->is_paused()) continue; + + if (pi->optimistically_unchoked) + { + TORRENT_ASSERT(!p->is_choked()); + opt_unchoke.push_back(pi); + } + + if (!p->is_connecting() + && !p->is_disconnecting() + && p->is_peer_interested() + && t->free_upload_slots() + && p->is_choked() + && !p->ignore_unchoke_slots() + && t->valid_metadata()) + { + opt_unchoke.push_back(pi); + } + } + + // find the peers that has been waiting the longest to be optimistically + // unchoked + + // avoid having a bias towards peers that happen to be sorted first + std::random_shuffle(opt_unchoke.begin(), opt_unchoke.end()); + + // sort all candidates based on when they were last optimistically + // unchoked. + std::sort(opt_unchoke.begin(), opt_unchoke.end() + , boost::bind(&policy::peer::last_optimistically_unchoked, _1) + < boost::bind(&policy::peer::last_optimistically_unchoked, _2)); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (ses_extension_list_t::iterator i = m_ses_extensions.begin() + , end(m_ses_extensions.end()); i != end; ++i) + { + if ((*i)->on_optimistic_unchoke(opt_unchoke)) + break; + } +#endif + + int num_opt_unchoke = m_settings.num_optimistic_unchoke_slots; + if (num_opt_unchoke == 0) num_opt_unchoke = (std::max)(1, m_allowed_upload_slots / 5); + + // unchoke the first num_opt_unchoke peers in the candidate set + // and make sure that the others are choked + for (std::vector::iterator i = opt_unchoke.begin() + , end(opt_unchoke.end()); i != end; ++i) + { + policy::peer* pi = *i; + if (num_opt_unchoke > 0) + { + --num_opt_unchoke; + if (!pi->optimistically_unchoked) + { + torrent* t = pi->connection->associated_torrent().lock().get(); + bool ret = t->unchoke_peer(*pi->connection, true); + if (ret) + { + pi->optimistically_unchoked = true; + ++m_num_unchoked; + pi->last_optimistically_unchoked = boost::uint16_t(session_time()); + } + else + { + // we failed to unchoke it, increment the count again + ++num_opt_unchoke; + } + } + } + else + { + if (pi->optimistically_unchoked) + { + torrent* t = pi->connection->associated_torrent().lock().get(); + pi->optimistically_unchoked = false; + t->choke_peer(*pi->connection); + --m_num_unchoked; + } + } + } + } + + void session_impl::try_connect_more_peers(int num_downloads, int num_downloads_peers) + { + // let torrents connect to peers if they want to + // if there are any torrents and any free slots + + // this loop will "hand out" max(connection_speed + // , half_open.free_slots()) to the torrents, in a + // round robin fashion, so that every torrent is + // equally likely to connect to a peer + + int free_slots = m_half_open.free_slots(); + int max_connections = m_settings.connection_speed; + // boost connections are connections made by torrent connection + // boost, which are done immediately on a tracker response. These + // connections needs to be deducted from this second + if (m_boost_connections > 0) + { + if (m_boost_connections > max_connections) + { + m_boost_connections -= max_connections; + max_connections = 0; + } + else + { + max_connections -= m_boost_connections; + m_boost_connections = 0; + } + } + + // this logic is here to smooth out the number of new connection + // attempts over time, to prevent connecting a large number of + // sockets, wait 10 seconds, and then try again + int limit = (std::min)(m_settings.connections_limit - num_connections(), free_slots); + if (m_settings.smooth_connects && max_connections > (limit+1) / 2) + max_connections = (limit+1) / 2; + + // TODO: use a lower limit than m_settings.connections_limit + // to allocate the to 10% or so of connection slots for incoming + // connections + if (!m_torrents.empty() + && free_slots > -m_half_open.limit() + && num_connections() < m_settings.connections_limit + && !m_abort + && m_settings.connection_speed > 0 + && max_connections > 0) + { + // this is the maximum number of connections we will + // attempt this tick +// int average_peers = 0; +// if (num_downloads > 0) +// average_peers = num_downloads_peers / num_downloads; + + if (m_next_connect_torrent == m_torrents.end()) + m_next_connect_torrent = m_torrents.begin(); + + int steps_since_last_connect = 0; + int num_torrents = int(m_torrents.size()); + for (;;) + { + torrent& t = *m_next_connect_torrent->second; + if (t.want_more_peers()) + { + TORRENT_ASSERT(t.allows_peers()); + // have a bias to give more connection attempts + // to downloading torrents than seed, and even + // more to downloading torrents with less than + // average number of connections + int num_attempts = 1; + if (!t.is_finished() && m_num_active_downloading > 0) + { + // TODO: make this bias configurable + // TODO: also take average_peers into account, to create a + // bias for downloading torrents with < average peers + num_attempts += m_num_active_finished / m_num_active_downloading; + } + while (m_current_connect_attempts < num_attempts) + { + TORRENT_TRY + { + ++m_current_connect_attempts; + if (t.try_connect_peer()) + { + --max_connections; + --free_slots; + steps_since_last_connect = 0; +#ifdef TORRENT_STATS + ++m_connection_attempts; +#endif + } + } + TORRENT_CATCH(std::bad_alloc&) + { + // we ran out of memory trying to connect to a peer + // lower the global limit to the number of peers + // we already have + m_settings.connections_limit = num_connections(); + if (m_settings.connections_limit < 2) m_settings.connections_limit = 2; + } + if (!t.want_more_peers()) break; + if (free_slots <= -m_half_open.limit()) return; + if (max_connections == 0) return; + if (num_connections() >= m_settings.connections_limit) return; + } + } + + ++m_next_connect_torrent; + m_current_connect_attempts = 0; + ++steps_since_last_connect; + if (m_next_connect_torrent == m_torrents.end()) + m_next_connect_torrent = m_torrents.begin(); + + // if we have gone a whole loop without + // handing out a single connection, break + if (steps_since_last_connect > num_torrents + 1) break; + // if there are no more free connection slots, abort + if (free_slots <= -m_half_open.limit()) break; + // if we should not make any more connections + // attempts this tick, abort + if (max_connections == 0) break; + // maintain the global limit on number of connections + if (num_connections() >= m_settings.connections_limit) break; + } + } + } + + void session_impl::recalculate_unchoke_slots(int congested_torrents + , int uncongested_torrents) + { + TORRENT_ASSERT(is_network_thread()); + INVARIANT_CHECK; + + ptime now = time_now(); + time_duration unchoke_interval = now - m_last_choke; + m_last_choke = now; + + // build list of all peers that are + // unchokable. + std::vector peers; + for (connection_map::iterator i = m_connections.begin(); + i != m_connections.end();) + { + boost::intrusive_ptr p = *i; + TORRENT_ASSERT(p); + ++i; + torrent* t = p->associated_torrent().lock().get(); + policy::peer* pi = p->peer_info_struct(); + + if (p->ignore_unchoke_slots() || t == 0 || pi == 0 || pi->web_seed || t->is_paused()) + continue; + + if (m_settings.choking_algorithm == session_settings::bittyrant_choker) + { + if (!p->is_choked() && p->is_interesting()) + { + if (!p->has_peer_choked()) + { + // we're unchoked, we may want to lower our estimated + // reciprocation rate + p->decrease_est_reciprocation_rate(); + } + else + { + // we've unchoked this peer, and it hasn't reciprocated + // we may want to increase our estimated reciprocation rate + p->increase_est_reciprocation_rate(); + } + } + } + + if (!p->is_peer_interested() + || p->is_disconnecting() + || p->is_connecting()) + { + // this peer is not unchokable. So, if it's unchoked + // already, make sure to choke it. + if (p->is_choked()) continue; + if (pi && pi->optimistically_unchoked) + { + pi->optimistically_unchoked = false; + // force a new optimistic unchoke + m_optimistic_unchoke_time_scaler = 0; + // TODO: post a message to have this happen + // immediately instead of waiting for the next tick + } + t->choke_peer(*p); + continue; + } + peers.push_back(p.get()); + } + + if (m_settings.choking_algorithm == session_settings::rate_based_choker) + { + m_allowed_upload_slots = 0; + std::sort(peers.begin(), peers.end() + , boost::bind(&peer_connection::upload_rate_compare, _1, _2)); + +#ifdef TORRENT_DEBUG + for (std::vector::const_iterator i = peers.begin() + , end(peers.end()), prev(peers.end()); i != end; ++i) + { + if (prev != end) + { + boost::shared_ptr t1 = (*prev)->associated_torrent().lock(); + TORRENT_ASSERT(t1); + boost::shared_ptr t2 = (*i)->associated_torrent().lock(); + TORRENT_ASSERT(t2); + TORRENT_ASSERT((*prev)->uploaded_in_last_round() * 1000 + * (1 + t1->priority()) / total_milliseconds(unchoke_interval) + >= (*i)->uploaded_in_last_round() * 1000 + * (1 + t2->priority()) / total_milliseconds(unchoke_interval)); + } + prev = i; + } +#endif + + // TODO: make configurable + int rate_threshold = 1024; + + for (std::vector::const_iterator i = peers.begin() + , end(peers.end()); i != end; ++i) + { + peer_connection const& p = **i; + int rate = int(p.uploaded_in_last_round() + * 1000 / total_milliseconds(unchoke_interval)); + + if (rate < rate_threshold) break; + + ++m_allowed_upload_slots; + + // TODO: make configurable + rate_threshold += 1024; + } + // allow one optimistic unchoke + ++m_allowed_upload_slots; + } + + if (m_settings.choking_algorithm == session_settings::bittyrant_choker) + { + // if we're using the bittyrant choker, sort peers by their return + // on investment. i.e. download rate / upload rate + std::sort(peers.begin(), peers.end() + , boost::bind(&peer_connection::bittyrant_unchoke_compare, _1, _2)); + } + else + { + // sorts the peers that are eligible for unchoke by download rate and secondary + // by total upload. The reason for this is, if all torrents are being seeded, + // the download rate will be 0, and the peers we have sent the least to should + // be unchoked + std::sort(peers.begin(), peers.end() + , boost::bind(&peer_connection::unchoke_compare, _1, _2)); + } + + // auto unchoke + int upload_limit = m_bandwidth_channel[peer_connection::upload_channel]->throttle(); + if (m_settings.choking_algorithm == session_settings::auto_expand_choker + && upload_limit > 0) + { + // if our current upload rate is less than 90% of our + // limit AND most torrents are not "congested", i.e. + // they are not holding back because of a per-torrent + // limit + if (m_stat.upload_rate() < upload_limit * 0.9f + && m_allowed_upload_slots <= m_num_unchoked + 1 + && congested_torrents < uncongested_torrents + && m_upload_rate.queue_size() < 2) + { + ++m_allowed_upload_slots; + } + else if (m_upload_rate.queue_size() > 1 + && m_allowed_upload_slots > m_settings.unchoke_slots_limit + && m_settings.unchoke_slots_limit >= 0) + { + --m_allowed_upload_slots; + } + } + + int num_opt_unchoke = m_settings.num_optimistic_unchoke_slots; + if (num_opt_unchoke == 0) num_opt_unchoke = (std::max)(1, m_allowed_upload_slots / 5); + + // reserve some upload slots for optimistic unchokes + int unchoke_set_size = m_allowed_upload_slots - num_opt_unchoke; + + int upload_capacity_left = 0; + if (m_settings.choking_algorithm == session_settings::bittyrant_choker) + { + upload_capacity_left = m_upload_channel.throttle(); + if (upload_capacity_left == 0) + { + // we don't know at what rate we can upload. If we have a + // measurement of the peak, use that + 10kB/s, otherwise + // assume 20 kB/s + upload_capacity_left = (std::max)(20000, m_peak_up_rate + 10000); + if (m_alerts.should_post()) + m_alerts.post_alert(performance_alert(torrent_handle() + , performance_alert::bittyrant_with_no_uplimit)); + } + } + + m_num_unchoked = 0; + // go through all the peers and unchoke the first ones and choke + // all the other ones. + for (std::vector::iterator i = peers.begin() + , end(peers.end()); i != end; ++i) + { + peer_connection* p = *i; + TORRENT_ASSERT(p); + TORRENT_ASSERT(!p->ignore_unchoke_slots()); + + // this will update the m_uploaded_at_last_unchoke + // #error this should be called for all peers! + p->reset_choke_counters(); + + torrent* t = p->associated_torrent().lock().get(); + TORRENT_ASSERT(t); + + // if this peer should be unchoked depends on different things + // in different unchoked schemes + bool unchoke = false; + if (m_settings.choking_algorithm == session_settings::bittyrant_choker) + { + unchoke = p->est_reciprocation_rate() <= upload_capacity_left; + } + else + { + unchoke = unchoke_set_size > 0; + } + + if (unchoke) + { + upload_capacity_left -= p->est_reciprocation_rate(); + + // yes, this peer should be unchoked + if (p->is_choked()) + { + if (!t->unchoke_peer(*p)) + continue; + } + + --unchoke_set_size; + ++m_num_unchoked; + + TORRENT_ASSERT(p->peer_info_struct()); + if (p->peer_info_struct()->optimistically_unchoked) + { + // force a new optimistic unchoke + // since this one just got promoted into the + // proper unchoke set + m_optimistic_unchoke_time_scaler = 0; + p->peer_info_struct()->optimistically_unchoked = false; + } + } + else + { + // no, this peer should be shoked + TORRENT_ASSERT(p->peer_info_struct()); + if (!p->is_choked() && !p->peer_info_struct()->optimistically_unchoked) + t->choke_peer(*p); + if (!p->is_choked()) + ++m_num_unchoked; + } + } + } + +#if defined _MSC_VER && defined TORRENT_DEBUG + static void straight_to_debugger(unsigned int, _EXCEPTION_POINTERS*) + { throw; } +#endif + + void session_impl::main_thread() + { +#if defined _MSC_VER && defined TORRENT_DEBUG + // workaround for microsofts + // hardware exceptions that makes + // it hard to debug stuff + ::_set_se_translator(straight_to_debugger); +#endif +#if TORRENT_USE_ASSERTS && defined BOOST_HAS_PTHREADS + m_network_thread = pthread_self(); +#endif + TORRENT_ASSERT(is_network_thread()); + + // initialize async operations + init(); + + bool stop_loop = false; + while (!stop_loop) + { + error_code ec; + m_io_service.run(ec); + if (ec) + { +#ifdef TORRENT_DEBUG + fprintf(stderr, "%s\n", ec.message().c_str()); + std::string err = ec.message(); +#endif + TORRENT_ASSERT(false); + } + m_io_service.reset(); + + stop_loop = m_abort; + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" locking mutex"); +#endif + +/* +#ifdef TORRENT_DEBUG + for (torrent_map::iterator i = m_torrents.begin(); + i != m_torrents.end(); ++i) + { + TORRENT_ASSERT(i->second->num_peers() == 0); + } +#endif +*/ +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" cleaning up torrents"); +#endif + m_torrents.clear(); + + TORRENT_ASSERT(m_torrents.empty()); + TORRENT_ASSERT(m_connections.empty()); + +#if TORRENT_USE_ASSERTS && defined BOOST_HAS_PTHREADS + m_network_thread = 0; +#endif + } + + + // the return value from this function is valid only as long as the + // session is locked! + boost::weak_ptr session_impl::find_torrent(sha1_hash const& info_hash) const + { + TORRENT_ASSERT(is_network_thread()); + + torrent_map::const_iterator i = m_torrents.find(info_hash); +#ifdef TORRENT_DEBUG + for (torrent_map::const_iterator j + = m_torrents.begin(); j != m_torrents.end(); ++j) + { + torrent* p = boost::get_pointer(j->second); + TORRENT_ASSERT(p); + } +#endif + if (i != m_torrents.end()) return i->second; + return boost::weak_ptr(); + } + + boost::weak_ptr session_impl::find_torrent(std::string const& uuid) const + { + TORRENT_ASSERT(is_network_thread()); + + std::map >::const_iterator i + = m_uuids.find(uuid); + if (i != m_uuids.end()) return i->second; + return boost::weak_ptr(); + } + + // returns true if lhs is a better disconnect candidate than rhs + bool compare_disconnect_torrent(session_impl::torrent_map::value_type const& lhs + , session_impl::torrent_map::value_type const& rhs) + { + // a torrent with 0 peers is never a good disconnect candidate + // since there's nothing to disconnect + if ((lhs.second->num_peers() == 0) != (rhs.second->num_peers() == 0)) + return lhs.second->num_peers() != 0; + + // other than that, always prefer to disconnect peers from seeding torrents + // in order to not harm downloading ones + if (lhs.second->is_seed() != rhs.second->is_seed()) + return lhs.second->is_seed(); + + return lhs.second->num_peers() > rhs.second->num_peers(); + } + + boost::weak_ptr session_impl::find_disconnect_candidate_torrent() const + { + aux::session_impl::torrent_map::const_iterator i = std::min_element(m_torrents.begin(), m_torrents.end() + , boost::bind(&compare_disconnect_torrent, _1, _2)); + + TORRENT_ASSERT(i != m_torrents.end()); + if (i == m_torrents.end()) return boost::shared_ptr(); + + return i->second; + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + boost::shared_ptr session_impl::create_log(std::string const& name + , int instance, bool append) + { + error_code ec; + // current options are file_logger, cout_logger and null_logger + return boost::shared_ptr(new logger(m_logpath, name, instance, append)); + } + + void session_impl::session_log(char const* fmt, ...) const + { + if (!m_logger) return; + + va_list v; + va_start(v, fmt); + + char usr[400]; + vsnprintf(usr, sizeof(usr), fmt, v); + va_end(v); + char buf[450]; + snprintf(buf, sizeof(buf), "%s: %s\n", time_now_string(), usr); + (*m_logger) << buf; + } +#endif + + void session_impl::get_torrent_status(std::vector* ret + , boost::function const& pred + , boost::uint32_t flags) const + { + for (torrent_map::const_iterator i + = m_torrents.begin(), end(m_torrents.end()); + i != end; ++i) + { + if (i->second->is_aborted()) continue; + torrent_status st; + i->second->status(&st, flags); + if (!pred(st)) continue; + ret->push_back(st); + } + } + + void session_impl::refresh_torrent_status(std::vector* ret + , boost::uint32_t flags) const + { + for (std::vector::iterator i + = ret->begin(), end(ret->end()); i != end; ++i) + { + boost::shared_ptr t = i->handle.m_torrent.lock(); + if (!t) continue; + t->status(&*i, flags); + } + } + + void session_impl::post_torrent_updates() + { + INVARIANT_CHECK; + + TORRENT_ASSERT(is_network_thread()); + + std::auto_ptr alert(new state_update_alert()); + alert->status.reserve(m_state_updates.size()); + +#if TORRENT_USE_ASSERTS + m_posting_torrent_updates = true; +#endif + + for (std::vector >::iterator i = m_state_updates.begin() + , end(m_state_updates.end()); i != end; ++i) + { + boost::shared_ptr t = i->lock(); + if (!t) continue; + alert->status.push_back(torrent_status()); + t->status(&alert->status.back(), 0xffffffff); + t->clear_in_state_update(); + } + m_state_updates.clear(); + +#if TORRENT_USE_ASSERTS + m_posting_torrent_updates = false; +#endif + + m_alerts.post_alert_ptr(alert.release()); + } + + std::vector session_impl::get_torrents() const + { + std::vector ret; + + for (torrent_map::const_iterator i + = m_torrents.begin(), end(m_torrents.end()); + i != end; ++i) + { + if (i->second->is_aborted()) continue; + ret.push_back(torrent_handle(i->second)); + } + return ret; + } + + torrent_handle session_impl::find_torrent_handle(sha1_hash const& info_hash) + { + return torrent_handle(find_torrent(info_hash)); + } + + void session_impl::async_add_torrent(add_torrent_params* params) + { + error_code ec; + torrent_handle handle = add_torrent(*params, ec); + delete params; + } + + torrent_handle session_impl::add_torrent(add_torrent_params const& p + , error_code& ec) + { + torrent_handle h = add_torrent_impl(p, ec); + m_alerts.post_alert(add_torrent_alert(h, p, ec)); + return h; + } + + torrent_handle session_impl::add_torrent_impl(add_torrent_params const& p + , error_code& ec) + { + TORRENT_ASSERT(!p.save_path.empty()); + +#ifndef TORRENT_NO_DEPRECATE + p.update_flags(); +#endif + + add_torrent_params params = p; + if (string_begins_no_case("magnet:", params.url.c_str())) + { + parse_magnet_uri(params.url, params, ec); + if (ec) return torrent_handle(); + params.url.clear(); + } + + if (params.ti && params.ti->is_valid() && params.ti->num_files() == 0) + { + ec = errors::no_files_in_torrent; + return torrent_handle(); + } + +#ifndef TORRENT_DISABLE_DHT + // add p.dht_nodes to the DHT, if enabled + if (m_dht && !p.dht_nodes.empty()) + { + for (std::vector >::const_iterator i = p.dht_nodes.begin() + , end(p.dht_nodes.end()); i != end; ++i) + m_dht->add_node(*i); + } +#endif + +// INVARIANT_CHECK; + + if (is_aborted()) + { + ec = errors::session_is_closing; + return torrent_handle(); + } + + // figure out the info hash of the torrent + sha1_hash const* ih = 0; + sha1_hash tmp; + if (params.ti) ih = ¶ms.ti->info_hash(); + else if (!params.url.empty()) + { + // in order to avoid info-hash collisions, for + // torrents where we don't have an info-hash, but + // just a URL, set the temporary info-hash to the + // hash of the URL. This will be changed once we + // have the actual .torrent file + tmp = hasher(¶ms.url[0], params.url.size()).final(); + ih = &tmp; + } + else ih = ¶ms.info_hash; + + // we don't have a torrent file. If the user provided + // resume data, there may be some metadata in there + if ((!params.ti || !params.ti->is_valid()) + && !params.resume_data.empty()) + { + int pos; + error_code ec; + lazy_entry tmp; + lazy_entry const* info = 0; +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + session_log("adding magnet link with resume data"); +#endif + if (lazy_bdecode(¶ms.resume_data[0], ¶ms.resume_data[0] + + params.resume_data.size(), tmp, ec, &pos) == 0 + && tmp.type() == lazy_entry::dict_t + && (info = tmp.dict_find_dict("info"))) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + session_log("found metadata in resume data"); +#endif + // verify the info-hash of the metadata stored in the resume file matches + // the torrent we're loading + + std::pair buf = info->data_section(); + sha1_hash resume_ih = hasher(buf.first, buf.second).final(); + + // if url is set, the info_hash is not actually the info-hash of the + // torrent, but the hash of the URL, until we have the full torrent + // only require the info-hash to match if we actually passed in one + if (resume_ih == params.info_hash + || !params.url.empty() + || params.info_hash.is_all_zeros()) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + session_log("info-hash matched"); +#endif + params.ti = new torrent_info(resume_ih); + + if (params.ti->parse_info_section(*info, ec, 0)) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + session_log("successfully loaded metadata from resume file"); +#endif + // make the info-hash be the one in the resume file + params.info_hash = resume_ih; + ih = ¶ms.info_hash; + } + else + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log("failed to load metadata from resume file: %s" + , ec.message().c_str()); +#endif + } + } +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + else + { + session_log("metadata info-hash failed"); + } +#endif + } +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + else + { + session_log("no metadata found"); + } +#endif + } + + // is the torrent already active? + boost::shared_ptr torrent_ptr = find_torrent(*ih).lock(); + if (!torrent_ptr && !params.uuid.empty()) torrent_ptr = find_torrent(params.uuid).lock(); + // if we still can't find the torrent, look for it by url + if (!torrent_ptr && !params.url.empty()) + { + std::map >::iterator i = std::find_if(m_torrents.begin() + , m_torrents.end(), boost::bind(&torrent::url, boost::bind(&std::pair >::second, _1)) == params.url); + if (i != m_torrents.end()) + torrent_ptr = i->second; + } + + if (torrent_ptr) + { + if ((params.flags & add_torrent_params::flag_duplicate_is_error) == 0) + { + if (!params.uuid.empty() && torrent_ptr->uuid().empty()) + torrent_ptr->set_uuid(params.uuid); + if (!params.url.empty() && torrent_ptr->url().empty()) + torrent_ptr->set_url(params.url); + if (!params.source_feed_url.empty() && torrent_ptr->source_feed_url().empty()) + torrent_ptr->set_source_feed_url(params.source_feed_url); + return torrent_handle(torrent_ptr); + } + + ec = errors::duplicate_torrent; + return torrent_handle(); + } + + int queue_pos = 0; + for (torrent_map::const_iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + int pos = i->second->queue_position(); + if (pos >= queue_pos) queue_pos = pos + 1; + } + + torrent_ptr.reset(new torrent(*this, m_listen_interface + , 16 * 1024, queue_pos, params, *ih)); + torrent_ptr->start(); + +#ifndef TORRENT_DISABLE_EXTENSIONS + typedef std::vector(torrent*, void*)> > + torrent_plugins_t; + + for (torrent_plugins_t::const_iterator i = params.extensions.begin() + , end(params.extensions.end()); i != end; ++i) + { + torrent_ptr->add_extension((*i)(torrent_ptr.get(), + params.userdata)); + } + + for (ses_extension_list_t::iterator i = m_ses_extensions.begin() + , end(m_ses_extensions.end()); i != end; ++i) + { + boost::shared_ptr tp((*i)->new_torrent(torrent_ptr.get(), params.userdata)); + if (tp) torrent_ptr->add_extension(tp); + } +#endif + +#ifndef TORRENT_DISABLE_DHT + if (m_dht && params.ti) + { + torrent_info::nodes_t const& nodes = params.ti->nodes(); + std::for_each(nodes.begin(), nodes.end(), boost::bind( + (void(dht::dht_tracker::*)(std::pair const&)) + &dht::dht_tracker::add_node + , boost::ref(m_dht), _1)); + } +#endif + + m_torrents.insert(std::make_pair(*ih, torrent_ptr)); + if (!params.uuid.empty() || !params.url.empty()) + m_uuids.insert(std::make_pair(params.uuid.empty() + ? params.url : params.uuid, torrent_ptr)); + + if (m_alerts.should_post()) + m_alerts.post_alert(torrent_added_alert(torrent_ptr->get_handle())); + + // recalculate auto-managed torrents sooner (or put it off) + // if another torrent will be added within one second from now + // we want to put it off again anyway. So that while we're adding + // a boat load of torrents, we postpone the recalculation until + // we're done adding them all (since it's kind of an expensive operation) + if (params.flags & add_torrent_params::flag_auto_managed) + trigger_auto_manage(); + + return torrent_handle(torrent_ptr); + } + + void session_impl::queue_check_torrent(boost::shared_ptr const& t) + { + if (m_abort) return; + TORRENT_ASSERT(t->should_check_files()); + TORRENT_ASSERT(t->state() != torrent_status::checking_files); + if (m_queued_for_checking.empty()) t->start_checking(); + else t->set_state(torrent_status::queued_for_checking); + TORRENT_ASSERT(std::find(m_queued_for_checking.begin() + , m_queued_for_checking.end(), t) == m_queued_for_checking.end()); + m_queued_for_checking.push_back(t); + } + + void session_impl::dequeue_check_torrent(boost::shared_ptr const& t) + { + INVARIANT_CHECK; + TORRENT_ASSERT(t->state() == torrent_status::checking_files + || t->state() == torrent_status::queued_for_checking); + + if (m_queued_for_checking.empty()) return; + + boost::shared_ptr next_check = *m_queued_for_checking.begin(); + check_queue_t::iterator done = m_queued_for_checking.end(); + for (check_queue_t::iterator i = m_queued_for_checking.begin() + , end(m_queued_for_checking.end()); i != end; ++i) + { + // the reason m_paused is in there is because when the session + // is paused, all torrents that are queued ar all of a sudden + // not supposed to be queued anymore. The first torrent that gets + // removed from the queue will hence trigger this assert, without + // the m_paused exception + TORRENT_ASSERT(*i == t || (*i)->should_check_files() || m_paused); + if (*i == t) done = i; + else if (next_check == t || next_check->queue_position() > (*i)->queue_position()) + next_check = *i; + } + TORRENT_ASSERT(next_check != t || m_queued_for_checking.size() == 1); + // only start a new one if we removed the one that is checking + TORRENT_ASSERT(done != m_queued_for_checking.end()); + if (done == m_queued_for_checking.end()) return; + + if (next_check != t + && t->state() == torrent_status::checking_files + && !m_paused) + { + next_check->start_checking(); + } + + m_queued_for_checking.erase(done); + } + + void session_impl::remove_torrent(const torrent_handle& h, int options) + { + INVARIANT_CHECK; + + boost::shared_ptr tptr = h.m_torrent.lock(); + if (!tptr) return; + + remove_torrent_impl(tptr, options); + + if (m_alerts.should_post()) + m_alerts.post_alert(torrent_removed_alert(tptr->get_handle(), tptr->info_hash())); + + tptr->abort(); + tptr->set_queue_position(-1); + } + + void session_impl::remove_torrent_impl(boost::shared_ptr tptr, int options) + { + // remove from uuid list + if (!tptr->uuid().empty()) + { + std::map >::iterator j + = m_uuids.find(tptr->uuid()); + if (j != m_uuids.end()) m_uuids.erase(j); + } + + torrent_map::iterator i = + m_torrents.find(tptr->torrent_file().info_hash()); + + // this torrent might be filed under the URL-hash + if (i == m_torrents.end() && !tptr->url().empty()) + { + std::string const& url = tptr->url(); + sha1_hash urlhash = hasher(&url[0], url.size()).final(); + i = m_torrents.find(urlhash); + } + + if (i == m_torrents.end()) return; + + torrent& t = *i->second; + if (options & session::delete_files) + { + if (!t.delete_files()) + { + if (m_alerts.should_post()) + m_alerts.post_alert(torrent_delete_failed_alert(t.get_handle() + , error_code(), t.torrent_file().info_hash())); + } + } + + tptr->update_guage(); + +#if TORRENT_USE_ASSERTS + sha1_hash i_hash = t.torrent_file().info_hash(); +#endif +#ifndef TORRENT_DISABLE_DHT + if (i == m_next_dht_torrent) + ++m_next_dht_torrent; +#endif + if (i == m_next_lsd_torrent) + ++m_next_lsd_torrent; + if (i == m_next_connect_torrent) + ++m_next_connect_torrent; + + m_torrents.erase(i); + +#ifndef TORRENT_DISABLE_DHT + if (m_next_dht_torrent == m_torrents.end()) + m_next_dht_torrent = m_torrents.begin(); +#endif + if (m_next_lsd_torrent == m_torrents.end()) + m_next_lsd_torrent = m_torrents.begin(); + if (m_next_connect_torrent == m_torrents.end()) + m_next_connect_torrent = m_torrents.begin(); + + std::list >::iterator k + = std::find(m_queued_for_checking.begin(), m_queued_for_checking.end(), tptr); + if (k != m_queued_for_checking.end()) m_queued_for_checking.erase(k); + TORRENT_ASSERT(m_torrents.find(i_hash) == m_torrents.end()); + } + + void session_impl::listen_on( + std::pair const& port_range + , error_code& ec + , const char* net_interface, int flags) + { + INVARIANT_CHECK; + + tcp::endpoint new_interface; + if (net_interface && std::strlen(net_interface) > 0) + { + new_interface = tcp::endpoint(address::from_string(net_interface, ec), port_range.first); + if (ec) + { + if (m_alerts.should_post()) + m_alerts.post_alert(listen_failed_alert(new_interface, listen_failed_alert::parse_addr, ec + , listen_failed_alert::tcp)); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log("listen_on: %s failed: %s" + , net_interface, ec.message().c_str()); +#endif + return; + } + } + else + { + new_interface = tcp::endpoint(address_v4::any(), port_range.first); + } + + m_listen_port_retries = port_range.second - port_range.first; + + // if the interface is the same and the socket is open + // don't do anything + if (new_interface == m_listen_interface + && !m_listen_sockets.empty()) + return; + + m_listen_interface = new_interface; + + open_listen_port(flags, ec); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + m_logger = create_log("main_session", listen_port(), false); + session_log("log created"); +#endif + } + + address session_impl::listen_address() const + { + for (std::list::const_iterator i = m_listen_sockets.begin() + , end(m_listen_sockets.end()); i != end; ++i) + { + if (i->external_address != address()) return i->external_address; + } + return address(); + } + + boost::uint16_t session_impl::listen_port() const + { + // if peer connections are set up to be received over a socks + // proxy, and it's the same one as we're using for the tracker + // just tell the tracker the socks5 port we're listening on + if (m_socks_listen_socket && m_socks_listen_socket->is_open()) + return m_socks_listen_port; + + // if not, don't tell the tracker anything if we're in force_proxy + // mode. We don't want to leak our listen port since it can + // potentially identify us if it is leaked elsewere + if (m_settings.force_proxy) return 0; + if (m_listen_sockets.empty()) return 0; + return m_listen_sockets.front().external_port; + } + + boost::uint16_t session_impl::ssl_listen_port() const + { +#ifdef TORRENT_USE_OPENSSL + // if peer connections are set up to be received over a socks + // proxy, and it's the same one as we're using for the tracker + // just tell the tracker the socks5 port we're listening on + if (m_socks_listen_socket && m_socks_listen_socket->is_open()) + return m_socks_listen_port; + + // if not, don't tell the tracker anything if we're in force_proxy + // mode. We don't want to leak our listen port since it can + // potentially identify us if it is leaked elsewere + if (m_settings.force_proxy) return 0; + if (m_listen_sockets.empty()) return 0; + for (std::list::const_iterator i = m_listen_sockets.begin() + , end(m_listen_sockets.end()); i != end; ++i) + { + if (i->ssl) return i->external_port; + } +#endif + return 0; + } + + void session_impl::announce_lsd(sha1_hash const& ih, int port, bool broadcast) + { + // use internal listen port for local peers + if (m_lsd.get()) + m_lsd->announce(ih, port, broadcast); + } + + void session_impl::on_lsd_peer(tcp::endpoint peer, sha1_hash const& ih) + { +#ifdef TORRENT_STATS + ++m_num_messages[on_lsd_peer_counter]; +#endif + TORRENT_ASSERT(is_network_thread()); + + INVARIANT_CHECK; + + boost::shared_ptr t = find_torrent(ih).lock(); + if (!t) return; + // don't add peers from lsd to private torrents + if (t->torrent_file().priv() || (t->torrent_file().is_i2p() + && !m_settings.allow_i2p_mixed)) return; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log("added peer from local discovery: %s", print_endpoint(peer).c_str()); +#endif + t->get_policy().add_peer(peer, peer_id(0), peer_info::lsd, 0); + if (m_alerts.should_post()) + m_alerts.post_alert(lsd_peer_alert(t->get_handle(), peer)); + } + + void session_impl::on_port_map_log( + char const* msg, int map_transport) + { + TORRENT_ASSERT(map_transport >= 0 && map_transport <= 1); + // log message +#ifdef TORRENT_UPNP_LOGGING + char const* transport_names[] = {"NAT-PMP", "UPnP"}; + m_upnp_log << time_now_string() << " " + << transport_names[map_transport] << ": " << msg; +#endif + if (m_alerts.should_post()) + m_alerts.post_alert(portmap_log_alert(map_transport, msg)); + } + + void session_impl::on_port_mapping(int mapping, address const& ip, int port + , error_code const& ec, int map_transport) + { + TORRENT_ASSERT(is_network_thread()); + + TORRENT_ASSERT(map_transport >= 0 && map_transport <= 1); + + if (mapping == m_udp_mapping[map_transport] && port != 0) + { + m_external_udp_port = port; + if (m_alerts.should_post()) + m_alerts.post_alert(portmap_alert(mapping, port + , map_transport)); + return; + } + + if (mapping == m_tcp_mapping[map_transport] && port != 0) + { + if (ip != address()) + { + // TODO: 1 report the proper address of the router as the source IP of + // this understanding of our external address, instead of the empty address + set_external_address(ip, source_router, address()); + } + + if (!m_listen_sockets.empty()) { + m_listen_sockets.front().external_address = ip; + m_listen_sockets.front().external_port = port; + } + if (m_alerts.should_post()) + m_alerts.post_alert(portmap_alert(mapping, port + , map_transport)); + return; + } + + if (ec) + { + if (m_alerts.should_post()) + m_alerts.post_alert(portmap_error_alert(mapping + , map_transport, ec)); + } + else + { + if (m_alerts.should_post()) + m_alerts.post_alert(portmap_alert(mapping, port + , map_transport)); + } + } + + session_status session_impl::status() const + { +// INVARIANT_CHECK; + TORRENT_ASSERT(is_network_thread()); + + session_status s; + + s.optimistic_unchoke_counter = m_optimistic_unchoke_time_scaler; + s.unchoke_counter = m_unchoke_time_scaler; + + s.num_peers = (int)m_connections.size(); + s.num_unchoked = m_num_unchoked; + s.allowed_upload_slots = m_allowed_upload_slots; + + s.total_redundant_bytes = m_total_redundant_bytes; + s.total_failed_bytes = m_total_failed_bytes; + + s.up_bandwidth_queue = m_upload_rate.queue_size(); + s.down_bandwidth_queue = m_download_rate.queue_size(); + + s.up_bandwidth_bytes_queue = int(m_upload_rate.queued_bytes()); + s.down_bandwidth_bytes_queue = int(m_download_rate.queued_bytes()); + + s.disk_write_queue = m_disk_queues[peer_connection::download_channel]; + s.disk_read_queue = m_disk_queues[peer_connection::upload_channel]; + + s.has_incoming_connections = m_incoming_connection; + + // total + s.download_rate = m_stat.download_rate(); + s.total_upload = m_stat.total_upload(); + s.upload_rate = m_stat.upload_rate(); + s.total_download = m_stat.total_download(); + + // payload + s.payload_download_rate = m_stat.transfer_rate(stat::download_payload); + s.total_payload_download = m_stat.total_transfer(stat::download_payload); + s.payload_upload_rate = m_stat.transfer_rate(stat::upload_payload); + s.total_payload_upload = m_stat.total_transfer(stat::upload_payload); + +#ifndef TORRENT_DISABLE_FULL_STATS + // IP-overhead + s.ip_overhead_download_rate = m_stat.transfer_rate(stat::download_ip_protocol); + s.total_ip_overhead_download = m_stat.total_transfer(stat::download_ip_protocol); + s.ip_overhead_upload_rate = m_stat.transfer_rate(stat::upload_ip_protocol); + s.total_ip_overhead_upload = m_stat.total_transfer(stat::upload_ip_protocol); + +#ifndef TORRENT_DISABLE_DHT + // DHT protocol + s.dht_download_rate = m_stat.transfer_rate(stat::download_dht_protocol); + s.total_dht_download = m_stat.total_transfer(stat::download_dht_protocol); + s.dht_upload_rate = m_stat.transfer_rate(stat::upload_dht_protocol); + s.total_dht_upload = m_stat.total_transfer(stat::upload_dht_protocol); +#else + s.dht_download_rate = 0; + s.total_dht_download = 0; + s.dht_upload_rate = 0; + s.total_dht_upload = 0; +#endif // TORRENT_DISABLE_DHT + + // tracker + s.tracker_download_rate = m_stat.transfer_rate(stat::download_tracker_protocol); + s.total_tracker_download = m_stat.total_transfer(stat::download_tracker_protocol); + s.tracker_upload_rate = m_stat.transfer_rate(stat::upload_tracker_protocol); + s.total_tracker_upload = m_stat.total_transfer(stat::upload_tracker_protocol); +#else + // IP-overhead + s.ip_overhead_download_rate = 0; + s.total_ip_overhead_download = 0; + s.ip_overhead_upload_rate = 0; + s.total_ip_overhead_upload = 0; + + // DHT protocol + s.dht_download_rate = 0; + s.total_dht_download = 0; + s.dht_upload_rate = 0; + s.total_dht_upload = 0; + + // tracker + s.tracker_download_rate = 0; + s.total_tracker_download = 0; + s.tracker_upload_rate = 0; + s.total_tracker_upload = 0; +#endif + +#ifndef TORRENT_DISABLE_DHT + if (m_dht) + { + m_dht->dht_status(s); + } + else +#endif + { + s.dht_nodes = 0; + s.dht_node_cache = 0; + s.dht_torrents = 0; + s.dht_global_nodes = 0; + s.dht_total_allocations = 0; + } + + m_utp_socket_manager.get_status(s.utp_stats); + + int peerlist_size = 0; + for (torrent_map::const_iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + peerlist_size += i->second->get_policy().num_peers(); + } + + s.peerlist_size = peerlist_size; + + return s; + } + +#ifndef TORRENT_DISABLE_DHT + + void session_impl::start_dht() + { start_dht(m_dht_state); } + + void on_bootstrap(alert_manager& alerts) + { + if (alerts.should_post()) + alerts.post_alert(dht_bootstrap_alert()); + } + + void session_impl::start_dht(entry const& startup_state) + { + INVARIANT_CHECK; + + stop_dht(); + m_dht = new dht::dht_tracker(*this, m_udp_socket, m_dht_settings, &startup_state); + + for (std::list::iterator i = m_dht_router_nodes.begin() + , end(m_dht_router_nodes.end()); i != end; ++i) + { + m_dht->add_router_node(*i); + } + + m_dht->start(startup_state, boost::bind(&on_bootstrap, boost::ref(m_alerts))); + + m_udp_socket.subscribe(m_dht.get()); + } + + void session_impl::stop_dht() + { + if (!m_dht) return; + m_udp_socket.unsubscribe(m_dht.get()); + m_dht->stop(); + m_dht = 0; + } + + void session_impl::set_dht_settings(dht_settings const& settings) + { + m_dht_settings = settings; + } + +#ifndef TORRENT_NO_DEPRECATE + entry session_impl::dht_state() const + { + if (!m_dht) return entry(); + return m_dht->state(); + } +#endif + + void session_impl::add_dht_node_name(std::pair const& node) + { + if (m_dht) m_dht->add_node(node); + } + + void session_impl::add_dht_router(std::pair const& node) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_dht_router_name_lookup"); +#endif + char port[7]; + snprintf(port, sizeof(port), "%d", node.second); + tcp::resolver::query q(node.first, port); + m_host_resolver.async_resolve(q, + boost::bind(&session_impl::on_dht_router_name_lookup, this, _1, _2)); + } + + void session_impl::on_dht_router_name_lookup(error_code const& e + , tcp::resolver::iterator host) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("session_impl::on_dht_router_name_lookup"); +#endif + if (e) + { + if (m_alerts.should_post()) + m_alerts.post_alert(dht_error_alert( + dht_error_alert::hostname_lookup, e)); + return; + } + + while (host != tcp::resolver::iterator()) + { + // router nodes should be added before the DHT is started (and bootstrapped) + udp::endpoint ep(host->endpoint().address(), host->endpoint().port()); + if (m_dht) m_dht->add_router_node(ep); + m_dht_router_nodes.push_back(ep); + ++host; + } + } + + // callback for dht_immutable_get + void session_impl::get_immutable_callback(sha1_hash target + , dht::item const& i) + { + TORRENT_ASSERT(!i.is_mutable()); + m_alerts.post_alert(dht_immutable_item_alert(target, i.value())); + } + + void session_impl::dht_get_immutable_item(sha1_hash const& target) + { + if (!m_dht) return; + m_dht->get_item(target, boost::bind(&session_impl::get_immutable_callback + , this, target, _1)); + } + + // callback for dht_mutable_get + void session_impl::get_mutable_callback(dht::item const& i) + { + TORRENT_ASSERT(i.is_mutable()); + m_alerts.post_alert(dht_mutable_item_alert(i.pk(), i.sig(), i.seq() + , i.salt(), i.value())); + } + + // key is a 32-byte binary string, the public key to look up. + // the salt is optional + void session_impl::dht_get_mutable_item(boost::array key + , std::string salt) + { + if (!m_dht) return; + m_dht->get_item(key.data(), boost::bind(&session_impl::get_mutable_callback + , this, _1), salt); + } + + void on_dht_put(alert_manager& alerts, sha1_hash target) + { + if (alerts.should_post()) + alerts.post_alert(dht_put_alert(target)); + } + + void session_impl::dht_put_item(entry data, sha1_hash target) + { + if (!m_dht) return; + m_dht->put_item(data, boost::bind(&on_dht_put, boost::ref(m_alerts) + , target)); + } + + void put_mutable_callback(alert_manager& alerts, dht::item& i + , boost::function& + , boost::uint64_t&, std::string const&)> cb) + { + entry value = i.value(); + boost::array sig = i.sig(); + boost::array pk = i.pk(); + boost::uint64_t seq = i.seq(); + std::string salt = i.salt(); + cb(value, sig, seq, salt); + i.assign(value, salt, seq, pk.data(), sig.data()); + + if (alerts.should_post()) + alerts.post_alert(dht_put_alert(pk, sig, salt, seq)); + } + + void session_impl::dht_put_mutable_item(boost::array key + , boost::function& + , boost::uint64_t&, std::string const&)> cb + , std::string salt) + { + if (!m_dht) return; + m_dht->put_item(key.data(), boost::bind(&put_mutable_callback + , boost::ref(m_alerts), _1, cb), salt); + } + +#endif + + void session_impl::maybe_update_udp_mapping(int nat, int local_port, int external_port) + { + int local, external, protocol; + if (nat == 0 && m_natpmp.get()) + { + if (m_udp_mapping[nat] != -1) + { + if (m_natpmp->get_mapping(m_udp_mapping[nat], local, external, protocol)) + { + // we already have a mapping. If it's the same, don't do anything + if (local == local_port && external == external_port && protocol == natpmp::udp) + return; + } + m_natpmp->delete_mapping(m_udp_mapping[nat]); + } + m_udp_mapping[nat] = m_natpmp->add_mapping(natpmp::udp + , local_port, external_port); + return; + } + else if (nat == 1 && m_upnp.get()) + { + if (m_udp_mapping[nat] != -1) + { + if (m_upnp->get_mapping(m_udp_mapping[nat], local, external, protocol)) + { + // we already have a mapping. If it's the same, don't do anything + if (local == local_port && external == external_port && protocol == natpmp::udp) + return; + } + m_upnp->delete_mapping(m_udp_mapping[nat]); + } + m_udp_mapping[nat] = m_upnp->add_mapping(upnp::udp + , local_port, external_port); + return; + } + } + +#ifndef TORRENT_DISABLE_ENCRYPTION + void session_impl::set_pe_settings(pe_settings const& settings) + { + m_pe_settings = settings; + } +#endif + + bool session_impl::is_listening() const + { + return !m_listen_sockets.empty(); + } + + session_impl::~session_impl() + { + TORRENT_ASSERT(is_not_network_thread()); + + m_io_service.post(boost::bind(&session_impl::abort, this)); + + // we need to wait for the disk-io thread to + // die first, to make sure it won't post any + // more messages to the io_service containing references + // to disk_io_pool inside the disk_io_thread. Once + // the main thread has handled all the outstanding requests + // we know it's safe to destruct the disk thread. + m_disk_thread.join(); + +#if defined TORRENT_ASIO_DEBUGGING + int counter = 0; + while (log_async()) + { + sleep(1000); + ++counter; + printf("\x1b[2J\x1b[0;0H\x1b[33m==== Waiting to shut down: %d ==== conn-queue: %d connecting: %d timeout (next: %f max: %f)\x1b[0m\n\n" + , counter, m_half_open.size(), m_half_open.num_connecting(), m_half_open.next_timeout() + , m_half_open.max_timeout()); + } + async_dec_threads(); +#endif + + if (m_thread) m_thread->join(); + + m_udp_socket.unsubscribe(this); + m_udp_socket.unsubscribe(&m_utp_socket_manager); + m_udp_socket.unsubscribe(&m_tracker_manager); + + TORRENT_ASSERT(m_torrents.empty()); + TORRENT_ASSERT(m_connections.empty()); + TORRENT_ASSERT(m_connections.empty()); + +#ifdef TORRENT_REQUEST_LOGGING + if (m_request_log) fclose(m_request_log); +#endif + +#ifdef TORRENT_STATS + if (m_stats_logger) fclose(m_stats_logger); +#endif + } + +#ifndef TORRENT_NO_DEPRECATE + int session_impl::max_connections() const + { + return m_settings.connections_limit; + } + + int session_impl::max_uploads() const + { + return m_settings.unchoke_slots_limit; + } + + int session_impl::max_half_open_connections() const + { + return m_settings.half_open_limit; + } + + void session_impl::set_local_download_rate_limit(int bytes_per_second) + { + session_settings s = m_settings; + s.local_download_rate_limit = bytes_per_second; + set_settings(s); + } + + void session_impl::set_local_upload_rate_limit(int bytes_per_second) + { + session_settings s = m_settings; + s.local_upload_rate_limit = bytes_per_second; + set_settings(s); + } + + void session_impl::set_download_rate_limit(int bytes_per_second) + { + session_settings s = m_settings; + s.download_rate_limit = bytes_per_second; + set_settings(s); + } + + void session_impl::set_upload_rate_limit(int bytes_per_second) + { + session_settings s = m_settings; + s.upload_rate_limit = bytes_per_second; + set_settings(s); + } + + void session_impl::set_max_half_open_connections(int limit) + { + session_settings s = m_settings; + s.half_open_limit = limit; + set_settings(s); + } + + void session_impl::set_max_connections(int limit) + { + session_settings s = m_settings; + s.connections_limit = limit; + set_settings(s); + } + + void session_impl::set_max_uploads(int limit) + { + session_settings s = m_settings; + s.unchoke_slots_limit = limit; + set_settings(s); + } + + int session_impl::local_upload_rate_limit() const + { + return m_local_upload_channel.throttle(); + } + + int session_impl::local_download_rate_limit() const + { + return m_local_download_channel.throttle(); + } + + int session_impl::upload_rate_limit() const + { + return m_upload_channel.throttle(); + } + + int session_impl::download_rate_limit() const + { + return m_download_channel.throttle(); + } +#endif + + void session_impl::update_unchoke_limit() + { + m_allowed_upload_slots = m_settings.unchoke_slots_limit; + if (m_allowed_upload_slots < 0) + m_allowed_upload_slots = (std::numeric_limits::max)(); + + if (m_settings.num_optimistic_unchoke_slots >= m_allowed_upload_slots / 2) + { + if (m_alerts.should_post()) + m_alerts.post_alert(performance_alert(torrent_handle() + , performance_alert::too_many_optimistic_unchoke_slots)); + } + } + + void session_impl::update_rate_settings() + { + if (m_settings.half_open_limit <= 0) m_settings.half_open_limit + = (std::numeric_limits::max)(); + m_half_open.limit(m_settings.half_open_limit); + + if (m_settings.local_download_rate_limit < 0) + m_settings.local_download_rate_limit = 0; + m_local_download_channel.throttle(m_settings.local_download_rate_limit); + + if (m_settings.local_upload_rate_limit < 0) + m_settings.local_upload_rate_limit = 0; + m_local_upload_channel.throttle(m_settings.local_upload_rate_limit); + + if (m_settings.download_rate_limit < 0) + m_settings.download_rate_limit = 0; + m_download_channel.throttle(m_settings.download_rate_limit); + + if (m_settings.upload_rate_limit < 0) + m_settings.upload_rate_limit = 0; + m_upload_channel.throttle(m_settings.upload_rate_limit); + } + + void session_impl::update_connections_limit() + { + if (m_settings.connections_limit <= 0) + { + m_settings.connections_limit = (std::numeric_limits::max)(); +#if TORRENT_USE_RLIMIT + rlimit l; + if (getrlimit(RLIMIT_NOFILE, &l) == 0 + && l.rlim_cur != RLIM_INFINITY) + { + m_settings.connections_limit = l.rlim_cur - m_settings.file_pool_size; + if (m_settings.connections_limit < 5) m_settings.connections_limit = 5; + } +#endif + } + + if (num_connections() > m_settings.connections_limit && !m_torrents.empty()) + { + // if we have more connections that we're allowed, disconnect + // peers from the torrents so that they are all as even as possible + + int to_disconnect = num_connections() - m_settings.connections_limit; + + int last_average = 0; + int average = m_settings.connections_limit / m_torrents.size(); + + // the number of slots that are unused by torrents + int extra = m_settings.connections_limit % m_torrents.size(); + + // run 3 iterations of this, then we're probably close enough + for (int iter = 0; iter < 4; ++iter) + { + // the number of torrents that are above average + int num_above = 0; + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + int num = i->second->num_peers(); + if (num <= last_average) continue; + if (num > average) ++num_above; + if (num < average) extra += average - num; + } + + // distribute extra among the torrents that are above average + if (num_above == 0) num_above = 1; + last_average = average; + average += extra / num_above; + if (extra == 0) break; + // save the remainder for the next iteration + extra = extra % num_above; + } + + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + int num = i->second->num_peers(); + if (num <= average) continue; + + // distribute the remainder + int my_average = average; + if (extra > 0) + { + ++my_average; + --extra; + } + + int disconnect = (std::min)(to_disconnect, num - my_average); + to_disconnect -= disconnect; + i->second->disconnect_peers(disconnect + , error_code(errors::too_many_connections, get_libtorrent_category())); + } + } + } + + void session_impl::set_alert_dispatch(boost::function)> const& fun) + { + m_alerts.set_dispatch_function(fun); + } + + std::auto_ptr session_impl::pop_alert() + { + return m_alerts.get(); + } + + void session_impl::pop_alerts(std::deque* alerts) + { + m_alerts.get_all(alerts); + } + + alert const* session_impl::wait_for_alert(time_duration max_wait) + { + return m_alerts.wait_for_alert(max_wait); + } + + void session_impl::set_alert_mask(boost::uint32_t m) + { + m_alerts.set_alert_mask(m); + } + +#ifndef TORRENT_NO_DEPRECATE + size_t session_impl::set_alert_queue_size_limit(size_t queue_size_limit_) + { + m_settings.alert_queue_size = queue_size_limit_; + return m_alerts.set_alert_queue_size_limit(queue_size_limit_); + } +#endif + + void session_impl::start_lsd() + { + INVARIANT_CHECK; + + if (m_lsd) return; + + m_lsd = new lsd(m_io_service + , m_listen_interface.address() + , boost::bind(&session_impl::on_lsd_peer, this, _1, _2)); + } + + natpmp* session_impl::start_natpmp() + { + INVARIANT_CHECK; + + if (m_natpmp) return m_natpmp.get(); + + // the natpmp constructor may fail and call the callbacks + // into the session_impl. + natpmp* n = new (std::nothrow) natpmp(m_io_service + , m_listen_interface.address() + , boost::bind(&session_impl::on_port_mapping + , this, _1, _2, _3, _4, 0) + , boost::bind(&session_impl::on_port_map_log + , this, _1, 0)); + if (n == 0) return 0; + + m_natpmp = n; + + if (m_listen_interface.port() > 0) + { + remap_tcp_ports(1, m_listen_interface.port(), ssl_listen_port()); + } + if (m_udp_socket.is_open()) + { + m_udp_mapping[0] = m_natpmp->add_mapping(natpmp::udp + , m_listen_interface.port(), m_listen_interface.port()); + } + return n; + } + + upnp* session_impl::start_upnp() + { + INVARIANT_CHECK; + + if (m_upnp) return m_upnp.get(); + + // the upnp constructor may fail and call the callbacks + upnp* u = new (std::nothrow) upnp(m_io_service + , m_half_open + , m_listen_interface.address() + , m_settings.user_agent + , boost::bind(&session_impl::on_port_mapping + , this, _1, _2, _3, _4, 1) + , boost::bind(&session_impl::on_port_map_log + , this, _1, 1) + , m_settings.upnp_ignore_nonrouters); + + if (u == 0) return 0; + + m_upnp = u; + + m_upnp->discover_device(); + if (m_listen_interface.port() > 0 || ssl_listen_port() > 0) + { + remap_tcp_ports(2, m_listen_interface.port(), ssl_listen_port()); + } + if (m_udp_socket.is_open()) + { + m_udp_mapping[1] = m_upnp->add_mapping(upnp::udp + , m_listen_interface.port(), m_listen_interface.port()); + } + return u; + } + + int session_impl::add_port_mapping(int t, int external_port + , int local_port) + { + int ret = 0; + if (m_upnp) ret = m_upnp->add_mapping((upnp::protocol_type)t, external_port + , local_port); + if (m_natpmp) ret = m_natpmp->add_mapping((natpmp::protocol_type)t, external_port + , local_port); + return ret; + } + + void session_impl::delete_port_mapping(int handle) + { + if (m_upnp) m_upnp->delete_mapping(handle); + if (m_natpmp) m_natpmp->delete_mapping(handle); + } + + void session_impl::stop_lsd() + { + if (m_lsd.get()) + m_lsd->close(); + m_lsd = 0; + } + + void session_impl::stop_natpmp() + { + if (m_natpmp.get()) + m_natpmp->close(); + m_natpmp = 0; + } + + void session_impl::stop_upnp() + { + if (m_upnp.get()) + { + m_upnp->close(); + m_udp_mapping[1] = -1; + m_tcp_mapping[1] = -1; +#ifdef TORRENT_USE_OPENSSL + m_ssl_mapping[1] = -1; +#endif + } + m_upnp = 0; + } + + external_ip const& session_impl::external_address() const + { return m_external_ip; } + + // this is the DHT observer version. DHT is the implied source + void session_impl::set_external_address(address const& ip + , address const& source) + { + set_external_address(ip, source_dht, source); + } + + void session_impl::set_external_address(address const& ip + , int source_type, address const& source) + { +#if defined TORRENT_VERBOSE_LOGGING + session_log(": set_external_address(%s, %d, %s)", print_address(ip).c_str() + , source_type, print_address(source).c_str()); +#endif + + if (!m_external_ip.cast_vote(ip, source_type, source)) return; + +#if defined TORRENT_VERBOSE_LOGGING + session_log(" external IP updated"); +#endif + + if (m_alerts.should_post()) + m_alerts.post_alert(external_ip_alert(ip)); + + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + i->second->new_external_ip(); + } + + // since we have a new external IP now, we need to + // restart the DHT with a new node ID +#ifndef TORRENT_DISABLE_DHT + // TODO: 1 we only need to do this if our global IPv4 address has changed + // since the DHT (currently) only supports IPv4. Since restarting the DHT + // is kind of expensive, it would be nice to not do it unnecessarily + if (m_dht) + { + entry s = m_dht->state(); + int cur_state = 0; + int prev_state = 0; + entry* nodes1 = s.find_key("nodes"); + if (nodes1 && nodes1->type() == entry::list_t) cur_state = nodes1->list().size(); + entry* nodes2 = m_dht_state.find_key("nodes"); + if (nodes2 && nodes2->type() == entry::list_t) prev_state = nodes2->list().size(); + if (cur_state > prev_state) m_dht_state = s; + start_dht(m_dht_state); + } +#endif + } + + void session_impl::free_disk_buffer(char* buf) + { + m_disk_thread.free_buffer(buf); + } + + char* session_impl::allocate_disk_buffer(char const* category) + { + return m_disk_thread.allocate_buffer(category); + } + + char* session_impl::allocate_buffer() + { + TORRENT_ASSERT(is_network_thread()); + +#ifdef TORRENT_DISK_STATS + TORRENT_ASSERT(m_buffer_allocations >= 0); + m_buffer_allocations++; + m_buffer_usage_logger << log_time() << " protocol_buffer: " + << (m_buffer_allocations * send_buffer_size) << std::endl; +#endif +#ifdef TORRENT_DISABLE_POOL_ALLOCATOR + int num_bytes = send_buffer_size; + return (char*)malloc(num_bytes); +#else + return (char*)m_send_buffers.malloc(); +#endif + } + +#ifdef TORRENT_DISK_STATS + void session_impl::log_buffer_usage() + { + TORRENT_ASSERT(is_network_thread()); + + int send_buffer_capacity = 0; + int used_send_buffer = 0; + for (connection_map::const_iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + send_buffer_capacity += (*i)->send_buffer_capacity(); + used_send_buffer += (*i)->send_buffer_size(); + } + TORRENT_ASSERT(send_buffer_capacity >= used_send_buffer); + m_buffer_usage_logger << log_time() << " send_buffer_size: " << send_buffer_capacity << std::endl; + m_buffer_usage_logger << log_time() << " used_send_buffer: " << used_send_buffer << std::endl; + m_buffer_usage_logger << log_time() << " send_buffer_utilization: " + << (used_send_buffer * 100.f / (std::max)(send_buffer_capacity, 1)) << std::endl; + } +#endif + + void session_impl::free_buffer(char* buf) + { + TORRENT_ASSERT(is_network_thread()); + +#ifdef TORRENT_DISK_STATS + m_buffer_allocations--; + TORRENT_ASSERT(m_buffer_allocations >= 0); + m_buffer_usage_logger << log_time() << " protocol_buffer: " + << (m_buffer_allocations * send_buffer_size) << std::endl; +#endif +#ifdef TORRENT_DISABLE_POOL_ALLOCATOR + free(buf); +#else + m_send_buffers.free(buf); +#endif + } + +#if TORRENT_USE_INVARIANT_CHECKS + void session_impl::check_invariant() const + { + TORRENT_ASSERT(is_network_thread()); + + if (m_settings.unchoke_slots_limit < 0 + && m_settings.choking_algorithm == session_settings::fixed_slots_choker) + TORRENT_ASSERT(m_allowed_upload_slots == (std::numeric_limits::max)()); + + int num_checking = 0; + int num_queued_for_checking = 0; + for (check_queue_t::const_iterator i = m_queued_for_checking.begin() + , end(m_queued_for_checking.end()); i != end; ++i) + { + if ((*i)->state() == torrent_status::checking_files) ++num_checking; + else if ((*i)->state() == torrent_status::queued_for_checking) + { + ++num_queued_for_checking; + } + } + + // the queue is either empty, or it has exactly one checking torrent in it + TORRENT_ASSERT(m_queued_for_checking.empty() || num_checking == 1 || (m_paused && num_checking == 0)); +// TORRENT_ASSERT(m_queued_for_checking.size() == num_queued_for_checking); + + std::set unique; + int num_active_downloading = 0; + int num_active_finished = 0; + int total_downloaders = 0; + for (torrent_map::const_iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + boost::shared_ptr t = i->second; + if (t->is_active_download()) ++num_active_downloading; + else if (t->is_active_finished()) ++num_active_finished; + + int pos = t->queue_position(); + if (pos < 0) + { + TORRENT_ASSERT(pos == -1); + continue; + } + ++total_downloaders; + + unique.insert(t->queue_position()); + } + TORRENT_ASSERT(int(unique.size()) == total_downloaders); + TORRENT_ASSERT(num_active_downloading == m_num_active_downloading); + TORRENT_ASSERT(num_active_finished == m_num_active_finished); + + std::set unique_peers; + TORRENT_ASSERT(m_settings.connections_limit > 0); + if (m_settings.choking_algorithm == session_settings::auto_expand_choker) + TORRENT_ASSERT(m_allowed_upload_slots >= m_settings.unchoke_slots_limit); + int unchokes = 0; + int num_optimistic = 0; + int disk_queue[2] = {0, 0}; + for (connection_map::const_iterator i = m_connections.begin(); + i != m_connections.end(); ++i) + { + TORRENT_ASSERT(*i); + boost::shared_ptr t = (*i)->associated_torrent().lock(); + TORRENT_ASSERT(unique_peers.find(i->get()) == unique_peers.end()); + unique_peers.insert(i->get()); + + if ((*i)->m_channel_state[0] & peer_info::bw_disk) ++disk_queue[0]; + if ((*i)->m_channel_state[1] & peer_info::bw_disk) ++disk_queue[1]; + + peer_connection* p = i->get(); + TORRENT_ASSERT(!p->is_disconnecting()); + if (p->ignore_unchoke_slots()) continue; + if (!p->is_choked()) ++unchokes; + if (p->peer_info_struct() + && p->peer_info_struct()->optimistically_unchoked) + { + ++num_optimistic; + TORRENT_ASSERT(!p->is_choked()); + } + if (t && p->peer_info_struct() && !p->peer_info_struct()->web_seed) + { + TORRENT_ASSERT(t->get_policy().has_connection(p)); + } + } + + TORRENT_ASSERT(disk_queue[0] == m_disk_queues[0]); + TORRENT_ASSERT(disk_queue[1] == m_disk_queues[1]); + + if (m_settings.num_optimistic_unchoke_slots) + { + TORRENT_ASSERT(num_optimistic <= m_settings.num_optimistic_unchoke_slots); + } + + if (m_num_unchoked != unchokes) + { + TORRENT_ASSERT(false); + } + for (torrent_map::const_iterator j + = m_torrents.begin(); j != m_torrents.end(); ++j) + { + TORRENT_ASSERT(boost::get_pointer(j->second)); + } + } +#endif + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + tracker_logger::tracker_logger(session_impl& ses): m_ses(ses) {} + void tracker_logger::tracker_warning(tracker_request const& req + , std::string const& str) + { + debug_log("*** tracker warning: %s", str.c_str()); + } + + void tracker_logger::tracker_response(tracker_request const& + , libtorrent::address const& tracker_ip + , std::list
const& ip_list + , std::vector& peers + , int interval + , int min_interval + , int complete + , int incomplete + , int downloaded + , address const& external_ip + , std::string const& tracker_id) + { + std::string s; + s = "TRACKER RESPONSE:\n"; + char tmp[200]; + snprintf(tmp, 200, "interval: %d\nmin_interval: %d\npeers:\n", interval, min_interval); + s += tmp; + for (std::vector::const_iterator i = peers.begin(); + i != peers.end(); ++i) + { + char pid[41]; + to_hex((const char*)&i->pid[0], 20, pid); + if (i->pid.is_all_zeros()) pid[0] = 0; + + snprintf(tmp, 200, " %-16s %-5d %s\n", i->ip.c_str(), i->port, pid); + s += tmp; + } + snprintf(tmp, 200, "external ip: %s\n", print_address(external_ip).c_str()); + s += tmp; + debug_log("%s", s.c_str()); + } + + void tracker_logger::tracker_request_timed_out( + tracker_request const&) + { + debug_log("*** tracker timed out"); + } + + void tracker_logger::tracker_request_error(tracker_request const& r + , int response_code, error_code const& ec, const std::string& str + , int retry_interval) + { + debug_log("*** tracker error: %d: %s %s" + , response_code, ec.message().c_str(), str.c_str()); + } + + void tracker_logger::debug_log(const char* fmt, ...) const + { + if (!m_ses.m_logger) return; + + va_list v; + va_start(v, fmt); + + char usr[1024]; + vsnprintf(usr, sizeof(usr), fmt, v); + va_end(v); + char buf[1280]; + snprintf(buf, sizeof(buf), "%s: %s\n", time_now_string(), usr); + (*m_ses.m_logger) << buf; + } +#endif +}} + diff --git a/apps/Launcher/ext/libtorrent/src/settings.cpp b/apps/Launcher/ext/libtorrent/src/settings.cpp new file mode 100644 index 0000000000..7616afc459 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/settings.cpp @@ -0,0 +1,142 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/settings.hpp" +#include "libtorrent/lazy_entry.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/assert.hpp" + +#include + +namespace libtorrent +{ + + void load_struct(lazy_entry const& e, void* s, bencode_map_entry const* m, int num) + { + for (int i = 0; i < num; ++i) + { + lazy_entry const* key = e.dict_find(m[i].name); + if (key == 0) continue; + void* dest = ((char*)s) + m[i].offset; + switch (m[i].type) + { + case std_string: + { + if (key->type() != lazy_entry::string_t) continue; + *((std::string*)dest) = key->string_value(); + break; + } + case character: + case integer16: + case boolean: + case integer: + case size_integer: + case time_integer: + case floating_point: + { + if (key->type() != lazy_entry::int_t) continue; + size_type val = key->int_value(); + switch (m[i].type) + { + case character: *((char*)dest) = char(val); break; + case integer16: *((boost::uint16_t*)dest) = boost::uint16_t(val); break; + case integer: *((int*)dest) = int(val); break; + case size_integer: *((size_type*)dest) = size_type(val); break; + case time_integer: *((time_t*)dest) = time_t(val); break; + case floating_point: *((float*)dest) = float(val) / 1000.f; break; + case boolean: *((bool*)dest) = (val != 0); break; + } + } + } + } + } + + void save_struct(entry& e, void const* s, bencode_map_entry const* m, int num, void const* def) + { + if (e.type() != entry::dictionary_t) e = entry(entry::dictionary_t); + for (int i = 0; i < num; ++i) + { + char const* key = m[i].name; + void const* src = ((char*)s) + m[i].offset; + if (def) + { + // if we have a default value for this field + // and it is the default, don't save it + void const* default_value = ((char*)def) + m[i].offset; + switch (m[i].type) + { + case std_string: + if (*((std::string*)src) == *((std::string*)default_value)) continue; + break; + case character: + if (*((char*)src) == *((char*)default_value)) continue; + break; + case integer: + if (*((int*)src) == *((int*)default_value)) continue; + break; + case integer16: + if (*((boost::uint16_t*)src) == *((boost::uint16_t*)default_value)) continue; + break; + case size_integer: + if (*((size_type*)src) == *((size_type*)default_value)) continue; + break; + case time_integer: + if (*((time_t*)src) == *((time_t*)default_value)) continue; + break; + case floating_point: + if (*((float*)src) == *((float*)default_value)) continue; + break; + case boolean: + if (*((bool*)src) == *((bool*)default_value)) continue; + break; + default: TORRENT_ASSERT(false); + } + } + entry& val = e[key]; + TORRENT_ASSERT_VAL(val.type() == entry::undefined_t, val.type()); + switch (m[i].type) + { + case std_string: val = *((std::string*)src); break; + case character: val = *((char*)src); break; + case integer: val = *((int*)src); break; + case integer16: val = *((boost::uint16_t*)src); break; + case size_integer: val = *((size_type*)src); break; + case time_integer: val = *((time_t*)src); break; + case floating_point: val = size_type(*((float*)src) * 1000.f); break; + case boolean: val = *((bool*)src); break; + default: TORRENT_ASSERT(false); + } + } + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/sha1.cpp b/apps/Launcher/ext/libtorrent/src/sha1.cpp new file mode 100644 index 0000000000..a3e64dfbb0 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/sha1.cpp @@ -0,0 +1,330 @@ +/* +SHA-1 C++ conversion + +original version: + +SHA-1 in C +By Steve Reid +100% Public Domain + +changelog at the end of the file. +*/ + +#include +#include + +// if you don't want boost +// replace with +// #include +// typedef uint32_t u32; +// typedef uint8_t u8; + +#include +typedef boost::uint32_t u32; +typedef boost::uint8_t u8; + +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + +struct TORRENT_EXPORT sha_ctx +{ + u32 state[5]; + u32 count[2]; + u8 buffer[64]; +}; + +// we don't want these to clash with openssl's libcrypto +TORRENT_EXPORT void SHA1_init(sha_ctx* context); +TORRENT_EXPORT void SHA1_update(sha_ctx* context, u8 const* data, u32 len); +TORRENT_EXPORT void SHA1_final(u8* digest, sha_ctx* context); + +namespace +{ + union CHAR64LONG16 + { + u8 c[64]; + u32 l[16]; + }; + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +// blk0() and blk() perform the initial expand. +// I got the idea of expanding during the round function from SSLeay + struct little_endian_blk0 + { + static u32 apply(CHAR64LONG16* block, int i) + { + return block->l[i] = (rol(block->l[i],24)&0xFF00FF00) + | (rol(block->l[i],8)&0x00FF00FF); + } + }; + + struct big_endian_blk0 + { + static u32 apply(CHAR64LONG16* block, int i) + { + return block->l[i]; + } + }; + + +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +// (R0+R1), R2, R3, R4 are the different operations used in SHA1 +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+BlkFun::apply(block, i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + // Hash a single 512-bit block. This is the core of the algorithm. + template + void SHA1transform(u32 state[5], u8 const buffer[64]) + { + using namespace std; + u32 a, b, c, d, e; + + CHAR64LONG16* block; + u8 workspace[64]; + block = (CHAR64LONG16*)workspace; + memcpy(block, buffer, 64); + + // Copy context->state[] to working vars + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + // 4 rounds of 20 operations each. Loop unrolled. + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + // Add the working vars back into context.state[] + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + } + +#ifdef VERBOSE + void SHAPrintContext(sha_ctx *context, char *msg) + { + using namespace std; + printf("%s (%d,%d) %x %x %x %x %x\n" + , msg, (unsigned int)context->count[0] + , (unsigned int)context->count[1] + , (unsigned int)context->state[0] + , (unsigned int)context->state[1] + , (unsigned int)context->state[2] + , (unsigned int)context->state[3] + , (unsigned int)context->state[4]); + } +#endif + + template + void internal_update(sha_ctx* context, u8 const* data, u32 len) + { + using namespace std; + u32 i, j; // JHB + +#ifdef VERBOSE + SHAPrintContext(context, "before"); +#endif + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) + { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) + { + SHA1transform(context->state, &data[i]); + } + j = 0; + } + else + { + i = 0; + } + memcpy(&context->buffer[j], &data[i], len - i); +#ifdef VERBOSE + SHAPrintContext(context, "after "); +#endif + } + +#if !defined BOOST_BIG_ENDIAN && !defined BOOST_LITTLE_ENDIAN + bool is_big_endian() + { + u32 test = 1; + return *reinterpret_cast(&test) == 0; + } +#endif +} + +// SHA1Init - Initialize new context + +void SHA1_init(sha_ctx* context) +{ + // SHA1 initialization constants + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +// Run your data through this. + +void SHA1_update(sha_ctx* context, u8 const* data, u32 len) +{ + // GCC standard defines for endianness + // test with: cpp -dM /dev/null +#if defined BOOST_BIG_ENDIAN + internal_update(context, data, len); +#elif defined BOOST_LITTLE_ENDIAN + internal_update(context, data, len); +#else + // select different functions depending on endianess + // and figure out the endianess runtime + if (is_big_endian()) + internal_update(context, data, len); + else + internal_update(context, data, len); +#endif +} + + +// Add padding and return the message digest. + +void SHA1_final(u8* digest, sha_ctx* context) +{ + u8 finalcount[8]; + + for (u32 i = 0; i < 8; ++i) + { + // Endian independent + finalcount[i] = static_cast( + (context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); + } + + SHA1_update(context, (u8 const*)"\200", 1); + while ((context->count[0] & 504) != 448) + SHA1_update(context, (u8 const*)"\0", 1); + SHA1_update(context, finalcount, 8); // Should cause a SHA1transform() + + for (u32 i = 0; i < 20; ++i) + { + digest[i] = static_cast( + (context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } +} + +} // libtorrent namespace + +/************************************************************ + +----------------- +Modified 7/98 +By James H. Brown +Still 100% Public Domain + +Corrected a problem which generated improper hash values on 16 bit machines +Routine SHA1Update changed from + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int +len) +to + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned +long len) + +The 'len' parameter was declared an int which works fine on 32 bit machines. +However, on 16 bit machines an int is too small for the shifts being done +against +it. This caused the hash function to generate incorrect values if len was +greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). + +Since the file IO in main() reads 16K at a time, any file 8K or larger would +be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million +"a"s). + +I also changed the declaration of variables i & j in SHA1Update to +unsigned long from unsigned int for the same reason. + +These changes should make no difference to any 32 bit implementations since +an +int and a long are the same size in those environments. + +-- +I also corrected a few compiler warnings generated by Borland C. +1. Added #include for exit() prototype +2. Removed unused variable 'j' in SHA1Final +3. Changed exit(0) to return(0) at end of main. + +ALL changes I made can be located by searching for comments containing 'JHB' +----------------- +Modified 8/98 +By Steve Reid +Still 100% public domain + +1- Removed #include and used return() instead of exit() +2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) +3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net + +----------------- +Modified 4/01 +By Saul Kravitz +Still 100% PD +Modified to run on Compaq Alpha hardware. + +----------------- +Converted to C++ 6/04 +By Arvid Norberg +1- made the input buffer const, and made the + previous SHA1HANDSOFF implicit +2- uses C99 types with size guarantees + from boost +3- if none of BOOST_BIG_ENDIAN or BOOST_LITTLE_ENDIAN + are defined, endianess is determined + at runtime. templates are used to duplicate + the transform function for each endianess +4- using anonymous namespace to avoid external + linkage on internal functions +5- using standard C++ includes +6- made API compatible with openssl + +still 100% PD +*/ + +/* +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ diff --git a/apps/Launcher/ext/libtorrent/src/smart_ban.cpp b/apps/Launcher/ext/libtorrent/src/smart_ban.cpp new file mode 100644 index 0000000000..6b47569cfd --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/smart_ban.cpp @@ -0,0 +1,389 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include +#include +#include +#include + +#include "libtorrent/hasher.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/extensions/smart_ban.hpp" +#include "libtorrent/disk_io_thread.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/peer_info.hpp" +#include "libtorrent/random.hpp" + +//#define TORRENT_LOG_HASH_FAILURES + +#ifdef TORRENT_LOG_HASH_FAILURES + +#include "libtorrent/peer_id.hpp" // sha1_hash +#include "libtorrent/escape_string.hpp" // to_hex + +void log_hash_block(FILE** f, libtorrent::torrent const& t, int piece, int block + , libtorrent::address a, char const* bytes, int len, bool corrupt) +{ + using namespace libtorrent; + + mkdir("hash_failures", 0755); + + if (*f == NULL) + { + char filename[1024]; + snprintf(filename, sizeof(filename), "hash_failures/%s.log" + , to_hex(t.info_hash().to_string()).c_str()); + *f = fopen(filename, "w"); + } + + file_storage const& fs = t.torrent_file().files(); + std::vector files = fs.map_block(piece, block * 0x4000, len); + + std::string fn = fs.file_path(fs.internal_at(files[0].file_index)); + + char filename[4094]; + int offset = 0; + for (int i = 0; i < files.size(); ++i) + { + offset += snprintf(filename+offset, sizeof(filename)-offset + , "%s[%"PRId64",%d]", libtorrent::filename(fn).c_str(), files[i].offset, int(files[i].size)); + if (offset >= sizeof(filename)) break; + } + + fprintf(*f, "%s\t%04d\t%04d\t%s\t%s\t%s\n", to_hex(t.info_hash().to_string()).c_str(), piece + , block, corrupt ? " bad" : "good", print_address(a).c_str(), filename); + + snprintf(filename, sizeof(filename), "hash_failures/%s_%d_%d_%s.block" + , to_hex(t.info_hash().to_string()).c_str(), piece, block, corrupt ? "bad" : "good"); + FILE* data = fopen(filename, "w+"); + fwrite(bytes, 1, len, data); + fclose(data); +} + +#endif + + +namespace libtorrent { + + class torrent; + +namespace +{ + + struct smart_ban_plugin : torrent_plugin, boost::enable_shared_from_this + { + smart_ban_plugin(torrent& t) + : m_torrent(t) + , m_salt(random()) + { +#ifdef TORRENT_LOG_HASH_FAILURES + m_log_file = NULL; +#endif + } + +#ifdef TORRENT_LOG_HASH_FAILURES + ~smart_ban_plugin() + { fclose(m_log_file); } +#endif + + void on_piece_pass(int p) + { +#ifdef TORRENT_LOGGING + (*m_torrent.session().m_logger) << time_now_string() << " PIECE PASS [ p: " << p + << " | block_hash_size: " << m_block_hashes.size() << " ]\n"; +#endif + // has this piece failed earlier? If it has, go through the + // CRCs from the time it failed and ban the peers that + // sent bad blocks + std::map::iterator i = m_block_hashes.lower_bound(piece_block(p, 0)); + if (i == m_block_hashes.end() || int(i->first.piece_index) != p) return; + + int size = m_torrent.torrent_file().piece_size(p); + peer_request r = {p, 0, (std::min)(16*1024, size)}; + piece_block pb(p, 0); + while (size > 0) + { + if (i->first.block_index == pb.block_index) + { + m_torrent.filesystem().async_read(r, boost::bind(&smart_ban_plugin::on_read_ok_block + , shared_from_this(), *i, _1, _2)); + m_block_hashes.erase(i++); + } + else + { + TORRENT_ASSERT(i->first.block_index > pb.block_index); + } + + if (i == m_block_hashes.end() || int(i->first.piece_index) != p) + break; + + r.start += 16*1024; + size -= 16*1024; + r.length = (std::min)(16*1024, size); + ++pb.block_index; + } + +#ifndef NDEBUG + // make sure we actually removed all the entries for piece 'p' + i = m_block_hashes.lower_bound(piece_block(p, 0)); + TORRENT_ASSERT(i == m_block_hashes.end() || int(i->first.piece_index) != p); +#endif + + if (m_torrent.is_seed()) + { + std::map().swap(m_block_hashes); + return; + } + } + + void on_piece_failed(int p) + { + // The piece failed the hash check. Record + // the CRC and origin peer of every block + + // if the torrent is aborted, no point in starting + // a bunch of read operations on it + if (m_torrent.is_aborted()) return; + + std::vector downloaders; + m_torrent.picker().get_downloaders(downloaders, p); + + int size = m_torrent.torrent_file().piece_size(p); + peer_request r = {p, 0, (std::min)(16*1024, size)}; + piece_block pb(p, 0); + for (std::vector::iterator i = downloaders.begin() + , end(downloaders.end()); i != end; ++i) + { + if (*i != 0) + { + m_torrent.filesystem().async_read(r, boost::bind(&smart_ban_plugin::on_read_failed_block + , shared_from_this(), pb, ((policy::peer*)*i)->address(), _1, _2)); + } + + r.start += 16*1024; + size -= 16*1024; + r.length = (std::min)(16*1024, size); + ++pb.block_index; + } + TORRENT_ASSERT(size <= 0); + } + + private: + + // this entry ties a specific block CRC to + // a peer. + struct block_entry + { + policy::peer* peer; + sha1_hash digest; + }; + + void on_read_failed_block(piece_block b, address a, int ret, disk_io_job const& j) + { + TORRENT_ASSERT(m_torrent.session().is_network_thread()); + + disk_buffer_holder buffer(m_torrent.session(), j.buffer); + + // ignore read errors + if (ret != j.buffer_size) return; + + hasher h; + h.update(j.buffer, j.buffer_size); + h.update((char const*)&m_salt, sizeof(m_salt)); + + std::pair range + = m_torrent.get_policy().find_peers(a); + + // there is no peer with this address anymore + if (range.first == range.second) return; + + policy::peer* p = (*range.first); + block_entry e = {p, h.final()}; + +#ifdef TORRENT_LOG_HASH_FAILURES + log_hash_block(&m_log_file, m_torrent, b.piece_index + , b.block_index, p->address(), j.buffer, j.buffer_size, true); +#endif + + std::map::iterator i = m_block_hashes.lower_bound(b); + + if (i != m_block_hashes.end() && i->first == b && i->second.peer == p) + { + // this peer has sent us this block before + if (i->second.digest != e.digest) + { + // this time the digest of the block is different + // from the first time it sent it + // at least one of them must be bad + + // verify that this is not a dangling pointer + // if the pointer is in the policy's list, it + // still live, if it's not, it has been removed + // and we can't use this pointer + if (!m_torrent.get_policy().has_peer(p)) return; + +#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + char const* client = "-"; + peer_info info; + if (p->connection) + { + p->connection->get_peer_info(info); + client = info.client.c_str(); + } + (*m_torrent.session().m_logger) << time_now_string() << " BANNING PEER [ p: " << b.piece_index + << " | b: " << b.block_index + << " | c: " << client + << " | hash1: " << i->second.digest + << " | hash2: " << e.digest + << " | ip: " << p->ip() << " ]\n"; +#endif + m_torrent.get_policy().ban_peer(p); + if (p->connection) p->connection->disconnect( + errors::peer_banned); + } + // we already have this exact entry in the map + // we don't have to insert it + return; + } + + m_block_hashes.insert(i, std::pair(b, e)); + +#ifdef TORRENT_LOGGING + char const* client = "-"; + peer_info info; + if (p->connection) + { + p->connection->get_peer_info(info); + client = info.client.c_str(); + } + (*m_torrent.session().m_logger) << time_now_string() << " STORE BLOCK CRC [ p: " << b.piece_index + << " | b: " << b.block_index + << " | c: " << client + << " | digest: " << e.digest + << " | ip: " << p->ip() << " ]\n"; +#endif + } + + void on_read_ok_block(std::pair b, int ret, disk_io_job const& j) + { + TORRENT_ASSERT(m_torrent.session().is_network_thread()); + + disk_buffer_holder buffer(m_torrent.session(), j.buffer); + + // ignore read errors + if (ret != j.buffer_size) return; + + hasher h; + h.update(j.buffer, j.buffer_size); + h.update((char const*)&m_salt, sizeof(m_salt)); + sha1_hash ok_digest = h.final(); + + policy::peer* p = b.second.peer; + + if (b.second.digest == ok_digest) return; + if (p == 0) return; + +#ifdef TORRENT_LOG_HASH_FAILURES + log_hash_block(&m_log_file, m_torrent, b.first.piece_index + , b.first.block_index, p->address(), j.buffer, j.buffer_size, false); +#endif + + if (!m_torrent.get_policy().has_peer(p)) return; + +#ifdef TORRENT_LOGGING + char const* client = "-"; + peer_info info; + if (p->connection) + { + p->connection->get_peer_info(info); + client = info.client.c_str(); + } + (*m_torrent.session().m_logger) << time_now_string() << " BANNING PEER [ p: " << b.first.piece_index + << " | b: " << b.first.block_index + << " | c: " << client + << " | ok_digest: " << ok_digest + << " | bad_digest: " << b.second.digest + << " | ip: " << p->ip() << " ]\n"; +#endif + m_torrent.get_policy().ban_peer(p); + if (p->connection) p->connection->disconnect( + errors::peer_banned); + } + + torrent& m_torrent; + + // This table maps a piece_block (piece and block index + // pair) to a peer and the block CRC. The CRC is calculated + // from the data in the block + the salt + std::map m_block_hashes; + + // This salt is a random value used to calculate the block CRCs + // Since the CRC function that is used is not a one way function + // the salt is required to avoid attacks where bad data is sent + // that is forged to match the CRC of the good data. + int m_salt; + +#ifdef TORRENT_LOG_HASH_FAILURES + FILE* m_log_file; +#endif + }; + +} } + +namespace libtorrent +{ + + boost::shared_ptr create_smart_ban_plugin(torrent* t, void*) + { + return boost::shared_ptr(new smart_ban_plugin(*t)); + } + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/socket_io.cpp b/apps/Launcher/ext/libtorrent/src/socket_io.cpp new file mode 100644 index 0000000000..d17cded5bc --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/socket_io.cpp @@ -0,0 +1,104 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include + +#include "libtorrent/escape_string.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/socket_io.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/io.hpp" // for write_uint16 +#include "libtorrent/hasher.hpp" // for hasher + +namespace libtorrent +{ + + std::string print_address(address const& addr) + { + error_code ec; + return addr.to_string(ec); + } + + std::string address_to_bytes(address const& a) + { + std::string ret; + std::back_insert_iterator out(ret); + detail::write_address(a, out); + return ret; + } + + std::string endpoint_to_bytes(udp::endpoint const& ep) + { + std::string ret; + std::back_insert_iterator out(ret); + detail::write_endpoint(ep, out); + return ret; + } + + std::string print_endpoint(tcp::endpoint const& ep) + { + error_code ec; + char buf[200]; + address const& addr = ep.address(); +#if TORRENT_USE_IPV6 + if (addr.is_v6()) + snprintf(buf, sizeof(buf), "[%s]:%d", addr.to_string(ec).c_str(), ep.port()); + else +#endif + snprintf(buf, sizeof(buf), "%s:%d", addr.to_string(ec).c_str(), ep.port()); + return buf; + } + + std::string print_endpoint(udp::endpoint const& ep) + { + return print_endpoint(tcp::endpoint(ep.address(), ep.port())); + } + + void hash_address(address const& ip, sha1_hash& h) + { +#if TORRENT_USE_IPV6 + if (ip.is_v6()) + { + address_v6::bytes_type b = ip.to_v6().to_bytes(); + h = hasher((char*)&b[0], b.size()).final(); + } + else +#endif + { + address_v4::bytes_type b = ip.to_v4().to_bytes(); + h = hasher((char*)&b[0], b.size()).final(); + } + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/socket_type.cpp b/apps/Launcher/ext/libtorrent/src/socket_type.cpp new file mode 100644 index 0000000000..dab172a9c6 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/socket_type.cpp @@ -0,0 +1,336 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/socket_type.hpp" + +#ifdef TORRENT_USE_OPENSSL +#include + +#if BOOST_VERSION >= 104700 +#include +#endif + +#endif + +namespace libtorrent +{ + + bool is_ssl(socket_type const& s) + { +#ifdef TORRENT_USE_OPENSSL +#define CASE(t) case socket_type_int_impl >::value: + switch (s.type()) + { + CASE(stream_socket) + CASE(socks5_stream) + CASE(http_stream) + CASE(utp_stream) + return true; + default: return false; + }; +#undef CASE +#else + return false; +#endif + } + + bool is_utp(socket_type const& s) + { + return s.get() +#ifdef TORRENT_USE_OPENSSL + || s.get >() +#endif + ; + } + +#if TORRENT_USE_I2P + bool is_i2p(socket_type const& s) + { + return s.get() +#ifdef TORRENT_USE_OPENSSL + || s.get >() +#endif + ; + } +#endif + + void setup_ssl_hostname(socket_type& s, std::string const& hostname, error_code& ec) + { +#if defined TORRENT_USE_OPENSSL && BOOST_VERSION >= 104700 + // for SSL connections, make sure to authenticate the hostname + // of the certificate +#define CASE(t) case socket_type_int_impl >::value: \ + s.get >()->set_verify_callback(asio::ssl::rfc2818_verification(hostname), ec); \ + ctx = SSL_get_SSL_CTX(s.get >()->native_handle()); \ + break; + + SSL_CTX* ctx = 0; + + switch(s.type()) + { + CASE(stream_socket) + CASE(socks5_stream) + CASE(http_stream) + CASE(utp_stream) + } +#undef CASE + +#if OPENSSL_VERSION_NUMBER >= 0x90812f + if (ctx) + { + SSL_CTX_set_tlsext_servername_callback(ctx, 0); + SSL_CTX_set_tlsext_servername_arg(ctx, 0); + } +#endif // OPENSSL_VERSION_NUMBER + +#endif + } + + void on_close_socket(socket_type* s, boost::shared_ptr holder) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("on_close_socket"); +#endif + error_code ec; + s->close(ec); + } + + // the second argument is a shared pointer to an object that + // will keep the socket (s) alive for the duration of the async operation + void async_shutdown(socket_type& s, boost::shared_ptr holder) + { + error_code e; + +#ifdef TORRENT_USE_OPENSSL + // for SSL connections, first do an async_shutdown, before closing the socket +#if defined TORRENT_ASIO_DEBUGGING +#define MAYBE_ASIO_DEBUGGING add_outstanding_async("on_close_socket"); +#else +#define MAYBE_ASIO_DEBUGGING +#endif +#define CASE(t) case socket_type_int_impl >::value: \ + MAYBE_ASIO_DEBUGGING \ + s.get >()->async_shutdown(boost::bind(&on_close_socket, &s, holder)); \ + break; + + switch (s.type()) + { + CASE(stream_socket) + CASE(socks5_stream) + CASE(http_stream) + CASE(utp_stream) + default: s.close(e); break; + } +#undef CASE +#else + s.close(e); +#endif // TORRENT_USE_OPENSSL + } + + void socket_type::destruct() + { + switch (m_type) + { + case 0: break; + case socket_type_int_impl::value: + get()->~stream_socket(); + break; + case socket_type_int_impl::value: + get()->~socks5_stream(); + break; + case socket_type_int_impl::value: + get()->~http_stream(); + break; + case socket_type_int_impl::value: + get()->~utp_stream(); + break; +#if TORRENT_USE_I2P + case socket_type_int_impl::value: + get()->~i2p_stream(); + break; +#endif +#ifdef TORRENT_USE_OPENSSL + case socket_type_int_impl >::value: + get >()->~ssl_stream(); + break; + case socket_type_int_impl >::value: + get >()->~ssl_stream(); + break; + case socket_type_int_impl >::value: + get >()->~ssl_stream(); + break; + case socket_type_int_impl >::value: + get >()->~ssl_stream(); + break; +#endif + default: TORRENT_ASSERT(false); + } + m_type = 0; + } + + void socket_type::construct(int type, void* userdata) + { + destruct(); + switch (type) + { + case 0: break; + case socket_type_int_impl::value: + new ((stream_socket*)m_data) stream_socket(m_io_service); + break; + case socket_type_int_impl::value: + new ((socks5_stream*)m_data) socks5_stream(m_io_service); + break; + case socket_type_int_impl::value: + new ((http_stream*)m_data) http_stream(m_io_service); + break; + case socket_type_int_impl::value: + new ((utp_stream*)m_data) utp_stream(m_io_service); + break; +#if TORRENT_USE_I2P + case socket_type_int_impl::value: + new ((i2p_stream*)m_data) i2p_stream(m_io_service); + break; +#endif +#ifdef TORRENT_USE_OPENSSL + case socket_type_int_impl >::value: + TORRENT_ASSERT(userdata); + new ((ssl_stream*)m_data) ssl_stream(m_io_service + , *((boost::asio::ssl::context*)userdata)); + break; + case socket_type_int_impl >::value: + TORRENT_ASSERT(userdata); + new ((ssl_stream*)m_data) ssl_stream(m_io_service + , *((boost::asio::ssl::context*)userdata)); + break; + case socket_type_int_impl >::value: + TORRENT_ASSERT(userdata); + new ((ssl_stream*)m_data) ssl_stream(m_io_service + , *((boost::asio::ssl::context*)userdata)); + break; + case socket_type_int_impl >::value: + TORRENT_ASSERT(userdata); + new ((ssl_stream*)m_data) ssl_stream(m_io_service + , *((boost::asio::ssl::context*)userdata)); + break; +#endif + default: TORRENT_ASSERT(false); + } + + m_type = type; + } + + char const* socket_type::type_name() const + { + static char const* const names[] = + { + "uninitialized", + "TCP", + "Socks5", + "HTTP", + "uTP", +#if TORRENT_USE_I2P + "I2P", +#else + "", +#endif +#ifdef TORRENT_USE_OPENSSL + "SSL/TCP", + "SSL/Socks5", + "SSL/HTTP", + "SSL/uTP" +#else + "","","","" +#endif + }; + return names[m_type]; + } + + io_service& socket_type::get_io_service() const + { return m_io_service; } + + socket_type::~socket_type() + { destruct(); } + + bool socket_type::is_open() const + { + if (m_type == 0) return false; + TORRENT_SOCKTYPE_FORWARD_RET(is_open(), false) + } + + void socket_type::open(protocol_type const& p, error_code& ec) + { TORRENT_SOCKTYPE_FORWARD(open(p, ec)) } + + void socket_type::close(error_code& ec) + { + if (m_type == 0) return; + TORRENT_SOCKTYPE_FORWARD(close(ec)) + } + + socket_type::endpoint_type socket_type::local_endpoint(error_code& ec) const + { TORRENT_SOCKTYPE_FORWARD_RET(local_endpoint(ec), socket_type::endpoint_type()) } + + socket_type::endpoint_type socket_type::remote_endpoint(error_code& ec) const + { TORRENT_SOCKTYPE_FORWARD_RET(remote_endpoint(ec), socket_type::endpoint_type()) } + + void socket_type::bind(endpoint_type const& endpoint, error_code& ec) + { TORRENT_SOCKTYPE_FORWARD(bind(endpoint, ec)) } + + std::size_t socket_type::available(error_code& ec) const + { TORRENT_SOCKTYPE_FORWARD_RET(available(ec), 0) } + + int socket_type::type() const { return m_type; } + +#ifndef BOOST_NO_EXCEPTIONS + void socket_type::open(protocol_type const& p) + { TORRENT_SOCKTYPE_FORWARD(open(p)) } + + void socket_type::close() + { + if (m_type == 0) return; + TORRENT_SOCKTYPE_FORWARD(close()) + } + + socket_type::endpoint_type socket_type::local_endpoint() const + { TORRENT_SOCKTYPE_FORWARD_RET(local_endpoint(), socket_type::endpoint_type()) } + + socket_type::endpoint_type socket_type::remote_endpoint() const + { TORRENT_SOCKTYPE_FORWARD_RET(remote_endpoint(), socket_type::endpoint_type()) } + + void socket_type::bind(endpoint_type const& endpoint) + { TORRENT_SOCKTYPE_FORWARD(bind(endpoint)) } + + std::size_t socket_type::available() const + { TORRENT_SOCKTYPE_FORWARD_RET(available(), 0) } +#endif + +} + diff --git a/apps/Launcher/ext/libtorrent/src/socks5_stream.cpp b/apps/Launcher/ext/libtorrent/src/socks5_stream.cpp new file mode 100644 index 0000000000..00b3500775 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/socks5_stream.cpp @@ -0,0 +1,600 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/socks5_stream.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/socket_io.hpp" + +namespace libtorrent +{ + + namespace socks_error + { + boost::system::error_code make_error_code(socks_error_code e) + { + return error_code(e, get_socks_category()); + } + } + + struct socks_error_category : boost::system::error_category + { + virtual const char* name() const BOOST_SYSTEM_NOEXCEPT + { return "socks error"; } + virtual std::string message(int ev) const BOOST_SYSTEM_NOEXCEPT + { + static char const* messages[] = + { + "SOCKS no error", + "SOCKS unsupported version", + "SOCKS unsupported authentication method", + "SOCKS unsupported authentication version", + "SOCKS authentication error", + "SOCKS username required", + "SOCKS general failure", + "SOCKS command not supported", + "SOCKS no identd running", + "SOCKS identd could not identify username" + }; + + if (ev < 0 || ev >= socks_error::num_errors) return "unknown error"; + return messages[ev]; + } + virtual boost::system::error_condition default_error_condition( + int ev) const BOOST_SYSTEM_NOEXCEPT + { return boost::system::error_condition(ev, *this); } + }; + + TORRENT_EXPORT boost::system::error_category& get_socks_category() + { + static socks_error_category socks_category; + return socks_category; + } + + void socks5_stream::name_lookup(error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("socks5_stream::name_lookup"); +#endif + if (e || i == tcp::resolver::iterator()) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + error_code ec; + if (!m_sock.is_open()) + { + m_sock.open(i->endpoint().protocol(), ec); + if (ec) + { + (*h)(ec); + close(ec); + return; + } + } + + // TOOD: we could bind the socket here, since we know what the + // target endpoint is of the proxy +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::connected"); +#endif + m_sock.async_connect(i->endpoint(), boost::bind( + &socks5_stream::connected, this, _1, h)); + } + + void socks5_stream::connected(error_code const& e, boost::shared_ptr h) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("socks5_stream::connected"); +#endif + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + using namespace libtorrent::detail; + if (m_version == 5) + { + // send SOCKS5 authentication methods + m_buffer.resize(m_user.empty()?3:4); + char* p = &m_buffer[0]; + write_uint8(5, p); // SOCKS VERSION 5 + if (m_user.empty()) + { + write_uint8(1, p); // 1 authentication method (no auth) + write_uint8(0, p); // no authentication + } + else + { + write_uint8(2, p); // 2 authentication methods + write_uint8(0, p); // no authentication + write_uint8(2, p); // username/password + } +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::handshake1"); +#endif + async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::handshake1, this, _1, h)); + } + else if (m_version == 4) + { + socks_connect(h); + } + else + { + (*h)(socks_error::unsupported_version); + error_code ec; + close(ec); + } + } + + void socks5_stream::handshake1(error_code const& e, boost::shared_ptr h) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("socks5_stream::handshake1"); +#endif + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::handshake2"); +#endif + m_buffer.resize(2); + async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::handshake2, this, _1, h)); + } + + void socks5_stream::handshake2(error_code const& e, boost::shared_ptr h) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("socks5_stream::handshake2"); +#endif + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + using namespace libtorrent::detail; + + char* p = &m_buffer[0]; + int version = read_uint8(p); + int method = read_uint8(p); + + if (version < m_version) + { + (*h)(socks_error::unsupported_version); + error_code ec; + close(ec); + return; + } + + if (method == 0) + { + socks_connect(h); + } + else if (method == 2) + { + if (m_user.empty()) + { + (*h)(socks_error::username_required); + error_code ec; + close(ec); + return; + } + + // start sub-negotiation + m_buffer.resize(m_user.size() + m_password.size() + 3); + char* p = &m_buffer[0]; + write_uint8(1, p); + write_uint8(m_user.size(), p); + write_string(m_user, p); + write_uint8(m_password.size(), p); + write_string(m_password, p); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::handshake3"); +#endif + async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::handshake3, this, _1, h)); + } + else + { + (*h)(socks_error::unsupported_authentication_method); + error_code ec; + close(ec); + return; + } + } + + void socks5_stream::handshake3(error_code const& e + , boost::shared_ptr h) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("socks5_stream::handshake3"); +#endif + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::handshake4"); +#endif + m_buffer.resize(2); + async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::handshake4, this, _1, h)); + } + + void socks5_stream::handshake4(error_code const& e + , boost::shared_ptr h) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("socks5_stream::handshake4"); +#endif + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + using namespace libtorrent::detail; + + char* p = &m_buffer[0]; + int version = read_uint8(p); + int status = read_uint8(p); + + if (version != 1) + { + (*h)(socks_error::unsupported_authentication_version); + error_code ec; + close(ec); + return; + } + + if (status != 0) + { + (*h)(socks_error::authentication_error); + error_code ec; + close(ec); + return; + } + + std::vector().swap(m_buffer); + socks_connect(h); + } + + void socks5_stream::socks_connect(boost::shared_ptr h) + { + using namespace libtorrent::detail; + + if (m_version == 5) + { + // send SOCKS5 connect command + m_buffer.resize(6 + (!m_dst_name.empty() + ?m_dst_name.size() + 1 + :(m_remote_endpoint.address().is_v4()?4:16))); + char* p = &m_buffer[0]; + write_uint8(5, p); // SOCKS VERSION 5 + write_uint8(m_command, p); // CONNECT/BIND command + write_uint8(0, p); // reserved + if (!m_dst_name.empty()) + { + write_uint8(3, p); // address type + TORRENT_ASSERT(m_dst_name.size() <= 255); + write_uint8(m_dst_name.size(), p); + std::copy(m_dst_name.begin(), m_dst_name.end(), p); + p += m_dst_name.size(); + } + else + { + write_uint8(m_remote_endpoint.address().is_v4()?1:4, p); // address type + write_address(m_remote_endpoint.address(), p); + } + write_uint16(m_remote_endpoint.port(), p); + } + else if (m_version == 4) + { + // SOCKS4 only supports IPv4 + if (!m_remote_endpoint.address().is_v4()) + { + (*h)(boost::asio::error::address_family_not_supported); + error_code ec; + close(ec); + return; + } + m_buffer.resize(m_user.size() + 9); + char* p = &m_buffer[0]; + write_uint8(4, p); // SOCKS VERSION 4 + write_uint8(m_command, p); // CONNECT/BIND command + write_uint16(m_remote_endpoint.port(), p); + write_uint32(m_remote_endpoint.address().to_v4().to_ulong(), p); + std::copy(m_user.begin(), m_user.end(), p); + p += m_user.size(); + write_uint8(0, p); // NULL terminator + } + else + { + (*h)(socks_error::unsupported_version); + error_code ec; + close(ec); + return; + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::connect1"); +#endif + async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::connect1, this, _1, h)); + } + + void socks5_stream::connect1(error_code const& e, boost::shared_ptr h) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("socks5_stream::connect1"); +#endif + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + if (m_version == 5) + m_buffer.resize(6 + 4); // assume an IPv4 address + else if (m_version == 4) + m_buffer.resize(8); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::connect2"); +#endif + async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::connect2, this, _1, h)); + } + + void socks5_stream::connect2(error_code const& e, boost::shared_ptr h) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("socks5_stream::connect2"); +#endif + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + using namespace libtorrent::detail; + + // send SOCKS5 connect command + char* p = &m_buffer[0]; + int version = read_uint8(p); + int response = read_uint8(p); + + if (m_version == 5) + { + if (version < m_version) + { + (*h)(socks_error::unsupported_version); + error_code ec; + close(ec); + return; + } + if (response != 0) + { + error_code ec(socks_error::general_failure, get_socks_category()); + switch (response) + { + case 2: ec = asio::error::no_permission; break; + case 3: ec = asio::error::network_unreachable; break; + case 4: ec = asio::error::host_unreachable; break; + case 5: ec = asio::error::connection_refused; break; + case 6: ec = asio::error::timed_out; break; + case 7: ec = socks_error::command_not_supported; break; + case 8: ec = asio::error::address_family_not_supported; break; + } + (*h)(ec); + close(ec); + return; + } + p += 1; // reserved + int atyp = read_uint8(p); + // we ignore the proxy IP it was bound to + if (atyp == 1) + { + if (m_command == 2) + { + if (m_listen == 0) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::connect1"); +#endif + m_listen = 1; + connect1(e, h); + return; + } + m_remote_endpoint.address(read_v4_address(p)); + m_remote_endpoint.port(read_uint16(p)); + std::vector().swap(m_buffer); + (*h)(e); + } + else + { + std::vector().swap(m_buffer); + (*h)(e); + } + return; + } + int extra_bytes = 0; + if (atyp == 4) + { + extra_bytes = 12; + } + else if (atyp == 3) + { + extra_bytes = read_uint8(p) - 3; + } + else + { + (*h)(asio::error::address_family_not_supported); + error_code ec; + close(ec); + return; + } + m_buffer.resize(m_buffer.size() + extra_bytes); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::connect3"); +#endif + TORRENT_ASSERT(extra_bytes > 0); + async_read(m_sock, asio::buffer(&m_buffer[m_buffer.size() - extra_bytes], extra_bytes) + , boost::bind(&socks5_stream::connect3, this, _1, h)); + } + else if (m_version == 4) + { + if (version != 0) + { + (*h)(socks_error::general_failure); + error_code ec; + close(ec); + return; + } + + // access granted + if (response == 90) + { + if (m_command == 2) + { + if (m_listen == 0) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::connect1"); +#endif + m_listen = 1; + connect1(e, h); + return; + } + m_remote_endpoint.address(read_v4_address(p)); + m_remote_endpoint.port(read_uint16(p)); + std::vector().swap(m_buffer); + (*h)(e); + } + else + { + std::vector().swap(m_buffer); + (*h)(e); + } + return; + } + + int code = socks_error::general_failure; + switch (response) + { + case 91: code = socks_error::authentication_error; break; + case 92: code = socks_error::no_identd; break; + case 93: code = socks_error::identd_error; break; + } + error_code ec(code, get_socks_category()); + (*h)(ec); + close(ec); + } + } + + void socks5_stream::connect3(error_code const& e, boost::shared_ptr h) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("socks5_stream::connect3"); +#endif + using namespace libtorrent::detail; + + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + if (m_command == 2) + { + if (m_listen == 0) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::connect1"); +#endif + m_listen = 1; + connect1(e, h); + return; + } + + char* p = &m_buffer[0]; + p += 2; // version and response code + int atyp = read_uint8(p); + TORRENT_ASSERT(atyp == 3 || atyp == 4); + if (atyp == 4) + { + // we don't support resolving the endpoint address + // if we receive a domain name, just set the remote + // endpoint to INADDR_ANY + m_remote_endpoint = tcp::endpoint(); + } + else if (atyp == 3) + { + m_remote_endpoint.address(read_v4_address(p)); + m_remote_endpoint.port(read_uint16(p)); + } + } + std::vector().swap(m_buffer); + (*h)(e); + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/stat.cpp b/apps/Launcher/ext/libtorrent/src/stat.cpp new file mode 100644 index 0000000000..353908928a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/stat.cpp @@ -0,0 +1,50 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include + +#include "libtorrent/stat.hpp" + +namespace libtorrent { + +void stat_channel::second_tick(int tick_interval_ms) +{ + int sample = int(size_type(m_counter) * 1000 / tick_interval_ms); + TORRENT_ASSERT(sample >= 0); + m_5_sec_average = size_type(m_5_sec_average) * 4 / 5 + sample / 5; + m_30_sec_average = size_type(m_30_sec_average) * 29 / 30 + sample / 30; + m_counter = 0; +} + +} + diff --git a/apps/Launcher/ext/libtorrent/src/storage.cpp b/apps/Launcher/ext/libtorrent/src/storage.cpp new file mode 100644 index 0000000000..574a6527bb --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/storage.cpp @@ -0,0 +1,3139 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg, Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#if BOOST_VERSION >= 103500 +#include +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/storage.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/file.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/file_pool.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/disk_buffer_holder.hpp" +#include "libtorrent/alloca.hpp" +#include "libtorrent/allocator.hpp" // page_size + +#include + +//#define TORRENT_PARTIAL_HASH_LOG + +#if defined(__APPLE__) +// for getattrlist() +#include +#include +// for statfs() +#include +#include +#endif + +#if defined(__linux__) +#include +#endif + +#if defined(__FreeBSD__) +// for statfs() +#include +#include +#endif + +// for convert_to_wstring and convert_to_native +#include "libtorrent/escape_string.hpp" + +namespace libtorrent +{ + std::vector > get_filesizes( + file_storage const& storage, std::string const& p) + { + std::string save_path = complete(p); + std::vector > sizes; + for (int i = 0; i < storage.num_files(); ++i) + { + size_type size = 0; + std::time_t time = 0; + + if (!storage.pad_file_at(i)) + { + file_status s; + error_code ec; + stat_file(storage.file_path(i, save_path), &s, ec); + + if (!ec) + { + size = s.file_size; + time = s.mtime; + } + } + sizes.push_back(std::make_pair(size, time)); + } + return sizes; + } + + // matches the sizes and timestamps of the files passed in + // in non-compact mode, actual file sizes and timestamps + // are allowed to be bigger and more recent than the fast + // resume data. This is because full allocation will not move + // pieces, so any older version of the resume data will + // still be a correct subset of the actual data on disk. + enum flags_t + { + compact_mode = 1, + ignore_timestamps = 2 + }; + + bool match_filesizes( + file_storage const& fs + , std::string p + , std::vector > const& sizes + , int flags + , error_code& error) + { + if ((int)sizes.size() != fs.num_files()) + { + error = errors::mismatching_number_of_files; + return false; + } + p = complete(p); + + std::vector >::const_iterator size_iter + = sizes.begin(); + for (int i = 0; i < fs.num_files(); ++i, ++size_iter) + { + size_type size = 0; + std::time_t time = 0; + if (fs.pad_file_at(i)) continue; + + file_status s; + error_code ec; + stat_file(fs.file_path(i, p), &s, ec); + + if (!ec) + { + size = s.file_size; + time = s.mtime; + } + + if (((flags & compact_mode) && size != size_iter->first) + || (!(flags & compact_mode) && size < size_iter->first)) + { + error = errors::mismatching_file_size; + return false; + } + + if (flags & ignore_timestamps) continue; + + // if there is no timestamp in the resume data, ignore it + if (size_iter->second == 0) continue; + + // allow one second 'slack', because of FAT volumes + // in sparse mode, allow the files to be more recent + // than the resume data, but only by 5 minutes + if (((flags & compact_mode) && (time > size_iter->second + 1 || time < size_iter->second - 1)) || + (!(flags & compact_mode) && (time > size_iter->second + 5 * 60 || time < size_iter->second - 1))) + { + error = errors::mismatching_file_timestamp; + return false; + } + } + return true; + } + + void storage_interface::set_error(std::string const& file, error_code const& ec) const + { + m_error_file = file; + m_error = ec; + } + + // for backwards compatibility, let the default readv and + // writev implementations be implemented in terms of the + // old read and write + int storage_interface::readv(file::iovec_t const* bufs + , int slot, int offset, int num_bufs, int flags) + { + int ret = 0; + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + int r = read((char*)i->iov_base, slot, offset, i->iov_len); + offset += i->iov_len; + if (r == -1) return -1; + ret += r; + } + return ret; + } + + int storage_interface::writev(file::iovec_t const* bufs, int slot + , int offset, int num_bufs, int flags) + { + int ret = 0; + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + int r = write((char const*)i->iov_base, slot, offset, i->iov_len); + offset += i->iov_len; + if (r == -1) return -1; + ret += r; + } + return ret; + } + + int copy_bufs(file::iovec_t const* bufs, int bytes, file::iovec_t* target) + { + int size = 0; + int ret = 1; + for (;;) + { + *target = *bufs; + size += bufs->iov_len; + if (size >= bytes) + { + target->iov_len -= size - bytes; + return ret; + } + ++bufs; + ++target; + ++ret; + } + } + + void advance_bufs(file::iovec_t*& bufs, int bytes) + { + int size = 0; + for (;;) + { + size += bufs->iov_len; + if (size >= bytes) + { + ((char*&)bufs->iov_base) += bufs->iov_len - (size - bytes); + bufs->iov_len = size - bytes; + return; + } + ++bufs; + } + } + + int bufs_size(file::iovec_t const* bufs, int num_bufs) + { + int size = 0; + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + size += i->iov_len; + return size; + } + + void clear_bufs(file::iovec_t const* bufs, int num_bufs) + { + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + std::memset(i->iov_base, 0, i->iov_len); + } + +#if TORRENT_USE_ASSERTS + int count_bufs(file::iovec_t const* bufs, int bytes) + { + int size = 0; + int count = 1; + if (bytes == 0) return 0; + for (file::iovec_t const* i = bufs;; ++i, ++count) + { + size += i->iov_len; + TORRENT_ASSERT(size <= bytes); + if (size >= bytes) return count; + } + } +#endif + + int piece_manager::hash_for_slot(int slot, partial_hash& ph, int piece_size + , int small_piece_size, sha1_hash* small_hash) + { + TORRENT_ASSERT_VAL(!error(), error()); + int num_read = 0; + int slot_size = piece_size - ph.offset; + if (slot_size > 0) + { + int block_size = 16 * 1024; + if (m_storage->disk_pool()) block_size = m_storage->disk_pool()->block_size(); + int size = slot_size; + int num_blocks = (size + block_size - 1) / block_size; + + // when we optimize for speed we allocate all the buffers we + // need for the rest of the piece, and read it all in one call + // and then hash it. When optimizing for memory usage, we read + // one block at a time and hash it. This ends up only using a + // single buffer + if (m_storage->settings().optimize_hashing_for_speed) + { + file::iovec_t* bufs = TORRENT_ALLOCA(file::iovec_t, num_blocks); + for (int i = 0; i < num_blocks; ++i) + { + bufs[i].iov_base = m_storage->disk_pool()->allocate_buffer("hash temp"); + bufs[i].iov_len = (std::min)(block_size, size); + size -= bufs[i].iov_len; + } + // deliberately pass in 0 as flags, to disable random_access + num_read = m_storage->readv(bufs, slot, ph.offset, num_blocks, 0); + // TODO: if the read fails, set error and exit immediately + + for (int i = 0; i < num_blocks; ++i) + { + if (small_hash && small_piece_size <= block_size) + { + ph.h.update((char const*)bufs[i].iov_base, small_piece_size); + *small_hash = hasher(ph.h).final(); + small_hash = 0; // avoid this case again + if (int(bufs[i].iov_len) > small_piece_size) + ph.h.update((char const*)bufs[i].iov_base + small_piece_size + , bufs[i].iov_len - small_piece_size); + } + else + { + ph.h.update((char const*)bufs[i].iov_base, bufs[i].iov_len); + small_piece_size -= bufs[i].iov_len; + } + ph.offset += bufs[i].iov_len; + m_storage->disk_pool()->free_buffer((char*)bufs[i].iov_base); + } + } + else + { + file::iovec_t buf; + disk_buffer_holder holder(*m_storage->disk_pool() + , m_storage->disk_pool()->allocate_buffer("hash temp")); + buf.iov_base = holder.get(); + for (int i = 0; i < num_blocks; ++i) + { + buf.iov_len = (std::min)(block_size, size); + // deliberately pass in 0 as flags, to disable random_access + int ret = m_storage->readv(&buf, slot, ph.offset, 1, 0); + if (ret > 0) num_read += ret; + // TODO: if the read fails, set error and exit immediately + + if (small_hash && small_piece_size <= block_size) + { + if (small_piece_size > 0) ph.h.update((char const*)buf.iov_base, small_piece_size); + *small_hash = hasher(ph.h).final(); + small_hash = 0; // avoid this case again + if (int(buf.iov_len) > small_piece_size) + ph.h.update((char const*)buf.iov_base + small_piece_size + , buf.iov_len - small_piece_size); + } + else + { + ph.h.update((char const*)buf.iov_base, buf.iov_len); + small_piece_size -= buf.iov_len; + } + + ph.offset += buf.iov_len; + size -= buf.iov_len; + } + } + if (error()) return 0; + } + return num_read; + } + + default_storage::default_storage(file_storage const& fs, file_storage const* mapped, std::string const& path + , file_pool& fp, std::vector const& file_prio) + : m_files(fs) + , m_file_priority(file_prio) + , m_pool(fp) + , m_page_size(page_size()) + , m_allocate_files(false) + { + if (mapped) m_mapped_files.reset(new file_storage(*mapped)); + + TORRENT_ASSERT(m_files.num_files() > 0); + m_save_path = complete(path); + } + + default_storage::~default_storage() { m_pool.release(this); } + + void default_storage::set_file_priority(std::vector const& prio) + { + m_file_priority = prio; + } + + bool default_storage::initialize(bool allocate_files) + { + m_allocate_files = allocate_files; + +#ifdef TORRENT_WINDOWS + // don't do full file allocations on network drives +#if TORRENT_USE_WSTRING + std::wstring f = convert_to_wstring(m_save_path); + int drive_type = GetDriveTypeW(f.c_str()); +#else + int drive_type = GetDriveTypeA(m_save_path.c_str()); +#endif + + if (drive_type == DRIVE_REMOTE) + m_allocate_files = false; +#endif + + error_code ec; + m_file_created.resize(files().num_files(), false); + + // first, create all missing directories + std::string last_path; + for (int file_index = 0; file_index < files().num_files(); ++file_index) + { + // ignore files that have priority 0 + if (int(m_file_priority.size()) > file_index + && m_file_priority[file_index] == 0) continue; + + // ignore pad files + if (files().pad_file_at(file_index)) continue; + + std::string file_path = files().file_path(file_index, m_save_path); + + file_status s; + stat_file(file_path, &s, ec); + if (ec && ec != boost::system::errc::no_such_file_or_directory + && ec != boost::system::errc::not_a_directory) + { + set_error(file_path, ec); + break; + } + + // if the file already exists, but is larger than what + // it's supposed to be, truncate it + // if the file is empty, just create it either way. + if ((!ec && s.file_size > files().file_size(file_index)) || files().file_size(file_index) == 0) + { + std::string dir = parent_path(file_path); + + if (dir != last_path) + { + last_path = dir; + + create_directories(last_path, ec); + if (ec) + { + set_error(dir, ec); + break; + } + } + ec.clear(); + + boost::intrusive_ptr f = open_file(file_index, file::read_write | file::random_access, ec); + if (ec) set_error(file_path, ec); + else if (f) + { + f->set_size(files().file_size(file_index), ec); + if (ec) set_error(file_path, ec); + } + if (ec) break; + } + ec.clear(); + } + + // close files that were opened in write mode + m_pool.release(this); + + return error() ? true : false; + } + +#ifndef TORRENT_NO_DEPRECATE + void default_storage::finalize_file(int index) {} +#endif + + bool default_storage::has_any_file() + { + for (int i = 0; i < files().num_files(); ++i) + { + error_code ec; + file_status s; + stat_file(files().file_path(i, m_save_path), &s, ec); + if (ec) continue; + if (s.mode & file_status::regular_file && files().file_size(i) > 0) + return true; + } + return false; + } + + bool default_storage::rename_file(int index, std::string const& new_filename) + { + if (index < 0 || index >= files().num_files()) return true; + std::string old_name = files().file_path(index, m_save_path); + m_pool.release(this, index); + + error_code ec; + std::string new_path; + if (is_complete(new_filename)) new_path = new_filename; + else new_path = combine_path(m_save_path, new_filename); + std::string new_dir = parent_path(new_path); + + // create any missing directories that the new filename + // lands in + create_directories(new_dir, ec); + if (ec) + { + set_error(new_dir, ec); + return true; + } + + rename(old_name, new_path, ec); + + // if old_name doesn't exist, that's not an error + // here. Once we start writing to the file, it will + // be written to the new filename + if (ec && ec != boost::system::errc::no_such_file_or_directory) + { + set_error(old_name, ec); + return true; + } + + // if old path doesn't exist, just rename the file + // in our file_storage, so that when it is created + // it will get the new name + if (!m_mapped_files) + { m_mapped_files.reset(new file_storage(m_files)); } + m_mapped_files->rename_file(index, new_filename); + return false; + } + + bool default_storage::release_files() + { + m_pool.release(this); + return false; + } + + void default_storage::delete_one_file(std::string const& p) + { + error_code ec; + remove(p, ec); + + if (ec && ec != boost::system::errc::no_such_file_or_directory) + set_error(p, ec); + } + + bool default_storage::delete_files() + { + // make sure we don't have the files open + m_pool.release(this); + + // delete the files from disk + std::set directories; + typedef std::set::iterator iter_t; + for (int i = 0; i < files().num_files(); ++i) + { + std::string fp = files().file_path(i); + bool complete = is_complete(fp); + std::string p = complete ? fp : combine_path(m_save_path, fp); + if (!complete) + { + std::string bp = parent_path(fp); + std::pair ret; + ret.second = true; + while (ret.second && !bp.empty()) + { + ret = directories.insert(combine_path(m_save_path, bp)); + bp = parent_path(bp); + } + } + delete_one_file(p); + } + + // remove the directories. Reverse order to delete + // subdirectories first + + for (std::set::reverse_iterator i = directories.rbegin() + , end(directories.rend()); i != end; ++i) + { + delete_one_file(*i); + } + + if (error()) return true; + return false; + } + + bool default_storage::write_resume_data(entry& rd) const + { + TORRENT_ASSERT(rd.type() == entry::dictionary_t); + + std::vector > file_sizes + = get_filesizes(files(), m_save_path); + + entry::list_type& fl = rd["file sizes"].list(); + for (std::vector >::iterator i + = file_sizes.begin(), end(file_sizes.end()); i != end; ++i) + { + entry::list_type p; + p.push_back(entry(i->first)); + p.push_back(entry(i->second)); + fl.push_back(entry(p)); + } + + return false; + } + + int default_storage::sparse_end(int slot) const + { + TORRENT_ASSERT(slot >= 0); + TORRENT_ASSERT(slot < files().num_pieces()); + + size_type file_offset = (size_type)slot * files().piece_length(); + int file_index = 0; + + for (;;) + { + if (file_offset < files().file_size(file_index)) + break; + + file_offset -= files().file_size(file_index); + ++file_index; + TORRENT_ASSERT(file_index != files().num_files()); + } + + error_code ec; + boost::intrusive_ptr file_handle = open_file(file_index, file::read_only, ec); + if (!file_handle || ec) return slot; + + size_type data_start = file_handle->sparse_end(file_offset); + return int((data_start + files().piece_length() - 1) / files().piece_length()); + } + + bool default_storage::verify_resume_data(lazy_entry const& rd, error_code& error) + { + // TODO: make this more generic to not just work if files have been + // renamed, but also if they have been merged into a single file for instance + // maybe use the same format as .torrent files and reuse some code from torrent_info + lazy_entry const* mapped_files = rd.dict_find_list("mapped_files"); + if (mapped_files && mapped_files->list_size() == m_files.num_files()) + { + m_mapped_files.reset(new file_storage(m_files)); + for (int i = 0; i < m_files.num_files(); ++i) + { + std::string new_filename = mapped_files->list_string_value_at(i); + if (new_filename.empty()) continue; + m_mapped_files->rename_file(i, new_filename); + } + } + + lazy_entry const* file_priority = rd.dict_find_list("file_priority"); + if (file_priority && file_priority->list_size() + == files().num_files()) + { + m_file_priority.resize(file_priority->list_size()); + for (int i = 0; i < file_priority->list_size(); ++i) + m_file_priority[i] = boost::uint8_t(file_priority->list_int_value_at(i, 1)); + } + + std::vector > file_sizes; + lazy_entry const* file_sizes_ent = rd.dict_find_list("file sizes"); + if (file_sizes_ent == 0) + { + error = errors::missing_file_sizes; + return false; + } + + for (int i = 0; i < file_sizes_ent->list_size(); ++i) + { + lazy_entry const* e = file_sizes_ent->list_at(i); + if (e->type() != lazy_entry::list_t + || e->list_size() != 2 + || e->list_at(0)->type() != lazy_entry::int_t + || e->list_at(1)->type() != lazy_entry::int_t) + continue; + file_sizes.push_back(std::pair( + e->list_int_value_at(0), std::time_t(e->list_int_value_at(1)))); + } + + if (file_sizes.empty()) + { + error = errors::no_files_in_resume_data; + return false; + } + + bool seed = false; + + lazy_entry const* slots = rd.dict_find_list("slots"); + if (slots) + { + if (int(slots->list_size()) == m_files.num_pieces()) + { + seed = true; + for (int i = 0; i < slots->list_size(); ++i) + { + if (slots->list_int_value_at(i, -1) >= 0) continue; + seed = false; + break; + } + } + } + else if (lazy_entry const* pieces = rd.dict_find_string("pieces")) + { + if (int(pieces->string_length()) == m_files.num_pieces()) + { + seed = true; + char const* p = pieces->string_ptr(); + for (int i = 0; i < pieces->string_length(); ++i) + { + if ((p[i] & 1) == 1) continue; + seed = false; + break; + } + } + } + else + { + error = errors::missing_pieces; + return false; + } + + bool full_allocation_mode = false; + if (rd.dict_find_string_value("allocation") != "compact") + full_allocation_mode = true; + + if (seed) + { + if (files().num_files() != (int)file_sizes.size()) + { + error = errors::mismatching_number_of_files; + return false; + } + + std::vector >::iterator + fs = file_sizes.begin(); + // the resume data says we have the entire torrent + // make sure the file sizes are the right ones + for (int i = 0; i < files().num_files(); ++i, ++fs) + { + if (!files().pad_file_at(i) && files().file_size(i) != fs->first) + { + error = errors::mismatching_file_size; + return false; + } + } + } + int flags = (full_allocation_mode ? 0 : compact_mode) + | (settings().ignore_resume_timestamps ? ignore_timestamps : 0); + + return match_filesizes(files(), m_save_path, file_sizes, flags, error); + + } + + // returns true on success + int default_storage::move_storage(std::string const& sp, int flags) + { + int ret = piece_manager::no_error; + std::string save_path = complete(sp); + + // check to see if any of the files exist + error_code ec; + file_storage const& f = files(); + + file_status s; + if (flags == fail_if_exist) + { + stat_file(combine_path(save_path, f.name()), &s, ec); + if (ec != boost::system::errc::no_such_file_or_directory) + { + // the directory exists, check all the files + for (int i = 0; i < f.num_files(); ++i) + { + // files moved out to absolute paths are ignored + if (is_complete(f.file_path(i))) continue; + + std::string new_path = f.file_path(i, save_path); + stat_file(new_path, &s, ec); + if (ec != boost::system::errc::no_such_file_or_directory) + return piece_manager::file_exist; + } + } + } + + // collect all directories in to_move. This is because we + // try to move entire directories by default (instead of + // files independently). + std::set to_move; + for (int i = 0; i < f.num_files(); ++i) + { + // files moved out to absolute paths are not moved + if (is_complete(f.file_path(i))) continue; + + std::string split = split_path(f.file_path(i)); + to_move.insert(to_move.begin(), split); + } + + ec.clear(); + stat_file(save_path, &s, ec); + if (ec == boost::system::errc::no_such_file_or_directory) + { + ec.clear(); + create_directories(save_path, ec); + } + + if (ec) + { + set_error(save_path, ec); + return piece_manager::fatal_disk_error; + } + + m_pool.release(this); + + for (std::set::const_iterator i = to_move.begin() + , end(to_move.end()); i != end; ++i) + { + std::string old_path = combine_path(m_save_path, *i); + std::string new_path = combine_path(save_path, *i); + + rename(old_path, new_path, ec); + if (ec) + { + if (flags == dont_replace && ec == boost::system::errc::file_exists) + { + if (ret == piece_manager::no_error) ret = piece_manager::need_full_check; + continue; + } + + if (ec != boost::system::errc::no_such_file_or_directory) + { + error_code ec; + recursive_copy(old_path, new_path, ec); + if (ec == boost::system::errc::no_such_file_or_directory) + { + // it's a bit weird that rename() would not return + // ENOENT, but the file still wouldn't exist. But, + // in case it does, we're done. + ec.clear(); + break; + } + if (ec) + { + set_error(old_path, ec); + ret = piece_manager::fatal_disk_error; + } + else + { + remove_all(old_path, ec); + } + break; + } + } + } + + if (ret == piece_manager::no_error || ret == piece_manager::need_full_check) + m_save_path = save_path; + + return ret; + } + +#ifdef TORRENT_DEBUG +/* + void default_storage::shuffle() + { + int num_pieces = files().num_pieces(); + + std::vector pieces(num_pieces); + for (std::vector::iterator i = pieces.begin(); + i != pieces.end(); ++i) + { + *i = static_cast(i - pieces.begin()); + } + std::srand((unsigned int)std::time(0)); + std::vector targets(pieces); + std::random_shuffle(pieces.begin(), pieces.end()); + std::random_shuffle(targets.begin(), targets.end()); + + for (int i = 0; i < (std::max)(num_pieces / 50, 1); ++i) + { + const int slot_index = targets[i]; + const int piece_index = pieces[i]; + const int slot_size =static_cast(m_files.piece_size(slot_index)); + std::vector buf(slot_size); + read(&buf[0], piece_index, 0, slot_size); + write(&buf[0], slot_index, 0, slot_size); + } + } +*/ +#endif + +#define TORRENT_ALLOCATE_BLOCKS(bufs, num_blocks, piece_size) \ + int num_blocks = (piece_size + disk_pool()->block_size() - 1) / disk_pool()->block_size(); \ + file::iovec_t* bufs = TORRENT_ALLOCA(file::iovec_t, num_blocks); \ + for (int i = 0, size = piece_size; i < num_blocks; ++i) \ + { \ + bufs[i].iov_base = disk_pool()->allocate_buffer("move temp"); \ + bufs[i].iov_len = (std::min)(disk_pool()->block_size(), size); \ + size -= bufs[i].iov_len; \ + } + +#define TORRENT_FREE_BLOCKS(bufs, num_blocks) \ + for (int i = 0; i < num_blocks; ++i) \ + disk_pool()->free_buffer((char*)bufs[i].iov_base); + +#define TORRENT_SET_SIZE(bufs, size, num_bufs) \ + for (num_bufs = 0; size > 0; size -= disk_pool()->block_size(), ++num_bufs) \ + bufs[num_bufs].iov_len = (std::min)(disk_pool()->block_size(), size) + + + bool default_storage::move_slot(int src_slot, int dst_slot) + { + bool r = true; + int piece_size = m_files.piece_size(dst_slot); + + TORRENT_ALLOCATE_BLOCKS(bufs, num_blocks, piece_size); + + readv(bufs, src_slot, 0, num_blocks); if (error()) goto ret; + writev(bufs, dst_slot, 0, num_blocks); if (error()) goto ret; + + r = false; +ret: + TORRENT_FREE_BLOCKS(bufs, num_blocks) + return r; + } + + bool default_storage::swap_slots(int slot1, int slot2) + { + bool r = true; + + // the size of the target slot is the size of the piece + int piece1_size = m_files.piece_size(slot2); + int piece2_size = m_files.piece_size(slot1); + + TORRENT_ALLOCATE_BLOCKS(bufs1, num_blocks1, piece1_size); + TORRENT_ALLOCATE_BLOCKS(bufs2, num_blocks2, piece2_size); + + readv(bufs1, slot1, 0, num_blocks1); if (error()) goto ret; + readv(bufs2, slot2, 0, num_blocks2); if (error()) goto ret; + writev(bufs1, slot2, 0, num_blocks1); if (error()) goto ret; + writev(bufs2, slot1, 0, num_blocks2); if (error()) goto ret; + + r = false; +ret: + TORRENT_FREE_BLOCKS(bufs1, num_blocks1) + TORRENT_FREE_BLOCKS(bufs2, num_blocks2) + return r; + } + + bool default_storage::swap_slots3(int slot1, int slot2, int slot3) + { + bool r = true; + + // the size of the target slot is the size of the piece + int piece_size = m_files.piece_length(); + int piece1_size = m_files.piece_size(slot2); + int piece2_size = m_files.piece_size(slot3); + int piece3_size = m_files.piece_size(slot1); + + TORRENT_ALLOCATE_BLOCKS(bufs1, num_blocks1, piece_size); + TORRENT_ALLOCATE_BLOCKS(bufs2, num_blocks2, piece_size); + + int tmp1 = 0; + int tmp2 = 0; + TORRENT_SET_SIZE(bufs1, piece1_size, tmp1); + readv(bufs1, slot1, 0, tmp1); if (error()) goto ret; + TORRENT_SET_SIZE(bufs2, piece2_size, tmp2); + readv(bufs2, slot2, 0, tmp2); if (error()) goto ret; + writev(bufs1, slot2, 0, tmp1); if (error()) goto ret; + TORRENT_SET_SIZE(bufs1, piece3_size, tmp1); + readv(bufs1, slot3, 0, tmp1); if (error()) goto ret; + writev(bufs2, slot3, 0, tmp2); if (error()) goto ret; + writev(bufs1, slot1, 0, tmp1); if (error()) goto ret; +ret: + TORRENT_FREE_BLOCKS(bufs1, num_blocks1) + TORRENT_FREE_BLOCKS(bufs2, num_blocks2) + return r; + } + + int default_storage::writev(file::iovec_t const* bufs, int slot, int offset + , int num_bufs, int flags) + { +#ifdef TORRENT_DISK_STATS + disk_buffer_pool* pool = disk_pool(); + if (pool) + { + pool->m_disk_access_log << log_time() << " write " + << physical_offset(slot, offset) << std::endl; + } +#endif + fileop op = { &file::writev, &default_storage::write_unaligned + , m_settings ? settings().disk_io_write_mode : 0, file::read_write | flags }; +#ifdef TORRENT_DISK_STATS + int ret = readwritev(bufs, slot, offset, num_bufs, op); + if (pool) + { + pool->m_disk_access_log << log_time() << " write_end " + << (physical_offset(slot, offset) + ret) << std::endl; + } + return ret; +#else + return readwritev(bufs, slot, offset, num_bufs, op); +#endif + } + + size_type default_storage::physical_offset(int slot, int offset) + { + TORRENT_ASSERT(slot >= 0); + TORRENT_ASSERT(slot < m_files.num_pieces()); + TORRENT_ASSERT(offset >= 0); + + // find the file and file + size_type tor_off = size_type(slot) + * files().piece_length() + offset; + int file_index = files().file_index_at_offset(tor_off); + while (files().pad_file_at(file_index)) + { + ++file_index; + if (file_index == files().num_files()) + return size_type(slot) * files().piece_length() + offset; + // update offset as well, since we're moving it up ahead + tor_off = files().file_offset(file_index); + + } + TORRENT_ASSERT(!files().pad_file_at(file_index)); + + size_type file_offset = tor_off - files().file_offset(file_index); + TORRENT_ASSERT(file_offset >= 0); + + // open the file read only to avoid re-opening + // it in case it's already opened in read-only mode + error_code ec; + boost::intrusive_ptr f = open_file(file_index, file::read_only | file::random_access, ec); + + size_type ret = 0; + if (f && !ec) ret = f->phys_offset(file_offset); + + if (ret == 0) + { + // this means we don't support true physical offset + // just make something up + return size_type(slot) * files().piece_length() + offset; + } + return ret; + } + + void default_storage::hint_read(int slot, int offset, int size) + { + size_type start = slot * (size_type)m_files.piece_length() + offset; + TORRENT_ASSERT(start + size <= m_files.total_size()); + + int file_index = files().file_index_at_offset(start); + TORRENT_ASSERT(start >= files().file_offset(file_index)); + TORRENT_ASSERT(start < files().file_offset(file_index) + files().file_size(file_index)); + size_type file_offset = start - files().file_offset(file_index); + + boost::intrusive_ptr file_handle; + int bytes_left = size; + int slot_size = static_cast(m_files.piece_size(slot)); + + if (offset + bytes_left > slot_size) + bytes_left = slot_size - offset; + + TORRENT_ASSERT(bytes_left >= 0); + + int file_bytes_left; + for (;bytes_left > 0; ++file_index, bytes_left -= file_bytes_left) + { + TORRENT_ASSERT(file_index < files().num_files()); + + file_bytes_left = bytes_left; + if (file_offset + file_bytes_left > files().file_size(file_index)) + file_bytes_left = (std::max)(static_cast(files().file_size(file_index) - file_offset), 0); + + if (file_bytes_left == 0) continue; + + if (files().pad_file_at(file_index)) continue; + + error_code ec; + file_handle = open_file(file_index, file::read_only | file::random_access, ec); + + // failing to hint that we want to read is not a big deal + // just swollow the error and keep going + if (!file_handle || ec) continue; + + file_handle->hint_read(file_offset, file_bytes_left); + file_offset = 0; + } + } + + int default_storage::readv(file::iovec_t const* bufs, int slot, int offset + , int num_bufs, int flags) + { +#ifdef TORRENT_DISK_STATS + disk_buffer_pool* pool = disk_pool(); + if (pool) + { + pool->m_disk_access_log << log_time() << " read " + << physical_offset(slot, offset) << std::endl; + } +#endif + fileop op = { &file::readv, &default_storage::read_unaligned + , m_settings ? settings().disk_io_read_mode : 0, file::read_only | flags }; +#ifdef TORRENT_SIMULATE_SLOW_READ + boost::thread::sleep(boost::get_system_time() + + boost::posix_time::milliseconds(1000)); +#endif +#ifdef TORRENT_DISK_STATS + int ret = readwritev(bufs, slot, offset, num_bufs, op); + if (pool) + { + pool->m_disk_access_log << log_time() << " read_end " + << (physical_offset(slot, offset) + ret) << std::endl; + } + return ret; +#else + return readwritev(bufs, slot, offset, num_bufs, op); +#endif + } + + // much of what needs to be done when reading and writing + // is buffer management and piece to file mapping. Most + // of that is the same for reading and writing. This function + // is a template, and the fileop decides what to do with the + // file and the buffers. + int default_storage::readwritev(file::iovec_t const* bufs, int slot, int offset + , int num_bufs, fileop const& op) + { + TORRENT_ASSERT(bufs != 0); + TORRENT_ASSERT(slot >= 0); + TORRENT_ASSERT(slot < m_files.num_pieces()); + TORRENT_ASSERT(offset >= 0); + TORRENT_ASSERT(offset < m_files.piece_size(slot)); + TORRENT_ASSERT(num_bufs > 0); + + int size = bufs_size(bufs, num_bufs); + TORRENT_ASSERT(size > 0); + +#if TORRENT_USE_ASSERTS + std::vector slices + = files().map_block(slot, offset, size); + TORRENT_ASSERT(!slices.empty()); +#endif + + size_type start = slot * (size_type)m_files.piece_length() + offset; + TORRENT_ASSERT(start + size <= m_files.total_size()); + + // find the file iterator and file offset + int file_index = files().file_index_at_offset(start); + TORRENT_ASSERT(start >= files().file_offset(file_index)); + TORRENT_ASSERT(start < files().file_offset(file_index) + files().file_size(file_index)); + size_type file_offset = start - files().file_offset(file_index); + + int buf_pos = 0; + error_code ec; + + boost::intrusive_ptr file_handle; + int bytes_left = size; + int slot_size = static_cast(m_files.piece_size(slot)); + + if (offset + bytes_left > slot_size) + bytes_left = slot_size - offset; + + TORRENT_ASSERT(bytes_left >= 0); + +#if TORRENT_USE_ASSERTS + int counter = 0; +#endif + + file::iovec_t* tmp_bufs = TORRENT_ALLOCA(file::iovec_t, num_bufs); + file::iovec_t* current_buf = TORRENT_ALLOCA(file::iovec_t, num_bufs); + copy_bufs(bufs, size, current_buf); + TORRENT_ASSERT(count_bufs(current_buf, size) == num_bufs); + int file_bytes_left; + for (;bytes_left > 0; ++file_index, bytes_left -= file_bytes_left + , buf_pos += file_bytes_left) + { + TORRENT_ASSERT(file_index < files().num_files()); + TORRENT_ASSERT(buf_pos >= 0); + + file_bytes_left = bytes_left; + if (file_offset + file_bytes_left > files().file_size(file_index)) + file_bytes_left = (std::max)(static_cast(files().file_size(file_index) - file_offset), 0); + + if (file_bytes_left == 0) continue; + +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(int(slices.size()) > counter); + size_type slice_size = slices[counter].size; + TORRENT_ASSERT(slice_size == file_bytes_left); + TORRENT_ASSERT(slices[counter].file_index == file_index); + ++counter; +#endif + + if (files().pad_file_at(file_index)) + { + if ((op.mode & file::rw_mask) == file::read_only) + { + int num_tmp_bufs = copy_bufs(current_buf, file_bytes_left, tmp_bufs); + TORRENT_ASSERT(count_bufs(tmp_bufs, file_bytes_left) == num_tmp_bufs); + TORRENT_ASSERT(num_tmp_bufs <= num_bufs); + clear_bufs(tmp_bufs, num_tmp_bufs); + } + advance_bufs(current_buf, file_bytes_left); + TORRENT_ASSERT(count_bufs(current_buf, bytes_left - file_bytes_left) <= num_bufs); + file_offset = 0; + continue; + } + + error_code ec; + file_handle = open_file(file_index, op.mode, ec); + if (((op.mode & file::rw_mask) != file::read_only) + && ec == boost::system::errc::no_such_file_or_directory) + { + // this means the directory the file is in doesn't exist. + // so create it + ec.clear(); + std::string path = files().file_path(file_index, m_save_path); + create_directories(parent_path(path), ec); + // if the directory creation failed, don't try to open the file again + // but actually just fail + if (!ec) file_handle = open_file(file_index, op.mode, ec); + } + + if (!file_handle || ec) + { + std::string path = files().file_path(file_index, m_save_path); + TORRENT_ASSERT(ec); + set_error(path, ec); + return -1; + } + + // if the file has priority 0, don't allocate it + if (m_allocate_files && (op.mode & file::rw_mask) != file::read_only + && (int(m_file_priority.size()) <= file_index || m_file_priority[file_index] > 0)) + { + TORRENT_ASSERT(int(m_file_created.size()) == files().num_files()); + if (m_file_created[file_index] == false) + { + file_handle->set_size(files().file_size(file_index), ec); + m_file_created.set_bit(file_index); + if (ec) + { + set_error(files().file_path(file_index, m_save_path), ec); + return -1; + } + } + } + + int num_tmp_bufs = copy_bufs(current_buf, file_bytes_left, tmp_bufs); + TORRENT_ASSERT(count_bufs(tmp_bufs, file_bytes_left) == num_tmp_bufs); + TORRENT_ASSERT(num_tmp_bufs <= num_bufs); + int bytes_transferred = 0; + // if the file is opened in no_buffer mode, and the + // read is unaligned, we need to fall back on a slow + // special read that reads aligned buffers and copies + // it into the one supplied + size_type adjusted_offset = files().file_base(file_index) + file_offset; + if ((file_handle->open_mode() & file::no_buffer) + && ((adjusted_offset & (file_handle->pos_alignment()-1)) != 0 + || (uintptr_t(tmp_bufs->iov_base) & (file_handle->buf_alignment()-1)) != 0)) + { + bytes_transferred = (int)(this->*op.unaligned_op)(file_handle, adjusted_offset + , tmp_bufs, num_tmp_bufs, ec); + if ((op.mode & file::rw_mask) != file::read_only + && adjusted_offset + bytes_transferred >= files().file_size(file_index) + && (file_handle->pos_alignment() > 0 || file_handle->size_alignment() > 0)) + { + // we were writing, and we just wrote the last block of the file + // we likely wrote a bit too much, since we're restricted to + // a specific alignment for writes. Make sure to truncate the size + + // TODO: 0 what if file_base is used to merge several virtual files + // into a single physical file? We should probably disable this + // if file_base is used. This is not a widely used feature though + file_handle->set_size(files().file_size(file_index), ec); + } + } + else + { + bytes_transferred = (int)((*file_handle).*op.regular_op)(adjusted_offset + , tmp_bufs, num_tmp_bufs, ec); + TORRENT_ASSERT(bytes_transferred <= bufs_size(tmp_bufs, num_tmp_bufs)); + } + file_offset = 0; + + if (ec) + { + set_error(files().file_path(file_index, m_save_path), ec); + return -1; + } + + if (file_bytes_left != bytes_transferred) + return bytes_transferred; + + advance_bufs(current_buf, bytes_transferred); + TORRENT_ASSERT(count_bufs(current_buf, bytes_left - file_bytes_left) <= num_bufs); + } + return size; + } + + // these functions are inefficient, but should be fairly uncommon. The read + // case happens if unaligned files are opened in no_buffer mode or if clients + // makes unaligned requests (and the disk cache is disabled or fully utilized + // for write cache). + + // they read an unaligned buffer from a file that requires aligned access + + size_type default_storage::read_unaligned(boost::intrusive_ptr const& file_handle + , size_type file_offset, file::iovec_t const* bufs, int num_bufs, error_code& ec) + { + const int pos_align = file_handle->pos_alignment()-1; + const int size_align = file_handle->size_alignment()-1; + + const int size = bufs_size(bufs, num_bufs); + const int start_adjust = file_offset & pos_align; + TORRENT_ASSERT(start_adjust == (file_offset % file_handle->pos_alignment())); + const size_type aligned_start = file_offset - start_adjust; + const int aligned_size = ((size+start_adjust) & size_align) + ? ((size+start_adjust) & ~size_align) + size_align + 1 : size + start_adjust; + TORRENT_ASSERT((aligned_size & size_align) == 0); + + // allocate a temporary, aligned, buffer + aligned_holder aligned_buf(aligned_size); + file::iovec_t b = {aligned_buf.get(), size_t(aligned_size) }; + size_type ret = file_handle->readv(aligned_start, &b, 1, ec); + if (ret < 0) + { + TORRENT_ASSERT(ec); + return ret; + } + if (ret - start_adjust < size) return (std::max)(ret - start_adjust, size_type(0)); + + char* read_buf = aligned_buf.get() + start_adjust; + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i != end; ++i) + { + memcpy(i->iov_base, read_buf, i->iov_len); + read_buf += i->iov_len; + } + + return size; + } + + // this is the really expensive one. To write unaligned, we need to read + // an aligned block, overlay the unaligned buffer, and then write it back + size_type default_storage::write_unaligned(boost::intrusive_ptr const& file_handle + , size_type file_offset, file::iovec_t const* bufs, int num_bufs, error_code& ec) + { + const int pos_align = file_handle->pos_alignment()-1; + const int size_align = file_handle->size_alignment()-1; + + const int size = bufs_size(bufs, num_bufs); + const int start_adjust = file_offset & pos_align; + TORRENT_ASSERT(start_adjust == (file_offset % file_handle->pos_alignment())); + const size_type aligned_start = file_offset - start_adjust; + const int aligned_size = ((size+start_adjust) & size_align) + ? ((size+start_adjust) & ~size_align) + size_align + 1 : size + start_adjust; + TORRENT_ASSERT((aligned_size & size_align) == 0); + + size_type actual_file_size = file_handle->get_size(ec); + if (ec && ec != make_error_code(boost::system::errc::no_such_file_or_directory)) return -1; + ec.clear(); + + // allocate a temporary, aligned, buffer + aligned_holder aligned_buf(aligned_size); + file::iovec_t b = {aligned_buf.get(), size_t(aligned_size) }; + // we have something to read + if (aligned_start < actual_file_size && !ec) + { + size_type ret = file_handle->readv(aligned_start, &b, 1, ec); + if (ec +#ifdef TORRENT_WINDOWS + && ec != error_code(ERROR_HANDLE_EOF, get_system_category()) +#endif + ) + return ret; + } + + ec.clear(); + + // OK, we read the portion of the file. Now, overlay the buffer we're writing + + char* write_buf = aligned_buf.get() + start_adjust; + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i != end; ++i) + { + memcpy(write_buf, i->iov_base, i->iov_len); + write_buf += i->iov_len; + } + + // write the buffer back to disk + size_type ret = file_handle->writev(aligned_start, &b, 1, ec); + + if (ret < 0) + { + TORRENT_ASSERT(ec); + return ret; + } + if (ret - start_adjust < size) return (std::max)(ret - start_adjust, size_type(0)); + return size; + } + + int default_storage::write( + const char* buf + , int slot + , int offset + , int size) + { + file::iovec_t b = { (file::iovec_base_t)buf, size_t(size) }; + return writev(&b, slot, offset, 1, 0); + } + + int default_storage::read( + char* buf + , int slot + , int offset + , int size) + { + file::iovec_t b = { (file::iovec_base_t)buf, size_t(size) }; + return readv(&b, slot, offset, 1); + } + + boost::intrusive_ptr default_storage::open_file(int file_index, int mode + , error_code& ec) const + { + int cache_setting = m_settings ? settings().disk_io_write_mode : 0; + if (cache_setting == session_settings::disable_os_cache + || (cache_setting == session_settings::disable_os_cache_for_aligned_files + && ((files().file_offset(file_index) + files().file_base(file_index)) & (m_page_size-1)) == 0)) + mode |= file::no_buffer; + bool lock_files = m_settings ? settings().lock_files : false; + if (lock_files) mode |= file::lock_file; + if (!m_allocate_files) mode |= file::sparse; + + // files with priority 0 should always be sparse + if (int(m_file_priority.size()) > file_index && m_file_priority[file_index] == 0) + mode |= file::sparse; + + if (m_settings && settings().no_atime_storage) mode |= file::no_atime; + + return m_pool.open_file(const_cast(this), m_save_path, file_index, files(), mode, ec); + } + + storage_interface* default_storage_constructor(file_storage const& fs + , file_storage const* mapped, std::string const& path, file_pool& fp + , std::vector const& file_prio) + { + return new default_storage(fs, mapped, path, fp, file_prio); + } + + int disabled_storage::readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs, int flags) + { +#ifdef TORRENT_DISK_STATS + disk_buffer_pool* pool = disk_pool(); + if (pool) + { + pool->m_disk_access_log << log_time() << " read " + << physical_offset(slot, offset) << std::endl; + } +#endif + int ret = 0; + for (int i = 0; i < num_bufs; ++i) + ret += bufs[i].iov_len; +#ifdef TORRENT_DISK_STATS + if (pool) + { + pool->m_disk_access_log << log_time() << " read_end " + << (physical_offset(slot, offset) + ret) << std::endl; + } +#endif + return ret; + } + + int disabled_storage::writev(file::iovec_t const* bufs, int slot, int offset, int num_bufs, int flags) + { +#ifdef TORRENT_DISK_STATS + disk_buffer_pool* pool = disk_pool(); + if (pool) + { + pool->m_disk_access_log << log_time() << " write " + << physical_offset(slot, offset) << std::endl; + } +#endif + int ret = 0; + for (int i = 0; i < num_bufs; ++i) + ret += bufs[i].iov_len; +#ifdef TORRENT_DISK_STATS + if (pool) + { + pool->m_disk_access_log << log_time() << " write_end " + << (physical_offset(slot, offset) + ret) << std::endl; + } +#endif + return ret; + } + + storage_interface* disabled_storage_constructor(file_storage const& fs + , file_storage const* mapped, std::string const& path, file_pool& fp + , std::vector const&) + { + return new disabled_storage(fs.piece_length()); + } + + // -- piece_manager ----------------------------------------------------- + + piece_manager::piece_manager( + boost::shared_ptr const& torrent + , boost::intrusive_ptr info + , std::string const& save_path + , file_pool& fp + , disk_io_thread& io + , storage_constructor_type sc + , storage_mode_t sm + , std::vector const& file_prio) + : m_info(info) + , m_files(m_info->files()) + , m_storage(sc(m_info->orig_files(), &m_info->files() != &m_info->orig_files() + ? &m_info->files() : 0, save_path, fp, file_prio)) + , m_storage_mode(sm) + , m_save_path(complete(save_path)) + , m_state(state_none) + , m_current_slot(0) + , m_out_of_place(false) + , m_scratch_piece(-1) + , m_last_piece(-1) + , m_storage_constructor(sc) + , m_io_thread(io) + , m_torrent(torrent) + { + m_storage->m_disk_pool = &m_io_thread; + } + + piece_manager::~piece_manager() + { + } + + void piece_manager::async_set_file_priority( + std::vector const& prios + , boost::function const& handler) + { + std::vector* p = new std::vector(prios); + + disk_io_job j; + j.storage = this; + j.buffer = (char*)p; + j.action = disk_io_job::file_priority; + m_io_thread.add_job(j, handler); + } + + void piece_manager::async_save_resume_data( + boost::function const& handler) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::save_resume_data; + m_io_thread.add_job(j, handler); + } + + void piece_manager::async_clear_read_cache( + boost::function const& handler) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::clear_read_cache; + m_io_thread.add_job(j, handler); + } + + void piece_manager::async_release_files( + boost::function const& handler) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::release_files; + m_io_thread.add_job(j, handler); + } + + void piece_manager::abort_disk_io() + { + m_io_thread.stop(this); + } + + void piece_manager::async_delete_files( + boost::function const& handler) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::delete_files; + m_io_thread.add_job(j, handler); + } + + void piece_manager::async_move_storage(std::string const& p, int flags + , boost::function const& handler) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::move_storage; + j.str = p; + j.piece = flags; + m_io_thread.add_job(j, handler); + } + + void piece_manager::async_check_fastresume(lazy_entry const* resume_data + , boost::function const& handler) + { + TORRENT_ASSERT(resume_data != 0); + disk_io_job j; + j.storage = this; + j.action = disk_io_job::check_fastresume; + j.buffer = (char*)resume_data; + m_io_thread.add_job(j, handler); + } + + void piece_manager::async_rename_file(int index, std::string const& name + , boost::function const& handler) + { + disk_io_job j; + j.storage = this; + j.piece = index; + j.str = name; + j.action = disk_io_job::rename_file; + m_io_thread.add_job(j, handler); + } + + void piece_manager::async_check_files( + boost::function const& handler) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::check_files; + m_io_thread.add_job(j, handler); + } + + void piece_manager::async_read_and_hash( + peer_request const& r + , boost::function const& handler + , int cache_expiry) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::read_and_hash; + j.piece = r.piece; + j.offset = r.start; + j.buffer_size = r.length; + j.buffer = 0; + j.cache_min_time = cache_expiry; + TORRENT_ASSERT(r.length <= 16 * 1024); + m_io_thread.add_job(j, handler); +#ifdef TORRENT_USE_ASSERTS + mutex::scoped_lock l(m_mutex); + // if this assert is hit, it suggests + // that check_files was not successful + TORRENT_ASSERT(slot_for(r.piece) >= 0); +#endif + } + + void piece_manager::async_cache(int piece + , boost::function const& handler + , int cache_expiry) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::cache_piece; + j.piece = piece; + j.offset = 0; + j.buffer_size = 0; + j.buffer = 0; + j.cache_min_time = cache_expiry; + m_io_thread.add_job(j, handler); + } + + void piece_manager::async_read( + peer_request const& r + , boost::function const& handler + , int cache_line_size + , int cache_expiry) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::read; + j.piece = r.piece; + j.offset = r.start; + j.buffer_size = r.length; + j.buffer = 0; + j.max_cache_line = cache_line_size; + j.cache_min_time = cache_expiry; + + // if a buffer is not specified, only one block can be read + // since that is the size of the pool allocator's buffers + TORRENT_ASSERT(r.length <= 16 * 1024); + m_io_thread.add_job(j, handler); +#ifdef TORRENT_USE_ASSERTS + mutex::scoped_lock l(m_mutex); + // if this assert is hit, it suggests + // that check_files was not successful + TORRENT_ASSERT(slot_for(r.piece) >= 0); +#endif + } + + int piece_manager::async_write( + peer_request const& r + , disk_buffer_holder& buffer + , boost::function const& handler) + { + TORRENT_ASSERT(r.length <= 16 * 1024); + // the buffer needs to be allocated through the io_thread + TORRENT_ASSERT(m_io_thread.is_disk_buffer(buffer.get())); + + disk_io_job j; + j.storage = this; + j.action = disk_io_job::write; + j.piece = r.piece; + j.offset = r.start; + j.buffer_size = r.length; + j.buffer = buffer.get(); + int queue_size = m_io_thread.add_job(j, handler); + buffer.release(); + + return queue_size; + } + + void piece_manager::async_hash(int piece + , boost::function const& handler) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::hash; + j.piece = piece; + + m_io_thread.add_job(j, handler); + } + + std::string piece_manager::save_path() const + { + mutex::scoped_lock l(m_mutex); + return m_save_path; + } + + sha1_hash piece_manager::hash_for_piece_impl(int piece, int* readback) + { + TORRENT_ASSERT(!m_storage->error()); + + partial_hash ph; + + std::map::iterator i = m_piece_hasher.find(piece); + if (i != m_piece_hasher.end()) + { + ph = i->second; + m_piece_hasher.erase(i); + } + + int slot = slot_for(piece); + TORRENT_ASSERT(slot != has_no_slot); + if (slot < 0) return sha1_hash(0); + int read = hash_for_slot(slot, ph, m_files.piece_size(piece)); + if (readback) *readback = read; + if (m_storage->error()) return sha1_hash(0); + return ph.h.final(); + } + + int piece_manager::move_storage_impl(std::string const& save_path, int flags) + { + int ret = m_storage->move_storage(save_path, flags); + + if (ret == no_error || ret == need_full_check) + { + m_save_path = complete(save_path); + } + return ret; + } + + void piece_manager::write_resume_data(entry& rd) const + { + mutex::scoped_lock lock(m_mutex); + + INVARIANT_CHECK; + + m_storage->write_resume_data(rd); + + if (m_storage_mode == internal_storage_mode_compact_deprecated) + { + entry::list_type& slots = rd["slots"].list(); + slots.clear(); + std::vector::const_reverse_iterator last; + for (last = m_slot_to_piece.rbegin(); + last != m_slot_to_piece.rend(); ++last) + { + if (*last != unallocated) break; + } + + for (std::vector::const_iterator i = + m_slot_to_piece.begin(); + i != last.base(); ++i) + { + slots.push_back((*i >= 0) ? *i : unassigned); + } + } + + rd["allocation"] = m_storage_mode == storage_mode_sparse?"sparse" + :m_storage_mode == storage_mode_allocate?"full":"compact"; + } + + void piece_manager::mark_failed(int piece_index) + { + mutex::scoped_lock lock(m_mutex); + + INVARIANT_CHECK; + + if (m_storage_mode != internal_storage_mode_compact_deprecated) return; + + TORRENT_ASSERT(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size()); + int slot_index = m_piece_to_slot[piece_index]; + TORRENT_ASSERT(slot_index >= 0); + + m_slot_to_piece[slot_index] = unassigned; + m_piece_to_slot[piece_index] = has_no_slot; + m_free_slots.push_back(slot_index); + } + + void piece_manager::hint_read_impl(int piece_index, int offset, int size) + { + m_last_piece = piece_index; + int slot = slot_for(piece_index); + if (slot <= 0) return; + m_storage->hint_read(slot, offset, size); + } + + int piece_manager::read_impl( + file::iovec_t* bufs + , int piece_index + , int offset + , int num_bufs) + { + TORRENT_ASSERT(bufs); + TORRENT_ASSERT(offset >= 0); + TORRENT_ASSERT(num_bufs > 0); + m_last_piece = piece_index; + int slot = slot_for(piece_index); + TORRENT_ASSERT(slot >= 0); + if (slot < 0) return 0; + return m_storage->readv(bufs, slot, offset, num_bufs); + } + + int piece_manager::write_impl( + file::iovec_t* bufs + , int piece_index + , int offset + , int num_bufs) + { + TORRENT_ASSERT(bufs); + TORRENT_ASSERT(offset >= 0); + TORRENT_ASSERT(num_bufs > 0); + TORRENT_ASSERT(piece_index >= 0 && piece_index < m_files.num_pieces()); + + int size = bufs_size(bufs, num_bufs); + + file::iovec_t* iov = TORRENT_ALLOCA(file::iovec_t, num_bufs); + std::copy(bufs, bufs + num_bufs, iov); + m_last_piece = piece_index; + int slot = allocate_slot_for_piece(piece_index); + int ret = m_storage->writev(bufs, slot, offset, num_bufs); + // only save the partial hash if the write succeeds + if (ret != size) return ret; + + if (m_storage->settings().disable_hash_checks) return ret; + + if (offset == 0) + { + partial_hash& ph = m_piece_hasher[piece_index]; + TORRENT_ASSERT(ph.offset == 0); + ph.offset = size; + + for (file::iovec_t* i = iov, *end(iov + num_bufs); i < end; ++i) + ph.h.update((char const*)i->iov_base, i->iov_len); + + } + else + { + std::map::iterator i = m_piece_hasher.find(piece_index); + if (i != m_piece_hasher.end()) + { +#ifdef TORRENT_USE_ASSERTS + TORRENT_ASSERT(i->second.offset > 0); + int hash_offset = i->second.offset; + TORRENT_ASSERT(offset >= hash_offset); +#endif + if (offset == i->second.offset) + { +#ifdef TORRENT_PARTIAL_HASH_LOG + out << time_now_string() << " UPDATING [" + " s: " << this + << " p: " << piece_index + << " off: " << offset + << " size: " << size + << " entries: " << m_piece_hasher.size() + << " ]" << std::endl; +#endif + for (file::iovec_t* b = iov, *end(iov + num_bufs); b < end; ++b) + { + i->second.h.update((char const*)b->iov_base, b->iov_len); + i->second.offset += b->iov_len; + } + } +#ifdef TORRENT_PARTIAL_HASH_LOG + else + { + out << time_now_string() << " SKIPPING (out of order) [" + " s: " << this + << " p: " << piece_index + << " off: " << offset + << " size: " << size + << " entries: " << m_piece_hasher.size() + << " ]" << std::endl; + } +#endif + } +#ifdef TORRENT_PARTIAL_HASH_LOG + else + { + out << time_now_string() << " SKIPPING (no entry) [" + " s: " << this + << " p: " << piece_index + << " off: " << offset + << " size: " << size + << " entries: " << m_piece_hasher.size() + << " ]" << std::endl; + } +#endif + } + + return ret; + } + + size_type piece_manager::physical_offset( + int piece_index + , int offset) + { + TORRENT_ASSERT(offset >= 0); + TORRENT_ASSERT(piece_index >= 0 && piece_index < m_files.num_pieces()); + + int slot = slot_for(piece_index); + // we may not have a slot for this piece yet. + // assume there is no re-mapping of slots + if (slot < 0) slot = piece_index; + return m_storage->physical_offset(slot, offset); + } + + int piece_manager::identify_data( + sha1_hash const& large_hash + , sha1_hash const& small_hash + , int current_slot) + { +// INVARIANT_CHECK; + typedef std::multimap::const_iterator map_iter; + map_iter begin1; + map_iter end1; + map_iter begin2; + map_iter end2; + + // makes the lookups for the small digest and the large digest + boost::tie(begin1, end1) = m_hash_to_piece.equal_range(small_hash); + boost::tie(begin2, end2) = m_hash_to_piece.equal_range(large_hash); + + // copy all potential piece indices into this vector + std::vector matching_pieces; + for (map_iter i = begin1; i != end1; ++i) + matching_pieces.push_back(i->second); + for (map_iter i = begin2; i != end2; ++i) + matching_pieces.push_back(i->second); + + // no piece matched the data in the slot + if (matching_pieces.empty()) + return unassigned; + + // ------------------------------------------ + // CHECK IF THE PIECE IS IN ITS CORRECT PLACE + // ------------------------------------------ + + if (std::find( + matching_pieces.begin() + , matching_pieces.end() + , current_slot) != matching_pieces.end()) + { + // the current slot is among the matching pieces, so + // we will assume that the piece is in the right place + const int piece_index = current_slot; + + int other_slot = m_piece_to_slot[piece_index]; + if (other_slot >= 0) + { + // we have already found a piece with + // this index. + + // take one of the other matching pieces + // that hasn't already been assigned + int other_piece = -1; + for (std::vector::iterator i = matching_pieces.begin(); + i != matching_pieces.end(); ++i) + { + if (m_piece_to_slot[*i] >= 0 || *i == piece_index) continue; + other_piece = *i; + break; + } + if (other_piece >= 0) + { + // replace the old slot with 'other_piece' + m_slot_to_piece[other_slot] = other_piece; + m_piece_to_slot[other_piece] = other_slot; + } + else + { + // this index is the only piece with this + // hash. The previous slot we found with + // this hash must be the same piece. Mark + // that piece as unassigned, since this slot + // is the correct place for the piece. + m_slot_to_piece[other_slot] = unassigned; + if (m_storage_mode == internal_storage_mode_compact_deprecated) + m_free_slots.push_back(other_slot); + } + TORRENT_ASSERT(m_piece_to_slot[piece_index] != current_slot); + TORRENT_ASSERT(m_piece_to_slot[piece_index] >= 0); + m_piece_to_slot[piece_index] = has_no_slot; + } + + TORRENT_ASSERT(m_piece_to_slot[piece_index] == has_no_slot); + + return piece_index; + } + + // find a matching piece that hasn't + // already been assigned + int free_piece = unassigned; + for (std::vector::iterator i = matching_pieces.begin(); + i != matching_pieces.end(); ++i) + { + if (m_piece_to_slot[*i] >= 0) continue; + free_piece = *i; + break; + } + + if (free_piece >= 0) + { + TORRENT_ASSERT(m_piece_to_slot[free_piece] == has_no_slot); + return free_piece; + } + else + { + TORRENT_ASSERT(free_piece == unassigned); + return unassigned; + } + } + + int piece_manager::check_no_fastresume(error_code& error) + { + bool has_files = false; + if (!m_storage->settings().no_recheck_incomplete_resume) + { + has_files = m_storage->has_any_file(); + if (m_storage->error()) + return fatal_disk_error; + + if (has_files) + { + m_state = state_full_check; + m_piece_to_slot.clear(); + m_piece_to_slot.resize(m_files.num_pieces(), has_no_slot); + m_slot_to_piece.clear(); + m_slot_to_piece.resize(m_files.num_pieces(), unallocated); + if (m_storage_mode == internal_storage_mode_compact_deprecated) + { + m_unallocated_slots.clear(); + m_free_slots.clear(); + } + TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); + return need_full_check; + } + } + + if (m_storage_mode == internal_storage_mode_compact_deprecated) + { + // in compact mode without checking, we need to + // populate the unallocated list + TORRENT_ASSERT(m_unallocated_slots.empty()); + for (int i = 0, end(m_files.num_pieces()); i < end; ++i) + m_unallocated_slots.push_back(i); + m_piece_to_slot.clear(); + m_piece_to_slot.resize(m_files.num_pieces(), has_no_slot); + m_slot_to_piece.clear(); + m_slot_to_piece.resize(m_files.num_pieces(), unallocated); + } + + return check_init_storage(error); + } + + int piece_manager::check_init_storage(error_code& error) + { + if (m_storage->initialize(m_storage_mode == storage_mode_allocate)) + { + error = m_storage->error(); + TORRENT_ASSERT(error); + m_current_slot = 0; + return fatal_disk_error; + } + m_state = state_finished; + m_scratch_buffer.reset(); + m_scratch_buffer2.reset(); + if (m_storage_mode != internal_storage_mode_compact_deprecated) + { + // if no piece is out of place + // since we're in full allocation mode, we can + // forget the piece allocation tables + std::vector().swap(m_piece_to_slot); + std::vector().swap(m_slot_to_piece); + std::vector().swap(m_free_slots); + std::vector().swap(m_unallocated_slots); + } + return no_error; + } + + // check if the fastresume data is up to date + // if it is, use it and return true. If it + // isn't return false and the full check + // will be run + int piece_manager::check_fastresume( + lazy_entry const& rd, error_code& error) + { + mutex::scoped_lock lock(m_mutex); + + INVARIANT_CHECK; + + TORRENT_ASSERT(m_files.piece_length() > 0); + + m_current_slot = 0; + + // if we don't have any resume data, return + if (rd.type() == lazy_entry::none_t) return check_no_fastresume(error); + + if (rd.type() != lazy_entry::dict_t) + { + error = errors::not_a_dictionary; + return check_no_fastresume(error); + } + + int block_size = (std::min)(16 * 1024, m_files.piece_length()); + int blocks_per_piece = int(rd.dict_find_int_value("blocks per piece", -1)); + if (blocks_per_piece != -1 + && blocks_per_piece != m_files.piece_length() / block_size) + { + error = errors::invalid_blocks_per_piece; + return check_no_fastresume(error); + } + + storage_mode_t storage_mode = internal_storage_mode_compact_deprecated; + if (rd.dict_find_string_value("allocation") != "compact") + storage_mode = storage_mode_sparse; + + if (!m_storage->verify_resume_data(rd, error)) + return check_no_fastresume(error); + + // assume no piece is out of place (i.e. in a slot + // other than the one it should be in) + bool out_of_place = false; + + // if we don't have a piece map, we need the slots + // if we're in compact mode, we also need the slots map + if (storage_mode == internal_storage_mode_compact_deprecated || rd.dict_find("pieces") == 0) + { + // read slots map + lazy_entry const* slots = rd.dict_find_list("slots"); + if (slots == 0) + { + error = errors::missing_slots; + return check_no_fastresume(error); + } + + if ((int)slots->list_size() > m_files.num_pieces()) + { + error = errors::too_many_slots; + return check_no_fastresume(error); + } + + if (m_storage_mode == internal_storage_mode_compact_deprecated) + { + int num_pieces = int(m_files.num_pieces()); + m_slot_to_piece.resize(num_pieces, unallocated); + m_piece_to_slot.resize(num_pieces, has_no_slot); + for (int i = 0; i < slots->list_size(); ++i) + { + lazy_entry const* e = slots->list_at(i); + if (e->type() != lazy_entry::int_t) + { + error = errors::invalid_slot_list; + return check_no_fastresume(error); + } + + int index = int(e->int_value()); + if (index >= num_pieces || index < -2) + { + error = errors::invalid_piece_index; + return check_no_fastresume(error); + } + if (index >= 0) + { + m_slot_to_piece[i] = index; + m_piece_to_slot[index] = i; + if (i != index) out_of_place = true; + } + else if (index == unassigned) + { + if (m_storage_mode == internal_storage_mode_compact_deprecated) + m_free_slots.push_back(i); + } + else + { + TORRENT_ASSERT(index == unallocated); + if (m_storage_mode == internal_storage_mode_compact_deprecated) + m_unallocated_slots.push_back(i); + } + } + } + else + { + for (int i = 0; i < slots->list_size(); ++i) + { + lazy_entry const* e = slots->list_at(i); + if (e->type() != lazy_entry::int_t) + { + error = errors::invalid_slot_list; + return check_no_fastresume(error); + } + + int index = int(e->int_value()); + if (index != i && index >= 0) + { + error = errors::invalid_piece_index; + return check_no_fastresume(error); + } + } + } + + // This will corrupt the storage + // use while debugging to find + // states that cannot be scanned + // by check_pieces. + // m_storage->shuffle(); + + if (m_storage_mode == internal_storage_mode_compact_deprecated) + { + if (m_unallocated_slots.empty()) switch_to_full_mode(); + } + else + { + TORRENT_ASSERT(m_free_slots.empty()); + TORRENT_ASSERT(m_unallocated_slots.empty()); + + if (out_of_place) + { + // in this case we're in full allocation mode, but + // we're resuming a compact allocated storage + m_state = state_expand_pieces; + m_current_slot = 0; + error = errors::pieces_need_reorder; + TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); + return need_full_check; + } + } + + } + else if (m_storage_mode == internal_storage_mode_compact_deprecated) + { + // read piece map + lazy_entry const* pieces = rd.dict_find("pieces"); + if (pieces == 0 || pieces->type() != lazy_entry::string_t) + { + error = errors::missing_pieces; + return check_no_fastresume(error); + } + + if ((int)pieces->string_length() != m_files.num_pieces()) + { + error = errors::too_many_slots; + return check_no_fastresume(error); + } + + int num_pieces = int(m_files.num_pieces()); + m_slot_to_piece.resize(num_pieces, unallocated); + m_piece_to_slot.resize(num_pieces, has_no_slot); + char const* have_pieces = pieces->string_ptr(); + for (int i = 0; i < num_pieces; ++i) + { + if (have_pieces[i] & 1) + { + m_slot_to_piece[i] = i; + m_piece_to_slot[i] = i; + } + else + { + m_free_slots.push_back(i); + } + } + if (m_unallocated_slots.empty()) switch_to_full_mode(); + } + + return check_init_storage(error); + } + +/* + state chart: + + check_fastresume() ----------+ + | + | | | + | v v + | +------------+ +---------------+ + | | full_check |-->| expand_pieses | + | +------------+ +---------------+ + | | | + | v | + | +--------------+ | + +->| finished | <------+ + +--------------+ +*/ + + + // performs the full check and full allocation + // (if necessary). returns true if finished and + // false if it should be called again + // the second return value is the progress the + // file check is at. 0 is nothing done, and 1 + // is finished + int piece_manager::check_files(int& current_slot, int& have_piece, error_code& error) + { + if (m_state == state_none) return check_no_fastresume(error); + + if (m_piece_to_slot.empty()) + { + m_piece_to_slot.clear(); + m_piece_to_slot.resize(m_files.num_pieces(), has_no_slot); + } + if (m_slot_to_piece.empty()) + { + m_slot_to_piece.clear(); + m_slot_to_piece.resize(m_files.num_pieces(), unallocated); + } + + current_slot = m_current_slot; + have_piece = -1; + if (m_state == state_expand_pieces) + { + INVARIANT_CHECK; + + if (m_scratch_piece >= 0) + { + int piece = m_scratch_piece; + int other_piece = m_slot_to_piece[piece]; + m_scratch_piece = -1; + + if (other_piece >= 0) + { + if (!m_scratch_buffer2.get()) + m_scratch_buffer2.reset(page_aligned_allocator::malloc(m_files.piece_length())); + + int piece_size = m_files.piece_size(other_piece); + file::iovec_t b = {m_scratch_buffer2.get(), size_t(piece_size) }; + if (m_storage->readv(&b, piece, 0, 1) != piece_size) + { + error = m_storage->error(); + TORRENT_ASSERT(error); + return fatal_disk_error; + } + m_scratch_piece = other_piece; + m_piece_to_slot[other_piece] = unassigned; + } + + // the slot where this piece belongs is + // free. Just move the piece there. + int piece_size = m_files.piece_size(piece); + file::iovec_t b = {m_scratch_buffer.get(), size_t(piece_size) }; + if (m_storage->writev(&b, piece, 0, 1) != piece_size) + { + error = m_storage->error(); + TORRENT_ASSERT(error); + return fatal_disk_error; + } + m_piece_to_slot[piece] = piece; + m_slot_to_piece[piece] = piece; + + if (other_piece >= 0) m_scratch_buffer.swap(m_scratch_buffer2); + + TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); + return need_full_check; + } + + while (m_current_slot < m_files.num_pieces() + && (m_slot_to_piece[m_current_slot] == m_current_slot + || m_slot_to_piece[m_current_slot] < 0)) + { + ++m_current_slot; + } + + if (m_current_slot == m_files.num_pieces()) + { + return check_init_storage(error); + } + + TORRENT_ASSERT(m_current_slot < m_files.num_pieces()); + + int piece = m_slot_to_piece[m_current_slot]; + TORRENT_ASSERT(piece >= 0); + int other_piece = m_slot_to_piece[piece]; + if (other_piece >= 0) + { + // there is another piece in the slot + // where this one goes. Store it in the scratch + // buffer until next iteration. + if (!m_scratch_buffer.get()) + m_scratch_buffer.reset(page_aligned_allocator::malloc(m_files.piece_length())); + + int piece_size = m_files.piece_size(other_piece); + file::iovec_t b = {m_scratch_buffer.get(), size_t(piece_size) }; + if (m_storage->readv(&b, piece, 0, 1) != piece_size) + { + error = m_storage->error(); + TORRENT_ASSERT(error); + return fatal_disk_error; + } + m_scratch_piece = other_piece; + m_piece_to_slot[other_piece] = unassigned; + } + + // the slot where this piece belongs is + // free. Just move the piece there. + m_last_piece = piece; + m_storage->move_slot(m_current_slot, piece); + if (m_storage->error()) return -1; + + m_piece_to_slot[piece] = piece; + m_slot_to_piece[m_current_slot] = unassigned; + m_slot_to_piece[piece] = piece; + + TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); + return need_full_check; + } + + TORRENT_ASSERT(m_state == state_full_check); + if (m_state == state_finished) return 0; + + int skip = check_one_piece(have_piece); + TORRENT_ASSERT(m_current_slot <= m_files.num_pieces()); + + if (skip == -1) + { + error = m_storage->error(); + TORRENT_ASSERT(error); + return fatal_disk_error; + } + + if (skip > 0) + { + clear_error(); + // skip means that the piece we checked failed to be read from disk + // completely. This may be caused by the file not being there, or the + // piece overlapping with a sparse region. We should skip 'skip' number + // of pieces + + if (m_storage_mode == internal_storage_mode_compact_deprecated) + { + for (int i = m_current_slot; i < m_current_slot + skip - 1; ++i) + { + TORRENT_ASSERT(m_slot_to_piece[i] == unallocated); + m_unallocated_slots.push_back(i); + } + } + + // current slot will increase by one below + m_current_slot += skip - 1; + TORRENT_ASSERT(m_current_slot <= m_files.num_pieces()); + } + + ++m_current_slot; + current_slot = m_current_slot; + + if (m_current_slot >= m_files.num_pieces()) + { + TORRENT_ASSERT(m_current_slot == m_files.num_pieces()); + + // clear the memory we've been using + std::multimap().swap(m_hash_to_piece); + + if (m_storage_mode != internal_storage_mode_compact_deprecated) + { + if (!m_out_of_place) + { + // if no piece is out of place + // since we're in full allocation mode, we can + // forget the piece allocation tables + + std::vector().swap(m_piece_to_slot); + std::vector().swap(m_slot_to_piece); + return check_init_storage(error); + } + else + { + // in this case we're in full allocation mode, but + // we're resuming a compact allocated storage + m_state = state_expand_pieces; + m_current_slot = 0; + current_slot = m_current_slot; + TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); + return need_full_check; + } + } + else if (m_unallocated_slots.empty()) + { + switch_to_full_mode(); + } + return check_init_storage(error); + } + + TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); + return need_full_check; + } + + int piece_manager::skip_file() const + { + size_type file_offset = 0; + size_type current_offset = size_type(m_current_slot) * m_files.piece_length(); + for (int i = 0; i < m_files.num_files(); ++i) + { + file_offset += m_files.file_size(i); + if (file_offset > current_offset) break; + } + + TORRENT_ASSERT(file_offset > current_offset); + int ret = static_cast( + (file_offset - current_offset + m_files.piece_length() - 1) + / m_files.piece_length()); + TORRENT_ASSERT(ret >= 1); + return ret; + } + + // -1 = error, 0 = ok, >0 = skip this many pieces + int piece_manager::check_one_piece(int& have_piece) + { + // ------------------------ + // DO THE FULL CHECK + // ------------------------ + + TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); + TORRENT_ASSERT(int(m_slot_to_piece.size()) == m_files.num_pieces()); + TORRENT_ASSERT(have_piece == -1); + + // initialization for the full check + if (m_hash_to_piece.empty()) + { + for (int i = 0; i < m_files.num_pieces(); ++i) + m_hash_to_piece.insert(std::pair(m_info->hash_for_piece(i), i)); + } + + partial_hash ph; + int num_read = 0; + int piece_size = m_files.piece_size(m_current_slot); + int small_piece_size = m_files.piece_size(m_files.num_pieces() - 1); + bool read_short = true; + sha1_hash small_hash; + if (piece_size == small_piece_size) + { + num_read = hash_for_slot(m_current_slot, ph, piece_size, 0, 0); + } + else + { + num_read = hash_for_slot(m_current_slot, ph, piece_size + , small_piece_size, &small_hash); + } + read_short = num_read != piece_size; + + if (read_short) + { + if (m_storage->error() +#ifdef TORRENT_WINDOWS + && m_storage->error() != error_code(ERROR_PATH_NOT_FOUND, get_system_category()) + && m_storage->error() != error_code(ERROR_FILE_NOT_FOUND, get_system_category()) + && m_storage->error() != error_code(ERROR_HANDLE_EOF, get_system_category()) + && m_storage->error() != error_code(ERROR_INVALID_HANDLE, get_system_category())) +#else + && m_storage->error() != error_code(ENOENT, get_posix_category())) +#endif + { + return -1; + } + // if the file is incomplete, skip the rest of it + return skip_file(); + } + + sha1_hash large_hash = ph.h.final(); + int piece_index = identify_data(large_hash, small_hash, m_current_slot); + + if (piece_index >= 0) have_piece = piece_index; + + if (piece_index != m_current_slot + && piece_index >= 0) + m_out_of_place = true; + + TORRENT_ASSERT(piece_index == unassigned || piece_index >= 0); + + const bool this_should_move = piece_index >= 0 && m_slot_to_piece[piece_index] != unallocated; + const bool other_should_move = m_piece_to_slot[m_current_slot] != has_no_slot; + + // check if this piece should be swapped with any other slot + // this section will ensure that the storage is correctly sorted + // libtorrent will never leave the storage in a state that + // requires this sorting, but other clients may. + + // example of worst case: + // | m_current_slot = 5 + // V + // +---+- - - +---+- - - +---+- - + // | x | | 5 | | 3 | <- piece data in slots + // +---+- - - +---+- - - +---+- - + // 3 y 5 <- slot index + + // in this example, the data in the m_current_slot (5) + // is piece 3. It has to be moved into slot 3. The data + // in slot y (piece 5) should be moved into the m_current_slot. + // and the data in slot 3 (piece x) should be moved to slot y. + + // there are three possible cases. + // 1. There's another piece that should be placed into this slot + // 2. This piece should be placed into another slot. + // 3. There's another piece that should be placed into this slot + // and this piece should be placed into another slot + + // swap piece_index with this slot + + // case 1 + if (this_should_move && !other_should_move) + { + TORRENT_ASSERT(piece_index != m_current_slot); + + const int other_slot = piece_index; + TORRENT_ASSERT(other_slot >= 0); + int other_piece = m_slot_to_piece[other_slot]; + + m_slot_to_piece[other_slot] = piece_index; + m_slot_to_piece[m_current_slot] = other_piece; + m_piece_to_slot[piece_index] = piece_index; + if (other_piece >= 0) m_piece_to_slot[other_piece] = m_current_slot; + + if (other_piece == unassigned) + { + std::vector::iterator i = + std::find(m_free_slots.begin(), m_free_slots.end(), other_slot); + TORRENT_ASSERT(i != m_free_slots.end()); + if (m_storage_mode == internal_storage_mode_compact_deprecated) + { + m_free_slots.erase(i); + m_free_slots.push_back(m_current_slot); + } + } + + bool ret = false; + m_last_piece = piece_index; + if (other_piece >= 0) + ret |= m_storage->swap_slots(other_slot, m_current_slot); + else + ret |= m_storage->move_slot(m_current_slot, other_slot); + + if (ret) return skip_file(); + + TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned + || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); + } + // case 2 + else if (!this_should_move && other_should_move) + { + TORRENT_ASSERT(piece_index != m_current_slot); + + const int other_piece = m_current_slot; + const int other_slot = m_piece_to_slot[other_piece]; + TORRENT_ASSERT(other_slot >= 0); + + m_slot_to_piece[m_current_slot] = other_piece; + m_slot_to_piece[other_slot] = piece_index; + m_piece_to_slot[other_piece] = m_current_slot; + + if (piece_index == unassigned + && m_storage_mode == internal_storage_mode_compact_deprecated) + m_free_slots.push_back(other_slot); + + bool ret = false; + if (piece_index >= 0) + { + m_piece_to_slot[piece_index] = other_slot; + ret |= m_storage->swap_slots(other_slot, m_current_slot); + } + else + { + ret |= m_storage->move_slot(other_slot, m_current_slot); + + } + m_last_piece = other_piece; + if (ret) return skip_file(); + + TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned + || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); + } + else if (this_should_move && other_should_move) + { + TORRENT_ASSERT(piece_index != m_current_slot); + TORRENT_ASSERT(piece_index >= 0); + + const int piece1 = m_slot_to_piece[piece_index]; + const int piece2 = m_current_slot; + const int slot1 = piece_index; + const int slot2 = m_piece_to_slot[piece2]; + + TORRENT_ASSERT(slot1 >= 0); + TORRENT_ASSERT(slot2 >= 0); + TORRENT_ASSERT(piece2 >= 0); + + if (slot1 == slot2) + { + // this means there are only two pieces involved in the swap + TORRENT_ASSERT(piece1 >= 0); + + // movement diagram: + // +-------------------------------+ + // | | + // +--> slot1 --> m_current_slot --+ + + m_slot_to_piece[slot1] = piece_index; + m_slot_to_piece[m_current_slot] = piece1; + + m_piece_to_slot[piece_index] = slot1; + m_piece_to_slot[piece1] = m_current_slot; + + TORRENT_ASSERT(piece1 == m_current_slot); + TORRENT_ASSERT(piece_index == slot1); + + m_last_piece = piece_index; + m_storage->swap_slots(m_current_slot, slot1); + + TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned + || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); + } + else + { + TORRENT_ASSERT(slot1 != slot2); + TORRENT_ASSERT(piece1 != piece2); + + // movement diagram: + // +-----------------------------------------+ + // | | + // +--> slot1 --> slot2 --> m_current_slot --+ + + m_slot_to_piece[slot1] = piece_index; + m_slot_to_piece[slot2] = piece1; + m_slot_to_piece[m_current_slot] = piece2; + + m_piece_to_slot[piece_index] = slot1; + m_piece_to_slot[m_current_slot] = piece2; + + if (piece1 == unassigned) + { + std::vector::iterator i = + std::find(m_free_slots.begin(), m_free_slots.end(), slot1); + TORRENT_ASSERT(i != m_free_slots.end()); + if (m_storage_mode == internal_storage_mode_compact_deprecated) + { + m_free_slots.erase(i); + m_free_slots.push_back(slot2); + } + } + + bool ret = false; + if (piece1 >= 0) + { + m_piece_to_slot[piece1] = slot2; + ret |= m_storage->swap_slots3(m_current_slot, slot1, slot2); + } + else + { + ret |= m_storage->move_slot(m_current_slot, slot1); + ret |= m_storage->move_slot(slot2, m_current_slot); + } + + m_last_piece = piece_index; + if (ret) return skip_file(); + + TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned + || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); + } + } + else + { + TORRENT_ASSERT(m_piece_to_slot[m_current_slot] == has_no_slot || piece_index != m_current_slot); + TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unallocated); + TORRENT_ASSERT(piece_index == unassigned || m_piece_to_slot[piece_index] == has_no_slot); + + // the slot was identified as piece 'piece_index' + if (piece_index != unassigned) + m_piece_to_slot[piece_index] = m_current_slot; + else if (m_storage_mode == internal_storage_mode_compact_deprecated) + m_free_slots.push_back(m_current_slot); + + m_slot_to_piece[m_current_slot] = piece_index; + + TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned + || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); + } + + if (piece_index == unassigned) + { + // the data did not match any piece. Maybe we're reading + // from a sparse region, see if we are and skip + if (m_current_slot == m_files.num_pieces() -1) return 0; + + int next_slot = m_storage->sparse_end(m_current_slot + 1); + if (next_slot > m_current_slot + 1) return next_slot - m_current_slot; + } + + return 0; + } + + void piece_manager::switch_to_full_mode() + { + TORRENT_ASSERT(m_storage_mode == internal_storage_mode_compact_deprecated); + TORRENT_ASSERT(m_unallocated_slots.empty()); + // we have allocated all slots, switch to + // full allocation mode in order to free + // some unnecessary memory. + m_storage_mode = storage_mode_sparse; + std::vector().swap(m_unallocated_slots); + std::vector().swap(m_free_slots); + std::vector().swap(m_piece_to_slot); + std::vector().swap(m_slot_to_piece); + } + + int piece_manager::allocate_slot_for_piece(int piece_index) + { + mutex::scoped_lock lock(m_mutex); + + if (m_storage_mode != internal_storage_mode_compact_deprecated) return piece_index; + +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + INVARIANT_CHECK; +#endif + + TORRENT_ASSERT(piece_index >= 0); + TORRENT_ASSERT(piece_index < (int)m_piece_to_slot.size()); + TORRENT_ASSERT(m_piece_to_slot.size() == m_slot_to_piece.size()); + + int slot_index = m_piece_to_slot[piece_index]; + + if (slot_index != has_no_slot) + { + TORRENT_ASSERT(slot_index >= 0); + TORRENT_ASSERT(slot_index < (int)m_slot_to_piece.size()); + return slot_index; + } + + if (m_free_slots.empty()) + { + allocate_slots_impl(1, lock); + TORRENT_ASSERT(!m_free_slots.empty()); + } + + std::vector::iterator iter( + std::find( + m_free_slots.begin() + , m_free_slots.end() + , piece_index)); + + if (iter == m_free_slots.end()) + { + TORRENT_ASSERT(m_slot_to_piece[piece_index] != unassigned); + TORRENT_ASSERT(!m_free_slots.empty()); + iter = m_free_slots.end() - 1; + + // special case to make sure we don't use the last slot + // when we shouldn't, since it's smaller than ordinary slots + if (*iter == m_files.num_pieces() - 1 && piece_index != *iter) + { + if (m_free_slots.size() == 1) + allocate_slots_impl(1, lock); + TORRENT_ASSERT(m_free_slots.size() > 1); + // assumes that all allocated slots + // are put at the end of the free_slots vector + iter = m_free_slots.end() - 1; + } + } + + slot_index = *iter; + m_free_slots.erase(iter); + + TORRENT_ASSERT(m_slot_to_piece[slot_index] == unassigned); + + m_slot_to_piece[slot_index] = piece_index; + m_piece_to_slot[piece_index] = slot_index; + + // there is another piece already assigned to + // the slot we are interested in, swap positions + if (slot_index != piece_index + && m_slot_to_piece[piece_index] >= 0) + { + int piece_at_our_slot = m_slot_to_piece[piece_index]; + TORRENT_ASSERT(m_piece_to_slot[piece_at_our_slot] == piece_index); + + std::swap( + m_slot_to_piece[piece_index] + , m_slot_to_piece[slot_index]); + + std::swap( + m_piece_to_slot[piece_index] + , m_piece_to_slot[piece_at_our_slot]); + + m_last_piece = piece_index; + m_storage->move_slot(piece_index, slot_index); + + TORRENT_ASSERT(m_slot_to_piece[piece_index] == piece_index); + TORRENT_ASSERT(m_piece_to_slot[piece_index] == piece_index); + + slot_index = piece_index; + +#if defined TORRENT_DEBUG && defined TORRENT_STORAGE_DEBUG + debug_log(); +#endif + } + TORRENT_ASSERT(slot_index >= 0); + TORRENT_ASSERT(slot_index < (int)m_slot_to_piece.size()); + + if (m_free_slots.empty() && m_unallocated_slots.empty()) + switch_to_full_mode(); + + return slot_index; + } + + bool piece_manager::allocate_slots_impl(int num_slots, mutex::scoped_lock& l + , bool abort_on_disk) + { + TORRENT_ASSERT(num_slots > 0); + +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + INVARIANT_CHECK; +#endif + + TORRENT_ASSERT(!m_unallocated_slots.empty()); + TORRENT_ASSERT(m_storage_mode == internal_storage_mode_compact_deprecated); + + bool written = false; + + for (int i = 0; i < num_slots && !m_unallocated_slots.empty(); ++i) + { + int pos = m_unallocated_slots.front(); + TORRENT_ASSERT(m_slot_to_piece[pos] == unallocated); + TORRENT_ASSERT(m_piece_to_slot[pos] != pos); + + int new_free_slot = pos; + if (m_piece_to_slot[pos] != has_no_slot) + { + m_last_piece = pos; + new_free_slot = m_piece_to_slot[pos]; + m_storage->move_slot(new_free_slot, pos); + m_slot_to_piece[pos] = pos; + m_piece_to_slot[pos] = pos; + written = true; + } + m_unallocated_slots.erase(m_unallocated_slots.begin()); + m_slot_to_piece[new_free_slot] = unassigned; + m_free_slots.push_back(new_free_slot); + if (abort_on_disk && written) break; + } + + TORRENT_ASSERT(m_free_slots.size() > 0); + return written; + } + + int piece_manager::slot_for(int piece) const + { + if (m_storage_mode != internal_storage_mode_compact_deprecated) return piece; + // this happens in seed mode, where we skip checking fastresume + if (m_piece_to_slot.empty()) return piece; + TORRENT_ASSERT(piece < int(m_piece_to_slot.size())); + TORRENT_ASSERT(piece >= 0); + return m_piece_to_slot[piece]; + } + + int piece_manager::piece_for(int slot) const + { + if (m_storage_mode != internal_storage_mode_compact_deprecated) return slot; + TORRENT_ASSERT(slot < int(m_slot_to_piece.size())); + TORRENT_ASSERT(slot >= 0); + return m_slot_to_piece[slot]; + } + +#if TORRENT_USE_INVARIANT_CHECKS + void piece_manager::check_invariant() const + { + TORRENT_ASSERT(m_current_slot <= m_files.num_pieces()); + + if (m_unallocated_slots.empty() + && m_free_slots.empty() + && m_state == state_finished) + { + TORRENT_ASSERT(m_storage_mode != internal_storage_mode_compact_deprecated + || m_files.num_pieces() == 0); + } + + if (m_storage_mode != internal_storage_mode_compact_deprecated) + { + TORRENT_ASSERT(m_unallocated_slots.empty()); + TORRENT_ASSERT(m_free_slots.empty()); + } + + if (m_storage_mode != internal_storage_mode_compact_deprecated + && m_state != state_expand_pieces + && m_state != state_full_check) + { + TORRENT_ASSERT(m_piece_to_slot.empty()); + TORRENT_ASSERT(m_slot_to_piece.empty()); + } + else + { + if (m_piece_to_slot.empty()) return; + + TORRENT_ASSERT((int)m_piece_to_slot.size() == m_files.num_pieces()); + TORRENT_ASSERT((int)m_slot_to_piece.size() == m_files.num_pieces()); + + for (std::vector::const_iterator i = m_free_slots.begin(); + i != m_free_slots.end(); ++i) + { + TORRENT_ASSERT(*i < (int)m_slot_to_piece.size()); + TORRENT_ASSERT(*i >= 0); + TORRENT_ASSERT(m_slot_to_piece[*i] == unassigned); + TORRENT_ASSERT(std::find(i+1, m_free_slots.end(), *i) + == m_free_slots.end()); + } + + for (std::vector::const_iterator i = m_unallocated_slots.begin(); + i != m_unallocated_slots.end(); ++i) + { + TORRENT_ASSERT(*i < (int)m_slot_to_piece.size()); + TORRENT_ASSERT(*i >= 0); + TORRENT_ASSERT(m_slot_to_piece[*i] == unallocated); + TORRENT_ASSERT(std::find(i+1, m_unallocated_slots.end(), *i) + == m_unallocated_slots.end()); + } + + for (int i = 0; i < m_files.num_pieces(); ++i) + { + // Check domain of piece_to_slot's elements + if (m_piece_to_slot[i] != has_no_slot) + { + TORRENT_ASSERT(m_piece_to_slot[i] >= 0); + TORRENT_ASSERT(m_piece_to_slot[i] < (int)m_slot_to_piece.size()); + } + + // Check domain of slot_to_piece's elements + if (m_slot_to_piece[i] != unallocated + && m_slot_to_piece[i] != unassigned) + { + TORRENT_ASSERT(m_slot_to_piece[i] >= 0); + TORRENT_ASSERT(m_slot_to_piece[i] < (int)m_piece_to_slot.size()); + } + + // do more detailed checks on piece_to_slot + if (m_piece_to_slot[i] >= 0) + { + TORRENT_ASSERT(m_slot_to_piece[m_piece_to_slot[i]] == i); + if (m_piece_to_slot[i] != i) + { + TORRENT_ASSERT(m_slot_to_piece[i] == unallocated); + } + } + else + { + TORRENT_ASSERT(m_piece_to_slot[i] == has_no_slot); + } + + // do more detailed checks on slot_to_piece + + if (m_slot_to_piece[i] >= 0) + { + TORRENT_ASSERT(m_slot_to_piece[i] < (int)m_piece_to_slot.size()); + TORRENT_ASSERT(m_piece_to_slot[m_slot_to_piece[i]] == i); +#ifdef TORRENT_STORAGE_DEBUG + TORRENT_ASSERT( + std::find( + m_unallocated_slots.begin() + , m_unallocated_slots.end() + , i) == m_unallocated_slots.end() + ); + TORRENT_ASSERT( + std::find( + m_free_slots.begin() + , m_free_slots.end() + , i) == m_free_slots.end() + ); +#endif + } + else if (m_slot_to_piece[i] == unallocated) + { +#ifdef TORRENT_STORAGE_DEBUG + TORRENT_ASSERT(m_unallocated_slots.empty() + || (std::find( + m_unallocated_slots.begin() + , m_unallocated_slots.end() + , i) != m_unallocated_slots.end()) + ); +#endif + } + else if (m_slot_to_piece[i] == unassigned) + { +#ifdef TORRENT_STORAGE_DEBUG + TORRENT_ASSERT( + std::find( + m_free_slots.begin() + , m_free_slots.end() + , i) != m_free_slots.end() + ); +#endif + } + else + { + TORRENT_ASSERT(false && "m_slot_to_piece[i] is invalid"); + } + } + } + } + +#endif +} // namespace libtorrent + diff --git a/apps/Launcher/ext/libtorrent/src/string_util.cpp b/apps/Launcher/ext/libtorrent/src/string_util.cpp new file mode 100644 index 0000000000..576a1ca0a2 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/string_util.cpp @@ -0,0 +1,169 @@ +/* + +Copyright (c) 2012-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" +#include "libtorrent/string_util.hpp" +#include "libtorrent/random.hpp" + +#include // for malloc/free +#include // for strcpy/strlen + +namespace libtorrent +{ + + bool is_alpha(char c) + { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); + } + + bool is_digit(char c) + { + return c >= '0' && c <= '9'; + } + + bool is_print(char c) + { + return c >= 32 && c < 127; + } + + bool is_space(char c) + { + const static char* ws = " \t\n\r\f\v"; + return strchr(ws, c) != 0; + } + + char to_lower(char c) + { + return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; + } + + int split_string(char const** tags, int buf_size, char* in) + { + int ret = 0; + char* i = in; + for (;*i; ++i) + { + if (!is_print(*i) || is_space(*i)) + { + *i = 0; + if (ret == buf_size) return ret; + continue; + } + if (i == in || i[-1] == 0) + { + tags[ret++] = i; + } + } + return ret; + } + + bool string_begins_no_case(char const* s1, char const* s2) + { + while (*s1 != 0) + { + if (to_lower(*s1) != to_lower(*s2)) return false; + ++s1; + ++s2; + } + return true; + } + + bool string_equal_no_case(char const* s1, char const* s2) + { + while (to_lower(*s1) == to_lower(*s2)) + { + if (*s1 == 0) return true; + ++s1; + ++s2; + } + return false; + } + + // generate a url-safe random string + void url_random(char* begin, char* end) + { + // http-accepted characters: + // excluding ', since some buggy trackers don't support that + static char const printable[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz-_.!~*()"; + + // the random number + while (begin != end) + *begin++ = printable[random() % (sizeof(printable)-1)]; + } + + char* allocate_string_copy(char const* str) + { + if (str == 0) return 0; + char* tmp = (char*)malloc(strlen(str) + 1); + if (tmp == 0) return 0; + strcpy(tmp, str); + return tmp; + } + + // 8-byte align pointer + void* align_pointer(void* p) + { + int offset = uintptr_t(p) & 0x7; + // if we're already aligned, don't do anything + if (offset == 0) return p; + + // offset is how far passed the last aligned address + // we are. We need to go forward to the next aligned + // one. Since aligned addresses are 8 bytes apart, add + // 8 - offset. + return static_cast(p) + (8 - offset); + } + + char* string_tokenize(char* last, char sep, char** next) + { + if (last == 0) return 0; + if (last[0] == '"') + { + *next = strchr(last + 1, '"'); + // consume the actual separator as well. + if (*next != NULL) + *next = strchr(*next, sep); + } + else + { + *next = strchr(last, sep); + } + if (*next == 0) return last; + **next = 0; + ++(*next); + while (**next == sep && **next) ++(*next); + return last; + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/thread.cpp b/apps/Launcher/ext/libtorrent/src/thread.cpp new file mode 100644 index 0000000000..c22aeb0e8d --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/thread.cpp @@ -0,0 +1,173 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/thread.hpp" +#include "libtorrent/assert.hpp" + +#ifdef TORRENT_BEOS +#include +#endif + +#ifdef BOOST_HAS_PTHREADS +#include // for gettimeofday() +#include +#endif + +namespace libtorrent +{ + void sleep(int milliseconds) + { +#if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN + Sleep(milliseconds); +#elif defined TORRENT_BEOS + snooze_until(system_time() + boost::int64_t(milliseconds) * 1000, B_SYSTEM_TIMEBASE); +#else + usleep(milliseconds * 1000); +#endif + } + +#ifdef BOOST_HAS_PTHREADS + + condition_variable::condition_variable() + { + pthread_cond_init(&m_cond, 0); + } + + condition_variable::~condition_variable() + { + pthread_cond_destroy(&m_cond); + } + + void condition_variable::wait(mutex::scoped_lock& l) + { + TORRENT_ASSERT(l.locked()); + // wow, this is quite a hack + pthread_cond_wait(&m_cond, (::pthread_mutex_t*)&l.mutex()); + } + + void condition_variable::wait_for(mutex::scoped_lock& l, time_duration rel_time) + { + TORRENT_ASSERT(l.locked()); + + struct timeval tv; + struct timespec ts; + gettimeofday(&tv, NULL); + boost::uint64_t microseconds = tv.tv_usec + total_microseconds(rel_time) % 1000000; + ts.tv_nsec = (microseconds % 1000000) * 1000; + ts.tv_sec = tv.tv_sec + total_seconds(rel_time) + microseconds / 1000000; + + // wow, this is quite a hack + pthread_cond_timedwait(&m_cond, (::pthread_mutex_t*)&l.mutex(), &ts); + } + + void condition_variable::notify_all() + { + pthread_cond_broadcast(&m_cond); + } +#elif defined TORRENT_WINDOWS || defined TORRENT_CYGWIN + condition_variable::condition_variable() + : m_num_waiters(0) + { + m_sem = CreateSemaphore(0, 0, INT_MAX, 0); + } + + condition_variable::~condition_variable() + { + CloseHandle(m_sem); + } + + void condition_variable::wait(mutex::scoped_lock& l) + { + TORRENT_ASSERT(l.locked()); + ++m_num_waiters; + l.unlock(); + WaitForSingleObject(m_sem, INFINITE); + l.lock(); + --m_num_waiters; + } + + void condition_variable::wait_for(mutex::scoped_lock& l, time_duration rel_time) + { + TORRENT_ASSERT(l.locked()); + ++m_num_waiters; + l.unlock(); + WaitForSingleObject(m_sem, total_milliseconds(rel_time)); + l.lock(); + --m_num_waiters; + } + + void condition_variable::notify_all() + { + ReleaseSemaphore(m_sem, m_num_waiters, 0); + } +#elif defined TORRENT_BEOS + condition_variable::condition_variable() + : m_num_waiters(0) + { + m_sem = create_sem(0, 0); + } + + condition_variable::~condition_variable() + { + delete_sem(m_sem); + } + + void condition_variable::wait(mutex::scoped_lock& l) + { + TORRENT_ASSERT(l.locked()); + ++m_num_waiters; + l.unlock(); + acquire_sem(m_sem); + l.lock(); + --m_num_waiters; + } + + void condition_variable::wait_for(mutex::scoped_lock& l, time_duration rel_time) + { + TORRENT_ASSERT(l.locked()); + ++m_num_waiters; + l.unlock(); + acquire_sem_etc(m_sem, 1, B_RELATIVE_TIMEOUT, total_microseconds(rel_time)); + l.lock(); + --m_num_waiters; + } + + void condition_variable::notify_all() + { + release_sem_etc(m_sem, m_num_waiters, 0); + } +#else +#error not implemented +#endif + +} + diff --git a/apps/Launcher/ext/libtorrent/src/time.cpp b/apps/Launcher/ext/libtorrent/src/time.cpp new file mode 100644 index 0000000000..bea93856c6 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/time.cpp @@ -0,0 +1,254 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include +#include +#include "libtorrent/config.hpp" +#include "libtorrent/time.hpp" + +#ifndef _WIN32 +#include +#endif + +namespace libtorrent +{ + namespace aux + { + // used to cache the current time + // every 100 ms. This is cheaper + // than a system call and can be + // used where more accurate time + // is not necessary + ptime g_current_time; + } + + TORRENT_EXPORT ptime const& time_now() { return aux::g_current_time; } + + char const* time_now_string() + { + static const ptime start = time_now_hires(); + static char ret[200]; + int t = total_milliseconds(time_now_hires() - start); + int h = t / 1000 / 60 / 60; + t -= h * 60 * 60 * 1000; + int m = t / 1000 / 60; + t -= m * 60 * 1000; + int s = t / 1000; + t -= s * 1000; + int ms = t; + snprintf(ret, sizeof(ret), "%02d:%02d:%02d.%03d", h, m, s, ms); + return ret; + } + + std::string log_time() + { + static const ptime start = time_now_hires(); + char ret[200]; + snprintf(ret, sizeof(ret), "%" PRId64, total_microseconds(time_now_hires() - start)); + return ret; + } +} + +#if defined TORRENT_USE_BOOST_DATE_TIME + +#include + +namespace libtorrent +{ + ptime time_now_hires() + { return boost::date_time::microsec_clock::universal_time(); } + ptime min_time() + { return boost::posix_time::ptime(boost::posix_time::min_date_time); } + ptime max_time() + { return boost::posix_time::ptime(boost::posix_time::max_date_time); } + time_duration seconds(boost::int64_t s) { return boost::posix_time::seconds(s); } + time_duration milliseconds(boost::int64_t s) { return boost::posix_time::milliseconds(s); } + time_duration microsec(boost::int64_t s) { return boost::posix_time::microsec(s); } + time_duration minutes(boost::int64_t s) { return boost::posix_time::minutes(s); } + time_duration hours(boost::int64_t s) { return boost::posix_time::hours(s); } + + boost::int64_t total_seconds(time_duration td) + { return td.total_seconds(); } + boost::int64_t total_milliseconds(time_duration td) + { return td.total_milliseconds(); } + boost::int64_t total_microseconds(time_duration td) + { return td.total_microseconds(); } +} + +#else // TORRENT_USE_BOOST_DATE_TIME + +namespace libtorrent +{ + ptime min_time() { return ptime(0); } + ptime max_time() { return ptime((std::numeric_limits::max)()); } +} + +#if defined TORRENT_USE_ABSOLUTE_TIME + +#include +#include +#include "libtorrent/assert.hpp" + +// high precision timer for darwin intel and ppc + +namespace libtorrent +{ + ptime time_now_hires() + { + static mach_timebase_info_data_t timebase_info = {0,0}; + if (timebase_info.denom == 0) + mach_timebase_info(&timebase_info); + boost::uint64_t at = mach_absolute_time(); + // make sure we don't overflow + TORRENT_ASSERT((at >= at / 1000 * timebase_info.numer / timebase_info.denom) + || (at < 0 && at < at / 1000 * timebase_info.numer / timebase_info.denom)); + return ptime(at / 1000 * timebase_info.numer / timebase_info.denom); + } +} +#elif defined TORRENT_USE_QUERY_PERFORMANCE_TIMER + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include "libtorrent/assert.hpp" + +namespace libtorrent +{ + boost::int64_t performance_counter_to_microseconds(boost::int64_t pc) + { + static LARGE_INTEGER performace_counter_frequency = {0,0}; + if (performace_counter_frequency.QuadPart == 0) + QueryPerformanceFrequency(&performace_counter_frequency); + +#ifdef TORRENT_DEBUG + // make sure we don't overflow + boost::int64_t ret = (pc * 1000 / performace_counter_frequency.QuadPart) * 1000; + TORRENT_ASSERT((pc >= 0 && pc >= ret) || (pc < 0 && pc < ret)); +#endif + return ((pc * 1000 + performace_counter_frequency.QuadPart / 2) / performace_counter_frequency.QuadPart) * 1000; + } + + boost::int64_t microseconds_to_performance_counter(boost::int64_t ms) + { + static LARGE_INTEGER performace_counter_frequency = {0,0}; + if (performace_counter_frequency.QuadPart == 0) + QueryPerformanceFrequency(&performace_counter_frequency); +#ifdef TORRENT_DEBUG + // make sure we don't overflow + boost::int64_t ret = (ms / 1000) * performace_counter_frequency.QuadPart / 1000; + TORRENT_ASSERT((ms >= 0 && ms <= ret) + || (ms < 0 && ms > ret)); +#endif + return (ms / 1000) * performace_counter_frequency.QuadPart / 1000; + } + + ptime time_now_hires() + { + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + return ptime(now.QuadPart); + } + + boost::int64_t total_seconds(time_duration td) + { + return boost::int64_t(performance_counter_to_microseconds(td.diff) + / 1000000); + } + boost::int64_t total_milliseconds(time_duration td) + { + return boost::uint64_t(performance_counter_to_microseconds(td.diff) + / 1000); + } + boost::int64_t total_microseconds(time_duration td) + { + return performance_counter_to_microseconds(td.diff); + } + + time_duration microsec(boost::int64_t s) + { + return time_duration(microseconds_to_performance_counter(s)); + } + time_duration milliseconds(boost::int64_t s) + { + return time_duration(microseconds_to_performance_counter( + s * 1000)); + } + time_duration seconds(boost::int64_t s) + { + return time_duration(microseconds_to_performance_counter( + s * 1000000)); + } + time_duration minutes(boost::int64_t s) + { + return time_duration(microseconds_to_performance_counter( + s * 1000000 * 60)); + } + time_duration hours(boost::int64_t s) + { + return time_duration(microseconds_to_performance_counter( + s * 1000000 * 60 * 60)); + } +} + +#elif defined TORRENT_USE_CLOCK_GETTIME + +#include +#include "libtorrent/assert.hpp" + +namespace libtorrent +{ + ptime time_now_hires() + { + timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ptime(boost::uint64_t(ts.tv_sec) * 1000000 + ts.tv_nsec / 1000); + } +} + +#elif defined TORRENT_USE_SYSTEM_TIME + +#include + +namespace libtorrent +{ + ptime time_now_hires() + { return ptime(system_time()); } +} + +#endif // TORRENT_USE_SYSTEM_TIME + +#endif // TORRENT_USE_BOOST_DATE_TIME + diff --git a/apps/Launcher/ext/libtorrent/src/timestamp_history.cpp b/apps/Launcher/ext/libtorrent/src/timestamp_history.cpp new file mode 100644 index 0000000000..8a126e4410 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/timestamp_history.cpp @@ -0,0 +1,106 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + + +#include "libtorrent/timestamp_history.hpp" + +namespace libtorrent { + +enum +{ + TIME_MASK = 0xffffffff +}; +// defined in utp_stream.cpp +bool compare_less_wrap(boost::uint32_t lhs, boost::uint32_t rhs + , boost::uint32_t mask); + +boost::uint32_t timestamp_history::add_sample(boost::uint32_t sample, bool step) +{ + if (!m_initialized) + { + for (int i = 0; i < history_size; ++i) + m_history[i] = sample; + m_base = sample; + m_initialized = true; + } + + ++m_num_samples; + + // if sample is less than base, update the base + // and update the history entry (because it will + // be less than that too) + if (compare_less_wrap(sample, m_base, TIME_MASK)) + { + m_base = sample; + m_history[m_index] = sample; + } + // if sample is less than our history entry, update it + else if (compare_less_wrap(sample, m_history[m_index], TIME_MASK)) + { + m_history[m_index] = sample; + } + + boost::uint32_t ret = sample - m_base; + + // don't step base delay history unless we have at least 120 + // samples. Anything less would suggest that the connection is + // essentially idle and the samples are probably not very reliable + if (step && m_num_samples > 120) + { + m_num_samples = 0; + m_index = (m_index + 1) % history_size; + + m_history[m_index] = sample; + // update m_base + m_base = sample; + for (int i = 0; i < history_size; ++i) + { + if (compare_less_wrap(m_history[i], m_base, TIME_MASK)) + m_base = m_history[i]; + } + } + return ret; +} + +void timestamp_history::adjust_base(int change) +{ + TORRENT_ASSERT(m_initialized); + m_base += change; + // make sure this adjustment sticks by updating all history slots + for (int i = 0; i < history_size; ++i) + { + if (compare_less_wrap(m_history[i], m_base, TIME_MASK)) + m_history[i] = m_base; + } +} + +} diff --git a/apps/Launcher/ext/libtorrent/src/torrent.cpp b/apps/Launcher/ext/libtorrent/src/torrent.cpp new file mode 100644 index 0000000000..ecda0668ab --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/torrent.cpp @@ -0,0 +1,9598 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include +#include +#include // for numeric_limits + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/parse_url.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/peer.hpp" +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/web_peer_connection.hpp" +#include "libtorrent/http_seed_connection.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/identify_client.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/instantiate_connection.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/broadcast_socket.hpp" +#include "libtorrent/kademlia/dht_tracker.hpp" +#include "libtorrent/peer_info.hpp" +#include "libtorrent/enum_net.hpp" +#include "libtorrent/http_connection.hpp" +#include "libtorrent/gzip.hpp" // for inflate_gzip +#include "libtorrent/random.hpp" +#include "libtorrent/string_util.hpp" // for allocate_string_copy +#include "libtorrent/alloca.hpp" + +#ifdef TORRENT_USE_OPENSSL +#include "libtorrent/ssl_stream.hpp" +#include +#include +#if BOOST_VERSION >= 104700 +#include +#endif // BOOST_VERSION +#endif // TORRENT_USE_OPENSSL + +using namespace libtorrent; +using boost::tuples::tuple; +using boost::tuples::get; +using boost::tuples::make_tuple; +using libtorrent::aux::session_impl; + +namespace +{ + struct find_peer_by_ip + { + find_peer_by_ip(tcp::endpoint const& a, const torrent* t) + : ip(a) + , tor(t) + { TORRENT_ASSERT(t != 0); } + + bool operator()(session_impl::connection_map::value_type const& c) const + { + tcp::endpoint const& sender = c->remote(); + if (sender.address() != ip.address()) return false; + if (tor != c->associated_torrent().lock().get()) return false; + return true; + } + + tcp::endpoint const& ip; + torrent const* tor; + }; + + struct peer_by_id + { + peer_by_id(const peer_id& i): pid(i) {} + + bool operator()(session_impl::connection_map::value_type const& p) const + { + if (p->pid() != pid) return false; + // have a special case for all zeros. We can have any number + // of peers with that pid, since it's used to indicate no pid. + if (pid.is_all_zeros()) return false; + return true; + } + + peer_id const& pid; + }; +} + +namespace libtorrent +{ + int root2(int x) + { + int ret = 0; + x >>= 1; + while (x > 0) + { + // if this assert triggers, the block size + // is not an even 2 exponent! + TORRENT_ASSERT(x == 1 || (x & 1) == 0); + ++ret; + x >>= 1; + } + return ret; + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + // defined in ut_pex.cpp + bool was_introduced_by(peer_plugin const*, tcp::endpoint const&); +#endif + + torrent::torrent( + session_impl& ses + , tcp::endpoint const& net_interface + , int block_size + , int seq + , add_torrent_params const& p + , sha1_hash const& info_hash) + : m_policy(this) + , m_total_uploaded(0) + , m_total_downloaded(0) + , m_started(time_now()) + , m_storage(0) + , m_num_connecting(0) + , m_tracker_timer(ses.m_io_service) + , m_ses(ses) + , m_host_resolver(ses.m_io_service) + , m_trackerid(p.trackerid) + , m_save_path(complete(p.save_path)) + , m_url(p.url) + , m_uuid(p.uuid) + , m_source_feed_url(p.source_feed_url) + , m_storage_constructor(p.storage) + , m_added_time(time(0)) + , m_completed_time(0) + , m_last_saved_resume(time(0)) + , m_last_seen_complete(0) + , m_swarm_last_seen_complete(0) + , m_num_verified(0) + , m_info_hash(info_hash) + , m_average_piece_time(0) + , m_piece_time_deviation(0) + , m_total_failed_bytes(0) + , m_total_redundant_bytes(0) + , m_sequence_number(seq) + , m_upload_mode_time(0) + , m_state(torrent_status::checking_resume_data) + , m_storage_mode(p.storage_mode) + , m_announcing(false) + , m_waiting_tracker(false) + , m_seed_mode(false) + , m_active_time(0) + , m_last_working_tracker(-1) + , m_finished_time(0) + , m_sequential_download(false) + , m_got_tracker_response(false) + , m_connections_initialized(false) + , m_super_seeding(false) + , m_override_resume_data(p.flags & add_torrent_params::flag_override_resume_data) +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + , m_resolving_country(false) + , m_resolve_countries(false) +#endif + , m_need_save_resume_data(true) + , m_seeding_time(0) + , m_time_scaler(0) + , m_max_uploads((1<<24)-1) + , m_save_resume_flags(0) + , m_num_uploads(0) + , m_block_size_shift(root2(block_size)) + , m_has_incoming(false) + , m_files_checked(false) + , m_queued_for_checking(false) + , m_max_connections((1<<24)-1) + , m_graceful_pause_mode(false) + , m_need_connect_boost(true) + , m_lsd_seq(0) + , m_magnet_link(false) + , m_apply_ip_filter(p.flags & add_torrent_params::flag_apply_ip_filter) + , m_merge_resume_trackers(p.flags & add_torrent_params::flag_merge_resume_trackers) + , m_padding(0) + , m_priority(0) + , m_complete(0xffffff) + , m_state_subscription(p.flags & add_torrent_params::flag_update_subscribe) + , m_in_state_updates(false) + , m_is_active_download(false) + , m_is_active_finished(false) + , m_ssl_torrent(false) + , m_deleted(false) + , m_moving_storage(false) + , m_inactive(false) + , m_incomplete(0xffffff) + , m_abort(false) + , m_announce_to_dht((p.flags & add_torrent_params::flag_paused) == 0) + , m_announce_to_trackers((p.flags & add_torrent_params::flag_paused) == 0) + , m_announce_to_lsd((p.flags & add_torrent_params::flag_paused) == 0) + , m_allow_peers((p.flags & add_torrent_params::flag_paused) == 0) + , m_upload_mode(p.flags & add_torrent_params::flag_upload_mode) + , m_auto_managed(p.flags & add_torrent_params::flag_auto_managed) + , m_share_mode(p.flags & add_torrent_params::flag_share_mode) + , m_last_download(0) + , m_last_scrape(0) + , m_last_upload(0) + , m_downloaded(0xffffff) + , m_interface_index(0) + , m_progress_ppm(0) + , m_inactive_counter(0) + , m_use_resume_save_path(p.flags & add_torrent_params::flag_use_resume_save_path) + { + // if there is resume data already, we don't need to trigger the initial save + // resume data + if (!p.resume_data.empty() && (p.flags & add_torrent_params::flag_override_resume_data) == 0) + m_need_save_resume_data = false; + +#if TORRENT_USE_ASSERTS + m_resume_data_loaded = false; +#endif +#if TORRENT_USE_UNC_PATHS + m_save_path = canonicalize_path(m_save_path); +#endif + + if (!m_apply_ip_filter) ++m_ses.m_non_filtered_torrents; + + update_guage(); + + if (!p.ti || !p.ti->is_valid()) + { + // we don't have metadata for this torrent. We'll download + // it either through the URL passed in, or through a metadata + // extension. Make sure that when we save resume data for this + // torrent, we also save the metadata + m_magnet_link = true; + } + + if (!m_torrent_file) + m_torrent_file = (p.ti ? p.ti : new torrent_info(info_hash)); + + // add web seeds from add_torrent_params + for (std::vector::const_iterator i = p.url_seeds.begin() + , end(p.url_seeds.end()); i != end; ++i) + { + m_web_seeds.push_back(web_seed_entry(*i, web_seed_entry::url_seed)); + } + + m_trackers = m_torrent_file->trackers(); + if (m_torrent_file->is_valid()) + { + m_seed_mode = p.flags & add_torrent_params::flag_seed_mode; + m_connections_initialized = true; + m_block_size_shift = root2((std::min)(block_size, m_torrent_file->piece_length())); + } + else + { + if (!p.name.empty()) m_name.reset(new std::string(p.name)); + } + + if (!m_url.empty() && m_uuid.empty()) m_uuid = m_url; + + TORRENT_ASSERT(m_ses.is_network_thread()); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("creating torrent: %s", torrent_file().name().c_str()); +#endif + m_net_interfaces.push_back(tcp::endpoint(net_interface.address(), 0)); + + m_file_priority = p.file_priorities; + + if (m_seed_mode) + m_verified.resize(m_torrent_file->num_pieces(), false); + + m_resume_data = p.resume_data; + +#ifndef TORRENT_DISABLE_ENCRYPTION + hasher h; + h.update("req2", 4); + h.update((char*)&m_torrent_file->info_hash()[0], 20); + m_obfuscated_hash = h.final(); +#endif + + INVARIANT_CHECK; + + if (p.flags & add_torrent_params::flag_sequential_download) + m_sequential_download = true; + + if (p.flags & add_torrent_params::flag_super_seeding) + m_super_seeding = true; + + set_max_uploads(p.max_uploads, false); + set_max_connections(p.max_connections, false); + set_upload_limit(p.upload_limit, false); + set_download_limit(p.download_limit, false); + + if (!m_name && !m_url.empty()) m_name.reset(new std::string(m_url)); + +#ifndef TORRENT_NO_DEPRECATE + if (p.tracker_url && std::strlen(p.tracker_url) > 0) + { + m_trackers.push_back(announce_entry(p.tracker_url)); + m_trackers.back().fail_limit = 0; + m_trackers.back().source = announce_entry::source_magnet_link; + m_torrent_file->add_tracker(p.tracker_url); + } +#endif + + for (std::vector::const_iterator i = p.trackers.begin() + , end(p.trackers.end()); i != end; ++i) + { + m_trackers.push_back(announce_entry(*i)); + m_trackers.back().fail_limit = 0; + m_trackers.back().source = announce_entry::source_magnet_link; + m_torrent_file->add_tracker(*i); + } + + if (settings().prefer_udp_trackers) + prioritize_udp_trackers(); + } + +#if 0 + + // NON BOTTLED VERSION. SUPPORTS PROGRESS REPORTING + + // since this download is not bottled, this callback will + // be called every time we receive another piece of the + // .torrent file + void torrent::on_torrent_download(error_code const& ec + , http_parser const& parser + , char const* data, int size) + { + if (m_abort) return; + + if (ec && ec != asio::error::eof) + { + set_error(ec, m_url); + pause(); + return; + } + + if (size > 0) + { + m_torrent_file_buf.insert(m_torrent_file_buf.end(), data, data + size); + if (parser.content_length() > 0) + set_progress_ppm(boost::int64_t(m_torrent_file_buf.size()) + * 1000000 / parser.content_length()); + } + + if (parser.header_finished() && parser.status_code() != 200) + { + set_error(error_code(parser.status_code(), get_http_category()), parser.message()); + pause(); + return; + } + + if (!ec) return; + + // if this was received with chunked encoding, we need to strip out + // the chunk headers + size = parser.collapse_chunk_headers((char*)&m_torrent_file_buf[0], m_torrent_file_buf.size()); + m_torrent_file_buf.resize(size); + + std::string const& encoding = parser.header("content-encoding"); + if ((encoding == "gzip" || encoding == "x-gzip") && m_torrent_file_buf.size()) + { + std::vector buf; + error_code ec; + inflate_gzip(&m_torrent_file_buf[0], m_torrent_file_buf.size() + , buf, 4 * 1024 * 1024, ex); + if (ec) + { + set_error(ec, m_url); + pause(); + std::vector().swap(m_torrent_file_buf); + return; + } + m_torrent_file_buf.swap(buf); + } + + // we're done! + error_code e; + intrusive_ptr tf(new torrent_info( + &m_torrent_file_buf[0], m_torrent_file_buf.size(), e)); + if (e) + { + set_error(e, m_url); + pause(); + std::vector().swap(m_torrent_file_buf); + return; + } + std::vector().swap(m_torrent_file_buf); + + // update our torrent_info object and move the + // torrent from the old info-hash to the new one + // as we replace the torrent_info object +#if TORRENT_USE_ASSERTS + int num_torrents = m_ses.m_torrents.size(); +#endif + // we're about to erase the session's reference to this + // torrent, create another reference + boost::shared_ptr me(shared_from_this()); + + m_ses.remove_torrent_impl(me, 0); + + m_torrent_file = tf; + + // now, we might already have this torrent in the session. + session_impl::torrent_map::iterator i = m_ses.m_torrents.find(m_torrent_file->info_hash()); + if (i != m_ses.m_torrents.end()) + { + if (!m_uuid.empty() && i->second->uuid().empty()) + i->second->set_uuid(m_uuid); + if (!m_url.empty() && i->second->url().empty()) + i->second->set_url(m_url); + if (!m_source_feed_url.empty() && i->second->source_feed_url().empty()) + i->second->set_source_feed_url(m_source_feed_url); + + // insert this torrent in the uuid index + if (!m_uuid.empty() || !m_url.empty()) + { + m_ses.m_uuids.insert(std::make_pair(m_uuid.empty() + ? m_url : m_uuid, i->second)); + } + set_error(error_code(errors::duplicate_torrent, get_libtorrent_category()), ""); + abort(); + return; + } + + m_ses.m_torrents.insert(std::make_pair(m_torrent_file->info_hash(), me)); + if (!m_uuid.empty()) m_ses.m_uuids.insert(std::make_pair(m_uuid, me)); + + TORRENT_ASSERT(num_torrents == int(m_ses.m_torrents.size())); + + // if the user added any trackers while downloading the + // .torrent file, serge them into the new tracker list + std::vector new_trackers = m_torrent_file->trackers(); + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + { + // if we already have this tracker, ignore it + if (std::find_if(new_trackers.begin(), new_trackers.end() + , boost::bind(&announce_entry::url, _1) == i->url) != new_trackers.end()) + continue; + + // insert the tracker ordered by tier + new_trackers.insert(std::find_if(new_trackers.begin(), new_trackers.end() + , boost::bind(&announce_entry::tier, _1) >= i->tier), *i); + } + m_trackers.swap(new_trackers); + +#ifndef TORRENT_DISABLE_ENCRYPTION + hasher h; + h.update("req2", 4); + h.update((char*)&m_torrent_file->info_hash()[0], 20); + m_obfuscated_hash = h.final(); +#endif + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(metadata_received_alert( + get_handle())); + } + + state_updated(); + + set_state(torrent_status::downloading); + + m_override_resume_data = true; + init(); + } +#else // if 0 + + void torrent::on_torrent_download(error_code const& ec + , http_parser const& parser, char const* data, int size) + { + if (m_abort) return; + + if (ec && ec != asio::error::eof) + { + set_error(ec, m_url); + pause(); + return; + } + + if (parser.status_code() != 200) + { + // #error there should really be an error code category for HTTP + set_error(errors::http_error, parser.message()); + pause(); + return; + } + + error_code e; + intrusive_ptr tf(new torrent_info(data, size, e)); + if (e) + { + set_error(e, m_url); + pause(); + return; + } + + // update our torrent_info object and move the + // torrent from the old info-hash to the new one + // as we replace the torrent_info object +#if TORRENT_USE_ASSERTS + int num_torrents = m_ses.m_torrents.size(); +#endif + + // we're about to erase the session's reference to this + // torrent, create another reference + boost::shared_ptr me(shared_from_this()); + + m_ses.remove_torrent_impl(me, 0); + + if (alerts().should_post()) + alerts().post_alert(torrent_update_alert(get_handle(), info_hash(), tf->info_hash())); + + m_torrent_file = tf; + m_info_hash = tf->info_hash(); + + // now, we might already have this torrent in the session. + session_impl::torrent_map::iterator i = m_ses.m_torrents.find(m_torrent_file->info_hash()); + if (i != m_ses.m_torrents.end()) + { + if (!m_uuid.empty() && i->second->uuid().empty()) + i->second->set_uuid(m_uuid); + if (!m_url.empty() && i->second->url().empty()) + i->second->set_url(m_url); + if (!m_source_feed_url.empty() && i->second->source_feed_url().empty()) + i->second->set_source_feed_url(m_source_feed_url); + + // insert this torrent in the uuid index + if (!m_uuid.empty() || !m_url.empty()) + { + m_ses.m_uuids.insert(std::make_pair(m_uuid.empty() + ? m_url : m_uuid, i->second)); + } + set_error(error_code(errors::duplicate_torrent, get_libtorrent_category()), ""); + abort(); + return; + } + + m_ses.m_torrents.insert(std::make_pair(m_torrent_file->info_hash(), me)); + if (!m_uuid.empty()) m_ses.m_uuids.insert(std::make_pair(m_uuid, me)); + + TORRENT_ASSERT(num_torrents == int(m_ses.m_torrents.size())); + + // if the user added any trackers while downloading the + // .torrent file, serge them into the new tracker list + std::vector new_trackers = m_torrent_file->trackers(); + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + { + // if we already have this tracker, ignore it + if (std::find_if(new_trackers.begin(), new_trackers.end() + , boost::bind(&announce_entry::url, _1) == i->url) != new_trackers.end()) + continue; + + // insert the tracker ordered by tier + new_trackers.insert(std::find_if(new_trackers.begin(), new_trackers.end() + , boost::bind(&announce_entry::tier, _1) >= i->tier), *i); + } + m_trackers.swap(new_trackers); + +#ifndef TORRENT_DISABLE_ENCRYPTION + hasher h; + h.update("req2", 4); + h.update((char*)&m_torrent_file->info_hash()[0], 20); + m_obfuscated_hash = h.final(); +#endif + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(metadata_received_alert( + get_handle())); + } + + state_updated(); + + set_state(torrent_status::downloading); + + m_override_resume_data = true; + init(); + } + +#endif // if 0 + + void torrent::leave_seed_mode(bool seed) + { + if (!m_seed_mode) return; + + if (!seed) + { + // this means the user promised we had all the + // files, but it turned out we didn't. This is + // an error. + + // TODO: 2 post alert + +#if defined TORRENT_ERROR_LOGGING + debug_log("*** FAILED SEED MODE, rechecking"); +#endif + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("*** LEAVING SEED MODE (%s)", seed ? "as seed" : "as non-seed"); +#endif + m_seed_mode = false; + // seed is false if we turned out not + // to be a seed after all + if (!seed) + { + set_state(torrent_status::downloading); + force_recheck(); + } + m_num_verified = 0; + m_verified.clear(); + } + + void torrent::verified(int piece) + { + TORRENT_ASSERT(piece < int(m_verified.size())); + TORRENT_ASSERT(piece >= 0); + TORRENT_ASSERT(m_verified.get_bit(piece) == false); + ++m_num_verified; + m_verified.set_bit(piece); + } + + void torrent::start() + { + TORRENT_ASSERT(m_ses.is_network_thread()); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("starting torrent"); +#endif + if (!m_seed_mode) + std::fill(m_file_progress.begin(), m_file_progress.end(), 0); + + if (!m_resume_data.empty()) + { + int pos; + error_code ec; + if (lazy_bdecode(&m_resume_data[0], &m_resume_data[0] + + m_resume_data.size(), m_resume_entry, ec, &pos) != 0) + { + std::vector().swap(m_resume_data); + lazy_entry().swap(m_resume_entry); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("resume data rejected: %s pos: %d", ec.message().c_str(), pos); +#endif + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(fastresume_rejected_alert(get_handle(), ec)); + } + } + } + + if (!m_torrent_file->is_valid() && !m_url.empty()) + { + // we need to download the .torrent file from m_url + start_download_url(); + } + else if (m_torrent_file->is_valid()) + { + init(); + } + else + { + // we need to start announcing since we don't have any + // metadata. To receive peers to ask for it. + set_state(torrent_status::downloading_metadata); + start_announcing(); + } + } + + bool torrent::is_active_download() const + { + return (m_state == torrent_status::downloading + || m_state == torrent_status::downloading_metadata) + && m_allow_peers + && !m_abort; + } + + bool torrent::is_active_finished() const + { + return (m_state == torrent_status::finished + || m_state == torrent_status::seeding) + && m_allow_peers + && !m_abort; + } + + void torrent::update_guage() + { + bool is_active_download = (m_state == torrent_status::downloading + || m_state == torrent_status::downloading_metadata) + && m_allow_peers + && !m_abort; + + bool is_active_finished = (m_state == torrent_status::finished + || m_state == torrent_status::seeding) + && m_allow_peers + && !m_abort; + + // update finished and downloading counters + if (is_active_download != m_is_active_download) + { + if (is_active_download) + m_ses.inc_active_downloading(); + else + m_ses.dec_active_downloading(); + m_is_active_download = is_active_download; + } + + if (is_active_finished != m_is_active_finished) + { + if (is_active_finished) + m_ses.inc_active_finished(); + else + m_ses.dec_active_finished(); + m_is_active_finished = is_active_finished; + } + } + + void torrent::start_download_url() + { + TORRENT_ASSERT(!m_url.empty()); + TORRENT_ASSERT(!m_torrent_file->is_valid()); + boost::shared_ptr conn( + new http_connection(m_ses.m_io_service, m_ses.m_half_open + , boost::bind(&torrent::on_torrent_download, shared_from_this() + , _1, _2, _3, _4) + , true // bottled + , m_ses.settings().max_http_recv_buffer_size // bottled buffer size + , http_connect_handler() + , http_filter_handler() +#ifdef TORRENT_USE_OPENSSL + , m_ssl_ctx.get() +#endif + )); + + conn->get(m_url, seconds(30), 0, &m_ses.proxy() + , 5, m_ses.m_settings.user_agent); + set_state(torrent_status::downloading_metadata); + } + + void torrent::set_apply_ip_filter(bool b) + { + if (b == m_apply_ip_filter) return; + if (b) + { + TORRENT_ASSERT(m_ses.m_non_filtered_torrents > 0); + --m_ses.m_non_filtered_torrents; + } + else + { + ++m_ses.m_non_filtered_torrents; + } + m_apply_ip_filter = b; + m_policy.ip_filter_updated(); + state_updated(); + } + +#ifndef TORRENT_DISABLE_DHT + bool torrent::should_announce_dht() const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (m_ses.m_listen_sockets.empty()) return false; + + if (!m_ses.m_dht) return false; + if (m_torrent_file->is_valid() && !m_files_checked) return false; + if (!m_announce_to_dht) return false; + if (!m_allow_peers) return false; + + // if we don't have the metadata, and we're waiting + // for a web server to serve it to us, no need to announce + // because the info-hash is just the URL hash + if (!m_torrent_file->is_valid() && !m_url.empty()) return false; + + // don't announce private torrents + if (m_torrent_file->is_valid() && m_torrent_file->priv()) return false; + if (m_trackers.empty()) return true; + if (!settings().use_dht_as_fallback) return true; + + int verified_trackers = 0; + for (std::vector::const_iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + if (i->verified) ++verified_trackers; + + return verified_trackers == 0; + } + +#endif + + torrent::~torrent() + { + if (!m_apply_ip_filter) + { + TORRENT_ASSERT(m_ses.m_non_filtered_torrents > 0); + --m_ses.m_non_filtered_torrents; + m_apply_ip_filter = true; + } + + TORRENT_ASSERT(m_ses.is_network_thread()); + // The invariant can't be maintained here, since the torrent + // is being destructed, all weak references to it have been + // reset, which means that all its peers already have an + // invalidated torrent pointer (so it cannot be verified to be correct) + + // i.e. the invariant can only be maintained if all connections have + // been closed by the time the torrent is destructed. And they are + // supposed to be closed. So we can still do the invariant check. + + INVARIANT_CHECK; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING || defined TORRENT_LOGGING + log_to_all_peers("DESTRUCTING TORRENT"); +#endif + + TORRENT_ASSERT(m_abort); + TORRENT_ASSERT(m_connections.empty()); + if (!m_connections.empty()) + disconnect_all(errors::torrent_aborted); + } + + void torrent::read_piece(int piece) + { + if (m_abort) + { + // failed + m_ses.m_alerts.post_alert(read_piece_alert( + get_handle(), piece, error_code(boost::system::errc::operation_canceled, get_system_category()))); + return; + } + + TORRENT_ASSERT(piece >= 0 && piece < m_torrent_file->num_pieces()); + int piece_size = m_torrent_file->piece_size(piece); + int blocks_in_piece = (piece_size + block_size() - 1) / block_size(); + + // if blocks_in_piece is 0, rp will leak + TORRENT_ASSERT(blocks_in_piece > 0); + TORRENT_ASSERT(piece_size > 0); + + read_piece_struct* rp = new read_piece_struct; + rp->piece_data.reset(new (std::nothrow) char[piece_size]); + rp->blocks_left = 0; + rp->fail = false; + + peer_request r; + r.piece = piece; + r.start = 0; + for (int i = 0; i < blocks_in_piece; ++i, r.start += block_size()) + { + r.length = (std::min)(piece_size - r.start, block_size()); + filesystem().async_read(r, boost::bind(&torrent::on_disk_read_complete + , shared_from_this(), _1, _2, r, rp)); + ++rp->blocks_left; + } + } + + void torrent::send_share_mode() + { +#ifndef TORRENT_DISABLE_EXTENSIONS + for (std::set::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + if ((*i)->type() != peer_connection::bittorrent_connection) continue; + bt_peer_connection* p = (bt_peer_connection*)*i; + p->write_share_mode(); + } +#endif + } + + void torrent::send_upload_only() + { +#ifndef TORRENT_DISABLE_EXTENSIONS + if (share_mode()) return; + if (super_seeding()) return; + + for (std::set::iterator i = m_connections.begin(); + i != m_connections.end();) + { + // since the call to disconnect_if_redundant() may + // delete the entry from this container, make sure + // to increment the iterator early + bt_peer_connection* p = (bt_peer_connection*)*i; + ++i; + if (p->type() == peer_connection::bittorrent_connection) + p->write_upload_only(); + } +#endif + } + + void torrent::set_share_mode(bool s) + { + if (s == m_share_mode) return; + + m_share_mode = s; + + // in share mode, all pieces have their priorities initialized to 0 + std::fill(m_file_priority.begin(), m_file_priority.end(), !m_share_mode); + + update_piece_priorities(); + + if (m_share_mode) recalc_share_mode(); + } + + void torrent::set_upload_mode(bool b) + { + if (b == m_upload_mode) return; + + m_upload_mode = b; + + state_updated(); + send_upload_only(); + + if (m_upload_mode) + { + // clear request queues of all peers + for (std::set::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + peer_connection* p = (*i); + p->cancel_all_requests(); + } + // this is used to try leaving upload only mode periodically + m_upload_mode_time = 0; + } + else + { + // reset last_connected, to force fast reconnect after leaving upload mode + for (policy::iterator i = m_policy.begin_peer() + , end(m_policy.end_peer()); i != end; ++i) + { + (*i)->last_connected = 0; + } + + // send_block_requests on all peers + for (std::set::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + peer_connection* p = (*i); + p->send_block_requests(); + } + } + } + + void torrent::handle_disk_error(disk_io_job const& j, peer_connection* c) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (!j.error) return; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("disk error: (%d) %s in file: %s", j.error.value(), j.error.message().c_str() + , j.error_file.c_str()); +#endif + + TORRENT_ASSERT(j.piece >= 0); + + piece_block block_finished(j.piece, j.offset / block_size()); + + if (j.action == disk_io_job::write) + { + // we failed to write j.piece to disk tell the piece picker + if (has_picker() && j.piece >= 0) picker().write_failed(block_finished); + } + + if (j.error == error_code(boost::system::errc::not_enough_memory, generic_category())) + { + if (alerts().should_post()) + alerts().post_alert(file_error_alert(j.error_file, get_handle(), j.error)); + if (c) c->disconnect(errors::no_memory); + return; + } + + // notify the user of the error + if (alerts().should_post()) + alerts().post_alert(file_error_alert(j.error_file, get_handle(), j.error)); + + // put the torrent in an error-state + set_error(j.error, j.error_file); + + if (j.action == disk_io_job::write + && (j.error == boost::system::errc::read_only_file_system + || j.error == boost::system::errc::permission_denied + || j.error == boost::system::errc::operation_not_permitted + || j.error == boost::system::errc::no_space_on_device + || j.error == boost::system::errc::file_too_large)) + { + // if we failed to write, stop downloading and just + // keep seeding. + // TODO: 1 make this depend on the error and on the filesystem the + // files are being downloaded to. If the error is no_space_left_on_device + // and the filesystem doesn't support sparse files, only zero the priorities + // of the pieces that are at the tails of all files, leaving everything + // up to the highest written piece in each file + set_upload_mode(true); + return; + } + + // if the error appears to be more serious than a full disk, just pause the torrent + pause(); + } + + void torrent::on_disk_read_complete(int ret, disk_io_job const& j + , peer_request r, read_piece_struct* rp) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + disk_buffer_holder buffer(m_ses, j.buffer); + + --rp->blocks_left; + if (ret != r.length) + { + rp->fail = true; + rp->error = j.error; + handle_disk_error(j); + } + else + { + std::memcpy(rp->piece_data.get() + r.start, j.buffer, r.length); + } + + if (rp->blocks_left == 0) + { + int size = m_torrent_file->piece_size(r.piece); + if (rp->fail) + { + m_ses.m_alerts.post_alert(read_piece_alert( + get_handle(), r.piece, rp->error)); + } + else + { + m_ses.m_alerts.post_alert(read_piece_alert( + get_handle(), r.piece, rp->piece_data, size)); + } + delete rp; + } + } + + void torrent::add_piece(int piece, char const* data, int flags) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(piece >= 0 && piece < m_torrent_file->num_pieces()); + int piece_size = m_torrent_file->piece_size(piece); + int blocks_in_piece = (piece_size + block_size() - 1) / block_size(); + + if (m_deleted) return; + + // avoid crash trying to access the picker when there is none + if (!has_picker()) return; + + if (picker().have_piece(piece) + && (flags & torrent::overwrite_existing) == 0) + return; + + peer_request p; + p.piece = piece; + p.start = 0; + picker().inc_refcount(piece, 0); + for (int i = 0; i < blocks_in_piece; ++i, p.start += block_size()) + { + if (picker().is_finished(piece_block(piece, i)) + && (flags & torrent::overwrite_existing) == 0) + continue; + + p.length = (std::min)(piece_size - p.start, int(block_size())); + char* buffer = m_ses.allocate_disk_buffer("add piece"); + // out of memory + if (buffer == 0) + { + picker().dec_refcount(piece, 0); + return; + } + disk_buffer_holder holder(m_ses, buffer); + std::memcpy(buffer, data + p.start, p.length); + filesystem().async_write(p, holder, boost::bind(&torrent::on_disk_write_complete + , shared_from_this(), _1, _2, p)); + piece_block block(piece, i); + picker().mark_as_downloading(block, 0, piece_picker::fast); + picker().mark_as_writing(block, 0); + } + async_verify_piece(piece, boost::bind(&torrent::piece_finished + , shared_from_this(), piece, _1)); + picker().dec_refcount(piece, 0); + } + + void torrent::on_disk_write_complete(int ret, disk_io_job const& j + , peer_request p) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + + if (m_abort) + { + piece_block block_finished(p.piece, p.start / block_size()); + return; + } + + piece_block block_finished(p.piece, p.start / block_size()); + + if (ret == -1) + { + handle_disk_error(j); + return; + } + + if (!has_picker()) return; + + // if we already have this block, just ignore it. + // this can happen if the same block is passed in through + // add_piece() multiple times + if (picker().is_finished(block_finished)) return; + + picker().mark_as_finished(block_finished, 0); + } + + void torrent::on_disk_cache_complete(int ret, disk_io_job const& j) + { + // suggest this piece to all peers + for (peer_iterator i = m_connections.begin(); + i != m_connections.end(); ++i) + (*i)->send_suggest(j.piece); + } + + bool torrent::add_merkle_nodes(std::map const& nodes, int piece) + { + return m_torrent_file->add_merkle_nodes(nodes, piece); + } + + peer_request torrent::to_req(piece_block const& p) const + { + int block_offset = p.block_index * block_size(); + int block = (std::min)(torrent_file().piece_size( + p.piece_index) - block_offset, int(block_size())); + TORRENT_ASSERT(block > 0); + TORRENT_ASSERT(block <= block_size()); + + peer_request r; + r.piece = p.piece_index; + r.start = block_offset; + r.length = block; + return r; + } + + std::string torrent::name() const + { + if (valid_metadata()) return m_torrent_file->name(); + if (m_name) return *m_name; + return ""; + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + + void torrent::add_extension(boost::shared_ptr ext) + { + m_extensions.push_back(ext); + } + + void torrent::add_extension(boost::function(torrent*, void*)> const& ext + , void* userdata) + { + boost::shared_ptr tp(ext(this, userdata)); + if (!tp) return; + + add_extension(tp); + + for (peer_iterator i = m_connections.begin(); + i != m_connections.end(); ++i) + { + peer_connection* p = *i; + boost::shared_ptr pp(tp->new_connection(p)); + if (pp) p->add_extension(pp); + } + + // if files are checked for this torrent, call the extension + // to let it initialize itself + if (m_connections_initialized) + tp->on_files_checked(); + } + +#endif + +#ifdef TORRENT_USE_OPENSSL + +#if BOOST_VERSION >= 104700 + bool torrent::verify_peer_cert(bool preverified, boost::asio::ssl::verify_context& ctx) + { + // if the cert wasn't signed by the correct CA, fail the verification + if (!preverified) return false; + + // we're only interested in checking the certificate at the end of the chain. + // TODO: is verify_peer_cert called once per certificate in the chain, and + // this function just tells us which depth we're at right now? If so, the comment + // makes sense. + // any certificate that isn't the leaf (i.e. the one presented by the peer) + // should be accepted automatically, given preverified is true. The leaf certificate + // need to be verified to make sure its DN matches the info-hash + int depth = X509_STORE_CTX_get_error_depth(ctx.native_handle()); + if (depth > 0) return true; + + X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); + + // Go through the alternate names in the certificate looking for matching DNS entries + GENERAL_NAMES* gens = static_cast( + X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0)); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + std::string names; + bool match = false; +#endif + for (int i = 0; i < sk_GENERAL_NAME_num(gens); ++i) + { + GENERAL_NAME* gen = sk_GENERAL_NAME_value(gens, i); + if (gen->type != GEN_DNS) continue; + ASN1_IA5STRING* domain = gen->d.dNSName; + if (domain->type != V_ASN1_IA5STRING || !domain->data || !domain->length) continue; + const char* torrent_name = reinterpret_cast(domain->data); + std::size_t name_length = domain->length; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (i > 1) names += " | n: "; + names.append(torrent_name, name_length); +#endif + if (strncmp(torrent_name, "*", name_length) == 0 + || strncmp(torrent_name, m_torrent_file->name().c_str(), name_length) == 0) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + match = true; + // if we're logging, keep looping over all names, + // for completeness of the log + continue; +#endif + return true; + } + } + + // no match in the alternate names, so try the common names. We should only + // use the "most specific" common name, which is the last one in the list. + X509_NAME* name = X509_get_subject_name(cert); + int i = -1; + ASN1_STRING* common_name = 0; + while ((i = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) + { + X509_NAME_ENTRY* name_entry = X509_NAME_get_entry(name, i); + common_name = X509_NAME_ENTRY_get_data(name_entry); + } + if (common_name && common_name->data && common_name->length) + { + const char* torrent_name = reinterpret_cast(common_name->data); + std::size_t name_length = common_name->length; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (!names.empty()) names += " | n: "; + names.append(torrent_name, name_length); +#endif + + if (strncmp(torrent_name, "*", name_length) == 0 + || strncmp(torrent_name, m_torrent_file->name().c_str(), name_length) == 0) + { +#if !defined(TORRENT_VERBOSE_LOGGING) && !defined(TORRENT_LOGGING) + return true; +#else + match = true; +#endif + } + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("<== incoming SSL CONNECTION [ n: %s | match: %s ]" + , names.c_str(), match?"yes":"no"); + return match; +#endif + + return false; + } +#endif // BOOST_VERSION + + void torrent::init_ssl(std::string const& cert) + { + using boost::asio::ssl::context; + + // this is needed for openssl < 1.0 to decrypt keys created by openssl 1.0+ + OpenSSL_add_all_algorithms(); + + boost::uint64_t now = total_microseconds(time_now_hires() - min_time()); + // assume 9 bits of entropy (i.e. about 1 millisecond) + RAND_add(&now, 8, 1.125); + RAND_add(&info_hash()[0], 20, 3); + // entropy is also added on incoming and completed connection attempts + + TORRENT_ASSERT(RAND_status() == 1); + +#if BOOST_VERSION >= 104700 + // create the SSL context for this torrent. We need to + // inject the root certificate, and no other, to + // verify other peers against + boost::shared_ptr ctx( + new (std::nothrow) context(m_ses.m_io_service, context::sslv23)); + + if (!ctx) + { + error_code ec(::ERR_get_error(), + asio::error::get_ssl_category()); + set_error(ec, "SSL context"); + pause(); + return; + } + + ctx->set_options(context::default_workarounds + | boost::asio::ssl::context::no_sslv2 + | boost::asio::ssl::context::single_dh_use); + + error_code ec; + ctx->set_verify_mode(context::verify_peer + | context::verify_fail_if_no_peer_cert + | context::verify_client_once, ec); + if (ec) + { + set_error(ec, "SSL verify mode"); + pause(); + return; + } + + // the verification function verifies the distinguished name + // of a peer certificate to make sure it matches the info-hash + // of the torrent, or that it's a "star-cert" + ctx->set_verify_callback(boost::bind(&torrent::verify_peer_cert, this, _1, _2), ec); + if (ec) + { + set_error(ec, "SSL verify callback"); + pause(); + return; + } + + SSL_CTX* ssl_ctx = ctx->impl(); + // create a new x.509 certificate store + X509_STORE* cert_store = X509_STORE_new(); + if (!cert_store) + { + error_code ec(::ERR_get_error(), + asio::error::get_ssl_category()); + set_error(ec, "x.509 certificate store"); + pause(); + return; + } + + // wrap the PEM certificate in a BIO, for openssl to read + BIO* bp = BIO_new_mem_buf((void*)cert.c_str(), cert.size()); + + // parse the certificate into OpenSSL's internal + // representation + X509* certificate = PEM_read_bio_X509_AUX(bp, 0, 0, 0); + + BIO_free(bp); + + if (!certificate) + { + error_code ec(::ERR_get_error(), + asio::error::get_ssl_category()); + X509_STORE_free(cert_store); + set_error(ec, "x.509 certificate"); + pause(); + return; + } + + // add cert to cert_store + X509_STORE_add_cert(cert_store, certificate); + + X509_free(certificate); + + // and lastly, replace the default cert store with ours + SSL_CTX_set_cert_store(ssl_ctx, cert_store); +#if 0 + char filename[100]; + snprintf(filename, sizeof(filename), "/tmp/%d.pem", rand()); + FILE* f = fopen(filename, "w+"); + fwrite(cert.c_str(), cert.size(), 1, f); + fclose(f); + ctx->load_verify_file(filename); +#endif + // if all went well, set the torrent ssl context to this one + m_ssl_ctx = ctx; + // tell the client we need a cert for this torrent + alerts().post_alert(torrent_need_cert_alert(get_handle())); +#else + set_error(asio::error::operation_not_supported, "x.509 certificate"); + pause(); +#endif + } + +#endif // TORRENT_OPENSSL + + peer_connection* torrent::find_lowest_ranking_peer() const + { + const_peer_iterator lowest_rank = end(); + for (const_peer_iterator i = begin(); i != end(); ++i) + { + // disconnecting peers don't count + if ((*i)->is_disconnecting()) continue; + if (lowest_rank == end() || (*lowest_rank)->peer_rank() > (*i)->peer_rank()) + lowest_rank = i; + } + + if (lowest_rank == end()) return NULL; + return *lowest_rank; + } + + // this may not be called from a constructor because of the call to + // shared_from_this() + void torrent::init() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(m_torrent_file->is_valid()); + TORRENT_ASSERT(m_torrent_file->num_files() > 0); + TORRENT_ASSERT(m_torrent_file->total_size() >= 0); + + if (int(m_file_priority.size()) > m_torrent_file->num_files()) + m_file_priority.resize(m_torrent_file->num_files()); + + std::string cert = m_torrent_file->ssl_cert(); + if (!cert.empty()) + { + m_ssl_torrent = true; +#ifdef TORRENT_USE_OPENSSL + init_ssl(cert); +#endif + } + + m_file_priority.resize(m_torrent_file->num_files(), 1); + + // initialize pad files to priority 0 + file_storage const& fs = m_torrent_file->files(); + for (int i = 0; i < fs.num_files(); ++i) + { + if (!fs.pad_file_at(i)) continue; + m_file_priority[i] = 0; + } + m_file_progress.resize(m_torrent_file->num_files(), 0); + + m_block_size_shift = root2((std::min)(int(block_size()), m_torrent_file->piece_length())); + + if (m_torrent_file->num_pieces() > piece_picker::max_pieces) + { + set_error(errors::too_many_pieces_in_torrent, ""); + pause(); + return; + } + + if (m_torrent_file->num_pieces() == 0) + { + set_error(errors::torrent_invalid_length, ""); + pause(); + return; + } + + if (m_resume_entry.type() == lazy_entry::dict_t) + { + int ev = 0; + if (m_resume_entry.dict_find_string_value("file-format") != "libtorrent resume file") + ev = errors::invalid_file_tag; + + std::string info_hash = m_resume_entry.dict_find_string_value("info-hash"); + if (!ev && info_hash.empty()) + ev = errors::missing_info_hash; + + if (!ev && sha1_hash(info_hash) != m_torrent_file->info_hash()) + ev = errors::mismatching_info_hash; + + if (ev && m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(fastresume_rejected_alert(get_handle() + , error_code(ev, get_libtorrent_category()))); + } + + if (ev) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("fastresume data rejected: %s" + , error_code(ev, get_libtorrent_category()).message().c_str()); +#endif + std::vector().swap(m_resume_data); + lazy_entry().swap(m_resume_entry); + } + else + { + read_resume_data(m_resume_entry); + } + } + + // the shared_from_this() will create an intentional + // cycle of ownership, se the hpp file for description. + m_owning_storage = new piece_manager(shared_from_this(), m_torrent_file + , m_save_path, m_ses.m_files, m_ses.m_disk_thread, m_storage_constructor + , (storage_mode_t)m_storage_mode, m_file_priority); + m_storage = m_owning_storage.get(); + + if (!m_seed_mode) + { + TORRENT_ASSERT(!m_picker); + + m_picker.reset(new piece_picker()); + + int blocks_per_piece = (m_torrent_file->piece_length() + block_size() - 1) / block_size(); + int blocks_in_last_piece = ((m_torrent_file->total_size() % m_torrent_file->piece_length()) + + block_size() - 1) / block_size(); + m_picker->init(blocks_per_piece, blocks_in_last_piece, m_torrent_file->num_pieces()); + + // this resume data reqires the piece picker to have been constructed + // that's why it's done later + if (m_resume_entry.type() == lazy_entry::dict_t) + { + lazy_entry const* piece_priority = m_resume_entry.dict_find_string("piece_priority"); + if (piece_priority && piece_priority->string_length() + == m_torrent_file->num_pieces()) + { + char const* p = piece_priority->string_ptr(); + for (int i = 0; i < piece_priority->string_length(); ++i) + m_picker->set_piece_priority(i, p[i]); + m_policy.recalculate_connect_candidates(); + } + } + } + + if (m_share_mode) + { + // in share mode, all pieces have their priorities initialized to 0 + std::fill(m_file_priority.begin(), m_file_priority.end(), 0); + } + + if (!m_connections_initialized) + { + m_connections_initialized = true; + // all peer connections have to initialize themselves now that the metadata + // is available + for (torrent::peer_iterator i = m_connections.begin(); + i != m_connections.end();) + { + peer_connection* pc = *i; + ++i; + if (pc->is_disconnecting()) continue; + pc->on_metadata_impl(); + if (pc->is_disconnecting()) continue; + pc->init(); + } + } + + // in case file priorities were passed in via the add_torrent_params + // and also in the case of share mode, we need to update the priorities + update_piece_priorities(); + + std::vector const& web_seeds = m_torrent_file->web_seeds(); + m_web_seeds.insert(m_web_seeds.end(), web_seeds.begin(), web_seeds.end()); + + set_state(torrent_status::checking_resume_data); + +#if TORRENT_USE_ASSERTS + m_resume_data_loaded = true; +#endif + + if (m_seed_mode) + { + m_ses.m_io_service.post(boost::bind(&torrent::files_checked, shared_from_this())); + std::vector().swap(m_resume_data); + lazy_entry().swap(m_resume_entry); +#if TORRENT_USE_ASSERTS + m_resume_data_loaded = true; +#endif + return; + } + + int num_pad_files = 0; + TORRENT_ASSERT(block_size() > 0); + for (int i = 0; i < fs.num_files(); ++i) + { + if (fs.pad_file_at(i)) ++num_pad_files; + + if (!fs.pad_file_at(i) || fs.file_size(i) == 0) continue; + m_padding += boost::uint32_t(fs.file_size(i)); + + peer_request pr = m_torrent_file->map_file(i, 0, fs.file_size(i)); + int off = pr.start & (block_size()-1); + if (off != 0) { pr.length -= block_size() - off; pr.start += block_size() - off; } + TORRENT_ASSERT((pr.start & (block_size()-1)) == 0); + + int block = block_size(); + int blocks_per_piece = m_torrent_file->piece_length() / block; + piece_block pb(pr.piece, pr.start / block); + for (; pr.length >= block; pr.length -= block, ++pb.block_index) + { + if (int(pb.block_index) == blocks_per_piece) { pb.block_index = 0; ++pb.piece_index; } + m_picker->mark_as_finished(pb, 0); + } + // ugly edge case where padfiles are not used they way they're + // supposed to be. i.e. added back-to back or at the end + if (int(pb.block_index) == blocks_per_piece) { pb.block_index = 0; ++pb.piece_index; } + if (pr.length > 0 && ((i+1 != fs.num_files() && fs.pad_file_at(i+1)) + || i + 1 == fs.num_files())) + { + m_picker->mark_as_finished(pb, 0); + } + } + + if (m_padding > 0) + { + // if we marked an entire piece as finished, we actually + // need to consider it finished + + std::vector const& dq + = m_picker->get_download_queue(); + + std::vector have_pieces; + + for (std::vector::const_iterator i + = dq.begin(); i != dq.end(); ++i) + { + int num_blocks = m_picker->blocks_in_piece(i->index); + if (i->finished < num_blocks) continue; + have_pieces.push_back(i->index); + } + + for (std::vector::iterator i = have_pieces.begin(); + i != have_pieces.end(); ++i) + { + we_have(*i); + } + } + + m_picker->set_num_pad_files(num_pad_files); + + m_storage->async_check_fastresume(&m_resume_entry + , boost::bind(&torrent::on_resume_data_checked + , shared_from_this(), _1, _2)); + } + + bt_peer_connection* torrent::find_introducer(tcp::endpoint const& ep) const + { +#ifndef TORRENT_DISABLE_EXTENSIONS + for (const_peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) + { + if ((*i)->type() != peer_connection::bittorrent_connection) continue; + bt_peer_connection* p = (bt_peer_connection*)(*i); + if (!p->supports_holepunch()) continue; + peer_plugin const* pp = p->find_plugin("ut_pex"); + if (!pp) continue; + if (was_introduced_by(pp, ep)) return (bt_peer_connection*)p; + } +#endif + return 0; + } + + bt_peer_connection* torrent::find_peer(tcp::endpoint const& ep) const + { + for (const_peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) + { + peer_connection* p = *i; + if (p->type() != peer_connection::bittorrent_connection) continue; + if (p->remote() == ep) return (bt_peer_connection*)p; + } + return 0; + } + + void torrent::on_resume_data_checked(int ret, disk_io_job const& j) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + if (ret == piece_manager::fatal_disk_error) + { + handle_disk_error(j); + auto_managed(false); + pause(); + set_state(torrent_status::queued_for_checking); + std::vector().swap(m_resume_data); + lazy_entry().swap(m_resume_entry); + return; + } + + state_updated(); + + if (m_resume_entry.type() == lazy_entry::dict_t) + { + using namespace libtorrent::detail; // for read_*_endpoint() + peer_id id(0); + + if (lazy_entry const* peers_entry = m_resume_entry.dict_find_string("peers")) + { + int num_peers = peers_entry->string_length() / (sizeof(address_v4::bytes_type) + 2); + char const* ptr = peers_entry->string_ptr(); + for (int i = 0; i < num_peers; ++i) + { + m_policy.add_peer(read_v4_endpoint(ptr) + , id, peer_info::resume_data, 0); + } + } + + if (lazy_entry const* banned_peers_entry = m_resume_entry.dict_find_string("banned_peers")) + { + int num_peers = banned_peers_entry->string_length() / (sizeof(address_v4::bytes_type) + 2); + char const* ptr = banned_peers_entry->string_ptr(); + for (int i = 0; i < num_peers; ++i) + { + policy::peer* p = m_policy.add_peer(read_v4_endpoint(ptr) + , id, peer_info::resume_data, 0); + if (p) m_policy.ban_peer(p); + } + } + +#if TORRENT_USE_IPV6 + if (lazy_entry const* peers6_entry = m_resume_entry.dict_find_string("peers6")) + { + int num_peers = peers6_entry->string_length() / (sizeof(address_v6::bytes_type) + 2); + char const* ptr = peers6_entry->string_ptr(); + for (int i = 0; i < num_peers; ++i) + { + m_policy.add_peer(read_v6_endpoint(ptr) + , id, peer_info::resume_data, 0); + } + } + + if (lazy_entry const* banned_peers6_entry = m_resume_entry.dict_find_string("banned_peers6")) + { + int num_peers = banned_peers6_entry->string_length() / (sizeof(address_v6::bytes_type) + 2); + char const* ptr = banned_peers6_entry->string_ptr(); + for (int i = 0; i < num_peers; ++i) + { + policy::peer* p = m_policy.add_peer(read_v6_endpoint(ptr) + , id, peer_info::resume_data, 0); + if (p) m_policy.ban_peer(p); + } + } +#endif + + // parse out "peers" from the resume data and add them to the peer list + if (lazy_entry const* peers_entry = m_resume_entry.dict_find_list("peers")) + { + for (int i = 0; i < peers_entry->list_size(); ++i) + { + lazy_entry const* e = peers_entry->list_at(i); + if (e->type() != lazy_entry::dict_t) continue; + std::string ip = e->dict_find_string_value("ip"); + int port = e->dict_find_int_value("port"); + if (ip.empty() || port == 0) continue; + error_code ec; + tcp::endpoint a(address::from_string(ip, ec), (unsigned short)port); + if (ec) continue; + m_policy.add_peer(a, id, peer_info::resume_data, 0); + } + } + + // parse out "banned_peers" and add them as banned + if (lazy_entry const* banned_peers_entry = m_resume_entry.dict_find_list("banned_peers")) + { + for (int i = 0; i < banned_peers_entry->list_size(); ++i) + { + lazy_entry const* e = banned_peers_entry->list_at(i); + if (e->type() != lazy_entry::dict_t) continue; + std::string ip = e->dict_find_string_value("ip"); + int port = e->dict_find_int_value("port"); + if (ip.empty() || port == 0) continue; + error_code ec; + tcp::endpoint a(address::from_string(ip, ec), (unsigned short)port); + if (ec) continue; + policy::peer* p = m_policy.add_peer(a, id, peer_info::resume_data, 0); + if (p) m_policy.ban_peer(p); + } + } + } + + // only report this error if the user actually provided resume data + if ((j.error || ret != 0) && !m_resume_data.empty() + && m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(fastresume_rejected_alert(get_handle(), j.error)); + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + if (ret != 0) + { + debug_log("fastresume data rejected: ret: %d (%d) %s" + , ret, j.error.value(), j.error.message().c_str()); + } +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + else + debug_log("fastresume data accepted"); +#endif +#endif + + // if ret != 0, it means we need a full check. We don't necessarily need + // that when the resume data check fails. For instance, if the resume data + // is incorrect, but we don't have any files, we skip the check and initialize + // the storage to not have anything. + if (ret == 0) + { + // there are either no files for this torrent + // or the resume_data was accepted + + if (!j.error && m_resume_entry.type() == lazy_entry::dict_t) + { + // parse have bitmask + lazy_entry const* pieces = m_resume_entry.dict_find("pieces"); + if (pieces && pieces->type() == lazy_entry::string_t + && int(pieces->string_length()) == m_torrent_file->num_pieces()) + { + char const* pieces_str = pieces->string_ptr(); + for (int i = 0, end(pieces->string_length()); i < end; ++i) + { + if (pieces_str[i] & 1) we_have(i); + if (m_seed_mode && (pieces_str[i] & 2)) m_verified.set_bit(i); + } + } + else + { + lazy_entry const* slots = m_resume_entry.dict_find("slots"); + if (slots && slots->type() == lazy_entry::list_t) + { + for (int i = 0; i < slots->list_size(); ++i) + { + int piece = slots->list_int_value_at(i, -1); + if (piece >= 0) we_have(piece); + } + } + } + + // parse unfinished pieces + int num_blocks_per_piece = + static_cast(torrent_file().piece_length()) / block_size(); + + if (lazy_entry const* unfinished_ent = m_resume_entry.dict_find_list("unfinished")) + { + for (int i = 0; i < unfinished_ent->list_size(); ++i) + { + lazy_entry const* e = unfinished_ent->list_at(i); + if (e->type() != lazy_entry::dict_t) continue; + int piece = e->dict_find_int_value("piece", -1); + if (piece < 0 || piece > torrent_file().num_pieces()) continue; + + if (m_picker->have_piece(piece)) + m_picker->we_dont_have(piece); + + std::string bitmask = e->dict_find_string_value("bitmask"); + if (bitmask.empty()) continue; + + const int num_bitmask_bytes = (std::max)(num_blocks_per_piece / 8, 1); + if ((int)bitmask.size() != num_bitmask_bytes) continue; + for (int k = 0; k < num_bitmask_bytes; ++k) + { + unsigned char bits = bitmask[k]; + int num_bits = (std::min)(num_blocks_per_piece - k*8, 8); + for (int b = 0; b < num_bits; ++b) + { + const int block = k * 8 + b; + if (bits & (1 << b)) + { + m_picker->mark_as_finished(piece_block(piece, block), 0); + if (m_picker->is_piece_finished(piece)) + async_verify_piece(piece, boost::bind(&torrent::piece_finished + , shared_from_this(), piece, _1)); + } + } + } + } + } + } + + files_checked(); + } + else + { + // either the fastresume data was rejected or there are + // some files + set_state(torrent_status::queued_for_checking); + if (should_check_files()) + queue_torrent_check(); + } + + std::vector().swap(m_resume_data); + lazy_entry().swap(m_resume_entry); + } + + void torrent::queue_torrent_check() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (m_queued_for_checking) return; + m_queued_for_checking = true; + m_ses.queue_check_torrent(shared_from_this()); + } + + void torrent::dequeue_torrent_check() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (!m_queued_for_checking) return; + m_queued_for_checking = false; + m_ses.dequeue_check_torrent(shared_from_this()); + } + + void torrent::force_recheck() + { + if (!valid_metadata()) return; + + // if the torrent is already queued to check its files + // don't do anything + if (should_check_files() + || m_state == torrent_status::checking_resume_data) + return; + + clear_error(); + + disconnect_all(errors::stopping_torrent); + stop_announcing(); + + m_owning_storage->async_release_files(); + if (!m_picker) m_picker.reset(new piece_picker()); + std::fill(m_file_progress.begin(), m_file_progress.end(), 0); + + int blocks_per_piece = (m_torrent_file->piece_length() + block_size() - 1) / block_size(); + int blocks_in_last_piece = ((m_torrent_file->total_size() % m_torrent_file->piece_length()) + + block_size() - 1) / block_size(); + m_picker->init(blocks_per_piece, blocks_in_last_piece, m_torrent_file->num_pieces()); + + // assume that we don't have anything + TORRENT_ASSERT(m_picker->num_have() == 0); + m_files_checked = false; + set_state(torrent_status::checking_resume_data); + + m_policy.recalculate_connect_candidates(); + + if (m_auto_managed && !is_finished()) + set_queue_position((std::numeric_limits::max)()); + + std::vector().swap(m_resume_data); + lazy_entry().swap(m_resume_entry); + m_storage->async_check_fastresume(&m_resume_entry + , boost::bind(&torrent::on_force_recheck + , shared_from_this(), _1, _2)); + } + + void torrent::on_force_recheck(int ret, disk_io_job const& j) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + state_updated(); + + if (ret == piece_manager::fatal_disk_error) + { + handle_disk_error(j); + return; + } + if (ret == 0) + { + // if there are no files, just start + files_checked(); + } + else + { + set_state(torrent_status::queued_for_checking); + if (should_check_files()) + queue_torrent_check(); + } + } + + void torrent::start_checking() + { + TORRENT_ASSERT(should_check_files()); + set_state(torrent_status::checking_files); + + m_storage->async_check_files(boost::bind( + &torrent::on_piece_checked + , shared_from_this(), _1, _2)); + } + + void torrent::on_piece_checked(int ret, disk_io_job const& j) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + state_updated(); + + if (ret == piece_manager::disk_check_aborted) + { + dequeue_torrent_check(); + pause(); + return; + } + if (ret == piece_manager::fatal_disk_error) + { + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(file_error_alert(j.error_file, get_handle(), j.error)); + } +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("fatal disk error: (%d) %s", j.error.value(), j.error.message().c_str()); +#endif + auto_managed(false); + pause(); + set_error(j.error, j.error_file); + // recalculate auto-managed torrents sooner + // in order to start checking the next torrent + m_ses.trigger_auto_manage(); + return; + } + + m_progress_ppm = size_type(j.piece) * 1000000 / torrent_file().num_pieces(); + + TORRENT_ASSERT(m_picker); + if (j.offset >= 0 && !m_picker->have_piece(j.offset)) + { + we_have(j.offset); + remove_time_critical_piece(j.offset); + } + + // we're not done checking yet + // this handler will be called repeatedly until + // we're done, or encounter a failure + if (ret == piece_manager::need_full_check) return; + + dequeue_torrent_check(); + files_checked(); + } + + void torrent::use_interface(std::string net_interfaces) + { + INVARIANT_CHECK; + m_net_interfaces.clear(); + + char* str = allocate_string_copy(net_interfaces.c_str()); + char* ptr = str; + + while (ptr) + { + char* space = strchr(ptr, ','); + if (space) *space++ = 0; + error_code ec; + address a(address::from_string(ptr, ec)); + ptr = space; + if (ec) continue; + m_net_interfaces.push_back(tcp::endpoint(a, 0)); + } + free(str); + } + + tcp::endpoint torrent::get_interface() const + { + if (m_net_interfaces.empty()) return tcp::endpoint(address_v4(), 0); + if (m_interface_index >= m_net_interfaces.size()) m_interface_index = 0; + return m_net_interfaces[m_interface_index++]; + } + + void torrent::on_tracker_announce_disp(boost::weak_ptr p + , error_code const& e) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("tracker::on_tracker_announce_disp"); +#endif + if (e) return; + boost::shared_ptr t = p.lock(); + if (!t) return; + t->on_tracker_announce(); + } + + void torrent::on_tracker_announce() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + m_waiting_tracker = false; + if (m_abort) return; + announce_with_tracker(); + } + + void torrent::lsd_announce() + { + if (m_abort) return; + + // if the files haven't been checked yet, we're + // not ready for peers. Except, if we don't have metadata, + // we need peers to download from + if (!m_files_checked && valid_metadata()) return; + + if (!m_announce_to_lsd) return; + + if (m_torrent_file->is_valid() + && (m_torrent_file->priv() + || (torrent_file().is_i2p() + && !settings().allow_i2p_mixed))) + return; + + if (is_paused()) return; + +#ifdef TORRENT_USE_OPENSSL + int port = is_ssl_torrent() ? m_ses.ssl_listen_port() : m_ses.listen_port(); +#else + int port = m_ses.listen_port(); +#endif + + // announce with the local discovery service + m_ses.announce_lsd(m_torrent_file->info_hash(), port + , m_ses.settings().broadcast_lsd && m_lsd_seq == 0); + ++m_lsd_seq; + } + +#ifndef TORRENT_DISABLE_DHT + + void torrent::dht_announce() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (!m_ses.m_dht) return; + if (!should_announce_dht()) return; + + TORRENT_ASSERT(m_allow_peers); + +#ifdef TORRENT_USE_OPENSSL + int port = is_ssl_torrent() ? m_ses.ssl_listen_port() : m_ses.listen_port(); +#else + int port = m_ses.listen_port(); +#endif + + boost::weak_ptr self(shared_from_this()); + + // if we're a seed, we tell the DHT for better scrape stats + int flags = is_seed() ? dht::dht_tracker::flag_seed : 0; + // if we allow incoming uTP connections, set the implied_port + // argument in the announce, this will make the DHT node use + // our source port in the packet as our listen port, which is + // likely more accurate when behind a NAT + if (settings().enable_incoming_utp) flags |= dht::dht_tracker::flag_implied_port; + + m_ses.m_dht->announce(m_torrent_file->info_hash() + , port, flags + , boost::bind(&torrent::on_dht_announce_response_disp, self, _1)); + } + + void torrent::on_dht_announce_response_disp(boost::weak_ptr t + , std::vector const& peers) + { + boost::shared_ptr tor = t.lock(); + if (!tor) return; + tor->on_dht_announce_response(peers); + } + + void torrent::on_dht_announce_response(std::vector const& peers) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (peers.empty()) return; + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(dht_reply_alert( + get_handle(), peers.size())); + } + + if (torrent_file().priv() || (torrent_file().is_i2p() + && !settings().allow_i2p_mixed)) return; + + std::for_each(peers.begin(), peers.end(), boost::bind( + &policy::add_peer, boost::ref(m_policy), _1, peer_id(0) + , peer_info::dht, 0)); + + do_connect_boost(); + } + +#endif + + void torrent::announce_with_tracker(tracker_request::event_t e + , address const& bind_interface) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + if (m_trackers.empty()) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("*** announce_with_tracker: no trackers"); +#endif + return; + } + + if (m_abort) e = tracker_request::stopped; + + // if we're not announcing to trackers, only allow + // stopping + if (e != tracker_request::stopped && !m_announce_to_trackers) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("*** announce_with_tracker: event != stopped && !m_announce_to_trackers"); +#endif + return; + } + + // if we're not allowing peers, there's no point in announcing + if (e != tracker_request::stopped && !m_allow_peers) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("*** announce_with_tracker: event != stopped && !m_allow_peers"); +#endif + return; + } + + TORRENT_ASSERT(m_allow_peers || e == tracker_request::stopped); + + if (e == tracker_request::none && is_finished() && !is_seed()) + e = tracker_request::paused; + + tracker_request req; + req.apply_ip_filter = m_apply_ip_filter && m_ses.m_settings.apply_ip_filter_to_trackers; + req.info_hash = m_torrent_file->info_hash(); + req.pid = m_ses.get_peer_id(); + req.downloaded = m_stat.total_payload_download() - m_total_failed_bytes; + req.uploaded = m_stat.total_payload_upload(); + req.corrupt = m_total_failed_bytes; + req.left = bytes_left(); + if (req.left == -1) req.left = 16*1024; +#ifdef TORRENT_USE_OPENSSL + // if this torrent contains an SSL certificate, make sure + // any SSL tracker presents a certificate signed by it + req.ssl_ctx = m_ssl_ctx.get(); +#endif + + // exclude redundant bytes if we should + if (!settings().report_true_downloaded) + req.downloaded -= m_total_redundant_bytes; + if (req.downloaded < 0) req.downloaded = 0; + + req.event = e; + error_code ec; + + // if we are aborting. we don't want any new peers + req.num_want = (req.event == tracker_request::stopped) + ?0:settings().num_want; + + // SSL torrents use their own listen socket +#ifdef TORRENT_USE_OPENSSL + if (is_ssl_torrent()) req.listen_port = m_ses.ssl_listen_port(); + else +#endif + req.listen_port = m_ses.listen_port(); + if (m_ses.m_key) + req.key = m_ses.m_key; + else + req.key = tracker_key(); + + ptime now = time_now_hires(); + + // the tier is kept as INT_MAX until we find the first + // tracker that works, then it's set to that tracker's + // tier. + int tier = INT_MAX; + + // have we sent an announce in this tier yet? + bool sent_announce = false; + + for (int i = 0; i < int(m_trackers.size()); ++i) + { + announce_entry& ae = m_trackers[i]; +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("*** announce with tracker: considering \"%s\" " + "[ announce_to_all_tiers: %d announce_to_all_trackers: %d" + " i->tier: %d tier: %d " + " is_working: %d fails: %d fail_limit: %d updating: %d" + " can_announce: %d sent_announce: %d ]" + , ae.url.c_str(), settings().announce_to_all_tiers + , settings().announce_to_all_trackers + , ae.tier, tier, ae.is_working(), ae.fails, ae.fail_limit + , ae.updating, ae.can_announce(now, is_seed()), sent_announce); +#endif + // if trackerid is not specified for tracker use default one, probably set explicitly + req.trackerid = ae.trackerid.empty() ? m_trackerid : ae.trackerid; + if (settings().announce_to_all_tiers + && !settings().announce_to_all_trackers + && sent_announce + && ae.tier <= tier + && tier != INT_MAX) + continue; + + if (ae.tier > tier && sent_announce && !settings().announce_to_all_tiers) break; + if (ae.is_working()) { tier = ae.tier; sent_announce = false; } + if (!ae.can_announce(now, is_seed())) + { + // this counts + if (ae.is_working()) sent_announce = true; + continue; + } + + req.url = ae.url; + req.event = e; + if (req.event == tracker_request::none) + { + if (!ae.start_sent) req.event = tracker_request::started; + else if (!ae.complete_sent && is_seed()) req.event = tracker_request::completed; + } + + if (!is_any(bind_interface)) req.bind_ip = bind_interface; + else req.bind_ip = m_ses.m_listen_interface.address(); + + if (settings().force_proxy) + { + // in force_proxy mode we don't talk directly to trackers + // we only allow trackers if there is a proxy and issue + // a warning if there isn't one + std::string protocol = req.url.substr(0, req.url.find(':')); + int proxy_type = m_ses.m_proxy.type; + + // http can run over any proxy, so as long as one is used + // it's OK. If no proxy is configured, skip this tracker + if ((protocol == "http" || protocol == "https") + && proxy_type == proxy_settings::none) + { + ae.next_announce = now + minutes(10); + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + anonymous_mode_alert(get_handle() + , anonymous_mode_alert::tracker_not_anonymous, req.url)); + } + continue; + } + + // for UDP, only socks5 and i2p proxies will work. + // if we're not using one of those proxues with a UDP + // tracker, skip it + if (protocol == "udp" + && proxy_type != proxy_settings::socks5 + && proxy_type != proxy_settings::socks5_pw + && proxy_type != proxy_settings::i2p_proxy) + { + ae.next_announce = now + minutes(10); + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + anonymous_mode_alert(get_handle() + , anonymous_mode_alert::tracker_not_anonymous, req.url)); + } + continue; + } + } +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("==> TRACKER REQUEST \"%s\" event: %s abort: %d" + , req.url.c_str() + , (req.event==tracker_request::stopped?"stopped" + :req.event==tracker_request::started?"started":"") + , m_abort); + + if (m_abort) + { + boost::shared_ptr tl(new aux::tracker_logger(m_ses)); + m_ses.m_tracker_manager.queue_request(m_ses.m_io_service, m_ses.m_half_open, req + , tracker_login(), tl); + } + else +#endif + { + m_ses.m_tracker_manager.queue_request(m_ses.m_io_service, m_ses.m_half_open, req + , tracker_login() , shared_from_this()); + } + + ae.updating = true; + ae.next_announce = now + seconds(20); + ae.min_announce = now + seconds(10); + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + tracker_announce_alert(get_handle(), req.url, req.event)); + } + + sent_announce = true; + if (ae.is_working() + && !settings().announce_to_all_trackers + && !settings().announce_to_all_tiers) + break; + } + update_tracker_timer(now); + } + + void torrent::scrape_tracker() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + m_last_scrape = 0; + + if (m_trackers.empty()) return; + + int i = m_last_working_tracker; + if (i == -1) i = 0; + + tracker_request req; + req.apply_ip_filter = m_apply_ip_filter && m_ses.m_settings.apply_ip_filter_to_trackers; + req.info_hash = m_torrent_file->info_hash(); + req.kind = tracker_request::scrape_request; + req.url = m_trackers[i].url; + req.bind_ip = m_ses.m_listen_interface.address(); + m_ses.m_tracker_manager.queue_request(m_ses.m_io_service, m_ses.m_half_open, req + , tracker_login(), shared_from_this()); + } + + void torrent::tracker_warning(tracker_request const& req, std::string const& msg) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(tracker_warning_alert(get_handle(), req.url, msg)); + } + + void torrent::tracker_scrape_response(tracker_request const& req + , int complete, int incomplete, int downloaded, int downloaders) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + TORRENT_ASSERT(req.kind == tracker_request::scrape_request); + + announce_entry* ae = find_tracker(req); + if (ae) + { + if (incomplete >= 0) ae->scrape_incomplete = incomplete; + if (complete >= 0) ae->scrape_complete = complete; + if (downloaded >= 0) ae->scrape_downloaded = downloaded; + + update_scrape_state(); + } + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(scrape_reply_alert( + get_handle(), incomplete, complete, req.url)); + } + } + + void torrent::update_scrape_state() + { + // loop over all trackers and find the largest numbers for each scrape field + // then update the torrent-wide understanding of number of downloaders and seeds + int complete = -1; + int incomplete = -1; + int downloaded = -1; + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + { + complete = (std::max)(i->scrape_complete, complete); + incomplete = (std::max)(i->scrape_incomplete, incomplete); + downloaded = (std::max)(i->scrape_downloaded, downloaded); + } + + if ((complete >= 0 && m_complete != complete) + || (incomplete >= 0 && m_incomplete != incomplete) + || (downloaded >= 0 && m_downloaded != downloaded)) + state_updated(); + + m_complete = complete; + m_incomplete = incomplete; + m_downloaded = downloaded; + } + + void torrent::tracker_response( + tracker_request const& r + , address const& tracker_ip // this is the IP we connected to + , std::list
const& tracker_ips // these are all the IPs it resolved to + , std::vector& peer_list + , int interval + , int min_interval + , int complete + , int incomplete + , int downloaded + , address const& external_ip + , const std::string& trackerid) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + TORRENT_ASSERT(r.kind == tracker_request::announce_request); + + if (external_ip != address() && !tracker_ips.empty()) + m_ses.set_external_address(external_ip, aux::session_impl::source_tracker + , *tracker_ips.begin()); + + ptime now = time_now(); + + if (interval < settings().min_announce_interval) + interval = settings().min_announce_interval; + + announce_entry* ae = find_tracker(r); + if (ae) + { + if (incomplete >= 0) ae->scrape_incomplete = incomplete; + if (complete >= 0) ae->scrape_complete = complete; + if (downloaded >= 0) ae->scrape_downloaded = downloaded; + if (!ae->start_sent && r.event == tracker_request::started) + ae->start_sent = true; + if (!ae->complete_sent && r.event == tracker_request::completed) + ae->complete_sent = true; + ae->verified = true; + ae->updating = false; + ae->fails = 0; + ae->next_announce = now + seconds(interval); + ae->min_announce = now + seconds(min_interval); + int tracker_index = ae - &m_trackers[0]; + m_last_working_tracker = prioritize_tracker(tracker_index); + + if ((!trackerid.empty()) && (ae->trackerid != trackerid)) + { + ae->trackerid = trackerid; + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(trackerid_alert(get_handle(), r.url, trackerid)); + } + + update_scrape_state(); + } + update_tracker_timer(now); + + if (complete >= 0 && incomplete >= 0) + m_last_scrape = 0; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("TRACKER RESPONSE\n" + "interval: %d\n" + "external ip: %s\n" + "we connected to: %s\n" + "peers:" + , interval + , print_address(external_ip).c_str() + , print_address(tracker_ip).c_str()); + + for (std::vector::const_iterator i = peer_list.begin(); + i != peer_list.end(); ++i) + { + debug_log(" %16s %5d %s %s", i->ip.c_str(), i->port + , i->pid.is_all_zeros()?"":to_hex(i->pid.to_string()).c_str() + , identify_client(i->pid).c_str()); + } +#endif + // for each of the peers we got from the tracker + for (std::vector::iterator i = peer_list.begin(); + i != peer_list.end(); ++i) + { + // don't make connections to ourself + if (i->pid == m_ses.get_peer_id()) + continue; + + error_code ec; + tcp::endpoint a(address::from_string(i->ip, ec), i->port); + + if (ec) + { + // assume this is because we got a hostname instead of + // an ip address from the tracker + +#if TORRENT_USE_I2P + char const* top_domain = strrchr(i->ip.c_str(), '.'); + if (top_domain && strcmp(top_domain, ".i2p") == 0 && m_ses.m_i2p_conn.is_open()) + { + // this is an i2p name, we need to use the sam connection + // to do the name lookup + /* + m_ses.m_i2p_conn.async_name_lookup(i->ip.c_str() + , boost::bind(&torrent::on_i2p_resolve + , shared_from_this(), _1, _2)); + */ + // it seems like you're not supposed to do a name lookup + // on the peers returned from the tracker, but just strip + // the .i2p and use it as a destination + i->ip.resize(i->ip.size() - 4); + m_policy.add_i2p_peer(i->ip.c_str(), peer_info::tracker, 0); + } + else +#endif + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("torrent::on_peer_name_lookup"); +#endif + tcp::resolver::query q(i->ip, to_string(i->port).elems); + m_host_resolver.async_resolve(q, + boost::bind(&torrent::on_peer_name_lookup, shared_from_this(), _1, _2, i->pid)); + } + } + else + { + // ignore local addresses from the tracker (unless the tracker is local too) + // there are 2 reasons to allow this: + // 1. retrackers are popular in russia, where an ISP runs a tracker within + // the AS (but not on the local network) giving out peers only from the + // local network + // 2. it might make sense to have a tracker extension in the future where + // trackers records a peer's internal and external IP, and match up + // peers on the same local network +// if (is_local(a.address()) && !is_local(tracker_ip)) continue; + m_policy.add_peer(a, i->pid, peer_info::tracker, 0); + } + } + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(tracker_reply_alert( + get_handle(), peer_list.size(), r.url)); + } + m_got_tracker_response = true; + + // we're listening on an interface type that was not used + // when talking to the tracker. If there is a matching interface + // type in the tracker IP list, make another tracker request + // using that interface + // in order to avoid triggering this case over and over, don't + // do it if the bind IP for the tracker request that just completed + // matches one of the listen interfaces, since that means this + // announce was the second one + // don't connect twice just to tell it we're stopping + + if (((!is_any(m_ses.m_ipv6_interface.address()) && tracker_ip.is_v4()) + || (!is_any(m_ses.m_ipv4_interface.address()) && tracker_ip.is_v6())) + && r.bind_ip != m_ses.m_ipv4_interface.address() + && r.bind_ip != m_ses.m_ipv6_interface.address() + && r.event != tracker_request::stopped) + { + std::list
::const_iterator i = std::find_if(tracker_ips.begin() + , tracker_ips.end(), boost::bind(&address::is_v4, _1) != tracker_ip.is_v4()); + if (i != tracker_ips.end()) + { + // the tracker did resolve to a different type of address, so announce + // to that as well + + // tell the tracker to bind to the opposite protocol type + address bind_interface = tracker_ip.is_v4() + ?m_ses.m_ipv6_interface.address() + :m_ses.m_ipv4_interface.address(); + announce_with_tracker(r.event, bind_interface); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("announce again using %s as the bind interface" + , print_address(bind_interface).c_str()); +#endif + } + } + + do_connect_boost(); + + state_updated(); + } + + void torrent::do_connect_boost() + { + if (!m_need_connect_boost) return; + + m_need_connect_boost = false; + // this is the first tracker response for this torrent + // instead of waiting one second for session_impl::on_tick() + // to be called, connect to a few peers immediately + int conns = (std::min)((std::min)((std::min)(m_ses.m_settings.torrent_connect_boost + , m_ses.m_settings.connections_limit - m_ses.num_connections()) + , m_ses.m_half_open.free_slots()) + , m_ses.m_boost_connections - m_ses.m_settings.connection_speed); + + while (want_more_peers() && conns > 0) + { + if (!m_policy.connect_one_peer(m_ses.session_time())) break; + // increase m_ses.m_boost_connections for each connection + // attempt. This will be deducted from the connect speed + // the next time session_impl::on_tick() is triggered + --conns; + ++m_ses.m_boost_connections; + } + + if (want_more_peers()) m_ses.prioritize_connections(shared_from_this()); + } + + ptime torrent::next_announce() const + { + return m_waiting_tracker?m_tracker_timer.expires_at():min_time(); + } + + void torrent::force_tracker_request(ptime t, int tracker_idx) + { + if (is_paused()) return; + if (tracker_idx == -1) + { + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + i->next_announce = (std::max)(t, i->min_announce) + seconds(1); + } + else + { + TORRENT_ASSERT(tracker_idx >= 0 && tracker_idx < int(m_trackers.size())); + if (tracker_idx < 0 || tracker_idx >= int(m_trackers.size())) + return; + announce_entry& e = m_trackers[tracker_idx]; + e.next_announce = (std::max)(t, e.min_announce) + seconds(1); + } + update_tracker_timer(time_now_hires()); + } + + void torrent::set_tracker_login( + std::string const& name + , std::string const& pw) + { + m_username = name; + m_password = pw; + } + +#if TORRENT_USE_I2P + void torrent::on_i2p_resolve(error_code const& ec, char const* dest) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + +#if defined TORRENT_LOGGING + if (ec) + debug_log("i2p_resolve error: %s", ec.message().c_str()); +#endif + if (ec || m_ses.is_aborted()) return; + + m_policy.add_i2p_peer(dest, peer_info::tracker, 0); + } +#endif + + void torrent::on_peer_name_lookup(error_code const& e, tcp::resolver::iterator host + , peer_id pid) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + +#if defined TORRENT_ASIO_DEBUGGING + complete_async("torrent::on_peer_name_lookup"); +#endif + +#if defined TORRENT_LOGGING + if (e) + debug_log("peer name lookup error: %s", e.message().c_str()); +#endif + if (e || host == tcp::resolver::iterator() || + m_ses.is_aborted()) return; + + if (m_apply_ip_filter + && m_ses.m_ip_filter.access(host->endpoint().address()) & ip_filter::blocked) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + error_code ec; + debug_log("blocked ip from tracker: %s", host->endpoint().address().to_string(ec).c_str()); +#endif + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(peer_blocked_alert(get_handle() + , host->endpoint().address(), peer_blocked_alert::ip_filter)); + return; + } + + m_policy.add_peer(*host, pid, peer_info::tracker, 0); + } + + size_type torrent::bytes_left() const + { + // if we don't have the metadata yet, we + // cannot tell how big the torrent is. + if (!valid_metadata()) return -1; + return m_torrent_file->total_size() + - quantized_bytes_done(); + } + + size_type torrent::quantized_bytes_done() const + { +// INVARIANT_CHECK; + + if (!valid_metadata()) return 0; + + if (m_torrent_file->num_pieces() == 0) + return 0; + + if (is_seed()) return m_torrent_file->total_size(); + + // if any piece hash fails, we'll be taken out of seed mode + // and m_seed_mode will be false + if (m_seed_mode) return m_torrent_file->total_size(); + + const int last_piece = m_torrent_file->num_pieces() - 1; + + size_type total_done + = size_type(num_have()) * m_torrent_file->piece_length(); + + // if we have the last piece, we have to correct + // the amount we have, since the first calculation + // assumed all pieces were of equal size + if (m_picker->have_piece(last_piece)) + { + int corr = m_torrent_file->piece_size(last_piece) + - m_torrent_file->piece_length(); + total_done += corr; + } + return total_done; + } + + // returns the number of bytes we are interested + // in for the given block. This returns block_size() + // for all blocks except the last one (if it's smaller + // than block_size()) and blocks that overlap a padding + // file + int torrent::block_bytes_wanted(piece_block const& p) const + { + file_storage const& fs = m_torrent_file->files(); + int piece_size = m_torrent_file->piece_size(p.piece_index); + int offset = p.block_index * block_size(); + if (m_padding == 0) return (std::min)(piece_size - offset, int(block_size())); + + std::vector files = fs.map_block( + p.piece_index, offset, (std::min)(piece_size - offset, int(block_size()))); + int ret = 0; + for (std::vector::iterator i = files.begin() + , end(files.end()); i != end; ++i) + { + if (fs.pad_file_at(i->file_index)) continue; + ret += i->size; + } + TORRENT_ASSERT(ret <= (std::min)(piece_size - offset, int(block_size()))); + return ret; + } + + // fills in total_wanted, total_wanted_done and total_done + void torrent::bytes_done(torrent_status& st, bool accurate) const + { + INVARIANT_CHECK; + + st.total_done = 0; + st.total_wanted_done = 0; + st.total_wanted = m_torrent_file->total_size(); + + TORRENT_ASSERT(st.total_wanted >= m_padding); + TORRENT_ASSERT(st.total_wanted >= 0); + + if (!valid_metadata() || m_torrent_file->num_pieces() == 0) + return; + + TORRENT_ASSERT(st.total_wanted >= size_type(m_torrent_file->piece_length()) + * (m_torrent_file->num_pieces() - 1)); + + const int last_piece = m_torrent_file->num_pieces() - 1; + const int piece_size = m_torrent_file->piece_length(); + + // if any piece hash fails, we'll be taken out of seed mode + // and m_seed_mode will be false + if (m_seed_mode || is_seed()) + { + st.total_done = m_torrent_file->total_size() - m_padding; + st.total_wanted_done = st.total_done; + st.total_wanted = st.total_done; + return; + } + + TORRENT_ASSERT(num_have() >= m_picker->num_have_filtered()); + st.total_wanted_done = size_type(num_have() - m_picker->num_have_filtered()) + * piece_size; + TORRENT_ASSERT(st.total_wanted_done >= 0); + + st.total_done = size_type(num_have()) * piece_size; + TORRENT_ASSERT(num_have() < m_torrent_file->num_pieces()); + + int num_filtered_pieces = m_picker->num_filtered() + + m_picker->num_have_filtered(); + int last_piece_index = m_torrent_file->num_pieces() - 1; + if (m_picker->piece_priority(last_piece_index) == 0) + { + st.total_wanted -= m_torrent_file->piece_size(last_piece_index); + --num_filtered_pieces; + } + st.total_wanted -= size_type(num_filtered_pieces) * piece_size; + + // if we have the last piece, we have to correct + // the amount we have, since the first calculation + // assumed all pieces were of equal size + if (m_picker->have_piece(last_piece)) + { + TORRENT_ASSERT(st.total_done >= piece_size); + int corr = m_torrent_file->piece_size(last_piece) + - piece_size; + TORRENT_ASSERT(corr <= 0); + TORRENT_ASSERT(corr > -piece_size); + st.total_done += corr; + if (m_picker->piece_priority(last_piece) != 0) + { + TORRENT_ASSERT(st.total_wanted_done >= piece_size); + st.total_wanted_done += corr; + } + } + TORRENT_ASSERT(st.total_wanted >= st.total_wanted_done); + + // this is expensive, we might not want to do it all the time + if (!accurate) return; + + // subtract padding files + if (m_padding > 0) + { + file_storage const& files = m_torrent_file->files(); + for (int i = 0; i < files.num_files(); ++i) + { + if (!files.pad_file_at(i)) continue; + peer_request p = files.map_file(i, 0, files.file_size(i)); + for (int j = p.piece; p.length > 0; ++j) + { + int deduction = (std::min)(p.length, piece_size - p.start); + bool done = m_picker->have_piece(j); + bool wanted = m_picker->piece_priority(j) > 0; + if (done) st.total_done -= deduction; + if (wanted) st.total_wanted -= deduction; + if (wanted && done) st.total_wanted_done -= deduction; + TORRENT_ASSERT(st.total_done >= 0); + TORRENT_ASSERT(st.total_wanted >= 0); + TORRENT_ASSERT(st.total_wanted_done >= 0); + p.length -= piece_size - p.start; + p.start = 0; + ++p.piece; + } + } + } + + TORRENT_ASSERT(st.total_done <= m_torrent_file->total_size() - m_padding); + TORRENT_ASSERT(st.total_wanted_done <= m_torrent_file->total_size() - m_padding); + TORRENT_ASSERT(st.total_wanted_done >= 0); + TORRENT_ASSERT(st.total_done >= st.total_wanted_done); + + const std::vector& dl_queue + = m_picker->get_download_queue(); + + const int blocks_per_piece = (piece_size + block_size() - 1) / block_size(); + + // look at all unfinished pieces and add the completed + // blocks to our 'done' counter + for (std::vector::const_iterator i = + dl_queue.begin(); i != dl_queue.end(); ++i) + { + int corr = 0; + int index = i->index; + // completed pieces are already accounted for + if (m_picker->have_piece(index)) continue; + TORRENT_ASSERT(i->finished <= m_picker->blocks_in_piece(index)); + +#if TORRENT_USE_ASSERTS + for (std::vector::const_iterator j = boost::next(i); + j != dl_queue.end(); ++j) + { + TORRENT_ASSERT(j->index != index); + } +#endif + + for (int j = 0; j < blocks_per_piece; ++j) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_ASSERT(m_picker->is_finished(piece_block(index, j)) + == (i->info[j].state == piece_picker::block_info::state_finished)); +#endif + if (i->info[j].state == piece_picker::block_info::state_finished) + { + corr += block_bytes_wanted(piece_block(index, j)); + } + TORRENT_ASSERT(corr >= 0); + TORRENT_ASSERT(index != last_piece || j < m_picker->blocks_in_last_piece() + || i->info[j].state != piece_picker::block_info::state_finished); + } + + st.total_done += corr; + if (m_picker->piece_priority(index) > 0) + st.total_wanted_done += corr; + } + + TORRENT_ASSERT(st.total_wanted <= m_torrent_file->total_size() - m_padding); + TORRENT_ASSERT(st.total_done <= m_torrent_file->total_size() - m_padding); + TORRENT_ASSERT(st.total_wanted_done <= m_torrent_file->total_size() - m_padding); + TORRENT_ASSERT(st.total_wanted_done >= 0); + TORRENT_ASSERT(st.total_done >= st.total_wanted_done); + + std::map downloading_piece; + for (const_peer_iterator i = begin(); i != end(); ++i) + { + peer_connection* pc = *i; + boost::optional p + = pc->downloading_piece_progress(); + if (!p) continue; + + if (m_picker->have_piece(p->piece_index)) + continue; + + piece_block block(p->piece_index, p->block_index); + if (m_picker->is_finished(block)) + continue; + + std::map::iterator dp + = downloading_piece.find(block); + if (dp != downloading_piece.end()) + { + if (dp->second < p->bytes_downloaded) + dp->second = p->bytes_downloaded; + } + else + { + downloading_piece[block] = p->bytes_downloaded; + } +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(p->bytes_downloaded <= p->full_block_bytes); + TORRENT_ASSERT(p->full_block_bytes == to_req(piece_block( + p->piece_index, p->block_index)).length); +#endif + } + for (std::map::iterator i = downloading_piece.begin(); + i != downloading_piece.end(); ++i) + { + int done = (std::min)(block_bytes_wanted(i->first), i->second); + st.total_done += done; + if (m_picker->piece_priority(i->first.piece_index) != 0) + st.total_wanted_done += done; + } + + TORRENT_ASSERT(st.total_done <= m_torrent_file->total_size() - m_padding); + TORRENT_ASSERT(st.total_wanted_done <= m_torrent_file->total_size() - m_padding); + +#ifdef TORRENT_DEBUG + + if (st.total_done >= m_torrent_file->total_size()) + { + // Thist happens when a piece has been downloaded completely + // but not yet verified against the hash + fprintf(stderr, "num_have: %d\nunfinished:\n", num_have()); + for (std::vector::const_iterator i = + dl_queue.begin(); i != dl_queue.end(); ++i) + { + fprintf(stderr, " %d ", i->index); + for (int j = 0; j < blocks_per_piece; ++j) + { + char const* state = i->info[j].state == piece_picker::block_info::state_finished ? "1" : "0"; + fputs(state, stderr); + } + fputs("\n", stderr); + } + + fputs("downloading pieces:\n", stderr); + + for (std::map::iterator i = downloading_piece.begin(); + i != downloading_piece.end(); ++i) + { + fprintf(stderr, " %d:%d %d\n", int(i->first.piece_index), int(i->first.block_index), i->second); + } + + } + + TORRENT_ASSERT(st.total_done <= m_torrent_file->total_size()); + TORRENT_ASSERT(st.total_wanted_done <= m_torrent_file->total_size()); + +#endif + + TORRENT_ASSERT(st.total_done >= st.total_wanted_done); + } + + // passed_hash_check + // 0: success, piece passed check + // -1: disk failure + // -2: piece failed check + void torrent::piece_finished(int index, int passed_hash_check) + { + TORRENT_ASSERT(m_ses.is_network_thread()); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("*** PIECE_FINISHED [ p: %d | chk: %s | size: %d ]" + , index, ((passed_hash_check == 0) + ?"passed":passed_hash_check == -1 + ?"disk failed":"failed") + , m_torrent_file->piece_size(index)); +#endif + + TORRENT_ASSERT(valid_metadata()); + + // it's possible to get here if the last piece was downloaded + // from peers and inserted with add_piece at the same time. + // if we're a seed, we won't have a piece picker, and can't continue + if (is_seed()) return; + + TORRENT_ASSERT(!m_picker->have_piece(index)); + + state_updated(); + + // even though the piece passed the hash-check + // it might still have failed being written to disk + // if so, piece_picker::write_failed() has been + // called, and the piece is no longer finished. + // in this case, we have to ignore the fact that + // it passed the check + if (!m_picker->is_piece_finished(index)) return; + + if (passed_hash_check == 0) + { + // the following call may cause picker to become invalid + // in case we just became a seed + piece_passed(index); + // if we're in seed mode, we just acquired this piece + // mark it as verified + if (m_seed_mode) verified(index); + } + else if (passed_hash_check == -2) + { + // piece_failed() will restore the piece + piece_failed(index); + } + else + { + TORRENT_ASSERT(passed_hash_check == -1); + m_picker->restore_piece(index); + restore_piece_state(index); + } + } + + void torrent::update_sparse_piece_prio(int i, int start, int end) + { + TORRENT_ASSERT(m_picker); + if (m_picker->have_piece(i) || m_picker->piece_priority(i) == 0) + return; + bool have_before = i == 0 || m_picker->have_piece(i - 1); + bool have_after = i == end - 1 || m_picker->have_piece(i + 1); + if (have_after && have_before) + m_picker->set_piece_priority(i, 7); + else if (have_after || have_before) + m_picker->set_piece_priority(i, 6); + } + + void torrent::we_have(int index) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + // update m_file_progress + TORRENT_ASSERT(m_picker); + TORRENT_ASSERT(!have_piece(index)); + TORRENT_ASSERT(!m_picker->have_piece(index)); + + const int piece_size = m_torrent_file->piece_length(); + size_type off = size_type(index) * piece_size; + int file_index = m_torrent_file->files().file_index_at_offset(off); + int size = m_torrent_file->piece_size(index); + file_storage const& fs = m_torrent_file->files(); + for (; size > 0; ++file_index) + { + size_type file_offset = off - fs.file_offset(file_index); + TORRENT_ASSERT(file_index != fs.num_files()); + TORRENT_ASSERT(file_offset <= fs.file_size(file_index)); + int add = (std::min)(fs.file_size(file_index) - file_offset, size_type(size)); + m_file_progress[file_index] += add; + + TORRENT_ASSERT(m_file_progress[file_index] + <= m_torrent_file->files().file_size(file_index)); + + if (m_file_progress[file_index] >= m_torrent_file->files().file_size(file_index)) + { + if (!m_torrent_file->files().pad_file_at(file_index)) + { + if (m_ses.m_alerts.should_post()) + { + // this file just completed, post alert + m_ses.m_alerts.post_alert(file_completed_alert(get_handle() + , file_index)); + } + } + } + size -= add; + off += add; + TORRENT_ASSERT(size >= 0); + } + + remove_time_critical_piece(index, true); + + m_picker->we_have(index); + } + + void torrent::piece_passed(int index) + { +// INVARIANT_CHECK; + TORRENT_ASSERT(m_ses.is_network_thread()); + + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_torrent_file->num_pieces()); +#ifdef TORRENT_DEBUG + // make sure all blocks were successfully written before we + // declare the piece as "we have". + piece_picker::downloading_piece dp; + m_picker->piece_info(index, dp); + int blocks_in_piece = m_picker->blocks_in_piece(index); + TORRENT_ASSERT(dp.finished == blocks_in_piece); + TORRENT_ASSERT(dp.writing == 0); + TORRENT_ASSERT(dp.requested == 0); + TORRENT_ASSERT(dp.index == index); +#endif + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(piece_finished_alert(get_handle() + , index)); + } + + m_need_save_resume_data = true; + state_updated(); + + remove_time_critical_piece(index, true); + + bool was_finished = m_picker->num_filtered() + num_have() + == torrent_file().num_pieces(); + + std::vector downloaders; + m_picker->get_downloaders(downloaders, index); + + // increase the trust point of all peers that sent + // parts of this piece. + std::set peers; + + // these policy::peer pointers are owned by m_policy and they may be + // invalidated if a peer disconnects. We cannot keep them across any + // significant operations, but we should use them right away + // ignore NULL pointers + std::remove_copy(downloaders.begin(), downloaders.end() + , std::inserter(peers, peers.begin()), (policy::peer*)0); + + for (std::set::iterator i = peers.begin() + , end(peers.end()); i != end; ++i) + { + policy::peer* p = static_cast(*i); + TORRENT_ASSERT(p != 0); + if (p == 0) continue; + TORRENT_ASSERT(p->in_use); + p->on_parole = false; + int trust_points = p->trust_points; + ++trust_points; + if (trust_points > 8) trust_points = 8; + p->trust_points = trust_points; + if (p->connection) + { + TORRENT_ASSERT(p->connection->m_in_use == 1337); + p->connection->received_valid_data(index); + } + } + + // announcing a piece may invalidate the policy::peer pointers + // so we can't use them anymore + + downloaders.clear(); + peers.clear(); + + we_have(index); + + for (peer_iterator i = m_connections.begin(); i != m_connections.end();) + { + intrusive_ptr p = *i; + ++i; + p->announce_piece(index); + } + + if (settings().max_sparse_regions > 0 + && m_picker->sparse_regions() > settings().max_sparse_regions) + { + // we have too many sparse regions. Prioritize pieces + // that won't introduce new sparse regions + // prioritize pieces that will reduce the number of sparse + // regions even higher + int start = m_picker->cursor(); + int end = m_picker->reverse_cursor(); + if (index > start) update_sparse_piece_prio(index - 1, start, end); + if (index < end - 1) update_sparse_piece_prio(index + 1, start, end); + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->on_piece_pass(index); + } TORRENT_CATCH (std::exception&) {} + } +#endif + + // since this piece just passed, we might have + // become uninterested in some peers where this + // was the last piece we were interested in + for (peer_iterator i = m_connections.begin(); + i != m_connections.end();) + { + peer_connection* p = *i; + // update_interest may disconnect the peer and + // invalidate the iterator + ++i; + // if we're not interested already, no need to check + if (!p->is_interesting()) continue; + // if the peer doesn't have the piece we just got, it + // wouldn't affect our interest + if (!p->has_piece(index)) continue; + p->update_interest(); + } + + if (!was_finished && is_finished()) + { + // torrent finished + // i.e. all the pieces we're interested in have + // been downloaded. Release the files (they will open + // in read only mode if needed) + finished(); + // if we just became a seed, picker is now invalid, since it + // is deallocated by the torrent once it starts seeding + } + + m_last_download = 0; + + if (m_share_mode) + recalc_share_mode(); + } + + void torrent::piece_failed(int index) + { + // if the last piece fails the peer connection will still + // think that it has received all of it until this function + // resets the download queue. So, we cannot do the + // invariant check here since it assumes: + // (total_done == m_torrent_file->total_size()) => is_seed() + INVARIANT_CHECK; + TORRENT_ASSERT(m_ses.is_network_thread()); + + TORRENT_ASSERT(m_storage); + TORRENT_ASSERT(m_storage->refcount() > 0); + TORRENT_ASSERT(m_picker.get()); + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_torrent_file->num_pieces()); + + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(hash_failed_alert(get_handle(), index)); + + // increase the total amount of failed bytes + add_failed_bytes(m_torrent_file->piece_size(index)); + + std::vector downloaders; + m_picker->get_downloaders(downloaders, index); + + // decrease the trust point of all peers that sent + // parts of this piece. + // first, build a set of all peers that participated + std::set peers; + std::copy(downloaders.begin(), downloaders.end(), std::inserter(peers, peers.begin())); + +#ifdef TORRENT_DEBUG + for (std::vector::iterator i = downloaders.begin() + , end(downloaders.end()); i != end; ++i) + { + policy::peer* p = (policy::peer*)*i; + if (p && p->connection) + { + p->connection->piece_failed = true; + } + } +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->on_piece_failed(index); + } TORRENT_CATCH (std::exception&) {} + } +#endif + + // did we receive this piece from a single peer? + bool single_peer = peers.size() == 1; + + for (std::set::iterator i = peers.begin() + , end(peers.end()); i != end; ++i) + { + policy::peer* p = static_cast(*i); + if (p == 0) continue; + TORRENT_ASSERT(p->in_use); + bool allow_disconnect = true; + if (p->connection) + { + TORRENT_ASSERT(p->connection->m_in_use == 1337); + + // the peer implementation can ask not to be disconnected. + // this is used for web seeds for instance, to instead of + // disconnecting, mark the file as not being haved. + allow_disconnect = p->connection->received_invalid_data(index, single_peer); + } + + if (m_ses.settings().use_parole_mode) + p->on_parole = true; + + int hashfails = p->hashfails; + int trust_points = p->trust_points; + + // we decrease more than we increase, to keep the + // allowed failed/passed ratio low. + trust_points -= 2; + ++hashfails; + if (trust_points < -7) trust_points = -7; + p->trust_points = trust_points; + if (hashfails > 255) hashfails = 255; + p->hashfails = hashfails; + + // either, we have received too many failed hashes + // or this was the only peer that sent us this piece. + // if we have failed more than 3 pieces from this peer, + // don't trust it regardless. + if (p->trust_points <= -7 + || (single_peer && allow_disconnect)) + { + // we don't trust this peer anymore + // ban it. + if (m_ses.m_alerts.should_post()) + { + peer_id pid(0); + if (p->connection) pid = p->connection->pid(); + m_ses.m_alerts.post_alert(peer_ban_alert( + get_handle(), p->ip(), pid)); + } + + // mark the peer as banned + m_policy.ban_peer(p); +#ifdef TORRENT_STATS + ++m_ses.m_banned_for_hash_failure; +#endif + + if (p->connection) + { +#ifdef TORRENT_LOGGING + debug_log("*** BANNING PEER: \"%s\" Too many corrupt pieces" + , print_endpoint(p->ip()).c_str()); +#endif +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + p->connection->peer_log("*** BANNING PEER: Too many corrupt pieces"); +#endif + p->connection->disconnect(errors::too_many_corrupt_pieces); + } + } + } + + // we have to let the piece_picker know that + // this piece failed the check as it can restore it + // and mark it as being interesting for download + m_picker->restore_piece(index); + + // we might still have outstanding requests to this + // piece that hasn't been received yet. If this is the + // case, we need to re-open the piece and mark any + // blocks we're still waiting for as requested + restore_piece_state(index); + + TORRENT_ASSERT(m_storage); + + TORRENT_ASSERT(m_picker->have_piece(index) == false); + +#ifdef TORRENT_DEBUG + for (std::vector::iterator i = downloaders.begin() + , end(downloaders.end()); i != end; ++i) + { + policy::peer* p = (policy::peer*)*i; + if (p && p->connection) + { + p->connection->piece_failed = false; + } + } +#endif + } + + void torrent::restore_piece_state(int index) + { + TORRENT_ASSERT(has_picker()); + for (peer_iterator i = m_connections.begin(); + i != m_connections.end(); ++i) + { + peer_connection* p = *i; + std::vector const& dq = p->download_queue(); + std::vector const& rq = p->request_queue(); + for (std::vector::const_iterator k = dq.begin() + , end(dq.end()); k != end; ++k) + { + if (k->timed_out || k->not_wanted) continue; + if (int(k->block.piece_index) != index) continue; + m_picker->mark_as_downloading(k->block, p->peer_info_struct() + , (piece_picker::piece_state_t)p->peer_speed()); + } + for (std::vector::const_iterator k = rq.begin() + , end(rq.end()); k != end; ++k) + { + if (int(k->block.piece_index) != index) continue; + m_picker->mark_as_downloading(k->block, p->peer_info_struct() + , (piece_picker::piece_state_t)p->peer_speed()); + } + } + } + + void torrent::abort() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + if (m_abort) return; + + m_abort = true; + + update_guage(); + + // if the torrent is paused, it doesn't need + // to announce with even=stopped again. + if (!is_paused()) + { + stop_announcing(); + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING || defined TORRENT_LOGGING + log_to_all_peers("ABORTING TORRENT"); +#endif + + // disconnect all peers and close all + // files belonging to the torrents + disconnect_all(errors::torrent_aborted); + + // post a message to the main thread to destruct + // the torrent object from there + if (m_owning_storage.get()) + { + m_storage->abort_disk_io(); + m_storage->async_release_files( + boost::bind(&torrent::on_cache_flushed, shared_from_this(), _1, _2)); + } + else + { + TORRENT_ASSERT(m_abort); + if (alerts().should_post()) + alerts().post_alert(cache_flushed_alert(get_handle())); + } + + dequeue_torrent_check(); + + if (m_state == torrent_status::checking_files) + set_state(torrent_status::queued_for_checking); + + m_owning_storage = 0; + m_host_resolver.cancel(); + } + + void torrent::super_seeding(bool on) + { + if (on == m_super_seeding) return; + + m_super_seeding = on; + + if (m_super_seeding) return; + + // disable super seeding for all peers + for (peer_iterator i = begin(); i != end(); ++i) + { + (*i)->superseed_piece(-1, -1); + } + } + + int torrent::get_piece_to_super_seed(bitfield const& bits) + { + // return a piece with low availability that is not in + // the bitfield and that is not currently being super + // seeded by any peer + TORRENT_ASSERT(m_super_seeding); + + // do a linear search from the first piece + int min_availability = 9999; + std::vector avail_vec; + for (int i = 0; i < m_torrent_file->num_pieces(); ++i) + { + if (bits[i]) continue; + + int availability = 0; + for (const_peer_iterator j = begin(); j != end(); ++j) + { + if ((*j)->super_seeded_piece(i)) + { + // avoid superseeding the same piece to more than one + // peer if we can avoid it. Do this by artificially + // increase the availability + availability = 999; + break; + } + if ((*j)->has_piece(i)) ++availability; + } + if (availability > min_availability) continue; + if (availability == min_availability) + { + avail_vec.push_back(i); + continue; + } + TORRENT_ASSERT(availability < min_availability); + min_availability = availability; + avail_vec.clear(); + avail_vec.push_back(i); + } + + return avail_vec[random() % avail_vec.size()]; + } + + void torrent::on_files_deleted(int ret, disk_io_job const& j) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + if (ret != 0) + { + alerts().post_alert(torrent_delete_failed_alert(get_handle(), j.error, m_torrent_file->info_hash())); + } + else + { + alerts().post_alert(torrent_deleted_alert(get_handle(), m_torrent_file->info_hash())); + } + } + + void torrent::on_files_released(int ret, disk_io_job const& j) + { +/* + TORRENT_ASSERT(m_ses.is_network_thread()); + + if (alerts().should_post()) + { + alerts().post_alert(torrent_paused_alert(get_handle())); + } +*/ + } + + void torrent::on_save_resume_data(int ret, disk_io_job const& j) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + if (!j.resume_data) + { + alerts().post_alert(save_resume_data_failed_alert(get_handle(), j.error)); + } + else + { + m_need_save_resume_data = false; + m_last_saved_resume = time(0); + write_resume_data(*j.resume_data); + alerts().post_alert(save_resume_data_alert(j.resume_data + , get_handle())); + state_updated(); + } + } + + void torrent::on_file_renamed(int ret, disk_io_job const& j) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + if (ret == 0) + { + if (alerts().should_post()) + alerts().post_alert(file_renamed_alert(get_handle(), j.str, j.piece)); + m_torrent_file->rename_file(j.piece, j.str); + } + else + { + if (alerts().should_post()) + alerts().post_alert(file_rename_failed_alert(get_handle() + , j.piece, j.error)); + } + } + + void torrent::on_torrent_paused(int ret, disk_io_job const& j) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + if (alerts().should_post()) + alerts().post_alert(torrent_paused_alert(get_handle())); + } + + std::string torrent::tracker_login() const + { + if (m_username.empty() && m_password.empty()) return ""; + return m_username + ":" + m_password; + } + + boost::uint32_t torrent::tracker_key() const + { + uintptr_t self = (uintptr_t)this; + uintptr_t ses = (uintptr_t)&m_ses; + sha1_hash h = hasher((char*)&self, sizeof(self)) + .update((char*)&m_storage, sizeof(m_storage)) + .update((char*)&ses, sizeof(ses)) + .final(); + unsigned char const* ptr = &h[0]; + return detail::read_uint32(ptr); + } + + void torrent::cancel_non_critical() + { + std::set time_critical; + for (std::deque::iterator i = m_time_critical_pieces.begin() + , end(m_time_critical_pieces.end()); i != end; ++i) + { + time_critical.insert(i->piece); + } + + for (std::set::iterator i + = m_connections.begin(), end(m_connections.end()); i != end; ++i) + { + // for each peer, go through its download and request queue + // and cancel everything, except pieces that are time critical + peer_connection* p = *i; + + std::vector dq = p->download_queue(); + for (std::vector::iterator k = dq.begin() + , end(dq.end()); k != end; ++k) + { + if (time_critical.count(k->block.piece_index)) continue; + if (k->not_wanted || k->timed_out) continue; + p->cancel_request(k->block, true); + } + + std::vector rq = p->request_queue(); + for (std::vector::const_iterator k = rq.begin() + , end(rq.end()); k != end; ++k) + { + if (time_critical.count(k->block.piece_index)) continue; + p->cancel_request(k->block, true); + } + } + } + + void torrent::set_piece_deadline(int piece, int t, int flags) + { + INVARIANT_CHECK; + + if (m_abort) + { + // failed + if (flags & torrent_handle::alert_when_available) + { + m_ses.m_alerts.post_alert(read_piece_alert( + get_handle(), piece, error_code(boost::system::errc::operation_canceled, get_system_category()))); + } + return; + } + + ptime deadline = time_now() + milliseconds(t); + + // if we already have the piece, no need to set the deadline. + // however, if the user asked to get the piece data back, we still + // need to read it and post it back to the user + if (is_seed() || m_picker->have_piece(piece)) + { + if (flags & torrent_handle::alert_when_available) + read_piece(piece); + return; + } + + // if this is the first time critical piece we add. in order to make it + // react quickly, cancel all the currently outstanding requests + if (m_time_critical_pieces.empty()) + { + // defer this by posting it to the end of the message queue. + // this gives the client a chance to specify multiple time critical + // pieces before libtorrent cancels requests + m_ses.m_io_service.post(boost::bind(&torrent::cancel_non_critical, this)); + } + + for (std::deque::iterator i = m_time_critical_pieces.begin() + , end(m_time_critical_pieces.end()); i != end; ++i) + { + if (i->piece != piece) continue; + i->deadline = deadline; + i->flags = flags; + + // resort i since deadline might have changed + while (boost::next(i) != m_time_critical_pieces.end() && i->deadline > boost::next(i)->deadline) + { + std::iter_swap(i, boost::next(i)); + ++i; + } + while (i != m_time_critical_pieces.begin() && i->deadline < boost::prior(i)->deadline) + { + std::iter_swap(i, boost::prior(i)); + --i; + } + // just in case this piece had priority 0 + m_picker->set_piece_priority(piece, 7); + return; + } + + time_critical_piece p; + p.first_requested = min_time(); + p.last_requested = min_time(); + p.flags = flags; + p.deadline = deadline; + p.peers = 0; + p.piece = piece; + std::deque::iterator i = std::upper_bound(m_time_critical_pieces.begin() + , m_time_critical_pieces.end(), p); + m_time_critical_pieces.insert(i, p); + + // just in case this piece had priority 0 + m_picker->set_piece_priority(piece, 7); + + piece_picker::downloading_piece pi; + m_picker->piece_info(piece, pi); + if (pi.requested == 0) return; + // this means we have outstanding requests (or queued + // up requests that haven't been sent yet). Promote them + // to deadline pieces immediately + std::vector downloaders; + m_picker->get_downloaders(downloaders, piece); + + int block = 0; + for (std::vector::iterator i = downloaders.begin() + , end(downloaders.end()); i != end; ++i, ++block) + { + policy::peer* p = (policy::peer*)*i; + if (p == 0 || p->connection == 0) continue; + p->connection->make_time_critical(piece_block(piece, block)); + } + } + + void torrent::reset_piece_deadline(int piece) + { + remove_time_critical_piece(piece); + } + + void torrent::remove_time_critical_piece(int piece, bool finished) + { + for (std::deque::iterator i = m_time_critical_pieces.begin() + , end(m_time_critical_pieces.end()); i != end; ++i) + { + if (i->piece != piece) continue; + if (finished) + { + if (i->flags & torrent_handle::alert_when_available) + { + read_piece(i->piece); + } + + // if first_requested is min_time(), it wasn't requested as a critical piece + // and we shouldn't adjust any average download times + if (i->first_requested != min_time()) + { + // update the average download time and average + // download time deviation + int dl_time = total_milliseconds(time_now() - i->first_requested); + + if (m_average_piece_time == 0) + { + m_average_piece_time = dl_time; + } + else + { + int diff = abs(int(dl_time - m_average_piece_time)); + if (m_piece_time_deviation == 0) m_piece_time_deviation = diff; + else m_piece_time_deviation = (m_piece_time_deviation * 9 + diff) / 10; + + m_average_piece_time = (m_average_piece_time * 9 + dl_time) / 10; + } + } + } + else if (i->flags & torrent_handle::alert_when_available) + { + // post an empty read_piece_alert to indicate it failed + m_ses.m_alerts.post_alert(read_piece_alert( + get_handle(), piece, error_code(boost::system::errc::operation_canceled, get_system_category()))); + } + if (has_picker()) m_picker->set_piece_priority(piece, 1); + m_time_critical_pieces.erase(i); + return; + } + } + + void torrent::clear_time_critical() + { + for (std::deque::iterator i = m_time_critical_pieces.begin(); + i != m_time_critical_pieces.end();) + { + if (i->flags & torrent_handle::alert_when_available) + { + // post an empty read_piece_alert to indicate it failed + m_ses.m_alerts.post_alert(read_piece_alert( + get_handle(), i->piece, error_code(boost::system::errc::operation_canceled, get_system_category()))); + } + if (has_picker()) m_picker->set_piece_priority(i->piece, 1); + i = m_time_critical_pieces.erase(i); + } + } + + // remove time critical pieces where priority is 0 + void torrent::remove_time_critical_pieces(std::vector const& priority) + { + for (std::deque::iterator i = m_time_critical_pieces.begin(); + i != m_time_critical_pieces.end();) + { + if (priority[i->piece] == 0) + { + if (i->flags & torrent_handle::alert_when_available) + { + // post an empty read_piece_alert to indicate it failed + m_ses.m_alerts.post_alert(read_piece_alert( + get_handle(), i->piece, error_code(boost::system::errc::operation_canceled, get_system_category()))); + } + i = m_time_critical_pieces.erase(i); + continue; + } + ++i; + } + } + + void torrent::piece_availability(std::vector& avail) const + { + INVARIANT_CHECK; + + TORRENT_ASSERT(valid_metadata()); + if (!has_picker()) + { + avail.clear(); + return; + } + + m_picker->get_availability(avail); + } + + void torrent::set_piece_priority(int index, int priority) + { +// INVARIANT_CHECK; + + TORRENT_ASSERT(valid_metadata()); + if (is_seed()) return; + + // this call is only valid on torrents with metadata + TORRENT_ASSERT(m_picker.get()); + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_torrent_file->num_pieces()); + if (index < 0 || index >= m_torrent_file->num_pieces()) return; + + bool was_finished = is_finished(); + bool filter_updated = m_picker->set_piece_priority(index, priority); + TORRENT_ASSERT(num_have() >= m_picker->num_have_filtered()); + if (filter_updated) + { + update_peer_interest(was_finished); + if (priority == 0) remove_time_critical_piece(index); + } + + } + + int torrent::piece_priority(int index) const + { +// INVARIANT_CHECK; + + TORRENT_ASSERT(valid_metadata()); + if (is_seed()) return 1; + + // this call is only valid on torrents with metadata + TORRENT_ASSERT(m_picker.get()); + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_torrent_file->num_pieces()); + if (index < 0 || index >= m_torrent_file->num_pieces()) return 0; + + return m_picker->piece_priority(index); + } + + void torrent::prioritize_pieces(std::vector const& pieces) + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + TORRENT_ASSERT(valid_metadata()); + if (is_seed()) return; + + TORRENT_ASSERT(m_picker.get()); + + int index = 0; + bool filter_updated = false; + bool was_finished = is_finished(); + for (std::vector::const_iterator i = pieces.begin() + , end(pieces.end()); i != end; ++i, ++index) + { + TORRENT_ASSERT(*i >= 0); + TORRENT_ASSERT(*i <= 7); + filter_updated |= m_picker->set_piece_priority(index, *i); + TORRENT_ASSERT(num_have() >= m_picker->num_have_filtered()); + } + if (filter_updated) + { + // we need to save this new state + m_need_save_resume_data = true; + + update_peer_interest(was_finished); + remove_time_critical_pieces(pieces); + } + + state_updated(); + } + + void torrent::piece_priorities(std::vector* pieces) const + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + TORRENT_ASSERT(valid_metadata()); + if (is_seed()) + { + pieces->clear(); + pieces->resize(m_torrent_file->num_pieces(), 1); + return; + } + + TORRENT_ASSERT(m_picker.get()); + m_picker->piece_priorities(*pieces); + } + + namespace + { + void set_if_greater(int& piece_prio, int file_prio) + { + if (file_prio > piece_prio) piece_prio = file_prio; + } + } + + void nop() {} + + void torrent::prioritize_files(std::vector const& files) + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + if (!valid_metadata() || is_seed()) return; + + // the bitmask need to have exactly one bit for every file + // in the torrent + TORRENT_ASSERT(int(files.size()) == m_torrent_file->num_files()); + + if (m_torrent_file->num_pieces() == 0) return; + + int limit = int(files.size()); + if (valid_metadata() && limit > m_torrent_file->num_files()) + limit = m_torrent_file->num_files(); + + if (int(m_file_priority.size()) < limit) + m_file_priority.resize(limit); + + std::copy(files.begin(), files.begin() + limit, m_file_priority.begin()); + + if (valid_metadata() && m_torrent_file->num_files() > int(m_file_priority.size())) + m_file_priority.resize(m_torrent_file->num_files(), 1); + + // storage may be NULL during shutdown + if (m_torrent_file->num_pieces() > 0 && m_storage) + { + filesystem().async_set_file_priority(m_file_priority + , boost::bind(&nop)); + } + + update_piece_priorities(); + } + + void torrent::set_file_priority(int index, int prio) + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + if (!valid_metadata() || is_seed()) return; + + if (index < 0 || index >= m_torrent_file->num_files()) return; + if (prio < 0) prio = 0; + else if (prio > 7) prio = 7; + if (int(m_file_priority.size()) <= index) + { + if (prio == 1) return; + m_file_priority.resize(m_torrent_file->num_files(), 1); + } + if (m_file_priority[index] == prio) return; + m_file_priority[index] = prio; + // stoage may be NULL during shutdown + if (m_storage) + { + filesystem().async_set_file_priority(m_file_priority + , boost::bind(&nop)); + } + update_piece_priorities(); + } + + int torrent::file_priority(int index) const + { + // this call is only valid on torrents with metadata + if (!valid_metadata()) return 1; + + if (index < 0 || index >= m_torrent_file->num_files()) return 0; + if (int(m_file_priority.size()) <= index) return 1; + return m_file_priority[index]; + } + + void torrent::file_priorities(std::vector* files) const + { + INVARIANT_CHECK; + if (!valid_metadata()) + { + files->resize(m_file_priority.size()); + std::copy(m_file_priority.begin(), m_file_priority.end(), files->begin()); + return; + } + + files->resize(m_torrent_file->num_files(), 1); + TORRENT_ASSERT(int(m_file_priority.size()) <= m_torrent_file->num_files()); + std::copy(m_file_priority.begin(), m_file_priority.end(), files->begin()); + } + + void torrent::update_piece_priorities() + { + INVARIANT_CHECK; + + if (m_torrent_file->num_pieces() == 0) return; + + size_type position = 0; + int piece_length = m_torrent_file->piece_length(); + // initialize the piece priorities to 0, then only allow + // setting higher priorities + std::vector pieces(m_torrent_file->num_pieces(), 0); + file_storage const& fs = m_torrent_file->files(); + for (int i = 0; i < fs.num_files(); ++i) + { + if (i >= m_torrent_file->num_files()) break; + size_type start = position; + size_type size = m_torrent_file->files().file_size(i); + if (size == 0) continue; + position += size; + if (m_file_priority[i] == 0) continue; + + // mark all pieces of the file with this file's priority + // but only if the priority is higher than the pieces + // already set (to avoid problems with overlapping pieces) + int start_piece = int(start / piece_length); + int last_piece = int((position - 1) / piece_length); + TORRENT_ASSERT(last_piece < int(pieces.size())); + // if one piece spans several files, we might + // come here several times with the same start_piece, end_piece + std::for_each(pieces.begin() + start_piece + , pieces.begin() + last_piece + 1 + , boost::bind(&set_if_greater, _1, m_file_priority[i])); + } + prioritize_pieces(pieces); + } + + // this is called when piece priorities have been updated + // updates the interested flag in peers + void torrent::update_peer_interest(bool was_finished) + { + for (peer_iterator i = begin(); i != end();) + { + peer_connection* p = *i; + // update_interest may disconnect the peer and + // invalidate the iterator + ++i; + p->update_interest(); + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("*** UPDATE_PEER_INTEREST [ finished: %d was_finished %d ]" + , is_finished(), was_finished); +#endif + + // the torrent just became finished + if (is_finished() && !was_finished) + { + finished(); + } + else if (!is_finished() && was_finished) + { + // if we used to be finished, but we aren't anymore + // we may need to connect to peers again + resume_download(); + } + } + + void torrent::filter_piece(int index, bool filter) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(valid_metadata()); + if (is_seed()) return; + + // this call is only valid on torrents with metadata + TORRENT_ASSERT(m_picker.get()); + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_torrent_file->num_pieces()); + + if (index < 0 || index >= m_torrent_file->num_pieces()) return; + + bool was_finished = is_finished(); + m_picker->set_piece_priority(index, filter ? 1 : 0); + update_peer_interest(was_finished); + } + + void torrent::filter_pieces(std::vector const& bitmask) + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + TORRENT_ASSERT(valid_metadata()); + if (is_seed()) return; + + TORRENT_ASSERT(m_picker.get()); + + bool was_finished = is_finished(); + int index = 0; + for (std::vector::const_iterator i = bitmask.begin() + , end(bitmask.end()); i != end; ++i, ++index) + { + if ((m_picker->piece_priority(index) == 0) == *i) continue; + if (*i) + m_picker->set_piece_priority(index, 0); + else + m_picker->set_piece_priority(index, 1); + } + update_peer_interest(was_finished); + } + + bool torrent::is_piece_filtered(int index) const + { + // this call is only valid on torrents with metadata + TORRENT_ASSERT(valid_metadata()); + if (is_seed()) return false; + + TORRENT_ASSERT(m_picker.get()); + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_torrent_file->num_pieces()); + + if (index < 0 || index >= m_torrent_file->num_pieces()) return true; + + return m_picker->piece_priority(index) == 0; + } + + void torrent::filtered_pieces(std::vector& bitmask) const + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + TORRENT_ASSERT(valid_metadata()); + if (is_seed()) + { + bitmask.clear(); + bitmask.resize(m_torrent_file->num_pieces(), false); + return; + } + + TORRENT_ASSERT(m_picker.get()); + m_picker->filtered_pieces(bitmask); + } + + void torrent::filter_files(std::vector const& bitmask) + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + if (!valid_metadata() || is_seed()) return; + + // the bitmask need to have exactly one bit for every file + // in the torrent + TORRENT_ASSERT((int)bitmask.size() == m_torrent_file->num_files()); + + if (int(bitmask.size()) != m_torrent_file->num_files()) return; + + size_type position = 0; + + if (m_torrent_file->num_pieces()) + { + int piece_length = m_torrent_file->piece_length(); + // mark all pieces as filtered, then clear the bits for files + // that should be downloaded + std::vector piece_filter(m_torrent_file->num_pieces(), true); + for (int i = 0; i < (int)bitmask.size(); ++i) + { + size_type start = position; + position += m_torrent_file->files().file_size(i); + // is the file selected for download? + if (!bitmask[i]) + { + // mark all pieces of the file as downloadable + int start_piece = int(start / piece_length); + int last_piece = int(position / piece_length); + // if one piece spans several files, we might + // come here several times with the same start_piece, end_piece + std::fill(piece_filter.begin() + start_piece, piece_filter.begin() + + last_piece + 1, false); + } + } + filter_pieces(piece_filter); + } + } + + bool has_empty_url(announce_entry const& e) { return e.url.empty(); } + + void torrent::replace_trackers(std::vector const& urls) + { + m_trackers.clear(); + std::remove_copy_if(urls.begin(), urls.end(), back_inserter(m_trackers) + , &has_empty_url); + + m_last_working_tracker = -1; + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + { + if (i->source == 0) i->source = announce_entry::source_client; + i->complete_sent = is_seed(); + } + + if (settings().prefer_udp_trackers) + prioritize_udp_trackers(); + + if (!m_trackers.empty()) announce_with_tracker(); + + m_need_save_resume_data = true; + } + + void torrent::prioritize_udp_trackers() + { + // look for udp-trackers + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + { + if (i->url.substr(0, 6) != "udp://") continue; + // now, look for trackers with the same hostname + // that is has higher priority than this one + // if we find one, swap with the udp-tracker + error_code ec; + std::string udp_hostname; + using boost::tuples::ignore; + boost::tie(ignore, ignore, udp_hostname, ignore, ignore) + = parse_url_components(i->url, ec); + for (std::vector::iterator j = m_trackers.begin(); + j != i; ++j) + { + std::string hostname; + boost::tie(ignore, ignore, hostname, ignore, ignore) + = parse_url_components(j->url, ec); + if (hostname != udp_hostname) continue; + if (j->url.substr(0, 6) == "udp://") continue; + using std::swap; + using std::iter_swap; + swap(i->tier, j->tier); + iter_swap(i, j); + break; + } + } + } + + bool torrent::add_tracker(announce_entry const& url) + { + std::vector::iterator k = std::find_if(m_trackers.begin() + , m_trackers.end(), boost::bind(&announce_entry::url, _1) == url.url); + if (k != m_trackers.end()) + { + k->source |= url.source; + return false; + } + k = std::upper_bound(m_trackers.begin(), m_trackers.end(), url + , boost::bind(&announce_entry::tier, _1) < boost::bind(&announce_entry::tier, _2)); + if (k - m_trackers.begin() < m_last_working_tracker) ++m_last_working_tracker; + k = m_trackers.insert(k, url); + if (k->source == 0) k->source = announce_entry::source_client; + if (!m_trackers.empty()) announce_with_tracker(); + return true; + } + + bool torrent::choke_peer(peer_connection& c) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(!c.is_choked()); + TORRENT_ASSERT(!c.ignore_unchoke_slots()); + TORRENT_ASSERT(m_num_uploads > 0); + if (!c.send_choke()) return false; + --m_num_uploads; + state_updated(); + return true; + } + + bool torrent::unchoke_peer(peer_connection& c, bool optimistic) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(!m_graceful_pause_mode); + TORRENT_ASSERT(c.is_choked()); + TORRENT_ASSERT(!c.ignore_unchoke_slots()); + // when we're unchoking the optimistic slots, we might + // exceed the limit temporarily while we're iterating + // over the peers + if (m_num_uploads >= m_max_uploads && !optimistic) return false; + if (!c.send_unchoke()) return false; + ++m_num_uploads; + state_updated(); + return true; + } + + void torrent::cancel_block(piece_block block) + { + INVARIANT_CHECK; + + for (peer_iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + (*i)->cancel_request(block); + } + } + +#ifdef TORRENT_USE_OPENSSL + std::string password_callback(int length, boost::asio::ssl::context::password_purpose p + , std::string pw) + { + if (p != boost::asio::ssl::context::for_reading) return ""; + return pw; + } + + // certificate is a filename to a .pem file which is our + // certificate. The certificate must be signed by the root + // cert of the torrent file. any peer we connect to or that + // connect to use must present a valid certificate signed + // by the torrent root cert as well + void torrent::set_ssl_cert(std::string const& certificate + , std::string const& private_key + , std::string const& dh_params + , std::string const& passphrase) + { + if (!m_ssl_ctx) + { + if (alerts().should_post()) + alerts().post_alert(torrent_error_alert(get_handle() + , error_code(errors::not_an_ssl_torrent))); + return; + } + + using boost::asio::ssl::context; + error_code ec; + m_ssl_ctx->set_password_callback(boost::bind(&password_callback, _1, _2, passphrase), ec); + if (ec) + { + if (alerts().should_post()) + alerts().post_alert(torrent_error_alert(get_handle(), ec)); + } + m_ssl_ctx->use_certificate_file(certificate, context::pem, ec); + if (ec) + { + if (alerts().should_post()) + alerts().post_alert(torrent_error_alert(get_handle(), ec)); + } + m_ssl_ctx->use_private_key_file(private_key, context::pem, ec); + if (ec) + { + if (alerts().should_post()) + alerts().post_alert(torrent_error_alert(get_handle(), ec)); + } + m_ssl_ctx->use_tmp_dh_file(dh_params, ec); + if (ec) + { + if (alerts().should_post()) + alerts().post_alert(torrent_error_alert(get_handle(), ec)); + } + } + + void torrent::set_ssl_cert_buffer(std::string const& certificate + , std::string const& private_key + , std::string const& dh_params) + { + if (!m_ssl_ctx) return; + +#if BOOST_VERSION < 105400 + if (alerts().should_post()) + alerts().post_alert(torrent_error_alert(get_handle() + , error_code(boost::system::errc::not_supported, get_system_category()))); +#else + boost::asio::const_buffer certificate_buf(certificate.c_str(), certificate.size()); + + using boost::asio::ssl::context; + error_code ec; + m_ssl_ctx->use_certificate(certificate_buf, context::pem, ec); + if (ec) + { + if (alerts().should_post()) + alerts().post_alert(torrent_error_alert(get_handle(), ec)); + } + + boost::asio::const_buffer private_key_buf(private_key.c_str(), private_key.size()); + m_ssl_ctx->use_private_key(private_key_buf, context::pem, ec); + if (ec) + { + if (alerts().should_post()) + alerts().post_alert(torrent_error_alert(get_handle(), ec)); + } + + boost::asio::const_buffer dh_params_buf(dh_params.c_str(), dh_params.size()); + m_ssl_ctx->use_tmp_dh(dh_params_buf, ec); + if (ec) + { + if (alerts().should_post()) + alerts().post_alert(torrent_error_alert(get_handle(), ec)); + } +#endif // BOOST_VERSION + } + +#endif + + void torrent::remove_peer(peer_connection* p) + { +// INVARIANT_CHECK; + + TORRENT_ASSERT(p != 0); + TORRENT_ASSERT(m_ses.is_network_thread()); + + peer_iterator i = m_connections.find(p); + if (i == m_connections.end()) + { + TORRENT_ASSERT(false); + return; + } + + if (ready_for_connections()) + { + TORRENT_ASSERT(p->associated_torrent().lock().get() == NULL + || p->associated_torrent().lock().get() == this); + + if (p->is_seed()) + { + if (m_picker.get()) + { + m_picker->dec_refcount_all(p); + } + } + else + { + if (m_picker.get()) + { + bitfield const& pieces = p->get_bitfield(); + TORRENT_ASSERT(pieces.count() <= int(pieces.size())); + m_picker->dec_refcount(pieces, p); + } + } + } + + if (!p->is_choked() && !p->ignore_unchoke_slots()) + { + --m_num_uploads; + m_ses.m_unchoke_time_scaler = 0; + } + + policy::peer* pp = p->peer_info_struct(); + if (pp) + { + if (pp->optimistically_unchoked) + m_ses.m_optimistic_unchoke_time_scaler = 0; + + TORRENT_ASSERT(pp->prev_amount_upload == 0); + TORRENT_ASSERT(pp->prev_amount_download == 0); + pp->prev_amount_download += p->statistics().total_payload_download() >> 10; + pp->prev_amount_upload += p->statistics().total_payload_upload() >> 10; + } + + m_policy.connection_closed(*p, m_ses.session_time()); + p->set_peer_info(0); + TORRENT_ASSERT(i != m_connections.end()); + m_connections.erase(i); + } + + void torrent::remove_web_seed(std::list::iterator web) + { + if (web->resolving) + { + web->removed = true; + return; + } + peer_connection * peer = web->peer_info.connection; + if (peer) + { + // if we have a connection for this web seed, we also need to + // disconnect it and clear its reference to the peer_info object + // that's part of the web_seed_entry we're about to remove + TORRENT_ASSERT(peer->m_in_use == 1337); + peer->disconnect(boost::asio::error::operation_aborted); + peer->set_peer_info(0); + } + if (has_picker()) picker().clear_peer(&web->peer_info); + + + m_web_seeds.erase(web); + } + + void torrent::connect_to_url_seed(std::list::iterator web) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + TORRENT_ASSERT(!web->resolving); + if (web->resolving) return; + + if (int(m_connections.size()) >= m_max_connections + || m_ses.num_connections() >= m_ses.settings().connections_limit) + return; + + std::string protocol; + std::string auth; + std::string hostname; + int port; + std::string path; + error_code ec; + boost::tie(protocol, auth, hostname, port, path) + = parse_url_components(web->url, ec); + if (port == -1) + { + port = protocol == "http" ? 80 : 443; + } + + if (ec) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("failed to parse web seed url: %s", ec.message().c_str()); +#endif + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), web->url, ec)); + } + // never try it again + remove_web_seed(web); + return; + } + + if (web->peer_info.banned) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("banned web seed: %s", web->url.c_str()); +#endif + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), web->url + , error_code(libtorrent::errors::peer_banned, get_libtorrent_category()))); + } + // never try it again + remove_web_seed(web); + return; + } + +#ifdef TORRENT_USE_OPENSSL + if (protocol != "http" && protocol != "https") +#else + if (protocol != "http") +#endif + { + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), web->url, errors::unsupported_url_protocol)); + } + // never try it again + remove_web_seed(web); + return; + } + + if (hostname.empty()) + { + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), web->url, errors::invalid_hostname)); + } + // never try it again + remove_web_seed(web); + return; + } + + if (port == 0) + { + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), web->url, errors::invalid_port)); + } + // never try it again + remove_web_seed(web); + return; + } + + if (m_ses.m_port_filter.access(port) & port_filter::blocked) + { + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), web->url, errors::port_blocked)); + } + // never try it again + remove_web_seed(web); + return; + } + + if (web->endpoint.port() != 0) + { + connect_web_seed(web, web->endpoint); + return; + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("resolving web seed: %s", web->url.c_str()); +#endif + + proxy_settings const& ps = m_ses.proxy(); + if (ps.type == proxy_settings::http + || ps.type == proxy_settings::http_pw) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("resolving proxy for web seed: %s", web->url.c_str()); +#endif + + // use proxy + web->resolving = true; + tcp::resolver::query q(ps.hostname, to_string(ps.port).elems); + m_host_resolver.async_resolve(q, + boost::bind(&torrent::on_proxy_name_lookup, shared_from_this(), _1, _2, web)); + } + else if (ps.proxy_hostnames + && (ps.type == proxy_settings::socks5 + || ps.type == proxy_settings::socks5_pw)) + { + connect_web_seed(web, tcp::endpoint(address(), port)); + } + else + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("resolving web seed: %s", web->url.c_str()); +#endif + + web->resolving = true; + tcp::resolver::query q(hostname, to_string(port).elems); + m_host_resolver.async_resolve(q, + boost::bind(&torrent::on_name_lookup, shared_from_this(), _1, _2, web + , tcp::endpoint())); + } + } + + void torrent::on_proxy_name_lookup(error_code const& e, tcp::resolver::iterator host + , std::list::iterator web) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + + TORRENT_ASSERT(web->resolving == true); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("completed resolve proxy hostname for: %s", web->url.c_str()); +#endif +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + if (e) + debug_log("proxy name lookup error: %s", e.message().c_str()); +#endif + web->resolving = false; + + if (web->removed) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("removed web seed"); +#endif + remove_web_seed(web); + return; + } + + if (m_abort) return; + + if (e || host == tcp::resolver::iterator()) + { + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), web->url, e)); + } + + // the name lookup failed for the http host. Don't try + // this host again + remove_web_seed(web); + return; + } + + if (m_ses.is_aborted()) return; + +#ifndef TORRENT_DISABLE_GEO_IP + int as = m_ses.as_for_ip(host->endpoint().address()); +#ifdef TORRENT_DEBUG + web->peer_info.inet_as_num = as; +#endif + web->peer_info.inet_as = m_ses.lookup_as(as); +#endif + + if (int(m_connections.size()) >= m_max_connections + || m_ses.num_connections() >= m_ses.settings().connections_limit) + return; + + tcp::endpoint a(host->endpoint()); + + using boost::tuples::ignore; + std::string hostname; + int port; + error_code ec; + std::string protocol; + boost::tie(protocol, ignore, hostname, port, ignore) + = parse_url_components(web->url, ec); + if (port == -1) port = protocol == "http" ? 80 : 443; + + if (ec) + { + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), web->url, ec)); + } + remove_web_seed(web); + return; + } + + if (m_apply_ip_filter + && m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked) + { + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(peer_blocked_alert(get_handle() + , a.address(), peer_blocked_alert::ip_filter)); + return; + } + + web->resolving = true; + tcp::resolver::query q(hostname, to_string(port).elems); + m_host_resolver.async_resolve(q, + boost::bind(&torrent::on_name_lookup, shared_from_this(), _1, _2, web, a)); + } + + void torrent::on_name_lookup(error_code const& e, tcp::resolver::iterator host + , std::list::iterator web, tcp::endpoint proxy) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + + TORRENT_ASSERT(web->resolving == true); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("completed resolve: %s", web->url.c_str()); +#endif + web->resolving = false; + if (web->removed) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("removed web seed"); +#endif + remove_web_seed(web); + return; + } + + if (m_abort) return; + + if (e || host == tcp::resolver::iterator()) + { + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(url_seed_alert(get_handle(), web->url, e)); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("*** HOSTNAME LOOKUP FAILED: %s: (%d) %s" + , web->url.c_str(), e.value(), e.message().c_str()); +#endif + + // unavailable, retry in 30 minutes + web->retry = time_now() + minutes(30); + return; + } + + tcp::endpoint a(host->endpoint()); + + // fill in the peer struct's address field + web->endpoint = a; + + if (int(m_connections.size()) >= m_max_connections + || m_ses.num_connections() >= m_ses.settings().connections_limit) + return; + + connect_web_seed(web, a); + } + + void torrent::connect_web_seed(std::list::iterator web, tcp::endpoint a) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (m_apply_ip_filter + && m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked) + { + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(peer_blocked_alert(get_handle() + , a.address(), peer_blocked_alert::ip_filter)); + return; + } + + TORRENT_ASSERT(web->resolving == false); + TORRENT_ASSERT(web->peer_info.connection == 0); + + web->endpoint = a; + if (a.address().is_v4()) + { + web->peer_info.addr = a.address().to_v4(); + web->peer_info.port = a.port(); + } + + if (is_paused()) return; + if (m_ses.is_aborted()) return; + + boost::shared_ptr s(new (std::nothrow) socket_type(m_ses.m_io_service)); + if (!s) return; + + void* userdata = 0; +#ifdef TORRENT_USE_OPENSSL + bool ssl = string_begins_no_case("https://", web->url.c_str()); + if (ssl) + { + userdata = m_ssl_ctx.get(); + if (!userdata) userdata = &m_ses.m_ssl_ctx; + } +#endif + bool ret = instantiate_connection(m_ses.m_io_service, m_ses.proxy(), *s, userdata, 0, true); + (void)ret; + TORRENT_ASSERT(ret); + + proxy_settings const& ps = m_ses.proxy(); + if (s->get()) + { + // the web seed connection will talk immediately to + // the proxy, without requiring CONNECT support + s->get()->set_no_connect(true); + } + + using boost::tuples::ignore; + std::string hostname; + error_code ec; + boost::tie(ignore, ignore, hostname, ignore, ignore) + = parse_url_components(web->url, ec); + if (ec) + { + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(url_seed_alert(get_handle(), web->url, ec)); + return; + } + + if (ps.proxy_hostnames + && (ps.type == proxy_settings::socks5 + || ps.type == proxy_settings::socks5_pw)) + { + // we're using a socks proxy and we're resolving + // hostnames through it + socks5_stream* str = +#ifdef TORRENT_USE_OPENSSL + ssl ? &s->get >()->next_layer() : +#endif + s->get(); + TORRENT_ASSERT(str); + + str->set_dst_name(hostname); + } + + setup_ssl_hostname(*s, hostname, ec); + if (ec) + { + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(url_seed_alert(get_handle(), web->url, ec)); + return; + } + + boost::intrusive_ptr c; + if (web->type == web_seed_entry::url_seed) + { + c = new (std::nothrow) web_peer_connection( + m_ses, shared_from_this(), s, a, *web); + } + else if (web->type == web_seed_entry::http_seed) + { + c = new (std::nothrow) http_seed_connection( + m_ses, shared_from_this(), s, a, *web); + } + if (!c) return; + +#if TORRENT_USE_ASSERTS + c->m_in_constructor = false; +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + boost::shared_ptr + pp((*i)->new_connection(c.get())); + if (pp) c->add_extension(pp); + } +#endif + + TORRENT_TRY + { + TORRENT_ASSERT(!c->m_in_constructor); + // add the newly connected peer to this torrent's peer list + m_connections.insert(boost::get_pointer(c)); + m_ses.m_connections.insert(c); + + TORRENT_ASSERT(!web->peer_info.connection); + web->peer_info.connection = c.get(); +#if TORRENT_USE_ASSERTS + web->peer_info.in_use = true; +#endif + + c->add_stat(size_type(web->peer_info.prev_amount_download) << 10 + , size_type(web->peer_info.prev_amount_upload) << 10); + web->peer_info.prev_amount_download = 0; + web->peer_info.prev_amount_upload = 0; +#if defined TORRENT_VERBOSE_LOGGING + debug_log("web seed connection started: %s", web->url.c_str()); +#endif + + c->start(); + + if (c->is_disconnecting()) return; + + m_ses.m_half_open.enqueue( + boost::bind(&peer_connection::on_connect, c, _1) + , boost::bind(&peer_connection::on_timeout, c) + , seconds(settings().peer_connect_timeout)); + } + TORRENT_CATCH (std::exception& e) + { + TORRENT_DECLARE_DUMMY(std::exception, e); + (void)e; +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("*** HOST NAME LOOKUP FAILED: %s", e.what()); +#endif + c->disconnect(errors::no_error, 1); + } + } + +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + namespace + { + unsigned long swap_bytes(unsigned long a) + { + return (a >> 24) | ((a & 0xff0000) >> 8) | ((a & 0xff00) << 8) | ((a & 0xff) << 24); + } + } + + void torrent::resolve_peer_country(boost::intrusive_ptr const& p) const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (m_resolving_country + || is_local(p->remote().address()) + || p->has_country() + || p->is_connecting() + || p->is_queued() + || p->in_handshake() + || p->remote().address().is_v6()) return; + + asio::ip::address_v4 reversed(swap_bytes(p->remote().address().to_v4().to_ulong())); + error_code ec; + tcp::resolver::query q(reversed.to_string(ec) + ".zz.countries.nerd.dk", "0"); + if (ec) + { + p->set_country("!!"); + return; + } + m_resolving_country = true; + m_host_resolver.async_resolve(q, + boost::bind(&torrent::on_country_lookup, shared_from_this(), _1, _2, p)); + } + + namespace + { + struct country_entry + { + int code; + char const* name; + }; + } + + void torrent::on_country_lookup(error_code const& error, tcp::resolver::iterator i + , intrusive_ptr p) const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + + m_resolving_country = false; + + if (m_abort) return; + + // must be ordered in increasing order + static const country_entry country_map[] = + { + { 4, "AF"}, { 8, "AL"}, { 10, "AQ"}, { 12, "DZ"}, { 16, "AS"} + , { 20, "AD"}, { 24, "AO"}, { 28, "AG"}, { 31, "AZ"}, { 32, "AR"} + , { 36, "AU"}, { 40, "AT"}, { 44, "BS"}, { 48, "BH"}, { 50, "BD"} + , { 51, "AM"}, { 52, "BB"}, { 56, "BE"}, { 60, "BM"}, { 64, "BT"} + , { 68, "BO"}, { 70, "BA"}, { 72, "BW"}, { 74, "BV"}, { 76, "BR"} + , { 84, "BZ"}, { 86, "IO"}, { 90, "SB"}, { 92, "VG"}, { 96, "BN"} + , {100, "BG"}, {104, "MM"}, {108, "BI"}, {112, "BY"}, {116, "KH"} + , {120, "CM"}, {124, "CA"}, {132, "CV"}, {136, "KY"}, {140, "CF"} + , {144, "LK"}, {148, "TD"}, {152, "CL"}, {156, "CN"}, {158, "TW"} + , {162, "CX"}, {166, "CC"}, {170, "CO"}, {174, "KM"}, {175, "YT"} + , {178, "CG"}, {180, "CD"}, {184, "CK"}, {188, "CR"}, {191, "HR"} + , {192, "CU"}, {203, "CZ"}, {204, "BJ"}, {208, "DK"}, {212, "DM"} + , {214, "DO"}, {218, "EC"}, {222, "SV"}, {226, "GQ"}, {231, "ET"} + , {232, "ER"}, {233, "EE"}, {234, "FO"}, {238, "FK"}, {239, "GS"} + , {242, "FJ"}, {246, "FI"}, {248, "AX"}, {250, "FR"}, {254, "GF"} + , {258, "PF"}, {260, "TF"}, {262, "DJ"}, {266, "GA"}, {268, "GE"} + , {270, "GM"}, {275, "PS"}, {276, "DE"}, {288, "GH"}, {292, "GI"} + , {296, "KI"}, {300, "GR"}, {304, "GL"}, {308, "GD"}, {312, "GP"} + , {316, "GU"}, {320, "GT"}, {324, "GN"}, {328, "GY"}, {332, "HT"} + , {334, "HM"}, {336, "VA"}, {340, "HN"}, {344, "HK"}, {348, "HU"} + , {352, "IS"}, {356, "IN"}, {360, "ID"}, {364, "IR"}, {368, "IQ"} + , {372, "IE"}, {376, "IL"}, {380, "IT"}, {384, "CI"}, {388, "JM"} + , {392, "JP"}, {398, "KZ"}, {400, "JO"}, {404, "KE"}, {408, "KP"} + , {410, "KR"}, {414, "KW"}, {417, "KG"}, {418, "LA"}, {422, "LB"} + , {426, "LS"}, {428, "LV"}, {430, "LR"}, {434, "LY"}, {438, "LI"} + , {440, "LT"}, {442, "LU"}, {446, "MO"}, {450, "MG"}, {454, "MW"} + , {458, "MY"}, {462, "MV"}, {466, "ML"}, {470, "MT"}, {474, "MQ"} + , {478, "MR"}, {480, "MU"}, {484, "MX"}, {492, "MC"}, {496, "MN"} + , {498, "MD"}, {500, "MS"}, {504, "MA"}, {508, "MZ"}, {512, "OM"} + , {516, "NA"}, {520, "NR"}, {524, "NP"}, {528, "NL"}, {530, "AN"} + , {533, "AW"}, {540, "NC"}, {548, "VU"}, {554, "NZ"}, {558, "NI"} + , {562, "NE"}, {566, "NG"}, {570, "NU"}, {574, "NF"}, {578, "NO"} + , {580, "MP"}, {581, "UM"}, {583, "FM"}, {584, "MH"}, {585, "PW"} + , {586, "PK"}, {591, "PA"}, {598, "PG"}, {600, "PY"}, {604, "PE"} + , {608, "PH"}, {612, "PN"}, {616, "PL"}, {620, "PT"}, {624, "GW"} + , {626, "TL"}, {630, "PR"}, {634, "QA"}, {634, "QA"}, {638, "RE"} + , {642, "RO"}, {643, "RU"}, {646, "RW"}, {654, "SH"}, {659, "KN"} + , {660, "AI"}, {662, "LC"}, {666, "PM"}, {670, "VC"}, {674, "SM"} + , {678, "ST"}, {682, "SA"}, {686, "SN"}, {690, "SC"}, {694, "SL"} + , {702, "SG"}, {703, "SK"}, {704, "VN"}, {705, "SI"}, {706, "SO"} + , {710, "ZA"}, {716, "ZW"}, {724, "ES"}, {732, "EH"}, {736, "SD"} + , {740, "SR"}, {744, "SJ"}, {748, "SZ"}, {752, "SE"}, {756, "CH"} + , {760, "SY"}, {762, "TJ"}, {764, "TH"}, {768, "TG"}, {772, "TK"} + , {776, "TO"}, {780, "TT"}, {784, "AE"}, {788, "TN"}, {792, "TR"} + , {795, "TM"}, {796, "TC"}, {798, "TV"}, {800, "UG"}, {804, "UA"} + , {807, "MK"}, {818, "EG"}, {826, "GB"}, {834, "TZ"}, {840, "US"} + , {850, "VI"}, {854, "BF"}, {858, "UY"}, {860, "UZ"}, {862, "VE"} + , {876, "WF"}, {882, "WS"}, {887, "YE"}, {891, "CS"}, {894, "ZM"} + }; + + if (error || i == tcp::resolver::iterator()) + { + // this is used to indicate that we shouldn't + // try to resolve it again + p->set_country("--"); + return; + } + + while (i != tcp::resolver::iterator() + && !i->endpoint().address().is_v4()) ++i; + if (i != tcp::resolver::iterator()) + { + // country is an ISO 3166 country code + int country = i->endpoint().address().to_v4().to_ulong() & 0xffff; + + // look up the country code in the map + const int size = sizeof(country_map)/sizeof(country_map[0]); + country_entry tmp = {country, ""}; + country_entry const* j = + std::lower_bound(country_map, country_map + size, tmp + , boost::bind(&country_entry::code, _1) < boost::bind(&country_entry::code, _2)); + if (j == country_map + size + || j->code != country) + { + // unknown country! + p->set_country("!!"); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("IP \"%s\" was mapped to unknown country: %d" + , print_address(p->remote().address()).c_str(), country); +#endif + return; + } + + p->set_country(j->name); + } + } +#endif + + void torrent::read_resume_data(lazy_entry const& rd) + { + m_total_uploaded = rd.dict_find_int_value("total_uploaded"); + m_total_downloaded = rd.dict_find_int_value("total_downloaded"); + m_active_time = rd.dict_find_int_value("active_time"); + m_finished_time = rd.dict_find_int_value("finished_time"); + m_seeding_time = rd.dict_find_int_value("seeding_time"); + m_last_seen_complete = rd.dict_find_int_value("last_seen_complete"); + m_complete = rd.dict_find_int_value("num_complete", 0xffffff); + m_incomplete = rd.dict_find_int_value("num_incomplete", 0xffffff); + m_downloaded = rd.dict_find_int_value("num_downloaded", 0xffffff); + + if (!m_override_resume_data) + { + int up_limit_ = rd.dict_find_int_value("upload_rate_limit", -1); + if (up_limit_ != -1) set_upload_limit(up_limit_); + + int down_limit_ = rd.dict_find_int_value("download_rate_limit", -1); + if (down_limit_ != -1) set_download_limit(down_limit_); + + int max_connections_ = rd.dict_find_int_value("max_connections", -1); + if (max_connections_ != -1) set_max_connections(max_connections_); + + int max_uploads_ = rd.dict_find_int_value("max_uploads", -1); + if (max_uploads_ != -1) set_max_uploads(max_uploads_); + + int seed_mode_ = rd.dict_find_int_value("seed_mode", -1); + if (seed_mode_ != -1) m_seed_mode = seed_mode_ && m_torrent_file->is_valid(); + + int super_seeding_ = rd.dict_find_int_value("super_seeding", -1); + if (super_seeding_ != -1) m_super_seeding = super_seeding_; + + int auto_managed_ = rd.dict_find_int_value("auto_managed", -1); + if (auto_managed_ != -1) m_auto_managed = auto_managed_; + + int sequential_ = rd.dict_find_int_value("sequential_download", -1); + if (sequential_ != -1) set_sequential_download(sequential_); + + int paused_ = rd.dict_find_int_value("paused", -1); + if (paused_ != -1) + { + set_allow_peers(!paused_); + m_announce_to_dht = !paused_; + m_announce_to_trackers = !paused_; + m_announce_to_lsd = !paused_; + } + int dht_ = rd.dict_find_int_value("announce_to_dht", -1); + if (dht_ != -1) m_announce_to_dht = dht_; + int lsd_ = rd.dict_find_int_value("announce_to_lsd", -1); + if (lsd_ != -1) m_announce_to_lsd = lsd_; + int track_ = rd.dict_find_int_value("announce_to_trackers", -1); + if (track_ != -1) m_announce_to_trackers = track_; + } + + if (m_seed_mode) + m_verified.resize(m_torrent_file->num_pieces(), false); + + m_last_scrape = rd.dict_find_int_value("last_scrape", 0); + m_last_download = rd.dict_find_int_value("last_download", 0); + m_last_upload = rd.dict_find_int_value("last_upload", 0); + + if (m_use_resume_save_path) + { + std::string p = rd.dict_find_string_value("save_path"); + if (!p.empty()) m_save_path = p; + } + + m_url = rd.dict_find_string_value("url"); + m_uuid = rd.dict_find_string_value("uuid"); + m_source_feed_url = rd.dict_find_string_value("feed"); + + if (!m_uuid.empty() || !m_url.empty()) + { + boost::shared_ptr me(shared_from_this()); + + // insert this torrent in the uuid index + m_ses.m_uuids.insert(std::make_pair(m_uuid.empty() + ? m_url : m_uuid, me)); + } + + // TODO: make this more generic to not just work if files have been + // renamed, but also if they have been merged into a single file for instance + // maybe use the same format as .torrent files and reuse some code from torrent_info + // The mapped_files needs to be read both in the network thread + // and in the disk thread, since they both have their own mapped files structures + // which are kept in sync + lazy_entry const* mapped_files = rd.dict_find_list("mapped_files"); + if (mapped_files && mapped_files->list_size() == m_torrent_file->num_files()) + { + for (int i = 0; i < m_torrent_file->num_files(); ++i) + { + std::string new_filename = mapped_files->list_string_value_at(i); + if (new_filename.empty()) continue; + m_torrent_file->rename_file(i, new_filename); + } + } + + m_added_time = rd.dict_find_int_value("added_time", m_added_time); + m_completed_time = rd.dict_find_int_value("completed_time", m_completed_time); + if (m_completed_time != 0 && m_completed_time < m_added_time) + m_completed_time = m_added_time; + + if (!m_seed_mode && !m_override_resume_data) + { + lazy_entry const* file_priority = rd.dict_find_list("file_priority"); + if (file_priority && file_priority->list_size() + == m_torrent_file->num_files()) + { + for (int i = 0; i < file_priority->list_size(); ++i) + m_file_priority[i] = file_priority->list_int_value_at(i, 1); + update_piece_priorities(); + } + } + + lazy_entry const* trackers = rd.dict_find_list("trackers"); + if (trackers) + { + if (!m_merge_resume_trackers) m_trackers.clear(); + int tier = 0; + for (int i = 0; i < trackers->list_size(); ++i) + { + lazy_entry const* tier_list = trackers->list_at(i); + if (tier_list == 0 || tier_list->type() != lazy_entry::list_t) + continue; + for (int j = 0; j < tier_list->list_size(); ++j) + { + announce_entry e(tier_list->list_string_value_at(j)); + if (std::find_if(m_trackers.begin(), m_trackers.end() + , boost::bind(&announce_entry::url, _1) == e.url) != m_trackers.end()) + continue; + e.tier = tier; + e.fail_limit = 0; + m_trackers.push_back(e); + } + ++tier; + } + std::sort(m_trackers.begin(), m_trackers.end(), boost::bind(&announce_entry::tier, _1) + < boost::bind(&announce_entry::tier, _2)); + + if (settings().prefer_udp_trackers) + prioritize_udp_trackers(); + } + + lazy_entry const* url_list = rd.dict_find_list("url-list"); + if (url_list) + { + for (int i = 0; i < url_list->list_size(); ++i) + { + std::string url = url_list->list_string_value_at(i); + if (url.empty()) continue; + if (m_torrent_file->num_files() > 1 && url[url.size()-1] != '/') url += '/'; + add_web_seed(url, web_seed_entry::url_seed); + } + } + + lazy_entry const* httpseeds = rd.dict_find_list("httpseeds"); + if (httpseeds) + { + for (int i = 0; i < httpseeds->list_size(); ++i) + { + std::string url = httpseeds->list_string_value_at(i); + if (url.empty()) continue; + add_web_seed(url, web_seed_entry::http_seed); + } + } + + if (m_torrent_file->is_merkle_torrent()) + { + lazy_entry const* mt = rd.dict_find_string("merkle tree"); + if (mt) + { + std::vector tree; + tree.resize(m_torrent_file->merkle_tree().size()); + std::memcpy(&tree[0], mt->string_ptr() + , (std::min)(mt->string_length(), int(tree.size()) * 20)); + if (mt->string_length() < int(tree.size()) * 20) + std::memset(&tree[0] + mt->string_length() / 20, 0 + , tree.size() - mt->string_length() / 20); + m_torrent_file->set_merkle_tree(tree); + } + else + { + // TODO: 0 if this is a merkle torrent and we can't + // restore the tree, we need to wipe all the + // bits in the have array, but not necessarily + // we might want to do a full check to see if we have + // all the pieces. This is low priority since almost + // no one uses merkle torrents + TORRENT_ASSERT(false); + } + } + } + + boost::intrusive_ptr torrent::get_torrent_copy() + { + if (!m_torrent_file->is_valid()) return boost::intrusive_ptr(); + + // copy the torrent_info object + return boost::intrusive_ptr(new torrent_info(*m_torrent_file)); + } + + void torrent::write_resume_data(entry& ret) const + { + using namespace libtorrent::detail; // for write_*_endpoint() + ret["file-format"] = "libtorrent resume file"; + ret["file-version"] = 1; + ret["libtorrent-version"] = LIBTORRENT_VERSION; + + ret["total_uploaded"] = m_total_uploaded; + ret["total_downloaded"] = m_total_downloaded; + + ret["active_time"] = m_active_time; + ret["finished_time"] = m_finished_time; + ret["seeding_time"] = m_seeding_time; + ret["last_seen_complete"] = m_last_seen_complete; + + ret["num_complete"] = m_complete; + ret["num_incomplete"] = m_incomplete; + ret["num_downloaded"] = m_downloaded; + + ret["sequential_download"] = m_sequential_download; + + ret["seed_mode"] = m_seed_mode; + ret["super_seeding"] = m_super_seeding; + + ret["added_time"] = m_added_time; + ret["completed_time"] = m_completed_time; + + ret["last_scrape"] = m_last_scrape; + ret["last_download"] = m_last_download; + ret["last_upload"] = m_last_upload; + + ret["save_path"] = m_save_path; + + if (!m_url.empty()) ret["url"] = m_url; + if (!m_uuid.empty()) ret["uuid"] = m_uuid; + if (!m_source_feed_url.empty()) ret["feed"] = m_source_feed_url; + + const sha1_hash& info_hash = torrent_file().info_hash(); + ret["info-hash"] = std::string((char*)info_hash.begin(), (char*)info_hash.end()); + + if (valid_metadata()) + { + if (m_magnet_link || (m_save_resume_flags & torrent_handle::save_info_dict)) + ret["info"] = bdecode(&torrent_file().metadata()[0] + , &torrent_file().metadata()[0] + torrent_file().metadata_size()); + } + + // blocks per piece + int num_blocks_per_piece = + static_cast(torrent_file().piece_length()) / block_size(); + ret["blocks per piece"] = num_blocks_per_piece; + + if (m_torrent_file->is_merkle_torrent()) + { + // we need to save the whole merkle hash tree + // in order to resume + std::string& tree_str = ret["merkle tree"].string(); + std::vector const& tree = m_torrent_file->merkle_tree(); + tree_str.resize(tree.size() * 20); + std::memcpy(&tree_str[0], &tree[0], tree.size() * 20); + } + + // if this torrent is a seed, we won't have a piece picker + // and there will be no half-finished pieces. + if (has_picker()) + { + const std::vector& q + = m_picker->get_download_queue(); + + // unfinished pieces + ret["unfinished"] = entry::list_type(); + entry::list_type& up = ret["unfinished"].list(); + + // info for each unfinished piece + for (std::vector::const_iterator i + = q.begin(); i != q.end(); ++i) + { + if (i->finished == 0) continue; + + entry piece_struct(entry::dictionary_t); + + // the unfinished piece's index + piece_struct["piece"] = i->index; + + std::string bitmask; + const int num_bitmask_bytes + = (std::max)(num_blocks_per_piece / 8, 1); + + for (int j = 0; j < num_bitmask_bytes; ++j) + { + unsigned char v = 0; + int bits = (std::min)(num_blocks_per_piece - j*8, 8); + for (int k = 0; k < bits; ++k) + v |= (i->info[j*8+k].state == piece_picker::block_info::state_finished) + ? (1 << k) : 0; + bitmask.append(1, v); + TORRENT_ASSERT(bits == 8 || j == num_bitmask_bytes - 1); + } + piece_struct["bitmask"] = bitmask; + // push the struct onto the unfinished-piece list + up.push_back(piece_struct); + } + } + + // save trackers + entry::list_type& tr_list = ret["trackers"].list(); + tr_list.push_back(entry::list_type()); + int tier = 0; + for (std::vector::const_iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + { + // don't save trackers we can't trust + // TODO: 1 save the send_stats state instead of throwing them away + // it may pose an issue when downgrading though + if (i->send_stats == false) continue; + if (i->tier == tier) + { + tr_list.back().list().push_back(i->url); + } + else + { + tr_list.push_back(entry::list_t); + tr_list.back().list().push_back(i->url); + tier = i->tier; + } + } + + // save web seeds + if (!m_web_seeds.empty()) + { + entry::list_type& url_list = ret["url-list"].list(); + entry::list_type& httpseed_list = ret["httpseeds"].list(); + for (std::list::const_iterator i = m_web_seeds.begin() + , end(m_web_seeds.end()); i != end; ++i) + { + if (i->type == web_seed_entry::url_seed) + url_list.push_back(i->url); + else if (i->type == web_seed_entry::http_seed) + httpseed_list.push_back(i->url); + } + } + + // write have bitmask + // the pieces string has one byte per piece. Each + // byte is a bitmask representing different properties + // for the piece + // bit 0: set if we have the piece + // bit 1: set if we have verified the piece (in seed mode) + entry::string_type& pieces = ret["pieces"].string(); + pieces.resize(m_torrent_file->num_pieces()); + if (is_seed()) + { + std::memset(&pieces[0], 1, pieces.size()); + } + else if (has_picker()) + { + for (int i = 0, end(pieces.size()); i < end; ++i) + pieces[i] = m_picker->have_piece(i) ? 1 : 0; + } + + if (m_seed_mode) + { + TORRENT_ASSERT(m_verified.size() == pieces.size()); + for (int i = 0, end(pieces.size()); i < end; ++i) + pieces[i] |= m_verified[i] ? 2 : 0; + } + + // write renamed files + // TODO: 0 make this more generic to not just work if files have been + // renamed, but also if they have been merged into a single file for instance. + // using file_base + if (&m_torrent_file->files() != &m_torrent_file->orig_files() + && m_torrent_file->files().num_files() == m_torrent_file->orig_files().num_files()) + { + entry::list_type& fl = ret["mapped_files"].list(); + file_storage const& fs = m_torrent_file->files(); + for (int i = 0; i < fs.num_files(); ++i) + { + fl.push_back(fs.file_path(i)); + } + } + + // write local peers + + std::back_insert_iterator peers(ret["peers"].string()); + std::back_insert_iterator banned_peers(ret["banned_peers"].string()); +#if TORRENT_USE_IPV6 + std::back_insert_iterator peers6(ret["peers6"].string()); + std::back_insert_iterator banned_peers6(ret["banned_peers6"].string()); +#endif + + // failcount is a 5 bit value + int max_failcount = (std::min)(settings().max_failcount, 31); + + int num_saved_peers = 0; + + for (policy::const_iterator i = m_policy.begin_peer() + , end(m_policy.end_peer()); i != end; ++i) + { + error_code ec; + policy::peer const* p = *i; + address addr = p->address(); + if (p->banned) + { +#if TORRENT_USE_IPV6 + if (addr.is_v6()) + { + write_address(addr, banned_peers6); + write_uint16(p->port, banned_peers6); + } + else +#endif + { + write_address(addr, banned_peers); + write_uint16(p->port, banned_peers); + } + continue; + } + + // we cannot save remote connection + // since we don't know their listen port + // unless they gave us their listen port + // through the extension handshake + // so, if the peer is not connectable (i.e. we + // don't know its listen port) or if it has + // been banned, don't save it. + if (!p->connectable) continue; + + // don't save peers that don't work + if (int(p->failcount) >= max_failcount) continue; + + // the more peers we've saved, the more picky we get + // about which ones are worth saving + if (num_saved_peers > 10 + && int (p->failcount) > 0 + && int(p->failcount) > (40 - (num_saved_peers - 10)) * max_failcount / 40) + continue; + + // if we have 40 peers, don't save any peers whom + // we've only heard from through the resume data + if (num_saved_peers > 40 && p->source == peer_info::resume_data) + continue; + +#if TORRENT_USE_IPV6 + if (addr.is_v6()) + { + write_address(addr, peers6); + write_uint16(p->port, peers6); + } + else +#endif + { + write_address(addr, peers); + write_uint16(p->port, peers); + } + ++num_saved_peers; + } + + ret["upload_rate_limit"] = upload_limit(); + ret["download_rate_limit"] = download_limit(); + ret["max_connections"] = max_connections(); + ret["max_uploads"] = max_uploads(); + ret["paused"] = is_torrent_paused(); + ret["announce_to_dht"] = m_announce_to_dht; + ret["announce_to_trackers"] = m_announce_to_trackers; + ret["announce_to_lsd"] = m_announce_to_lsd; + ret["auto_managed"] = m_auto_managed; + + // write piece priorities + entry::string_type& piece_priority = ret["piece_priority"].string(); + piece_priority.resize(m_torrent_file->num_pieces()); + if (is_seed()) + { + std::memset(&piece_priority[0], 1, pieces.size()); + } + else if (has_picker()) + { + for (int i = 0, end(piece_priority.size()); i < end; ++i) + piece_priority[i] = m_picker->piece_priority(i); + } + + // write file priorities + entry::list_type& file_priority = ret["file_priority"].list(); + file_priority.clear(); + for (int i = 0, end(m_file_priority.size()); i < end; ++i) + file_priority.push_back(m_file_priority[i]); + } + + void torrent::get_full_peer_list(std::vector& v) const + { + v.clear(); + v.reserve(m_policy.num_peers()); + for (policy::const_iterator i = m_policy.begin_peer(); + i != m_policy.end_peer(); ++i) + { + peer_list_entry e; + e.ip = (*i)->ip(); + e.flags = (*i)->banned ? peer_list_entry::banned : 0; + e.failcount = (*i)->failcount; + e.source = (*i)->source; + v.push_back(e); + } + } + + void torrent::get_peer_info(std::vector& v) + { + v.clear(); + for (peer_iterator i = begin(); + i != end(); ++i) + { + peer_connection* peer = *i; + TORRENT_ASSERT(peer->m_in_use == 1337); + + // incoming peers that haven't finished the handshake should + // not be included in this list + if (peer->associated_torrent().expired()) continue; + + v.push_back(peer_info()); + peer_info& p = v.back(); + + peer->get_peer_info(p); +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + if (resolving_countries()) + resolve_peer_country(intrusive_ptr(peer)); +#endif + } + } + + void torrent::get_download_queue(std::vector* queue) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + queue->clear(); + std::vector& blk = m_ses.m_block_info_storage; + blk.clear(); + + if (!valid_metadata() || !has_picker()) return; + piece_picker const& p = picker(); + std::vector const& q + = p.get_download_queue(); + + const int blocks_per_piece = m_picker->blocks_in_piece(0); + blk.resize(q.size() * blocks_per_piece); + // for some weird reason valgrind claims these are uninitialized + // unless it's zeroed out here (block_info has a construct that's + // supposed to initialize it) + if (!blk.empty()) + memset(&blk[0], 0, sizeof(blk[0]) * blk.size()); + + int counter = 0; + for (std::vector::const_iterator i + = q.begin(); i != q.end(); ++i, ++counter) + { + partial_piece_info pi; + pi.piece_state = (partial_piece_info::state_t)i->state; + pi.blocks_in_piece = p.blocks_in_piece(i->index); + pi.finished = (int)i->finished; + pi.writing = (int)i->writing; + pi.requested = (int)i->requested; + TORRENT_ASSERT(counter * blocks_per_piece + pi.blocks_in_piece <= int(blk.size())); + pi.blocks = &blk[counter * blocks_per_piece]; + int piece_size = int(torrent_file().piece_size(i->index)); + for (int j = 0; j < pi.blocks_in_piece; ++j) + { + block_info& bi = pi.blocks[j]; + bi.state = i->info[j].state; + bi.block_size = j < pi.blocks_in_piece - 1 ? block_size() + : piece_size - (j * block_size()); + bool complete = bi.state == block_info::writing + || bi.state == block_info::finished; + if (i->info[j].peer == 0) + { + bi.set_peer(tcp::endpoint()); + bi.bytes_progress = complete ? bi.block_size : 0; + } + else + { + policy::peer* p = static_cast(i->info[j].peer); + if (p->connection) + { + bi.set_peer(p->connection->remote()); + if (bi.state == block_info::requested) + { + boost::optional pbp + = p->connection->downloading_piece_progress(); + if (pbp && pbp->piece_index == i->index && pbp->block_index == j) + { + bi.bytes_progress = pbp->bytes_downloaded; + TORRENT_ASSERT(bi.bytes_progress <= bi.block_size); + } + else + { + bi.bytes_progress = 0; + } + } + else + { + bi.bytes_progress = complete ? bi.block_size : 0; + } + } + else + { + bi.set_peer(p->ip()); + bi.bytes_progress = complete ? bi.block_size : 0; + } + } + + pi.blocks[j].num_peers = i->info[j].num_peers; + } + pi.piece_index = i->index; + queue->push_back(pi); + } + + } + + bool torrent::connect_to_peer(policy::peer* peerinfo, bool ignore_limit) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + TORRENT_ASSERT(peerinfo); + TORRENT_ASSERT(peerinfo->connection == 0); + + peerinfo->last_connected = m_ses.session_time(); +#ifdef TORRENT_DEBUG + if (!settings().allow_multiple_connections_per_ip) + { + // this asserts that we don't have duplicates in the policy's peer list + peer_iterator i_ = std::find_if(m_connections.begin(), m_connections.end() + , boost::bind(&peer_connection::remote, _1) == peerinfo->ip()); +#if TORRENT_USE_I2P + TORRENT_ASSERT(i_ == m_connections.end() + || (*i_)->type() != peer_connection::bittorrent_connection + || peerinfo->is_i2p_addr); +#else + TORRENT_ASSERT(i_ == m_connections.end() + || (*i_)->type() != peer_connection::bittorrent_connection); +#endif + } +#endif + + // extend connect timeout by this many seconds + int timeout_extend = 0; + + TORRENT_ASSERT(want_more_peers() || ignore_limit); + TORRENT_ASSERT(m_ses.num_connections() < m_ses.settings().connections_limit || ignore_limit); + + tcp::endpoint a(peerinfo->ip()); + TORRENT_ASSERT(!m_apply_ip_filter + || (m_ses.m_ip_filter.access(peerinfo->address()) & ip_filter::blocked) == 0); + + boost::shared_ptr s(new socket_type(m_ses.m_io_service)); + +#if TORRENT_USE_I2P + bool i2p = peerinfo->is_i2p_addr; + if (i2p) + { + if (m_ses.i2p_proxy().hostname.empty()) + { + // we have an i2p torrent, but we're not connected to an i2p + // SAM proxy. + if (alerts().should_post()) + alerts().post_alert(i2p_alert(error_code(errors::no_i2p_router + , get_libtorrent_category()))); + return false; + } + + bool ret = instantiate_connection(m_ses.m_io_service, m_ses.i2p_proxy(), *s); + (void)ret; + TORRENT_ASSERT(ret); + s->get()->set_destination(static_cast(peerinfo)->destination); + s->get()->set_command(i2p_stream::cmd_connect); + s->get()->set_session_id(m_ses.m_i2p_conn.session_id()); + // i2p setups are slow + timeout_extend = 20; + } + else +#endif + { + // this is where we determine if we open a regular TCP connection + // or a uTP connection. If the m_utp_socket_manager pointer is not passed in + // we'll instantiate a TCP connection + utp_socket_manager* sm = 0; + + if (m_ses.m_settings.enable_outgoing_utp + && (!m_ses.m_settings.enable_outgoing_tcp + || peerinfo->supports_utp + || peerinfo->confirmed_supports_utp)) + sm = &m_ses.m_utp_socket_manager; + + // don't make a TCP connection if it's disabled + if (sm == 0 && !m_ses.m_settings.enable_outgoing_tcp) return false; + + void* userdata = 0; +#ifdef TORRENT_USE_OPENSSL + if (is_ssl_torrent() && m_ses.settings().ssl_listen != 0) + { + userdata = m_ssl_ctx.get(); + // SSL handshakes are slow + timeout_extend = 10; + } +#endif + + bool ret = instantiate_connection(m_ses.m_io_service, m_ses.proxy(), *s, userdata, sm, true); + (void)ret; + TORRENT_ASSERT(ret); + +#if defined TORRENT_USE_OPENSSL && BOOST_VERSION >= 104700 + if (is_ssl_torrent()) + { + // for ssl sockets, set the hostname + std::string host_name = to_hex(m_torrent_file->info_hash().to_string()); + +#define CASE(t) case socket_type_int_impl >::value: \ + s->get >()->set_host_name(host_name); break; + + switch (s->type()) + { + CASE(stream_socket) + CASE(socks5_stream) + CASE(http_stream) + CASE(utp_stream) + default: break; + }; + } +#undef CASE +#endif + } + + m_ses.setup_socket_buffers(*s); + + boost::intrusive_ptr c(new bt_peer_connection( + m_ses, s, a, peerinfo, m_ses.get_peer_id(), shared_from_this(), true)); + +#if TORRENT_USE_ASSERTS + c->m_in_constructor = false; +#endif + + c->add_stat(size_type(peerinfo->prev_amount_download) << 10 + , size_type(peerinfo->prev_amount_upload) << 10); + peerinfo->prev_amount_download = 0; + peerinfo->prev_amount_upload = 0; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + boost::shared_ptr pp((*i)->new_connection(c.get())); + if (pp) c->add_extension(pp); + } TORRENT_CATCH (std::exception&) {} + } +#endif + + // add the newly connected peer to this torrent's peer list + m_connections.insert(boost::get_pointer(c)); + m_ses.m_connections.insert(c); + m_policy.set_connection(peerinfo, c.get()); + c->start(); + + int timeout = settings().peer_connect_timeout; + if (peerinfo) timeout += 3 * peerinfo->failcount; + timeout += timeout_extend; + + TORRENT_TRY + { + m_ses.m_half_open.enqueue( + boost::bind(&peer_connection::on_connect, c, _1) + , boost::bind(&peer_connection::on_timeout, c) + , seconds(timeout)); + } + TORRENT_CATCH (std::exception&) + { + std::set::iterator i + = m_connections.find(boost::get_pointer(c)); + if (i != m_connections.end()) m_connections.erase(i); + c->disconnect(errors::no_error, 1); + return false; + } + + if (m_share_mode) + recalc_share_mode(); + + return peerinfo->connection; + } + + bool torrent::set_metadata(char const* metadata_buf, int metadata_size) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + if (m_torrent_file->is_valid()) return false; + + hasher h; + h.update(metadata_buf, metadata_size); + sha1_hash info_hash = h.final(); + + if (info_hash != m_torrent_file->info_hash()) + { + if (alerts().should_post()) + { + alerts().post_alert(metadata_failed_alert(get_handle() + , error_code(errors::mismatching_info_hash, get_libtorrent_category()))); + } + return false; + } + + lazy_entry metadata; + error_code ec; + int ret = lazy_bdecode(metadata_buf, metadata_buf + metadata_size, metadata, ec); + if (ret != 0 || !m_torrent_file->parse_info_section(metadata, ec, 0)) + { + // this means the metadata is correct, since we + // verified it against the info-hash, but we + // failed to parse it. Pause the torrent + if (alerts().should_post()) + { + alerts().post_alert(metadata_failed_alert(get_handle(), ec)); + } + set_error(errors::invalid_swarm_metadata, ""); + pause(); + return false; + } + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(metadata_received_alert( + get_handle())); + } + + // this makes the resume data "paused" and + // "auto_managed" fields be ignored. If the paused + // field is not ignored, the invariant check will fail + // since we will be paused but without having disconnected + // any of the peers. + m_override_resume_data = true; + + // we have to initialize the torrent before we start + // disconnecting redundant peers, otherwise we'll think + // we're a seed, because we have all 0 pieces + init(); + + // disconnect redundant peers + for (std::set::iterator i = m_connections.begin() + , end(m_connections.end()); i != end;) + { + std::set::iterator p = i++; + (*p)->disconnect_if_redundant(); + } + + m_need_save_resume_data = true; + + return true; + } + + bool connecting_time_compare(peer_connection const* lhs, peer_connection const* rhs) + { + bool lhs_connecting = lhs->is_connecting() && !lhs->is_disconnecting(); + bool rhs_connecting = rhs->is_connecting() && !rhs->is_disconnecting(); + if (lhs_connecting > rhs_connecting) return false; + if (lhs_connecting < rhs_connecting) return true; + + // a lower value of connected_time means it's been waiting + // longer. This is a less-than comparison, so if lhs has + // waited longer than rhs, we should return false. + return lhs->connected_time() > rhs->connected_time(); + } + + bool torrent::attach_peer(peer_connection* p) + { +// INVARIANT_CHECK; + +#ifdef TORRENT_USE_OPENSSL +#if BOOST_VERSION >= 104700 + if (is_ssl_torrent()) + { + // if this is an SSL torrent, don't allow non SSL peers on it + boost::shared_ptr s = p->get_socket(); + + // +#define SSL(t) socket_type_int_impl >::value: \ + ssl_conn = s->get >()->native_handle(); \ + break; + + SSL* ssl_conn = 0; + + switch (s->type()) + { + case SSL(stream_socket) + case SSL(socks5_stream) + case SSL(http_stream) + case SSL(utp_stream) + }; + +#undef SSL + + if (ssl_conn == 0) + { + // don't allow non SSL peers on SSL torrents + p->disconnect(errors::requires_ssl_connection); + return false; + } + + if (!m_ssl_ctx) + { + // we don't have a valid cert, don't accept any connection! + p->disconnect(errors::invalid_ssl_cert); + return false; + } + + if (SSL_get_SSL_CTX(ssl_conn) != m_ssl_ctx->native_handle()) + { + // if the SSL_CTX associated with this connection is + // not the one belonging to this torrent, the SSL handshake + // connected to one torrent, and the BitTorrent protocol + // to a different one. This is probably an attempt to circumvent + // access control. Don't allow it. + p->disconnect(errors::invalid_ssl_cert); + return false; + } + } +#else // BOOST_VERSION + if (is_ssl_torrent()) + { + p->disconnect(asio::error::operation_not_supported); + return false; + } +#endif +#else // TORRENT_USE_OPENSSL + if (is_ssl_torrent()) + { + // Don't accidentally allow seeding of SSL torrents, just + // because libtorrent wasn't built with SSL support + p->disconnect(errors::requires_ssl_connection); + return false; + } +#endif // TORRENT_USE_OPENSSL + + TORRENT_ASSERT(p != 0); + TORRENT_ASSERT(!p->is_outgoing()); + + m_has_incoming = true; + + if (m_apply_ip_filter + && m_ses.m_ip_filter.access(p->remote().address()) & ip_filter::blocked) + { + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(peer_blocked_alert(get_handle() + , p->remote().address(), peer_blocked_alert::ip_filter)); + p->disconnect(errors::banned_by_ip_filter); + return false; + } + + if ((m_state == torrent_status::queued_for_checking + || m_state == torrent_status::checking_files + || m_state == torrent_status::checking_resume_data) + && valid_metadata()) + { + p->disconnect(errors::torrent_not_ready); + return false; + } + + if (m_ses.m_connections.find(p) == m_ses.m_connections.end()) + { + p->disconnect(errors::peer_not_constructed); + return false; + } + + if (m_ses.is_aborted()) + { + p->disconnect(errors::session_closing); + return false; + } + + bool maybe_replace_peer = false; + + if (m_connections.size() >= m_max_connections) + { + // if more than 10% of the connections are outgoing + // connection attempts that haven't completed yet, + // disconnect one of them and let this incoming + // connection through. + if (m_num_connecting > m_max_connections / 10) + { + // find one of the connecting peers and disconnect it + // find any peer that's connecting (i.e. a half-open TCP connection) + // that's also not disconnecting + + // disconnect the peer that's been wating to establish a connection + // the longest + std::set::iterator i = std::max_element(begin(), end() + , &connecting_time_compare); + + if (i == end() || !(*i)->is_connecting() || (*i)->is_disconnecting()) + { + // this seems odd, but we might as well handle it + p->disconnect(errors::too_many_connections); + return false; + } + (*i)->disconnect(errors::too_many_connections); + + // if this peer was let in via connections slack, + // it has done its duty of causing the disconnection + // of another peer + p->peer_disconnected_other(); + } + else + { + maybe_replace_peer = true; + } + } + + TORRENT_TRY + { +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + boost::shared_ptr pp((*i)->new_connection(p)); + if (pp) p->add_extension(pp); + } +#endif + if (!m_policy.new_connection(*p, m_ses.session_time())) + { +#if defined TORRENT_LOGGING + debug_log("CLOSING CONNECTION \"%s\" peer list full" + , print_endpoint(p->remote()).c_str()); +#endif + p->disconnect(errors::too_many_connections); + return false; + } + } + TORRENT_CATCH (std::exception& e) + { + TORRENT_DECLARE_DUMMY(std::exception, e); + (void)e; +#if defined TORRENT_LOGGING + debug_log("CLOSING CONNECTION \"%s\" caught exception: %s" + , print_endpoint(p->remote()).c_str(), e.what()); +#endif + p->disconnect(errors::no_error); + return false; + } + TORRENT_ASSERT(m_connections.find(p) == m_connections.end()); + m_connections.insert(p); +#ifdef TORRENT_DEBUG + error_code ec; + TORRENT_ASSERT(p->remote() == p->get_socket()->remote_endpoint(ec) || ec); +#endif + + TORRENT_ASSERT(p->peer_info_struct() != NULL); + + // we need to do this after we've added the peer to the policy + // since that's when the peer is assigned its peer_info object, + // which holds the rank + if (maybe_replace_peer) + { + // now, find the lowest rank peer and disconnect that + // if it's lower rank than the incoming connection + peer_connection* peer = find_lowest_ranking_peer(); + + // TODO: 3 if peer is a really good peer, maybe we shouldn't disconnect it + if (peer && peer->peer_rank() < p->peer_rank()) + { + peer->disconnect(errors::too_many_connections); + p->peer_disconnected_other(); + } + else + { + p->disconnect(errors::too_many_connections); + // we have to do this here because from the peer's point of + // it wasn't really attached to the torrent, but we do need + // to let policy know we're removing it + remove_peer(p); + return false; + } + } + +#if TORRENT_USE_INVARIANT_CHECKS + m_policy.check_invariant(); +#endif + + if (m_share_mode) + recalc_share_mode(); + + return true; + } + + bool torrent::want_more_peers() const + { + return m_connections.size() < m_max_connections + && !is_paused() + && ((m_state != torrent_status::checking_files + && m_state != torrent_status::checking_resume_data + && m_state != torrent_status::queued_for_checking) + || !valid_metadata()) + && m_policy.num_connect_candidates() > 0 + && !m_abort + && (m_ses.settings().seeding_outgoing_connections + || (m_state != torrent_status::seeding + && m_state != torrent_status::finished)); + } + + void torrent::disconnect_all(error_code const& ec) + { +// doesn't work with the !m_allow_peers -> m_num_peers == 0 condition +// INVARIANT_CHECK; + + while (!m_connections.empty()) + { + peer_connection* p = *m_connections.begin(); + TORRENT_ASSERT(p->associated_torrent().lock().get() == this); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + p->peer_log("*** CLOSING CONNECTION \"%s\"", ec.message().c_str()); +#endif +#if TORRENT_USE_ASSERTS + std::size_t size = m_connections.size(); +#endif + if (p->is_disconnecting()) + m_connections.erase(m_connections.begin()); + else + p->disconnect(ec); + TORRENT_ASSERT(m_connections.size() <= size); + } + } + + // this returns true if lhs is a better disconnect candidate than rhs + bool compare_disconnect_peer(peer_connection const* lhs, peer_connection const* rhs) + { + // prefer to disconnect peers that are already disconnecting + if (lhs->is_disconnecting() != rhs->is_disconnecting()) + return lhs->is_disconnecting(); + + // prefer to disconnect peers we're not interested in + if (lhs->is_interesting() != rhs->is_interesting()) + return rhs->is_interesting(); + + // prefer to disconnect peers that are not seeds + if (lhs->is_seed() != rhs->is_seed()) + return rhs->is_seed(); + + // prefer to disconnect peers that are on parole + if (lhs->on_parole() != rhs->on_parole()) + return lhs->on_parole(); + + // prefer to disconnect peers that send data at a lower rate + size_type lhs_transferred = lhs->statistics().total_payload_download(); + size_type rhs_transferred = rhs->statistics().total_payload_download(); + + ptime now = time_now(); + size_type lhs_time_connected = total_seconds(now - lhs->connected_time()); + size_type rhs_time_connected = total_seconds(now - rhs->connected_time()); + + lhs_transferred /= lhs_time_connected + 1; + rhs_transferred /= (rhs_time_connected + 1); + if (lhs_transferred != rhs_transferred) + return lhs_transferred < rhs_transferred; + + // prefer to disconnect peers that chokes us + if (lhs->is_choked() != rhs->is_choked()) + return lhs->is_choked(); + + return lhs->last_received() < rhs->last_received(); + } + + int torrent::disconnect_peers(int num, error_code const& ec) + { + INVARIANT_CHECK; + +#ifdef TORRENT_DEBUG + for (std::set::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + // make sure this peer is not a dangling pointer + TORRENT_ASSERT(m_ses.has_peer(*i)); + } +#endif + int ret = 0; + while (ret < num && !m_connections.empty()) + { + std::set::iterator i = std::min_element( + m_connections.begin(), m_connections.end(), compare_disconnect_peer); + + peer_connection* p = *i; + ++ret; + TORRENT_ASSERT(p->associated_torrent().lock().get() == this); +#if TORRENT_USE_ASSERTS + int num_conns = m_connections.size(); +#endif + p->disconnect(ec); + TORRENT_ASSERT(int(m_connections.size()) == num_conns - 1); + } + + return ret; + } + + int torrent::bandwidth_throttle(int channel) const + { + return m_bandwidth_channel[channel].throttle(); + } + + // called when torrent is finished (all interesting + // pieces have been downloaded) + void torrent::finished() + { + INVARIANT_CHECK; + + TORRENT_ASSERT(is_finished()); + TORRENT_ASSERT(m_state != torrent_status::finished && m_state != torrent_status::seeding); + + set_state(torrent_status::finished); + set_queue_position(-1); + + // we have to call completed() before we start + // disconnecting peers, since there's an assert + // to make sure we're cleared the piece picker + if (is_seed()) completed(); + + send_upload_only(); + + state_updated(); + + if (m_completed_time == 0) + m_completed_time = time(0); + + // disconnect all seeds + if (settings().close_redundant_connections) + { + // TODO: 1 should disconnect all peers that have the pieces we have + // not just seeds. It would be pretty expensive to check all pieces + // for all peers though + std::vector seeds; + for (peer_iterator i = m_connections.begin(); + i != m_connections.end(); ++i) + { + peer_connection* p = *i; + TORRENT_ASSERT(p->associated_torrent().lock().get() == this); + if (p->upload_only()) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + p->peer_log("*** SEED, CLOSING CONNECTION"); +#endif + seeds.push_back(p); + } + } + std::for_each(seeds.begin(), seeds.end() + , boost::bind(&peer_connection::disconnect, _1, errors::torrent_finished, 0)); + } + + if (m_abort) return; + + m_policy.recalculate_connect_candidates(); + + TORRENT_ASSERT(m_storage); + // we need to keep the object alive during this operation + m_storage->async_release_files( + boost::bind(&torrent::on_files_released, shared_from_this(), _1, _2)); + + // this torrent just completed downloads, which means it will fall + // under a different limit with the auto-manager. Make sure we + // update auto-manage torrents in that case + if (m_auto_managed) + m_ses.trigger_auto_manage(); + } + + // this is called when we were finished, but some files were + // marked for downloading, and we are no longer finished + void torrent::resume_download() + { + INVARIANT_CHECK; + + if (m_state == torrent_status::checking_resume_data + || m_state == torrent_status::checking_files + || m_state == torrent_status::allocating) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("*** RESUME_DOWNLOAD [ skipping, state: %d ]" + , int(m_state)); +#endif + return; + } + + TORRENT_ASSERT(!is_finished()); + set_state(torrent_status::downloading); + set_queue_position((std::numeric_limits::max)()); + m_policy.recalculate_connect_candidates(); + + m_completed_time = 0; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("*** RESUME_DOWNLOAD"); +#endif + send_upload_only(); + } + + // called when torrent is complete (all pieces downloaded) + void torrent::completed() + { + m_picker.reset(); + + set_state(torrent_status::seeding); + if (!m_announcing) return; + + ptime now = time_now(); + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + { + if (i->complete_sent) continue; + i->next_announce = now; + i->min_announce = now; + } + announce_with_tracker(); + } + + // this will move the tracker with the given index + // to a prioritized position in the list (move it towards + // the begining) and return the new index to the tracker. + int torrent::prioritize_tracker(int index) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < int(m_trackers.size())); + if (index >= (int)m_trackers.size()) return -1; + + while (index > 0 && m_trackers[index].tier == m_trackers[index-1].tier) + { + using std::swap; + swap(m_trackers[index], m_trackers[index-1]); + if (m_last_working_tracker == index) --m_last_working_tracker; + else if (m_last_working_tracker == index - 1) ++m_last_working_tracker; + --index; + } + return index; + } + + int torrent::deprioritize_tracker(int index) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < int(m_trackers.size())); + if (index >= (int)m_trackers.size()) return -1; + + while (index < int(m_trackers.size()) - 1 && m_trackers[index].tier == m_trackers[index + 1].tier) + { + using std::swap; + swap(m_trackers[index], m_trackers[index + 1]); + if (m_last_working_tracker == index) ++m_last_working_tracker; + else if (m_last_working_tracker == index + 1) --m_last_working_tracker; + ++index; + } + return index; + } + + void torrent::files_checked() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(m_torrent_file->is_valid()); + + if (m_abort) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("files_checked(), paused"); +#endif + return; + } + + // we might be finished already, in which case we should + // not switch to downloading mode. If all files are + // filtered, we're finished when we start. + if (m_state != torrent_status::finished + && m_state != torrent_status::seeding) + set_state(torrent_status::downloading); + + INVARIANT_CHECK; + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(torrent_checked_alert( + get_handle())); + } + + // calling pause will also trigger the auto managed + // recalculation + // if we just got here by downloading the metadata, + // just keep going, no need to disconnect all peers just + // to restart the torrent in a second + if (m_auto_managed) + { + // if this is an auto managed torrent, force a recalculation + // of which torrents to have active + m_ses.trigger_auto_manage(); + } + + if (!is_seed()) + { + // turn off super seeding if we're not a seed + if (m_super_seeding) m_super_seeding = false; + + // if we just finished checking and we're not a seed, we are + // likely to be unpaused + m_ses.trigger_auto_manage(); + + if (is_finished() && m_state != torrent_status::finished) + finished(); + } + else + { + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + i->complete_sent = true; + + if (m_state != torrent_status::finished + && m_state != torrent_status::seeding) + finished(); + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->on_files_checked(); + } TORRENT_CATCH (std::exception&) {} + } +#endif + + m_connections_initialized = true; + m_files_checked = true; + + for (torrent::peer_iterator i = m_connections.begin(); + i != m_connections.end();) + { + peer_connection* pc = *i; + ++i; + + // all peer connections have to initialize themselves now that the metadata + // is available + if (!m_connections_initialized) + { + if (pc->is_disconnecting()) continue; + pc->on_metadata_impl(); + if (pc->is_disconnecting()) continue; + pc->init(); + } + +#ifdef TORRENT_VERBOSE_LOGGING + pc->peer_log("*** ON_FILES_CHECKED"); +#endif + if (pc->is_interesting() && !pc->has_peer_choked()) + { + request_a_block(*this, *pc); + pc->send_block_requests(); + } + } + + + start_announcing(); + + maybe_connect_web_seeds(); + } + + alert_manager& torrent::alerts() const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + return m_ses.m_alerts; + } + + std::string torrent::save_path() const + { + return m_save_path; + } + + bool torrent::rename_file(int index, std::string const& name) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_torrent_file->num_files()); + + if (!m_owning_storage.get()) return false; + + m_owning_storage->async_rename_file(index, name + , boost::bind(&torrent::on_file_renamed, shared_from_this(), _1, _2)); + return true; + } + + void torrent::move_storage(std::string const& save_path, int flags) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + if (m_abort) + { + if (alerts().should_post()) + alerts().post_alert(storage_moved_failed_alert(get_handle(), boost::asio::error::operation_aborted)); + return; + } + + // storage may be NULL during shutdown + if (m_owning_storage.get()) + { +#if TORRENT_USE_UNC_PATHS + std::string path = canonicalize_path(save_path); +#else + std::string const& path = save_path; +#endif + m_owning_storage->async_move_storage(path, flags + , boost::bind(&torrent::on_storage_moved, shared_from_this(), _1, _2)); + m_moving_storage = true; + } + else + { +#if TORRENT_USE_UNC_PATHS + m_save_path = canonicalize_path(save_path); +#else + + m_save_path = save_path; +#endif + m_need_save_resume_data = true; + + if (alerts().should_post()) + { + alerts().post_alert(storage_moved_alert(get_handle(), m_save_path)); + } + } + } + + void torrent::on_storage_moved(int ret, disk_io_job const& j) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + m_moving_storage = false; + + if (ret == piece_manager::no_error || ret == piece_manager::need_full_check) + { + if (alerts().should_post()) + alerts().post_alert(storage_moved_alert(get_handle(), j.str)); + m_save_path = j.str; + m_need_save_resume_data = true; + if (ret == piece_manager::need_full_check) + force_recheck(); + } + else + { + if (alerts().should_post()) + alerts().post_alert(storage_moved_failed_alert(get_handle(), j.error)); + } + } + + piece_manager& torrent::filesystem() + { + TORRENT_ASSERT(m_owning_storage.get()); + TORRENT_ASSERT(m_storage); + return *m_storage; + } + + + torrent_handle torrent::get_handle() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + return torrent_handle(shared_from_this()); + } + + session_settings const& torrent::settings() const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + return m_ses.settings(); + } + +#if TORRENT_USE_INVARIANT_CHECKS + void torrent::check_invariant() const + { + for (std::deque::const_iterator i = m_time_critical_pieces.begin() + , end(m_time_critical_pieces.end()); i != end; ++i) + { + TORRENT_ASSERT(!is_seed()); + TORRENT_ASSERT(!has_picker() || !m_picker->have_piece(i->piece)); + } + + TORRENT_ASSERT(m_ses.is_network_thread()); + if (is_paused()) TORRENT_ASSERT(num_peers() == 0 || m_graceful_pause_mode); + + if (!should_check_files()) + TORRENT_ASSERT(m_state != torrent_status::checking_files); + else + TORRENT_ASSERT(m_queued_for_checking); + + if (m_torrent_file) + { + TORRENT_ASSERT(m_info_hash == m_torrent_file->info_hash()); + } + +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + if (!m_ses.m_queued_for_checking.empty()) + { + // if there are torrents waiting to be checked + // assert that there's a torrent that is being + // processed right now + int found = 0; + int found_active = 0; + for (aux::session_impl::torrent_map::iterator i = m_ses.m_torrents.begin() + , end(m_ses.m_torrents.end()); i != end; ++i) + if (i->second->m_state == torrent_status::checking_files) + { + ++found; + if (i->second->should_check_files()) ++found_active; + } + + // if the session is paused, there might still be some torrents + // in the checking_files state that haven't been dequeued yet + if (m_ses.is_paused()) + { + TORRENT_ASSERT(found_active == 0); + } + else + { + // the case of 2 is in the special case where one switches over from + // checking to complete. + TORRENT_ASSERT(found_active >= 1); + TORRENT_ASSERT(found_active <= 2); + TORRENT_ASSERT(found >= 1); + } + } +#endif + + TORRENT_ASSERT(m_resume_entry.type() == lazy_entry::dict_t + || m_resume_entry.type() == lazy_entry::none_t); + + int num_uploads = 0; + std::map num_requests; + for (const_peer_iterator i = this->begin(); i != this->end(); ++i) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + // make sure this peer is not a dangling pointer + TORRENT_ASSERT(m_ses.has_peer(*i)); +#endif + peer_connection const& p = *(*i); + for (std::vector::const_iterator i = p.request_queue().begin() + , end(p.request_queue().end()); i != end; ++i) + ++num_requests[i->block]; + for (std::vector::const_iterator i = p.download_queue().begin() + , end(p.download_queue().end()); i != end; ++i) + if (!i->not_wanted && !i->timed_out) ++num_requests[i->block]; + if (!p.is_choked() && !p.ignore_unchoke_slots()) ++num_uploads; + torrent* associated_torrent = p.associated_torrent().lock().get(); + if (associated_torrent != this && associated_torrent != 0) + TORRENT_ASSERT(false); + } + TORRENT_ASSERT(num_uploads == int(m_num_uploads)); + + if (has_picker()) + { + for (std::map::iterator i = num_requests.begin() + , end(num_requests.end()); i != end; ++i) + { + piece_block b = i->first; + int count = i->second; + int picker_count = m_picker->num_peers(b); + // if we're no longer downloading the piece + // (for instance, it may be fully downloaded and waiting + // for the hash check to return), the piece picker always + // returns 0 requests, regardless of how many peers may still + // have the block in their queue + if (!m_picker->is_downloaded(b) && m_picker->is_downloading(b.piece_index)) + TORRENT_ASSERT(picker_count == count); + } + TORRENT_ASSERT(num_have() >= m_picker->num_have_filtered()); + } + + if (valid_metadata()) + { + TORRENT_ASSERT(m_abort || m_error || !m_picker || m_picker->num_pieces() == m_torrent_file->num_pieces()); + } + else + { + TORRENT_ASSERT(m_abort || m_error || !m_picker || m_picker->num_pieces() == 0); + } + +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + // make sure we haven't modified the peer object + // in a way that breaks the sort order + if (m_policy.begin_peer() != m_policy.end_peer()) + { + policy::const_iterator i = m_policy.begin_peer(); + policy::const_iterator prev = i++; + policy::const_iterator end(m_policy.end_peer()); + policy::peer_address_compare cmp; + for (; i != end; ++i, ++prev) + { + TORRENT_ASSERT(!cmp(*i, *prev)); + } + } +#endif + + size_type total_done = quantized_bytes_done(); + if (m_torrent_file->is_valid()) + { + if (is_seed()) + TORRENT_ASSERT(total_done == m_torrent_file->total_size()); + else + TORRENT_ASSERT(total_done != m_torrent_file->total_size() || !m_files_checked); + + TORRENT_ASSERT(block_size() <= m_torrent_file->piece_length()); + } + else + { + TORRENT_ASSERT(total_done == 0); + } +/* + if (m_picker && !m_abort) + { + // make sure that pieces that have completed the download + // of all their blocks are in the disk io thread's queue + // to be checked. + const std::vector& dl_queue + = m_picker->get_download_queue(); + for (std::vector::const_iterator i = + dl_queue.begin(); i != dl_queue.end(); ++i) + { + const int blocks_per_piece = m_picker->blocks_in_piece(i->index); + + bool complete = true; + for (int j = 0; j < blocks_per_piece; ++j) + { + if (i->info[j].state == piece_picker::block_info::state_finished) + continue; + complete = false; + break; + } + TORRENT_ASSERT(complete); + } + } +*/ + if (m_files_checked && valid_metadata()) + { + TORRENT_ASSERT(block_size() > 0); + } + + + for (std::vector::const_iterator i = m_file_progress.begin() + , end(m_file_progress.end()); i != end; ++i) + { + int index = i - m_file_progress.begin(); + TORRENT_ASSERT(*i <= m_torrent_file->files().file_size(index)); + } + } +#endif + + void torrent::set_sequential_download(bool sd) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (m_sequential_download == sd) return; + m_sequential_download = sd; + + m_need_save_resume_data = true; + + state_updated(); + } + + void torrent::queue_up() + { + set_queue_position(queue_position() == 0 + ? queue_position() : queue_position() - 1); + } + + void torrent::queue_down() + { + set_queue_position(queue_position() + 1); + } + + void torrent::set_queue_position(int p) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT((p == -1) == is_finished() + || (!m_auto_managed && p == -1) + || (m_abort && p == -1)); + if (is_finished() && p != -1) return; + if (p == m_sequence_number) return; + + state_updated(); + + session_impl::torrent_map& torrents = m_ses.m_torrents; + if (p >= 0 && m_sequence_number == -1) + { + int max_seq = -1; + for (session_impl::torrent_map::iterator i = torrents.begin() + , end(torrents.end()); i != end; ++i) + { + torrent* t = i->second.get(); + if (t->m_sequence_number > max_seq) max_seq = t->m_sequence_number; + if (t->m_sequence_number >= p) + { + ++t->m_sequence_number; + t->state_updated(); + } + } + m_sequence_number = (std::min)(max_seq + 1, p); + } + else if (p < 0) + { + for (session_impl::torrent_map::iterator i = torrents.begin() + , end(torrents.end()); i != end; ++i) + { + torrent* t = i->second.get(); + if (t == this) continue; + if (t->m_sequence_number >= m_sequence_number + && t->m_sequence_number != -1) + { + --t->m_sequence_number; + t->state_updated(); + } + } + m_sequence_number = p; + } + else if (p < m_sequence_number) + { + for (session_impl::torrent_map::iterator i = torrents.begin() + , end(torrents.end()); i != end; ++i) + { + torrent* t = i->second.get(); + if (t == this) continue; + if (t->m_sequence_number >= p + && t->m_sequence_number < m_sequence_number + && t->m_sequence_number != -1) + { + ++t->m_sequence_number; + t->state_updated(); + } + } + m_sequence_number = p; + } + else if (p > m_sequence_number) + { + int max_seq = 0; + for (session_impl::torrent_map::iterator i = torrents.begin() + , end(torrents.end()); i != end; ++i) + { + torrent* t = i->second.get(); + int pos = t->m_sequence_number; + if (pos > max_seq) max_seq = pos; + if (t == this) continue; + + if (pos <= p + && pos > m_sequence_number + && pos != -1) + { + --t->m_sequence_number; + t->state_updated(); + } + + } + m_sequence_number = (std::min)(max_seq, p); + } + + m_ses.m_auto_manage_time_scaler = 2; + } + + void torrent::set_max_uploads(int limit, bool state_update) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(limit >= -1); + if (limit <= 0) limit = (1<<24)-1; + if (m_max_uploads != limit && state_update) state_updated(); + m_max_uploads = limit; + + m_need_save_resume_data = true; + } + + void torrent::set_max_connections(int limit, bool state_update) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(limit >= -1); + if (limit <= 0) limit = (1<<24)-1; + if (m_max_connections != limit && state_update) state_updated(); + m_max_connections = limit; + + if (num_peers() > int(m_max_connections)) + { + disconnect_peers(num_peers() - m_max_connections + , error_code(errors::too_many_connections, get_libtorrent_category())); + } + + m_need_save_resume_data = true; + } + + int torrent::get_peer_upload_limit(tcp::endpoint ip) const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + const_peer_iterator i = std::find_if(m_connections.begin(), m_connections.end() + , boost::bind(&peer_connection::remote, _1) == ip); + if (i == m_connections.end()) return -1; + return (*i)->get_upload_limit(); + } + + int torrent::get_peer_download_limit(tcp::endpoint ip) const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + const_peer_iterator i = std::find_if(m_connections.begin(), m_connections.end() + , boost::bind(&peer_connection::remote, _1) == ip); + if (i == m_connections.end()) return -1; + return (*i)->get_download_limit(); + } + + void torrent::set_peer_upload_limit(tcp::endpoint ip, int limit) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(limit >= -1); + peer_iterator i = std::find_if(m_connections.begin(), m_connections.end() + , boost::bind(&peer_connection::remote, _1) == ip); + if (i == m_connections.end()) return; + (*i)->set_upload_limit(limit); + } + + void torrent::set_peer_download_limit(tcp::endpoint ip, int limit) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(limit >= -1); + peer_iterator i = std::find_if(m_connections.begin(), m_connections.end() + , boost::bind(&peer_connection::remote, _1) == ip); + if (i == m_connections.end()) return; + (*i)->set_download_limit(limit); + } + + void torrent::set_upload_limit(int limit, bool state_update) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(limit >= -1); + if (limit <= 0) limit = 0; + if (m_bandwidth_channel[peer_connection::upload_channel].throttle() != limit + && state_update) + state_updated(); + m_bandwidth_channel[peer_connection::upload_channel].throttle(limit); + + m_need_save_resume_data = true; + } + + int torrent::upload_limit() const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + int limit = m_bandwidth_channel[peer_connection::upload_channel].throttle(); + if (limit == (std::numeric_limits::max)()) limit = -1; + return limit; + } + + void torrent::set_download_limit(int limit, bool state_update) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(limit >= -1); + if (limit <= 0) limit = 0; + if (m_bandwidth_channel[peer_connection::download_channel].throttle() != limit + && state_update) + state_updated(); + m_bandwidth_channel[peer_connection::download_channel].throttle(limit); + + m_need_save_resume_data = true; + } + + int torrent::download_limit() const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + int limit = m_bandwidth_channel[peer_connection::download_channel].throttle(); + if (limit == (std::numeric_limits::max)()) limit = -1; + return limit; + } + + bool torrent::delete_files() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING || defined TORRENT_LOGGING + log_to_all_peers("DELETING FILES IN TORRENT"); +#endif + + disconnect_all(errors::torrent_removed); + stop_announcing(); + + // storage may be NULL during shutdown + if (m_owning_storage.get()) + { + TORRENT_ASSERT(m_storage); + m_storage->async_delete_files( + boost::bind(&torrent::on_files_deleted, shared_from_this(), _1, _2)); + m_deleted = true; + return true; + } + return false; + } + + void torrent::clear_error() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (!m_error) return; + bool checking_files = should_check_files(); + m_ses.trigger_auto_manage(); + m_error = error_code(); + m_error_file.clear(); + + state_updated(); + + // if we haven't downloaded the metadata from m_url, try again + if (!m_url.empty() && !m_torrent_file->is_valid()) + { + start_download_url(); + return; + } + // if the error happened during initialization, try again now + if (!m_storage) init(); + if (!checking_files && should_check_files()) + queue_torrent_check(); + } + + void torrent::set_error(error_code const& ec, std::string const& error_file) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + bool checking_files = should_check_files(); + m_error = ec; + m_error_file = error_file; + + if (alerts().should_post()) + alerts().post_alert(torrent_error_alert(get_handle(), ec)); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING || defined TORRENT_LOGGING + if (ec) + { + char buf[1024]; + snprintf(buf, sizeof(buf), "TORRENT ERROR: %s: %s", ec.message().c_str(), error_file.c_str()); + log_to_all_peers(buf); + } +#endif + + if (checking_files && !should_check_files()) + { + // stop checking + m_storage->abort_disk_io(); + dequeue_torrent_check(); + set_state(torrent_status::queued_for_checking); + } + + state_updated(); + } + + void torrent::auto_managed(bool a) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + if (m_auto_managed == a) return; + bool checking_files = should_check_files(); + m_auto_managed = a; + + state_updated(); + + // we need to save this new state as well + m_need_save_resume_data = true; + + // recalculate which torrents should be + // paused + m_ses.trigger_auto_manage(); + + if (!checking_files && should_check_files()) + { + queue_torrent_check(); + } + else if (checking_files && !should_check_files()) + { + // stop checking + m_storage->abort_disk_io(); + dequeue_torrent_check(); + set_state(torrent_status::queued_for_checking); + } + + // if this torrent is running and just became auto-managed + // we might want to pause it in favor of some other torrent + if (m_auto_managed && !is_paused()) + m_ses.m_auto_manage_time_scaler = 2; + } + + // the higher seed rank, the more important to seed + int torrent::seed_rank(session_settings const& s) const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + enum flags + { + seed_ratio_not_met = 0x40000000, + no_seeds = 0x20000000, + recently_started = 0x10000000, + prio_mask = 0x0fffffff + }; + + if (!is_finished()) return 0; + + int scale = 1000; + if (!is_seed()) scale = 500; + + int ret = 0; + + ptime now = time_now(); + + int finished_time = m_finished_time; + int download_time = int(m_active_time) - finished_time; + + // if we haven't yet met the seed limits, set the seed_ratio_not_met + // flag. That will make this seed prioritized + // downloaded may be 0 if the torrent is 0-sized + size_type downloaded = (std::max)(m_total_downloaded, m_torrent_file->total_size()); + if (finished_time < s.seed_time_limit + && (download_time > 1 && finished_time / float(download_time) < s.seed_time_ratio_limit) + && downloaded > 0 + && m_total_uploaded / float(downloaded) < s.share_ratio_limit) + ret |= seed_ratio_not_met; + + // if this torrent is running, and it was started less + // than 30 minutes ago, give it priority, to avoid oscillation + if (!is_paused() && now - m_started < minutes(30)) + ret |= recently_started; + + // if we have any scrape data, use it to calculate + // seed rank + int seeds = 0; + int downloaders = 0; + + if (m_complete != 0xffffff) seeds = m_complete; + else seeds = m_policy.num_seeds(); + + if (m_incomplete != 0xffffff) downloaders = m_incomplete; + else downloaders = m_policy.num_peers() - m_policy.num_seeds(); + + if (seeds == 0) + { + ret |= no_seeds; + ret |= downloaders & prio_mask; + } + else + { + ret |= ((1 + downloaders) * scale / seeds) & prio_mask; + } + + return ret; + } + + // this is an async operation triggered by the client + void torrent::save_resume_data(int flags) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + if (!valid_metadata()) + { + alerts().post_alert(save_resume_data_failed_alert(get_handle() + , errors::no_metadata)); + return; + } + + // storage may be NULL during shutdown + if (!m_owning_storage.get() || !m_storage) + { + alerts().post_alert(save_resume_data_failed_alert(get_handle() + , errors::destructing_torrent)); + return; + } + + m_need_save_resume_data = false; + m_last_saved_resume = time(0); + m_save_resume_flags = boost::uint8_t(flags); + state_updated(); + + TORRENT_ASSERT(m_storage); + if (m_state == torrent_status::queued_for_checking + || m_state == torrent_status::checking_files + || m_state == torrent_status::checking_resume_data) + { + boost::shared_ptr rd(new entry); + write_resume_data(*rd); + alerts().post_alert(save_resume_data_alert(rd + , get_handle())); + return; + } + + if ((flags & torrent_handle::flush_disk_cache)) + m_storage->async_release_files(); + + m_storage->async_save_resume_data( + boost::bind(&torrent::on_save_resume_data, shared_from_this(), _1, _2)); + } + + bool torrent::should_check_files() const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + // #error should m_allow_peers really affect checking? + return (m_state == torrent_status::checking_files + || m_state == torrent_status::queued_for_checking) + && (m_allow_peers || m_auto_managed) + && !has_error() + && !m_abort + && !m_graceful_pause_mode + && !m_ses.is_paused(); + } + + void torrent::flush_cache() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + // storage may be NULL during shutdown + if (!m_owning_storage) + { + TORRENT_ASSERT(m_abort); + return; + } + m_storage->async_release_files( + boost::bind(&torrent::on_cache_flushed, shared_from_this(), _1, _2)); + } + + void torrent::on_cache_flushed(int /* ret */, disk_io_job const& j) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + if (m_ses.is_aborted()) return; + + if (alerts().should_post()) + alerts().post_alert(cache_flushed_alert(get_handle())); + } + + bool torrent::is_paused() const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + return !m_allow_peers || m_ses.is_paused() || m_graceful_pause_mode; + } + + void torrent::pause(bool graceful) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + if (!m_allow_peers) return; + if (!graceful) set_allow_peers(false); + m_announce_to_dht = false; + m_announce_to_trackers = false; + m_announce_to_lsd = false; + + // we need to save this new state + m_need_save_resume_data = true; + state_updated(); + + bool prev_graceful = m_graceful_pause_mode; + m_graceful_pause_mode = graceful; + + if (!m_ses.is_paused() || (prev_graceful && !m_graceful_pause_mode)) + { + do_pause(); + // if this torrent was just paused + // we might have to resume some other auto-managed torrent + m_ses.trigger_auto_manage(); + } + } + + void torrent::do_pause() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (!is_paused()) return; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + if ((*i)->on_pause()) return; + } TORRENT_CATCH (std::exception&) {} + } +#endif + + m_inactive = false; + + state_updated(); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING || defined TORRENT_LOGGING + log_to_all_peers("PAUSING TORRENT"); +#endif + + // this will make the storage close all + // files and flush all cached data + if (m_owning_storage.get()) + { + TORRENT_ASSERT(m_storage); + m_storage->async_release_files( + boost::bind(&torrent::on_torrent_paused, shared_from_this(), _1, _2)); + m_storage->async_clear_read_cache(); + } + else + { + if (alerts().should_post()) + alerts().post_alert(torrent_paused_alert(get_handle())); + } + + if (!m_graceful_pause_mode) + { + disconnect_all(errors::torrent_paused); + } + else + { + // disconnect all peers with no outstanding data to receive + // and choke all remaining peers to prevent responding to new + // requests + for (std::set::iterator i = m_connections.begin() + , end(m_connections.end()); i != end;) + { + std::set::iterator j = i++; + peer_connection* p = *j; + TORRENT_ASSERT(p->associated_torrent().lock().get() == this); + + if (p->is_disconnecting()) + { + m_connections.erase(j); + continue; + } + + if (p->outstanding_bytes() > 0) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + p->peer_log("*** CHOKING PEER: torrent graceful paused"); +#endif + // remove any un-sent requests from the queue + p->clear_request_queue(); + // don't accept new requests from the peer + if (!p->is_choked()) m_ses.choke_peer(*p); + continue; + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + p->peer_log("*** CLOSING CONNECTION: torrent_paused"); +#endif + p->disconnect(errors::torrent_paused); + } + } + + stop_announcing(); + + if (m_queued_for_checking && !should_check_files()) + { + // stop checking + m_storage->abort_disk_io(); + dequeue_torrent_check(); + set_state(torrent_status::queued_for_checking); + TORRENT_ASSERT(!m_queued_for_checking); + } + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING || defined TORRENT_LOGGING + void torrent::log_to_all_peers(char const* message) + { + TORRENT_ASSERT(m_ses.is_network_thread()); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + for (peer_iterator i = m_connections.begin(); + i != m_connections.end(); ++i) + { + (*i)->peer_log("*** %s", message); + } +#endif + + debug_log("%s", message); + } +#endif + + // add or remove a url that will be attempted for + // finding the file(s) in this torrent. + void torrent::add_web_seed(std::string const& url, web_seed_entry::type_t type) + { + web_seed_entry ent(url, type); + // don't add duplicates + if (std::find(m_web_seeds.begin(), m_web_seeds.end(), ent) != m_web_seeds.end()) return; + m_web_seeds.push_back(ent); + } + + void torrent::add_web_seed(std::string const& url, web_seed_entry::type_t type + , std::string const& auth, web_seed_entry::headers_t const& extra_headers) + { + web_seed_entry ent(url, type, auth, extra_headers); + // don't add duplicates + if (std::find(m_web_seeds.begin(), m_web_seeds.end(), ent) != m_web_seeds.end()) return; + m_web_seeds.push_back(ent); + } + + void torrent::set_allow_peers(bool b, bool graceful) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + if (m_allow_peers == b + && m_graceful_pause_mode == graceful) return; + + m_allow_peers = b; + if (!m_ses.is_paused()) + m_graceful_pause_mode = graceful; + + if (!b) + { + m_announce_to_dht = false; + m_announce_to_trackers = false; + m_announce_to_lsd = false; + do_pause(); + } + else + { + do_resume(); + } + + update_guage(); + } + + void torrent::resume() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + if (m_allow_peers + && m_announce_to_dht + && m_announce_to_trackers + && m_announce_to_lsd) return; + m_announce_to_dht = true; + m_announce_to_trackers = true; + m_announce_to_lsd = true; + // this call will trigger a tracker announce, that's + // why it's important to set announce_to_trackers to + // true first + set_allow_peers(true); + if (!m_ses.is_paused()) m_graceful_pause_mode = false; + + // we need to save this new state + m_need_save_resume_data = true; + + do_resume(); + } + + void torrent::do_resume() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (is_paused()) return; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + if ((*i)->on_resume()) return; + } TORRENT_CATCH (std::exception&) {} + } +#endif + + if (alerts().should_post()) + alerts().post_alert(torrent_resumed_alert(get_handle())); + + state_updated(); + + m_started = time_now(); + clear_error(); + start_announcing(); + if (!m_queued_for_checking && should_check_files()) + queue_torrent_check(); + } + + void torrent::update_tracker_timer(ptime now) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (!m_announcing) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("*** update tracker timer: not announcing"); +#endif + return; + } + + ptime next_announce = max_time(); + int tier = INT_MAX; + + bool found_working = false; + + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + char msg[1000]; + snprintf(msg, sizeof(msg), "*** update tracker timer: considering \"%s\" " + "[ announce_to_all_tiers: %d announce_to_all_trackers: %d" + " found_working: %d i->tier: %d tier: %d " + " is_working: %d fails: %d fail_limit: %d updating: %d ]" + , i->url.c_str(), settings().announce_to_all_tiers + , settings().announce_to_all_trackers, found_working + , i->tier, tier, i->is_working(), i->fails, i->fail_limit + , i->updating); + debug_log(msg); +#endif + if (settings().announce_to_all_tiers + && found_working + && i->tier <= tier + && tier != INT_MAX) + continue; + + if (i->tier > tier && !settings().announce_to_all_tiers) break; + if (i->is_working()) { tier = i->tier; found_working = false; } + if (i->fails >= i->fail_limit && i->fail_limit != 0) continue; + if (i->updating) + { + found_working = true; + } + else + { + ptime next_tracker_announce = (std::max)(i->next_announce, i->min_announce); + if (next_tracker_announce < next_announce + && (!found_working || i->is_working())) + next_announce = next_tracker_announce; + } + if (i->is_working()) found_working = true; + if (found_working + && !settings().announce_to_all_trackers + && !settings().announce_to_all_tiers) break; + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + char msg[200]; + snprintf(msg, sizeof(msg), "*** update tracker timer: next_announce < now %d" + " m_waiting_tracker: %d next_announce_in: %d" + , next_announce <= now, m_waiting_tracker, total_seconds(now - next_announce)); + debug_log(msg); +#endif + if (next_announce <= now) next_announce = now; + + // don't re-issue the timer if it's the same expiration time as last time + // if m_waiting_tracker is false, expires_at() is undefined + if (m_waiting_tracker && m_tracker_timer.expires_at() == next_announce) return; + + m_waiting_tracker = true; + error_code ec; + boost::weak_ptr self(shared_from_this()); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("tracker::on_tracker_announce_disp"); +#endif + m_tracker_timer.expires_at(next_announce, ec); + m_tracker_timer.async_wait(boost::bind(&torrent::on_tracker_announce_disp, self, _1)); + } + + void torrent::start_announcing() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (is_paused()) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("start_announcing(), paused"); +#endif + return; + } + // if we don't have metadata, we need to announce + // before checking files, to get peers to + // request the metadata from + if (!m_files_checked && valid_metadata()) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("start_announcing(), files not checked (with valid metadata)"); +#endif + return; + } + if (!m_torrent_file->is_valid() && !m_url.empty()) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("start_announcing(), downloading URL"); +#endif + return; + } + if (m_announcing) return; + + m_announcing = true; + +#ifndef TORRENT_DISABLE_DHT + if (m_policy.num_peers() < 50 && m_ses.m_dht) + { + // we don't have any peers, prioritize + // announcing this torrent with the DHT + m_ses.prioritize_dht(shared_from_this()); + } +#endif + + if (!m_trackers.empty()) + { + // tell the tracker that we're back + std::for_each(m_trackers.begin(), m_trackers.end() + , boost::bind(&announce_entry::reset, _1)); + } + + // reset the stats, since from the tracker's + // point of view, this is a new session + m_total_failed_bytes = 0; + m_total_redundant_bytes = 0; + m_stat.clear(); + + announce_with_tracker(); + + // private torrents are never announced on LSD + // or on DHT, we don't need this timer. + if (!m_torrent_file->is_valid() + || (!m_torrent_file->priv() + && (!m_torrent_file->is_i2p() + || settings().allow_i2p_mixed))) + { + if (m_ses.m_lsd) lsd_announce(); + } + } + + void torrent::stop_announcing() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (!m_announcing) return; + + error_code ec; + m_tracker_timer.cancel(ec); + + m_announcing = false; + + ptime now = time_now(); + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + { + i->next_announce = now; + i->min_announce = now; + } + announce_with_tracker(tracker_request::stopped); + } + + void torrent::second_tick(stat& accumulator, int tick_interval_ms) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + boost::weak_ptr self(shared_from_this()); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->tick(); + } TORRENT_CATCH (std::exception&) {} + } + + if (m_abort) return; +#endif + + m_time_scaler--; + if (m_time_scaler <= 0) + { + m_time_scaler = 10; + + if (settings().max_sparse_regions > 0 + && m_picker + && m_picker->sparse_regions() > settings().max_sparse_regions) + { + // we have too many sparse regions. Prioritize pieces + // that won't introduce new sparse regions + // prioritize pieces that will reduce the number of sparse + // regions even higher + int start = m_picker->cursor(); + int end = m_picker->reverse_cursor(); + for (int i = start; i < end; ++i) + update_sparse_piece_prio(i, start, end); + } + } + + // if we're in upload only mode and we're auto-managed + // leave upload mode every 10 minutes hoping that the error + // condition has been fixed + if (m_upload_mode && m_auto_managed && int(m_upload_mode_time) + >= settings().optimistic_disk_retry) + { + set_upload_mode(false); + } + + if (is_paused() && !m_graceful_pause_mode) + { + // let the stats fade out to 0 + accumulator += m_stat; + m_stat.second_tick(tick_interval_ms); + // if the rate is 0, there's no update because of network transfers + if (m_stat.low_pass_upload_rate() > 0 || m_stat.low_pass_download_rate() > 0) + state_updated(); + return; + } + + if (settings().rate_limit_ip_overhead) + { + int up_limit = m_bandwidth_channel[peer_connection::upload_channel].throttle(); + int down_limit = m_bandwidth_channel[peer_connection::download_channel].throttle(); + + if (down_limit > 0 + && m_stat.download_ip_overhead() >= down_limit + && alerts().should_post()) + { + alerts().post_alert(performance_alert(get_handle() + , performance_alert::download_limit_too_low)); + } + + if (up_limit > 0 + && m_stat.upload_ip_overhead() >= up_limit + && alerts().should_post()) + { + alerts().post_alert(performance_alert(get_handle() + , performance_alert::upload_limit_too_low)); + } + } + + int seconds_since_last_tick = 1; + if (m_ses.m_tick_residual >= 1000) ++seconds_since_last_tick; + + if (is_seed()) m_seeding_time += seconds_since_last_tick; + if (is_finished()) m_finished_time += seconds_since_last_tick; + if (m_upload_mode) m_upload_mode_time += seconds_since_last_tick; + m_last_scrape += seconds_since_last_tick; + m_active_time += seconds_since_last_tick; + m_last_download += seconds_since_last_tick; + m_last_upload += seconds_since_last_tick; + + // ---- TIME CRITICAL PIECES ---- + +#if TORRENT_DEBUG_STREAMING > 0 + std::vector queue; + get_download_queue(&queue); + + std::vector peer_list; + get_peer_info(peer_list); + + std::sort(queue.begin(), queue.end(), boost::bind(&partial_piece_info::piece_index, _1) + < boost::bind(&partial_piece_info::piece_index, _2)); + + printf("average piece download time: %.2f s (+/- %.2f s)\n" + , m_average_piece_time / 1000.f + , m_piece_time_deviation / 1000.f); + for (std::vector::iterator i = queue.begin() + , end(queue.end()); i != end; ++i) + { + extern void print_piece(libtorrent::partial_piece_info* pp + , std::vector const& peers + , std::vector const& time_critical); + + print_piece(&*i, peer_list, m_time_critical_pieces); + } +#endif // TORRENT_DEBUG_STREAMING + + if (!m_time_critical_pieces.empty() && !upload_mode()) + { + request_time_critical_pieces(); + } + + // ---- WEB SEEDS ---- + + maybe_connect_web_seeds(); + + m_swarm_last_seen_complete = m_last_seen_complete; + for (peer_iterator i = m_connections.begin(); + i != m_connections.end();) + { + peer_connection* p = *i; + ++i; + + // look for the peer that saw a seed most recently + m_swarm_last_seen_complete = (std::max)(p->last_seen_complete(), m_swarm_last_seen_complete); + + if (!p->ignore_stats()) + m_stat += p->statistics(); + + // updates the peer connection's ul/dl bandwidth + // resource requests + TORRENT_TRY { + p->second_tick(tick_interval_ms); + } + TORRENT_CATCH (std::exception& e) + { + TORRENT_DECLARE_DUMMY(std::exception, e); + (void)e; +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + p->peer_log("*** ERROR %s", e.what()); +#endif + p->disconnect(errors::no_error, 1); + } + } + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(stats_alert(get_handle(), tick_interval_ms, m_stat)); + + accumulator += m_stat; + m_total_uploaded += m_stat.last_payload_uploaded(); + m_total_downloaded += m_stat.last_payload_downloaded(); + m_stat.second_tick(tick_interval_ms); + + // if the rate is 0, there's no update because of network transfers + if (m_stat.low_pass_upload_rate() > 0 || m_stat.low_pass_download_rate() > 0) + state_updated(); + + // this section determines whether the torrent is active or not. When it + // changes state, it may also trigger the auto-manage logic to reconsider + // which torrents should be queued and started. There is a low pass + // filter in order to avoid flapping (auto_manage_startup). + bool is_inactive = false; + if (is_finished()) + is_inactive = m_stat.upload_payload_rate() < m_ses.m_settings.inactive_up_rate; + else + is_inactive = m_stat.download_payload_rate() < m_ses.m_settings.inactive_down_rate; + + if (is_inactive) + { + if (m_inactive_counter < 0) m_inactive_counter = 0; + if (m_inactive_counter < (std::numeric_limits::max)()) + { + ++m_inactive_counter; + + // if this torrent was just considered inactive, we may want + // to dequeue some other torrent + if (m_inactive == false + && m_inactive_counter >= m_ses.m_settings.auto_manage_startup) + { + m_inactive = true; + if (m_ses.m_settings.dont_count_slow_torrents) + m_ses.trigger_auto_manage(); + } + } + } + else + { + if (m_inactive_counter > 0) m_inactive_counter = 0; + if (m_inactive_counter > (std::numeric_limits::min)()) + { + --m_inactive_counter; + + // if this torrent was just considered active, we may want + // to queue some other torrent + if (m_inactive == true + && m_inactive_counter <= -m_ses.m_settings.auto_manage_startup) + { + m_inactive = false; + if (m_ses.m_settings.dont_count_slow_torrents) + m_ses.trigger_auto_manage(); + } + } + } + } + + void torrent::maybe_connect_web_seeds() + { + if (m_abort) return; + + // if we have everything we want we don't need to connect to any web-seed + if (!is_finished() && !m_web_seeds.empty() && m_files_checked + && int(m_connections.size()) < m_max_connections + && m_ses.num_connections() < m_ses.settings().connections_limit) + { + // keep trying web-seeds if there are any + // first find out which web seeds we are connected to + for (std::list::iterator i = m_web_seeds.begin(); + i != m_web_seeds.end();) + { + std::list::iterator w = i++; + if (w->peer_info.connection) continue; + if (w->retry > time_now()) continue; + if (w->resolving) continue; + + connect_to_url_seed(w); + } + } + } + + void torrent::recalc_share_mode() + { + TORRENT_ASSERT(share_mode()); + if (is_seed()) return; + + int pieces_in_torrent = m_torrent_file->num_pieces(); + int num_seeds = 0; + int num_peers = 0; + int num_downloaders = 0; + int missing_pieces = 0; + int num_interested = 0; + for (std::set::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + peer_connection* p = *i; + if (p->is_connecting()) continue; + ++num_peers; + if (p->is_seed()) + { + ++num_seeds; + continue; + } + + if (p->share_mode()) continue; + + if ((*i)->is_peer_interested()) ++num_interested; + ++num_downloaders; + missing_pieces += pieces_in_torrent - p->num_have_pieces(); + } + + if (num_peers == 0) return; + + if (num_seeds * 100 / num_peers > 50 + && (num_peers * 100 / m_max_connections > 90 + || num_peers > 20)) + { + // we are connected to more than 90% seeds (and we're beyond + // 90% of the max number of connections). That will + // limit our ability to upload. We need more downloaders. + // disconnect some seeds so that we don't have more than 50% + int to_disconnect = num_seeds - num_peers / 2; + std::vector seeds; + seeds.reserve(num_seeds); + for (std::set::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + peer_connection* p = *i; + if (p->is_seed()) seeds.push_back(p); + } + + std::random_shuffle(seeds.begin(), seeds.end()); + TORRENT_ASSERT(to_disconnect <= int(seeds.size())); + for (int i = 0; i < to_disconnect; ++i) + seeds[i]->disconnect(errors::upload_upload_connection); + } + + if (num_downloaders == 0) return; + + // assume that the seeds are about as fast as us. During the time + // we can download one piece, and upload one piece, each seed + // can upload two pieces. + missing_pieces -= 2 * num_seeds; + + if (missing_pieces <= 0) return; + + // missing_pieces represents our opportunity to download pieces + // and share them more than once each + + // now, download at least one piece, otherwise download one more + // piece if our downloaded (and downloading) pieces is less than 50% + // of the uploaded bytes + int num_downloaded_pieces = (std::max)(m_picker->num_have() + , pieces_in_torrent - m_picker->num_filtered()); + + if (boost::int64_t(num_downloaded_pieces) * m_torrent_file->piece_length() + * settings().share_mode_target > m_total_uploaded + && num_downloaded_pieces > 0) + return; + + // don't have more pieces downloading in parallel than 5% of the total + // number of pieces we have downloaded + if (int(m_picker->get_download_queue().size()) > num_downloaded_pieces / 20) + return; + + // one more important property is that there are enough pieces + // that more than one peer wants to download + // make sure that there are enough downloaders for the rarest + // piece. Go through all pieces, figure out which one is the rarest + // and how many peers that has that piece + + std::vector rarest_pieces; + + int num_pieces = m_torrent_file->num_pieces(); + int rarest_rarity = INT_MAX; + bool prio_updated = false; + for (int i = 0; i < num_pieces; ++i) + { + piece_picker::piece_pos const& pp = m_picker->piece_stats(i); + if (pp.peer_count == 0) continue; + if (pp.filtered() && (pp.have() || pp.downloading)) + { + m_picker->set_piece_priority(i, 1); + prio_updated = true; + continue; + } + // don't count pieces we already have or are downloading + if (!pp.filtered() || pp.have()) continue; + if (int(pp.peer_count) > rarest_rarity) continue; + if (int(pp.peer_count) == rarest_rarity) + { + rarest_pieces.push_back(i); + continue; + } + + rarest_pieces.clear(); + rarest_rarity = pp.peer_count; + rarest_pieces.push_back(i); + } + + if (prio_updated) + m_policy.recalculate_connect_candidates(); + + // now, rarest_pieces is a list of all pieces that are the rarest ones. + // and rarest_rarity is the number of peers that have the rarest pieces + + // if there's only a single peer that doesn't have the rarest piece + // it's impossible for us to download one piece and upload it + // twice. i.e. we cannot get a positive share ratio + if (num_peers - rarest_rarity < settings().share_mode_target) return; + + // we might be able to do better than a share ratio of 2 if there are + // enough downloaders of the pieces we already have. + // TODO: go through the pieces we have and count the total number + // of downloaders we have. Only count peers that are interested in us + // since some peers might not send have messages for pieces we have + // it num_interested == 0, we need to pick a new piece + + // now, pick one of the rarest pieces to download + int pick = random() % rarest_pieces.size(); + bool was_finished = is_finished(); + m_picker->set_piece_priority(rarest_pieces[pick], 1); + update_peer_interest(was_finished); + + m_policy.recalculate_connect_candidates(); + } + + void torrent::refresh_explicit_cache(int cache_size) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (!ready_for_connections()) return; + + if (m_abort) return; + TORRENT_ASSERT(m_storage); + + // rotate the cached pieces + + // add blocks_per_piece / 2 in order to round to closest whole piece + int blocks_per_piece = m_torrent_file->piece_length() / block_size(); + int num_cache_pieces = (cache_size + blocks_per_piece / 2) / blocks_per_piece; + if (num_cache_pieces > m_torrent_file->num_pieces()) + num_cache_pieces = m_torrent_file->num_pieces(); + + std::vector avail_vec; + if (has_picker()) + { + m_picker->get_availability(avail_vec); + } + else + { + // we don't keep track of availability, do it the expensive way + // do a linear search from the first piece + for (int i = 0; i < m_torrent_file->num_pieces(); ++i) + { + int availability = 0; + if (!have_piece(i)) + { + avail_vec.push_back(INT_MAX); + continue; + } + + for (const_peer_iterator j = this->begin(); j != this->end(); ++j) + if ((*j)->has_piece(i)) ++availability; + avail_vec.push_back(availability); + } + } + + // now pick the num_cache_pieces rarest pieces from avail_vec + std::vector > pieces(m_torrent_file->num_pieces()); + for (int i = 0; i < m_torrent_file->num_pieces(); ++i) + { + pieces[i].second = i; + if (!have_piece(i)) pieces[i].first = INT_MAX; + else pieces[i].first = avail_vec[i]; + } + + // decrease the availability of the pieces that are + // already in the read cache, to move them closer to + // the beginning of the pieces list, and more likely + // to be included in this round of cache pieces + std::vector ret; + m_ses.m_disk_thread.get_cache_info(info_hash(), ret); + // remove write cache entries + ret.erase(std::remove_if(ret.begin(), ret.end() + , boost::bind(&cached_piece_info::kind, _1) == cached_piece_info::write_cache) + , ret.end()); + for (std::vector::iterator i = ret.begin() + , end(ret.end()); i != end; ++i) + { + --pieces[i->piece].first; + } + + std::random_shuffle(pieces.begin(), pieces.end()); + std::stable_sort(pieces.begin(), pieces.end() + , boost::bind(&std::pair::first, _1) < + boost::bind(&std::pair::first, _2)); + avail_vec.clear(); + for (int i = 0; i < num_cache_pieces; ++i) + { + if (pieces[i].first == INT_MAX) break; + avail_vec.push_back(pieces[i].second); + } + + if (!avail_vec.empty()) + { + // the number of pieces to cache for this torrent is proportional + // the number of peers it has, divided by the total number of peers. + // Each peer gets an equal share of the cache + + avail_vec.resize((std::min)(num_cache_pieces, int(avail_vec.size()))); + + for (std::vector::iterator i = avail_vec.begin() + , end(avail_vec.end()); i != end; ++i) + filesystem().async_cache(*i, boost::bind(&torrent::on_disk_cache_complete + , shared_from_this(), _1, _2)); + } + } + + void torrent::get_suggested_pieces(std::vector& s) const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (settings().suggest_mode == session_settings::no_piece_suggestions) + { + s.clear(); + return; + } + + std::vector ret; + m_ses.m_disk_thread.get_cache_info(info_hash(), ret); + + // remove write cache entries + ret.erase(std::remove_if(ret.begin(), ret.end() + , boost::bind(&cached_piece_info::kind, _1) == cached_piece_info::write_cache) + , ret.end()); + + // sort by how new the cached entry is, new pieces first + std::sort(ret.begin(), ret.end() + , boost::bind(&cached_piece_info::last_use, _1) + < boost::bind(&cached_piece_info::last_use, _2)); + + // cut off the oldest pieces that we don't want to suggest + // if we have an explicit cache, it's much more likely to + // stick around, so we should suggest all pieces + int num_pieces_to_suggest = int(ret.size()); + if (num_pieces_to_suggest == 0) return; + + if (!settings().explicit_read_cache) + num_pieces_to_suggest = (std::max)(1, int(ret.size() / 2)); + ret.resize(num_pieces_to_suggest); + + std::transform(ret.begin(), ret.end(), std::back_inserter(s) + , boost::bind(&cached_piece_info::piece, _1)); + } + + void torrent::add_stats(stat const& s) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + // these stats are propagated to the session + // stats the next time second_tick is called + m_stat += s; + } + +#if TORRENT_DEBUG_STREAMING > 0 + char const* esc(char const* code) + { + // this is a silly optimization + // to avoid copying of strings + enum { num_strings = 200 }; + static char buf[num_strings][20]; + static int round_robin = 0; + char* ret = buf[round_robin]; + ++round_robin; + if (round_robin >= num_strings) round_robin = 0; + ret[0] = '\033'; + ret[1] = '['; + int i = 2; + int j = 0; + while (code[j]) ret[i++] = code[j++]; + ret[i++] = 'm'; + ret[i++] = 0; + return ret; + } + + int peer_index(libtorrent::tcp::endpoint addr + , std::vector const& peers) + { + using namespace libtorrent; + std::vector::const_iterator i = std::find_if(peers.begin() + , peers.end(), boost::bind(&peer_info::ip, _1) == addr); + if (i == peers.end()) return -1; + + return i - peers.begin(); + } + + void print_piece(libtorrent::partial_piece_info* pp + , std::vector const& peers + , std::vector const& time_critical) + { + using namespace libtorrent; + + ptime now = time_now_hires(); + + float deadline = 0.f; + float last_request = 0.f; + int timed_out = -1; + + int piece = pp->piece_index; + std::vector::const_iterator i + = std::find_if(time_critical.begin(), time_critical.end() + , boost::bind(&time_critical_piece::piece, _1) == piece); + if (i != time_critical.end()) + { + deadline = total_milliseconds(i->deadline - now) / 1000.f; + last_request = total_milliseconds(now - i->last_requested) / 1000.f; + timed_out = i->timed_out; + } + + int num_blocks = pp->blocks_in_piece; + + printf("%5d: [", piece); + for (int j = 0; j < num_blocks; ++j) + { + int index = pp ? peer_index(pp->blocks[j].peer(), peers) % 36 : -1; + char chr = '+'; + if (index >= 0) + chr = (index < 10)?'0' + index:'A' + index - 10; + + char const* color = ""; + char const* multi_req = ""; + + if (pp->blocks[j].num_peers > 1) + multi_req = esc("1"); + + if (pp->blocks[j].bytes_progress > 0 + && pp->blocks[j].state == block_info::requested) + { + color = esc("33;7"); + chr = '0' + (pp->blocks[j].bytes_progress * 10 / pp->blocks[j].block_size); + } + else if (pp->blocks[j].state == block_info::finished) color = esc("32;7"); + else if (pp->blocks[j].state == block_info::writing) color = esc("36;7"); + else if (pp->blocks[j].state == block_info::requested) color = esc("0"); + else { color = esc("0"); chr = ' '; } + + printf("%s%s%c%s", color, multi_req, chr, esc("0")); + } + printf("%s]", esc("0")); + if (deadline != 0.f) + printf(" deadline: %f last-req: %f timed_out: %d\n" + , deadline, last_request, timed_out); + else + printf("\n"); + } +#endif // TORRENT_DEBUG_STREAMING + + struct busy_block_t + { + int peers; + int index; + bool operator<(busy_block_t rhs) const { return peers < rhs.peers; } + }; + + void pick_busy_blocks(int piece, int blocks_in_piece + , int timed_out + , std::vector& interesting_blocks + , piece_picker::downloading_piece const& pi) + { + // if there aren't any free blocks in the piece, and the piece is + // old enough, we may switch into busy mode for this piece. In this + // case busy_blocks and busy_count are set to contain the eligible + // busy blocks we may pick + // first, figure out which blocks are eligible for picking + // in "busy-mode" + busy_block_t* busy_blocks + = TORRENT_ALLOCA(busy_block_t, blocks_in_piece); + int busy_count = 0; + + // pick busy blocks from the piece + for (int k = 0; k < blocks_in_piece; ++k) + { + // only consider blocks that have been requested + // and we're still waiting for them + if (pi.info[k].state != piece_picker::block_info::state_requested) + continue; + + piece_block b(piece, k); + + // only allow a single additional request per block, in order + // to spread it out evenly across all stalled blocks + if (pi.info[k].num_peers > timed_out) + continue; + + busy_blocks[busy_count].peers = pi.info[k].num_peers; + busy_blocks[busy_count].index = k; + ++busy_count; + +#if TORRENT_DEBUG_STREAMING > 1 + printf(" [%d (%d)]", b.block_index, pi.info[k].num_peers); +#endif + } +#if TORRENT_DEBUG_STREAMING > 1 + printf("\n"); +#endif + + // then sort blocks by the number of peers with requests + // to the blocks (request the blocks with the fewest peers + // first) + std::sort(busy_blocks, busy_blocks + busy_count); + + // then insert them into the interesting_blocks vector + for (int k = 0; k < busy_count; ++k) + { + interesting_blocks.push_back( + piece_block(piece, busy_blocks[k].index)); + } + } + + void pick_time_critical_block(std::vector& peers + , std::vector& ignore_peers + , std::set& peers_with_requests + , piece_picker::downloading_piece const& pi + , time_critical_piece* i + , piece_picker* picker + , int blocks_in_piece + , int timed_out) + { + std::vector interesting_blocks; + std::vector backup1; + std::vector backup2; + std::vector ignore; + + ptime now = time_now(); + + // loop until every block has been requested from this piece (i->piece) + do + { + // if this peer's download time exceeds 2 seconds, we're done. + // We don't want to build unreasonably long request queues + if (!peers.empty() && peers[0]->download_queue_time() > milliseconds(2000)) + { +#if TORRENT_DEBUG_STREAMING > 1 + printf("queue time: %d ms, done\n" + , int(total_milliseconds(peers[0]->download_queue_time()))); +#endif + break; + } + + // pick the peer with the lowest download_queue_time that has i->piece + std::vector::iterator p = std::find_if(peers.begin(), peers.end() + , boost::bind(&peer_connection::has_piece, _1, i->piece)); + + // obviously we'll have to skip it if we don't have a peer that has + // this piece + if (p == peers.end()) + { +#if TORRENT_DEBUG_STREAMING > 1 + printf("out of peers, done\n"); +#endif + break; + } + peer_connection& c = **p; + + interesting_blocks.clear(); + backup1.clear(); + backup2.clear(); + + // specifically request blocks with no affinity towards fast or slow + // pieces. If we would, the picked block might end up in one of + // the backup lists + picker->add_blocks(i->piece, c.get_bitfield(), interesting_blocks + , backup1, backup2, blocks_in_piece, 0, c.peer_info_struct() + , ignore, piece_picker::none, 0); + + interesting_blocks.insert(interesting_blocks.end() + , backup1.begin(), backup1.end()); + interesting_blocks.insert(interesting_blocks.end() + , backup2.begin(), backup2.end()); + + bool busy_mode = false; + + if (interesting_blocks.empty()) + { + busy_mode = true; + +#if TORRENT_DEBUG_STREAMING > 1 + printf("interesting_blocks.empty()\n"); +#endif + + // there aren't any free blocks to pick, and the piece isn't + // old enough to pick busy blocks yet. break to continue to + // the next piece. + if (timed_out == 0) + { +#if TORRENT_DEBUG_STREAMING > 1 + printf("not timed out, moving on to next piece\n"); +#endif + break; + } + +#if TORRENT_DEBUG_STREAMING > 1 + printf("pick busy blocks\n"); +#endif + + pick_busy_blocks(i->piece, blocks_in_piece, timed_out + , interesting_blocks, pi); + } + + // we can't pick anything from this piece, we're done with it. + // move on to the next one + if (interesting_blocks.empty()) break; + + piece_block b = interesting_blocks.front(); + + // in busy mode we need to make sure we don't do silly + // things like requesting the same block twice from the + // same peer + std::vector const& dq = c.download_queue(); + + bool already_requested = std::find_if(dq.begin(), dq.end() + , has_block(b)) != dq.end(); + + if (already_requested) + { + // if the piece is stalled, we may end up picking a block + // that we've already requested from this peer. If so, we should + // simply disregard this peer from this piece, since this peer + // is likely to be causing the stall. We should request it + // from the next peer in the list + // the peer will be put back in the set for the next piece + ignore_peers.push_back(*p); + peers.erase(p); +#if TORRENT_DEBUG_STREAMING > 1 + printf("piece already requested by peer, try next peer\n"); +#endif + // try next peer + continue; + } + + std::vector const& rq = c.request_queue(); + + bool already_in_queue = std::find_if(rq.begin(), rq.end() + , has_block(b)) != rq.end(); + + if (already_in_queue) + { + if (!c.make_time_critical(b)) + { +#if TORRENT_DEBUG_STREAMING > 1 + printf("piece already time-critical and in queue for peer, trying next peer\n"); +#endif + ignore_peers.push_back(*p); + peers.erase(p); + continue; + } + i->last_requested = now; + +#if TORRENT_DEBUG_STREAMING > 1 + printf("piece already in queue for peer, making time-critical\n"); +#endif + + // we inserted a new block in the request queue, this + // makes us actually send it later + peers_with_requests.insert(peers_with_requests.begin(), &c); + } + else + { + if (!c.add_request(b, peer_connection::req_time_critical + | (busy_mode ? peer_connection::req_busy : 0))) + { +#if TORRENT_DEBUG_STREAMING > 1 + printf("failed to request block [%d, %d]\n" + , b.piece_index, b.block_index); +#endif + ignore_peers.push_back(*p); + peers.erase(p); + continue; + } + +#if TORRENT_DEBUG_STREAMING > 1 + printf("requested block [%d, %d]\n" + , b.piece_index, b.block_index); +#endif + peers_with_requests.insert(peers_with_requests.begin(), &c); + } + + if (!busy_mode) i->last_requested = now; + + if (i->first_requested == min_time()) i->first_requested = now; + + if (!c.can_request_time_critical()) + { +#if TORRENT_DEBUG_STREAMING > 1 + printf("peer cannot pick time critical pieces\n"); +#endif + peers.erase(p); + // try next peer + continue; + } + + // resort p, since it will have a higher download_queue_time now + while (p != peers.end()-1 && (*p)->download_queue_time() + > (*(p+1))->download_queue_time()) + { + std::iter_swap(p, p+1); + ++p; + } + } while (!interesting_blocks.empty()); + } + + void torrent::request_time_critical_pieces() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(!upload_mode()); + + // build a list of peers and sort it by download_queue_time + // we use this sorted list to determine which peer we should + // request a block from. The earlier a peer is in the list, + // the sooner we will fully download the block we request. + std::vector peers; + peers.reserve(m_connections.size()); + + // some peers are marked as not being able to request time critical + // blocks from. For instance, peers that have choked us, peers that are + // on parole (i.e. they are believed to have sent us bad data), peers + // that are being disconnected, in upload mode etc. + std::remove_copy_if(m_connections.begin(), m_connections.end() + , std::back_inserter(peers), !boost::bind(&peer_connection::can_request_time_critical, _1)); + + // sort by the time we believe it will take this peer to send us all + // blocks we've requested from it. The shorter time, the better candidate + // it is to request a time critical block from. + std::sort(peers.begin(), peers.end() + , boost::bind(&peer_connection::download_queue_time, _1, 16*1024) + < boost::bind(&peer_connection::download_queue_time, _2, 16*1024)); + + // remove the bottom 10% of peers from the candidate set. + // this is just to remove outliers that might stall downloads + int new_size = (peers.size() * 9 + 9) / 10; + TORRENT_ASSERT(new_size <= int(peers.size())); + peers.resize(new_size); + + // remember all the peers we issued requests to, so we can commit them + // at the end of this function. Instead of sending the requests right + // away, we batch them up and send them in a single write to the TCP + // socket, increasing the chance that they will all be sent in the same + // packet. + std::set peers_with_requests; + + // peers that should be temporarily ignored for a specific piece + // in order to give priority to other peers. They should be used for + // subsequent pieces, so they are stored in this vector until the + // piece is done + std::vector ignore_peers; + + ptime now = time_now_hires(); + + // now, iterate over all time critical pieces, in order of importance, and + // request them from the peers, in order of responsiveness. i.e. request + // the most time critical pieces from the fastest peers. + for (std::deque::iterator i = m_time_critical_pieces.begin() + , end(m_time_critical_pieces.end()); i != end; ++i) + { +#if TORRENT_DEBUG_STREAMING > 1 + printf("considering %d\n", i->piece); +#endif + + if (peers.empty()) + { +#if TORRENT_DEBUG_STREAMING > 1 + printf("out of peers, done\n"); +#endif + break; + } + + // the +1000 is to compensate for the fact that we only call this + // function once per second, so if we need to request it 500 ms from + // now, we should request it right away + if (i != m_time_critical_pieces.begin() && i->deadline > now + + milliseconds(m_average_piece_time + m_piece_time_deviation * 4 + 1000)) + { + // don't request pieces whose deadline is too far in the future + // this is one of the termination conditions. We don't want to + // send requests for all pieces in the torrent right away +#if TORRENT_DEBUG_STREAMING > 0 + printf("reached deadline horizon [%f + %f * 4 + 1]\n" + , m_average_piece_time / 1000.f + , m_piece_time_deviation / 1000.f); +#endif + break; + } + + piece_picker::downloading_piece pi; + m_picker->piece_info(i->piece, pi); + + // the number of "times" this piece has timed out. + int timed_out = 0; + + int blocks_in_piece = m_picker->blocks_in_piece(i->piece); + +#if TORRENT_DEBUG_STREAMING > 0 + i->timed_out = timed_out; +#endif + int free_to_request = blocks_in_piece + - pi.finished - pi.writing - pi.requested; + + if (free_to_request == 0) + { + if (i->last_requested == min_time()) + i->last_requested = now; + + // if it's been more than half of the typical download time + // of a piece since we requested the last block, allow + // one more request per block + if (m_average_piece_time > 0) + timed_out = total_milliseconds(now - i->last_requested) + / (std::max)(int(m_average_piece_time + m_piece_time_deviation / 2), 1); + +#if TORRENT_DEBUG_STREAMING > 0 + i->timed_out = timed_out; +#endif + // every block in this piece is already requested + // there's no need to consider this piece, unless it + // appears to be stalled. + if (pi.requested == 0 || timed_out == 0) + { +#if TORRENT_DEBUG_STREAMING > 1 + printf("skipping %d (full) [req: %d timed_out: %d ]\n" + , i->piece, pi.requested + , timed_out); +#endif + + // if requested is 0, it meants all blocks have been received, and + // we're just waiting for it to flush them to disk. + // if last_requested is recent enough, we should give it some + // more time + // skip to the next piece + continue; + } + + // it's been too long since we requested the last block from + // this piece. Allow re-requesting blocks from this piece +#if TORRENT_DEBUG_STREAMING > 1 + printf("timed out [average-piece-time: %d ms ]\n" + , m_average_piece_time); +#endif + } + + // pick all blocks for this piece. the peers list is kept up to date + // and sorted. when we issue a request to a peer, its download queue + // time will increase and it may need to be bumped in the peers list, + // since it's ordered by download queue time + pick_time_critical_block(peers, ignore_peers + , peers_with_requests + , pi, &*i, m_picker.get() + , blocks_in_piece, timed_out); + + // put back the peers we ignored into the peer list for the next piece + if (!ignore_peers.empty()) + { + peers.insert(peers.begin(), ignore_peers.begin(), ignore_peers.end()); + ignore_peers.clear(); + + // TODO: instead of resorting the whole list, insert the peers + // directly into the right place + std::sort(peers.begin(), peers.end() + , boost::bind(&peer_connection::download_queue_time, _1, 16*1024) + < boost::bind(&peer_connection::download_queue_time, _2, 16*1024)); + } + + // if this peer's download time exceeds 2 seconds, we're done. + // We don't want to build unreasonably long request queues + if (!peers.empty() && peers[0]->download_queue_time() > milliseconds(2000)) + break; + } + + // commit all the time critical requests + for (std::set::iterator i = peers_with_requests.begin() + , end(peers_with_requests.end()); i != end; ++i) + { + (*i)->send_block_requests(); + } + } + + std::set torrent::web_seeds(web_seed_entry::type_t type) const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + std::set ret; + for (std::list::const_iterator i = m_web_seeds.begin() + , end(m_web_seeds.end()); i != end; ++i) + { + if (i->peer_info.banned) continue; + if (i->type != type) continue; + ret.insert(i->url); + } + return ret; + } + + void torrent::remove_web_seed(std::string const& url, web_seed_entry::type_t type) + { + std::list::iterator i = std::find_if(m_web_seeds.begin(), m_web_seeds.end() + , (boost::bind(&web_seed_entry::url, _1) + == url && boost::bind(&web_seed_entry::type, _1) == type)); + if (i != m_web_seeds.end()) remove_web_seed(i); + } + + void torrent::disconnect_web_seed(peer_connection* p) + { + std::list::iterator i = std::find_if(m_web_seeds.begin(), m_web_seeds.end() + , (boost::bind(&policy::peer::connection, boost::bind(&web_seed_entry::peer_info, _1)) == p)); + // this happens if the web server responded with a redirect + // or with something incorrect, so that we removed the web seed + // immediately, before we disconnected + if (i == m_web_seeds.end()) return; + + TORRENT_ASSERT(i->resolving == false); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("disconnect web seed: \"%s\"", i->url.c_str()); +#endif + TORRENT_ASSERT(i->peer_info.connection); + i->peer_info.connection = 0; + } + + void torrent::remove_web_seed(peer_connection* p) + { + std::list::iterator i = std::find_if(m_web_seeds.begin(), m_web_seeds.end() + , (boost::bind(&policy::peer::connection, boost::bind(&web_seed_entry::peer_info, _1)) == p)); + TORRENT_ASSERT(i != m_web_seeds.end()); + if (i == m_web_seeds.end()) return; + p->set_peer_info(0); + if (has_picker()) picker().clear_peer(&i->peer_info); + m_web_seeds.erase(i); + } + + void torrent::retry_web_seed(peer_connection* p, int retry) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + std::list::iterator i = std::find_if(m_web_seeds.begin(), m_web_seeds.end() + , (boost::bind(&policy::peer::connection, boost::bind(&web_seed_entry::peer_info, _1)) == p)); + + TORRENT_ASSERT(i != m_web_seeds.end()); + if (i == m_web_seeds.end()) return; + if (retry == 0) retry = m_ses.settings().urlseed_wait_retry; + i->retry = time_now() + seconds(retry); + } + + bool torrent::try_connect_peer() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(want_more_peers()); + bool ret = m_policy.connect_one_peer(m_ses.session_time()); + return ret; + } + + void torrent::add_peer(tcp::endpoint const& adr, int source) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + peer_id id(0); + m_policy.add_peer(adr, id, source, 0); + + state_updated(); + } + + void torrent::async_verify_piece(int piece_index, boost::function const& f) + { + TORRENT_ASSERT(m_ses.is_network_thread()); +// INVARIANT_CHECK; + + TORRENT_ASSERT(m_storage); + TORRENT_ASSERT(m_storage->refcount() > 0); + TORRENT_ASSERT(piece_index >= 0); + TORRENT_ASSERT(piece_index < m_torrent_file->num_pieces()); + TORRENT_ASSERT(piece_index < (int)m_picker->num_pieces()); + TORRENT_ASSERT(!m_picker || !m_picker->have_piece(piece_index)); +#ifdef TORRENT_DEBUG + if (m_picker) + { + int blocks_in_piece = m_picker->blocks_in_piece(piece_index); + for (int i = 0; i < blocks_in_piece; ++i) + { + TORRENT_ASSERT(m_picker->num_peers(piece_block(piece_index, i)) == 0); + } + } +#endif + + m_storage->async_hash(piece_index, boost::bind(&torrent::on_piece_verified + , shared_from_this(), _1, _2, f)); +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif + } + + void torrent::on_piece_verified(int ret, disk_io_job const& j + , boost::function f) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + // return value: + // 0: success, piece passed hash check + // -1: disk failure + // -2: hash check failed + + state_updated(); + + if (ret == -1) handle_disk_error(j); + f(ret); + } + + tcp::endpoint torrent::current_tracker() const + { + return m_tracker_address; + } + + announce_entry* torrent::find_tracker(tracker_request const& r) + { + std::vector::iterator i = std::find_if( + m_trackers.begin(), m_trackers.end() + , boost::bind(&announce_entry::url, _1) == r.url); + if (i == m_trackers.end()) return 0; + return &*i; + } + +#if !TORRENT_NO_FPU + void torrent::file_progress(std::vector& fp) const + { + fp.clear(); + if (!valid_metadata()) return; + + fp.resize(m_torrent_file->num_files(), 1.f); + if (is_seed()) return; + + std::vector progress; + file_progress(progress); + for (int i = 0; i < m_torrent_file->num_files(); ++i) + { + file_entry const& f = m_torrent_file->file_at(i); + if (f.size == 0) fp[i] = 1.f; + else fp[i] = float(progress[i]) / f.size; + } + } +#endif + + void torrent::file_progress(std::vector& fp, int flags) const + { + if (!valid_metadata()) + { + fp.clear(); + return; + } + + fp.resize(m_torrent_file->num_files(), 0); + + // if we're a seed, just fill in the full file sizes as a shortcut + if (is_seed()) + { + for (int i = 0; i < m_torrent_file->num_files(); ++i) + fp[i] = m_torrent_file->files().file_size(i); + return; + } + + // we're not a seed and we don't have a picker, that means we donn't + // have any piece yet. + if (!has_picker()) + return; + + if (flags & torrent_handle::piece_granularity) + { + std::copy(m_file_progress.begin(), m_file_progress.end(), fp.begin()); + return; + } + + TORRENT_ASSERT(has_picker()); + + for (int i = 0; i < m_torrent_file->num_files(); ++i) + { + peer_request ret = m_torrent_file->files().map_file(i, 0, 0); + size_type size = m_torrent_file->files().file_size(i); + TORRENT_ASSERT(ret.piece >= 0); + TORRENT_ASSERT(ret.piece < m_picker->num_pieces()); + if (ret.piece < 0 || ret.piece >= m_picker->num_pieces()) + { + // this is not supposed to happen. + fp[i] = 0; + continue; + } + + size_type done = 0; + while (size > 0) + { + TORRENT_ASSERT(ret.piece < m_picker->num_pieces()); + TORRENT_ASSERT(ret.piece >= 0); + + size_type bytes_step = (std::min)(size_type(m_torrent_file->piece_size(ret.piece) + - ret.start), size); + + if (m_picker->have_piece(ret.piece)) done += bytes_step; + ++ret.piece; + ret.start = 0; + size -= bytes_step; + } + TORRENT_ASSERT(size == 0); + + fp[i] = done; + } + + const std::vector& q + = m_picker->get_download_queue(); + + file_storage const& fs = m_torrent_file->files(); + for (std::vector::const_iterator + i = q.begin(), end(q.end()); i != end; ++i) + { + size_type offset = size_type(i->index) * m_torrent_file->piece_length(); + int file = fs.file_index_at_offset(offset); + int num_blocks = m_picker->blocks_in_piece(i->index); + piece_picker::block_info const* info = i->info; + for (int k = 0; k < num_blocks; ++k) + { + TORRENT_ASSERT(file < fs.num_files()); + TORRENT_ASSERT(offset == size_type(i->index) * m_torrent_file->piece_length() + + k * block_size()); + TORRENT_ASSERT(offset < m_torrent_file->total_size()); + while (offset >= fs.file_offset(file) + fs.file_size(file)) + { + ++file; + } + TORRENT_ASSERT(file < fs.num_files()); + + size_type block = block_size(); + + if (info[k].state == piece_picker::block_info::state_none) + { + offset += block; + continue; + } + + if (info[k].state == piece_picker::block_info::state_requested) + { + block = 0; + policy::peer* p = static_cast(info[k].peer); + if (p && p->connection) + { + boost::optional pbp + = p->connection->downloading_piece_progress(); + if (pbp && pbp->piece_index == i->index && pbp->block_index == k) + block = pbp->bytes_downloaded; + TORRENT_ASSERT(block <= block_size()); + } + + if (block == 0) + { + offset += block_size(); + continue; + } + } + + if (offset + block > fs.file_offset(file) + fs.file_size(file)) + { + int left_over = int(block_size() - block); + // split the block on multiple files + while (block > 0) + { + TORRENT_ASSERT(offset <= fs.file_offset(file) + fs.file_size(file)); + size_type slice = (std::min)(fs.file_offset(file) + fs.file_size(file) - offset + , block); + fp[file] += slice; + offset += slice; + block -= slice; + TORRENT_ASSERT(offset <= fs.file_offset(file) + fs.file_size(file)); + if (offset == fs.file_offset(file) + fs.file_size(file)) + { + ++file; + if (file == fs.num_files()) + { + offset += block; + break; + } + } + } + offset += left_over; + TORRENT_ASSERT(offset == size_type(i->index) * m_torrent_file->piece_length() + + (k+1) * block_size()); + } + else + { + fp[file] += block; + offset += block_size(); + } + TORRENT_ASSERT(file <= m_torrent_file->num_files()); + } + } + } + + void torrent::new_external_ip() + { + m_policy.clear_peer_prio(); + } + + void torrent::set_state(torrent_status::state_t s) + { + TORRENT_ASSERT(m_ses.is_network_thread()); +#if TORRENT_USE_ASSERTS + if (s != torrent_status::checking_files + && s != torrent_status::queued_for_checking) + { + // the only valid transition away from queued_for_checking + // is to checking_files. One exception is to finished + // in case all the files are marked with priority 0 + if (m_queued_for_checking) + { + std::vector pieces; + m_picker->piece_priorities(pieces); + // make sure all pieces have priority 0 + TORRENT_ASSERT(std::accumulate(pieces.begin(), pieces.end(), 0) == 0); + } + } + if (s == torrent_status::seeding) + TORRENT_ASSERT(is_seed()); + + if (s == torrent_status::seeding) + TORRENT_ASSERT(is_seed()); + if (s == torrent_status::finished) + TORRENT_ASSERT(is_finished()); + if (s == torrent_status::downloading && m_state == torrent_status::finished) + TORRENT_ASSERT(!is_finished()); +#endif + + if (int(m_state) == s) return; + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(state_changed_alert(get_handle() + , s, (torrent_status::state_t)m_state)); + } + + if (s == torrent_status::finished + && m_ses.m_alerts.should_post()) + { + alerts().post_alert(torrent_finished_alert( + get_handle())); + } + + + m_state = s; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("set_state() %d", m_state); +#endif + + update_guage(); + + state_updated(); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->on_state(m_state); + } TORRENT_CATCH (std::exception&) {} + } +#endif + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + void torrent::notify_extension_add_peer(tcp::endpoint const& ip + , int src, int flags) + { + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->on_add_peer(ip, src, flags); + } TORRENT_CATCH (std::exception&) {} + } + } +#endif + + void torrent::state_updated() + { + // if this fails, this function is probably called + // from within the torrent constructor, which it + // shouldn't be. Whichever function ends up calling + // this should probably be moved to torrent::start() + TORRENT_ASSERT(shared_from_this()); + + // we can't call state_updated() while the session + // is building the status update alert + TORRENT_ASSERT(!m_ses.m_posting_torrent_updates); + + // we're either not subscribing to this torrent, or + // it has already been updated this round, no need to + // add it to the list twice + if (!m_state_subscription) return; + if (m_in_state_updates) + { +#ifndef TORRENT_DISABLE_INVARIANT_CHECKS + TORRENT_ASSERT(m_ses.in_state_updates(shared_from_this())); +#endif + return; + } + + m_ses.add_to_update_queue(shared_from_this()); + m_in_state_updates = true; + } + + void torrent::status(torrent_status* st, boost::uint32_t flags) + { + INVARIANT_CHECK; + + ptime now = time_now(); + + st->handle = get_handle(); + st->info_hash = info_hash(); + + if (flags & torrent_handle::query_name) + st->name = name(); + + if (flags & torrent_handle::query_save_path) + st->save_path = save_path(); + + if (flags & torrent_handle::query_torrent_file) + st->torrent_file = m_torrent_file; + + st->has_incoming = m_has_incoming; + if (m_error) st->error = convert_from_native(m_error.message()) + ": " + m_error_file; + st->seed_mode = m_seed_mode; + st->moving_storage = m_moving_storage; + + st->added_time = m_added_time; + st->completed_time = m_completed_time; + + st->last_scrape = m_last_scrape; + st->share_mode = m_share_mode; + st->upload_mode = m_upload_mode; + st->up_bandwidth_queue = 0; + st->down_bandwidth_queue = 0; + st->priority = m_priority; + + st->num_peers = int(m_connections.size()) - m_num_connecting; + + st->list_peers = m_policy.num_peers(); + st->list_seeds = m_policy.num_seeds(); + st->connect_candidates = m_policy.num_connect_candidates(); + st->seed_rank = seed_rank(settings()); + + st->all_time_upload = m_total_uploaded; + st->all_time_download = m_total_downloaded; + + // activity time + st->finished_time = m_finished_time; + st->active_time = m_active_time; + st->seeding_time = m_seeding_time; + st->time_since_upload = m_last_upload; + st->time_since_download = m_last_download; + + st->storage_mode = (storage_mode_t)m_storage_mode; + + st->num_complete = (m_complete == 0xffffff) ? -1 : m_complete; + st->num_incomplete = (m_incomplete == 0xffffff) ? -1 : m_incomplete; + st->paused = is_torrent_paused(); + st->auto_managed = m_auto_managed; + st->sequential_download = m_sequential_download; + st->is_seeding = is_seed(); + st->is_finished = is_finished(); + st->super_seeding = m_super_seeding; + st->has_metadata = valid_metadata(); + bytes_done(*st, flags & torrent_handle::query_accurate_download_counters); + TORRENT_ASSERT(st->total_wanted_done >= 0); + TORRENT_ASSERT(st->total_done >= st->total_wanted_done); + + // payload transfer + st->total_payload_download = m_stat.total_payload_download(); + st->total_payload_upload = m_stat.total_payload_upload(); + + // total transfer + st->total_download = m_stat.total_payload_download() + + m_stat.total_protocol_download(); + st->total_upload = m_stat.total_payload_upload() + + m_stat.total_protocol_upload(); + + // failed bytes + st->total_failed_bytes = m_total_failed_bytes; + st->total_redundant_bytes = m_total_redundant_bytes; + + // transfer rate + st->download_rate = m_stat.download_rate(); + st->upload_rate = m_stat.upload_rate(); + st->download_payload_rate = m_stat.download_payload_rate(); + st->upload_payload_rate = m_stat.upload_payload_rate(); + + if (m_waiting_tracker && !is_paused()) + st->next_announce = boost::posix_time::seconds( + total_seconds(next_announce() - now)); + else + st->next_announce = boost::posix_time::seconds(0); + + if (st->next_announce.is_negative()) + st->next_announce = boost::posix_time::seconds(0); + + st->announce_interval = boost::posix_time::seconds(0); + + st->current_tracker.clear(); + if (m_last_working_tracker >= 0) + { + TORRENT_ASSERT(m_last_working_tracker < int(m_trackers.size())); + st->current_tracker = m_trackers[m_last_working_tracker].url; + } + else + { + std::vector::const_iterator i; + for (i = m_trackers.begin(); i != m_trackers.end(); ++i) + { + if (!i->updating) continue; + st->current_tracker = i->url; + break; + } + } + + if ((flags & torrent_handle::query_verified_pieces)) + { + st->verified_pieces = m_verified; + } + + st->num_uploads = m_num_uploads; + st->uploads_limit = m_max_uploads == (1<<24)-1 ? -1 : m_max_uploads; + st->num_connections = int(m_connections.size()); + st->connections_limit = m_max_connections == (1<<24)-1 ? -1 : m_max_connections; + // if we don't have any metadata, stop here + + st->queue_position = queue_position(); + st->need_save_resume = need_save_resume_data(); + st->ip_filter_applies = m_apply_ip_filter; + + st->state = (torrent_status::state_t)m_state; + + if (!valid_metadata()) + { + st->state = torrent_status::downloading_metadata; + st->progress_ppm = m_progress_ppm; +#if !TORRENT_NO_FPU + st->progress = m_progress_ppm / 1000000.f; +#endif + st->block_size = 0; + return; + } + + st->block_size = block_size(); + + if (m_state == torrent_status::checking_files) + { + st->progress_ppm = m_progress_ppm; +#if !TORRENT_NO_FPU + st->progress = m_progress_ppm / 1000000.f; +#endif + } + else if (st->total_wanted == 0) + { + st->progress_ppm = 1000000; + st->progress = 1.f; + } + else + { + st->progress_ppm = st->total_wanted_done * 1000000 + / st->total_wanted; +#if !TORRENT_NO_FPU + st->progress = st->progress_ppm / 1000000.f; +#endif + } + + if (has_picker() && (flags & torrent_handle::query_pieces)) + { + st->sparse_regions = m_picker->sparse_regions(); + int num_pieces = m_picker->num_pieces(); + st->pieces.resize(num_pieces, false); + for (int i = 0; i < num_pieces; ++i) + if (m_picker->have_piece(i)) st->pieces.set_bit(i); + } + else if (is_seed()) + { + int num_pieces = m_torrent_file->num_pieces(); + st->pieces.resize(num_pieces, true); + } + st->num_pieces = num_have(); + st->num_seeds = num_seeds(); + if ((flags & torrent_handle::query_distributed_copies) && m_picker.get()) + { + boost::tie(st->distributed_full_copies, st->distributed_fraction) = + m_picker->distributed_copies(); +#if TORRENT_NO_FPU + st->distributed_copies = -1.f; +#else + st->distributed_copies = st->distributed_full_copies + + float(st->distributed_fraction) / 1000; +#endif + } + else + { + st->distributed_full_copies = -1; + st->distributed_fraction = -1; + st->distributed_copies = -1.f; + } + + st->last_seen_complete = m_swarm_last_seen_complete; + } + + void torrent::add_redundant_bytes(int b, torrent::wasted_reason_t reason) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(b > 0); + m_total_redundant_bytes += b; + m_ses.add_redundant_bytes(b, reason); +// TORRENT_ASSERT(m_total_redundant_bytes + m_total_failed_bytes +// <= m_stat.total_payload_download()); + } + + void torrent::add_failed_bytes(int b) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(b > 0); + m_total_failed_bytes += b; + m_ses.add_failed_bytes(b); +// TORRENT_ASSERT(m_total_redundant_bytes + m_total_failed_bytes +// <= m_stat.total_payload_download()); + } + + int torrent::num_seeds() const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + int ret = 0; + for (std::set::const_iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + if ((*i)->is_seed()) ++ret; + return ret; + } + + void torrent::tracker_request_error(tracker_request const& r + , int response_code, error_code const& ec, std::string const& msg + , int retry_interval) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("*** tracker error: (%d) %s %s", ec.value() + , ec.message().c_str(), msg.c_str()); +#endif + if (r.kind == tracker_request::announce_request) + { + announce_entry* ae = find_tracker(r); + if (ae) + { + ae->failed(settings(), retry_interval); + ae->last_error = ec; + ae->message = msg; + int tracker_index = ae - &m_trackers[0]; +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("*** increment tracker fail count [%d]", ae->fails); +#endif + // never talk to this tracker again + if (response_code == 410) ae->fail_limit = 1; + + deprioritize_tracker(tracker_index); + } + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(tracker_error_alert(get_handle() + , ae?ae->fails:0, response_code, r.url, ec, msg)); + } + } + else if (r.kind == tracker_request::scrape_request) + { + if (response_code == 410) + { + // never talk to this tracker again + announce_entry* ae = find_tracker(r); + if (ae) ae->fail_limit = 1; + } + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(scrape_failed_alert(get_handle(), r.url, ec)); + } + } + // announce to the next working tracker + if ((!m_abort && !is_paused()) || r.event == tracker_request::stopped) + announce_with_tracker(r.event); + update_tracker_timer(time_now()); + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + void torrent::debug_log(const char* fmt, ...) const + { + if (!m_ses.m_logger) return; + + va_list v; + va_start(v, fmt); + + char usr[1024]; + vsnprintf(usr, sizeof(usr), fmt, v); + va_end(v); + char buf[1280]; + snprintf(buf, sizeof(buf), "%s: %s: %s\n", time_now_string() + , to_hex(info_hash().to_string()).substr(0, 6).c_str(), usr); + (*m_ses.m_logger) << buf; + } +#endif + +} + diff --git a/apps/Launcher/ext/libtorrent/src/torrent_handle.cpp b/apps/Launcher/ext/libtorrent/src/torrent_handle.cpp new file mode 100644 index 0000000000..4e13755100 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/torrent_handle.cpp @@ -0,0 +1,914 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/utf8.hpp" +#include "libtorrent/thread.hpp" + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std +{ + using ::srand; + using ::isalnum; +}; +#endif + +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + + torrent_status::torrent_status() + : total_download(0) + , total_upload(0) + , total_payload_download(0) + , total_payload_upload(0) + , total_failed_bytes(0) + , total_redundant_bytes(0) + , total_done(0) + , total_wanted_done(0) + , total_wanted(0) + , all_time_upload(0) + , all_time_download(0) + , added_time(0) + , completed_time(0) + , last_seen_complete(0) + , storage_mode(storage_mode_sparse) + , progress(0.f) + , progress_ppm(0) + , queue_position(0) + , download_rate(0) + , upload_rate(0) + , download_payload_rate(0) + , upload_payload_rate(0) + , num_seeds(0) + , num_peers(0) + , num_complete(-1) + , num_incomplete(-1) + , list_seeds(0) + , list_peers(0) + , connect_candidates(0) + , num_pieces(0) + , distributed_full_copies(0) + , distributed_fraction(0) + , distributed_copies(0.f) + , block_size(0) + , num_uploads(0) + , num_connections(0) + , uploads_limit(0) + , connections_limit(0) + , up_bandwidth_queue(0) + , down_bandwidth_queue(0) + , time_since_upload(0) + , time_since_download(0) + , active_time(0) + , finished_time(0) + , seeding_time(0) + , seed_rank(0) + , last_scrape(0) + , sparse_regions(0) + , priority(0) + , state(checking_resume_data) + , need_save_resume(false) + , ip_filter_applies(true) + , upload_mode(false) + , share_mode(false) + , super_seeding(false) + , paused(false) + , auto_managed(false) + , sequential_download(false) + , is_seeding(false) + , is_finished(false) + , has_metadata(false) + , has_incoming(false) + , seed_mode(false) + , moving_storage(false) + , info_hash(0) + {} + + torrent_status::~torrent_status() {} + + template + void fun_ret(R* ret, bool* done, condition_variable* e, mutex* m, boost::function f) + { + *ret = f(); + mutex::scoped_lock l(*m); + *done = true; + e->notify_all(); + } + + // defined in session.cpp + void fun_wrap(bool* done, condition_variable* e, mutex* m, boost::function f); + +#define TORRENT_ASYNC_CALL(x) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (!t) return; \ + session_impl& ses = t->session(); \ + ses.m_io_service.dispatch(boost::bind(&torrent:: x, t)) + +#define TORRENT_ASYNC_CALL1(x, a1) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (!t) return; \ + session_impl& ses = t->session(); \ + ses.m_io_service.dispatch(boost::bind(&torrent:: x, t, a1)) + +#define TORRENT_ASYNC_CALL2(x, a1, a2) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (!t) return; \ + session_impl& ses = t->session(); \ + ses.m_io_service.dispatch(boost::bind(&torrent:: x, t, a1, a2)) + +#define TORRENT_ASYNC_CALL3(x, a1, a2, a3) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (!t) return; \ + session_impl& ses = t->session(); \ + ses.m_io_service.dispatch(boost::bind(&torrent:: x, t, a1, a2, a3)) + +#define TORRENT_ASYNC_CALL4(x, a1, a2, a3, a4) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (!t) return; \ + session_impl& ses = t->session(); \ + ses.m_io_service.dispatch(boost::bind(&torrent:: x, t, a1, a2, a3, a4)) + +#define TORRENT_SYNC_CALL(x) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (!t) return; \ + bool done = false; \ + session_impl& ses = t->session(); \ + mutex::scoped_lock l(ses.mut); \ + ses.m_io_service.dispatch(boost::bind(&fun_wrap, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&torrent:: x, t)))); \ + while (!done) { ses.cond.wait(l); } + +#define TORRENT_SYNC_CALL1(x, a1) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (t) { \ + bool done = false; \ + session_impl& ses = t->session(); \ + mutex::scoped_lock l(ses.mut); \ + ses.m_io_service.dispatch(boost::bind(&fun_wrap, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&torrent:: x, t, a1)))); \ + t.reset(); \ + while (!done) { ses.cond.wait(l); }; } + +#define TORRENT_SYNC_CALL2(x, a1, a2) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (t) { \ + bool done = false; \ + session_impl& ses = t->session(); \ + mutex::scoped_lock l(ses.mut); \ + ses.m_io_service.dispatch(boost::bind(&fun_wrap, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&torrent:: x, t, a1, a2)))); \ + t.reset(); \ + while (!done) { ses.cond.wait(l); }; } + +#define TORRENT_SYNC_CALL3(x, a1, a2, a3) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (t) { \ + bool done = false; \ + session_impl& ses = t->session(); \ + mutex::scoped_lock l(ses.mut); \ + ses.m_io_service.dispatch(boost::bind(&fun_wrap, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&torrent:: x, t, a1, a2, a3)))); \ + t.reset(); \ + while (!done) { ses.cond.wait(l); }; } + +#define TORRENT_SYNC_CALL_RET(type, def, x) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (!t) return def; \ + bool done = false; \ + session_impl& ses = t->session(); \ + type r; \ + mutex::scoped_lock l(ses.mut); \ + ses.m_io_service.dispatch(boost::bind(&fun_ret, &r, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&torrent:: x, t)))); \ + t.reset(); \ + while (!done) { ses.cond.wait(l); } + +#define TORRENT_SYNC_CALL_RET1(type, def, x, a1) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (!t) return def; \ + bool done = false; \ + session_impl& ses = t->session(); \ + type r; \ + mutex::scoped_lock l(ses.mut); \ + ses.m_io_service.dispatch(boost::bind(&fun_ret, &r, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&torrent:: x, t, a1)))); \ + t.reset(); \ + while (!done) { ses.cond.wait(l); } + +#define TORRENT_SYNC_CALL_RET2(type, def, x, a1, a2) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (!t) return def; \ + bool done = false; \ + session_impl& ses = t->session(); \ + type r; \ + mutex::scoped_lock l(ses.mut); \ + ses.m_io_service.dispatch(boost::bind(&fun_ret, &r, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&torrent:: x, t, a1, a2)))); \ + t.reset(); \ + while (!done) { ses.cond.wait(l); } + +#ifndef BOOST_NO_EXCEPTIONS + void throw_invalid_handle() + { + throw libtorrent_exception(errors::invalid_torrent_handle); + } +#endif + + sha1_hash torrent_handle::info_hash() const + { + boost::shared_ptr t = m_torrent.lock(); + const static sha1_hash empty; + if (!t) return empty; + return t->info_hash(); + } + + int torrent_handle::max_uploads() const + { + TORRENT_SYNC_CALL_RET(int, 0, max_uploads); + return r; + } + + void torrent_handle::set_max_uploads(int max_uploads) const + { + TORRENT_ASSERT_PRECOND(max_uploads >= 2 || max_uploads == -1); + TORRENT_ASYNC_CALL2(set_max_uploads, max_uploads, true); + } + + int torrent_handle::max_connections() const + { + TORRENT_SYNC_CALL_RET(int, 0, max_connections); + return r; + } + + void torrent_handle::set_max_connections(int max_connections) const + { + TORRENT_ASSERT_PRECOND(max_connections >= 2 || max_connections == -1); + TORRENT_ASYNC_CALL2(set_max_connections, max_connections, true); + } + + void torrent_handle::set_upload_limit(int limit) const + { + TORRENT_ASSERT_PRECOND(limit >= -1); + TORRENT_ASYNC_CALL2(set_upload_limit, limit, true); + } + + int torrent_handle::upload_limit() const + { + TORRENT_SYNC_CALL_RET(int, 0, upload_limit); + return r; + } + + void torrent_handle::set_download_limit(int limit) const + { + TORRENT_ASSERT_PRECOND(limit >= -1); + TORRENT_ASYNC_CALL2(set_download_limit, limit, true); + } + + int torrent_handle::download_limit() const + { + TORRENT_SYNC_CALL_RET(int, 0, download_limit); + return r; + } + + void torrent_handle::move_storage( + std::string const& save_path, int flags) const + { + TORRENT_ASYNC_CALL2(move_storage, save_path, flags); + } + +#if TORRENT_USE_WSTRING +#ifndef TORRENT_NO_DEPRECATE + void torrent_handle::move_storage( + std::wstring const& save_path, int flags) const + { + std::string utf8; + wchar_utf8(save_path, utf8); + TORRENT_ASYNC_CALL2(move_storage, utf8, flags); + } + + void torrent_handle::rename_file(int index, std::wstring const& new_name) const + { + std::string utf8; + wchar_utf8(new_name, utf8); + TORRENT_ASYNC_CALL2(rename_file, index, utf8); + } +#endif // TORRENT_NO_DEPRECATE +#endif // TORRENT_USE_WSTRING + + void torrent_handle::rename_file(int index, std::string const& new_name) const + { + TORRENT_ASYNC_CALL2(rename_file, index, new_name); + } + + void torrent_handle::add_extension( + boost::function(torrent*, void*)> const& ext + , void* userdata) + { +#ifndef TORRENT_DISABLE_EXTENSIONS + TORRENT_ASYNC_CALL2(add_extension, ext, userdata); +#endif + } + + bool torrent_handle::set_metadata(char const* metadata, int size) const + { + TORRENT_SYNC_CALL_RET2(bool, false, set_metadata, metadata, size); + return r; + } + + void torrent_handle::pause(int flags) const + { + TORRENT_ASYNC_CALL1(pause, bool(flags & graceful_pause)); + } + + void torrent_handle::apply_ip_filter(bool b) const + { + TORRENT_ASYNC_CALL1(set_apply_ip_filter, b); + } + + void torrent_handle::set_share_mode(bool b) const + { + TORRENT_ASYNC_CALL1(set_share_mode, b); + } + + void torrent_handle::set_upload_mode(bool b) const + { + TORRENT_ASYNC_CALL1(set_upload_mode, b); + } + + void torrent_handle::flush_cache() const + { + TORRENT_ASYNC_CALL(flush_cache); + } + + void torrent_handle::set_ssl_certificate( + std::string const& certificate + , std::string const& private_key + , std::string const& dh_params + , std::string const& passphrase) + { +#ifdef TORRENT_USE_OPENSSL + TORRENT_ASYNC_CALL4(set_ssl_cert, certificate, private_key, dh_params, passphrase); +#endif + } + + void torrent_handle::set_ssl_certificate_buffer( + std::string const& certificate + , std::string const& private_key + , std::string const& dh_params) + { +#ifdef TORRENT_USE_OPENSSL + TORRENT_ASYNC_CALL3(set_ssl_cert_buffer, certificate, private_key, dh_params); +#endif + } + + void torrent_handle::save_resume_data(int f) const + { + TORRENT_ASYNC_CALL1(save_resume_data, f); + } + + bool torrent_handle::need_save_resume_data() const + { + TORRENT_SYNC_CALL_RET(bool, false, need_save_resume_data); + return r; + } + + void torrent_handle::force_recheck() const + { + TORRENT_ASYNC_CALL(force_recheck); + } + + void torrent_handle::resume() const + { + TORRENT_ASYNC_CALL(resume); + } + + void torrent_handle::auto_managed(bool m) const + { + TORRENT_ASYNC_CALL1(auto_managed, m); + } + + void torrent_handle::set_priority(int p) const + { + TORRENT_ASYNC_CALL1(set_priority, p); + } + + int torrent_handle::queue_position() const + { + TORRENT_SYNC_CALL_RET(int, -1, queue_position); + return r; + } + + void torrent_handle::queue_position_up() const + { + TORRENT_ASYNC_CALL(queue_up); + } + + void torrent_handle::queue_position_down() const + { + TORRENT_ASYNC_CALL(queue_down); + } + + void torrent_handle::queue_position_top() const + { + TORRENT_ASYNC_CALL1(set_queue_position, 0); + } + + void torrent_handle::queue_position_bottom() const + { + TORRENT_ASYNC_CALL1(set_queue_position, INT_MAX); + } + + void torrent_handle::clear_error() const + { + TORRENT_ASYNC_CALL(clear_error); + } + + void torrent_handle::set_tracker_login(std::string const& name + , std::string const& password) const + { + TORRENT_ASYNC_CALL2(set_tracker_login, name, password); + } + + void torrent_handle::file_progress(std::vector& progress, int flags) const + { + TORRENT_SYNC_CALL2(file_progress, boost::ref(progress), flags); + } + + torrent_status torrent_handle::status(boost::uint32_t flags) const + { + torrent_status st; + TORRENT_SYNC_CALL2(status, &st, flags); + return st; + } + + void torrent_handle::set_sequential_download(bool sd) const + { + TORRENT_ASYNC_CALL1(set_sequential_download, sd); + } + + void torrent_handle::piece_availability(std::vector& avail) const + { + TORRENT_SYNC_CALL1(piece_availability, boost::ref(avail)); + } + + void torrent_handle::piece_priority(int index, int priority) const + { + TORRENT_ASYNC_CALL2(set_piece_priority, index, priority); + } + + int torrent_handle::piece_priority(int index) const + { + TORRENT_SYNC_CALL_RET1(int, 0, piece_priority, index); + return r; + } + + void torrent_handle::prioritize_pieces(std::vector const& pieces) const + { + TORRENT_ASYNC_CALL1(prioritize_pieces, pieces); + } + + std::vector torrent_handle::piece_priorities() const + { + std::vector ret; + TORRENT_SYNC_CALL1(piece_priorities, &ret); + return ret; + } + + void torrent_handle::file_priority(int index, int priority) const + { + TORRENT_ASYNC_CALL2(set_file_priority, index, priority); + } + + int torrent_handle::file_priority(int index) const + { + TORRENT_SYNC_CALL_RET1(int, 0, file_priority, index); + return r; + } + + void torrent_handle::prioritize_files(std::vector const& files) const + { + TORRENT_ASYNC_CALL1(prioritize_files, files); + } + + std::vector torrent_handle::file_priorities() const + { + std::vector ret; + TORRENT_SYNC_CALL1(file_priorities, &ret); + return ret; + } + + void torrent_handle::use_interface(const char* net_interface) const + { + TORRENT_ASYNC_CALL1(use_interface, std::string(net_interface)); + } + +#ifndef TORRENT_NO_DEPRECATE +// ============ start deprecation =============== + +#if !TORRENT_NO_FPU + void torrent_handle::file_progress(std::vector& progress) const + { + TORRENT_SYNC_CALL1(file_progress, boost::ref(progress)); + } +#endif + + int torrent_handle::get_peer_upload_limit(tcp::endpoint ip) const + { + TORRENT_SYNC_CALL_RET1(int, -1, get_peer_upload_limit, ip); + return r; + } + + int torrent_handle::get_peer_download_limit(tcp::endpoint ip) const + { + TORRENT_SYNC_CALL_RET1(int, -1, get_peer_download_limit, ip); + return r; + } + + void torrent_handle::set_peer_upload_limit(tcp::endpoint ip, int limit) const + { + TORRENT_ASSERT_PRECOND(limit >= -1); + TORRENT_ASYNC_CALL2(set_peer_upload_limit, ip, limit); + } + + void torrent_handle::set_peer_download_limit(tcp::endpoint ip, int limit) const + { + TORRENT_ASSERT_PRECOND(limit >= -1); + TORRENT_ASYNC_CALL2(set_peer_download_limit, ip, limit); + } + + void torrent_handle::set_ratio(float ratio) const + { + TORRENT_ASSERT_PRECOND(ratio >= 0.f); + } + + bool torrent_handle::is_seed() const + { + TORRENT_SYNC_CALL_RET(bool, false, is_seed); + return r; + } + + bool torrent_handle::is_finished() const + { + TORRENT_SYNC_CALL_RET(bool, false, is_finished); + return r; + } + + bool torrent_handle::is_paused() const + { + TORRENT_SYNC_CALL_RET(bool, false, is_torrent_paused); + return r; + } + + bool torrent_handle::is_sequential_download() const + { + TORRENT_SYNC_CALL_RET(bool, false, is_sequential_download); + return r; + } + + bool torrent_handle::is_auto_managed() const + { + TORRENT_SYNC_CALL_RET(bool, false, is_auto_managed); + return r; + } + + bool torrent_handle::has_metadata() const + { + TORRENT_SYNC_CALL_RET(bool, false, valid_metadata); + return r; + } + + void torrent_handle::filter_piece(int index, bool filter) const + { + TORRENT_ASYNC_CALL2(filter_piece, index, filter); + } + + void torrent_handle::filter_pieces(std::vector const& pieces) const + { + TORRENT_ASYNC_CALL1(filter_pieces, pieces); + } + + bool torrent_handle::is_piece_filtered(int index) const + { + TORRENT_SYNC_CALL_RET1(bool, false, is_piece_filtered, index); + return r; + } + + std::vector torrent_handle::filtered_pieces() const + { + std::vector ret; + TORRENT_SYNC_CALL1(filtered_pieces, ret); + return ret; + } + + void torrent_handle::filter_files(std::vector const& files) const + { + TORRENT_ASYNC_CALL1(filter_files, files); + } + + bool torrent_handle::super_seeding() const + { + TORRENT_SYNC_CALL_RET(bool, false, super_seeding); + return r; + } + +// ============ end deprecation =============== +#endif + + std::vector torrent_handle::trackers() const + { + const static std::vector empty; + TORRENT_SYNC_CALL_RET(std::vector, empty, trackers); + return r; + } + + void torrent_handle::add_url_seed(std::string const& url) const + { + TORRENT_ASYNC_CALL2(add_web_seed, url, web_seed_entry::url_seed); + } + + void torrent_handle::remove_url_seed(std::string const& url) const + { + TORRENT_ASYNC_CALL2(remove_web_seed, url, web_seed_entry::url_seed); + } + + std::set torrent_handle::url_seeds() const + { + const static std::set empty; + TORRENT_SYNC_CALL_RET1(std::set, empty, web_seeds, web_seed_entry::url_seed); + return r; + } + + void torrent_handle::add_http_seed(std::string const& url) const + { + TORRENT_ASYNC_CALL2(add_web_seed, url, web_seed_entry::http_seed); + } + + void torrent_handle::remove_http_seed(std::string const& url) const + { + TORRENT_ASYNC_CALL2(remove_web_seed, url, web_seed_entry::http_seed); + } + + std::set torrent_handle::http_seeds() const + { + const static std::set empty; + TORRENT_SYNC_CALL_RET1(std::set, empty, web_seeds, web_seed_entry::http_seed); + return r; + } + + void torrent_handle::replace_trackers( + std::vector const& urls) const + { + TORRENT_ASYNC_CALL1(replace_trackers, urls); + } + + void torrent_handle::add_tracker(announce_entry const& url) const + { + TORRENT_ASYNC_CALL1(add_tracker, url); + } + + void torrent_handle::add_piece(int piece, char const* data, int flags) const + { + TORRENT_SYNC_CALL3(add_piece, piece, data, flags); + } + + void torrent_handle::read_piece(int piece) const + { + TORRENT_ASYNC_CALL1(read_piece, piece); + } + + bool torrent_handle::have_piece(int piece) const + { + TORRENT_SYNC_CALL_RET1(bool, false, have_piece, piece); + return r; + } + + storage_interface* torrent_handle::get_storage_impl() const + { + TORRENT_SYNC_CALL_RET(storage_interface*, 0, get_storage); + return r; + } + + bool torrent_handle::is_valid() const + { + return !m_torrent.expired(); + } + + boost::intrusive_ptr torrent_handle::torrent_file() const + { + TORRENT_SYNC_CALL_RET(boost::intrusive_ptr + , boost::intrusive_ptr(), get_torrent_copy); + return r; + } + +#ifndef TORRENT_NO_DEPRECATE + // this function should either be removed, or return + // reference counted handle to the torrent_info which + // forces the torrent to stay loaded while the client holds it + torrent_info const& torrent_handle::get_torrent_info() const + { +#ifdef BOOST_NO_EXCEPTIONS + const static torrent_info empty(sha1_hash(0)); +#endif + boost::shared_ptr t = m_torrent.lock(); + if (!t) +#ifdef BOOST_NO_EXCEPTIONS + return empty; +#else + throw_invalid_handle(); +#endif + if (!t->valid_metadata()) +#ifdef BOOST_NO_EXCEPTIONS + return empty; +#else + throw_invalid_handle(); +#endif + return t->torrent_file(); + } + + entry torrent_handle::write_resume_data() const + { + entry ret(entry::dictionary_t); + TORRENT_SYNC_CALL1(write_resume_data, boost::ref(ret)); + t = m_torrent.lock(); + if (t) + { + bool done = false; + session_impl& ses = t->session(); + mutex::scoped_lock l(ses.mut); + ses.m_io_service.dispatch(boost::bind(&fun_wrap, &done, &ses.cond + , &ses.mut, boost::function(boost::bind( + &piece_manager::write_resume_data, &t->filesystem(), boost::ref(ret))))); + t.reset(); + while (!done) { ses.cond.wait(l); } + } + + return ret; + } + + std::string torrent_handle::save_path() const + { + TORRENT_SYNC_CALL_RET(std::string, "", save_path); + return r; + } + + std::string torrent_handle::name() const + { + TORRENT_SYNC_CALL_RET(std::string, "", name); + return r; + } + +#endif + + void torrent_handle::connect_peer(tcp::endpoint const& adr, int source) const + { + TORRENT_ASYNC_CALL2(add_peer, adr, source); + } + +#ifndef TORRENT_NO_DEPRECATE + void torrent_handle::force_reannounce( + boost::posix_time::time_duration duration) const + { + TORRENT_ASYNC_CALL2(force_tracker_request, time_now() + + seconds(duration.total_seconds()), -1); + } +#endif + + void torrent_handle::force_dht_announce() const + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL(dht_announce); +#endif + } + + void torrent_handle::force_reannounce(int s, int idx) const + { + TORRENT_ASYNC_CALL2(force_tracker_request, time_now() + seconds(s), idx); + } + + void torrent_handle::scrape_tracker() const + { + TORRENT_ASYNC_CALL(scrape_tracker); + } + + void torrent_handle::super_seeding(bool on) const + { + TORRENT_ASYNC_CALL1(super_seeding, on); + } + + void torrent_handle::resolve_countries(bool r) + { +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + TORRENT_ASYNC_CALL1(resolve_countries, r); +#endif + } + + bool torrent_handle::resolve_countries() const + { +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + TORRENT_SYNC_CALL_RET(bool, false, resolving_countries); + return r; +#else + return false; +#endif + } + + void torrent_handle::get_full_peer_list(std::vector& v) const + { + TORRENT_SYNC_CALL1(get_full_peer_list, boost::ref(v)); + } + + void torrent_handle::get_peer_info(std::vector& v) const + { + TORRENT_SYNC_CALL1(get_peer_info, boost::ref(v)); + } + + void torrent_handle::get_download_queue(std::vector& queue) const + { + TORRENT_SYNC_CALL1(get_download_queue, &queue); + } + + void torrent_handle::set_piece_deadline(int index, int deadline, int flags) const + { + TORRENT_ASYNC_CALL3(set_piece_deadline, index, deadline, flags); + } + + void torrent_handle::reset_piece_deadline(int index) const + { + TORRENT_ASYNC_CALL1(reset_piece_deadline, index); + } + + void torrent_handle::clear_piece_deadlines() const + { + TORRENT_ASYNC_CALL(clear_time_critical); + } + + boost::shared_ptr torrent_handle::native_handle() const + { + return m_torrent.lock(); + } + + std::size_t hash_value(torrent_status const& ts) + { + return hash_value(ts.handle); + } + + std::size_t hash_value(torrent_handle const& th) + { + // using the locked shared_ptr value as hash doesn't work + // for expired weak_ptrs. So, we're left with a hack + return std::size_t(*reinterpret_cast(&th.m_torrent)); + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/torrent_info.cpp b/apps/Launcher/ext/libtorrent/src/torrent_info.cpp new file mode 100644 index 0000000000..a14e8ddbc7 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/torrent_info.cpp @@ -0,0 +1,1473 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include + +#if !defined TORRENT_NO_DEPRECATE && TORRENT_USE_IOSTREAM +#include +#include +#endif + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/ConvertUTF.h" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/escape_string.hpp" // is_space +#include "libtorrent/bencode.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/file.hpp" +#include "libtorrent/utf8.hpp" +#include "libtorrent/time.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/add_torrent_params.hpp" +#include "libtorrent/magnet_uri.hpp" + +#if TORRENT_USE_I2P +#include "libtorrent/parse_url.hpp" +#endif + +namespace libtorrent +{ + + bool valid_path_character(char c) + { +#ifdef TORRENT_WINDOWS + static const char invalid_chars[] = "?<>\"|\b*:"; +#else + static const char invalid_chars[] = ""; +#endif + if (c >= 0 && c < 32) return false; + return std::strchr(invalid_chars, c) == 0; + } + + // fixes invalid UTF-8 sequences and + // replaces characters that are invalid + // in paths + TORRENT_EXTRA_EXPORT bool verify_encoding(std::string& target, bool fix_paths = false) + { + if (target.empty()) return true; + + std::string tmp_path; + bool valid_encoding = true; + + UTF8 const* ptr = (UTF8 const*)&target[0]; + UTF8 const* end = (UTF8 const*)&target[0] + target.size(); + while (ptr < end) + { + UTF32 codepoint; + UTF32* cp = &codepoint; + + // decode a single utf-8 character + ConversionResult res = ConvertUTF8toUTF32(&ptr, end, &cp, cp + 1 + , lenientConversion); + + // this was the last character, and nothing was + // written to the destination buffer (i.e. the source character was + // truncated) + if (res == sourceExhausted + || res == sourceIllegal) + { + if (cp == &codepoint) + { + if (res == sourceExhausted) + ptr = end; + else + ++ptr; + + codepoint = '_'; + valid_encoding = false; + } + } + else if ((res != conversionOK && res != targetExhausted) + || codepoint == UNI_REPLACEMENT_CHAR) + { + // we expect the conversion to fail with targetExhausted, since we + // only pass in a single destination character slot. The last + // character will succeed though. Also, if the character was replaced, + // use our own replacement symbol (underscore). + codepoint = '_'; + valid_encoding = false; + } + + // if fix paths is true, also replace characters that are invalid + // in filenames + if (fix_paths && codepoint < 0x7f && !valid_path_character(codepoint)) + { + codepoint = '_'; + valid_encoding = false; + } + + // encode codepoint into utf-8 + cp = &codepoint; + UTF8 sequence[5]; + UTF8* start = sequence; + res = ConvertUTF32toUTF8((const UTF32**)&cp, cp + 1, &start, start + 5, lenientConversion); + TORRENT_ASSERT(res == conversionOK); + + for (int i = 0; i < start - sequence; ++i) + tmp_path += (char)sequence[i]; + } + + // the encoding was not valid utf-8 + // save the original encoding and replace the + // commonly used path with the correctly + // encoded string + if (!valid_encoding) target = tmp_path; + return valid_encoding; + } + + // TODO: 1 we might save constructing a std::string if this would take a char const* instead + bool valid_path_element(std::string const& element) + { + if (element.empty() + || element == "." || element == ".." + || element[0] == '/' || element[0] == '\\' + || element[element.size()-1] == ':') + return false; + return true; + } + + TORRENT_EXTRA_EXPORT void trim_path_element(std::string& element) + { + const int max_path_len = TORRENT_MAX_PATH; + + // on windows, the max path is expressed in + // unicode characters, not bytes +#if defined TORRENT_WINDOWS && TORRENT_USE_WSTRING + std::wstring path_element; + utf8_wchar(element, path_element); + if (path_element.size() > max_path_len) + { + // truncate filenames that are too long. But keep extensions! + std::wstring ext; + wchar_t const* ext1 = wcsrchr(path_element.c_str(), '.'); + if (ext1 != NULL) ext = ext1; + + if (ext.size() > 15) + { + path_element.resize(max_path_len); + } + else + { + path_element.resize(max_path_len - ext.size()); + path_element += ext; + } + } + // remove trailing spaces and dots. These aren't allowed in filenames on windows + for (int i = path_element.size() - 1; i >= 0; --i) + { + if (path_element[i] != L' ' && path_element[i] != L'.') break; + path_element.resize(i); + } + if (path_element.empty()) path_element = L"_"; + wchar_utf8(path_element, element); +#else + std::string& path_element = element; + if (int(path_element.size()) > max_path_len) + { + + // truncate filenames that are too long. But keep extensions! + std::string ext = extension(path_element); + if (ext.size() > 15) + { + path_element.resize(max_path_len); + } + else + { + path_element.resize(max_path_len - ext.size()); + path_element += ext; + } + } + + // remove trailing spaces and dots. These aren't allowed in filenames on windows + // apply rules consistently across platforms though + for (int i = path_element.size() - 1; i >= 0; --i) + { + if (path_element[i] != ' ' && path_element[i] != '.') break; + path_element.resize(i); + } + + if (path_element.empty()) path_element = "_"; +#endif + } + + TORRENT_EXTRA_EXPORT std::string sanitize_path(std::string const& p) + { + std::string new_path; + std::string split = split_path(p); + for (char const* e = split.c_str(); e != 0; e = next_path_element(e)) + { + std::string pe = e; +#if !TORRENT_USE_UNC_PATHS && defined TORRENT_WINDOWS + // if we're not using UNC paths on windows, there + // are certain filenames we're not allowed to use + const static char const* reserved_names[] = + { + "con", "prn", "aux", "clock$", "nul", + "com0", "com1", "com2", "com3", "com4", + "com5", "com6", "com7", "com8", "com9", + "lpt0", "lpt1", "lpt2", "lpt3", "lpt4", + "lpt5", "lpt6", "lpt7", "lpt8", "lpt9" + }; + int num_names = sizeof(reserved_names)/sizeof(reserved_names[0]); + + char const* file_end = strrchr(pe.c_str(), '.'); + std::string name; + if (file_end) name.assign(pe.c_str(), file_end); + else name = pe; + std::transform(name.begin(), name.end(), name.begin(), &to_lower); + char const** str = std::find(reserved_names, reserved_names + num_names, name); + if (str != reserved_names + num_names) + { + pe += "_"; + } +#endif + if (!valid_path_element(pe)) continue; + trim_path_element(pe); + new_path = combine_path(new_path, pe); + } + return new_path; + } + + bool extract_single_file(lazy_entry const& dict, file_entry& target + , std::string const& root_dir, lazy_entry const** filehash + , lazy_entry const** filename, time_t* mtime) + { + if (dict.type() != lazy_entry::dict_t) return false; + lazy_entry const* length = dict.dict_find("length"); + if (length == 0 || length->type() != lazy_entry::int_t) + return false; + target.size = length->int_value(); + if (target.size < 0) + return false; + + size_type ts = dict.dict_find_int_value("mtime", -1); + if (ts > 0) *mtime = std::time_t(ts); + + // prefer the name.utf-8 + // because if it exists, it is more + // likely to be correctly encoded + + lazy_entry const* p = dict.dict_find("path.utf-8"); + if (p == 0 || p->type() != lazy_entry::list_t) + p = dict.dict_find("path"); + if (p == 0 || p->type() != lazy_entry::list_t) + return false; + + std::string path = root_dir; + for (int i = 0, end(p->list_size()); i < end; ++i) + { + if (p->list_at(i)->type() != lazy_entry::string_t) + return false; + std::string path_element = p->list_at(i)->string_value(); + if (path_element.empty()) + path_element = "_"; + if (!valid_path_element(path_element)) continue; + if (i == end - 1) *filename = p->list_at(i); + trim_path_element(path_element); + path = combine_path(path, path_element); + } + path = sanitize_path(path); + verify_encoding(path, true); + + // bitcomet pad file + if (path.find("_____padding_file_") != std::string::npos) + target.pad_file = true; + + target.path = path; + + lazy_entry const* attr = dict.dict_find_string("attr"); + if (attr) + { + for (int i = 0; i < attr->string_length(); ++i) + { + switch (attr->string_ptr()[i]) + { + case 'l': target.symlink_attribute = true; target.size = 0; break; + case 'x': target.executable_attribute = true; break; + case 'h': target.hidden_attribute = true; break; + case 'p': target.pad_file = true; break; + } + } + } + + lazy_entry const* fh = dict.dict_find_string("sha1"); + if (fh && fh->string_length() == 20 && filehash) + *filehash = fh; + + lazy_entry const* s_p = dict.dict_find("symlink path"); + if (s_p != 0 && s_p->type() == lazy_entry::list_t && target.symlink_attribute) + { + for (int i = 0, end(s_p->list_size()); i < end; ++i) + { + std::string path_element = s_p->list_at(i)->string_value(); + trim_path_element(path_element); + target.symlink_path = combine_path(target.symlink_path, path_element); + } + } + else + { + target.symlink_attribute = false; + } + + return true; + } + + struct string_less_no_case + { + bool operator()(std::string const& lhs, std::string const& rhs) + { + char c1, c2; + char const* s1 = lhs.c_str(); + char const* s2 = rhs.c_str(); + + while (*s1 != 0 || *s2 != 0) + { + c1 = to_lower(*s1); + c2 = to_lower(*s2); + if (c1 < c2) return true; + if (c1 > c2) return false; + ++s1; + ++s2; + } + return false; + } + }; + + bool extract_files(lazy_entry const& list, file_storage& target + , std::string const& root_dir, ptrdiff_t info_ptr_diff) + { + if (list.type() != lazy_entry::list_t) return false; + target.reserve(list.list_size()); + + // TODO: 1 this logic should be a separate step + // done once the torrent is loaded, and the original + // filenames should be preserved! + std::set files; + + for (int i = 0, end(list.list_size()); i < end; ++i) + { + lazy_entry const* file_hash = 0; + time_t mtime = 0; + file_entry e; + lazy_entry const* fee = 0; + if (!extract_single_file(*list.list_at(i), e, root_dir + , &file_hash, &fee, &mtime)) + return false; + + // as long as this file already exists + // increase the counter + int cnt = 0; + if (!files.insert(e.path).second) + { + std::string base = remove_extension(e.path); + std::string ext = extension(e.path); + do + { + ++cnt; + char new_ext[50]; + snprintf(new_ext, sizeof(new_ext), ".%d%s", cnt, ext.c_str()); + e.path = base + new_ext; + } while (!files.insert(e.path).second); + } + target.add_file(e, file_hash ? file_hash->string_ptr() + info_ptr_diff : 0); + + // This is a memory optimization! Instead of having + // each entry keep a string for its filename, make it + // simply point into the info-section buffer + int last_index = target.num_files() - 1; + // TODO: 1 once the filename renaming is removed from here + // this check can be removed as well + if (fee && target.file_name(last_index) == fee->string_value()) + { + // this string pointer does not necessarily point into + // the m_info_section buffer. + char const* str_ptr = fee->string_ptr() + info_ptr_diff; + target.rename_file_borrow(last_index, str_ptr, fee->string_length()); + } + } + return true; + } + + int merkle_get_parent(int tree_node) + { + // node 0 doesn't have a parent + TORRENT_ASSERT(tree_node > 0); + return (tree_node - 1) / 2; + } + + int merkle_get_sibling(int tree_node) + { + // node 0 doesn't have a sibling + TORRENT_ASSERT(tree_node > 0); + // even numbers have their sibling to the left + // odd numbers have their sibling to the right + return tree_node + (tree_node&1?1:-1); + } + + int merkle_num_nodes(int leafs) + { + TORRENT_ASSERT(leafs > 0); + return (leafs << 1) - 1; + } + + int merkle_num_leafs(int pieces) + { + TORRENT_ASSERT(pieces > 0); + // round up to nearest 2 exponent + int ret = 1; + while (pieces > ret) ret <<= 1; + return ret; + } + + int load_file(std::string const& filename, std::vector& v, error_code& ec, int limit = 8000000) + { + ec.clear(); + file f; + if (!f.open(filename, file::read_only, ec)) return -1; + size_type s = f.get_size(ec); + if (ec) return -1; + if (s > limit) + { + ec = error_code(errors::metadata_too_large, get_libtorrent_category()); + return -2; + } + v.resize((unsigned int)s); + if (s == 0) return 0; + file::iovec_t b = {&v[0], size_t(s) }; + size_type read = f.readv(0, &b, 1, ec); + if (read != s) return -3; + if (ec) return -3; + return 0; + } + + announce_entry::announce_entry(std::string const& u) + : url(u) + , next_announce(min_time()) + , min_announce(min_time()) + , scrape_incomplete(-1) + , scrape_complete(-1) + , scrape_downloaded(-1) + , tier(0) + , fail_limit(0) + , fails(0) + , updating(false) + , source(0) + , verified(false) + , start_sent(false) + , complete_sent(false) + , send_stats(true) + {} + + announce_entry::announce_entry() + : next_announce(min_time()) + , min_announce(min_time()) + , scrape_incomplete(-1) + , scrape_complete(-1) + , scrape_downloaded(-1) + , tier(0) + , fail_limit(0) + , fails(0) + , updating(false) + , source(0) + , verified(false) + , start_sent(false) + , complete_sent(false) + , send_stats(true) + {} + + announce_entry::~announce_entry() {} + + int announce_entry::next_announce_in() const + { return total_seconds(next_announce - time_now()); } + + int announce_entry::min_announce_in() const + { return total_seconds(min_announce - time_now()); } + + void announce_entry::failed(session_settings const& sett, int retry_interval) + { + ++fails; + // the exponential back-off ends up being: + // 7, 15, 27, 45, 95, 127, 165, ... seconds + // with the default tracker_backoff of 250 + int delay = (std::min)(tracker_retry_delay_min + int(fails) * int(fails) + * tracker_retry_delay_min * sett.tracker_backoff / 100 + , int(tracker_retry_delay_max)); + delay = (std::max)(delay, retry_interval); + next_announce = time_now() + seconds(delay); + updating = false; + } + + bool announce_entry::can_announce(ptime now, bool is_seed) const + { + // if we're a seed and we haven't sent a completed + // event, we need to let this announce through + bool need_send_complete = is_seed && !complete_sent; + + return now >= next_announce + && (now >= min_announce || need_send_complete) + && (fails < fail_limit || fail_limit == 0) + && !updating; + } + + void announce_entry::trim() + { + while (!url.empty() && is_space(url[0])) + url.erase(url.begin()); + } + + web_seed_entry::web_seed_entry(std::string const& url_, type_t type_ + , std::string const& auth_ + , headers_t const& extra_headers_) + : url(url_), type(type_) + , auth(auth_), extra_headers(extra_headers_) + , retry(time_now()) + , supports_keepalive(true) + , resolving(false), removed(false) + , peer_info(tcp::endpoint(), true, 0) + { + peer_info.web_seed = true; + restart_request.piece = -1; + } + + torrent_info::torrent_info(torrent_info const& t, int flags) + : m_merkle_first_leaf(t.m_merkle_first_leaf) + , m_files(t.m_files) + , m_orig_files(t.m_orig_files) + , m_urls(t.m_urls) + , m_web_seeds(t.m_web_seeds) + , m_nodes(t.m_nodes) + , m_merkle_tree(t.m_merkle_tree) + , m_piece_hashes(t.m_piece_hashes) + , m_comment(t.m_comment) + , m_created_by(t.m_created_by) + , m_creation_date(t.m_creation_date) + , m_info_hash(t.m_info_hash) + , m_info_section_size(t.m_info_section_size) + , m_multifile(t.m_multifile) + , m_private(t.m_private) + , m_i2p(t.m_i2p) + { +#if TORRENT_USE_INVARIANT_CHECKS + t.check_invariant(); +#endif + if (m_info_section_size > 0) + { + error_code ec; + m_info_section.reset(new char[m_info_section_size]); + memcpy(m_info_section.get(), t.m_info_section.get(), m_info_section_size); +#if TORRENT_USE_ASSERTS || !defined BOOST_NO_EXCEPTIONS + int ret = +#endif + lazy_bdecode(m_info_section.get(), m_info_section.get() + + m_info_section_size, m_info_dict, ec); +#ifndef BOOST_NO_EXCEPTIONS + if (ret != 0) throw libtorrent_exception(ec); +#endif + TORRENT_ASSERT(ret == 0); + + ptrdiff_t offset = m_info_section.get() - t.m_info_section.get(); + + m_piece_hashes += offset; + TORRENT_ASSERT(m_piece_hashes >= m_info_section.get()); + TORRENT_ASSERT(m_piece_hashes < m_info_section.get() + m_info_section_size); + } + INVARIANT_CHECK; + } + + void torrent_info::remap_files(file_storage const& f) + { + INVARIANT_CHECK; + + // the new specified file storage must have the exact + // same size as the current file storage + TORRENT_ASSERT(m_files.total_size() == f.total_size()); + + if (m_files.total_size() != f.total_size()) return; + copy_on_write(); + m_files = f; + m_files.set_num_pieces(m_orig_files->num_pieces()); + m_files.set_piece_length(m_orig_files->piece_length()); + } + +#ifndef TORRENT_NO_DEPRECATE + // standard constructor that parses a torrent file + torrent_info::torrent_info(entry const& torrent_file) + : m_merkle_first_leaf(0) + , m_piece_hashes(0) + , m_creation_date(0) + , m_info_section_size(0) + , m_multifile(false) + , m_private(false) + , m_i2p(false) + { + std::vector tmp; + std::back_insert_iterator > out(tmp); + bencode(out, torrent_file); + + lazy_entry e; + error_code ec; + if (tmp.size() == 0 || lazy_bdecode(&tmp[0], &tmp[0] + tmp.size(), e, ec) != 0) + { +#ifndef BOOST_NO_EXCEPTIONS + throw invalid_torrent_file(ec); +#endif + return; + } +#ifndef BOOST_NO_EXCEPTIONS + if (!parse_torrent_file(e, ec, 0)) + throw invalid_torrent_file(ec); +#else + parse_torrent_file(e, ec, 0); +#endif + INVARIANT_CHECK; + } +#endif + +#ifndef BOOST_NO_EXCEPTIONS + torrent_info::torrent_info(lazy_entry const& torrent_file, int flags) + : m_merkle_first_leaf(0) + , m_piece_hashes(0) + , m_creation_date(0) + , m_info_section_size(0) + , m_multifile(false) + , m_private(false) + , m_i2p(false) + { + error_code ec; + if (!parse_torrent_file(torrent_file, ec, flags)) + throw invalid_torrent_file(ec); + + INVARIANT_CHECK; + } + + torrent_info::torrent_info(char const* buffer, int size, int flags) + : m_merkle_first_leaf(0) + , m_piece_hashes(0) + , m_creation_date(0) + , m_info_section_size(0) + , m_multifile(false) + , m_private(false) + , m_i2p(false) + { + error_code ec; + lazy_entry e; + if (lazy_bdecode(buffer, buffer + size, e, ec) != 0) + throw invalid_torrent_file(ec); + + if (!parse_torrent_file(e, ec, flags)) + throw invalid_torrent_file(ec); + + INVARIANT_CHECK; + } + + torrent_info::torrent_info(std::string const& filename, int flags) + : m_merkle_first_leaf(0) + , m_piece_hashes(0) + , m_creation_date(0) + , m_info_section_size(0) + , m_multifile(false) + , m_private(false) + , m_i2p(false) + { + std::vector buf; + error_code ec; + int ret = load_file(filename, buf, ec); + if (ret < 0) throw invalid_torrent_file(ec); + + lazy_entry e; + if (buf.size() == 0 || lazy_bdecode(&buf[0], &buf[0] + buf.size(), e, ec) != 0) + throw invalid_torrent_file(ec); + + if (!parse_torrent_file(e, ec, flags)) + throw invalid_torrent_file(ec); + + INVARIANT_CHECK; + } + +#if TORRENT_USE_WSTRING +#ifndef TORRENT_NO_DEPRECATE + torrent_info::torrent_info(std::wstring const& filename, int flags) + : m_merkle_first_leaf(0) + , m_piece_hashes(0) + , m_creation_date(0) + , m_info_section_size(0) + , m_multifile(false) + , m_private(false) + , m_i2p(false) + { + std::vector buf; + std::string utf8; + wchar_utf8(filename, utf8); + error_code ec; + int ret = load_file(utf8, buf, ec); + if (ret < 0) throw invalid_torrent_file(ec); + + lazy_entry e; + if (buf.size() == 0 || lazy_bdecode(&buf[0], &buf[0] + buf.size(), e, ec) != 0) + throw invalid_torrent_file(ec); + + if (!parse_torrent_file(e, ec, flags)) + throw invalid_torrent_file(ec); + + INVARIANT_CHECK; + } + + void torrent_info::rename_file(int index, std::wstring const& new_filename) + { + copy_on_write(); + m_files.rename_file_deprecated(index, new_filename); + } +#endif // TORRENT_NO_DEPRECATE +#endif // TORRENT_USE_WSTRING +#endif + + torrent_info::torrent_info(lazy_entry const& torrent_file, error_code& ec, int flags) + : m_merkle_first_leaf(0) + , m_piece_hashes(0) + , m_creation_date(0) + , m_info_section_size(0) + , m_multifile(false) + , m_private(false) + , m_i2p(false) + { + parse_torrent_file(torrent_file, ec, flags); + + INVARIANT_CHECK; + } + + torrent_info::torrent_info(char const* buffer, int size, error_code& ec, int flags) + : m_merkle_first_leaf(0) + , m_piece_hashes(0) + , m_creation_date(0) + , m_info_section_size(0) + , m_multifile(false) + , m_private(false) + , m_i2p(false) + { + lazy_entry e; + if (lazy_bdecode(buffer, buffer + size, e, ec) != 0) + return; + parse_torrent_file(e, ec, flags); + + INVARIANT_CHECK; + } + + torrent_info::torrent_info(std::string const& filename, error_code& ec, int flags) + : m_merkle_first_leaf(0) + , m_piece_hashes(0) + , m_creation_date(0) + , m_info_section_size(0) + , m_multifile(false) + , m_private(false) + , m_i2p(false) + { + std::vector buf; + int ret = load_file(filename, buf, ec); + if (ret < 0) return; + + lazy_entry e; + if (buf.size() == 0 || lazy_bdecode(&buf[0], &buf[0] + buf.size(), e, ec) != 0) + return; + parse_torrent_file(e, ec, flags); + + INVARIANT_CHECK; + } + +#if TORRENT_USE_WSTRING +#ifndef TORRENT_NO_DEPRECATE + torrent_info::torrent_info(std::wstring const& filename, error_code& ec, int flags) + : m_merkle_first_leaf(0) + , m_piece_hashes(0) + , m_creation_date(0) + , m_info_section_size(0) + , m_multifile(false) + , m_private(false) + , m_i2p(false) + { + std::vector buf; + std::string utf8; + wchar_utf8(filename, utf8); + int ret = load_file(utf8, buf, ec); + if (ret < 0) return; + + lazy_entry e; + if (buf.size() == 0 || lazy_bdecode(&buf[0], &buf[0] + buf.size(), e, ec) != 0) + return; + parse_torrent_file(e, ec, flags); + + INVARIANT_CHECK; + } +#endif // TORRENT_NO_DEPRECATE +#endif // TORRENT_USE_WSTRING + + // constructor used for creating new torrents + // will not contain any hashes, comments, creation date + // just the necessary to use it with piece manager + // used for torrents with no metadata + torrent_info::torrent_info(sha1_hash const& info_hash, int flags) + : m_merkle_first_leaf(0) + , m_piece_hashes(0) + , m_creation_date(time(0)) + , m_info_hash(info_hash) + , m_info_section_size(0) + , m_multifile(false) + , m_private(false) + , m_i2p(false) + {} + + torrent_info::~torrent_info() + {} + + void torrent_info::copy_on_write() + { + INVARIANT_CHECK; + + if (m_orig_files) return; + m_orig_files.reset(new file_storage(m_files)); + } + +#define SWAP(a, b) \ + tmp = a; \ + a = b; \ + b = tmp; + + void torrent_info::swap(torrent_info& ti) + { + INVARIANT_CHECK; + + using std::swap; + m_urls.swap(ti.m_urls); + m_web_seeds.swap(ti.m_web_seeds); + m_files.swap(ti.m_files); + m_orig_files.swap(ti.m_orig_files); + m_nodes.swap(ti.m_nodes); + swap(m_info_hash, ti.m_info_hash); + swap(m_creation_date, ti.m_creation_date); + m_comment.swap(ti.m_comment); + m_created_by.swap(ti.m_created_by); + boost::uint32_t tmp; + SWAP(m_multifile, ti.m_multifile); + SWAP(m_private, ti.m_private); + SWAP(m_i2p, ti.m_i2p); + swap(m_info_section, ti.m_info_section); + SWAP(m_info_section_size, ti.m_info_section_size); + swap(m_piece_hashes, ti.m_piece_hashes); + m_info_dict.swap(ti.m_info_dict); + swap(m_merkle_tree, ti.m_merkle_tree); + SWAP(m_merkle_first_leaf, ti.m_merkle_first_leaf); + } + +#undef SWAP + + std::string torrent_info::ssl_cert() const + { + // this is parsed lazily + if (m_info_dict.type() == lazy_entry::none_t) + { + error_code ec; + lazy_bdecode(m_info_section.get(), m_info_section.get() + + m_info_section_size, m_info_dict, ec); + if (ec) return ""; + } + if (m_info_dict.type() != lazy_entry::dict_t) return ""; + return m_info_dict.dict_find_string_value("ssl-cert"); + } + + bool torrent_info::parse_info_section(lazy_entry const& info, error_code& ec, int flags) + { + if (info.type() != lazy_entry::dict_t) + { + ec = errors::torrent_info_no_dict; + return false; + } + + // hash the info-field to calculate info-hash + hasher h; + std::pair section = info.data_section(); + h.update(section.first, section.second); + m_info_hash = h.final(); + + // copy the info section + m_info_section_size = section.second; + m_info_section.reset(new char[m_info_section_size]); + std::memcpy(m_info_section.get(), section.first, m_info_section_size); + TORRENT_ASSERT(section.first[0] == 'd'); + TORRENT_ASSERT(section.first[m_info_section_size-1] == 'e'); + + // when translating a pointer that points into the 'info' tree's + // backing buffer, into a pointer to our copy of the info section, + // this is the pointer offset to use. + ptrdiff_t info_ptr_diff = m_info_section.get() - section.first; + + // extract piece length + int piece_length = info.dict_find_int_value("piece length", -1); + if (piece_length <= 0) + { + ec = errors::torrent_missing_piece_length; + return false; + } + m_files.set_piece_length(piece_length); + + // extract file name (or the directory name if it's a multifile libtorrent) + lazy_entry const* name_ent = info.dict_find_string("name.utf-8"); + if (name_ent == 0) name_ent = info.dict_find_string("name"); + if (name_ent == 0) + { + ec = errors::torrent_missing_name; + return false; + } + + std::string name = name_ent->string_value(); + if (name.empty()) name = to_hex(m_info_hash.to_string()); + name = sanitize_path(name); + + if (!valid_path_element(name)) + { + ec = errors::torrent_invalid_name; + return false; + } + + // correct utf-8 encoding errors + verify_encoding(name, true); + + // extract file list + lazy_entry const* i = info.dict_find_list("files"); + if (i == 0) + { + // if there's no list of files, there has to be a length + // field. + file_entry e; + e.path = name; + e.offset = 0; + e.size = info.dict_find_int_value("length", -1); + if (e.size < 0) + { + ec = errors::torrent_invalid_length; + return false; + } + e.mtime = info.dict_find_int_value("mtime", 0); + lazy_entry const* attr = info.dict_find_string("attr"); + if (attr) + { + for (int i = 0; i < attr->string_length(); ++i) + { + switch (attr->string_ptr()[i]) + { + case 'l': e.symlink_attribute = true; e.size = 0; break; + case 'x': e.executable_attribute = true; break; + case 'h': e.hidden_attribute = true; break; + case 'p': e.pad_file = true; break; + } + } + } + + lazy_entry const* s_p = info.dict_find("symlink path"); + if (s_p != 0 && s_p->type() == lazy_entry::list_t) + { + for (int i = 0, end(s_p->list_size()); i < end; ++i) + { + std::string path_element = s_p->list_at(i)->string_value(); + trim_path_element(path_element); + e.symlink_path = combine_path(e.symlink_path, path_element); + } + } + else + { + e.symlink_attribute = false; + } + + lazy_entry const* fh = info.dict_find_string("sha1"); + if (fh && fh->string_length() != 20) fh = 0; + + // bitcomet pad file + if (e.path.find("_____padding_file_") != std::string::npos) + e.pad_file = true; + m_files.add_file(e, fh ? fh->string_ptr() + info_ptr_diff : 0); + m_multifile = false; + } + else + { + if (!extract_files(*i, m_files, name, info_ptr_diff)) + { + ec = errors::torrent_file_parse_failed; + return false; + } + m_multifile = true; + } + TORRENT_ASSERT(!m_files.name().empty()); + + // extract sha-1 hashes for all pieces + // we want this division to round upwards, that's why we have the + // extra addition + + m_files.set_num_pieces(int((m_files.total_size() + m_files.piece_length() - 1) + / m_files.piece_length())); + + lazy_entry const* pieces = info.dict_find_string("pieces"); + lazy_entry const* root_hash = info.dict_find_string("root hash"); + if (pieces == 0 && root_hash == 0) + { + ec = errors::torrent_missing_pieces; + return false; + } + + if (pieces) + { + if (pieces->string_length() != m_files.num_pieces() * 20) + { + ec = errors::torrent_invalid_hashes; + return false; + } + + m_piece_hashes = pieces->string_ptr() + info_ptr_diff; + TORRENT_ASSERT(m_piece_hashes >= m_info_section.get()); + TORRENT_ASSERT(m_piece_hashes < m_info_section.get() + m_info_section_size); + } + else + { + TORRENT_ASSERT(root_hash); + if (root_hash->string_length() != 20) + { + ec = errors::torrent_invalid_hashes; + return false; + } + int num_leafs = merkle_num_leafs(m_files.num_pieces()); + int num_nodes = merkle_num_nodes(num_leafs); + m_merkle_first_leaf = num_nodes - num_leafs; + m_merkle_tree.resize(num_nodes); + std::memset(&m_merkle_tree[0], 0, num_nodes * 20); + m_merkle_tree[0].assign(root_hash->string_ptr()); + } + + m_private = info.dict_find_int_value("private", 0); + + return true; + } + + bool torrent_info::add_merkle_nodes(std::map const& subtree + , int piece) + { + INVARIANT_CHECK; + + int n = m_merkle_first_leaf + piece; + typedef std::map::const_iterator iter; + iter i = subtree.find(n); + if (i == subtree.end()) return false; + sha1_hash h = i->second; + + // if the verification passes, these are the + // nodes to add to our tree + std::map to_add; + + while (n > 0) + { + int sibling = merkle_get_sibling(n); + int parent = merkle_get_parent(n); + iter sibling_hash = subtree.find(sibling); + if (sibling_hash == subtree.end()) + return false; + to_add[n] = h; + to_add[sibling] = sibling_hash->second; + hasher hs; + if (sibling < n) + { + hs.update((char const*)&sibling_hash->second[0], 20); + hs.update((char const*)&h[0], 20); + } + else + { + hs.update((char const*)&h[0], 20); + hs.update((char const*)&sibling_hash->second[0], 20); + } + h = hs.final(); + n = parent; + } + if (h != m_merkle_tree[0]) return false; + + // the nodes and piece hash matched the root-hash + // insert them into our tree + + for (std::map::iterator i = to_add.begin() + , end(to_add.end()); i != end; ++i) + { + m_merkle_tree[i->first] = i->second; + } + return true; + } + + // builds a list of nodes that are required to verify + // the given piece + std::map torrent_info::build_merkle_list(int piece) const + { + INVARIANT_CHECK; + + std::map ret; + int n = m_merkle_first_leaf + piece; + ret[n] = m_merkle_tree[n]; + ret[0] = m_merkle_tree[0]; + while (n > 0) + { + int sibling = merkle_get_sibling(n); + int parent = merkle_get_parent(n); + ret[sibling] = m_merkle_tree[sibling]; + // we cannot build the tree path if one + // of the nodes in the tree is missing + TORRENT_ASSERT(m_merkle_tree[sibling] != sha1_hash(0)); + n = parent; + } + return ret; + } + +#if TORRENT_USE_I2P + bool is_i2p_url(std::string const& url) + { + using boost::tuples::ignore; + std::string hostname; + error_code ec; + boost::tie(ignore, ignore, hostname, ignore, ignore) + = parse_url_components(url, ec); + char const* top_domain = strrchr(hostname.c_str(), '.'); + return top_domain && strcmp(top_domain, ".i2p") == 0; + } +#endif + + bool torrent_info::parse_torrent_file(lazy_entry const& torrent_file, error_code& ec, int flags) + { + if (torrent_file.type() != lazy_entry::dict_t) + { + ec = errors::torrent_is_no_dict; + return false; + } + + lazy_entry const* info = torrent_file.dict_find_dict("info"); + if (info == 0) + { + lazy_entry const* link = torrent_file.dict_find_string("magnet-uri"); + if (link) + { + std::string uri = link->string_value(); + + add_torrent_params p; + parse_magnet_uri(uri, p, ec); + if (ec) return false; + + m_info_hash = p.info_hash; + for (std::vector::iterator i = p.trackers.begin() + , end(p.trackers.end()); i != end; ++i) + m_urls.push_back(*i); + + return true; + } + + ec = errors::torrent_missing_info; + return false; + } + if (!parse_info_section(*info, ec, flags)) return false; + + // extract the url of the tracker + lazy_entry const* i = torrent_file.dict_find_list("announce-list"); + if (i) + { + m_urls.reserve(i->list_size()); + for (int j = 0, end(i->list_size()); j < end; ++j) + { + lazy_entry const* tier = i->list_at(j); + if (tier->type() != lazy_entry::list_t) continue; + for (int k = 0, end(tier->list_size()); k < end; ++k) + { + announce_entry e(tier->list_string_value_at(k)); + e.trim(); + if (e.url.empty()) continue; + e.tier = j; + e.fail_limit = 0; + e.source = announce_entry::source_torrent; +#if TORRENT_USE_I2P + if (is_i2p_url(e.url)) m_i2p = true; +#endif + m_urls.push_back(e); + } + } + + if (!m_urls.empty()) + { + // shuffle each tier + std::vector::iterator start = m_urls.begin(); + std::vector::iterator stop; + int current_tier = m_urls.front().tier; + for (stop = m_urls.begin(); stop != m_urls.end(); ++stop) + { + if (stop->tier != current_tier) + { + std::random_shuffle(start, stop); + start = stop; + current_tier = stop->tier; + } + } + std::random_shuffle(start, stop); + } + } + + + if (m_urls.empty()) + { + announce_entry e(torrent_file.dict_find_string_value("announce")); + e.fail_limit = 0; + e.source = announce_entry::source_torrent; + e.trim(); +#if TORRENT_USE_I2P + if (is_i2p_url(e.url)) m_i2p = true; +#endif + if (!e.url.empty()) m_urls.push_back(e); + } + + lazy_entry const* nodes = torrent_file.dict_find_list("nodes"); + if (nodes) + { + for (int i = 0, end(nodes->list_size()); i < end; ++i) + { + lazy_entry const* n = nodes->list_at(i); + if (n->type() != lazy_entry::list_t + || n->list_size() < 2 + || n->list_at(0)->type() != lazy_entry::string_t + || n->list_at(1)->type() != lazy_entry::int_t) + continue; + m_nodes.push_back(std::make_pair( + n->list_at(0)->string_value() + , int(n->list_at(1)->int_value()))); + } + } + + // extract creation date + size_type cd = torrent_file.dict_find_int_value("creation date", -1); + if (cd >= 0) + { + m_creation_date = long(cd); + } + + // if there are any url-seeds, extract them + lazy_entry const* url_seeds = torrent_file.dict_find("url-list"); + if (url_seeds && url_seeds->type() == lazy_entry::string_t && url_seeds->string_length() > 0) + { + web_seed_entry ent(maybe_url_encode(url_seeds->string_value()) + , web_seed_entry::url_seed); + if (m_multifile && ent.url[ent.url.size()-1] != '/') ent.url += '/'; + m_web_seeds.push_back(ent); + } + else if (url_seeds && url_seeds->type() == lazy_entry::list_t) + { + // only add a URL once + std::set unique; + for (int i = 0, end(url_seeds->list_size()); i < end; ++i) + { + lazy_entry const* url = url_seeds->list_at(i); + if (url->type() != lazy_entry::string_t) continue; + if (url->string_length() == 0) continue; + web_seed_entry ent(maybe_url_encode(url->string_value()) + , web_seed_entry::url_seed); + if (m_multifile && ent.url[ent.url.size()-1] != '/') ent.url += '/'; + if (unique.count(ent.url)) continue; + unique.insert(ent.url); + m_web_seeds.push_back(ent); + } + } + + // if there are any http-seeds, extract them + lazy_entry const* http_seeds = torrent_file.dict_find("httpseeds"); + if (http_seeds && http_seeds->type() == lazy_entry::string_t && http_seeds->string_length() > 0) + { + m_web_seeds.push_back(web_seed_entry(maybe_url_encode(http_seeds->string_value()) + , web_seed_entry::http_seed)); + } + else if (http_seeds && http_seeds->type() == lazy_entry::list_t) + { + // only add a URL once + std::set unique; + for (int i = 0, end(http_seeds->list_size()); i < end; ++i) + { + lazy_entry const* url = http_seeds->list_at(i); + if (url->type() != lazy_entry::string_t || url->string_length() == 0) continue; + std::string u = maybe_url_encode(url->string_value()); + if (unique.count(u)) continue; + unique.insert(u); + m_web_seeds.push_back(web_seed_entry(u, web_seed_entry::http_seed)); + } + } + + m_comment = torrent_file.dict_find_string_value("comment.utf-8"); + if (m_comment.empty()) m_comment = torrent_file.dict_find_string_value("comment"); + verify_encoding(m_comment); + + m_created_by = torrent_file.dict_find_string_value("created by.utf-8"); + if (m_created_by.empty()) m_created_by = torrent_file.dict_find_string_value("created by"); + verify_encoding(m_created_by); + + return true; + } + + boost::optional + torrent_info::creation_date() const + { + if (m_creation_date != 0) + { + return boost::optional(m_creation_date); + } + return boost::optional(); + } + + void torrent_info::add_tracker(std::string const& url, int tier) + { + announce_entry e(url); + e.tier = tier; + e.source = announce_entry::source_client; + m_urls.push_back(e); + + std::sort(m_urls.begin(), m_urls.end(), boost::bind(&announce_entry::tier, _1) + < boost::bind(&announce_entry::tier, _2)); + } + +#ifndef TORRENT_NO_DEPRECATE + namespace + { + struct filter_web_seed_type + { + filter_web_seed_type(web_seed_entry::type_t t_) : t(t_) {} + void operator() (web_seed_entry const& w) + { if (w.type == t) urls.push_back(w.url); } + std::vector urls; + web_seed_entry::type_t t; + }; + } + + std::vector torrent_info::url_seeds() const + { + return std::for_each(m_web_seeds.begin(), m_web_seeds.end() + , filter_web_seed_type(web_seed_entry::url_seed)).urls; + } + + std::vector torrent_info::http_seeds() const + { + return std::for_each(m_web_seeds.begin(), m_web_seeds.end() + , filter_web_seed_type(web_seed_entry::http_seed)).urls; + } + +#endif // TORRENT_NO_DEPRECATE + + void torrent_info::add_url_seed(std::string const& url + , std::string const& ext_auth + , web_seed_entry::headers_t const& ext_headers) + { + m_web_seeds.push_back(web_seed_entry(url, web_seed_entry::url_seed + , ext_auth, ext_headers)); + } + + void torrent_info::add_http_seed(std::string const& url + , std::string const& auth + , web_seed_entry::headers_t const& extra_headers) + { + m_web_seeds.push_back(web_seed_entry(url, web_seed_entry::http_seed + , auth, extra_headers)); + } + + +#if !defined TORRENT_NO_DEPRECATE && TORRENT_USE_IOSTREAM +// ------- start deprecation ------- + + void torrent_info::print(std::ostream& os) const + { + INVARIANT_CHECK; + + os << "trackers:\n"; + for (std::vector::const_iterator i = trackers().begin(); + i != trackers().end(); ++i) + { + os << i->tier << ": " << i->url << "\n"; + } + if (!m_comment.empty()) + os << "comment: " << m_comment << "\n"; + os << "private: " << (m_private?"yes":"no") << "\n"; + os << "number of pieces: " << num_pieces() << "\n"; + os << "piece length: " << piece_length() << "\n"; + os << "files:\n"; + for (int i = 0; i < m_files.num_files(); ++i) + os << " " << std::setw(11) << m_files.file_size(i) + << " " << m_files.file_path(i) << "\n"; + } + +// ------- end deprecation ------- +#endif + +#if TORRENT_USE_INVARIANT_CHECKS + void torrent_info::check_invariant() const + { + for (int i = 0; i < m_files.num_files(); ++i) + { + TORRENT_ASSERT(m_files.file_name_ptr(i) != 0); + if (m_files.file_name_len(i) != -1) + { + // name needs to point into the allocated info section buffer + TORRENT_ASSERT(m_files.file_name_ptr(i) >= m_info_section.get()); + TORRENT_ASSERT(m_files.file_name_ptr(i) < m_info_section.get() + m_info_section_size); + } + else + { + // name must be a valid string + TORRENT_ASSERT(strlen(m_files.file_name_ptr(i)) < 2048); + } + } + + if (m_piece_hashes != 0) + { + TORRENT_ASSERT(m_piece_hashes >= m_info_section.get()); + TORRENT_ASSERT(m_piece_hashes < m_info_section.get() + m_info_section_size); + } + } +#endif + +} + diff --git a/apps/Launcher/ext/libtorrent/src/tracker_manager.cpp b/apps/Launcher/ext/libtorrent/src/tracker_manager.cpp new file mode 100644 index 0000000000..b556325bb5 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/tracker_manager.cpp @@ -0,0 +1,357 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include + +#include + +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/udp_tracker_connection.hpp" +#include "libtorrent/aux_/session_impl.hpp" + +using boost::tuples::make_tuple; +using boost::tuples::tuple; + +namespace +{ + enum + { + minimum_tracker_response_length = 3, + http_buffer_size = 2048 + }; + +} + +namespace libtorrent +{ + timeout_handler::timeout_handler(io_service& ios) + : m_start_time(time_now_hires()) + , m_read_time(m_start_time) + , m_timeout(ios) + , m_completion_timeout(0) + , m_read_timeout(0) + , m_abort(false) + {} + + void timeout_handler::set_timeout(int completion_timeout, int read_timeout) + { + m_completion_timeout = completion_timeout; + m_read_timeout = read_timeout; + m_start_time = m_read_time = time_now_hires(); + + TORRENT_ASSERT(completion_timeout > 0 || read_timeout > 0); + + if (m_abort) return; + + int timeout = 0; + if (m_read_timeout > 0) timeout = m_read_timeout; + if (m_completion_timeout > 0) + { + timeout = timeout == 0 + ? m_completion_timeout + : (std::min)(m_completion_timeout, timeout); + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("timeout_handler::timeout_callback"); +#endif + error_code ec; + m_timeout.expires_at(m_read_time + seconds(timeout), ec); + m_timeout.async_wait(boost::bind( + &timeout_handler::timeout_callback, self(), _1)); + } + + void timeout_handler::restart_read_timeout() + { + m_read_time = time_now_hires(); + } + + void timeout_handler::cancel() + { + m_abort = true; + m_completion_timeout = 0; + error_code ec; + m_timeout.cancel(ec); + } + + void timeout_handler::timeout_callback(error_code const& error) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("timeout_handler::timeout_callback"); +#endif + if (m_abort) return; + + ptime now = time_now_hires(); + time_duration receive_timeout = now - m_read_time; + time_duration completion_timeout = now - m_start_time; + + if ((m_read_timeout + && m_read_timeout <= total_seconds(receive_timeout)) + || (m_completion_timeout + && m_completion_timeout <= total_seconds(completion_timeout)) + || error) + { + on_timeout(error); + return; + } + + int timeout = 0; + if (m_read_timeout > 0) timeout = m_read_timeout; + if (m_completion_timeout > 0) + { + timeout = timeout == 0 + ? int(m_completion_timeout - total_seconds(m_read_time - m_start_time)) + : (std::min)(int(m_completion_timeout - total_seconds(m_read_time - m_start_time)), timeout); + } +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("timeout_handler::timeout_callback"); +#endif + error_code ec; + m_timeout.expires_at(m_read_time + seconds(timeout), ec); + m_timeout.async_wait( + boost::bind(&timeout_handler::timeout_callback, self(), _1)); + } + + tracker_connection::tracker_connection( + tracker_manager& man + , tracker_request const& req + , io_service& ios + , boost::weak_ptr r) + : timeout_handler(ios) + , m_requester(r) + , m_man(man) + , m_req(req) + {} + + boost::shared_ptr tracker_connection::requester() const + { + return m_requester.lock(); + } + + void tracker_connection::fail(error_code const& ec, int code + , char const* msg, int interval, int min_interval) + { + // we need to post the error to avoid deadlock + get_io_service().post(boost::bind(&tracker_connection::fail_impl + , self(), ec, code, std::string(msg), interval, min_interval)); + } + + void tracker_connection::fail_impl(error_code const& ec, int code + , std::string msg, int interval, int min_interval) + { + boost::shared_ptr cb = requester(); + if (cb) cb->tracker_request_error(m_req, code, ec, msg.c_str() + , interval == 0 ? min_interval : interval); + close(); + } + + void tracker_connection::sent_bytes(int bytes) + { + m_man.sent_bytes(bytes); + } + + void tracker_connection::received_bytes(int bytes) + { + m_man.received_bytes(bytes); + } + + void tracker_connection::close() + { + cancel(); + m_man.remove_request(this); + } + + tracker_manager::~tracker_manager() + { + TORRENT_ASSERT(m_abort); + abort_all_requests(true); + } + + void tracker_manager::sent_bytes(int bytes) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + m_ses.m_stat.sent_tracker_bytes(bytes); + } + + void tracker_manager::received_bytes(int bytes) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + m_ses.m_stat.received_tracker_bytes(bytes); + } + + void tracker_manager::remove_request(tracker_connection const* c) + { + mutex_t::scoped_lock l(m_mutex); + + tracker_connections_t::iterator i = std::find(m_connections.begin() + , m_connections.end(), boost::intrusive_ptr(c)); + if (i == m_connections.end()) return; + + m_connections.erase(i); + } + + void tracker_manager::queue_request( + io_service& ios + , connection_queue& cc + , tracker_request req + , std::string const& auth + , boost::weak_ptr c) + { + mutex_t::scoped_lock l(m_mutex); + TORRENT_ASSERT(req.num_want >= 0); + TORRENT_ASSERT(!m_abort || req.event == tracker_request::stopped); + if (m_abort && req.event != tracker_request::stopped) return; + if (req.event == tracker_request::stopped) + req.num_want = 0; + + TORRENT_ASSERT(!m_abort || req.event == tracker_request::stopped); + if (m_abort && req.event != tracker_request::stopped) + return; + + std::string protocol = req.url.substr(0, req.url.find(':')); + + boost::intrusive_ptr con; + +#ifdef TORRENT_USE_OPENSSL + if (protocol == "http" || protocol == "https") +#else + if (protocol == "http") +#endif + { + con = new http_tracker_connection( + ios, cc, *this, req, c + , m_ses, m_proxy, auth +#if TORRENT_USE_I2P + , &m_ses.m_i2p_conn +#endif + ); + } + else if (protocol == "udp") + { + con = new udp_tracker_connection( + ios, cc, *this, req , c, m_ses + , m_proxy); + } + else + { + // we need to post the error to avoid deadlock + if (boost::shared_ptr r = c.lock()) + ios.post(boost::bind(&request_callback::tracker_request_error, r, req + , -1, error_code(errors::unsupported_url_protocol) + , "", 0)); + return; + } + + m_connections.push_back(con); + + boost::shared_ptr cb = con->requester(); + if (cb) cb->m_manager = this; + con->start(); + } + + bool tracker_manager::incoming_packet(error_code const& e + , udp::endpoint const& ep, char const* buf, int size) + { + // m_ses.m_stat.received_tracker_bytes(len + 28); + for (tracker_connections_t::iterator i = m_connections.begin(); + i != m_connections.end();) + { + boost::intrusive_ptr p = *i; + ++i; + // on_receive() may remove the tracker connection from the list + if (p->on_receive(e, ep, buf, size)) return true; + } + return false; + } + + bool tracker_manager::incoming_packet(error_code const& e + , char const* hostname, char const* buf, int size) + { + // m_ses.m_stat.received_tracker_bytes(len + 28); + for (tracker_connections_t::iterator i = m_connections.begin(); + i != m_connections.end();) + { + boost::intrusive_ptr p = *i; + ++i; + // on_receive() may remove the tracker connection from the list + if (p->on_receive_hostname(e, hostname, buf, size)) return true; + } + return false; + } + + void tracker_manager::abort_all_requests(bool all) + { + // removes all connections from m_connections + // except 'event=stopped'-requests + mutex_t::scoped_lock l(m_mutex); + + m_abort = true; + tracker_connections_t close_connections; + + for (tracker_connections_t::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + intrusive_ptr c = *i; + tracker_request const& req = c->tracker_req(); + if (req.event == tracker_request::stopped && !all) + continue; + + close_connections.push_back(c); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + boost::shared_ptr rc = c->requester(); + if (rc) rc->debug_log("aborting: %s", req.url.c_str()); +#endif + } + l.unlock(); + + for (tracker_connections_t::iterator i = close_connections.begin() + , end(close_connections.end()); i != end; ++i) + { + (*i)->close(); + } + } + + bool tracker_manager::empty() const + { + mutex_t::scoped_lock l(m_mutex); + return m_connections.empty(); + } + + int tracker_manager::num_requests() const + { + mutex_t::scoped_lock l(m_mutex); + return m_connections.size(); + } +} diff --git a/apps/Launcher/ext/libtorrent/src/udp_socket.cpp b/apps/Launcher/ext/libtorrent/src/udp_socket.cpp new file mode 100644 index 0000000000..a76efb8f60 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/udp_socket.cpp @@ -0,0 +1,1485 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" // for print_backtrace +#include "libtorrent/socket.hpp" +#include "libtorrent/udp_socket.hpp" +#include "libtorrent/connection_queue.hpp" +#include "libtorrent/socket_io.hpp" +#include "libtorrent/error.hpp" +#include "libtorrent/string_util.hpp" // for allocate_string_copy +#include "libtorrent/broadcast_socket.hpp" // for is_any +#include +#include +#include +#include +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif + +#if defined TORRENT_ASIO_DEBUGGING +#include "libtorrent/debug.hpp" +#endif + +using namespace libtorrent; + +udp_socket::udp_socket(asio::io_service& ios + , connection_queue& cc) + : m_observers_locked(false) + , m_ipv4_sock(ios) + , m_buf_size(0) + , m_new_buf_size(0) + , m_buf(0) +#if TORRENT_USE_IPV6 + , m_ipv6_sock(ios) +#endif + , m_bind_port(0) + , m_v4_outstanding(0) +#if TORRENT_USE_IPV6 + , m_v6_outstanding(0) +#endif + , m_socks5_sock(ios) + , m_connection_ticket(-1) + , m_cc(cc) + , m_resolver(ios) + , m_queue_packets(false) + , m_tunnel_packets(false) + , m_force_proxy(false) + , m_abort(false) + , m_outstanding_ops(0) +#if TORRENT_USE_IPV6 + , m_v6_write_subscribed(false) +#endif + , m_v4_write_subscribed(false) +{ +#if TORRENT_USE_ASSERTS + m_magic = 0x1337; + m_started = false; + m_outstanding_when_aborted = -1; + m_outstanding_connect_queue = 0; + m_outstanding_connect = 0; + m_outstanding_timeout = 0; + m_outstanding_resolve = 0; + m_outstanding_socks = 0; +#if defined BOOST_HAS_PTHREADS + m_thread = 0; +#endif +#endif + + m_buf_size = 2048; + m_new_buf_size = m_buf_size; + m_buf = (char*)malloc(m_buf_size); +} + +udp_socket::~udp_socket() +{ + free(m_buf); +#if TORRENT_USE_IPV6 + TORRENT_ASSERT_VAL(m_v6_outstanding == 0, m_v6_outstanding); +#endif + TORRENT_ASSERT_VAL(m_v4_outstanding == 0, m_v4_outstanding); + TORRENT_ASSERT(m_magic == 0x1337); + TORRENT_ASSERT(m_observers_locked == false); +#if TORRENT_USE_ASSERTS + m_magic = 0; +#endif + TORRENT_ASSERT(m_outstanding_ops == 0); +} + +#if TORRENT_USE_ASSERTS + #define CHECK_MAGIC check_magic_ cm_(m_magic) + struct check_magic_ + { + check_magic_(int& m_): m(m_) { TORRENT_ASSERT(m == 0x1337); } + ~check_magic_() { TORRENT_ASSERT(m == 0x1337); } + int& m; + }; +#else + #define CHECK_MAGIC do {} while (false) +#endif + +void udp_socket::send_hostname(char const* hostname, int port + , char const* p, int len, error_code& ec, int flags) +{ + CHECK_MAGIC; + + TORRENT_ASSERT(is_single_thread()); + + // if the sockets are closed, the udp_socket is closing too + if (!is_open()) + { + ec = error_code(boost::system::errc::bad_file_descriptor, generic_category()); + return; + } + + if (m_tunnel_packets) + { + // send udp packets through SOCKS5 server + wrap(hostname, port, p, len, ec); + return; + } + + // this function is only supported when we're using a proxy + if (!m_queue_packets && !m_force_proxy) + { + address target = address::from_string(hostname, ec); + if (!ec) send(udp::endpoint(target, port), p, len, ec, 0); + return; + } + + if (m_queue.size() > 1000 || (flags & dont_queue)) return; + + m_queue.push_back(queued_packet()); + queued_packet& qp = m_queue.back(); + qp.ep.port(port); + + address target = address::from_string(hostname, ec); + if (ec) qp.ep.address(target); + else qp.hostname = allocate_string_copy(hostname); + qp.buf.insert(qp.buf.begin(), p, p + len); + qp.flags = 0; +} + +void udp_socket::send(udp::endpoint const& ep, char const* p, int len + , error_code& ec, int flags) +{ + CHECK_MAGIC; + + TORRENT_ASSERT(is_single_thread()); + + // if the sockets are closed, the udp_socket is closing too + if (!is_open()) + { + ec = error_code(boost::system::errc::bad_file_descriptor, generic_category()); + return; + } + + if (!(flags & peer_connection) || m_proxy_settings.proxy_peer_connections) + { + if (m_tunnel_packets) + { + // send udp packets through SOCKS5 server + wrap(ep, p, len, ec); + return; + } + + if (m_queue_packets) + { + if (m_queue.size() > 1000 || (flags & dont_queue)) return; + + m_queue.push_back(queued_packet()); + queued_packet& qp = m_queue.back(); + qp.ep = ep; + qp.hostname = 0; + qp.flags = flags; + qp.buf.insert(qp.buf.begin(), p, p + len); + return; + } + } + + if (m_force_proxy) return; + +#if TORRENT_USE_IPV6 + if (ep.address().is_v6() && m_ipv6_sock.is_open()) + m_ipv6_sock.send_to(asio::buffer(p, len), ep, 0, ec); + else +#endif + m_ipv4_sock.send_to(asio::buffer(p, len), ep, 0, ec); + + if (ec == error::would_block || ec == error::try_again) + { +#if TORRENT_USE_IPV6 + if (ep.address().is_v6() && m_ipv6_sock.is_open()) + { + if (!m_v6_write_subscribed) + { + m_ipv6_sock.async_send(asio::null_buffers() + , boost::bind(&udp_socket::on_writable, this, _1, &m_ipv6_sock)); + m_v6_write_subscribed = true; + } + } + else +#endif + { + if (!m_v4_write_subscribed) + { + m_ipv4_sock.async_send(asio::null_buffers() + , boost::bind(&udp_socket::on_writable, this, _1, &m_ipv4_sock)); + m_v4_write_subscribed = true; + } + } + } +} + +void udp_socket::on_writable(error_code const& ec, udp::socket* s) +{ +#if TORRENT_USE_IPV6 + if (s == &m_ipv6_sock) + m_v6_write_subscribed = false; + else +#endif + m_v4_write_subscribed = false; + + call_writable_handler(); +} + +// called whenever the socket is readable +void udp_socket::on_read(error_code const& ec, udp::socket* s) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_socket::on_read"); +#endif + + TORRENT_ASSERT(m_magic == 0x1337); + TORRENT_ASSERT(is_single_thread()); + +#if TORRENT_USE_IPV6 + if (s == &m_ipv6_sock) + { + TORRENT_ASSERT(m_v6_outstanding > 0); + --m_v6_outstanding; + } + else +#endif + { + TORRENT_ASSERT(m_v4_outstanding > 0); + --m_v4_outstanding; + } + + if (ec == asio::error::operation_aborted) return; + if (m_abort) return; + + CHECK_MAGIC; + + for (;;) + { + error_code ec; + udp::endpoint ep; + size_t bytes_transferred = s->receive_from(asio::buffer(m_buf, m_buf_size), ep, 0, ec); + + // TODO: it would be nice to detect this on posix systems also +#ifdef TORRENT_WINDOWS + if ((ec == error_code(ERROR_MORE_DATA, get_system_category()) + || ec == error_code(WSAEMSGSIZE, get_system_category())) + && m_buf_size < 65536) + { + // if this function fails to allocate memory, m_buf_size + // is set to 0. In that case, don't issue the async_read(). + set_buf_size(m_buf_size * 2); + if (m_buf_size == 0) return; + continue; + } +#endif + + if (ec == asio::error::would_block || ec == asio::error::try_again) break; + on_read_impl(s, ep, ec, bytes_transferred); + } + call_drained_handler(); + setup_read(s); +} + +void udp_socket::call_handler(error_code const& ec, udp::endpoint const& ep, char const* buf, int size) +{ + m_observers_locked = true; + for (std::vector::iterator i = m_observers.begin(); + i != m_observers.end();) + { + bool ret = false; + TORRENT_TRY { + ret = (*i)->incoming_packet(ec, ep, buf, size); + } TORRENT_CATCH (std::exception&) {} + if (*i == NULL) i = m_observers.erase(i); + else ++i; + if (ret) break; + } + if (!m_added_observers.empty()) + { + m_observers.insert(m_observers.end(), m_added_observers.begin(), m_added_observers.end()); + m_added_observers.clear(); + } + m_observers_locked = false; + if (m_new_buf_size != m_buf_size) + set_buf_size(m_new_buf_size); +} + +void udp_socket::call_handler(error_code const& ec, const char* host, char const* buf, int size) +{ + m_observers_locked = true; + for (std::vector::iterator i = m_observers.begin(); + i != m_observers.end();) + { + bool ret = false; + TORRENT_TRY { + ret = (*i)->incoming_packet(ec, host, buf, size); + } TORRENT_CATCH (std::exception&) {} + if (*i == NULL) i = m_observers.erase(i); + else ++i; + if (ret) break; + } + if (!m_added_observers.empty()) + { + m_observers.insert(m_observers.end(), m_added_observers.begin(), m_added_observers.end()); + m_added_observers.clear(); + } + m_observers_locked = false; + if (m_new_buf_size != m_buf_size) + set_buf_size(m_new_buf_size); +} + +void udp_socket::call_drained_handler() +{ + m_observers_locked = true; + for (std::vector::iterator i = m_observers.begin(); + i != m_observers.end();) + { + TORRENT_TRY { + (*i)->socket_drained(); + } TORRENT_CATCH (std::exception&) {} + if (*i == NULL) i = m_observers.erase(i); + else ++i; + } + if (!m_added_observers.empty()) + { + m_observers.insert(m_observers.end(), m_added_observers.begin(), m_added_observers.end()); + m_added_observers.clear(); + } + m_observers_locked = false; + if (m_new_buf_size != m_buf_size) + set_buf_size(m_new_buf_size); +} + +void udp_socket::call_writable_handler() +{ + m_observers_locked = true; + for (std::vector::iterator i = m_observers.begin(); + i != m_observers.end();) + { + TORRENT_TRY { + (*i)->writable(); + } TORRENT_CATCH (std::exception&) {} + if (*i == NULL) i = m_observers.erase(i); + else ++i; + } + if (!m_added_observers.empty()) + { + m_observers.insert(m_observers.end(), m_added_observers.begin(), m_added_observers.end()); + m_added_observers.clear(); + } + m_observers_locked = false; + if (m_new_buf_size != m_buf_size) + set_buf_size(m_new_buf_size); +} + +void udp_socket::subscribe(udp_socket_observer* o) +{ + TORRENT_ASSERT(std::find(m_observers.begin(), m_observers.end(), o) == m_observers.end()); + if (m_observers_locked) + m_added_observers.push_back(o); + else + m_observers.push_back(o); +} + +void udp_socket::unsubscribe(udp_socket_observer* o) +{ + std::vector::iterator i = std::find(m_observers.begin(), m_observers.end(), o); + if (i == m_observers.end()) return; + if (m_observers_locked) + *i = NULL; + else + m_observers.erase(i); +} + +void udp_socket::on_read_impl(udp::socket* s, udp::endpoint const& ep + , error_code const& e, std::size_t bytes_transferred) +{ + TORRENT_ASSERT(m_magic == 0x1337); + TORRENT_ASSERT(is_single_thread()); + + if (e) + { + call_handler(e, ep, 0, 0); + + // don't stop listening on recoverable errors + if (e != asio::error::host_unreachable + && e != asio::error::fault + && e != asio::error::connection_reset + && e != asio::error::connection_refused + && e != asio::error::connection_aborted + && e != asio::error::operation_aborted + && e != asio::error::network_reset + && e != asio::error::network_unreachable +#ifdef WIN32 + // ERROR_MORE_DATA means the same thing as EMSGSIZE + && e != error_code(ERROR_MORE_DATA, get_system_category()) + && e != error_code(ERROR_HOST_UNREACHABLE, get_system_category()) + && e != error_code(ERROR_PORT_UNREACHABLE, get_system_category()) + && e != error_code(ERROR_RETRY, get_system_category()) + && e != error_code(ERROR_NETWORK_UNREACHABLE, get_system_category()) + && e != error_code(ERROR_CONNECTION_REFUSED, get_system_category()) + && e != error_code(ERROR_CONNECTION_ABORTED, get_system_category()) +#endif + && e != asio::error::message_size) + { + return; + } + + if (m_abort) return; + + return; + } + + TORRENT_TRY { + + if (m_tunnel_packets) + { + // if the source IP doesn't match the proxy's, ignore the packet + if (ep == m_udp_proxy_addr) + unwrap(e, m_buf, bytes_transferred); + } + else if (!m_force_proxy) // block incoming packets that aren't coming via the proxy + { + call_handler(e, ep, m_buf, bytes_transferred); + } + + } TORRENT_CATCH (std::exception&) {} +} + +void udp_socket::setup_read(udp::socket* s) +{ + if (m_abort) return; + +#if TORRENT_USE_IPV6 + if (s == &m_ipv6_sock) + ++m_v6_outstanding; + else +#endif + ++m_v4_outstanding; + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_socket::on_read"); +#endif + + udp::endpoint ep; + TORRENT_TRY + { + s->async_receive_from(asio::null_buffers() + , ep, boost::bind(&udp_socket::on_read, this, _1, s)); + } + TORRENT_CATCH(boost::system::system_error& e) + { +#ifdef BOOST_NO_EXCEPTIONS + // dummy + error_code ec; + boost::system::system_error e(ec); +#endif + get_io_service().post(boost::bind(&udp_socket::on_read + , this, e.code(), s)); + } +} + +void udp_socket::wrap(udp::endpoint const& ep, char const* p, int len, error_code& ec) +{ + CHECK_MAGIC; + using namespace libtorrent::detail; + + char header[25]; + char* h = header; + + write_uint16(0, h); // reserved + write_uint8(0, h); // fragment + write_uint8(ep.address().is_v4()?1:4, h); // atyp + write_endpoint(ep, h); + + boost::array iovec; + iovec[0] = asio::const_buffer(header, h - header); + iovec[1] = asio::const_buffer(p, len); + +#if TORRENT_USE_IPV6 + if (m_udp_proxy_addr.address().is_v4() && m_ipv4_sock.is_open()) +#endif + m_ipv4_sock.send_to(iovec, m_udp_proxy_addr, 0, ec); +#if TORRENT_USE_IPV6 + else + m_ipv6_sock.send_to(iovec, m_udp_proxy_addr, 0, ec); +#endif +} + +void udp_socket::wrap(char const* hostname, int port, char const* p, int len, error_code& ec) +{ + CHECK_MAGIC; + using namespace libtorrent::detail; + + char header[270]; + char* h = header; + + write_uint16(0, h); // reserved + write_uint8(0, h); // fragment + write_uint8(3, h); // atyp + int hostlen = (std::min)(strlen(hostname), size_t(255)); + write_uint8(hostlen, h); // hostname len + memcpy(h, hostname, hostlen); + h += hostlen; + write_uint16(port, h); + + boost::array iovec; + iovec[0] = asio::const_buffer(header, h - header); + iovec[1] = asio::const_buffer(p, len); + +#if TORRENT_USE_IPV6 + if (m_udp_proxy_addr.address().is_v4() && m_ipv4_sock.is_open()) +#endif + m_ipv4_sock.send_to(iovec, m_udp_proxy_addr, 0, ec); +#if TORRENT_USE_IPV6 + else + m_ipv6_sock.send_to(iovec, m_udp_proxy_addr, 0, ec); +#endif +} + +// unwrap the UDP packet from the SOCKS5 header +void udp_socket::unwrap(error_code const& e, char const* buf, int size) +{ + CHECK_MAGIC; + using namespace libtorrent::detail; + + // the minimum socks5 header size + if (size <= 10) return; + + char const* p = buf; + p += 2; // reserved + int frag = read_uint8(p); + // fragmentation is not supported + if (frag != 0) return; + + udp::endpoint sender; + + int atyp = read_uint8(p); + if (atyp == 1) + { + // IPv4 + sender = read_v4_endpoint(p); + } +#if TORRENT_USE_IPV6 + else if (atyp == 4) + { + // IPv6 + sender = read_v6_endpoint(p); + } +#endif + else + { + int len = read_uint8(p); + if (len > (buf + size) - p) return; + std::string hostname(p, p + len); + p += len; + call_handler(e, hostname.c_str(), p, size - (p - buf)); + return; + } + + call_handler(e, sender, p, size - (p - buf)); +} + +#if !defined BOOST_ASIO_ENABLE_CANCELIO && defined TORRENT_WINDOWS +#error BOOST_ASIO_ENABLE_CANCELIO needs to be defined when building libtorrent to enable cancel() in asio on windows +#endif + +void udp_socket::close() +{ + TORRENT_ASSERT(is_single_thread()); + TORRENT_ASSERT(m_magic == 0x1337); + + error_code ec; + // if we close the socket here, we can't shut down + // utp connections or NAT-PMP. We need to cancel the + // outstanding operations + m_ipv4_sock.cancel(ec); + if (ec == error::operation_not_supported) + m_ipv4_sock.close(ec); + TORRENT_ASSERT_VAL(!ec || ec == error::bad_descriptor, ec); +#if TORRENT_USE_IPV6 + m_ipv6_sock.cancel(ec); + if (ec == error::operation_not_supported) + m_ipv6_sock.close(ec); + TORRENT_ASSERT_VAL(!ec || ec == error::bad_descriptor, ec); +#endif + m_socks5_sock.cancel(ec); + if (ec == error::operation_not_supported) + m_socks5_sock.close(ec); + TORRENT_ASSERT_VAL(!ec || ec == error::bad_descriptor, ec); + m_resolver.cancel(); + m_abort = true; + +#if TORRENT_USE_ASSERTS + m_outstanding_when_aborted = num_outstanding(); +#endif + + if (m_connection_ticket >= 0) + { + if (m_cc.done(m_connection_ticket)) + m_connection_ticket = -1; + + // we just called done, which means on_timeout + // won't be called. Decrement the outstanding + // ops counter for that +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_timeout > 0); + --m_outstanding_timeout; + + print_backtrace(timeout_stack, sizeof(timeout_stack)); +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + if (m_abort) return; + } + +} + +void udp_socket::set_buf_size(int s) +{ + TORRENT_ASSERT(is_single_thread()); + + if (m_observers_locked) + { + // we can't actually reallocate the buffer while + // it's being used by the observers, we have to + // do that once we're done iterating over them + m_new_buf_size = s; + return; + } + + if (s == m_buf_size) return; + + bool no_mem = false; + void* tmp = realloc(m_buf, s); + if (tmp != 0) + { + m_buf = (char*)tmp; + m_buf_size = s; + m_new_buf_size = s; + } + else + { + no_mem = true; + } + + if (no_mem) + { + free(m_buf); + m_buf = 0; + m_buf_size = 0; + m_new_buf_size = 0; + udp::endpoint ep; + call_handler(error::no_memory, ep, 0, 0); + close(); + } + + int size = m_buf_size; + + // don't shrink the size of the receive buffer + error_code ec; + boost::asio::socket_base::receive_buffer_size recv_size; + m_ipv4_sock.get_option(recv_size, ec); + if (!ec) size = (std::max)(recv_size.value(), size); +#if TORRENT_USE_IPV6 + m_ipv6_sock.get_option(recv_size, ec); + if (!ec) size = (std::max)(recv_size.value(), size); +#endif + + error_code ignore_errors; + // set the internal buffer sizes as well + m_ipv4_sock.set_option(boost::asio::socket_base::receive_buffer_size(size) + , ignore_errors); +#if TORRENT_USE_IPV6 + m_ipv6_sock.set_option(boost::asio::socket_base::receive_buffer_size(size) + , ignore_errors); +#endif +} + +void udp_socket::bind(udp::endpoint const& ep, error_code& ec) +{ + CHECK_MAGIC; + TORRENT_ASSERT(is_single_thread()); + + TORRENT_ASSERT(m_abort == false); + if (m_abort) + { + ec = boost::asio::error::operation_aborted; + return; + } + + if (m_ipv4_sock.is_open()) m_ipv4_sock.close(ec); +#if TORRENT_USE_IPV6 + if (m_ipv6_sock.is_open()) m_ipv6_sock.close(ec); +#endif + + if (ep.address().is_v4()) + { + m_ipv4_sock.open(udp::v4(), ec); + if (ec) return; + m_ipv4_sock.bind(ep, ec); + if (ec) return; + udp::socket::non_blocking_io ioc(true); + m_ipv4_sock.io_control(ioc, ec); + if (ec) return; + setup_read(&m_ipv4_sock); + } + +#if TORRENT_USE_IPV6 + if (supports_ipv6() && (ep.address().is_v6() || is_any(ep.address()))) + { + udp::endpoint ep6 = ep; + if (is_any(ep.address())) ep6.address(address_v6::any()); + m_ipv6_sock.open(udp::v6(), ec); + if (ec) return; +#ifdef IPV6_V6ONLY + m_ipv6_sock.set_option(v6only(true), ec); + ec.clear(); +#endif + m_ipv6_sock.bind(ep6, ec); + if (ec) return; + udp::socket::non_blocking_io ioc(true); + m_ipv6_sock.io_control(ioc, ec); + if (ec) return; + setup_read(&m_ipv6_sock); + } +#endif +#if TORRENT_USE_ASSERTS + m_started = true; +#endif + m_bind_port = ep.port(); +} + +void udp_socket::set_proxy_settings(proxy_settings const& ps) +{ + CHECK_MAGIC; + TORRENT_ASSERT(is_single_thread()); + + error_code ec; + m_socks5_sock.close(ec); + m_tunnel_packets = false; + + m_proxy_settings = ps; + + if (m_abort) return; + + if (ps.type == proxy_settings::socks5 + || ps.type == proxy_settings::socks5_pw) + { + m_queue_packets = true; + // connect to socks5 server and open up the UDP tunnel + tcp::resolver::query q(ps.hostname, to_string(ps.port).elems); + ++m_outstanding_ops; +#if TORRENT_USE_ASSERTS + ++m_outstanding_resolve; +#endif +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_socket::on_name_lookup"); +#endif + m_resolver.async_resolve(q, boost::bind( + &udp_socket::on_name_lookup, this, _1, _2)); + } +} + +void udp_socket::on_name_lookup(error_code const& e, tcp::resolver::iterator i) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_socket::on_name_lookup"); +#endif +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_resolve > 0); + --m_outstanding_resolve; +#endif + + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + + if (m_abort) return; + CHECK_MAGIC; + + if (e == asio::error::operation_aborted) return; + + TORRENT_ASSERT(is_single_thread()); + + if (e) + { + if (m_force_proxy) + { + call_handler(e, udp::endpoint(), 0, 0); + } + else + { + // if we can't connect to the proxy, and + // we're not in privacy mode, try to just + // not use a proxy + m_proxy_settings = proxy_settings(); + m_tunnel_packets = false; + } + + drain_queue(); + return; + } + + m_proxy_addr.address(i->endpoint().address()); + m_proxy_addr.port(i->endpoint().port()); + // on_connect may be called from within this thread + // the semantics for on_connect and on_timeout is + // a bit complicated. See comments in connection_queue.hpp + // for more details. This semantic determines how and + // when m_outstanding_ops may be decremented + // To simplyfy this, it's probably a good idea to + // merge on_connect and on_timeout to a single function + + // on_timeout may be called before on_connected + // so increment the outstanding ops + // it may also not be called in case we call + // connection_queue::done first, so be sure to + // decrement if that happens + m_outstanding_ops += 2; + +#if TORRENT_USE_ASSERTS + ++m_outstanding_timeout; + ++m_outstanding_connect_queue; +#endif + m_cc.enqueue(boost::bind(&udp_socket::on_connect, this, _1) + , boost::bind(&udp_socket::on_timeout, this), seconds(10)); +} + +void udp_socket::on_timeout() +{ +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_timeout > 0); + --m_outstanding_timeout; + print_backtrace(timeout_stack, sizeof(timeout_stack)); +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + m_queue_packets = false; + + if (m_abort) return; + + CHECK_MAGIC; + TORRENT_ASSERT(is_single_thread()); + + error_code ec; + m_socks5_sock.close(ec); + TORRENT_ASSERT(m_cc.done(m_connection_ticket) == false); + m_connection_ticket = -1; +} + +void udp_socket::on_connect(int ticket) +{ + TORRENT_ASSERT(is_single_thread()); +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_connect_queue > 0); + --m_outstanding_connect_queue; +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + + CHECK_MAGIC; + + if (ticket == -1) + { +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_timeout > 0); + --m_outstanding_timeout; + print_backtrace(timeout_stack, sizeof(timeout_stack)); +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + close(); + return; + } + + if (m_abort) return; + if (is_closed()) return; + + if (m_connection_ticket != -1) + { + // there's already an outstanding connect. Cancel it. + m_socks5_sock.close(); + m_connection_ticket = -1; + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_socket::on_connected"); +#endif + m_connection_ticket = ticket; + + error_code ec; + m_socks5_sock.open(m_proxy_addr.address().is_v4()?tcp::v4():tcp::v6(), ec); + + // enable keepalives + m_socks5_sock.set_option(boost::asio::socket_base::keep_alive(true), ec); + + ++m_outstanding_ops; +#if TORRENT_USE_ASSERTS + ++m_outstanding_connect; +#endif + m_socks5_sock.async_connect(tcp::endpoint(m_proxy_addr.address(), m_proxy_addr.port()) + , boost::bind(&udp_socket::on_connected, this, _1, ticket)); +} + +void udp_socket::on_connected(error_code const& e, int ticket) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_socket::on_connected"); +#endif +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_connect > 0); + --m_outstanding_connect; +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + CHECK_MAGIC; + + TORRENT_ASSERT(is_single_thread()); + if (m_connection_ticket == -1 + && e == asio::error::operation_aborted) + { + // this socket has already been aborted and closed. + return; + } + + if (m_cc.done(ticket)) + { + // if the tickets mismatch, another connection attempt + // was initiated while waiting for this one to complete. + if (ticket == m_connection_ticket) + m_connection_ticket = -1; + } + + // we just called done, which means on_timeout + // won't be called. Decrement the outstanding + // ops counter for that +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_timeout > 0); + --m_outstanding_timeout; + print_backtrace(timeout_stack, sizeof(timeout_stack)); +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + + if (e == asio::error::operation_aborted) return; + + // if ticket != m_connection_ticket, it means m_connection_ticket + // will not have been reset, and it means we are still waiting + // for a connection attempt. + if (m_connection_ticket != -1) return; + + if (m_abort) return; + + if (e) + { + call_handler(e, udp::endpoint(), 0, 0); + return; + } + + using namespace libtorrent::detail; + + // send SOCKS5 authentication methods + char* p = &m_tmp_buf[0]; + write_uint8(5, p); // SOCKS VERSION 5 + if (m_proxy_settings.username.empty() + || m_proxy_settings.type == proxy_settings::socks5) + { + write_uint8(1, p); // 1 authentication method (no auth) + write_uint8(0, p); // no authentication + } + else + { + write_uint8(2, p); // 2 authentication methods + write_uint8(0, p); // no authentication + write_uint8(2, p); // username/password + } + TORRENT_ASSERT_VAL(p - m_tmp_buf < int(sizeof(m_tmp_buf)), (p - m_tmp_buf)); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_socket::on_handshake1"); +#endif + ++m_outstanding_ops; +#if TORRENT_USE_ASSERTS + ++m_outstanding_socks; +#endif + asio::async_write(m_socks5_sock, asio::buffer(m_tmp_buf, p - m_tmp_buf) + , boost::bind(&udp_socket::handshake1, this, _1)); +} + +void udp_socket::handshake1(error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_socket::on_handshake1"); +#endif +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_socks > 0); + --m_outstanding_socks; +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + if (m_abort) return; + CHECK_MAGIC; + if (e) + { + drain_queue(); + return; + } + + TORRENT_ASSERT(is_single_thread()); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_socket::on_handshake2"); +#endif + ++m_outstanding_ops; +#if TORRENT_USE_ASSERTS + ++m_outstanding_socks; +#endif + asio::async_read(m_socks5_sock, asio::buffer(m_tmp_buf, 2) + , boost::bind(&udp_socket::handshake2, this, _1)); +} + +void udp_socket::handshake2(error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_socket::on_handshake2"); +#endif +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_socks > 0); + --m_outstanding_socks; +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + if (m_abort) return; + CHECK_MAGIC; + + if (e) + { + drain_queue(); + return; + } + + using namespace libtorrent::detail; + + TORRENT_ASSERT(is_single_thread()); + + char* p = &m_tmp_buf[0]; + int version = read_uint8(p); + int method = read_uint8(p); + + if (version < 5) + { + error_code ec; + m_socks5_sock.close(ec); + drain_queue(); + return; + } + + if (method == 0) + { + socks_forward_udp(/*l*/); + } + else if (method == 2) + { + if (m_proxy_settings.username.empty()) + { + error_code ec; + m_socks5_sock.close(ec); + drain_queue(); + return; + } + + // start sub-negotiation + char* p = &m_tmp_buf[0]; + write_uint8(1, p); + write_uint8(m_proxy_settings.username.size(), p); + write_string(m_proxy_settings.username, p); + write_uint8(m_proxy_settings.password.size(), p); + write_string(m_proxy_settings.password, p); + TORRENT_ASSERT_VAL(p - m_tmp_buf < int(sizeof(m_tmp_buf)), (p - m_tmp_buf)); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_socket::on_handshake3"); +#endif + ++m_outstanding_ops; +#if TORRENT_USE_ASSERTS + ++m_outstanding_socks; +#endif + asio::async_write(m_socks5_sock, asio::buffer(m_tmp_buf, p - m_tmp_buf) + , boost::bind(&udp_socket::handshake3, this, _1)); + } + else + { + drain_queue(); + error_code ec; + m_socks5_sock.close(ec); + return; + } +} + +void udp_socket::handshake3(error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_socket::on_handshake3"); +#endif +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_socks > 0); + --m_outstanding_socks; +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + if (m_abort) return; + CHECK_MAGIC; + if (e) + { + drain_queue(); + return; + } + + TORRENT_ASSERT(is_single_thread()); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_socket::on_handshake4"); +#endif + ++m_outstanding_ops; +#if TORRENT_USE_ASSERTS + ++m_outstanding_socks; +#endif + asio::async_read(m_socks5_sock, asio::buffer(m_tmp_buf, 2) + , boost::bind(&udp_socket::handshake4, this, _1)); +} + +void udp_socket::handshake4(error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_socket::on_handshake4"); +#endif +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_socks > 0); + --m_outstanding_socks; +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + if (m_abort) return; + CHECK_MAGIC; + if (e) + { + drain_queue(); + return; + } + + TORRENT_ASSERT(is_single_thread()); + + using namespace libtorrent::detail; + + char* p = &m_tmp_buf[0]; + int version = read_uint8(p); + int status = read_uint8(p); + + if (version != 1 || status != 0) + { + drain_queue(); + return; + } + + socks_forward_udp(/*l*/); +} + +void udp_socket::socks_forward_udp() +{ + CHECK_MAGIC; + using namespace libtorrent::detail; + + // send SOCKS5 UDP command + char* p = &m_tmp_buf[0]; + write_uint8(5, p); // SOCKS VERSION 5 + write_uint8(3, p); // UDP ASSOCIATE command + write_uint8(0, p); // reserved + error_code ec; + write_uint8(1, p); // ATYP = IPv4 + write_uint32(0, p); // 0.0.0.0 + write_uint16(0, p); // :0 + TORRENT_ASSERT_VAL(p - m_tmp_buf < int(sizeof(m_tmp_buf)), (p - m_tmp_buf)); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_socket::connect1"); +#endif + ++m_outstanding_ops; +#if TORRENT_USE_ASSERTS + ++m_outstanding_socks; +#endif + asio::async_write(m_socks5_sock, asio::buffer(m_tmp_buf, p - m_tmp_buf) + , boost::bind(&udp_socket::connect1, this, _1)); +} + +void udp_socket::connect1(error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_socket::connect1"); +#endif +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_socks > 0); + --m_outstanding_socks; +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + if (m_abort) return; + CHECK_MAGIC; + if (e) + { + drain_queue(); + return; + } + + TORRENT_ASSERT(is_single_thread()); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_socket::connect2"); +#endif + ++m_outstanding_ops; +#if TORRENT_USE_ASSERTS + ++m_outstanding_socks; +#endif + asio::async_read(m_socks5_sock, asio::buffer(m_tmp_buf, 10) + , boost::bind(&udp_socket::connect2, this, _1)); +} + +void udp_socket::connect2(error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_socket::connect2"); +#endif +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_socks > 0); + --m_outstanding_socks; +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + + if (m_abort) + { + m_queue.clear(); + return; + } + CHECK_MAGIC; + if (e) + { + drain_queue(); + return; + } + + TORRENT_ASSERT(is_single_thread()); + + using namespace libtorrent::detail; + + char* p = &m_tmp_buf[0]; + int version = read_uint8(p); // VERSION + int status = read_uint8(p); // STATUS + ++p; // RESERVED + int atyp = read_uint8(p); // address type + + if (version != 5 || status != 0) + { + drain_queue(); + return; + } + + if (atyp == 1) + { + m_udp_proxy_addr.address(address_v4(read_uint32(p))); + m_udp_proxy_addr.port(read_uint16(p)); + } + else + { + // in this case we need to read more data from the socket + TORRENT_ASSERT(false && "not implemented yet!"); + drain_queue(); + return; + } + + m_tunnel_packets = true; + drain_queue(); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_socket::hung_up"); +#endif + ++m_outstanding_ops; +#if TORRENT_USE_ASSERTS + ++m_outstanding_socks; +#endif + asio::async_read(m_socks5_sock, asio::buffer(m_tmp_buf, 10) + , boost::bind(&udp_socket::hung_up, this, _1)); +} + +void udp_socket::hung_up(error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_socket::hung_up"); +#endif +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_socks > 0); + --m_outstanding_socks; +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + if (m_abort) return; + CHECK_MAGIC; + TORRENT_ASSERT(is_single_thread()); + + if (e == asio::error::operation_aborted || m_abort) return; + + // the socks connection was closed, re-open it + set_proxy_settings(m_proxy_settings); +} + +void udp_socket::drain_queue() +{ + m_queue_packets = false; + + // forward all packets that were put in the queue + while (!m_queue.empty()) + { + queued_packet const& p = m_queue.front(); + error_code ec; + if (p.hostname) + { + udp_socket::send_hostname(p.hostname, p.ep.port(), &p.buf[0] + , p.buf.size(), ec, p.flags | dont_queue); + free(p.hostname); + } + else + { + udp_socket::send(p.ep, &p.buf[0], p.buf.size(), ec, p.flags | dont_queue); + } + m_queue.pop_front(); + } +} + +rate_limited_udp_socket::rate_limited_udp_socket(io_service& ios + , connection_queue& cc) + : udp_socket(ios, cc) + , m_rate_limit(8000) + , m_quota(8000) + , m_last_tick(time_now()) +{ +} + +bool rate_limited_udp_socket::send(udp::endpoint const& ep, char const* p + , int len, error_code& ec, int flags) +{ + ptime now = time_now_hires(); + time_duration delta = now - m_last_tick; + m_last_tick = now; + + // add any new quota we've accrued since last time + m_quota += boost::uint64_t(m_rate_limit) * total_microseconds(delta) / 1000000; + + // allow 3 seconds worth of burst + if (m_quota > 3 * m_rate_limit) m_quota = 3 * m_rate_limit; + + // if there's no quota, and it's OK to drop, just + // drop the packet + if (m_quota < len && (flags & dont_drop) == 0) return false; + + m_quota -= len; + if (m_quota < 0) m_quota = 0; + udp_socket::send(ep, p, len, ec, flags); + return true; +} + diff --git a/apps/Launcher/ext/libtorrent/src/udp_tracker_connection.cpp b/apps/Launcher/ext/libtorrent/src/udp_tracker_connection.cpp new file mode 100644 index 0000000000..07230f11f5 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/udp_tracker_connection.cpp @@ -0,0 +1,724 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/parse_url.hpp" +#include "libtorrent/udp_tracker_connection.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/broadcast_socket.hpp" // for is_any +#include "libtorrent/random.hpp" + +namespace libtorrent +{ + + std::map + udp_tracker_connection::m_connection_cache; + + mutex udp_tracker_connection::m_cache_mutex; + + udp_tracker_connection::udp_tracker_connection( + io_service& ios + , connection_queue& cc + , tracker_manager& man + , tracker_request const& req + , boost::weak_ptr c + , aux::session_impl& ses + , proxy_settings const& proxy) + : tracker_connection(man, req, ios, c) + , m_abort(false) + , m_transaction_id(0) + , m_ses(ses) + , m_attempts(0) + , m_state(action_error) + , m_proxy(proxy) + { + } + + void udp_tracker_connection::start() + { + std::string hostname; + std::string protocol; + int port; + error_code ec; + + using boost::tuples::ignore; + boost::tie(protocol, ignore, hostname, port, ignore) + = parse_url_components(tracker_req().url, ec); + if (port == -1) port = protocol == "http" ? 80 : 443; + + if (ec) + { + tracker_connection::fail(ec); + return; + } + + session_settings const& settings = m_ses.settings(); + + if (m_proxy.proxy_hostnames + && (m_proxy.type == proxy_settings::socks5 + || m_proxy.type == proxy_settings::socks5_pw)) + { + m_hostname = hostname; + m_target.port(port); + start_announce(); + } + else + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_tracker_connection::name_lookup"); +#endif + tcp::resolver::query q(hostname, to_string(port).elems); + m_ses.m_host_resolver.async_resolve(q + , boost::bind( + &udp_tracker_connection::name_lookup, self(), _1, _2)); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + boost::shared_ptr cb = requester(); + if (cb) cb->debug_log("*** UDP_TRACKER [ initiating name lookup: \"%s\" ]" + , hostname.c_str()); +#endif + } + + set_timeout(tracker_req().event == tracker_request::stopped + ? settings.stop_tracker_timeout + : settings.tracker_completion_timeout + , settings.tracker_receive_timeout); + } + + void udp_tracker_connection::fail(error_code const& ec, int code + , char const* msg, int interval, int min_interval) + { + // m_target failed. remove it from the endpoint list + std::list::iterator i = std::find(m_endpoints.begin() + , m_endpoints.end(), tcp::endpoint(m_target.address(), m_target.port())); + + if (i != m_endpoints.end()) m_endpoints.erase(i); + + // if that was the last one, fail the whole announce + if (m_endpoints.empty()) + { + tracker_connection::fail(ec, code, msg, interval, min_interval); + return; + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + boost::shared_ptr cb = requester(); + if (cb) cb->debug_log("*** UDP_TRACKER [ host: \"%s\" ip: \"%s\" | error: \"%s\" ]" + , m_hostname.c_str(), print_endpoint(m_target).c_str(), ec.message().c_str()); +#endif + + // pick another target endpoint and try again + m_target = pick_target_endpoint(); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + if (cb) cb->debug_log("*** UDP_TRACKER trying next IP [ host: \"%s\" ip: \"%s\" ]" + , m_hostname.c_str(), print_endpoint(m_target).c_str()); +#endif + m_ses.m_io_service.post(boost::bind( + &udp_tracker_connection::start_announce, self())); + + session_settings const& settings = m_ses.settings(); + set_timeout(tracker_req().event == tracker_request::stopped + ? settings.stop_tracker_timeout + : settings.tracker_completion_timeout + , settings.tracker_receive_timeout); + } + + void udp_tracker_connection::name_lookup(error_code const& error + , tcp::resolver::iterator i) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_tracker_connection::name_lookup"); +#endif + if (m_abort) return; + if (error == asio::error::operation_aborted) return; + if (error || i == tcp::resolver::iterator()) + { + fail(error); + return; + } + + boost::shared_ptr cb = requester(); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + if (cb) cb->debug_log("*** UDP_TRACKER [ name lookup successful ]"); +#endif + if (cancelled()) + { + fail(error_code(errors::torrent_aborted)); + return; + } + + restart_read_timeout(); + + // look for an address that has the same kind as the one + // we're listening on. To make sure the tracker get our + // correct listening address. + + std::transform(i, tcp::resolver::iterator(), std::back_inserter(m_endpoints) + , boost::bind(&tcp::resolver::iterator::value_type::endpoint, _1)); + + if (tracker_req().apply_ip_filter) + { + // remove endpoints that are filtered by the IP filter + for (std::list::iterator k = m_endpoints.begin(); + k != m_endpoints.end();) + { + if (m_ses.m_ip_filter.access(k->address()) == ip_filter::blocked) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + if (cb) cb->debug_log("*** UDP_TRACKER [ IP blocked by filter: %s ]" + , print_address(k->address()).c_str()); +#endif + k = m_endpoints.erase(k); + } + else + ++k; + } + } + + // if all endpoints were filtered by the IP filter, we can't connect + if (m_endpoints.empty()) + { + fail(error_code(errors::banned_by_ip_filter)); + return; + } + + m_target = pick_target_endpoint(); + + if (cb) cb->m_tracker_address = tcp::endpoint(m_target.address(), m_target.port()); + + start_announce(); + } + + udp::endpoint udp_tracker_connection::pick_target_endpoint() const + { + std::list::const_iterator iter = m_endpoints.begin(); + udp::endpoint target = udp::endpoint(iter->address(), iter->port()); + + if (bind_interface() != address_v4::any()) + { + // find first endpoint that matches our bind interface type + for (; iter != m_endpoints.end() && iter->address().is_v4() + != bind_interface().is_v4(); ++iter); + + if (iter == m_endpoints.end()) + { + TORRENT_ASSERT(target.address().is_v4() != bind_interface().is_v4()); + boost::shared_ptr cb = requester(); + if (cb) + { + char const* tracker_address_type = target.address().is_v4() ? "IPv4" : "IPv6"; + char const* bind_address_type = bind_interface().is_v4() ? "IPv4" : "IPv6"; + char msg[200]; + snprintf(msg, sizeof(msg) + , "the tracker only resolves to an %s address, and you're " + "listening on an %s socket. This may prevent you from receiving " + "incoming connections." + , tracker_address_type, bind_address_type); + + cb->tracker_warning(tracker_req(), msg); + } + } + else + { + target = udp::endpoint(iter->address(), iter->port()); + } + } + + return target; + } + + void udp_tracker_connection::start_announce() + { + mutex::scoped_lock l(m_cache_mutex); + std::map::iterator cc + = m_connection_cache.find(m_target.address()); + if (cc != m_connection_cache.end()) + { + // we found a cached entry! Now, we can only + // use if if it hasn't expired + if (time_now() < cc->second.expires) + { + if (tracker_req().kind == tracker_request::announce_request) + send_udp_announce(); + else if (tracker_req().kind == tracker_request::scrape_request) + send_udp_scrape(); + return; + } + // if it expired, remove it from the cache + m_connection_cache.erase(cc); + } + l.unlock(); + + send_udp_connect(); + } + + void udp_tracker_connection::on_timeout(error_code const& ec) + { + if (ec) + { + fail(ec); + return; + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + boost::shared_ptr cb = requester(); + if (cb) cb->debug_log("*** UDP_TRACKER [ timed out url: %s ]", tracker_req().url.c_str()); +#endif + fail(error_code(errors::timed_out)); + } + + void udp_tracker_connection::close() + { + error_code ec; + tracker_connection::close(); + } + + bool udp_tracker_connection::on_receive_hostname(error_code const& e + , char const* hostname, char const* buf, int size) + { + // just ignore the hostname this came from, pretend that + // it's from the same endpoint we sent it to (i.e. the same + // port). We have so many other ways of confirming this packet + // comes from the tracker anyway, so it's not a big deal + return on_receive(e, m_target, buf, size); + } + + bool udp_tracker_connection::on_receive(error_code const& e + , udp::endpoint const& ep, char const* buf, int size) + { + // ignore resposes before we've sent any requests + if (m_state == action_error) return false; + + if (m_abort) return false; + + // ignore packet not sent from the tracker + // if m_target is inaddr_any, it suggests that we + // sent the packet through a proxy only knowing + // the hostname, in which case this packet might be for us + if (!is_any(m_target.address()) && m_target != ep) return false; + + if (e) fail(e); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + boost::shared_ptr cb = requester(); + if (cb) + { + cb->debug_log("<== UDP_TRACKER_PACKET [ size: %d ]", size); + } +#endif + + // ignore packets smaller than 8 bytes + if (size < 8) return false; + + const char* ptr = buf; + int action = detail::read_int32(ptr); + int transaction = detail::read_int32(ptr); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + if (cb) + { + cb->debug_log("*** UDP_TRACKER_PACKET [ action: %d ]", action); + } +#endif + + // ignore packets with incorrect transaction id + if (m_transaction_id != transaction) return false; + + if (action == action_error) + { + fail(error_code(errors::tracker_failure), -1, std::string(ptr, size - 8).c_str()); + return true; + } + + // ignore packets that's not a response to our message + if (action != m_state) return false; + + restart_read_timeout(); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + if (cb) + { + cb->debug_log("*** UDP_TRACKER_RESPONSE [ tid: %x ]" + , int(transaction)); + } +#endif + + switch (m_state) + { + case action_connect: + return on_connect_response(buf, size); + case action_announce: + return on_announce_response(buf, size); + case action_scrape: + return on_scrape_response(buf, size); + default: break; + } + return false; + } + + bool udp_tracker_connection::on_connect_response(char const* buf, int size) + { + // ignore packets smaller than 16 bytes + if (size < 16) return false; + + restart_read_timeout(); + buf += 8; // skip header + + // reset transaction + m_transaction_id = 0; + m_attempts = 0; + boost::uint64_t connection_id = detail::read_int64(buf); + + mutex::scoped_lock l(m_cache_mutex); + connection_cache_entry& cce = m_connection_cache[m_target.address()]; + cce.connection_id = connection_id; + cce.expires = time_now() + seconds(m_ses.m_settings.udp_tracker_token_expiry); + + if (tracker_req().kind == tracker_request::announce_request) + send_udp_announce(); + else if (tracker_req().kind == tracker_request::scrape_request) + send_udp_scrape(); + return true; + } + + void udp_tracker_connection::send_udp_connect() + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + boost::shared_ptr cb = requester(); + if (cb) + { + char hex_ih[41]; + to_hex((char const*)&tracker_req().info_hash[0], 20, hex_ih); + cb->debug_log("==> UDP_TRACKER_CONNECT [%s]", hex_ih); + } +#endif + if (m_abort) return; + + char buf[16]; + char* ptr = buf; + + if (m_transaction_id == 0) + m_transaction_id = random() ^ (random() << 16); + + detail::write_uint32(0x417, ptr); + detail::write_uint32(0x27101980, ptr); // connection_id + detail::write_int32(action_connect, ptr); // action (connect) + detail::write_int32(m_transaction_id, ptr); // transaction_id + TORRENT_ASSERT(ptr - buf == sizeof(buf)); + + error_code ec; + if (!m_hostname.empty()) + { + m_ses.m_udp_socket.send_hostname(m_hostname.c_str(), m_target.port(), buf, 16, ec); + } + else + { + m_ses.m_udp_socket.send(m_target, buf, 16, ec); + } + m_state = action_connect; + sent_bytes(16 + 28); // assuming UDP/IP header + ++m_attempts; + if (ec) + { + fail(ec); + return; + } + } + + void udp_tracker_connection::send_udp_scrape() + { + if (m_transaction_id == 0) + m_transaction_id = random() ^ (random() << 16); + + if (m_abort) return; + + std::map::iterator i + = m_connection_cache.find(m_target.address()); + // this isn't really supposed to happen + TORRENT_ASSERT(i != m_connection_cache.end()); + if (i == m_connection_cache.end()) return; + + char buf[8 + 4 + 4 + 20]; + char* out = buf; + + detail::write_int64(i->second.connection_id, out); // connection_id + detail::write_int32(action_scrape, out); // action (scrape) + detail::write_int32(m_transaction_id, out); // transaction_id + // info_hash + std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end(), out); +#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS + out += 20; + TORRENT_ASSERT(out - buf == sizeof(buf)); +#endif + + error_code ec; + if (!m_hostname.empty()) + { + m_ses.m_udp_socket.send_hostname(m_hostname.c_str(), m_target.port(), buf, sizeof(buf), ec); + } + else + { + m_ses.m_udp_socket.send(m_target, buf, sizeof(buf), ec); + } + m_state = action_scrape; + sent_bytes(sizeof(buf) + 28); // assuming UDP/IP header + ++m_attempts; + if (ec) + { + fail(ec); + return; + } + } + + bool udp_tracker_connection::on_announce_response(char const* buf, int size) + { + if (size < 20) return false; + + buf += 8; // skip header + restart_read_timeout(); + int interval = detail::read_int32(buf); + int min_interval = 60; + int incomplete = detail::read_int32(buf); + int complete = detail::read_int32(buf); + int num_peers = (size - 20) / 6; + if ((size - 20) % 6 != 0) + { + fail(error_code(errors::invalid_tracker_response_length)); + return false; + } + + boost::shared_ptr cb = requester(); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + if (cb) + { + boost::shared_ptr cb = requester(); + cb->debug_log("<== UDP_TRACKER_RESPONSE [ url: %s ]", tracker_req().url.c_str()); + } +#endif + + if (!cb) + { + close(); + return true; + } + + std::vector peer_list; + for (int i = 0; i < num_peers; ++i) + { + // TODO: it would be more efficient to not use a string here. + // however, the problem is that some trackers will respond + // with actual strings. For example i2p trackers + peer_entry e; + char ip_string[100]; + unsigned int a = detail::read_uint8(buf); + unsigned int b = detail::read_uint8(buf); + unsigned int c = detail::read_uint8(buf); + unsigned int d = detail::read_uint8(buf); + snprintf(ip_string, 100, "%u.%u.%u.%u", a, b, c, d); + e.ip = ip_string; + e.port = detail::read_uint16(buf); + e.pid.clear(); + peer_list.push_back(e); + } + + std::list
ip_list; + for (std::list::const_iterator i = m_endpoints.begin() + , end(m_endpoints.end()); i != end; ++i) + { + ip_list.push_back(i->address()); + } + + cb->tracker_response(tracker_req(), m_target.address(), ip_list + , peer_list, interval, min_interval, complete, incomplete, 0, address(), "" /*trackerid*/); + + close(); + return true; + } + + bool udp_tracker_connection::on_scrape_response(char const* buf, int size) + { + restart_read_timeout(); + int action = detail::read_int32(buf); + int transaction = detail::read_int32(buf); + + if (transaction != m_transaction_id) + { + fail(error_code(errors::invalid_tracker_transaction_id)); + return false; + } + + if (action == action_error) + { + fail(error_code(errors::tracker_failure), -1, std::string(buf, size - 8).c_str()); + return true; + } + + if (action != action_scrape) + { + fail(error_code(errors::invalid_tracker_action)); + return true; + } + + if (size < 20) + { + fail(error_code(errors::invalid_tracker_response_length)); + return true; + } + + int complete = detail::read_int32(buf); + int downloaded = detail::read_int32(buf); + int incomplete = detail::read_int32(buf); + + boost::shared_ptr cb = requester(); + if (!cb) + { + close(); + return true; + } + + cb->tracker_scrape_response(tracker_req() + , complete, incomplete, downloaded, -1); + + close(); + return true; + } + + void udp_tracker_connection::send_udp_announce() + { + if (m_transaction_id == 0) + m_transaction_id = random() ^ (random() << 16); + + if (m_abort) return; + + char buf[800]; + char* out = buf; + + tracker_request const& req = tracker_req(); + const bool stats = req.send_stats; + session_settings const& settings = m_ses.settings(); + + std::map::iterator i + = m_connection_cache.find(m_target.address()); + // this isn't really supposed to happen + TORRENT_ASSERT(i != m_connection_cache.end()); + if (i == m_connection_cache.end()) return; + + detail::write_int64(i->second.connection_id, out); // connection_id + detail::write_int32(action_announce, out); // action (announce) + detail::write_int32(m_transaction_id, out); // transaction_id + std::copy(req.info_hash.begin(), req.info_hash.end(), out); // info_hash + out += 20; + std::copy(req.pid.begin(), req.pid.end(), out); // peer_id + out += 20; + detail::write_int64(stats ? req.downloaded : 0, out); // downloaded + detail::write_int64(stats ? req.left : 0, out); // left + detail::write_int64(stats ? req.uploaded : 0, out); // uploaded + detail::write_int32(req.event, out); // event + // ip address + address_v4 announce_ip; + + if (!m_ses.settings().anonymous_mode + && !settings.announce_ip.empty()) + { + error_code ec; + address ip = address::from_string(settings.announce_ip.c_str(), ec); + if (!ec && ip.is_v4()) announce_ip = ip.to_v4(); + } + detail::write_uint32(announce_ip.to_ulong(), out); + detail::write_int32(req.key, out); // key + detail::write_int32(req.num_want, out); // num_want + detail::write_uint16(req.listen_port, out); // port + + std::string request_string; + error_code ec; + using boost::tuples::ignore; + boost::tie(ignore, ignore, ignore, ignore, request_string) = parse_url_components(req.url, ec); + if (ec) request_string.clear(); + + if (!request_string.empty()) + { + int str_len = (std::min)(int(request_string.size()), 255); + request_string.resize(str_len); + + detail::write_uint8(2, out); + detail::write_uint8(str_len, out); + detail::write_string(request_string, out); + } + + TORRENT_ASSERT(out - buf <= int(sizeof(buf))); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + boost::shared_ptr cb = requester(); + if (cb) + { + char hex_ih[41]; + to_hex((char const*)&req.info_hash[0], 20, hex_ih); + cb->debug_log("==> UDP_TRACKER_ANNOUNCE [%s]", hex_ih); + } +#endif + + if (!m_hostname.empty()) + { + m_ses.m_udp_socket.send_hostname(m_hostname.c_str(), m_target.port(), buf, out - buf, ec); + } + else + { + m_ses.m_udp_socket.send(m_target, buf, out - buf, ec); + } + m_state = action_announce; + sent_bytes(out - buf + 28); // assuming UDP/IP header + ++m_attempts; + if (ec) + { + fail(ec); + return; + } + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/upnp.cpp b/apps/Launcher/ext/libtorrent/src/upnp.cpp new file mode 100644 index 0000000000..f3ef5b5747 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/upnp.cpp @@ -0,0 +1,1603 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/socket.hpp" +#include "libtorrent/socket_io.hpp" +#include "libtorrent/upnp.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/parse_url.hpp" +#include "libtorrent/xml_parse.hpp" +#include "libtorrent/connection_queue.hpp" +#include "libtorrent/enum_net.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/random.hpp" + +#if defined TORRENT_ASIO_DEBUGGING +#include "libtorrent/debug.hpp" +#endif + +#include +#include +#if BOOST_VERSION < 103500 +#include +#include +#else +#include +#include +#endif +#include + +namespace libtorrent { + +namespace upnp_errors +{ + boost::system::error_code make_error_code(error_code_enum e) + { + return error_code(e, get_upnp_category()); + } + +} // upnp_errors namespace + +static error_code ec; + +// TODO: listen_interface is not used. It's meant to bind the broadcast socket +upnp::upnp(io_service& ios, connection_queue& cc + , address const& listen_interface, std::string const& user_agent + , portmap_callback_t const& cb, log_callback_t const& lcb + , bool ignore_nonrouters, void* state) + : m_user_agent(user_agent) + , m_callback(cb) + , m_log_callback(lcb) + , m_retry_count(0) + , m_io_service(ios) + , m_socket(udp::endpoint(address_v4::from_string("239.255.255.250", ec), 1900) + , boost::bind(&upnp::on_reply, self(), _1, _2, _3)) + , m_broadcast_timer(ios) + , m_refresh_timer(ios) + , m_map_timer(ios) + , m_disabled(false) + , m_closing(false) + , m_ignore_non_routers(ignore_nonrouters) + , m_cc(cc) +{ + TORRENT_ASSERT(cb); + + error_code ec; + m_socket.open(ios, ec); + + if (state) + { + upnp_state_t* s = (upnp_state_t*)state; + m_devices.swap(s->devices); + m_mappings.swap(s->mappings); + delete s; + } + + m_mappings.reserve(10); +} + +void* upnp::drain_state() +{ + upnp_state_t* s = new upnp_state_t; + s->mappings.swap(m_mappings); + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end; ++i) + i->upnp_connection.reset(); + s->devices.swap(m_devices); + return s; +} + +upnp::~upnp() +{ +} + +void upnp::discover_device() +{ + mutex::scoped_lock l(m_mutex); + if (m_socket.num_send_sockets() == 0) + log("No network interfaces to broadcast to", l); + + discover_device_impl(l); +} + +void upnp::log(char const* msg, mutex::scoped_lock& l) +{ + l.unlock(); + m_log_callback(msg); + l.lock(); +} + +void upnp::discover_device_impl(mutex::scoped_lock& l) +{ + const char msearch[] = + "M-SEARCH * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "ST:upnp:rootdevice\r\n" + "MAN:\"ssdp:discover\"\r\n" + "MX:3\r\n" + "\r\n\r\n"; + + error_code ec; +#ifdef TORRENT_DEBUG_UPNP + // simulate packet loss + if (m_retry_count & 1) +#endif + m_socket.send(msearch, sizeof(msearch) - 1, ec); + + if (ec) + { + char msg[500]; + snprintf(msg, sizeof(msg), "broadcast failed: %s. Aborting." + , convert_from_native(ec.message()).c_str()); + log(msg, l); + disable(ec, l); + return; + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("upnp::resend_request"); +#endif + ++m_retry_count; + m_broadcast_timer.expires_from_now(seconds(2 * m_retry_count), ec); + m_broadcast_timer.async_wait(boost::bind(&upnp::resend_request + , self(), _1)); + + log("broadcasting search for rootdevice", l); +} + +// returns a reference to a mapping or -1 on failure +int upnp::add_mapping(upnp::protocol_type p, int external_port, int local_port) +{ + // external port 0 means _every_ port + TORRENT_ASSERT(external_port != 0); + + mutex::scoped_lock l(m_mutex); + + char msg[500]; + snprintf(msg, sizeof(msg), "adding port map: [ protocol: %s ext_port: %u " + "local_port: %u ] %s", (p == tcp?"tcp":"udp"), external_port + , local_port, m_disabled ? "DISABLED": ""); + log(msg, l); + if (m_disabled) return -1; + + std::vector::iterator i = std::find_if( + m_mappings.begin(), m_mappings.end() + , boost::bind(&global_mapping_t::protocol, _1) == int(none)); + + if (i == m_mappings.end()) + { + m_mappings.push_back(global_mapping_t()); + i = m_mappings.end() - 1; + } + + i->protocol = p; + i->external_port = external_port; + i->local_port = local_port; + + int mapping_index = i - m_mappings.begin(); + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end; ++i) + { + rootdevice& d = const_cast(*i); + TORRENT_ASSERT(d.magic == 1337); + + if (int(d.mapping.size()) <= mapping_index) + d.mapping.resize(mapping_index + 1); + mapping_t& m = d.mapping[mapping_index]; + + m.action = mapping_t::action_add; + m.protocol = p; + m.external_port = external_port; + m.local_port = local_port; + + if (d.service_namespace) update_map(d, mapping_index, l); + } + + return mapping_index; +} + +void upnp::delete_mapping(int mapping) +{ + mutex::scoped_lock l(m_mutex); + + if (mapping >= int(m_mappings.size())) return; + + global_mapping_t& m = m_mappings[mapping]; + + char msg[500]; + snprintf(msg, sizeof(msg), "deleting port map: [ protocol: %s ext_port: %u " + "local_port: %u ]", (m.protocol == tcp?"tcp":"udp"), m.external_port + , m.local_port); + log(msg, l); + + if (m.protocol == none) return; + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end; ++i) + { + rootdevice& d = const_cast(*i); + TORRENT_ASSERT(d.magic == 1337); + + TORRENT_ASSERT(mapping < int(d.mapping.size())); + d.mapping[mapping].action = mapping_t::action_delete; + + if (d.service_namespace) update_map(d, mapping, l); + } +} + +bool upnp::get_mapping(int index, int& local_port, int& external_port, int& protocol) const +{ + TORRENT_ASSERT(index < int(m_mappings.size()) && index >= 0); + if (index >= int(m_mappings.size()) || index < 0) return false; + global_mapping_t const& m = m_mappings[index]; + if (m.protocol == none) return false; + local_port = m.local_port; + external_port = m.external_port; + protocol = m.protocol; + return true; +} + +void upnp::resend_request(error_code const& ec) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("upnp::resend_request"); +#endif + if (ec) return; + + boost::intrusive_ptr me(self()); + + mutex::scoped_lock l(m_mutex); + + if (m_closing) return; + + if (m_retry_count < 12 + && (m_devices.empty() || m_retry_count < 4)) + { + discover_device_impl(l); + return; + } + + if (m_devices.empty()) + { + disable(errors::no_router, l); + return; + } + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end; ++i) + { + if (i->control_url.empty() && !i->upnp_connection && !i->disabled) + { + // we don't have a WANIP or WANPPP url for this device, + // ask for it + rootdevice& d = const_cast(*i); + TORRENT_ASSERT(d.magic == 1337); + TORRENT_TRY + { + char msg[500]; + snprintf(msg, sizeof(msg), "connecting to: %s", d.url.c_str()); + log(msg, l); + if (d.upnp_connection) d.upnp_connection->close(); + d.upnp_connection.reset(new http_connection(m_io_service + , m_cc, boost::bind(&upnp::on_upnp_xml, self(), _1, _2 + , boost::ref(d), _5))); + d.upnp_connection->get(d.url, seconds(30), 1); + } + TORRENT_CATCH (std::exception& exc) + { + TORRENT_DECLARE_DUMMY(std::exception, exc); + char msg[500]; + snprintf(msg, sizeof(msg), "connection failed to: %s %s", d.url.c_str(), exc.what()); + log(msg, l); + d.disabled = true; + } + } + } +} + +void upnp::on_reply(udp::endpoint const& from, char* buffer + , std::size_t bytes_transferred) +{ + boost::intrusive_ptr me(self()); + + mutex::scoped_lock l(m_mutex); + + using namespace libtorrent::detail; + + // parse out the url for the device + +/* + the response looks like this: + + HTTP/1.1 200 OK + ST:upnp:rootdevice + USN:uuid:000f-66d6-7296000099dc::upnp:rootdevice + Location: http://192.168.1.1:5431/dyndev/uuid:000f-66d6-7296000099dc + Server: Custom/1.0 UPnP/1.0 Proc/Ver + EXT: + Cache-Control:max-age=180 + DATE: Fri, 02 Jan 1970 08:10:38 GMT + + a notification looks like this: + + NOTIFY * HTTP/1.1 + Host:239.255.255.250:1900 + NT:urn:schemas-upnp-org:device:MediaServer:1 + NTS:ssdp:alive + Location:http://10.0.3.169:2869/upnphost/udhisapi.dll?content=uuid:c17f0c32-d19b-4938-ae94-65f945c3a26e + USN:uuid:c17f0c32-d19b-4938-ae94-65f945c3a26e::urn:schemas-upnp-org:device:MediaServer:1 + Cache-Control:max-age=900 + Server:Microsoft-Windows-NT/5.1 UPnP/1.0 UPnP-Device-Host/1.0 + +*/ + error_code ec; + if (!in_local_network(m_io_service, from.address(), ec)) + { + if (ec) + { + char msg[500]; + snprintf(msg, sizeof(msg), "when receiving response from: %s: %s" + , print_endpoint(from).c_str(), convert_from_native(ec.message()).c_str()); + log(msg, l); + } + else + { + char msg[400]; + int num_chars = snprintf(msg, sizeof(msg) + , "ignoring response from: %s. IP is not on local network. " + , print_endpoint(from).c_str()); + + std::vector net = enum_net_interfaces(m_io_service, ec); + for (std::vector::const_iterator i = net.begin() + , end(net.end()); i != end && num_chars < int(sizeof(msg)); ++i) + { + num_chars += snprintf(msg + num_chars, sizeof(msg) - num_chars, "(%s,%s) " + , print_address(i->interface_address).c_str(), print_address(i->netmask).c_str()); + } + log(msg, l); + return; + } + } + + bool non_router = false; + if (m_ignore_non_routers) + { + std::vector routes = enum_routes(m_io_service, ec); + if (std::find_if(routes.begin(), routes.end() + , boost::bind(&ip_route::gateway, _1) == from.address()) == routes.end()) + { + // this upnp device is filtered because it's not in the + // list of configured routers + if (ec) + { + char msg[500]; + snprintf(msg, sizeof(msg), "failed to enumerate routes when " + "receiving response from: %s: %s" + , print_endpoint(from).c_str(), convert_from_native(ec.message()).c_str()); + log(msg, l); + } + else + { + char msg[400]; + int num_chars = snprintf(msg, sizeof(msg), "SSDP response from: " + "%s: IP is not a router. " + , print_endpoint(from).c_str()); + for (std::vector::const_iterator i = routes.begin() + , end(routes.end()); i != end && num_chars < int(sizeof(msg)); ++i) + { + num_chars += snprintf(msg + num_chars, sizeof(msg) - num_chars, "(%s,%s) " + , print_address(i->gateway).c_str(), print_address(i->netmask).c_str()); + } + log(msg, l); + non_router = true; + } + } + } + + http_parser p; + bool error = false; + p.incoming(buffer::const_interval(buffer + , buffer + bytes_transferred), error); + if (error) + { + char msg[500]; + snprintf(msg, sizeof(msg), "received malformed HTTP from: %s" + , print_endpoint(from).c_str()); + log(msg, l); + return; + } + + if (p.status_code() != 200 && p.method() != "notify") + { + if (p.method().empty()) + { + char msg[500]; + snprintf(msg, sizeof(msg), "HTTP status %u from %s" + , p.status_code(), print_endpoint(from).c_str()); + log(msg, l); + } + else + { + char msg[500]; + snprintf(msg, sizeof(msg), "HTTP method %s from %s" + , p.method().c_str(), print_endpoint(from).c_str()); + log(msg, l); + } + return; + } + + if (!p.header_finished()) + { + char msg[500]; + snprintf(msg, sizeof(msg), "incomplete HTTP packet from %s" + , print_endpoint(from).c_str()); + log(msg, l); + return; + } + + std::string url = p.header("location"); + if (url.empty()) + { + char msg[500]; + snprintf(msg, sizeof(msg), "missing location header from %s" + , print_endpoint(from).c_str()); + log(msg, l); + return; + } + + rootdevice d; + d.url = url; + + std::set::iterator i = m_devices.find(d); + + if (i == m_devices.end()) + { + std::string protocol; + std::string auth; + error_code ec; + // we don't have this device in our list. Add it + boost::tie(protocol, auth, d.hostname, d.port, d.path) + = parse_url_components(d.url, ec); + if (d.port == -1) d.port = protocol == "http" ? 80 : 443; + + if (ec) + { + char msg[500]; + snprintf(msg, sizeof(msg), "invalid URL %s from %s: %s" + , d.url.c_str(), print_endpoint(from).c_str(), convert_from_native(ec.message()).c_str()); + log(msg, l); + return; + } + + // ignore the auth here. It will be re-parsed + // by the http connection later + + if (protocol != "http") + { + char msg[500]; + snprintf(msg, sizeof(msg), "unsupported protocol %s from %s" + , protocol.c_str(), print_endpoint(from).c_str()); + log(msg, l); + return; + } + + if (d.port == 0) + { + char msg[500]; + snprintf(msg, sizeof(msg), "URL with port 0 from %s" + , print_endpoint(from).c_str()); + log(msg, l); + return; + } + + char msg[500]; + snprintf(msg, sizeof(msg), "found rootdevice: %s (%d)" + , d.url.c_str(), int(m_devices.size())); + log(msg, l); + + if (m_devices.size() >= 50) + { + char msg[500]; + snprintf(msg, sizeof(msg), "too many rootdevices: (%d). Ignoring %s" + , int(m_devices.size()), d.url.c_str()); + log(msg, l); + return; + } + d.non_router = non_router; + + TORRENT_ASSERT(d.mapping.empty()); + for (std::vector::iterator j = m_mappings.begin() + , end(m_mappings.end()); j != end; ++j) + { + mapping_t m; + m.action = mapping_t::action_add; + m.local_port = j->local_port; + m.external_port = j->external_port; + m.protocol = j->protocol; + d.mapping.push_back(m); + } + boost::tie(i, boost::tuples::ignore) = m_devices.insert(d); + } + + + // iterate over the devices we know and connect and issue the mappings + try_map_upnp(l); + + if (m_ignore_non_routers) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("upnp::map_timer"); +#endif + // check back in in a little bit to see if we have seen any + // devices at one of our default routes. If not, we want to override + // ignoring them and use them instead (better than not working). + m_map_timer.expires_from_now(seconds(1), ec); + m_map_timer.async_wait(boost::bind(&upnp::map_timer + , self(), _1)); + } +} + +void upnp::map_timer(error_code const& ec) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("upnp::map_timer"); +#endif + if (ec) return; + if (m_closing) return; + + mutex::scoped_lock l(m_mutex); + try_map_upnp(l, true); +} + +void upnp::try_map_upnp(mutex::scoped_lock& l, bool timer) +{ + if (m_devices.empty()) return; + + bool override_ignore_non_routers = false; + if (m_ignore_non_routers && timer) + { + // if we don't ave any devices that match our default route, we + // should try to map with the ones we did hear from anyway, + // regardless of if they are not running at our gateway. + override_ignore_non_routers = std::find_if(m_devices.begin() + , m_devices.end(), boost::bind(&rootdevice::non_router, _1) == false) + == m_devices.end(); + if (override_ignore_non_routers) + { + char msg[500]; + snprintf(msg, sizeof(msg), "overriding ignore non-routers"); + log(msg, l); + } + } + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end; ++i) + { + // if we're ignoring non-routers, skip them. If on_timer is + // set, we expect to have received all responses and if we don't + // have any devices at our default route, then issue requests + // to any device we found. + if (m_ignore_non_routers && i->non_router + && !override_ignore_non_routers) + continue; + + if (i->control_url.empty() && !i->upnp_connection && !i->disabled) + { + // we don't have a WANIP or WANPPP url for this device, + // ask for it + rootdevice& d = const_cast(*i); + TORRENT_ASSERT(d.magic == 1337); + TORRENT_TRY + { + char msg[500]; + snprintf(msg, sizeof(msg), "connecting to: %s" + , d.url.c_str()); + log(msg, l); + + if (d.upnp_connection) d.upnp_connection->close(); + d.upnp_connection.reset(new http_connection(m_io_service + , m_cc, boost::bind(&upnp::on_upnp_xml, self(), _1, _2 + , boost::ref(d), _5))); + d.upnp_connection->get(d.url, seconds(30), 1); + } + TORRENT_CATCH (std::exception& exc) + { + TORRENT_DECLARE_DUMMY(std::exception, exc); + char msg[500]; + snprintf(msg, sizeof(msg), "connection failed to: %s %s" + , d.url.c_str(), exc.what()); + log(msg, l); + d.disabled = true; + } + } + } +} + +void upnp::post(upnp::rootdevice const& d, char const* soap + , char const* soap_action, mutex::scoped_lock& l) +{ + TORRENT_ASSERT(d.magic == 1337); + TORRENT_ASSERT(d.upnp_connection); + + char header[2048]; + snprintf(header, sizeof(header), "POST %s HTTP/1.1\r\n" + "Host: %s:%u\r\n" + "Content-Type: text/xml; charset=\"utf-8\"\r\n" + "Content-Length: %d\r\n" + "Soapaction: \"%s#%s\"\r\n\r\n" + "%s" + , d.path.c_str(), d.hostname.c_str(), d.port + , int(strlen(soap)), d.service_namespace, soap_action + , soap); + + d.upnp_connection->sendbuffer = header; + + char msg[1024]; + snprintf(msg, sizeof(msg), "sending: %s", header); + log(msg, l); +} + +void upnp::create_port_mapping(http_connection& c, rootdevice& d, int i) +{ + mutex::scoped_lock l(m_mutex); + + TORRENT_ASSERT(d.magic == 1337); + + if (!d.upnp_connection) + { + TORRENT_ASSERT(d.disabled); + char msg[500]; + snprintf(msg, sizeof(msg), "mapping %u aborted", i); + log(msg, l); + return; + } + + char const* soap_action = "AddPortMapping"; + + std::string local_endpoint = print_address(c.socket().local_endpoint(ec).address()); + + char soap[2048]; + error_code ec; + snprintf(soap, sizeof(soap), "\n" + "" + "" + "" + "%u" + "%s" + "%u" + "%s" + "1" + "%s at %s:%d" + "%u" + "" + , soap_action, d.service_namespace, d.mapping[i].external_port + , (d.mapping[i].protocol == udp ? "UDP" : "TCP") + , d.mapping[i].local_port + , local_endpoint.c_str() + , m_user_agent.c_str(), local_endpoint.c_str(), d.mapping[i].local_port + , d.lease_duration, soap_action); + + post(d, soap, soap_action, l); +} + +void upnp::next(rootdevice& d, int i, mutex::scoped_lock& l) +{ + if (i < num_mappings() - 1) + { + update_map(d, i + 1, l); + } + else + { + std::vector::iterator j + = std::find_if(d.mapping.begin(), d.mapping.end() + , boost::bind(&mapping_t::action, _1) != int(mapping_t::action_none)); + if (j == d.mapping.end()) return; + + update_map(d, j - d.mapping.begin(), l); + } +} + +void upnp::update_map(rootdevice& d, int i, mutex::scoped_lock& l) +{ + TORRENT_ASSERT(d.magic == 1337); + TORRENT_ASSERT(i < int(d.mapping.size())); + TORRENT_ASSERT(d.mapping.size() == m_mappings.size()); + + if (d.upnp_connection) return; + + boost::intrusive_ptr me(self()); + + mapping_t& m = d.mapping[i]; + + if (m.action == mapping_t::action_none + || m.protocol == none) + { + char msg[500]; + snprintf(msg, sizeof(msg), "mapping %u does not need updating, skipping", i); + log(msg, l); + m.action = mapping_t::action_none; + next(d, i, l); + return; + } + + TORRENT_ASSERT(!d.upnp_connection); + TORRENT_ASSERT(d.service_namespace); + + char msg[500]; + snprintf(msg, sizeof(msg), "connecting to %s", d.hostname.c_str()); + log(msg, l); + if (m.action == mapping_t::action_add) + { + if (m.failcount > 5) + { + m.action = mapping_t::action_none; + // giving up + next(d, i, l); + return; + } + + if (d.upnp_connection) d.upnp_connection->close(); + d.upnp_connection.reset(new http_connection(m_io_service + , m_cc, boost::bind(&upnp::on_upnp_map_response, self(), _1, _2 + , boost::ref(d), i, _5), true, default_max_bottled_buffer_size + , boost::bind(&upnp::create_port_mapping, self(), _1, boost::ref(d), i))); + + d.upnp_connection->start(d.hostname, to_string(d.port).elems + , seconds(10), 1); + } + else if (m.action == mapping_t::action_delete) + { + if (d.upnp_connection) d.upnp_connection->close(); + d.upnp_connection.reset(new http_connection(m_io_service + , m_cc, boost::bind(&upnp::on_upnp_unmap_response, self(), _1, _2 + , boost::ref(d), i, _5), true, default_max_bottled_buffer_size + , boost::bind(&upnp::delete_port_mapping, self(), boost::ref(d), i))); + d.upnp_connection->start(d.hostname, to_string(d.port).elems + , seconds(10), 1); + } + + m.action = mapping_t::action_none; +} + +void upnp::delete_port_mapping(rootdevice& d, int i) +{ + mutex::scoped_lock l(m_mutex); + + TORRENT_ASSERT(d.magic == 1337); + + if (!d.upnp_connection) + { + TORRENT_ASSERT(d.disabled); + char msg[500]; + snprintf(msg, sizeof(msg), "unmapping %u aborted", i); + log(msg, l); + return; + } + + char const* soap_action = "DeletePortMapping"; + + char soap[2048]; + error_code ec; + snprintf(soap, sizeof(soap), "\n" + "" + "" + "" + "%u" + "%s" + "" + , soap_action, d.service_namespace + , d.mapping[i].external_port + , (d.mapping[i].protocol == udp ? "UDP" : "TCP") + , soap_action); + + post(d, soap, soap_action, l); +} + +namespace +{ + void copy_tolower(std::string& dst, char const* src) + { + dst.clear(); + while (*src) dst.push_back(to_lower(*src++)); + } +} + +struct parse_state +{ + parse_state(): in_service(false), service_type(0) {} + void reset(char const* st) + { + in_service = false; + service_type = st; + tag_stack.clear(); + control_url.clear(); + model.clear(); + url_base.clear(); + } + bool in_service; + std::list tag_stack; + std::string control_url; + char const* service_type; + std::string model; + std::string url_base; + bool top_tags(const char* str1, const char* str2) + { + std::list::reverse_iterator i = tag_stack.rbegin(); + if (i == tag_stack.rend()) return false; + if (!string_equal_no_case(i->c_str(), str2)) return false; + ++i; + if (i == tag_stack.rend()) return false; + if (!string_equal_no_case(i->c_str(), str1)) return false; + return true; + } +}; + +TORRENT_EXTRA_EXPORT void find_control_url(int type, char const* string, parse_state& state) +{ + if (type == xml_start_tag) + { + std::string tag; + copy_tolower(tag, string); + state.tag_stack.push_back(tag); +// std::copy(state.tag_stack.begin(), state.tag_stack.end(), std::ostream_iterator(std::cout, " ")); +// std::cout << std::endl; + } + else if (type == xml_end_tag) + { + if (!state.tag_stack.empty()) + { + if (state.in_service && state.tag_stack.back() == "service") + state.in_service = false; + state.tag_stack.pop_back(); + } + } + else if (type == xml_string) + { + if (state.tag_stack.empty()) return; +// std::cout << " " << string << std::endl; + if (!state.in_service && state.top_tags("service", "servicetype")) + { + if (string_equal_no_case(string, state.service_type)) + state.in_service = true; + } + else if (state.control_url.empty() && state.in_service && state.top_tags("service", "controlurl")) + { + // default to the first (or only) control url in the router's listing + state.control_url = string; + } + else if (state.model.empty() && state.top_tags("device", "modelname")) + { + state.model = string; + } + else if (state.tag_stack.back() == "urlbase") + { + state.url_base = string; + } + } +} + +void upnp::on_upnp_xml(error_code const& e + , libtorrent::http_parser const& p, rootdevice& d + , http_connection& c) +{ + boost::intrusive_ptr me(self()); + + mutex::scoped_lock l(m_mutex); + + TORRENT_ASSERT(d.magic == 1337); + if (d.upnp_connection && d.upnp_connection.get() == &c) + { + d.upnp_connection->close(); + d.upnp_connection.reset(); + } + + if (e && e != asio::error::eof) + { + char msg[500]; + snprintf(msg, sizeof(msg), "error while fetching control url from: %s: %s" + , d.url.c_str(), convert_from_native(e.message()).c_str()); + log(msg, l); + d.disabled = true; + return; + } + + if (!p.header_finished()) + { + char msg[500]; + snprintf(msg, sizeof(msg), "error while fetching control url from: %s: incomplete HTTP message" + , d.url.c_str()); + log(msg, l); + d.disabled = true; + return; + } + + if (p.status_code() != 200) + { + char msg[500]; + snprintf(msg, sizeof(msg), "error while fetching control url from: %s: %s" + , d.url.c_str(), convert_from_native(p.message()).c_str()); + log(msg, l); + d.disabled = true; + return; + } + + parse_state s; + s.reset("urn:schemas-upnp-org:service:WANIPConnection:1"); + xml_parse((char*)p.get_body().begin, (char*)p.get_body().end + , boost::bind(&find_control_url, _1, _2, boost::ref(s))); + if (!s.control_url.empty()) + { + d.service_namespace = s.service_type; + if (!s.model.empty()) m_model = s.model; + } + else + { + // we didn't find the WAN IP connection, look for + // a PPP connection + s.reset("urn:schemas-upnp-org:service:WANPPPConnection:1"); + xml_parse((char*)p.get_body().begin, (char*)p.get_body().end + , boost::bind(&find_control_url, _1, _2, boost::ref(s))); + if (!s.control_url.empty()) + { + d.service_namespace = s.service_type; + if (!s.model.empty()) m_model = s.model; + } + else + { + char msg[500]; + snprintf(msg, sizeof(msg), "could not find a port mapping interface in response from: %s" + , d.url.c_str()); + log(msg, l); + d.disabled = true; + return; + } + } + + if (!s.url_base.empty() && s.control_url.substr(0, 7) != "http://") + { + // avoid double slashes in path + if (s.url_base[s.url_base.size()-1] == '/' + && !s.control_url.empty() + && s.control_url[0] == '/') + s.url_base.erase(s.url_base.end()-1); + d.control_url = s.url_base + s.control_url; + } + else d.control_url = s.control_url; + + std::string protocol; + std::string auth; + error_code ec; + if (!d.control_url.empty() && d.control_url[0] == '/') + { + boost::tie(protocol, auth, d.hostname, d.port, d.path) + = parse_url_components(d.url, ec); + if (d.port == -1) d.port = protocol == "http" ? 80 : 443; + d.control_url = protocol + "://" + d.hostname + ":" + + to_string(d.port).elems + s.control_url; + } + + char msg[500]; + snprintf(msg, sizeof(msg), "found control URL: %s namespace %s " + "urlbase: %s in response from %s" + , d.control_url.c_str(), d.service_namespace + , s.url_base.c_str(), d.url.c_str()); + log(msg, l); + + boost::tie(protocol, auth, d.hostname, d.port, d.path) + = parse_url_components(d.control_url, ec); + if (d.port == -1) d.port = protocol == "http" ? 80 : 443; + + if (ec) + { + char msg[500]; + snprintf(msg, sizeof(msg), "failed to parse URL '%s': %s" + , d.control_url.c_str(), convert_from_native(ec.message()).c_str()); + log(msg, l); + d.disabled = true; + return; + } + + d.upnp_connection.reset(new http_connection(m_io_service + , m_cc, boost::bind(&upnp::on_upnp_get_ip_address_response, self(), _1, _2 + , boost::ref(d), _5), true, default_max_bottled_buffer_size + , boost::bind(&upnp::get_ip_address, self(), boost::ref(d)))); + d.upnp_connection->start(d.hostname, to_string(d.port).elems + , seconds(10), 1); +} + +void upnp::get_ip_address(rootdevice& d) +{ + mutex::scoped_lock l(m_mutex); + + TORRENT_ASSERT(d.magic == 1337); + + if (!d.upnp_connection) + { + TORRENT_ASSERT(d.disabled); + char msg[500]; + snprintf(msg, sizeof(msg), "getting external IP address"); + log(msg, l); + return; + } + + char const* soap_action = "GetExternalIPAddress"; + + char soap[2048]; + error_code ec; + snprintf(soap, sizeof(soap), "\n" + "" + "" + "" + , soap_action, d.service_namespace + , soap_action); + + post(d, soap, soap_action, l); +} + +void upnp::disable(error_code const& ec, mutex::scoped_lock& l) +{ + m_disabled = true; + + // kill all mappings + for (std::vector::iterator i = m_mappings.begin() + , end(m_mappings.end()); i != end; ++i) + { + if (i->protocol == none) continue; + i->protocol = none; + l.unlock(); + m_callback(i - m_mappings.begin(), address(), 0, ec); + l.lock(); + } + + // we cannot clear the devices since there + // might be outstanding requests relying on + // the device entry being present when they + // complete + error_code e; + m_broadcast_timer.cancel(e); + m_refresh_timer.cancel(e); + m_map_timer.cancel(e); + m_socket.close(); +} + +namespace +{ + struct error_code_parse_state + { + error_code_parse_state(): in_error_code(false), exit(false), error_code(-1) {} + bool in_error_code; + bool exit; + int error_code; + }; + + void find_error_code(int type, char const* string, error_code_parse_state& state) + { + if (state.exit) return; + if (type == xml_start_tag && !std::strcmp("errorCode", string)) + { + state.in_error_code = true; + } + else if (type == xml_string && state.in_error_code) + { + state.error_code = std::atoi(string); + state.exit = true; + } + } + + struct ip_address_parse_state: public error_code_parse_state + { + ip_address_parse_state(): in_ip_address(false) {} + bool in_ip_address; + std::string ip_address; + }; + + void find_ip_address(int type, char const* string, ip_address_parse_state& state) + { + find_error_code(type, string, state); + if (state.exit) return; + + if (type == xml_start_tag && !std::strcmp("NewExternalIPAddress", string)) + { + state.in_ip_address = true; + } + else if (type == xml_string && state.in_ip_address) + { + state.ip_address = string; + state.exit = true; + } + } + + struct error_code_t + { + int code; + char const* msg; + }; + + error_code_t error_codes[] = + { + {0, "no error"} + , {402, "Invalid Arguments"} + , {501, "Action Failed"} + , {714, "The specified value does not exist in the array"} + , {715, "The source IP address cannot be wild-carded"} + , {716, "The external port cannot be wild-carded"} + , {718, "The port mapping entry specified conflicts with " + "a mapping assigned previously to another client"} + , {724, "Internal and External port values must be the same"} + , {725, "The NAT implementation only supports permanent " + "lease times on port mappings"} + , {726, "RemoteHost must be a wildcard and cannot be a " + "specific IP address or DNS name"} + , {727, "ExternalPort must be a wildcard and cannot be a specific port "} + }; + +} + +#if BOOST_VERSION >= 103500 + +struct upnp_error_category : boost::system::error_category +{ + virtual const char* name() const BOOST_SYSTEM_NOEXCEPT + { + return "UPnP error"; + } + + virtual std::string message(int ev) const BOOST_SYSTEM_NOEXCEPT + { + int num_errors = sizeof(error_codes) / sizeof(error_codes[0]); + error_code_t* end = error_codes + num_errors; + error_code_t tmp = {ev, 0}; + error_code_t* e = std::lower_bound(error_codes, end, tmp + , boost::bind(&error_code_t::code, _1) < boost::bind(&error_code_t::code, _2)); + if (e != end && e->code == ev) + { + return e->msg; + } + char msg[500]; + snprintf(msg, sizeof(msg), "unknown UPnP error (%d)", ev); + return msg; + } + + virtual boost::system::error_condition default_error_condition( + int ev) const BOOST_SYSTEM_NOEXCEPT + { + return boost::system::error_condition(ev, *this); + } +}; + +boost::system::error_category& get_upnp_category() +{ + static upnp_error_category cat; + return cat; +} + +#else + +boost::system::error_category& get_upnp_category() +{ + static ::asio::error::error_category cat(21); + return cat; +} + +#endif + +void upnp::on_upnp_get_ip_address_response(error_code const& e + , libtorrent::http_parser const& p, rootdevice& d + , http_connection& c) +{ + boost::intrusive_ptr me(self()); + + mutex::scoped_lock l(m_mutex); + + TORRENT_ASSERT(d.magic == 1337); + if (d.upnp_connection && d.upnp_connection.get() == &c) + { + d.upnp_connection->close(); + d.upnp_connection.reset(); + } + + if (m_closing) return; + + if (e && e != asio::error::eof) + { + char msg[500]; + snprintf(msg, sizeof(msg), "error while getting external IP address: %s" + , convert_from_native(e.message()).c_str()); + log(msg, l); + if (num_mappings() > 0) update_map(d, 0, l); + return; + } + + if (!p.header_finished()) + { + log("error while getting external IP address: incomplete http message", l); + if (num_mappings() > 0) update_map(d, 0, l); + return; + } + + if (p.status_code() != 200) + { + char msg[500]; + snprintf(msg, sizeof(msg), "error while getting external IP address: %s" + , convert_from_native(p.message()).c_str()); + log(msg, l); + if (num_mappings() > 0) update_map(d, 0, l); + return; + } + + // response may look like + // + // + // + // 192.168.160.19 + // + // + // + + char msg[500]; + snprintf(msg, sizeof(msg), "get external IP address response: %s" + , std::string(p.get_body().begin, p.get_body().end).c_str()); + log(msg, l); + + ip_address_parse_state s; + xml_parse((char*)p.get_body().begin, (char*)p.get_body().end + , boost::bind(&find_ip_address, _1, _2, boost::ref(s))); + if (s.error_code != -1) + { + char msg[500]; + snprintf(msg, sizeof(msg), "error while getting external IP address, code: %u" + , s.error_code); + log(msg, l); + } + + if (!s.ip_address.empty()) { + snprintf(msg, sizeof(msg), "got router external IP address %s", s.ip_address.c_str()); + log(msg, l); + d.external_ip = address::from_string(s.ip_address.c_str(), ec); + } else { + log("failed to find external IP address in response", l); + } + + if (num_mappings() > 0) update_map(d, 0, l); +} + +void upnp::on_upnp_map_response(error_code const& e + , libtorrent::http_parser const& p, rootdevice& d, int mapping + , http_connection& c) +{ + boost::intrusive_ptr me(self()); + + mutex::scoped_lock l(m_mutex); + + TORRENT_ASSERT(d.magic == 1337); + if (d.upnp_connection && d.upnp_connection.get() == &c) + { + d.upnp_connection->close(); + d.upnp_connection.reset(); + } + + if (e && e != asio::error::eof) + { + char msg[500]; + snprintf(msg, sizeof(msg), "error while adding port map: %s" + , convert_from_native(e.message()).c_str()); + log(msg, l); + d.disabled = true; + return; + } + + if (m_closing) return; + +// error code response may look like this: +// +// +// +// s:Client +// UPnPError +// +// +// 402 +// Invalid Args +// +// +// +// +// + + if (!p.header_finished()) + { + log("error while adding port map: incomplete http message", l); + next(d, mapping, l); + return; + } + + std::string ct = p.header("content-type"); + if (!ct.empty() + && ct.find_first_of("text/xml") == std::string::npos + && ct.find_first_of("text/soap+xml") == std::string::npos + && ct.find_first_of("application/xml") == std::string::npos + && ct.find_first_of("application/soap+xml") == std::string::npos + ) + { + char msg[300]; + snprintf(msg, sizeof(msg), "error while adding port map: invalid content-type, \"%s\". Expected text/xml or application/soap+xml" + , ct.c_str()); + log(msg, l); + next(d, mapping, l); + return; + } + + // We don't want to ignore responses with return codes other than 200 + // since those might contain valid UPnP error codes + + error_code_parse_state s; + xml_parse((char*)p.get_body().begin, (char*)p.get_body().end + , boost::bind(&find_error_code, _1, _2, boost::ref(s))); + + if (s.error_code != -1) + { + char msg[500]; + snprintf(msg, sizeof(msg), "error while adding port map, code: %u" + , s.error_code); + log(msg, l); + } + + mapping_t& m = d.mapping[mapping]; + + if (s.error_code == 725) + { + // only permanent leases supported + d.lease_duration = 0; + m.action = mapping_t::action_add; + ++m.failcount; + update_map(d, mapping, l); + return; + } + else if (s.error_code == 727) + { + return_error(mapping, s.error_code, l); + } + else if ((s.error_code == 718 || s.error_code == 501) && m.failcount < 4) + { + // some routers return 501 action failed, instead of 716 + // The external port conflicts with another mapping + // pick a random port + m.external_port = 40000 + (random() % 10000); + m.action = mapping_t::action_add; + ++m.failcount; + update_map(d, mapping, l); + return; + } + else if (s.error_code != -1) + { + return_error(mapping, s.error_code, l); + } + + char msg[500]; + snprintf(msg, sizeof(msg), "map response: %s" + , std::string(p.get_body().begin, p.get_body().end).c_str()); + log(msg, l); + + if (s.error_code == -1) + { + l.unlock(); + m_callback(mapping, d.external_ip, m.external_port, error_code()); + l.lock(); + if (d.lease_duration > 0) + { + m.expires = time_now() + + seconds(int(d.lease_duration * 0.75f)); + ptime next_expire = m_refresh_timer.expires_at(); + if (next_expire < time_now() + || next_expire > m.expires) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("upnp::on_expire"); +#endif + error_code ec; + m_refresh_timer.expires_at(m.expires, ec); + m_refresh_timer.async_wait(boost::bind(&upnp::on_expire, self(), _1)); + } + } + else + { + m.expires = max_time(); + } + m.failcount = 0; + } + + next(d, mapping, l); +} + +void upnp::return_error(int mapping, int code, mutex::scoped_lock& l) +{ + int num_errors = sizeof(error_codes) / sizeof(error_codes[0]); + error_code_t* end = error_codes + num_errors; + error_code_t tmp = {code, 0}; + error_code_t* e = std::lower_bound(error_codes, end, tmp + , boost::bind(&error_code_t::code, _1) < boost::bind(&error_code_t::code, _2)); + std::string error_string = "UPnP mapping error "; + error_string += to_string(code).elems; + if (e != end && e->code == code) + { + error_string += ": "; + error_string += e->msg; + } + l.unlock(); + m_callback(mapping, address(), 0, error_code(code, get_upnp_category())); + l.lock(); +} + +void upnp::on_upnp_unmap_response(error_code const& e + , libtorrent::http_parser const& p, rootdevice& d, int mapping + , http_connection& c) +{ + boost::intrusive_ptr me(self()); + + mutex::scoped_lock l(m_mutex); + + TORRENT_ASSERT(d.magic == 1337); + if (d.upnp_connection && d.upnp_connection.get() == &c) + { + d.upnp_connection->close(); + d.upnp_connection.reset(); + } + + if (e && e != asio::error::eof) + { + char msg[500]; + snprintf(msg, sizeof(msg), "error while deleting portmap: %s" + , convert_from_native(e.message()).c_str()); + log(msg, l); + } + else if (!p.header_finished()) + { + log("error while deleting portmap: incomplete http message", l); + } + else if (p.status_code() != 200) + { + char msg[500]; + snprintf(msg, sizeof(msg), "error while deleting portmap: %s" + , convert_from_native(p.message()).c_str()); + log(msg, l); + } + else + { + char msg[500]; + snprintf(msg, sizeof(msg), "unmap response: %s" + , std::string(p.get_body().begin, p.get_body().end).c_str()); + log(msg, l); + } + + error_code_parse_state s; + if (p.header_finished()) + { + xml_parse((char*)p.get_body().begin, (char*)p.get_body().end + , boost::bind(&find_error_code, _1, _2, boost::ref(s))); + } + + l.unlock(); + m_callback(mapping, address(), 0, p.status_code() != 200 + ? error_code(p.status_code(), get_http_category()) + : error_code(s.error_code, get_upnp_category())); + l.lock(); + + d.mapping[mapping].protocol = none; + + next(d, mapping, l); +} + +void upnp::on_expire(error_code const& ec) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("upnp::on_expire"); +#endif + if (ec) return; + + ptime now = time_now(); + ptime next_expire = max_time(); + + mutex::scoped_lock l(m_mutex); + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end; ++i) + { + rootdevice& d = const_cast(*i); + TORRENT_ASSERT(d.magic == 1337); + for (int m = 0; m < num_mappings(); ++m) + { + if (d.mapping[m].expires != max_time()) + continue; + + if (d.mapping[m].expires < now) + { + d.mapping[m].expires = max_time(); + update_map(d, m, l); + } + else if (d.mapping[m].expires < next_expire) + { + next_expire = d.mapping[m].expires; + } + } + } + if (next_expire != max_time()) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("upnp::on_expire"); +#endif + error_code e; + m_refresh_timer.expires_at(next_expire, e); + m_refresh_timer.async_wait(boost::bind(&upnp::on_expire, self(), _1)); + } +} + +void upnp::close() +{ + mutex::scoped_lock l(m_mutex); + + error_code ec; + m_refresh_timer.cancel(ec); + m_broadcast_timer.cancel(ec); + m_map_timer.cancel(ec); + m_closing = true; + m_socket.close(); + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end; ++i) + { + rootdevice& d = const_cast(*i); + TORRENT_ASSERT(d.magic == 1337); + if (d.control_url.empty()) continue; + for (std::vector::iterator j = d.mapping.begin() + , end(d.mapping.end()); j != end; ++j) + { + if (j->protocol == none) continue; + if (j->action == mapping_t::action_add) + { + j->action = mapping_t::action_none; + continue; + } + j->action = mapping_t::action_delete; + m_mappings[j - d.mapping.begin()].protocol = none; + } + if (num_mappings() > 0) update_map(d, 0, l); + } +} + +} + diff --git a/apps/Launcher/ext/libtorrent/src/ut_metadata.cpp b/apps/Launcher/ext/libtorrent/src/ut_metadata.cpp new file mode 100644 index 0000000000..270459a7ba --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/ut_metadata.cpp @@ -0,0 +1,613 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include +#include +#include + +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/extensions/ut_metadata.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/random.hpp" +#ifdef TORRENT_STATS +#include "libtorrent/aux_/session_impl.hpp" +#endif + +namespace libtorrent { namespace +{ + enum + { + // this is the max number of bytes we'll + // queue up in the send buffer. If we exceed this, + // we'll wait another second before checking + // the send buffer size again. So, this may limit + // the rate at which we can server metadata to + // 160 kiB/s + send_buffer_limit = 0x4000 * 10, + + // this is the max number of requests we'll queue + // up. If we get more requests tha this, we'll + // start rejecting them, claiming we don't have + // metadata. If the torrent is greater than 16 MiB, + // we may hit this case (and the client requesting + // doesn't throttle its requests) + max_incoming_requests = 1024 + }; + + int div_round_up(int numerator, int denominator) + { + return (numerator + denominator - 1) / denominator; + } + + struct ut_metadata_peer_plugin; + + struct ut_metadata_plugin : torrent_plugin + { + ut_metadata_plugin(torrent& t) + : m_torrent(t) + , m_metadata_progress(0) + , m_metadata_size(0) + { + } + + virtual void on_files_checked() + { + // if the torrent is a seed, copy the metadata from + // the torrent before it is deallocated + if (m_torrent.is_seed()) + metadata(); + } + + virtual boost::shared_ptr new_connection( + peer_connection* pc); + + buffer::const_interval metadata() const + { + TORRENT_ASSERT(m_torrent.valid_metadata()); + if (!m_metadata) + { + m_metadata = m_torrent.torrent_file().metadata(); + m_metadata_size = m_torrent.torrent_file().metadata_size(); + TORRENT_ASSERT(hasher(m_metadata.get(), m_metadata_size).final() + == m_torrent.torrent_file().info_hash()); + } + return buffer::const_interval(m_metadata.get(), m_metadata.get() + + m_metadata_size); + } + + bool received_metadata(ut_metadata_peer_plugin& source + , char const* buf, int size, int piece, int total_size); + + // returns a piece of the metadata that + // we should request. + // returns -1 if we should hold off the request + int metadata_request(bool has_metadata); + + // this is called from the peer_connection for + // each piece of metadata it receives + void metadata_progress(int total_size, int received) + { + m_metadata_progress += received; + m_metadata_size = total_size; + m_torrent.set_progress_ppm(boost::int64_t(m_metadata_progress) * 1000000 / m_metadata_size); + } + + void on_piece_pass(int) + { + // if we became a seed, copy the metadata from + // the torrent before it is deallocated + if (m_torrent.is_seed()) + metadata(); + } + + void metadata_size(int size) + { + if (m_metadata_size > 0 || size <= 0 || size > 4 * 1024 * 1024) return; + m_metadata_size = size; + m_metadata.reset(new char[size]); + m_requested_metadata.resize(div_round_up(size, 16 * 1024)); + } + + private: + torrent& m_torrent; + + // this buffer is filled with the info-section of + // the metadata file while downloading it from + // peers, and while sending it. + // it is mutable because it's generated lazily + mutable boost::shared_array m_metadata; + + int m_metadata_progress; + mutable int m_metadata_size; + + struct metadata_piece + { + metadata_piece(): num_requests(0), last_request(0) {} + int num_requests; + time_t last_request; + boost::weak_ptr source; + bool operator<(metadata_piece const& rhs) const + { return num_requests < rhs.num_requests; } + }; + + // this vector keeps track of how many times each meatdata + // block has been requested and who we ended up getting it from + // std::numeric_limits::max() means we have the piece + std::vector m_requested_metadata; + }; + + + struct ut_metadata_peer_plugin : peer_plugin, boost::enable_shared_from_this + { + friend struct ut_metadata_plugin; + + ut_metadata_peer_plugin(torrent& t, bt_peer_connection& pc + , ut_metadata_plugin& tp) + : m_message_index(0) + , m_request_limit(min_time()) + , m_torrent(t) + , m_pc(pc) + , m_tp(tp) + {} + + virtual char const* type() const { return "ut_metadata"; } + + // can add entries to the extension handshake + virtual void add_handshake(entry& h) + { + entry& messages = h["m"]; + messages["ut_metadata"] = 2; + if (m_torrent.valid_metadata()) + h["metadata_size"] = m_tp.metadata().left(); + } + + // called when the extension handshake from the other end is received + virtual bool on_extension_handshake(lazy_entry const& h) + { + m_message_index = 0; + if (h.type() != lazy_entry::dict_t) return false; + lazy_entry const* messages = h.dict_find_dict("m"); + if (!messages) return false; + + int index = messages->dict_find_int_value("ut_metadata", -1); + if (index == -1) return false; + m_message_index = index; + + int metadata_size = h.dict_find_int_value("metadata_size"); + if (metadata_size > 0) + m_tp.metadata_size(metadata_size); + else + m_pc.set_has_metadata(false); + + maybe_send_request(); + return true; + } + + void write_metadata_packet(int type, int piece) + { + TORRENT_ASSERT(type >= 0 && type <= 2); + TORRENT_ASSERT(!m_pc.associated_torrent().expired()); + +#ifdef TORRENT_VERBOSE_LOGGING + char const* names[] = {"request", "data", "dont-have"}; + char const* n = ""; + if (type >= 0 && type < 3) n = names[type]; + m_pc.peer_log("==> UT_METADATA [ type: %d (%s) | piece: %d ]", type, n, piece); +#endif + + // abort if the peer doesn't support the metadata extension + if (m_message_index == 0) return; + + entry e; + e["msg_type"] = type; + e["piece"] = piece; + + char const* metadata = 0; + int metadata_piece_size = 0; + + if (type == 1) + { + + if (piece < 0 || piece >= int(m_tp.metadata().left() + 16 * 1024 - 1)/(16*1024)) + { +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("*** UT_METADATA [ invalid piece %d metadata size: %d ]" + , piece, int(m_tp.metadata().left())); +#endif + m_pc.disconnect(errors::invalid_metadata_message, 2); + return; + } + + TORRENT_ASSERT(m_pc.associated_torrent().lock()->valid_metadata()); + e["total_size"] = m_tp.metadata().left(); + int offset = piece * 16 * 1024; + metadata = m_tp.metadata().begin + offset; + metadata_piece_size = (std::min)( + int(m_tp.metadata().left() - offset), 16 * 1024); + TORRENT_ASSERT(metadata_piece_size > 0); + TORRENT_ASSERT(offset >= 0); + TORRENT_ASSERT(offset + metadata_piece_size <= int(m_tp.metadata().left())); + } + + char msg[200]; + char* header = msg; + char* p = &msg[6]; + int len = bencode(p, e); + int total_size = 2 + len + metadata_piece_size; + namespace io = detail; + io::write_uint32(total_size, header); + io::write_uint8(bt_peer_connection::msg_extended, header); + io::write_uint8(m_message_index, header); + + m_pc.send_buffer(msg, len + 6); + if (metadata_piece_size) m_pc.append_const_send_buffer( + metadata, metadata_piece_size); + } + + virtual bool on_extended(int length + , int extended_msg, buffer::const_interval body) + { + if (extended_msg != 2) return false; + if (m_message_index == 0) return false; + + if (length > 17 * 1024) + { +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("<== UT_METADATA [ packet too big %d ]", length); +#endif + m_pc.disconnect(errors::invalid_metadata_message, 2); + return true; + } + + if (!m_pc.packet_finished()) return true; + + int len; + entry msg = bdecode(body.begin, body.end, len); + if (msg.type() != entry::dictionary_t) + { +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("<== UT_METADATA [ not a dictionary ]"); +#endif + m_pc.disconnect(errors::invalid_metadata_message, 2); + return true; + } + + entry const* type_ent = msg.find_key("msg_type"); + entry const* piece_ent = msg.find_key("piece"); + if (type_ent == 0 || type_ent->type() != entry::int_t + || piece_ent == 0 || piece_ent->type() != entry::int_t) + { +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("<== UT_METADATA [ missing or invalid keys ]"); +#endif + m_pc.disconnect(errors::invalid_metadata_message, 2); + return true; + } + int type = type_ent->integer(); + int piece = piece_ent->integer(); + +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("<== UT_METADATA [ type: %d | piece: %d ]", type, piece); +#endif + + switch (type) + { + case 0: // request + { + if (!m_torrent.valid_metadata()) + { + write_metadata_packet(2, piece); + return true; + } + if (m_pc.send_buffer_size() < send_buffer_limit) + write_metadata_packet(1, piece); + else if (m_incoming_requests.size() < max_incoming_requests) + m_incoming_requests.push_back(piece); + else + write_metadata_packet(2, piece); + } + break; + case 1: // data + { + std::vector::iterator i = std::find(m_sent_requests.begin() + , m_sent_requests.end(), piece); + + // unwanted piece? + if (i == m_sent_requests.end()) + { +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("*** UT_METADATA [ UNWANTED / TIMED OUT ]"); +#endif + return true; + } + + m_sent_requests.erase(i); + entry const* total_size = msg.find_key("total_size"); + m_tp.received_metadata(*this, body.begin + len, body.left() - len, piece + , (total_size && total_size->type() == entry::int_t) ? total_size->integer() : 0); + maybe_send_request(); + } + break; + case 2: // have no data + { + m_request_limit = (std::max)(time_now() + minutes(1), m_request_limit); + std::vector::iterator i = std::find(m_sent_requests.begin() + , m_sent_requests.end(), piece); + // unwanted piece? + if (i == m_sent_requests.end()) return true; + m_sent_requests.erase(i); + } + break; + default: + // unknown message, ignore + break; + } + return true; + } + + virtual void tick() + { + maybe_send_request(); + while (!m_incoming_requests.empty() + && m_pc.send_buffer_size() < send_buffer_limit) + { + int piece = m_incoming_requests.front(); + m_incoming_requests.erase(m_incoming_requests.begin()); + write_metadata_packet(1, piece); + } + } + + void maybe_send_request() + { + if (m_pc.is_disconnecting()) return; + + // if we don't have any metadata, and this peer + // supports the request metadata extension + // and we aren't currently waiting for a request + // reply. Then, send a request for some metadata. + if (!m_torrent.valid_metadata() + && m_message_index != 0 + && m_sent_requests.size() < 2 + && has_metadata()) + { + int piece = m_tp.metadata_request(m_pc.has_metadata()); + if (piece == -1) return; + + m_sent_requests.push_back(piece); + write_metadata_packet(0, piece); + } + } + + bool has_metadata() const + { + return m_pc.has_metadata() || (time_now() > m_request_limit); + } + + void failed_hash_check(ptime const& now) + { + m_request_limit = now + seconds(20 + (boost::int64_t(random()) * 50) / UINT_MAX); + } + + private: + + // this is the message index the remote peer uses + // for metadata extension messages. + int m_message_index; + + // this is set to the next time we can request pieces + // again. It is updated every time we get a + // "I don't have metadata" message, but also when + // we receive metadata that fails the infohash check + ptime m_request_limit; + + // request queues + std::vector m_sent_requests; + std::vector m_incoming_requests; + + torrent& m_torrent; + bt_peer_connection& m_pc; + ut_metadata_plugin& m_tp; + }; + + boost::shared_ptr ut_metadata_plugin::new_connection( + peer_connection* pc) + { + if (pc->type() != peer_connection::bittorrent_connection) + return boost::shared_ptr(); + + bt_peer_connection* c = static_cast(pc); + return boost::shared_ptr(new ut_metadata_peer_plugin(m_torrent, *c, *this)); + } + + // has_metadata is false if the peer making the request has not announced + // that it has metadata. In this case, it shouldn't prevent other peers + // from requesting this block by setting a timeout on it. + int ut_metadata_plugin::metadata_request(bool has_metadata) + { + std::vector::iterator i = std::min_element( + m_requested_metadata.begin(), m_requested_metadata.end()); + + if (m_requested_metadata.empty()) + { + // if we don't know how many pieces there are + // just ask for piece 0 + m_requested_metadata.resize(1); + i = m_requested_metadata.begin(); + } + + int piece = i - m_requested_metadata.begin(); + + // don't request the same block more than once every 3 seconds + time_t now = time(0); + if (now - m_requested_metadata[piece].last_request < 3) return -1; + + ++m_requested_metadata[piece].num_requests; + + // only set the timeout on this block, only if the peer + // has metadata. This is to prevent peers with no metadata + // to starve out sending requests to peers with metadata + if (has_metadata) + m_requested_metadata[piece].last_request = now; + + return piece; + } + + inline bool ut_metadata_plugin::received_metadata( + ut_metadata_peer_plugin& source + , char const* buf, int size, int piece, int total_size) + { + if (m_torrent.valid_metadata()) + { +#ifdef TORRENT_VERBOSE_LOGGING + source.m_pc.peer_log("*** UT_METADATA [ ALREADY HAVE METADATA ]"); +#endif + m_torrent.add_redundant_bytes(size, torrent::piece_unknown); + return false; + } + + if (!m_metadata) + { + // verify the total_size + if (total_size <= 0 || total_size > m_torrent.session().settings().max_metadata_size) + { +#ifdef TORRENT_VERBOSE_LOGGING + source.m_pc.peer_log("*** UT_METADATA [ metadata size too big: %d ]", total_size); +#endif +// #error post alert + return false; + } + + m_metadata.reset(new char[total_size]); + m_requested_metadata.resize(div_round_up(total_size, 16 * 1024)); + m_metadata_size = total_size; + } + + if (piece < 0 || piece >= int(m_requested_metadata.size())) + { +#ifdef TORRENT_VERBOSE_LOGGING + source.m_pc.peer_log("*** UT_METADATA [ piece: %d INVALID ]", piece); +#endif + return false; + } + + if (total_size != m_metadata_size) + { +#ifdef TORRENT_VERBOSE_LOGGING + source.m_pc.peer_log("*** UT_METADATA [ total_size: %d INCONSISTENT WITH: %d ]" + , total_size, m_metadata_size); +#endif + // they disagree about the size! + return false; + } + + if (piece * 16 * 1024 + size > m_metadata_size) + { + // this piece is invalid + return false; + } + + std::memcpy(&m_metadata[piece * 16 * 1024], buf, size); + // mark this piece has 'have' + m_requested_metadata[piece].num_requests = (std::numeric_limits::max)(); + m_requested_metadata[piece].source = source.shared_from_this(); + + bool have_all = std::count_if(m_requested_metadata.begin() + , m_requested_metadata.end(), boost::bind(&metadata_piece::num_requests, _1) + == (std::numeric_limits::max)()) == int(m_requested_metadata.size()); + + if (!have_all) return false; + + if (!m_torrent.set_metadata(&m_metadata[0], m_metadata_size)) + { + if (!m_torrent.valid_metadata()) + { + ptime now = time_now(); + // any peer that we downloaded metadata from gets a random time + // penalty, from 5 to 30 seconds or so. During this time we don't + // make any metadata requests from those peers (to mix it up a bit + // of which peers we use) + // if we only have one block, and thus requested it from a single + // peer, we bump up the retry time a lot more to try other peers + bool single_peer = m_requested_metadata.size() == 1; + for (int i = 0; i < int(m_requested_metadata.size()); ++i) + { + m_requested_metadata[i].num_requests = 0; + boost::shared_ptr peer + = m_requested_metadata[i].source.lock(); + if (!peer) continue; + + peer->failed_hash_check(single_peer ? now + minutes(5) : now); + } + } + return false; + } + + // clear the storage for the bitfield + std::vector().swap(m_requested_metadata); + + return true; + } + +} } + +namespace libtorrent +{ + + boost::shared_ptr create_ut_metadata_plugin(torrent* t, void*) + { + // don't add this extension if the torrent is private + if (t->valid_metadata() && t->torrent_file().priv()) return boost::shared_ptr(); + return boost::shared_ptr(new ut_metadata_plugin(*t)); + } + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/ut_pex.cpp b/apps/Launcher/ext/libtorrent/src/ut_pex.cpp new file mode 100644 index 0000000000..eeefbb7830 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/ut_pex.cpp @@ -0,0 +1,690 @@ +/* + +Copyright (c) 2006-2014, MassaRoddel, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/broadcast_socket.hpp" +#include "libtorrent/socket_io.hpp" +#include "libtorrent/peer_info.hpp" +#include "libtorrent/random.hpp" + +#include "libtorrent/extensions/ut_pex.hpp" + +#ifdef TORRENT_VERBOSE_LOGGING +#include "libtorrent/lazy_entry.hpp" +#endif + +namespace libtorrent { namespace +{ + const char extension_name[] = "ut_pex"; + + enum + { + extension_index = 1, + max_peer_entries = 100 + }; + + bool send_peer(peer_connection const& p) + { + // don't send out those peers that we haven't connected to + // (that have connected to us) and that aren't sharing their + // listening port + if (!p.is_outgoing() && !p.received_listen_port()) return false; + // don't send out peers that we haven't successfully connected to + if (p.is_connecting()) return false; + if (p.in_handshake()) return false; + return true; + } + + struct ut_pex_plugin: torrent_plugin + { + // randomize when we rebuild the pex message + // to evenly spread it out across all torrents + // the more torrents we have, the longer we can + // delay the rebuilding + ut_pex_plugin(torrent& t) + : m_torrent(t) + , m_last_msg(min_time()) + , m_peers_in_message(0) {} + + virtual boost::shared_ptr new_connection(peer_connection* pc); + + std::vector& get_ut_pex_msg() + { + return m_ut_pex_msg; + } + + int peers_in_msg() const + { + return m_peers_in_message; + } + + // the second tick of the torrent + // each minute the new lists of "added" + "added.f" and "dropped" + // are calculated here and the pex message is created + // each peer connection will use this message + // max_peer_entries limits the packet size + virtual void tick() + { + ptime now = time_now(); + if (now - m_last_msg < seconds(60)) return; + m_last_msg = now; + + int num_peers = m_torrent.num_peers(); + if (num_peers == 0) return; + + entry pex; + std::string& pla = pex["added"].string(); + std::string& pld = pex["dropped"].string(); + std::string& plf = pex["added.f"].string(); + std::back_insert_iterator pla_out(pla); + std::back_insert_iterator pld_out(pld); + std::back_insert_iterator plf_out(plf); +#if TORRENT_USE_IPV6 + std::string& pla6 = pex["added6"].string(); + std::string& pld6 = pex["dropped6"].string(); + std::string& plf6 = pex["added6.f"].string(); + std::back_insert_iterator pla6_out(pla6); + std::back_insert_iterator pld6_out(pld6); + std::back_insert_iterator plf6_out(plf6); +#endif + + std::set dropped; + m_old_peers.swap(dropped); + + m_peers_in_message = 0; + int num_added = 0; + for (torrent::peer_iterator i = m_torrent.begin() + , end(m_torrent.end()); i != end; ++i) + { + peer_connection* peer = *i; + if (!send_peer(*peer)) continue; + + tcp::endpoint remote = peer->remote(); + m_old_peers.insert(remote); + + std::set::iterator di = dropped.find(remote); + if (di == dropped.end()) + { + // don't write too big of a package + if (num_added >= max_peer_entries) break; + + // only send proper bittorrent peers + if (peer->type() != peer_connection::bittorrent_connection) + continue; + + bt_peer_connection* p = static_cast(peer); + + // if the peer has told us which port its listening on, + // use that port. But only if we didn't connect to the peer. + // if we connected to it, use the port we know works + policy::peer *pi = 0; + if (!p->is_outgoing() && (pi = peer->peer_info_struct()) && pi->port > 0) + remote.port(pi->port); + + // no supported flags to set yet + // 0x01 - peer supports encryption + // 0x02 - peer is a seed + // 0x04 - supports uTP. This is only a positive flags + // passing 0 doesn't mean the peer doesn't + // support uTP + // 0x08 - supports holepunching protocol. If this + // flag is received from a peer, it can be + // used as a rendezvous point in case direct + // connections to the peer fail + int flags = p->is_seed() ? 2 : 0; +#ifndef TORRENT_DISABLE_ENCRYPTION + flags |= p->supports_encryption() ? 1 : 0; +#endif + flags |= is_utp(*p->get_socket()) ? 4 : 0; + flags |= p->supports_holepunch() ? 8 : 0; + + // i->first was added since the last time + if (remote.address().is_v4()) + { + detail::write_endpoint(remote, pla_out); + detail::write_uint8(flags, plf_out); + } +#if TORRENT_USE_IPV6 + else + { + detail::write_endpoint(remote, pla6_out); + detail::write_uint8(flags, plf6_out); + } +#endif + ++num_added; + ++m_peers_in_message; + } + else + { + // this was in the previous message + // so, it wasn't dropped + dropped.erase(di); + } + } + + for (std::set::const_iterator i = dropped.begin() + , end(dropped.end()); i != end; ++i) + { + if (i->address().is_v4()) + detail::write_endpoint(*i, pld_out); +#if TORRENT_USE_IPV6 + else + detail::write_endpoint(*i, pld6_out); +#endif + ++m_peers_in_message; + } + + m_ut_pex_msg.clear(); + bencode(std::back_inserter(m_ut_pex_msg), pex); + } + + private: + torrent& m_torrent; + + std::set m_old_peers; + ptime m_last_msg; + std::vector m_ut_pex_msg; + int m_peers_in_message; + }; + + + struct ut_pex_peer_plugin : peer_plugin + { + ut_pex_peer_plugin(torrent& t, peer_connection& pc, ut_pex_plugin& tp) + : m_torrent(t) + , m_pc(pc) + , m_tp(tp) + , m_last_msg(min_time()) + , m_message_index(0) + , m_first_time(true) + { + const int num_pex_timers = sizeof(m_last_pex)/sizeof(m_last_pex[0]); + for (int i = 0; i < num_pex_timers; ++i) + { + m_last_pex[i]= min_time(); + } + } + + virtual char const* type() const { return "ut_pex"; } + + virtual void add_handshake(entry& h) + { + entry& messages = h["m"]; + messages[extension_name] = extension_index; + } + + virtual bool on_extension_handshake(lazy_entry const& h) + { + m_message_index = 0; + if (h.type() != lazy_entry::dict_t) return false; + lazy_entry const* messages = h.dict_find("m"); + if (!messages || messages->type() != lazy_entry::dict_t) return false; + + int index = int(messages->dict_find_int_value(extension_name, -1)); + if (index == -1) return false; + m_message_index = index; + return true; + } + + virtual bool on_extended(int length, int msg, buffer::const_interval body) + { + if (msg != extension_index) return false; + if (m_message_index == 0) return false; + + if (length > 500 * 1024) + { + m_pc.disconnect(errors::pex_message_too_large, 2); + return true; + } + + if (body.left() < length) return true; + + ptime now = time_now(); + if (now - m_last_pex[0] < seconds(60)) + { + // this client appears to be trying to flood us + // with pex messages. Don't allow that. + m_pc.disconnect(errors::too_frequent_pex); + return true; + } + + const int num_pex_timers = sizeof(m_last_pex)/sizeof(m_last_pex[0]); + for (int i = 0; i < num_pex_timers-1; ++i) + m_last_pex[i] = m_last_pex[i+1]; + m_last_pex[num_pex_timers-1] = now; + + lazy_entry pex_msg; + error_code ec; + int ret = lazy_bdecode(body.begin, body.end, pex_msg, ec); + if (ret != 0 || pex_msg.type() != lazy_entry::dict_t) + { + m_pc.disconnect(errors::invalid_pex_message, 2); + return true; + } + + lazy_entry const* p = pex_msg.dict_find_string("dropped"); + +#ifdef TORRENT_VERBOSE_LOGGING + int num_dropped = 0; + int num_added = 0; + if (p) num_dropped += p->string_length()/6; +#endif + if (p) + { + int num_peers = p->string_length() / 6; + char const* in = p->string_ptr(); + + for (int i = 0; i < num_peers; ++i) + { + tcp::endpoint adr = detail::read_v4_endpoint(in); + peers4_t::value_type v(adr.address().to_v4().to_bytes(), adr.port()); + peers4_t::iterator j = std::lower_bound(m_peers.begin(), m_peers.end(), v); + if (j != m_peers.end() && *j == v) m_peers.erase(j); + } + } + + p = pex_msg.dict_find_string("added"); + lazy_entry const* pf = pex_msg.dict_find_string("added.f"); + +#ifdef TORRENT_VERBOSE_LOGGING + if (p) num_added += p->string_length() / 6; +#endif + if (p != 0 + && pf != 0 + && pf->string_length() == p->string_length() / 6) + { + int num_peers = pf->string_length(); + char const* in = p->string_ptr(); + char const* fin = pf->string_ptr(); + + peer_id pid(0); + policy& p = m_torrent.get_policy(); + for (int i = 0; i < num_peers; ++i) + { + tcp::endpoint adr = detail::read_v4_endpoint(in); + char flags = *fin++; + + if (int(m_peers.size()) >= m_torrent.settings().max_pex_peers) break; + + // ignore local addresses unless the peer is local to us + if (is_local(adr.address()) && !is_local(m_pc.remote().address())) continue; + + peers4_t::value_type v(adr.address().to_v4().to_bytes(), adr.port()); + peers4_t::iterator j = std::lower_bound(m_peers.begin(), m_peers.end(), v); + // do we already know about this peer? + if (j != m_peers.end() && *j == v) continue; + m_peers.insert(j, v); + p.add_peer(adr, pid, peer_info::pex, flags); + } + } + +#if TORRENT_USE_IPV6 + + lazy_entry const* p6 = pex_msg.dict_find("dropped6"); +#ifdef TORRENT_VERBOSE_LOGGING + if (p6) num_dropped += p6->string_length() / 18; +#endif + if (p6 != 0 && p6->type() == lazy_entry::string_t) + { + int num_peers = p6->string_length() / 18; + char const* in = p6->string_ptr(); + + for (int i = 0; i < num_peers; ++i) + { + tcp::endpoint adr = detail::read_v6_endpoint(in); + peers6_t::value_type v(adr.address().to_v6().to_bytes(), adr.port()); + peers6_t::iterator j = std::lower_bound(m_peers6.begin(), m_peers6.end(), v); + if (j != m_peers6.end() && *j == v) m_peers6.erase(j); + } + } + + p6 = pex_msg.dict_find("added6"); +#ifdef TORRENT_VERBOSE_LOGGING + if (p6) num_added += p6->string_length() / 18; +#endif + lazy_entry const* p6f = pex_msg.dict_find("added6.f"); + if (p6 != 0 + && p6f != 0 + && p6->type() == lazy_entry::string_t + && p6f->type() == lazy_entry::string_t + && p6f->string_length() == p6->string_length() / 18) + { + int num_peers = p6f->string_length(); + char const* in = p6->string_ptr(); + char const* fin = p6f->string_ptr(); + + peer_id pid(0); + policy& p = m_torrent.get_policy(); + for (int i = 0; i < num_peers; ++i) + { + tcp::endpoint adr = detail::read_v6_endpoint(in); + char flags = *fin++; + // ignore local addresses unless the peer is local to us + if (is_local(adr.address()) && !is_local(m_pc.remote().address())) continue; + if (int(m_peers6.size()) >= m_torrent.settings().max_pex_peers) break; + + peers6_t::value_type v(adr.address().to_v6().to_bytes(), adr.port()); + peers6_t::iterator j = std::lower_bound(m_peers6.begin(), m_peers6.end(), v); + // do we already know about this peer? + if (j != m_peers6.end() && *j == v) continue; + m_peers6.insert(j, v); + p.add_peer(adr, pid, peer_info::pex, flags); + } + } +#endif +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("<== PEX [ dropped: %d added: %d ]" + , num_dropped, num_added); +#endif + return true; + } + + // the peers second tick + // every minute we send a pex message + virtual void tick() + { + // no handshake yet + if (!m_message_index) return; + + ptime now = time_now(); + if (now - m_last_msg < seconds(60)) + { +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("*** PEX [ waiting: %d seconds to next msg ]" + , total_seconds(seconds(60) - (now - m_last_msg))); +#endif + return; + } + static ptime global_last = min_time(); + + int num_peers = m_torrent.num_peers(); + if (num_peers <= 1) return; + + // don't send pex messages more often than 1 every 100 ms, and + // allow pex messages to be sent 5 seconds apart if there isn't + // contention + int delay = (std::min)((std::max)(60000 / num_peers, 100), 3000); + + if (now - global_last < milliseconds(delay)) + { +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("*** PEX [ global-wait: %d ]", total_seconds(milliseconds(delay) - (now - global_last))); +#endif + return; + } + + // this will allow us to catch up, even if our timer + // has lower resolution than delay + if (global_last == min_time()) + global_last = now; + else + global_last += milliseconds(delay); + + m_last_msg = now; + + if (m_first_time) + { + send_ut_peer_list(); + m_first_time = false; + } + else + { + send_ut_peer_diff(); + } + } + + void send_ut_peer_diff() + { + // if there's no change in out peer set, don't send anything + if (m_tp.peers_in_msg() == 0) return; + + std::vector const& pex_msg = m_tp.get_ut_pex_msg(); + + char msg[6]; + char* ptr = msg; + + detail::write_uint32(1 + 1 + pex_msg.size(), ptr); + detail::write_uint8(bt_peer_connection::msg_extended, ptr); + detail::write_uint8(m_message_index, ptr); + m_pc.send_buffer(msg, sizeof(msg)); + m_pc.send_buffer(&pex_msg[0], pex_msg.size()); + +#ifdef TORRENT_VERBOSE_LOGGING + lazy_entry m; + error_code ec; + int ret = lazy_bdecode(&pex_msg[0], &pex_msg[0] + pex_msg.size(), m, ec); + TORRENT_ASSERT(ret == 0); + TORRENT_ASSERT(!ec); + int num_dropped = 0; + int num_added = 0; + lazy_entry const* e = m.dict_find_string("added"); + if (e) num_added += e->string_length() / 6; + e = m.dict_find_string("dropped"); + if (e) num_dropped += e->string_length() / 6; + e = m.dict_find_string("added6"); + if (e) num_added += e->string_length() / 18; + e = m.dict_find_string("dropped6"); + if (e) num_dropped += e->string_length() / 18; + m_pc.peer_log("==> PEX_DIFF [ dropped: %d added: %d msg_size: %d ]" + , num_dropped, num_added, int(pex_msg.size())); +#endif + } + + void send_ut_peer_list() + { + entry pex; + // leave the dropped string empty + pex["dropped"].string(); + std::string& pla = pex["added"].string(); + std::string& plf = pex["added.f"].string(); + std::back_insert_iterator pla_out(pla); + std::back_insert_iterator plf_out(plf); + +#if TORRENT_USE_IPV6 + pex["dropped6"].string(); + std::string& pla6 = pex["added6"].string(); + std::string& plf6 = pex["added6.f"].string(); + std::back_insert_iterator pla6_out(pla6); + std::back_insert_iterator plf6_out(plf6); +#endif + + int num_added = 0; + for (torrent::peer_iterator i = m_torrent.begin() + , end(m_torrent.end()); i != end; ++i) + { + peer_connection* peer = *i; + if (!send_peer(*peer)) continue; + + // don't write too big of a package + if (num_added >= max_peer_entries) break; + + // only send proper bittorrent peers + if (peer->type() != peer_connection::bittorrent_connection) + continue; + + bt_peer_connection* p = static_cast(peer); + + // no supported flags to set yet + // 0x01 - peer supports encryption + // 0x02 - peer is a seed + // 0x04 - supports uTP. This is only a positive flags + // passing 0 doesn't mean the peer doesn't + // support uTP + // 0x08 - supports holepunching protocol. If this + // flag is received from a peer, it can be + // used as a rendezvous point in case direct + // connections to the peer fail + int flags = p->is_seed() ? 2 : 0; +#ifndef TORRENT_DISABLE_ENCRYPTION + flags |= p->supports_encryption() ? 1 : 0; +#endif + flags |= is_utp(*p->get_socket()) ? 4 : 0; + flags |= p->supports_holepunch() ? 8 : 0; + + tcp::endpoint remote = peer->remote(); + + policy::peer *pi = 0; + if (!p->is_outgoing() && (pi = peer->peer_info_struct()) && pi->port > 0) + remote.port(pi->port); + + // i->first was added since the last time + if (remote.address().is_v4()) + { + detail::write_endpoint(remote, pla_out); + detail::write_uint8(flags, plf_out); + } +#if TORRENT_USE_IPV6 + else + { + detail::write_endpoint(remote, pla6_out); + detail::write_uint8(flags, plf6_out); + } +#endif + ++num_added; + } + std::vector pex_msg; + bencode(std::back_inserter(pex_msg), pex); + + char msg[6]; + char* ptr = msg; + + detail::write_uint32(1 + 1 + pex_msg.size(), ptr); + detail::write_uint8(bt_peer_connection::msg_extended, ptr); + detail::write_uint8(m_message_index, ptr); + m_pc.send_buffer(msg, sizeof(msg)); + m_pc.send_buffer(&pex_msg[0], pex_msg.size()); + +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("==> PEX_FULL [ added: %d msg_size: %d ]", num_added, int(pex_msg.size())); +#endif + } + + torrent& m_torrent; + peer_connection& m_pc; + ut_pex_plugin& m_tp; + // stores all peers this this peer is connected to. These lists + // are updated with each pex message and are limited in size + // to protect against malicious clients. These lists are also + // used for looking up which peer a peer that supports holepunch + // came from. + // these are vectors to save memory and keep the items close + // together for performance. Inserting and removing is relatively + // cheap since the lists' size is limited + typedef std::vector > peers4_t; + peers4_t m_peers; +#if TORRENT_USE_IPV6 + typedef std::vector > peers6_t; + peers6_t m_peers6; +#endif + // the last pex messages we received + // [0] is the oldest one. There is a problem with + // rate limited connections, because we may sit + // for a long time, accumulating pex messages, and + // then once we read from the socket it will look like + // we received them all back to back. That's why + // we look at 6 pex messages back. + ptime m_last_pex[6]; + + ptime m_last_msg; + int m_message_index; + + // this is initialized to true, and set to + // false after the first pex message has been sent. + // it is used to know if a diff message or a) ful + // message should be sent. + bool m_first_time; + }; + + boost::shared_ptr ut_pex_plugin::new_connection(peer_connection* pc) + { + if (pc->type() != peer_connection::bittorrent_connection) + return boost::shared_ptr(); + + return boost::shared_ptr(new ut_pex_peer_plugin(m_torrent + , *pc, *this)); + } +} } + +namespace libtorrent +{ + boost::shared_ptr create_ut_pex_plugin(torrent* t, void*) + { + if (t->torrent_file().priv() || (t->torrent_file().is_i2p() + && !t->settings().allow_i2p_mixed)) + { + return boost::shared_ptr(); + } + return boost::shared_ptr(new ut_pex_plugin(*t)); + } + + bool was_introduced_by(peer_plugin const* pp, tcp::endpoint const& ep) + { + ut_pex_peer_plugin* p = (ut_pex_peer_plugin*)pp; +#if TORRENT_USE_IPV6 + if (ep.address().is_v4()) + { +#endif + ut_pex_peer_plugin::peers4_t::value_type v(ep.address().to_v4().to_bytes(), ep.port()); + ut_pex_peer_plugin::peers4_t::const_iterator i + = std::lower_bound(p->m_peers.begin(), p->m_peers.end(), v); + return i != p->m_peers.end() && *i == v; +#if TORRENT_USE_IPV6 + } + else + { + ut_pex_peer_plugin::peers6_t::value_type v(ep.address().to_v6().to_bytes(), ep.port()); + ut_pex_peer_plugin::peers6_t::iterator i + = std::lower_bound(p->m_peers6.begin(), p->m_peers6.end(), v); + return i != p->m_peers6.end() && *i == v; + } +#endif + } +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/utf8.cpp b/apps/Launcher/ext/libtorrent/src/utf8.cpp new file mode 100644 index 0000000000..92ef3bc6ef --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/utf8.cpp @@ -0,0 +1,104 @@ +/* + +Copyright (c) 2012-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" +#include "libtorrent/utf8.hpp" +#include "libtorrent/ConvertUTF.h" + +// on windows we need these functions for +// convert_to_native and convert_from_native +#if TORRENT_USE_WSTRING || defined TORRENT_WINDOWS + +namespace libtorrent +{ + utf8_conv_result_t utf8_wchar(const std::string &utf8, std::wstring &wide) + { + // allocate space for worst-case + wide.resize(utf8.size()); + wchar_t const* dst_start = wide.c_str(); + char const* src_start = utf8.c_str(); + ConversionResult ret; + if (sizeof(wchar_t) == sizeof(UTF32)) + { + ret = ConvertUTF8toUTF32((const UTF8**)&src_start, (const UTF8*)src_start + + utf8.size(), (UTF32**)&dst_start, (UTF32*)dst_start + wide.size() + , lenientConversion); + wide.resize(dst_start - wide.c_str()); + return (utf8_conv_result_t)ret; + } + else if (sizeof(wchar_t) == sizeof(UTF16)) + { + ret = ConvertUTF8toUTF16((const UTF8**)&src_start, (const UTF8*)src_start + + utf8.size(), (UTF16**)&dst_start, (UTF16*)dst_start + wide.size() + , lenientConversion); + wide.resize(dst_start - wide.c_str()); + return (utf8_conv_result_t)ret; + } + else + { + return source_illegal; + } + } + + utf8_conv_result_t wchar_utf8(const std::wstring &wide, std::string &utf8) + { + // allocate space for worst-case + utf8.resize(wide.size() * 6); + if (wide.empty()) return conversion_ok; + char* dst_start = &utf8[0]; + wchar_t const* src_start = wide.c_str(); + ConversionResult ret; + if (sizeof(wchar_t) == sizeof(UTF32)) + { + ret = ConvertUTF32toUTF8((const UTF32**)&src_start, (const UTF32*)src_start + + wide.size(), (UTF8**)&dst_start, (UTF8*)dst_start + utf8.size() + , lenientConversion); + utf8.resize(dst_start - &utf8[0]); + return (utf8_conv_result_t)ret; + } + else if (sizeof(wchar_t) == sizeof(UTF16)) + { + ret = ConvertUTF16toUTF8((const UTF16**)&src_start, (const UTF16*)src_start + + wide.size(), (UTF8**)&dst_start, (UTF8*)dst_start + utf8.size() + , lenientConversion); + utf8.resize(dst_start - &utf8[0]); + return (utf8_conv_result_t)ret; + } + else + { + return source_illegal; + } + } +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/utp_socket_manager.cpp b/apps/Launcher/ext/libtorrent/src/utp_socket_manager.cpp new file mode 100644 index 0000000000..c89fdf95ac --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/utp_socket_manager.cpp @@ -0,0 +1,479 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/utp_stream.hpp" +#include "libtorrent/udp_socket.hpp" +#include "libtorrent/utp_socket_manager.hpp" +#include "libtorrent/instantiate_connection.hpp" +#include "libtorrent/socket_io.hpp" +#include "libtorrent/broadcast_socket.hpp" // for is_teredo +#include "libtorrent/random.hpp" + +// #define TORRENT_DEBUG_MTU 1135 + +namespace libtorrent +{ + + utp_socket_manager::utp_socket_manager(session_settings const& sett, udp_socket& s + , incoming_utp_callback_t cb) + : m_sock(s) + , m_cb(cb) + , m_last_socket(0) + , m_new_connection(-1) + , m_sett(sett) + , m_last_route_update(min_time()) + , m_last_if_update(min_time()) + , m_sock_buf_size(0) + { + memset(m_counters, 0, sizeof(m_counters)); + } + + utp_socket_manager::~utp_socket_manager() + { + for (socket_map_t::iterator i = m_utp_sockets.begin() + , end(m_utp_sockets.end()); i != end; ++i) + { + delete_utp_impl(i->second); + } + } + + void utp_socket_manager::get_status(utp_status& s) const + { + s.num_idle = 0; + s.num_syn_sent = 0; + s.num_connected = 0; + s.num_fin_sent = 0; + s.num_close_wait = 0; + + s.packet_loss = m_counters[packet_loss]; + s.timeout = m_counters[timeout]; + s.packets_in = m_counters[packets_in]; + s.packets_out = m_counters[packets_out]; + s.fast_retransmit = m_counters[fast_retransmit]; + s.packet_resend = m_counters[packet_resend]; + s.samples_above_target = m_counters[samples_above_target]; + s.samples_below_target = m_counters[samples_below_target]; + s.payload_pkts_in = m_counters[payload_pkts_in]; + s.payload_pkts_out = m_counters[payload_pkts_out]; + s.invalid_pkts_in = m_counters[invalid_pkts_in]; + s.redundant_pkts_in = m_counters[redundant_pkts_in]; + + for (socket_map_t::const_iterator i = m_utp_sockets.begin() + , end(m_utp_sockets.end()); i != end; ++i) + { + int state = utp_socket_state(i->second); + switch (state) + { + case 0: ++s.num_idle; break; + case 1: ++s.num_syn_sent; break; + case 2: ++s.num_connected; break; + case 3: ++s.num_fin_sent; break; + case 4: ++s.num_close_wait; break; + case 5: ++s.num_close_wait; break; + } + } + } + + void utp_socket_manager::tick(ptime now) + { + for (socket_map_t::iterator i = m_utp_sockets.begin() + , end(m_utp_sockets.end()); i != end;) + { + if (should_delete(i->second)) + { + delete_utp_impl(i->second); + if (m_last_socket == i->second) m_last_socket = 0; + m_utp_sockets.erase(i++); + continue; + } + tick_utp_impl(i->second, now); + ++i; + } + } + + void utp_socket_manager::mtu_for_dest(address const& addr, int& link_mtu, int& utp_mtu) + { + if (time_now() - m_last_route_update > seconds(60)) + { + m_last_route_update = time_now(); + error_code ec; + m_routes = enum_routes(m_sock.get_io_service(), ec); + } + + int mtu = 0; + if (!m_routes.empty()) + { + for (std::vector::iterator i = m_routes.begin() + , end(m_routes.end()); i != end; ++i) + { + if (!match_addr_mask(addr, i->destination, i->netmask)) continue; + + // assume that we'll actually use the route with the largest + // MTU (seems like a reasonable assumption). + // this could however be improved by using the route metrics + // and the prefix length of the netmask to order the matches + if (mtu < i->mtu) mtu = i->mtu; + } + } + + if (mtu == 0) + { + if (is_teredo(addr)) mtu = TORRENT_TEREDO_MTU; + else mtu = TORRENT_ETHERNET_MTU; + } + +#if defined __APPLE__ + // apple has a very strange loopback. It appears you can't + // send messages of the reported MTU size, and you don't get + // EWOULDBLOCK either. + if (is_loopback(addr)) + { + if (is_teredo(addr)) mtu = TORRENT_TEREDO_MTU; + else mtu = TORRENT_ETHERNET_MTU; + } +#endif + + // clamp the MTU within reasonable bounds + if (mtu < TORRENT_INET_MIN_MTU) mtu = TORRENT_INET_MIN_MTU; + else if (mtu > TORRENT_INET_MAX_MTU) mtu = TORRENT_INET_MAX_MTU; + + link_mtu = mtu; + + mtu -= TORRENT_UDP_HEADER; + + if (m_sock.get_proxy_settings().type == proxy_settings::socks5 + || m_sock.get_proxy_settings().type == proxy_settings::socks5_pw) + { + // this is for the IP layer + address proxy_addr = m_sock.proxy_addr().address(); + if (proxy_addr.is_v4()) mtu -= TORRENT_IPV4_HEADER; + else mtu -= TORRENT_IPV6_HEADER; + + // this is for the SOCKS layer + mtu -= TORRENT_SOCKS5_HEADER; + + // the address field in the SOCKS header + if (addr.is_v4()) mtu -= 4; + else mtu -= 16; + + } + else + { + if (addr.is_v4()) mtu -= TORRENT_IPV4_HEADER; + else mtu -= TORRENT_IPV6_HEADER; + } + + utp_mtu = mtu; + } + + void utp_socket_manager::send_packet(udp::endpoint const& ep, char const* p + , int len, error_code& ec, int flags) + { + if (!m_sock.is_open()) + { + ec = asio::error::operation_aborted; + return; + } + +#ifdef TORRENT_DEBUG_MTU + // drop packets that exceed the debug MTU + if ((flags & dont_fragment) && len > TORRENT_DEBUG_MTU) return; +#endif + +#ifdef TORRENT_HAS_DONT_FRAGMENT + error_code tmp; + if (flags & utp_socket_manager::dont_fragment) + m_sock.set_option(libtorrent::dont_fragment(true), tmp); +#endif + m_sock.send(ep, p, len, ec); +#ifdef TORRENT_HAS_DONT_FRAGMENT + if (flags & utp_socket_manager::dont_fragment) + m_sock.set_option(libtorrent::dont_fragment(false), tmp); +#endif + } + + int utp_socket_manager::local_port(error_code& ec) const + { + return m_sock.local_endpoint(ec).port(); + } + + tcp::endpoint utp_socket_manager::local_endpoint(address const& remote, error_code& ec) const + { + tcp::endpoint socket_ep = m_sock.local_endpoint(ec); + + // first enumerate the routes in the routing table + if (time_now() - m_last_route_update > seconds(60)) + { + m_last_route_update = time_now(); + error_code ec; + m_routes = enum_routes(m_sock.get_io_service(), ec); + if (ec) return socket_ep; + } + + if (m_routes.empty()) return socket_ep; + // then find the best match + ip_route* best = &m_routes[0]; + for (std::vector::iterator i = m_routes.begin() + , end(m_routes.end()); i != end; ++i) + { + if (is_any(i->destination) && i->destination.is_v4() == remote.is_v4()) + { + best = &*i; + continue; + } + + if (match_addr_mask(remote, i->destination, i->netmask)) + { + best = &*i; + continue; + } + } + + // best now tells us which interface we would send over + // for this target. Now figure out what the local address + // is for that interface + + if (time_now() - m_last_if_update > seconds(60)) + { + m_last_if_update = time_now(); + error_code ec; + m_interfaces = enum_net_interfaces(m_sock.get_io_service(), ec); + if (ec) return socket_ep; + } + + for (std::vector::iterator i = m_interfaces.begin() + , end(m_interfaces.end()); i != end; ++i) + { + if (i->interface_address.is_v4() != remote.is_v4()) + continue; + + if (strcmp(best->name, i->name) == 0) + return tcp::endpoint(i->interface_address, socket_ep.port()); + } + return socket_ep; + } + + bool utp_socket_manager::incoming_packet(error_code const& ec, udp::endpoint const& ep + , char const* p, int size) + { +// UTP_LOGV("incoming packet size:%d\n", size); + + if (size < int(sizeof(utp_header))) return false; + + utp_header const* ph = (utp_header*)p; + +// UTP_LOGV("incoming packet version:%d\n", int(ph->get_version())); + + if (ph->get_version() != 1) return false; + + const ptime receive_time = time_now_hires(); + + // parse out connection ID and look for existing + // connections. If found, forward to the utp_stream. + boost::uint16_t id = ph->connection_id; + + // first test to see if it's the same socket as last time + // in most cases it is + if (m_last_socket + && utp_match(m_last_socket, ep, id)) + { + return utp_incoming_packet(m_last_socket, p, size, ep, receive_time); + } + + std::pair r = + m_utp_sockets.equal_range(id); + + for (; r.first != r.second; ++r.first) + { + if (!utp_match(r.first->second, ep, id)) continue; + bool ret = utp_incoming_packet(r.first->second, p, size, ep, receive_time); + if (ret) m_last_socket = r.first->second; + return ret; + } + +// UTP_LOGV("incoming packet id:%d source:%s\n", id, print_endpoint(ep).c_str()); + + if (!m_sett.enable_incoming_utp) + return false; + + // if not found, see if it's a SYN packet, if it is, + // create a new utp_stream + if (ph->get_type() == ST_SYN) + { + // possible SYN flood. Just ignore + if (int(m_utp_sockets.size()) > m_sett.connections_limit * 2) + return false; + +// UTP_LOGV("not found, new connection id:%d\n", m_new_connection); + + boost::shared_ptr c(new (std::nothrow) socket_type(m_sock.get_io_service())); + if (!c) return false; + + TORRENT_ASSERT(m_new_connection == -1); + // create the new socket with this ID + m_new_connection = id; + + instantiate_connection(m_sock.get_io_service(), proxy_settings(), *c, 0, this); + utp_stream* str = c->get(); + TORRENT_ASSERT(str); + int link_mtu, utp_mtu; + mtu_for_dest(ep.address(), link_mtu, utp_mtu); + utp_init_mtu(str->get_impl(), link_mtu, utp_mtu); + bool ret = utp_incoming_packet(str->get_impl(), p, size, ep, receive_time); + if (!ret) return false; + m_cb(c); + // the connection most likely changed its connection ID here + // we need to move it to the correct ID + return true; + } + + if (ph->get_type() == ST_RESET) return false; + + // #error send reset + + return false; + } + + void utp_socket_manager::subscribe_writable(utp_socket_impl* s) + { + TORRENT_ASSERT(std::find(m_stalled_sockets.begin(), m_stalled_sockets.end() + , s) == m_stalled_sockets.end()); + m_stalled_sockets.push_back(s); + } + + void utp_socket_manager::writable() + { + std::vector stalled_sockets; + m_stalled_sockets.swap(stalled_sockets); + for (std::vector::iterator i = stalled_sockets.begin() + , end(stalled_sockets.end()); i != end; ++i) + { + utp_socket_impl* s = *i; + utp_writable(s); + } + } + + void utp_socket_manager::socket_drained() + { + // flush all deferred acks + + std::vector deferred_acks; + m_deferred_acks.swap(deferred_acks); + for (std::vector::iterator i = deferred_acks.begin() + , end(deferred_acks.end()); i != end; ++i) + { + utp_socket_impl* s = *i; + utp_send_ack(s); + } + + std::vector drained_event; + m_drained_event.swap(drained_event); + for (std::vector::iterator i = drained_event.begin() + , end(drained_event.end()); i != end; ++i) + { + utp_socket_impl* s = *i; + utp_socket_drained(s); + } + } + + void utp_socket_manager::defer_ack(utp_socket_impl* s) + { + TORRENT_ASSERT(std::find(m_deferred_acks.begin(), m_deferred_acks.end(), s) + == m_deferred_acks.end()); + m_deferred_acks.push_back(s); + } + + void utp_socket_manager::subscribe_drained(utp_socket_impl* s) + { + TORRENT_ASSERT(std::find(m_drained_event.begin(), m_drained_event.end(), s) + == m_drained_event.end()); + m_drained_event.push_back(s); + } + + void utp_socket_manager::remove_socket(boost::uint16_t id) + { + socket_map_t::iterator i = m_utp_sockets.find(id); + if (i == m_utp_sockets.end()) return; + delete_utp_impl(i->second); + if (m_last_socket == i->second) m_last_socket = 0; + m_utp_sockets.erase(i); + } + + void utp_socket_manager::set_sock_buf(int size) + { + if (size < m_sock_buf_size) return; + m_sock.set_buf_size(size); + error_code ec; + // add more socket buffer storage on the lower level socket + // to avoid dropping packets because of a full receive buffer + // while processing a packet + + // only update the buffer size if it's bigger than + // what we already have + datagram_socket::receive_buffer_size recv_buf_size_opt; + m_sock.get_option(recv_buf_size_opt, ec); + if (recv_buf_size_opt.value() < size * 10) + { + m_sock.set_option(datagram_socket::receive_buffer_size(size * 10), ec); + m_sock.set_option(datagram_socket::send_buffer_size(size * 3), ec); + } + m_sock_buf_size = size; + } + + void utp_socket_manager::inc_stats_counter(int counter) + { + TORRENT_ASSERT(counter >= 0); + TORRENT_ASSERT(counter < num_counters); + ++m_counters[counter]; + } + + utp_socket_impl* utp_socket_manager::new_utp_socket(utp_stream* str) + { + boost::uint16_t send_id = 0; + boost::uint16_t recv_id = 0; + if (m_new_connection != -1) + { + send_id = m_new_connection; + recv_id = m_new_connection + 1; + m_new_connection = -1; + } + else + { + send_id = random() & 0xffff; + recv_id = send_id - 1; + } + utp_socket_impl* impl = construct_utp_impl(recv_id, send_id, str, this); + m_utp_sockets.insert(std::make_pair(recv_id, impl)); + return impl; + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/utp_stream.cpp b/apps/Launcher/ext/libtorrent/src/utp_stream.cpp new file mode 100644 index 0000000000..52bcb01f6b --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/utp_stream.cpp @@ -0,0 +1,3525 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" +#include "libtorrent/utp_stream.hpp" +#include "libtorrent/sliding_average.hpp" +#include "libtorrent/utp_socket_manager.hpp" +#include "libtorrent/alloca.hpp" +#include "libtorrent/timestamp_history.hpp" +#include "libtorrent/error.hpp" +#include "libtorrent/random.hpp" +#include "libtorrent/invariant_check.hpp" +#include +#include + +#define TORRENT_UTP_LOG 0 +#define TORRENT_VERBOSE_UTP_LOG 0 +#define TORRENT_UT_SEQ 1 + +#if TORRENT_UTP_LOG +#include +#include "libtorrent/socket_io.hpp" +#endif + +namespace libtorrent { + +#if TORRENT_UTP_LOG + +char const* packet_type_names[] = { "ST_DATA", "ST_FIN", "ST_STATE", "ST_RESET", "ST_SYN" }; +char const* socket_state_names[] = { "NONE", "SYN_SENT", "CONNECTED", "FIN_SENT", "ERROR", "DELETE" }; + +static struct utp_logger +{ + FILE* utp_log_file; + mutex utp_log_mutex; + + utp_logger() : utp_log_file(0) + { + utp_log_file = fopen("utp.log", "w+"); + } + ~utp_logger() + { + if (utp_log_file) fclose(utp_log_file); + } +} log_file_holder; + +void utp_log(char const* fmt, ...) +{ + mutex::scoped_lock lock(log_file_holder.utp_log_mutex); + static ptime start = time_now_hires(); + fprintf(log_file_holder.utp_log_file, "[%012" PRId64 "] ", total_microseconds(time_now_hires() - start)); + va_list l; + va_start(l, fmt); + vfprintf(log_file_holder.utp_log_file, fmt, l); + va_end(l); +} + +#define UTP_LOG utp_log +#if TORRENT_VERBOSE_UTP_LOG +#define UTP_LOGV utp_log +#else +#define UTP_LOGV if (false) printf +#endif + +#else + +#define UTP_LOG if (false) printf +#define UTP_LOGV if (false) printf + +#endif + +enum +{ + ACK_MASK = 0xffff, + + // the number of packets that'll fit in the reorder buffer + max_packets_reorder = 512, + + // if a packet receives more than this number of + // duplicate acks, we'll trigger a fast re-send + dup_ack_limit = 3, + + // the max number of packets to fast-resend per + // selective ack message + // only re-sending a single packet per sack + // appears to improve performance by making it + // less likely to loose the re-sent packet. Because + // when that happens, we must time-out in order + // to continue, which takes a long time. + sack_resend_limit = 1 +}; + +// compare if lhs is less than rhs, taking wrapping +// into account. if lhs is close to UINT_MAX and rhs +// is close to 0, lhs is assumed to have wrapped and +// considered smaller +TORRENT_EXTRA_EXPORT bool compare_less_wrap(boost::uint32_t lhs, boost::uint32_t rhs, boost::uint32_t mask) +{ + // distance walking from lhs to rhs, downwards + boost::uint32_t dist_down = (lhs - rhs) & mask; + // distance walking from lhs to rhs, upwards + boost::uint32_t dist_up = (rhs - lhs) & mask; + + // if the distance walking up is shorter, lhs + // is less than rhs. If the distance walking down + // is shorter, then rhs is less than lhs + return dist_up < dist_down; +} + +// used for out-of-order incoming packets +// as well as sent packets that are waiting to be ACKed +struct packet +{ + // the last time this packet was sent + ptime send_time; + + // the number of bytes actually allocated in 'buf' + boost::uint16_t allocated; + + // the size of the buffer 'buf' points to + boost::uint16_t size; + + // this is the offset to the payload inside the buffer + // this is also used as a cursor to describe where the + // next payload that hasn't been consumed yet starts + boost::uint16_t header_size; + + // the number of times this packet has been sent + boost::uint8_t num_transmissions:6; + + // true if we need to send this packet again. All + // outstanding packets are marked as needing to be + // resent on timeouts + bool need_resend:1; + + // this is set to true for packets that were + // sent with the DF bit set (Don't Fragment) + bool mtu_probe:1; + +#ifdef TORRENT_DEBUG + int num_fast_resend; +#endif + + // the actual packet buffer + boost::uint8_t buf[1]; +}; + +// since the uTP socket state may be needed after the +// utp_stream is closed, it's kept in a separate struct +// whose lifetime is not tied to the lifetime of utp_stream + +// the utp socket is closely modelled after the asio async +// operations and handler model. For writing to the socket, +// the client provides a list of buffers (for gather/writev +// style of I/O) and whenever the socket can write another +// packet to the stream, it picks up data from these buffers. +// When all of the data has been written, or enough time has +// passed since we first started writing, the write handler +// is called and the write buffer is reset. This means that +// we're not writing anything at all while waiting for the +// client to re-issue a write request. + +// reading is a little bit more complicated, since we must +// be able to receive data even when the user doesn't have +// an outstanding read operation on the socket. When the user +// does however, we want to receive data directly into the +// user's buffer instead of first copying it into our receive +// buffer. This is why the receive case is more complicated. +// There are two receive buffers. One provided by the user, +// which when present is always used. The other one is used +// when the user doesn't have an outstanding read request, +// and hence hasn't provided any buffer space to receive into. + +// the user provided read buffer is called "m_read_buffer" and +// its size is "m_read_buffer_size". The buffer we spill over +// into when the user provided buffer is full or when there +// is none, is "m_receive_buffer" and "m_receive_buffer_size" +// respectively. + +// in order to know when to trigger the read and write handlers +// there are two counters, m_read and m_written, which count +// the number of bytes we've stuffed into the user provided +// read buffer or written to the stream from the write buffer. +// These are used to trigger the handlers if we're written a +// large number of bytes. It's also triggered if we're filled +// the whole read buffer, or written the entire write buffer. +// The last way the handlers can be triggered is if we're read +// or written some, and enough time has elapsed since then. + +// when we receive data into m_receive_buffer (i.e. the buffer +// used when there's no user provided one) is stored as a +// number of heap allocated packets. This is just because it's +// simple to reuse the data structured and it provides all the +// functionality needed for this buffer. + +struct utp_socket_impl +{ + utp_socket_impl(boost::uint16_t recv_id, boost::uint16_t send_id + , void* userdata, utp_socket_manager* sm) + : m_sm(sm) + , m_userdata(userdata) + , m_nagle_packet(NULL) + , m_read_handler(0) + , m_write_handler(0) + , m_connect_handler(0) + , m_remote_address() + , m_timeout(time_now_hires() + milliseconds(m_sm->connect_timeout())) + , m_last_history_step(time_now_hires()) + , m_cwnd(TORRENT_ETHERNET_MTU << 16) + , m_ssthres(0) + , m_buffered_incoming_bytes(0) + , m_reply_micro(0) + , m_adv_wnd(TORRENT_ETHERNET_MTU) + , m_bytes_in_flight(0) + , m_read(0) + , m_write_buffer_size(0) + , m_written(0) + , m_receive_buffer_size(0) + , m_read_buffer_size(0) + , m_in_buf_size(1024 * 1024) + , m_in_packets(0) + , m_out_packets(0) + , m_send_delay(0) + , m_recv_delay(0) + , m_port(0) + , m_send_id(send_id) + , m_recv_id(recv_id) + , m_ack_nr(0) + , m_seq_nr(0) + , m_acked_seq_nr(0) + , m_fast_resend_seq_nr(0) + , m_eof_seq_nr(0) + , m_loss_seq_nr(0) + , m_mtu(TORRENT_ETHERNET_MTU - TORRENT_IPV4_HEADER - TORRENT_UDP_HEADER - 8 - 24 - 36) + , m_mtu_floor(TORRENT_INET_MIN_MTU - TORRENT_IPV4_HEADER - TORRENT_UDP_HEADER) + , m_mtu_ceiling(TORRENT_ETHERNET_MTU - TORRENT_IPV4_HEADER - TORRENT_UDP_HEADER) + , m_mtu_seq(0) + , m_duplicate_acks(0) + , m_num_timeouts(0) + , m_delay_sample_idx(0) + , m_state(UTP_STATE_NONE) + , m_eof(false) + , m_attached(true) + , m_nagle(true) + , m_slow_start(true) + , m_cwnd_full(false) + , m_deferred_ack(false) + , m_subscribe_drained(false) + , m_stalled(false) + { + TORRENT_ASSERT(m_userdata); + for (int i = 0; i != num_delay_hist; ++i) + m_delay_sample_hist[i] = (std::numeric_limits::max)(); + } + + ~utp_socket_impl(); + + void tick(ptime const& now); + void init_mtu(int link_mtu, int utp_mtu); + bool incoming_packet(boost::uint8_t const* buf, int size + , udp::endpoint const& ep, ptime receive_time); + void writable(); + + bool should_delete() const; + tcp::endpoint remote_endpoint(error_code& ec) const + { + if (m_state == UTP_STATE_NONE) + ec = asio::error::not_connected; + else + TORRENT_ASSERT(m_remote_address != address_v4::any()); + return tcp::endpoint(m_remote_address, m_port); + } + std::size_t available() const; + // returns true if there were handlers cancelled + // if it returns false, we can detach immediately + bool destroy(); + void detach(); + void send_syn(); + void send_fin(); + + void subscribe_drained(); + void defer_ack(); + void remove_sack_header(packet* p); + + enum packet_flags_t { pkt_ack = 1, pkt_fin = 2 }; + bool send_pkt(int flags = 0); + bool resend_packet(packet* p, bool fast_resend = false); + void send_reset(utp_header* ph); + void parse_sack(boost::uint16_t packet_ack, boost::uint8_t const* ptr, int size, int* acked_bytes + , ptime const now, boost::uint32_t& min_rtt); + void write_payload(boost::uint8_t* ptr, int size); + void maybe_inc_acked_seq_nr(); + void ack_packet(packet* p, ptime const& receive_time + , boost::uint32_t& min_rtt, boost::uint16_t seq_nr); + void write_sack(boost::uint8_t* buf, int size) const; + void incoming(boost::uint8_t const* buf, int size, packet* p, ptime now); + void do_ledbat(int acked_bytes, int delay, int in_flight, ptime const now); + int packet_timeout() const; + bool test_socket_state(); + void maybe_trigger_receive_callback(); + void maybe_trigger_send_callback(); + bool cancel_handlers(error_code const& ec, bool kill); + bool consume_incoming_data( + utp_header const* ph, boost::uint8_t const* ptr, int payload_size, ptime now); + void update_mtu_limits(); + void experienced_loss(int seq_nr); + + void check_receive_buffers() const; + +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + + utp_socket_manager* m_sm; + + // userdata pointer passed along + // with any callback. This is initialized to 0 + // then set to point to the utp_stream when + // hooked up, and then reset to 0 once the utp_stream + // detaches. This is used to know whether or not + // the socket impl is still attached to a utp_stream + // object. When it isn't, we'll never be able to + // signal anything back to the client, and in case + // of errors, we just have to delete ourselves + // i.e. transition to the UTP_STATE_DELETED state + void* m_userdata; + + // This is a platform-independent replacement + // for the regular iovec type in posix. Since + // it's not used in any system call, we might as + // well define our own type instead of wrapping + // the system's type. + struct iovec_t + { + iovec_t(void* b, size_t l): buf(b), len(l) {} + void* buf; + size_t len; + }; + + // if there's currently an async read or write + // operation in progress, these buffers are initialized + // and used, otherwise any bytes received are stuck in + // m_receive_buffer until another read is made + // as we flush from the write buffer, individual iovecs + // are updated to only refer to unflushed portions of the + // buffers. Buffers that empty are erased from the vector. + std::vector m_write_buffer; + + // if this is non NULL, it's a packet. This packet was held off because + // of NAGLE. We couldn't send it immediately. It's left + // here to accrue more bytes before we send it. + packet* m_nagle_packet; + + // the user provided read buffer. If this has a size greater + // than 0, we'll always prefer using it over putting received + // data in the m_receive_buffer. As data is stored in the + // read buffer, the iovec_t elements are adjusted to only + // refer to the unwritten portions of the buffers, and the + // ones that fill up are erased from the vector + std::vector m_read_buffer; + + // packets we've received without a read operation + // active. Store them here until the client triggers + // an async_read_some + std::vector m_receive_buffer; + + // this is the error on this socket. If m_state is + // set to UTP_STATE_ERROR_WAIT, this error should be + // forwarded to the client as soon as we have a new + // async operation initiated + error_code m_error; + + // these are the callbacks made into the utp_stream object + // on read/write/connect events + utp_stream::handler_t m_read_handler; + utp_stream::handler_t m_write_handler; + utp_stream::connect_handler_t m_connect_handler; + + // the address of the remote endpoint + address m_remote_address; + + // the local address + address m_local_address; + + // the send and receive buffers + // maps packet sequence numbers + packet_buffer m_inbuf; + packet_buffer m_outbuf; + + // the time when the last packet we sent times out. Including re-sends. + // if we ever end up not having sent anything in one second ( + // or one mean rtt + 2 average deviations, whichever is greater) + // we set our cwnd to 1 MSS. This condition can happen either because + // a packet has timed out and needs to be resent or because our + // cwnd is set to less than one MSS during congestion control. + // it can also happen if the other end sends an advertized window + // size less than one MSS. + ptime m_timeout; + + // the last time we stepped the timestamp history + ptime m_last_history_step; + + // the max number of bytes in-flight. This is a fixed point + // value, to get the true number of bytes, shift right 16 bits + // the value is always >= 0, but the calculations performed on + // it in do_ledbat() are signed. + boost::int64_t m_cwnd; + + timestamp_history m_delay_hist; + timestamp_history m_their_delay_hist; + + // the slow-start threshold. This is the congestion window size (m_cwnd) + // in bytes the last time we left slow-start mode. This is used as a + // threshold to leave slow-start earlier next time, to avoid packet-loss + boost::int32_t m_ssthres; + + // the number of bytes we have buffered in m_inbuf + boost::int32_t m_buffered_incoming_bytes; + + // the timestamp diff in the last packet received + // this is what we'll send back + boost::uint32_t m_reply_micro; + + // this is the advertized receive window the other end sent + // we'll never have more un-acked bytes in flight + // if this ever gets set to zero, we'll try one packet every + // second until the window opens up again + boost::uint32_t m_adv_wnd; + + // the number of un-acked bytes we have sent + boost::int32_t m_bytes_in_flight; + + // the number of bytes read into the user provided + // buffer. If this grows too big, we'll trigger the + // read handler. + boost::int32_t m_read; + + // the sum of the lengths of all iovec in m_write_buffer + boost::int32_t m_write_buffer_size; + + // the number of bytes already written to packets + // from m_write_buffer + boost::int32_t m_written; + + // the sum of all packets stored in m_receive_buffer + boost::int32_t m_receive_buffer_size; + + // the sum of all buffers in m_read_buffer + boost::int32_t m_read_buffer_size; + + // max number of bytes to allocate for receive buffer + boost::int32_t m_in_buf_size; + + // this holds the 3 last delay measurements, + // these are the actual corrected delay measurements. + // the lowest of the 3 last ones is used in the congestion + // controller. This is to not completely close the cwnd + // by a single outlier. + enum { num_delay_hist = 3 }; + boost::uint32_t m_delay_sample_hist[num_delay_hist]; + + // counters + boost::uint32_t m_in_packets; + boost::uint32_t m_out_packets; + + // the last send delay sample + boost::int32_t m_send_delay; + // the last receive delay sample + boost::int32_t m_recv_delay; + + // average RTT + sliding_average<16> m_rtt; + + // port of destination endpoint + boost::uint16_t m_port; + + boost::uint16_t m_send_id; + boost::uint16_t m_recv_id; + + // this is the ack we're sending back. We have + // received all packets up to this sequence number + boost::uint16_t m_ack_nr; + + // the sequence number of the next packet + // we'll send + boost::uint16_t m_seq_nr; + + // this is the sequence number of the packet that + // everything has been ACKed up to. Everything we've + // sent up to this point has been received by the other + // end. + boost::uint16_t m_acked_seq_nr; + + // each packet gets one chance of "fast resend". i.e. + // if we have multiple duplicate acks, we may send a + // packet immediately, if m_fast_resend_seq_nr is set + // to that packet's sequence number + boost::uint16_t m_fast_resend_seq_nr; + + // this is the sequence number of the FIN packet + // we've received. This sequence number is only + // valid if m_eof is true. We should not accept + // any packets beyond this sequence number from the + // other end + boost::uint16_t m_eof_seq_nr; + + // this is the lowest sequence number that, when lost, + // will cause the window size to be cut in half + boost::uint16_t m_loss_seq_nr; + + // the max number of bytes we can send in a packet + // including the header + boost::uint16_t m_mtu; + + // the floor is the largest packet that we have + // been able to get through without fragmentation + boost::uint16_t m_mtu_floor; + + // the ceiling is the largest packet that we might + // be able to get through without fragmentation. + // i.e. ceiling +1 is very likely to not get through + // or we have in fact experienced a drop or ICMP + // message indicating that it is + boost::uint16_t m_mtu_ceiling; + + // the sequence number of the probe in-flight + // this is 0 if there is no probe in flight + boost::uint16_t m_mtu_seq; + + // this is a counter of how many times the current m_acked_seq_nr + // has been ACKed. If it's ACKed more than 3 times, we assume the + // packet with the next sequence number has been lost, and we trigger + // a re-send. Ovbiously an ACK only counts as a duplicate as long as + // we have outstanding packets following it. + boost::uint8_t m_duplicate_acks; + + // the number of packet timeouts we've seen in a row + // this affects the packet timeout time + boost::uint8_t m_num_timeouts; + + enum state_t { + // not yet connected + UTP_STATE_NONE, + // sent a syn packet, not received any acks + UTP_STATE_SYN_SENT, + // syn-ack received and in normal operation + // of sending and receiving data + UTP_STATE_CONNECTED, + // fin sent, but all packets up to the fin packet + // have not yet been acked. We might still be waiting + // for a FIN from the other end + UTP_STATE_FIN_SENT, + + // ====== states beyond this point ===== + // === are considered closing states === + // === and will cause the socket to ==== + // ============ be deleted ============= + + // the socket has been gracefully disconnected + // and is waiting for the client to make a + // socket call so that we can communicate this + // fact and actually delete all the state, or + // there is an error on this socket and we're + // waiting to communicate this to the client in + // a callback. The error in either case is stored + // in m_error. If the socket has gracefully shut + // down, the error is error::eof. + UTP_STATE_ERROR_WAIT, + + // there are no more references to this socket + // and we can delete it + UTP_STATE_DELETE + }; + + // this is the cursor into m_delay_sample_hist + boost::uint8_t m_delay_sample_idx:2; + + // the state the socket is in + boost::uint8_t m_state:3; + + // this is set to true when we receive a fin + bool m_eof:1; + + // is this socket state attached to a user space socket? + bool m_attached:1; + + // this is true if nagle is enabled (which it is by default) + bool m_nagle:1; + + // this is true while the socket is in slow start mode. It's + // only in slow-start during the start-up phase. Slow start + // (contrary to what its name suggest) means that we're growing + // the congestion window (cwnd) exponetially rather than linearly. + // this is done at startup of a socket in order to find its + // link capacity faster. This behaves similar to TCP slow start + bool m_slow_start:1; + + // this is true as long as we have as many packets in + // flight as allowed by the congestion window (cwnd) + bool m_cwnd_full:1; + + // this is set to true when this socket has added itself to + // the utp socket manager's list of deferred acks. Once the + // burst of incoming UDP packets is all drained, the utp socket + // manager will send acks for all sockets on this list. + bool m_deferred_ack:1; + + // this is true if this socket has subscribed to be notified + // when this receive round is done + bool m_subscribe_drained:1; + + // if this socket tries to send a packet via the utp socket + // manager, and it fails with EWOULDBLOCK, the socket + // is stalled and this is set. It's also added to a list + // of sockets in the utp_socket_manager to be notified of + // the socket being writable again + bool m_stalled:1; +}; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING +int socket_impl_size() { return sizeof(utp_socket_impl); } +#endif + +utp_socket_impl* construct_utp_impl(boost::uint16_t recv_id + , boost::uint16_t send_id, void* userdata + , utp_socket_manager* sm) +{ + return new utp_socket_impl(recv_id, send_id, userdata, sm); +} + +void detach_utp_impl(utp_socket_impl* s) +{ + s->detach(); +} + +void delete_utp_impl(utp_socket_impl* s) +{ + delete s; +} + +bool should_delete(utp_socket_impl* s) +{ + return s->should_delete(); +} + +void tick_utp_impl(utp_socket_impl* s, ptime const& now) +{ + s->tick(now); +} + +void utp_init_mtu(utp_socket_impl* s, int link_mtu, int utp_mtu) +{ + s->init_mtu(link_mtu, utp_mtu); +} + +bool utp_incoming_packet(utp_socket_impl* s, char const* p + , int size, udp::endpoint const& ep, ptime receive_time) +{ + return s->incoming_packet((boost::uint8_t const*)p, size, ep, receive_time); +} + +bool utp_match(utp_socket_impl* s, udp::endpoint const& ep, boost::uint16_t id) +{ + return s->m_remote_address == ep.address() + && s->m_port == ep.port() + && s->m_recv_id == id; +} + +udp::endpoint utp_remote_endpoint(utp_socket_impl* s) +{ + return udp::endpoint(s->m_remote_address, s->m_port); +} + +boost::uint16_t utp_receive_id(utp_socket_impl* s) +{ + return s->m_recv_id; +} + +void utp_writable(utp_socket_impl* s) +{ + TORRENT_ASSERT(s->m_stalled); + s->m_stalled = false; + s->writable(); +} + +void utp_send_ack(utp_socket_impl* s) +{ + TORRENT_ASSERT(s->m_deferred_ack); + s->m_deferred_ack = false; + s->send_pkt(utp_socket_impl::pkt_ack); +} + +void utp_socket_drained(utp_socket_impl* s) +{ + s->m_subscribe_drained = false; + + // at this point, we know we won't receive any + // more packets this round. So, we may want to + // call the receive callback function to + // let the user consume it + + s->maybe_trigger_receive_callback(); + s->maybe_trigger_send_callback(); +} + +void utp_socket_impl::update_mtu_limits() +{ + INVARIANT_CHECK; + + TORRENT_ASSERT(m_mtu_floor <= m_mtu_ceiling); + m_mtu = (m_mtu_floor + m_mtu_ceiling) / 2; + + if ((m_cwnd >> 16) < m_mtu) m_cwnd = boost::int64_t(m_mtu) << 16; + + UTP_LOGV("%8p: updating MTU to: %d [%d, %d]\n" + , this, m_mtu, m_mtu_floor, m_mtu_ceiling); + + // clear the mtu probe sequence number since + // it was either dropped or acked + m_mtu_seq = 0; +} + +int utp_socket_state(utp_socket_impl const* s) +{ + return s->m_state; +} + +int utp_stream::send_delay() const +{ + return m_impl ? m_impl->m_send_delay : 0; +} + +int utp_stream::recv_delay() const +{ + return m_impl ? m_impl->m_recv_delay : 0; +} + +utp_stream::utp_stream(asio::io_service& io_service) + : m_io_service(io_service) + , m_impl(0) + , m_open(false) +{ +} + +utp_socket_impl* utp_stream::get_impl() +{ + return m_impl; +} + +void utp_stream::close() +{ + if (!m_impl) return; + if (!m_impl->destroy()) + { + if (!m_impl) return; + detach_utp_impl(m_impl); + m_impl = 0; + } +} + +std::size_t utp_stream::available() const +{ + return m_impl->available(); +} + +utp_stream::endpoint_type utp_stream::remote_endpoint(error_code& ec) const +{ + if (!m_impl) + { + ec = asio::error::not_connected; + return endpoint_type(); + } + return m_impl->remote_endpoint(ec); +} + +utp_stream::endpoint_type utp_stream::local_endpoint(error_code& ec) const +{ + if (m_impl == 0 || m_impl->m_sm == 0) + { + ec = asio::error::not_connected; + return endpoint_type(); + } + return tcp::endpoint(m_impl->m_local_address, m_impl->m_sm->local_port(ec)); +} + +utp_stream::~utp_stream() +{ + if (m_impl) + { + UTP_LOGV("%8p: utp_stream destructed\n", m_impl); + m_impl->destroy(); + detach_utp_impl(m_impl); + } + + m_impl = 0; +} + +void utp_stream::set_impl(utp_socket_impl* impl) +{ + TORRENT_ASSERT(m_impl == 0); + TORRENT_ASSERT(!m_open); + m_impl = impl; + m_open = true; +} + +int utp_stream::read_buffer_size() const +{ + TORRENT_ASSERT(m_impl); + return m_impl->m_receive_buffer_size; +} + +void utp_stream::on_read(void* self, size_t bytes_transferred, error_code const& ec, bool kill) +{ + utp_stream* s = (utp_stream*)self; + + UTP_LOGV("%8p: calling read handler read:%d ec:%s kill:%d\n", s->m_impl + , int(bytes_transferred), ec.message().c_str(), kill); + + TORRENT_ASSERT(s->m_read_handler); + TORRENT_ASSERT(bytes_transferred > 0 || ec); + s->m_io_service.post(boost::bind(s->m_read_handler, ec, bytes_transferred)); + s->m_read_handler.clear(); + if (kill && s->m_impl) + { + detach_utp_impl(s->m_impl); + s->m_impl = 0; + } +} + +void utp_stream::on_write(void* self, size_t bytes_transferred, error_code const& ec, bool kill) +{ + utp_stream* s = (utp_stream*)self; + + UTP_LOGV("%8p: calling write handler written:%d ec:%s kill:%d\n", s->m_impl + , int(bytes_transferred), ec.message().c_str(), kill); + + TORRENT_ASSERT(s->m_write_handler); + TORRENT_ASSERT(bytes_transferred > 0 || ec); + s->m_io_service.post(boost::bind(s->m_write_handler, ec, bytes_transferred)); + s->m_write_handler.clear(); + if (kill && s->m_impl) + { + detach_utp_impl(s->m_impl); + s->m_impl = 0; + } +} + +void utp_stream::on_connect(void* self, error_code const& ec, bool kill) +{ + utp_stream* s = (utp_stream*)self; + TORRENT_ASSERT(s); + + UTP_LOGV("%8p: calling connect handler ec:%s kill:%d\n" + , s->m_impl, ec.message().c_str(), kill); + + TORRENT_ASSERT(s->m_connect_handler); + s->m_io_service.post(boost::bind(s->m_connect_handler, ec)); + s->m_connect_handler.clear(); + if (kill && s->m_impl) + { + detach_utp_impl(s->m_impl); + s->m_impl = 0; + } +} + +void utp_stream::add_read_buffer(void* buf, size_t len) +{ + TORRENT_ASSERT(m_impl); + TORRENT_ASSERT(len < INT_MAX); + TORRENT_ASSERT(len > 0); + TORRENT_ASSERT(buf); + m_impl->m_read_buffer.push_back(utp_socket_impl::iovec_t(buf, len)); + m_impl->m_read_buffer_size += len; + + UTP_LOGV("%8p: add_read_buffer %d bytes\n", m_impl, int(len)); +} + +// this is the wrapper to add a user provided write buffer to the +// utp_socket_impl. It makes sure the m_write_buffer_size is kept +// up to date +void utp_stream::add_write_buffer(void const* buf, size_t len) +{ + TORRENT_ASSERT(m_impl); + TORRENT_ASSERT(len < INT_MAX); + TORRENT_ASSERT(len > 0); + TORRENT_ASSERT(buf); + +#ifdef TORRENT_DEBUG + int write_buffer_size = 0; + for (std::vector::iterator i = m_impl->m_write_buffer.begin() + , end(m_impl->m_write_buffer.end()); i != end; ++i) + { + write_buffer_size += i->len; + } + TORRENT_ASSERT(m_impl->m_write_buffer_size == write_buffer_size); +#endif + + m_impl->m_write_buffer.push_back(utp_socket_impl::iovec_t((void*)buf, len)); + m_impl->m_write_buffer_size += len; + +#ifdef TORRENT_DEBUG + write_buffer_size = 0; + for (std::vector::iterator i = m_impl->m_write_buffer.begin() + , end(m_impl->m_write_buffer.end()); i != end; ++i) + { + write_buffer_size += i->len; + } + TORRENT_ASSERT(m_impl->m_write_buffer_size == write_buffer_size); +#endif + + UTP_LOGV("%8p: add_write_buffer %d bytes\n", m_impl, int(len)); +} + +// this is called when all user provided read buffers have been added +// and it's time to execute the async operation. The first thing we +// do is to copy any data stored in m_receive_buffer into the user +// provided buffer. This might be enough to in turn trigger the read +// handler immediately. +void utp_stream::set_read_handler(handler_t h) +{ + TORRENT_ASSERT(m_impl->m_userdata); + m_impl->m_read_handler = h; + if (m_impl->test_socket_state()) return; + + UTP_LOGV("%8p: new read handler. %d bytes in buffer\n" + , m_impl, m_impl->m_receive_buffer_size); + + TORRENT_ASSERT(m_impl->m_read_buffer_size > 0); + + // so, the client wants to read. If we already + // have some data in the read buffer, move it into the + // client's buffer right away + + m_impl->m_read += read_some(false); + m_impl->maybe_trigger_receive_callback(); +} + +size_t utp_stream::read_some(bool clear_buffers) +{ + if (m_impl->m_receive_buffer_size == 0) + { + if (clear_buffers) + { + m_impl->m_read_buffer_size = 0; + m_impl->m_read_buffer.clear(); + } + return 0; + } + + std::vector::iterator target = m_impl->m_read_buffer.begin(); + + size_t ret = 0; + + int pop_packets = 0; + for (std::vector::iterator i = m_impl->m_receive_buffer.begin() + , end(m_impl->m_receive_buffer.end()); i != end;) + { + if (target == m_impl->m_read_buffer.end()) + { + UTP_LOGV(" No more target buffers: %d bytes left in buffer\n" + , m_impl->m_receive_buffer_size); + TORRENT_ASSERT(m_impl->m_read_buffer.empty()); + break; + } + + m_impl->check_receive_buffers(); + + packet* p = *i; + int to_copy = (std::min)(p->size - p->header_size, int(target->len)); + TORRENT_ASSERT(to_copy >= 0); + memcpy(target->buf, p->buf + p->header_size, to_copy); + ret += to_copy; + target->buf = ((char*)target->buf) + to_copy; + TORRENT_ASSERT(int(target->len) >= to_copy); + target->len -= to_copy; + m_impl->m_receive_buffer_size -= to_copy; + TORRENT_ASSERT(m_impl->m_read_buffer_size >= to_copy); + m_impl->m_read_buffer_size -= to_copy; + p->header_size += to_copy; + if (target->len == 0) target = m_impl->m_read_buffer.erase(target); + + m_impl->check_receive_buffers(); + + TORRENT_ASSERT(m_impl->m_receive_buffer_size >= 0); + + // Consumed entire packet + if (p->header_size == p->size) + { + free(p); + ++pop_packets; + *i = 0; + ++i; + } + + if (m_impl->m_receive_buffer_size == 0) + { + UTP_LOGV(" Didn't fill entire target: %d bytes left in buffer\n" + , m_impl->m_receive_buffer_size); + break; + } + } + // remove the packets from the receive_buffer that we already copied over + // and freed + m_impl->m_receive_buffer.erase(m_impl->m_receive_buffer.begin() + , m_impl->m_receive_buffer.begin() + pop_packets); + // we exited either because we ran out of bytes to copy + // or because we ran out of space to copy the bytes to + TORRENT_ASSERT(m_impl->m_receive_buffer_size == 0 + || m_impl->m_read_buffer.empty()); + + UTP_LOGV("%8p: %d packets moved from buffer to user space (%d bytes)\n" + , m_impl, pop_packets, int(ret)); + + if (clear_buffers) + { + m_impl->m_read_buffer_size = 0; + m_impl->m_read_buffer.clear(); + } + TORRENT_ASSERT(ret > 0); + return ret; +} + +// this is called when all user provided write buffers have been +// added. Start trying to send packets with the payload immediately. +void utp_stream::set_write_handler(handler_t h) +{ + UTP_LOGV("%8p: new write handler. %d bytes to write\n" + , m_impl, m_impl->m_write_buffer_size); + + TORRENT_ASSERT(m_impl->m_write_buffer_size > 0); + + TORRENT_ASSERT(m_impl->m_userdata); + m_impl->m_write_handler = h; + m_impl->m_written = 0; + if (m_impl->test_socket_state()) return; + + // try to write. send_pkt returns false if there's + // no more payload to send or if the congestion window + // is full and we can't send more packets right now + while (m_impl->send_pkt()); + + // if there was an error in send_pkt(), m_impl may be + // 0 at this point + if (m_impl) m_impl->maybe_trigger_send_callback(); +} + +void utp_stream::do_connect(tcp::endpoint const& ep, utp_stream::connect_handler_t handler) +{ + int link_mtu, utp_mtu; + m_impl->m_sm->mtu_for_dest(ep.address(), link_mtu, utp_mtu); + m_impl->init_mtu(link_mtu, utp_mtu); + TORRENT_ASSERT(m_impl->m_connect_handler == 0); + m_impl->m_remote_address = ep.address(); + m_impl->m_port = ep.port(); + m_impl->m_connect_handler = handler; + + error_code ec; + m_impl->m_local_address = m_impl->m_sm->local_endpoint(m_impl->m_remote_address, ec).address(); + + if (m_impl->test_socket_state()) return; + m_impl->send_syn(); +} + +// =========== utp_socket_impl ============ + +utp_socket_impl::~utp_socket_impl() +{ + INVARIANT_CHECK; + + TORRENT_ASSERT(!m_attached); + TORRENT_ASSERT(!m_deferred_ack); + + UTP_LOGV("%8p: destroying utp socket state\n", this); + + // free any buffers we're holding + for (boost::uint16_t i = m_inbuf.cursor(), end((m_inbuf.cursor() + + m_inbuf.capacity()) & ACK_MASK); + i != end; i = (i + 1) & ACK_MASK) + { + void* p = m_inbuf.remove(i); + free(p); + } + for (boost::uint16_t i = m_outbuf.cursor(), end((m_outbuf.cursor() + + m_outbuf.capacity()) & ACK_MASK); + i != end; i = (i + 1) & ACK_MASK) + { + void* p = m_outbuf.remove(i); + free(p); + } + + for (std::vector::iterator i = m_receive_buffer.begin() + , end = m_receive_buffer.end(); i != end; ++i) + { + free(*i); + } + + free(m_nagle_packet); + m_nagle_packet = NULL; +} + +bool utp_socket_impl::should_delete() const +{ + INVARIANT_CHECK; + + // if the socket state is not attached anymore we're free + // to delete it from the client's point of view. The other + // endpoint however might still need to be told that we're + // closing the socket. Only delete the state if we're not + // attached and we're in a state where the other end doesn't + // expect the socket to still be alive + // when m_stalled is true, it means the socket manager has a + // pointer to this socket, waiting for the UDP socket to + // become writable again. We have to wait for that, so that + // the pointer is removed from that queue. Otherwise we would + // leave a dangling pointer in the socket manager + bool ret = (m_state >= UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_NONE) + && !m_attached && !m_stalled; + + if (ret) + { + UTP_LOGV("%8p: should_delete() = true\n", this); + } + + return ret; +} + +void utp_socket_impl::maybe_trigger_receive_callback() +{ + INVARIANT_CHECK; + + // nothing has been read or there's no outstanding read operation + if (m_read == 0 || m_read_handler == 0) return; + + UTP_LOGV("%8p: calling read handler read:%d\n", this, m_read); + m_read_handler(m_userdata, m_read, m_error, false); + m_read_handler = 0; + m_read = 0; + m_read_buffer_size = 0; + m_read_buffer.clear(); +} + +void utp_socket_impl::maybe_trigger_send_callback() +{ + INVARIANT_CHECK; + + // nothing has been written or there's no outstanding write operation + if (m_written == 0 || m_write_handler == 0) return; + + UTP_LOGV("%8p: calling write handler written:%d\n", this, m_written); + + m_write_handler(m_userdata, m_written, m_error, false); + m_write_handler = 0; + m_written = 0; + m_write_buffer_size = 0; + m_write_buffer.clear(); +} + +bool utp_socket_impl::destroy() +{ + INVARIANT_CHECK; + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: destroy state:%s\n", this, socket_state_names[m_state]); +#endif + + if (m_userdata == 0) return false; + + if (m_state == UTP_STATE_CONNECTED) + send_fin(); + + bool cancelled = cancel_handlers(asio::error::operation_aborted, true); + + m_userdata = 0; + + m_read_buffer.clear(); + m_read_buffer_size = 0; + + m_write_buffer.clear(); + m_write_buffer_size = 0; + + if ((m_state == UTP_STATE_ERROR_WAIT + || m_state == UTP_STATE_NONE + || m_state == UTP_STATE_SYN_SENT) && cancelled) + { + m_state = UTP_STATE_DELETE; +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: state:%s\n", this, socket_state_names[m_state]); +#endif + } + + return cancelled; + + // #error our end is closing. Wait for everything to be acked +} + +void utp_socket_impl::detach() +{ + INVARIANT_CHECK; + + UTP_LOGV("%8p: detach()\n", this); + m_attached = false; +} + +void utp_socket_impl::send_syn() +{ + INVARIANT_CHECK; + + m_seq_nr = random() & 0xffff; + m_acked_seq_nr = (m_seq_nr - 1) & ACK_MASK; + m_loss_seq_nr = m_acked_seq_nr; + m_ack_nr = 0; + m_fast_resend_seq_nr = m_seq_nr; + + packet* p = (packet*)malloc(sizeof(packet) + sizeof(utp_header)); + p->size = sizeof(utp_header); + p->header_size = sizeof(utp_header); + p->num_transmissions = 0; +#ifdef TORRENT_DEBUG + p->num_fast_resend = 0; +#endif + p->need_resend = false; + utp_header* h = (utp_header*)p->buf; + h->type_ver = (ST_SYN << 4) | 1; + h->extension = 0; + // using recv_id here is intentional! This is an odd + // thing in uTP. The syn packet is sent with the connection + // ID that it expects to receive the syn ack on. All + // subsequent connection IDs will be this plus one. + h->connection_id = m_recv_id; + h->timestamp_difference_microseconds = m_reply_micro; + h->wnd_size = 0; + h->seq_nr = m_seq_nr; + h->ack_nr = 0; + + ptime now = time_now_hires(); + p->send_time = now; + h->timestamp_microseconds = boost::uint32_t(total_microseconds(now - min_time()) & 0xffffffff); + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: send_syn seq_nr:%d id:%d target:%s\n" + , this, int(m_seq_nr), int(m_recv_id) + , print_endpoint(udp::endpoint(m_remote_address, m_port)).c_str()); +#endif + + error_code ec; + m_sm->send_packet(udp::endpoint(m_remote_address, m_port), (char const*)h + , sizeof(utp_header), ec); + + if (ec == error::would_block || ec == error::try_again) + { +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: socket stalled\n", this); +#endif + if (!m_stalled) + { + m_stalled = true; + m_sm->subscribe_writable(this); + } + } + else if (ec) + { + free(p); + m_error = ec; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + return; + } + + if (!m_stalled) + ++p->num_transmissions; + + TORRENT_ASSERT(!m_outbuf.at(m_seq_nr)); + m_outbuf.insert(m_seq_nr, p); + TORRENT_ASSERT(h->seq_nr == m_seq_nr); + TORRENT_ASSERT(p->buf == (boost::uint8_t*)h); + + m_seq_nr = (m_seq_nr + 1) & ACK_MASK; + + TORRENT_ASSERT(!m_error); + m_state = UTP_STATE_SYN_SENT; +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: state:%s\n", this, socket_state_names[m_state]); +#endif +} + +// if a send ever failed with EWOULDBLOCK, we +// subscribe to the udp socket and will be +// signalled with this function. +void utp_socket_impl::writable() +{ +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: writable\n", this); +#endif + if (should_delete()) return; + + while(send_pkt()); + + maybe_trigger_send_callback(); +} + +void utp_socket_impl::send_fin() +{ + INVARIANT_CHECK; + + send_pkt(pkt_fin); + // unless there was an error, we're now + // in FIN-SENT state + if (!m_error) + m_state = UTP_STATE_FIN_SENT; + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: state:%s\n", this, socket_state_names[m_state]); +#endif +} + +void utp_socket_impl::send_reset(utp_header* ph) +{ + INVARIANT_CHECK; + + utp_header h; + h.type_ver = (ST_RESET << 4) | 1; + h.extension = 0; + h.connection_id = m_send_id; + h.timestamp_difference_microseconds = m_reply_micro; + h.wnd_size = 0; + h.seq_nr = random() & 0xffff; + h.ack_nr = ph->seq_nr; + ptime now = time_now_hires(); + h.timestamp_microseconds = boost::uint32_t(total_microseconds(now - min_time())); + + UTP_LOGV("%8p: send_reset seq_nr:%d id:%d ack_nr:%d\n" + , this, int(h.seq_nr), int(m_send_id), int(ph->seq_nr)); + + // ignore errors here + error_code ec; + m_sm->send_packet(udp::endpoint(m_remote_address, m_port), (char const*)&h, sizeof(h), ec); +} + +std::size_t utp_socket_impl::available() const +{ + return m_receive_buffer_size; +} + +void utp_socket_impl::parse_sack(boost::uint16_t packet_ack, boost::uint8_t const* ptr + , int size, int* acked_bytes, ptime const now, boost::uint32_t& min_rtt) +{ + INVARIANT_CHECK; + + if (size == 0) return; + + // this is the sequence number the current bit represents + int ack_nr = (packet_ack + 2) & ACK_MASK; + +#if TORRENT_VERBOSE_UTP_LOG + std::string bitmask; + bitmask.reserve(size); + for (boost::uint8_t const* b = ptr, *end = ptr + size; b != end; ++b) + { + unsigned char bitfield = unsigned(*b); + unsigned char mask = 1; + // for each bit + for (int i = 0; i < 8; ++i) + { + bitmask += (mask & bitfield) ? "1" : "0"; + mask <<= 1; + } + } + UTP_LOGV("%8p: got SACK first:%d %s our_seq_nr:%u\n" + , this, ack_nr, bitmask.c_str(), m_seq_nr); +#endif + + // the number of acked packets past the fast re-send sequence number + // this is used to determine if we should trigger more fast re-sends + int dups = 0; + + // the sequence number of the last ACKed packet + int last_ack = packet_ack; + + // for each byte + for (boost::uint8_t const* end = ptr + size; ptr != end; ++ptr) + { + unsigned char bitfield = unsigned(*ptr); + unsigned char mask = 1; + // for each bit + for (int i = 0; i < 8; ++i) + { + if (mask & bitfield) + { + last_ack = ack_nr; + if (m_fast_resend_seq_nr == ack_nr) + m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; + + if (compare_less_wrap(m_fast_resend_seq_nr, ack_nr, ACK_MASK)) ++dups; + // this bit was set, ack_nr was received + packet* p = (packet*)m_outbuf.remove(ack_nr); + if (p) + { + *acked_bytes += p->size - p->header_size; + // each ACKed packet counts as a duplicate ack + UTP_LOGV("%8p: duplicate_acks:%u fast_resend_seq_nr:%u\n" + , this, m_duplicate_acks, m_fast_resend_seq_nr); + ack_packet(p, now, min_rtt, ack_nr); + } + else + { + // this packet might have been acked by a previous + // selective ack + maybe_inc_acked_seq_nr(); + } + } + + mask <<= 1; + ack_nr = (ack_nr + 1) & ACK_MASK; + + // we haven't sent packets past this point. + // if there are any more bits set, we have to + // ignore them anyway + if (ack_nr == m_seq_nr) break; + } + if (ack_nr == m_seq_nr) break; + } + + TORRENT_ASSERT(m_outbuf.at((m_acked_seq_nr + 1) & ACK_MASK) || ((m_seq_nr - m_acked_seq_nr) & ACK_MASK) <= 1); + + // we received more than dup_ack_limit ACKs in this SACK message. + // trigger fast re-send + if (dups >= dup_ack_limit && compare_less_wrap(m_fast_resend_seq_nr, last_ack, ACK_MASK)) + { + experienced_loss(m_fast_resend_seq_nr); + int num_resent = 0; + while (m_fast_resend_seq_nr != last_ack) + { + packet* p = (packet*)m_outbuf.at(m_fast_resend_seq_nr); + m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; + if (!p) continue; + ++num_resent; + if (!resend_packet(p, true)) break; + m_duplicate_acks = 0; + if (num_resent >= sack_resend_limit) break; + } + } +} + +// copies data from the write buffer into the packet +// pointed to by ptr +void utp_socket_impl::write_payload(boost::uint8_t* ptr, int size) +{ + INVARIANT_CHECK; + +#ifdef TORRENT_DEBUG + int write_buffer_size = 0; + for (std::vector::iterator i = m_write_buffer.begin() + , end(m_write_buffer.end()); i != end; ++i) + { + write_buffer_size += i->len; + } + TORRENT_ASSERT(m_write_buffer_size == write_buffer_size); +#endif + TORRENT_ASSERT(!m_write_buffer.empty() || size == 0); + TORRENT_ASSERT(m_write_buffer_size >= size); + std::vector::iterator i = m_write_buffer.begin(); + + if (size == 0) return; + + int buffers_to_clear = 0; + while (size > 0) + { + // i points to the iovec we'll start copying from + int to_copy = (std::min)(size, int(i->len)); + TORRENT_ASSERT(to_copy >= 0); + TORRENT_ASSERT(to_copy < INT_MAX / 2 && m_written < INT_MAX / 2); + memcpy(ptr, static_cast(i->buf), to_copy); + size -= to_copy; + m_written += to_copy; + ptr += to_copy; + i->len -= to_copy; + TORRENT_ASSERT(m_write_buffer_size >= to_copy); + m_write_buffer_size -= to_copy; + ((char const*&)i->buf) += to_copy; + if (i->len == 0) ++buffers_to_clear; + ++i; + } + + if (buffers_to_clear) + m_write_buffer.erase(m_write_buffer.begin() + , m_write_buffer.begin() + buffers_to_clear); + +#ifdef TORRENT_DEBUG + write_buffer_size = 0; + for (std::vector::iterator i = m_write_buffer.begin() + , end(m_write_buffer.end()); i != end; ++i) + { + write_buffer_size += i->len; + } + TORRENT_ASSERT(m_write_buffer_size == write_buffer_size); +#endif +} + +void utp_socket_impl::subscribe_drained() +{ + INVARIANT_CHECK; + + if (m_subscribe_drained) return; + + UTP_LOGV("%8p: subscribe drained\n", this); + m_subscribe_drained = true; + m_sm->subscribe_drained(this); +} + +void utp_socket_impl::defer_ack() +{ + INVARIANT_CHECK; + + if (m_deferred_ack) return; + + UTP_LOGV("%8p: defer ack\n", this); + m_deferred_ack = true; + m_sm->defer_ack(this); +} + +void utp_socket_impl::remove_sack_header(packet* p) +{ + INVARIANT_CHECK; + + // remove the sack header + boost::uint8_t* ptr = p->buf + sizeof(utp_header); + utp_header* h = (utp_header*)p->buf; + + TORRENT_ASSERT(h->extension == 1); + + h->extension = ptr[0]; + int sack_size = ptr[1]; + TORRENT_ASSERT(h->extension == 0); + + UTP_LOGV("%8p: removing SACK header, %d bytes\n" + , this, sack_size + 2); + + TORRENT_ASSERT(p->size >= p->header_size); + TORRENT_ASSERT(p->header_size >= sizeof(utp_header) + sack_size + 2); + memmove(ptr, ptr + sack_size + 2, p->size - p->header_size); + p->header_size -= sack_size + 2; + p->size -= sack_size + 2; +} + +struct holder +{ + holder(char* buf = NULL): m_buf(buf) {} + ~holder() { free(m_buf); } + + void reset(char* buf) + { + free(m_buf); + m_buf = buf; + } + + char* release() + { + char* ret = m_buf; + m_buf = NULL; + return ret; + } + +private: + + char* m_buf; +}; + +// sends a packet, pulls data from the write buffer (if there's any) +// if ack is true, we need to send a packet regardless of if there's +// any data. Returns true if we could send more data (i.e. call +// send_pkt() again) +// returns true if there is more space for payload in our +// congestion window, false if there is no more space. +bool utp_socket_impl::send_pkt(int flags) +{ + INVARIANT_CHECK; + + bool force = (flags & pkt_ack) || (flags & pkt_fin); + +// TORRENT_ASSERT(m_state != UTP_STATE_FIN_SENT || (flags & pkt_ack)); + + // first see if we need to resend any packets + + // TODO: this loop may not be very efficient + for (int i = (m_acked_seq_nr + 1) & ACK_MASK; i != m_seq_nr; i = (i + 1) & ACK_MASK) + { + packet* p = (packet*)m_outbuf.at(i); + if (!p) continue; + if (!p->need_resend) continue; + if (!resend_packet(p)) + { + // we couldn't resend the packet. It probably doesn't + // fit in our cwnd. If force is set, we need to continue + // to send our packet anyway, if we don't have force set, + // we might as well return + if (!force) return false; + // resend_packet might have failed + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return false; + break; + } + + // don't fast-resend this packet + if (m_fast_resend_seq_nr == i) + m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; + } + + int sack = 0; + if (m_inbuf.size()) + { + // the SACK bitfield should ideally fit all + // the pieces we have successfully received + sack = (m_inbuf.span() + 7) / 8; + if (sack > 32) sack = 32; + } + + int header_size = sizeof(utp_header) + (sack ? sack + 2 : 0); + int payload_size = m_write_buffer_size; + if (m_mtu - header_size < payload_size) + payload_size = m_mtu - header_size; + + // if we have one MSS worth of data, make sure it fits in our + // congestion window and the advertized receive window from + // the other end. + if (m_bytes_in_flight + payload_size > (std::min)(int(m_cwnd >> 16) + , int(m_adv_wnd - m_bytes_in_flight))) + { + // this means there's not enough room in the send window for + // another packet. We have to hold off sending this data. + // we still need to send an ACK though + // if we're trying to send a FIN, make an exception + if ((flags & pkt_fin) == 0) payload_size = 0; + + // we're constrained by the window size + m_cwnd_full = true; + + UTP_LOGV("%8p: no space in window send_buffer_size:%d cwnd:%d " + "adv_wnd:%d in-flight:%d mtu:%d\n" + , this, m_write_buffer_size, int(m_cwnd >> 16) + , m_adv_wnd, m_bytes_in_flight, m_mtu); + + if (!force) + { +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: skipping send seq_nr:%d ack_nr:%d " + "id:%d target:%s header_size:%d error:%s send_buffer_size:%d cwnd:%d " + "adv_wnd:%d in-flight:%d mtu:%d\n" + , this, int(m_seq_nr), int(m_ack_nr) + , m_send_id, print_endpoint(udp::endpoint(m_remote_address, m_port)).c_str() + , header_size, m_error.message().c_str(), m_write_buffer_size, int(m_cwnd >> 16) + , m_adv_wnd, m_bytes_in_flight, m_mtu); +#endif + return false; + } + } + + // if we don't have any data to send, or can't send any data + // and we don't have any data to force, don't send a packet + if (payload_size == 0 && !force && !m_nagle_packet) + { +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: skipping send (no payload and no force) seq_nr:%d ack_nr:%d " + "id:%d target:%s header_size:%d error:%s send_buffer_size:%d cwnd:%d " + "adv_wnd:%d in-flight:%d mtu:%d\n" + , this, int(m_seq_nr), int(m_ack_nr) + , m_send_id, print_endpoint(udp::endpoint(m_remote_address, m_port)).c_str() + , header_size, m_error.message().c_str(), m_write_buffer_size, int(m_cwnd >> 16) + , m_adv_wnd, m_bytes_in_flight, m_mtu); +#endif + return false; + } + + int packet_size = header_size + payload_size; + + packet* p = NULL; + boost::uint8_t* ptr = NULL; + utp_header* h = NULL; + +#if TORRENT_USE_ASSERTS + bool stack_alloced = false; +#endif + + // used to free the packet buffer in case we exit the + // function early + holder buf_holder; + + // payload size being zero means we're just sending + // an force. We should not pick up the nagle packet + if (!m_nagle_packet || (payload_size == 0 && force)) + { + // we only need a heap allocation if we have payload and + // need to keep the packet around (in the outbuf) + if (payload_size) + { + p = (packet*)malloc(sizeof(packet) + m_mtu); + p->allocated = m_mtu; + buf_holder.reset((char*)p); + + m_sm->inc_stats_counter(utp_socket_manager::payload_pkts_out); + } + else + { +#if TORRENT_USE_ASSERTS + stack_alloced = true; +#endif + TORRENT_ASSERT(force); + // this alloca() statement won't necessarily produce + // correctly aligned memory. That's why we ask for 7 more bytes + // and adjust our pointer to be aligned later + p = (packet*)TORRENT_ALLOCA(char, sizeof(packet) + packet_size + + sizeof(packet*) - 1); + p = (packet*)align_pointer(p); + UTP_LOGV("%8p: allocating %d bytes on the stack\n", this, packet_size); + p->allocated = packet_size; + } + + p->size = packet_size; + p->header_size = packet_size - payload_size; + p->num_transmissions = 0; +#ifdef TORRENT_DEBUG + p->num_fast_resend = 0; +#endif + p->need_resend = false; + ptr = p->buf; + h = (utp_header*)ptr; + ptr += sizeof(utp_header); + + h->extension = sack ? 1 : 0; + h->connection_id = m_send_id; + // seq_nr is ignored for ST_STATE packets, so it doesn't + // matter that we say this is a sequence number we haven't + // actually sent yet + h->seq_nr = m_seq_nr; + h->type_ver = ((payload_size ? ST_DATA : ST_STATE) << 4) | 1; + + write_payload(p->buf + p->header_size, payload_size); + } + else + { + // pick up the nagle packet and keep adding bytes to it + p = m_nagle_packet; + + ptr = p->buf + sizeof(utp_header); + h = (utp_header*)p->buf; + TORRENT_ASSERT(h->seq_nr == m_seq_nr); + + // if the packet has a selective force header, we'll need + // to update it + if (h->extension == 1) + { + sack = ptr[1]; + // if we no longer have any out-of-order packets waiting + // to be delivered, there's no selective ack to be sent. + if (m_inbuf.size() == 0) + { + // we need to remove the sack header + remove_sack_header(p); + sack = 0; + } + } + else + sack = 0; + + boost::int32_t size_left = p->allocated - p->size; + TORRENT_ASSERT(size_left > 0); + size_left = (std::min)(size_left, m_write_buffer_size); + write_payload(p->buf + p->size, size_left); + p->size += size_left; + + UTP_LOGV("%8p: NAGLE appending %d bytes to nagle packet. new size: %d allocated: %d\n" + , this, size_left, p->size, p->allocated); + + // did we fill up the whole mtu? + // if we didn't, we may still send it if there's + // no bytes in flight + if (m_bytes_in_flight > 0 + && p->size < p->allocated + && !force + && m_nagle) + { + return false; + } + + // clear the nagle packet pointer and fall through + // sending p + m_nagle_packet = NULL; + + packet_size = p->size; + payload_size = p->size - p->header_size; + } + + if (sack) + { + *ptr++ = 0; // end of extension chain + *ptr++ = sack; // bytes for SACK bitfield + write_sack(ptr, sack); + ptr += sack; + TORRENT_ASSERT(ptr <= p->buf + p->header_size); + } + + if (m_bytes_in_flight > 0 + && p->size < p->allocated + && !force + && m_nagle) + { + // this is nagle. If we don't have a full packet + // worth of payload to send AND we have at least + // one outstanding packet, hold off. Once the + // outstanding packet is acked, we'll send this + // payload + UTP_LOGV("%8p: NAGLE not enough payload send_buffer_size:%d cwnd:%d " + "adv_wnd:%d in-flight:%d mtu:%d\n" + , this, m_write_buffer_size, int(m_cwnd >> 16) + , m_adv_wnd, m_bytes_in_flight, m_mtu); + TORRENT_ASSERT(m_nagle_packet == NULL); + TORRENT_ASSERT(h->seq_nr == m_seq_nr); + m_nagle_packet = p; + buf_holder.release(); + return false; + } + + // MTU DISCOVERY + if (m_mtu_seq == 0 + && p->size > m_mtu_floor + && m_seq_nr != 0) + { + p->mtu_probe = true; + m_mtu_seq = m_seq_nr; + } + else + { + p->mtu_probe = false; + } + + h->timestamp_difference_microseconds = m_reply_micro; + h->wnd_size = (std::max)(m_in_buf_size - m_buffered_incoming_bytes + - m_receive_buffer_size, boost::int32_t(0)); + h->ack_nr = m_ack_nr; + + // if this is a FIN packet, override the type + if (flags & pkt_fin) + h->type_ver = (ST_FIN << 4) | 1; + + // fill in the timestamp as late as possible + ptime now = time_now_hires(); + p->send_time = now; + h->timestamp_microseconds = boost::uint32_t( + total_microseconds(now - min_time()) & 0xffffffff); + +#if TORRENT_UTP_LOG + UTP_LOG("%8p: sending packet seq_nr:%d ack_nr:%d type:%s " + "id:%d target:%s size:%d error:%s send_buffer_size:%d cwnd:%d " + "adv_wnd:%d in-flight:%d mtu:%d timestamp:%u time_diff:%u " + "mtu_probe:%d extension:%d\n" + , this, int(h->seq_nr), int(h->ack_nr), packet_type_names[h->get_type()] + , m_send_id, print_endpoint(udp::endpoint(m_remote_address, m_port)).c_str() + , p->size, m_error.message().c_str(), m_write_buffer_size, int(m_cwnd >> 16) + , m_adv_wnd, m_bytes_in_flight, m_mtu, boost::uint32_t(h->timestamp_microseconds) + , boost::uint32_t(h->timestamp_difference_microseconds), int(p->mtu_probe) + , h->extension); +#endif + + error_code ec; +#ifdef TORRENT_DEBUG + // simulate 1% packet loss +// if ((rand() % 100) > 0) +#endif + m_sm->send_packet(udp::endpoint(m_remote_address, m_port) + , (char const*)h, p->size, ec + , p->mtu_probe ? utp_socket_manager::dont_fragment : 0); + + ++m_out_packets; + m_sm->inc_stats_counter(utp_socket_manager::packets_out); + + if (ec == error::message_size) + { +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: error sending packet: %s\n", this, ec.message().c_str()); +#endif + // if we fail even though this is not a probe, we're screwed + // since we'd have to repacketize + TORRENT_ASSERT(p->mtu_probe); + m_mtu_ceiling = p->size - 1; + if (m_mtu_floor > m_mtu_ceiling) m_mtu_floor = m_mtu_ceiling; + update_mtu_limits(); + // resend the packet immediately without + // it being an MTU probe + p->mtu_probe = false; + if (m_mtu_seq == m_ack_nr) + m_mtu_seq = 0; + ec.clear(); + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: re-sending\n", this); +#endif + m_sm->send_packet(udp::endpoint(m_remote_address, m_port) + , (char const*)h, p->size, ec, 0); + } + + if (ec == error::would_block || ec == error::try_again) + { +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: socket stalled\n", this); +#endif + if (!m_stalled) + { + m_stalled = true; + m_sm->subscribe_writable(this); + } + } + else if (ec) + { + TORRENT_ASSERT(stack_alloced != bool(payload_size)); + m_error = ec; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + return false; + } + + if (!m_stalled) + ++p->num_transmissions; + + // if we have payload, we need to save the packet until it's acked + // and progress m_seq_nr + if (p->size > p->header_size) + { + // if we're sending a payload packet, there should not + // be a nagle packet waiting for more data + TORRENT_ASSERT(m_nagle_packet == NULL); + +#if !TORRENT_UT_SEQ + // if the other end closed the connection immediately + // our FIN packet will end up having the same sequence + // number as the SYN, so this assert is invalid + TORRENT_ASSERT(!m_outbuf.at(m_seq_nr)); +#endif + TORRENT_ASSERT(h->seq_nr == m_seq_nr); + + // release the buffer, we're saving it in the circular + // buffer of outgoing packets + buf_holder.release(); + packet* old = (packet*)m_outbuf.insert(m_seq_nr, p); + if (old) + { + TORRENT_ASSERT(((utp_header*)old->buf)->seq_nr == m_seq_nr); + if (!old->need_resend) m_bytes_in_flight -= old->size - old->header_size; + free(old); + } + TORRENT_ASSERT(h->seq_nr == m_seq_nr); + m_seq_nr = (m_seq_nr + 1) & ACK_MASK; + TORRENT_ASSERT(payload_size >= 0); + m_bytes_in_flight += p->size - p->header_size; + } + else + { + TORRENT_ASSERT(h->seq_nr == m_seq_nr); + } + + // if the socket is stalled, always return false, don't + // try to write more packets. We'll keep writing once + // the underlying UDP socket becomes writable + return m_write_buffer_size > 0 && !m_cwnd_full && !m_stalled; +} + +// size is in bytes +void utp_socket_impl::write_sack(boost::uint8_t* buf, int size) const +{ + INVARIANT_CHECK; + + TORRENT_ASSERT(m_inbuf.size()); + int ack_nr = (m_ack_nr + 2) & ACK_MASK; + boost::uint8_t* end = buf + size; + + for (; buf != end; ++buf) + { + *buf = 0; + int mask = 1; + for (int i = 0; i < 8; ++i) + { + if (m_inbuf.at(ack_nr)) *buf |= mask; + mask <<= 1; + ack_nr = (ack_nr + 1) & ACK_MASK; + } + } +} + +bool utp_socket_impl::resend_packet(packet* p, bool fast_resend) +{ + INVARIANT_CHECK; + + // for fast re-sends the packet hasn't been marked as needing resending + TORRENT_ASSERT(p->need_resend || fast_resend); + + if (m_error) return false; + + if (((m_acked_seq_nr + 1) & ACK_MASK) == m_mtu_seq + && m_mtu_seq != 0) + { + m_mtu_seq = 0; + p->mtu_probe = false; + // we got multiple acks for the packet before our probe, assume + // it was dropped because it was too big + m_mtu_ceiling = p->size - 1; + update_mtu_limits(); + } + + // we can only resend the packet if there's + // enough space in our congestion window + // since we can't re-packetize, some packets that are + // larger than the congestion window must be allowed through + // but only if we don't have any outstanding bytes + int window_size_left = (std::min)(int(m_cwnd >> 16), int(m_adv_wnd)) - m_bytes_in_flight; + if (!fast_resend + && p->size - p->header_size > window_size_left + && m_bytes_in_flight > 0) + { + m_cwnd_full = true; + return false; + } + + // plus one since we have fast-resend as well, which doesn't + // necessarily trigger by a timeout + TORRENT_ASSERT(p->num_transmissions < m_sm->num_resends() + 1); + + TORRENT_ASSERT(p->size - p->header_size >= 0); + if (p->need_resend) m_bytes_in_flight += p->size - p->header_size; + + m_sm->inc_stats_counter(utp_socket_manager::packet_resend); + if (fast_resend) m_sm->inc_stats_counter(utp_socket_manager::fast_retransmit); + +#ifdef TORRENT_DEBUG + if (fast_resend) ++p->num_fast_resend; +#endif + p->need_resend = false; + utp_header* h = (utp_header*)p->buf; + // update packet header + h->timestamp_difference_microseconds = m_reply_micro; + p->send_time = time_now_hires(); + h->timestamp_microseconds = boost::uint32_t( + total_microseconds(p->send_time - min_time()) & 0xffffffff); + + // if the packet has a selective ack header, we'll need + // to update it + if (h->extension == 1 && h->ack_nr != m_ack_nr) + { + boost::uint8_t* ptr = p->buf + sizeof(utp_header); + int sack_size = ptr[1]; + if (m_inbuf.size()) + { + // update the sack header + write_sack(ptr + 2, sack_size); + TORRENT_ASSERT(ptr + sack_size + 2 <= p->buf + p->header_size); + } + else + { + remove_sack_header(p); + } + } + + h->ack_nr = m_ack_nr; + + error_code ec; + m_sm->send_packet(udp::endpoint(m_remote_address, m_port) + , (char const*)p->buf, p->size, ec); + ++m_out_packets; + m_sm->inc_stats_counter(utp_socket_manager::packets_out); + + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: re-sending packet seq_nr:%d ack_nr:%d type:%s " + "id:%d target:%s size:%d error:%s send_buffer_size:%d cwnd:%d " + "adv_wnd:%d in-flight:%d mtu:%d timestamp:%u time_diff:%u\n" + , this, int(h->seq_nr), int(h->ack_nr), packet_type_names[h->get_type()] + , m_send_id, print_endpoint(udp::endpoint(m_remote_address, m_port)).c_str() + , p->size, ec.message().c_str(), m_write_buffer_size, int(m_cwnd >> 16) + , m_adv_wnd, m_bytes_in_flight, m_mtu, boost::uint32_t(h->timestamp_microseconds) + , boost::uint32_t(h->timestamp_difference_microseconds)); +#endif + + if (ec == error::would_block || ec == error::try_again) + { +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: socket stalled\n", this); +#endif + if (!m_stalled) + { + m_stalled = true; + m_sm->subscribe_writable(this); + } + } + else if (ec) + { + m_error = ec; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + return false; + } + + if (!m_stalled) + ++p->num_transmissions; + + return !m_stalled; +} + +void utp_socket_impl::experienced_loss(int seq_nr) +{ + INVARIANT_CHECK; + + // since loss often comes in bursts, we only cut the + // window in half once per RTT. This is implemented + // by limiting which packets can cause us to cut the + // window size. The first packet that's lost will + // update the limit to the last sequence number we sent. + // i.e. only packet sent after this loss can cause another + // window size cut. The +1 is to turn the comparison into + // less than or equal to. If we experience loss of the + // same packet again, ignore it. + if (compare_less_wrap(seq_nr, m_loss_seq_nr + 1, ACK_MASK)) return; + + // if we happen to be in slow-start mode, we need to leave it + if (m_slow_start) + { + m_ssthres = m_cwnd >> 16; + m_slow_start = false; + UTP_LOGV("%8p: experienced loss, slow_start -> 0\n", this); + } + + // cut window size in 2 + m_cwnd = (std::max)(m_cwnd * m_sm->loss_multiplier() / 100, boost::int64_t(m_mtu << 16)); + m_loss_seq_nr = m_seq_nr; + UTP_LOGV("%8p: Lost packet %d caused cwnd cut\n", this, seq_nr); + + // the window size could go below one MMS here, if it does, + // we'll get a timeout in about one second + + m_sm->inc_stats_counter(utp_socket_manager::packet_loss); +} + +void utp_socket_impl::maybe_inc_acked_seq_nr() +{ + INVARIANT_CHECK; + + bool incremented = false; + // don't pass m_seq_nr, since we move into sequence + // numbers that haven't been sent yet, and aren't + // supposed to be in m_outbuf + // if the slot in m_outbuf is 0, it means the + // packet has been ACKed and removed from the send buffer + while (((m_acked_seq_nr + 1) & ACK_MASK) != m_seq_nr + && m_outbuf.at((m_acked_seq_nr + 1) & ACK_MASK) == 0) + { + // increment the fast resend sequence number + if (m_fast_resend_seq_nr == m_acked_seq_nr) + m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; + + m_acked_seq_nr = (m_acked_seq_nr + 1) & ACK_MASK; + incremented = true; + } + + if (!incremented) return; + + // update loss seq number if it's less than the packet + // that was just acked. If loss seq nr is greater, it suggests + // that we're still in a window that has experienced loss + if (compare_less_wrap(m_loss_seq_nr, m_acked_seq_nr, ACK_MASK)) + m_loss_seq_nr = m_acked_seq_nr; + m_duplicate_acks = 0; +} + +void utp_socket_impl::ack_packet(packet* p, ptime const& receive_time + , boost::uint32_t& min_rtt, boost::uint16_t seq_nr) +{ + INVARIANT_CHECK; + + TORRENT_ASSERT(p); + + // verify that the packet we're removing was in fact sent + // with the sequence number we expect + TORRENT_ASSERT(((utp_header*)p->buf)->seq_nr == seq_nr); + + if (!p->need_resend) + { + TORRENT_ASSERT(m_bytes_in_flight >= p->size - p->header_size); + m_bytes_in_flight -= p->size - p->header_size; + } + + if (seq_nr == m_mtu_seq && m_mtu_seq != 0) + { + TORRENT_ASSERT(p->mtu_probe); + // our mtu probe was acked! + m_mtu_floor = (std::max)(m_mtu_floor, p->size); + if (m_mtu_ceiling < m_mtu_floor) m_mtu_ceiling = m_mtu_floor; + update_mtu_limits(); + } + + // increment the acked sequence number counter + maybe_inc_acked_seq_nr(); + + boost::uint32_t rtt = boost::uint32_t(total_microseconds(receive_time - p->send_time)); + if (receive_time < p->send_time) + { + // this means our clock is not monotonic. Just assume the RTT was 100 ms + rtt = 100000; + + // the clock for this plaform is not monotonic! + TORRENT_ASSERT(false); + } + + UTP_LOGV("%8p: acked packet %d (%d bytes) (rtt:%u)\n" + , this, seq_nr, p->size - p->header_size, rtt / 1000); + + m_rtt.add_sample(rtt / 1000); + if (rtt < min_rtt) min_rtt = rtt; + free(p); +} + +void utp_socket_impl::incoming(boost::uint8_t const* buf, int size, packet* p, ptime now) +{ + INVARIANT_CHECK; + + while (!m_read_buffer.empty()) + { + if (p) + { + buf = p->buf + p->header_size; + TORRENT_ASSERT(p->size - p->header_size >= size); + } + iovec_t* target = &m_read_buffer.front(); + + int to_copy = (std::min)(size, int(target->len)); + memcpy(target->buf, buf, to_copy); + m_read += to_copy; + target->buf = ((boost::uint8_t*)target->buf) + to_copy; + target->len -= to_copy; + buf += to_copy; + UTP_LOGV("%8p: copied %d bytes into user receive buffer\n", this, to_copy); + TORRENT_ASSERT(m_read_buffer_size >= to_copy); + m_read_buffer_size -= to_copy; + size -= to_copy; + if (target->len == 0) m_read_buffer.erase(m_read_buffer.begin()); + if (p) + { + p->header_size += to_copy; + TORRENT_ASSERT(p->header_size <= p->size); + } + + if (size == 0) + { + TORRENT_ASSERT(p == 0 || p->header_size == p->size); + free(p); + return; + } + } + + TORRENT_ASSERT(m_read_buffer_size == 0); + + if (!p) + { + TORRENT_ASSERT(buf); + p = (packet*)malloc(sizeof(packet) + size); + p->size = size; + p->header_size = 0; + memcpy(p->buf, buf, size); + } + // save this packet until the client issues another read + m_receive_buffer.push_back(p); + m_receive_buffer_size += p->size - p->header_size; + + check_receive_buffers(); +} + +bool utp_socket_impl::cancel_handlers(error_code const& ec, bool kill) +{ + INVARIANT_CHECK; + + TORRENT_ASSERT(ec); + bool ret = m_read_handler || m_write_handler || m_connect_handler; + + // calling the callbacks with m_userdata being 0 will just crash + TORRENT_ASSERT((ret && bool(m_userdata)) || !ret); + + if (m_read_handler) m_read_handler(m_userdata, 0, ec, kill); + m_read_handler = 0; + if (m_write_handler) m_write_handler(m_userdata, 0, ec, kill); + m_write_handler = 0; + if (m_connect_handler) m_connect_handler(m_userdata, ec, kill); + m_connect_handler = 0; + return ret; +} + +bool utp_socket_impl::consume_incoming_data( + utp_header const* ph, boost::uint8_t const* ptr, int payload_size + , ptime now) +{ + INVARIANT_CHECK; + + if (ph->get_type() != ST_DATA) return false; + + if (m_eof && m_ack_nr == m_eof_seq_nr) + { + // What?! We've already received a FIN and everything up + // to it has been acked. Ignore this packet + UTP_LOG("%8p: ERROR: ignoring packet on shut down socket\n", this); + return true; + } + + if (m_read_buffer_size == 0 + && m_receive_buffer_size >= m_in_buf_size - m_buffered_incoming_bytes) + { + // if we don't have a buffer from the upper layer, and the + // number of queued up bytes, waiting for the upper layer, + // exceeds the advertized receive window, start ignoring + // more data packets + UTP_LOG("%8p: ERROR: our advertized window is not honored. " + "recv_buf: %d buffered_in: %d max_size: %d\n" + , this, m_receive_buffer_size, m_buffered_incoming_bytes, m_in_buf_size); + return false; + } + + if (ph->seq_nr == ((m_ack_nr + 1) & ACK_MASK)) + { + TORRENT_ASSERT(m_inbuf.at(m_ack_nr) == 0); + + if (m_buffered_incoming_bytes + m_receive_buffer_size + payload_size > m_in_buf_size) + { + UTP_LOGV("%8p: other end is not honoring our advertised window, dropping packet\n", this); + return true; + } + + // we received a packet in order + incoming(ptr, payload_size, 0, now); + m_ack_nr = (m_ack_nr + 1) & ACK_MASK; + + // If this packet was previously in the reorder buffer + // it would have been acked when m_ack_nr-1 was acked. + TORRENT_ASSERT(m_inbuf.at(m_ack_nr) == 0); + + UTP_LOGV("%8p: remove inbuf: %d (%d)\n" + , this, m_ack_nr, int(m_inbuf.size())); + + for (;;) + { + int const next_ack_nr = (m_ack_nr + 1) & ACK_MASK; + + packet* p = (packet*)m_inbuf.remove(next_ack_nr); + + if (!p) break; + + m_buffered_incoming_bytes -= p->size - p->header_size; + incoming(0, p->size - p->header_size, p, now); + + m_ack_nr = next_ack_nr; + + UTP_LOGV("%8p: reordered remove inbuf: %d (%d)\n" + , this, m_ack_nr, int(m_inbuf.size())); + } + } + else + { + // this packet was received out of order. Stick it in the + // reorder buffer until it can be delivered in order + + // have we already received this packet and passed it on + // to the client? + if (!compare_less_wrap(m_ack_nr, ph->seq_nr, ACK_MASK)) + { + UTP_LOGV("%8p: already received seq_nr: %d\n" + , this, int(ph->seq_nr)); + return true; + } + + // do we already have this packet? If so, just ignore it + if (m_inbuf.at(ph->seq_nr)) + { + UTP_LOGV("%8p: already received seq_nr: %d\n" + , this, int(ph->seq_nr)); + return true; + } + + if (m_buffered_incoming_bytes + m_receive_buffer_size + payload_size > m_in_buf_size) + { + UTP_LOGV("%8p: other end is not honoring our advertised window, dropping packet %d\n" + , this, int(ph->seq_nr)); + return true; + } + + // we don't need to save the packet header, just the payload + packet* p = (packet*)malloc(sizeof(packet) + payload_size); + p->size = payload_size; + p->header_size = 0; + p->num_transmissions = 0; +#ifdef TORRENT_DEBUG + p->num_fast_resend = 0; +#endif + p->need_resend = false; + memcpy(p->buf, ptr, payload_size); + m_inbuf.insert(ph->seq_nr, p); + m_buffered_incoming_bytes += p->size; + + UTP_LOGV("%8p: out of order. insert inbuf: %d (%d) m_ack_nr: %d\n" + , this, int(ph->seq_nr), int(m_inbuf.size()), m_ack_nr); + } + + return false; +} + +// returns true of the socket was closed +bool utp_socket_impl::test_socket_state() +{ + INVARIANT_CHECK; + + // if the socket is in a state where it's dead, just waiting to + // tell the client that it's closed. Do that and transition into + // the deleted state, where it will be deleted + // it might be possible to get here twice, in which we need to + // cancel any new handlers as well, even though we're already + // in the delete state + if (!m_error) return false; + TORRENT_ASSERT(m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE); + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: state:%s error:%s\n" + , this, socket_state_names[m_state], m_error.message().c_str()); +#endif + + if (cancel_handlers(m_error, true)) + { + m_state = UTP_STATE_DELETE; +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: state:%s\n", this, socket_state_names[m_state]); +#endif + return true; + } + return false; +} + +void utp_socket_impl::init_mtu(int link_mtu, int utp_mtu) +{ + INVARIANT_CHECK; + + // if we're in a RAM constrained environment, don't increase + // the buffer size for interfaces with large MTUs. Just stick + // to ethernet frame sizes + if (m_sm->allow_dynamic_sock_buf()) + { + // Make sure that we have enough socket buffer space + // for sending and receiving packets of this size + // add 10% for smaller ACKs and other overhead + m_sm->set_sock_buf(link_mtu * 11 / 10); + } + else if (link_mtu > TORRENT_ETHERNET_MTU) + { + // we can't use larger packets than this since we're + // not allocating any more memory for socket buffers + int decrease = link_mtu - TORRENT_ETHERNET_MTU; + utp_mtu -= decrease; + link_mtu -= decrease; + } + + // set the ceiling to what we found out from the interface + m_mtu_ceiling = utp_mtu; + + // however, start the search from a more conservative MTU + int overhead = link_mtu - utp_mtu; + m_mtu = TORRENT_ETHERNET_MTU - overhead; + if (m_mtu > m_mtu_ceiling) m_mtu = m_mtu_ceiling; + + if (m_mtu_floor > utp_mtu) m_mtu_floor = utp_mtu; + + // if the window size is smaller than one packet size + // set it to one + if ((m_cwnd >> 16) < m_mtu) m_cwnd = boost::int64_t(m_mtu) << 16; + + UTP_LOGV("%8p: initializing MTU to: %d [%d, %d]\n" + , this, m_mtu, m_mtu_floor, m_mtu_ceiling); +} + +// return false if this is an invalid packet +bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size + , udp::endpoint const& ep, ptime receive_time) +{ + INVARIANT_CHECK; + + utp_header* ph = (utp_header*)buf; + + m_sm->inc_stats_counter(utp_socket_manager::packets_in); + + if (ph->get_version() != 1) + { + UTP_LOG("%8p: ERROR: incoming packet version:%d (ignored)\n" + , this, int(ph->get_version())); + m_sm->inc_stats_counter(utp_socket_manager::invalid_pkts_in); + return false; + } + + // SYN packets have special (reverse) connection ids + if (ph->get_type() != ST_SYN && ph->connection_id != m_recv_id) + { + UTP_LOG("%8p: ERROR: incoming packet id:%d expected:%d (ignored)\n" + , this, int(ph->connection_id), int(m_recv_id)); + m_sm->inc_stats_counter(utp_socket_manager::invalid_pkts_in); + return false; + } + + if (ph->get_type() >= NUM_TYPES) + { + UTP_LOG("%8p: ERROR: incoming packet type:%d (ignored)\n" + , this, int(ph->get_type())); + m_sm->inc_stats_counter(utp_socket_manager::invalid_pkts_in); + return false; + } + + if (m_state == UTP_STATE_NONE && ph->get_type() == ST_SYN) + { + m_remote_address = ep.address(); + m_port = ep.port(); + } + + if (m_state != UTP_STATE_NONE && ph->get_type() == ST_SYN) + { + UTP_LOG("%8p: ERROR: incoming packet type:ST_SYN (ignored)\n", this); + m_sm->inc_stats_counter(utp_socket_manager::invalid_pkts_in); + return true; + } + + bool step = false; + if (receive_time - m_last_history_step > minutes(1)) + { + step = true; + m_last_history_step = receive_time; + } + + // this is the difference between their send time and our receive time + // 0 means no sample yet + boost::uint32_t their_delay = 0; + if (ph->timestamp_microseconds != 0) + { + boost::uint32_t timestamp = boost::uint32_t(total_microseconds(receive_time - min_time()) & 0xffffffff); + m_reply_micro = timestamp - ph->timestamp_microseconds; + boost::uint32_t prev_base = m_their_delay_hist.initialized() ? m_their_delay_hist.base() : 0; + their_delay = m_their_delay_hist.add_sample(m_reply_micro, step); + int base_change = m_their_delay_hist.base() - prev_base; + UTP_LOGV("%8p: their_delay::add_sample:%u prev_base:%u new_base:%u\n" + , this, m_reply_micro, prev_base, m_their_delay_hist.base()); + + if (prev_base && base_change < 0 && base_change > -10000 && m_delay_hist.initialized()) + { + // their base delay went down. This is caused by clock drift. To compensate, + // adjust our base delay upwards + // don't adjust more than 10 ms. If the change is that big, something is probably wrong + m_delay_hist.adjust_base(-base_change); + } + + UTP_LOGV("%8p: incoming packet reply_micro:%u base_change:%d\n" + , this, m_reply_micro, prev_base ? base_change : 0); + } + + // is this ACK valid? If the other end is ACKing + // a packet that hasn't been sent yet + // just ignore it. A 3rd party could easily inject a packet + // like this in a stream, don't sever it because of it. + // since m_seq_nr is the sequence number of the next packet + // we'll send (and m_seq_nr-1 was the last packet we sent), + // if the ACK we got is greater than the last packet we sent + // something is wrong. + // If our state is state_none, this packet must be a syn packet + // and the ack_nr should be ignored + boost::uint16_t cmp_seq_nr = (m_seq_nr - 1) & ACK_MASK; +#if TORRENT_UT_SEQ + if (m_state == UTP_STATE_SYN_SENT && ph->get_type() == ST_STATE) + cmp_seq_nr = m_seq_nr; +#endif + if (m_state != UTP_STATE_NONE + && compare_less_wrap(cmp_seq_nr, ph->ack_nr, ACK_MASK)) + { + UTP_LOG("%8p: ERROR: incoming packet ack_nr:%d our seq_nr:%d (ignored)\n" + , this, int(ph->ack_nr), m_seq_nr); + m_sm->inc_stats_counter(utp_socket_manager::redundant_pkts_in); + return true; + } + + // check to make sure the sequence number of this packet + // is reasonable. If it's a data packet and we've already + // received it, ignore it. This is either a stray old packet + // that finally made it here (after having been re-sent) or + // an attempt to interfere with the connection from a 3rd party + // in both cases, we can safely ignore the timestamp and ACK + // information in this packet +/* + // even if we've already received this packet, we need to + // send another ack to it, since it may be a resend caused by + // our ack getting dropped + if (m_state != UTP_STATE_SYN_SENT + && ph->get_type() == ST_DATA + && !compare_less_wrap(m_ack_nr, ph->seq_nr, ACK_MASK)) + { + // we've already received this packet + UTP_LOGV("%8p: incoming packet seq_nr:%d our ack_nr:%d (ignored)\n" + , this, int(ph->seq_nr), m_ack_nr); + m_sm->inc_stats_counter(utp_socket_manager::redundant_pkts_in); + return true; + } +*/ + + // if the socket is closing, always ignore any packet + // with a higher sequence number than the FIN sequence number + if (m_eof && compare_less_wrap(m_eof_seq_nr, ph->seq_nr, ACK_MASK)) + { +#if TORRENT_UTP_LOG + UTP_LOG("%8p: ERROR: incoming packet type: %s seq_nr:%d eof_seq_nr:%d (ignored)\n" + , this, packet_type_names[ph->get_type()], int(ph->seq_nr), m_eof_seq_nr); +#endif + return true; + } + + if (ph->get_type() == ST_DATA) + m_sm->inc_stats_counter(utp_socket_manager::payload_pkts_in); + + if (m_state != UTP_STATE_NONE + && m_state != UTP_STATE_SYN_SENT + && compare_less_wrap((m_ack_nr + max_packets_reorder) & ACK_MASK, ph->seq_nr, ACK_MASK)) + { + // this is too far out to fit in our reorder buffer. Drop it + // This is either an attack to try to break the connection + // or a seariously damaged connection that lost a lot of + // packets. Neither is very likely, and it should be OK + // to drop the timestamp information. + UTP_LOG("%8p: ERROR: incoming packet seq_nr:%d our ack_nr:%d (ignored)\n" + , this, int(ph->seq_nr), m_ack_nr); + m_sm->inc_stats_counter(utp_socket_manager::redundant_pkts_in); + return true; + } + + if (ph->get_type() == ST_RESET) + { + if (compare_less_wrap(cmp_seq_nr, ph->ack_nr, ACK_MASK)) + { + UTP_LOG("%8p: ERROR: invalid RESET packet, ack_nr:%d our seq_nr:%d (ignored)\n" + , this, int(ph->ack_nr), m_seq_nr); + return true; + } + if (compare_less_wrap(ph->ack_nr, m_acked_seq_nr , ACK_MASK)) + { + UTP_LOG("%8p: ERROR: invalid RESET packet, ack_nr:%d our acked_seq_nr:%d (ignored)\n" + , this, int(ph->ack_nr), m_acked_seq_nr); + return true; + } + UTP_LOGV("%8p: incoming packet type:RESET\n", this); + m_error = asio::error::connection_reset; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + return true; + } + + ++m_in_packets; + + // this is a valid incoming packet, update the timeout timer + m_num_timeouts = 0; + m_timeout = receive_time + milliseconds(packet_timeout()); + UTP_LOGV("%8p: updating timeout to: now + %d\n" + , this, packet_timeout()); + + // the test for INT_MAX here is a work-around for a bug in uTorrent where + // it's sometimes sent as INT_MAX when it is in fact uninitialized + const boost::uint32_t sample = ph->timestamp_difference_microseconds == INT_MAX + ? 0 : ph->timestamp_difference_microseconds; + + boost::uint32_t delay = 0; + if (sample != 0) + { + delay = m_delay_hist.add_sample(sample, step); + m_delay_sample_hist[m_delay_sample_idx++] = delay; + if (m_delay_sample_idx >= num_delay_hist) m_delay_sample_idx = 0; + } + + int acked_bytes = 0; + + TORRENT_ASSERT(m_bytes_in_flight >= 0); + int prev_bytes_in_flight = m_bytes_in_flight; + + m_adv_wnd = ph->wnd_size; + + // if we get an ack for the same sequence number as + // was last ACKed, and we have outstanding packets, + // it counts as a duplicate ack + if (ph->ack_nr == m_acked_seq_nr && m_outbuf.size()) + { + ++m_duplicate_acks; + } + + boost::uint32_t min_rtt = (std::numeric_limits::max)(); + + TORRENT_ASSERT(m_outbuf.at((m_acked_seq_nr + 1) & ACK_MASK) || ((m_seq_nr - m_acked_seq_nr) & ACK_MASK) <= 1); + + // has this packet already been ACKed? + // if the ACK we just got is less than the max ACKed + // sequence number, it doesn't tell us anything. + // So, only act on it if the ACK is greater than the last acked + // sequence number + if (m_state != UTP_STATE_NONE && compare_less_wrap(m_acked_seq_nr, ph->ack_nr, ACK_MASK)) + { + int const next_ack_nr = ph->ack_nr; + + for (int ack_nr = (m_acked_seq_nr + 1) & ACK_MASK; + ack_nr != ((next_ack_nr + 1) & ACK_MASK); + ack_nr = (ack_nr + 1) & ACK_MASK) + { + if (m_fast_resend_seq_nr == ack_nr) + m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; + packet* p = (packet*)m_outbuf.remove(ack_nr); + + if (!p) continue; + + acked_bytes += p->size - p->header_size; + ack_packet(p, receive_time, min_rtt, ack_nr); + } + + maybe_inc_acked_seq_nr(); + } + + // look for extended headers + boost::uint8_t const* ptr = buf; + ptr += sizeof(utp_header); + + unsigned int extension = ph->extension; + while (extension) + { + // invalid packet. It says it has an extension header + // but the packet is too short + if (ptr - buf + 2 > size) + { + UTP_LOG("%8p: ERROR: invalid extension header\n", this); + m_sm->inc_stats_counter(utp_socket_manager::invalid_pkts_in); + return true; + } + int next_extension = *ptr++; + int len = *ptr++; + if (len < 0) + { + UTP_LOGV("%8p: invalid extension length:%d packet:%d\n" + , this, len, int(ptr - buf)); + m_sm->inc_stats_counter(utp_socket_manager::invalid_pkts_in); + return true; + } + if (ptr - buf + len > ptrdiff_t(size)) + { + UTP_LOG("%8p: ERROR: invalid extension header size:%d packet:%d\n" + , this, len, int(ptr - buf)); + m_sm->inc_stats_counter(utp_socket_manager::invalid_pkts_in); + return true; + } + switch(extension) + { + case 1: // selective ACKs + parse_sack(ph->ack_nr, ptr, len, &acked_bytes, receive_time, min_rtt); + break; + } + ptr += len; + extension = next_extension; + } + + // the send operation in parse_sack() may have set the socket to an error + // state, in which case we shouldn't continue + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; + + if (m_duplicate_acks >= dup_ack_limit + && ((m_acked_seq_nr + 1) & ACK_MASK) == m_fast_resend_seq_nr) + { + // LOSS + + UTP_LOGV("%8p: Packet %d lost. (%d duplicate acks, trigger fast-resend)\n", this, m_fast_resend_seq_nr, m_duplicate_acks); + + // resend the lost packet + packet* p = (packet*)m_outbuf.at(m_fast_resend_seq_nr); + TORRENT_ASSERT(p); + + // don't fast-resend this again + m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; + + if (p) + { + experienced_loss(m_fast_resend_seq_nr); + resend_packet(p, true); + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; + } + } + + // ptr points to the payload of the packet + // size is the packet size, payload is the + // number of payload bytes are in this packet + const int header_size = ptr - buf; + const int payload_size = size - header_size; + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: incoming packet seq_nr:%d ack_nr:%d type:%s id:%d size:%d timestampdiff:%u timestamp:%u " + "our ack_nr:%d our seq_nr:%d our acked_seq_nr:%d our state:%s\n" + , this, int(ph->seq_nr), int(ph->ack_nr), packet_type_names[ph->get_type()] + , int(ph->connection_id), payload_size, boost::uint32_t(ph->timestamp_difference_microseconds) + , boost::uint32_t(ph->timestamp_microseconds), m_ack_nr, m_seq_nr, m_acked_seq_nr, socket_state_names[m_state]); +#endif + + if (ph->get_type() == ST_FIN) + { + // We ignore duplicate FIN packets, but we still need to ACK them. + if (ph->seq_nr == ((m_ack_nr + 1) & ACK_MASK) + || ph->seq_nr == m_ack_nr) + { + UTP_LOGV("%8p: FIN received in order\n", this); + + // The FIN arrived in order, nothing else is in the + // reorder buffer. + +// TORRENT_ASSERT(m_inbuf.size() == 0); + m_ack_nr = ph->seq_nr; + + // Transition to UTP_STATE_FIN_SENT. The sent FIN is also an ack + // to the FIN we received. Once we're in UTP_STATE_FIN_SENT we + // just need to wait for our FIN to be acked. + + if (m_state == UTP_STATE_FIN_SENT) + { + send_pkt(pkt_ack); + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; + } + else + { + send_fin(); + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; + } + } + + if (m_eof) + { + UTP_LOGV("%8p: duplicate FIN packet (ignoring)\n", this); + return true; + } + m_eof = true; + m_eof_seq_nr = ph->seq_nr; + + // we will respond with a fin once we have received everything up to m_eof_seq_nr + } + + switch (m_state) + { + case UTP_STATE_NONE: + { + if (ph->get_type() == ST_SYN) + { + // if we're in state_none, the only thing + // we accept are SYN packets. + m_state = UTP_STATE_CONNECTED; + + m_remote_address = ep.address(); + m_port = ep.port(); + + error_code ec; + m_local_address = m_sm->local_endpoint(m_remote_address, ec).address(); + + m_ack_nr = ph->seq_nr; + m_seq_nr = random() & 0xffff; + m_acked_seq_nr = (m_seq_nr - 1) & ACK_MASK; + m_loss_seq_nr = m_acked_seq_nr; + m_fast_resend_seq_nr = m_seq_nr; + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: received ST_SYN state:%s seq_nr:%d ack_nr:%d\n" + , this, socket_state_names[m_state], m_seq_nr, m_ack_nr); +#endif + TORRENT_ASSERT(m_send_id == ph->connection_id); + TORRENT_ASSERT(m_recv_id == ((m_send_id + 1) & 0xffff)); + + defer_ack(); + + return true; + } + else + { +#if TORRENT_UTP_LOG + UTP_LOG("%8p: ERROR: type:%s state:%s (ignored)\n" + , this, packet_type_names[ph->get_type()], socket_state_names[m_state]); +#endif + return true; + } + break; + } + case UTP_STATE_SYN_SENT: + { + // just wait for an ack to our SYN, ignore everything else + if (ph->ack_nr != ((m_seq_nr - 1) & ACK_MASK)) + { +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: incorrect ack_nr (%d) waiting for %d\n" + , this, int(ph->ack_nr), (m_seq_nr - 1) & ACK_MASK); +#endif + return true; + } + + TORRENT_ASSERT(!m_error); + m_state = UTP_STATE_CONNECTED; +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: state:%s\n", this, socket_state_names[m_state]); +#endif + + // only progress our ack_nr on ST_DATA messages + // since our m_ack_nr is uninitialized at this point + // we still need to set it to something regardless + if (ph->get_type() == ST_DATA) + m_ack_nr = ph->seq_nr; + else + m_ack_nr = (ph->seq_nr - 1) & ACK_MASK; + + // notify the client that the socket connected + if (m_connect_handler) + { + UTP_LOGV("%8p: calling connect handler\n", this); + m_connect_handler(m_userdata, m_error, false); + } + m_connect_handler = 0; + // fall through + } + case UTP_STATE_CONNECTED: + { + // the lowest seen RTT can be used to clamp the delay + // within reasonable bounds. The one-way delay is never + // higher than the round-trip time. + + if (sample && acked_bytes && prev_bytes_in_flight) + { + // it's impossible for delay to be more than the RTT, so make + // sure to clamp it as a sanity check + if (delay > min_rtt) delay = min_rtt; + + // only use the minimum from the last 3 delay measurements + delay = *std::min_element(m_delay_sample_hist, m_delay_sample_hist + num_delay_hist); + + do_ledbat(acked_bytes, delay, prev_bytes_in_flight, receive_time); + m_send_delay = delay; + } + + m_recv_delay = (std::min)(their_delay, min_rtt); + + consume_incoming_data(ph, ptr, payload_size, receive_time); + + // the parameter to send_pkt tells it if we're acking data + // If we are, we'll send an ACK regardless of if we have any + // space left in our send window or not. If we just got an ACK + // (i.e. ST_STATE) we're not ACKing anything. If we just + // received a FIN packet, we need to ack that as well + bool has_ack = ph->get_type() == ST_DATA || ph->get_type() == ST_FIN || ph->get_type() == ST_SYN; + boost::uint32_t prev_out_packets = m_out_packets; + + // try to send more data as long as we can + // if send_pkt returns true + while (send_pkt()); + + if (has_ack && prev_out_packets == m_out_packets) + { + // we need to ack some data we received, and we didn't + // end up sending any payload packets in the loop + // above (becasue m_out_packets would have been incremented + // in that case). This means we need to send an ack. + // don't do it right away, because we may still receive + // more packets. defer the ack to send as few acks as possible + defer_ack(); + } + + // we may want to call the user callback function at the end + // of this round. Subscribe to that event + subscribe_drained(); + + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; + + // Everything up to the FIN has been receieved, respond with a FIN + // from our side. + if (m_eof && m_ack_nr == ((m_eof_seq_nr - 1) & ACK_MASK)) + { + UTP_LOGV("%8p: incoming stream consumed\n", this); + + // This transitions to the UTP_STATE_FIN_SENT state. + send_fin(); + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; + } + +#if TORRENT_UTP_LOG + if (sample && acked_bytes && prev_bytes_in_flight) + { + char their_delay_base[20]; + if (m_their_delay_hist.initialized()) + snprintf(their_delay_base, sizeof(their_delay_base), "%u", m_their_delay_hist.base()); + else + strcpy(their_delay_base, "-"); + + char our_delay_base[20]; + if (m_delay_hist.initialized()) + snprintf(our_delay_base, sizeof(our_delay_base), "%u", m_delay_hist.base()); + else + strcpy(our_delay_base, "-"); + + UTP_LOG("%8p: " + "actual_delay:%u " + "our_delay:%f " + "their_delay:%f " + "off_target:%f " + "max_window:%u " + "upload_rate:%d " + "delay_base:%s " + "delay_sum:%f " + "target_delay:%d " + "acked_bytes:%d " + "cur_window:%d " + "scaled_gain:%f " + "rtt:%u " + "rate:%d " + "quota:%d " + "wnduser:%u " + "rto:%d " + "timeout:%d " + "get_microseconds:%u " + "cur_window_packets:%u " + "packet_size:%d " + "their_delay_base:%s " + "their_actual_delay:%u " + "seq_nr:%u " + "acked_seq_nr:%u " + "reply_micro:%u " + "min_rtt:%u " + "send_buffer:%d " + "recv_buffer:%d " + "fast_resend_seq_nr:%d " + "ssthres:%d " + "\n" + , this + , sample + , float(delay / 1000.f) + , float(their_delay / 1000.f) + , float(int(m_sm->target_delay() - delay)) / 1000.f + , boost::uint32_t(m_cwnd >> 16) + , 0 + , our_delay_base + , float(delay + their_delay) / 1000.f + , m_sm->target_delay() / 1000 + , acked_bytes + , m_bytes_in_flight + , 0.f // float(scaled_gain) + , m_rtt.mean() + , int((m_cwnd * 1000 / (m_rtt.mean()?m_rtt.mean():50)) >> 16) + , 0 + , m_adv_wnd + , packet_timeout() + , int(total_milliseconds(m_timeout - receive_time)) + , int(total_microseconds(receive_time - min_time())) + , (m_seq_nr - m_acked_seq_nr) & ACK_MASK + , m_mtu + , their_delay_base + , boost::uint32_t(m_reply_micro) + , m_seq_nr + , m_acked_seq_nr + , m_reply_micro + , min_rtt / 1000 + , m_write_buffer_size + , m_read_buffer_size + , m_fast_resend_seq_nr + , m_ssthres); + } +#endif + + return true; + } + case UTP_STATE_FIN_SENT: + { + // There are two ways we can end up in this state: + // + // 1. If the socket has been explicitly closed on our + // side, in which case m_eof is false. + // + // 2. If we received a FIN from the remote side, in which + // case m_eof is true. If this is the case, we don't + // come here until everything up to the FIN has been + // received. + // + // + // + + // At this point m_seq_nr - 1 is the FIN sequence number. + + // We can receive both ST_DATA and ST_STATE here, because after + // we have closed our end of the socket, the remote end might + // have data in the pipeline. We don't really care about the + // data, but we do have to ack it. Or rather, we have to ack + // the FIN that will come after the data. + + // Case 1: + // --------------------------------------------------------------- + // + // If we are here because the local endpoint was closed, we need + // to first wait for all of our messages to be acked: + // + // if (m_acked_seq_nr == ((m_seq_nr - 1) & ACK_MASK)) + // + // `m_seq_nr - 1` is the ST_FIN message that we sent. + // + // ---------------------- + // + // After that has happened we need to wait for the remote side + // to send its ST_FIN message. When we receive that we send an + // ST_STATE back to ack, and wait for a sufficient period. + // During this wait we keep acking incoming ST_FIN's. This is + // all handled at the top of this function. + // + // Note that the user handlers are all cancelled when the initial + // close() call happens, so nothing will happen on the user side + // after that. + + // Case 2: + // --------------------------------------------------------------- + // + // If we are here because we received a ST_FIN message, and then + // sent our own ST_FIN to ack that, we need to wait for our ST_FIN + // to be acked: + // + // if (m_acked_seq_nr == ((m_seq_nr - 1) & ACK_MASK)) + // + // `m_seq_nr - 1` is the ST_FIN message that we sent. + // + // After that has happened we know the remote side has all our + // data, and we can gracefully shut down. + + if (consume_incoming_data(ph, ptr, payload_size, receive_time)) + return true; + + if (m_acked_seq_nr == ((m_seq_nr - 1) & ACK_MASK)) + { + // When this happens we know that the remote side has + // received all of our packets. + + UTP_LOGV("%8p: FIN acked\n", this); + + if (!m_attached) + { + UTP_LOGV("%8p: close initiated here, delete socket\n", this); + m_error = asio::error::eof; + m_state = UTP_STATE_DELETE; + test_socket_state(); + } + else + { + UTP_LOGV("%8p: closing socket\n", this); + m_error = asio::error::eof; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + } + } + + return true; + } + case UTP_STATE_DELETE: + default: + { + // respond with a reset + send_reset(ph); + return true; + } + } + + return false; +} + +void utp_socket_impl::do_ledbat(int acked_bytes, int delay, int in_flight, ptime const now) +{ + INVARIANT_CHECK; + + // the portion of the in-flight bytes that were acked. This is used to make + // the gain factor be scaled by the rtt. The formula is applied once per + // rtt, or on every ACK skaled by the number of ACKs per rtt + TORRENT_ASSERT(in_flight > 0); + TORRENT_ASSERT(acked_bytes > 0); + + int target_delay = m_sm->target_delay(); + + // true if the upper layer is pushing enough data down the socket to be + // limited by the cwnd. If this is not the case, we should not adjust cwnd. + bool cwnd_saturated = (m_bytes_in_flight + acked_bytes + m_mtu > (m_cwnd >> 16)); + + // all of these are fixed points with 16 bits fraction portion + boost::int64_t window_factor = (boost::int64_t(acked_bytes) << 16) / in_flight; + boost::int64_t delay_factor = (boost::int64_t(target_delay - delay) << 16) / target_delay; + boost::int64_t scaled_gain; + + if (delay >= target_delay) + { + if (m_slow_start) + { + UTP_LOGV("%8p: off_target: %d slow_start -> 0\n", this, target_delay - delay); + m_ssthres = m_cwnd >> 16; + m_slow_start = false; + } + + m_sm->inc_stats_counter(utp_socket_manager::samples_above_target); + } + else + { + m_sm->inc_stats_counter(utp_socket_manager::samples_below_target); + } + + boost::int64_t linear_gain = (window_factor * delay_factor) >> 16; + linear_gain *= boost::int64_t(m_sm->gain_factor()); + + // if the user is not saturating the link (i.e. not filling the + // congestion window), don't adjust it at all. + if (cwnd_saturated) + { + boost::int64_t exponential_gain = boost::int64_t(acked_bytes) << 16; + if (m_slow_start) + { + // mimic TCP slow-start by adding the number of acked + // bytes to cwnd + if (m_ssthres != 0 && ((m_cwnd + exponential_gain) >> 16) > m_ssthres) + { + // if we would exeed the slow start threshold by growing the cwnd + // exponentially, don't do it, and leave slow-start mode. This + // make us avoid causing more delay and/or packet loss by being too + // aggressive + m_slow_start = false; + scaled_gain = linear_gain; + UTP_LOGV("%8p: cwnd > ssthres (%d) slow_start -> 0\n", this, m_ssthres); + } + else + { + scaled_gain = (std::max)(exponential_gain, linear_gain); + } + } + else + { + scaled_gain = linear_gain; + } + } + else + { + scaled_gain = 0; + } + + // make sure we don't wrap the cwnd + if (scaled_gain >= (std::numeric_limits::max)() - m_cwnd) + scaled_gain = (std::numeric_limits::max)() - m_cwnd - 1; + + UTP_LOGV("%8p: do_ledbat delay:%d off_target: %d window_factor:%f target_factor:%f " + "scaled_gain:%f cwnd:%d slow_start:%d\n" + , this, delay, target_delay - delay, window_factor / float(1 << 16) + , delay_factor / float(1 << 16) + , scaled_gain / float(1 << 16), int(m_cwnd >> 16) + , int(m_slow_start)); + + // if scaled_gain + m_cwnd <= 0, set m_cwnd to 0 + if (-scaled_gain >= m_cwnd) + { + m_cwnd = 0; + } + else + { + m_cwnd += scaled_gain; + TORRENT_ASSERT(m_cwnd > 0); + } + + TORRENT_ASSERT(m_cwnd >= 0); + + int window_size_left = (std::min)(int(m_cwnd >> 16), int(m_adv_wnd)) - in_flight + acked_bytes; + if (window_size_left >= m_mtu) + { + UTP_LOGV("%8p: mtu:%d in_flight:%d adv_wnd:%d cwnd:%d acked_bytes:%d cwnd_full -> 0\n" + , this, m_mtu, in_flight, int(m_adv_wnd), int(m_cwnd >> 16), acked_bytes); + m_cwnd_full = false; + } + + if ((m_cwnd >> 16) >= m_adv_wnd) + { + m_slow_start = false; + UTP_LOGV("%8p: cwnd > advertized wnd (%d) slow_start -> 0\n" + , this, m_adv_wnd); + } +} + +void utp_stream::bind(endpoint_type const& ep, error_code& ec) { } + +// returns the number of milliseconds a packet would have before +// it would time-out if it was sent right now. Takes the RTT estimate +// into account +int utp_socket_impl::packet_timeout() const +{ + INVARIANT_CHECK; + + // SYN packets have a bit longer timeout, since we don't + // have an RTT estimate yet, make a conservative guess + if (m_state == UTP_STATE_NONE) return 3000; + + // avoid overflow by simply capping based on number of timeouts as well + if (m_num_timeouts >= 7) return 60000; + + int timeout = (std::max)(m_sm->min_timeout(), m_rtt.mean() + m_rtt.avg_deviation() * 2); + if (m_num_timeouts > 0) timeout += (1 << (int(m_num_timeouts) - 1)) * 1000; + return timeout; +} + +void utp_socket_impl::tick(ptime const& now) +{ + INVARIANT_CHECK; + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: tick:%s r: %d (%s) w: %d (%s)\n" + , this, socket_state_names[m_state], m_read, m_read_handler ? "handler" : "no handler" + , m_written, m_write_handler ? "handler" : "no handler"); +#endif + + TORRENT_ASSERT(m_outbuf.at((m_acked_seq_nr + 1) & ACK_MASK) || ((m_seq_nr - m_acked_seq_nr) & ACK_MASK) <= 1); + + // if we're already in an error state, we're just waiting for the + // client to perform an operation so that we can communicate the + // error. No need to do anything else with this socket + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return; + + if (now > m_timeout) + { + // TIMEOUT! + // set cwnd to 1 MSS + + m_sm->inc_stats_counter(utp_socket_manager::timeout); + + if (m_outbuf.size()) ++m_num_timeouts; + + if (m_num_timeouts > m_sm->num_resends()) + { + // the connection is dead + m_error = asio::error::timed_out; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + return; + } + + if (((m_acked_seq_nr + 1) & ACK_MASK) == m_mtu_seq + && ((m_seq_nr - 1) & ACK_MASK) == m_mtu_seq + && m_mtu_seq != 0) + { + // we timed out, and the only outstanding packet + // we had was the probe. Assume it was dropped + // because it was too big + m_mtu_ceiling = m_mtu - 1; + if (m_mtu_floor > m_mtu_ceiling) m_mtu_floor = m_mtu_ceiling; + update_mtu_limits(); + } + + if (m_bytes_in_flight == 0 && (m_cwnd >> 16) >= m_mtu) + { + // this is just a timeout because this direction of + // the stream is idle. Don't reset the cwnd, just decay it + m_cwnd = (std::max)(m_cwnd * 2 / 3, boost::int64_t(m_mtu) << 16); + } + else + { + // we timed out because a packet was not ACKed or because + // the cwnd was made smaller than one packet + m_cwnd = boost::int64_t(m_mtu) << 16; + } + + TORRENT_ASSERT(m_cwnd >= 0); + + m_timeout = now + milliseconds(packet_timeout()); + + UTP_LOGV("%8p: timeout resetting cwnd:%d\n" + , this, int(m_cwnd >> 16)); + + // we dropped all packets, that includes the mtu probe + m_mtu_seq = 0; + + // since we've already timed out now, don't count + // loss that we might detect for packets that just + // timed out + m_loss_seq_nr = m_seq_nr; + + // when we time out, the cwnd is reset to 1 MSS, which means we + // need to ramp it up quickly again. enter slow start mode. This time + // we're very likely to have an ssthres set, which will make us leave + // slow start before inducing more delay or loss. + m_slow_start = true; + UTP_LOGV("%8p: timeout slow_start -> 1\n", this); + + // we need to go one past m_seq_nr to cover the case + // where we just sent a SYN packet and then adjusted for + // the uTorrent sequence number reuse + for (int i = m_acked_seq_nr & ACK_MASK; + i != ((m_seq_nr + 1) & ACK_MASK); + i = (i + 1) & ACK_MASK) + { + packet* p = (packet*)m_outbuf.at(i); + if (!p) continue; + if (p->need_resend) continue; + p->need_resend = true; + TORRENT_ASSERT(m_bytes_in_flight >= p->size - p->header_size); + m_bytes_in_flight -= p->size - p->header_size; + UTP_LOGV("%8p: Packet %d lost (timeout).\n", this, i); + } + + TORRENT_ASSERT(m_bytes_in_flight == 0); + + // if we have a packet that needs re-sending, resend it + packet* p = (packet*)m_outbuf.at((m_acked_seq_nr + 1) & ACK_MASK); + if (p) + { + if (p->num_transmissions >= m_sm->num_resends() + || (m_state == UTP_STATE_SYN_SENT && p->num_transmissions >= m_sm->syn_resends()) + || (m_state == UTP_STATE_FIN_SENT && p->num_transmissions >= m_sm->fin_resends())) + { +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: %d failed sends in a row. Socket timed out. state:%s\n" + , this, p->num_transmissions, socket_state_names[m_state]); +#endif + + // the connection is dead + m_error = asio::error::timed_out; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + return; + } + + // don't fast-resend this packet + if (m_fast_resend_seq_nr == ((m_acked_seq_nr + 1) & ACK_MASK)) + m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; + + // the packet timed out, resend it + resend_packet(p); + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return; + } + else if (m_state < UTP_STATE_FIN_SENT) + { + send_pkt(); + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return; + } + else if (m_state == UTP_STATE_FIN_SENT) + { + // the connection is dead + m_error = asio::error::eof; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + return; + } + } + + switch (m_state) + { + case UTP_STATE_NONE: + case UTP_STATE_DELETE: + return; +// case UTP_STATE_SYN_SENT: +// +// break; + } +} + +void utp_socket_impl::check_receive_buffers() const +{ + INVARIANT_CHECK; + + std::size_t size = 0; + + for (std::vector::const_iterator i = m_receive_buffer.begin() + , end(m_receive_buffer.end()); i != end; ++i) + { + if (packet const* p = *i) + size += p->size - p->header_size; + } + + TORRENT_ASSERT(int(size) == m_receive_buffer_size); +} + +#if TORRENT_USE_INVARIANT_CHECKS +void utp_socket_impl::check_invariant() const +{ + for (int i = m_outbuf.cursor(); + i != int((m_outbuf.cursor() + m_outbuf.span()) & ACK_MASK); + i = (i + 1) & ACK_MASK) + { + packet* p = (packet*)m_outbuf.at(i); + if (m_mtu_seq == i && m_mtu_seq != 0 && p) + { + TORRENT_ASSERT(p->mtu_probe); + } + if (!p) continue; + TORRENT_ASSERT(((utp_header*)p->buf)->seq_nr == i); + } + + if (m_nagle_packet) + { + // if this packet is full, it should have been sent + TORRENT_ASSERT(m_nagle_packet->size < m_nagle_packet->allocated); + } +} +#endif +} + diff --git a/apps/Launcher/ext/libtorrent/src/web_connection_base.cpp b/apps/Launcher/ext/libtorrent/src/web_connection_base.cpp new file mode 100644 index 0000000000..c30ac4c961 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/web_connection_base.cpp @@ -0,0 +1,205 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include + +#include "libtorrent/web_connection_base.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/identify_client.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/version.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/parse_url.hpp" +#include "libtorrent/peer_info.hpp" + +using boost::shared_ptr; +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + web_connection_base::web_connection_base( + session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote + , web_seed_entry& web) + : peer_connection(ses, t, s, remote, &web.peer_info) + , m_parser(http_parser::dont_parse_chunks) + , m_external_auth(web.auth) + , m_extra_headers(web.extra_headers) + , m_first_request(true) + , m_ssl(false) + , m_body_start(0) + { + INVARIANT_CHECK; + + // we only want left-over bandwidth + set_priority(1); + + // since this is a web seed, change the timeout + // according to the settings. + set_timeout(ses.settings().urlseed_timeout); + + std::string protocol; + error_code ec; + boost::tie(protocol, m_basic_auth, m_host, m_port, m_path) + = parse_url_components(web.url, ec); + TORRENT_ASSERT(!ec); + + if (m_port == -1 && protocol == "http") + m_port = 80; + +#ifdef TORRENT_USE_OPENSSL + if (protocol == "https") + { + m_ssl = true; + if (m_port == -1) m_port = 443; + } +#endif + + if (!m_basic_auth.empty()) + m_basic_auth = base64encode(m_basic_auth); + + m_server_string = "URL seed @ "; + m_server_string += m_host; + } + + void web_connection_base::start() + { + set_upload_only(true); + if (is_disconnecting()) return; + peer_connection::start(); + } + + web_connection_base::~web_connection_base() + {} + + void web_connection_base::on_connected() + { + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + // this is always a seed + incoming_have_all(); + + // it is always possible to request pieces + incoming_unchoke(); + + reset_recv_buffer(t->block_size() + 1024); + } + + void web_connection_base::add_headers(std::string& request + , proxy_settings const& ps, bool using_proxy) const + { + request += "Host: "; + request += m_host; + if (m_first_request || m_ses.settings().always_send_user_agent) { + request += "\r\nUser-Agent: "; + request += m_ses.settings().user_agent; + } + if (!m_external_auth.empty()) { + request += "\r\nAuthorization: "; + request += m_external_auth; + } else if (!m_basic_auth.empty()) { + request += "\r\nAuthorization: Basic "; + request += m_basic_auth; + } + if (ps.type == proxy_settings::http_pw) { + request += "\r\nProxy-Authorization: Basic "; + request += base64encode(ps.username + ":" + ps.password); + } + for (web_seed_entry::headers_t::const_iterator it = m_extra_headers.begin(); + it != m_extra_headers.end(); ++it) { + request += "\r\n"; + request += it->first; + request += ": "; + request += it->second; + } + if (using_proxy) { + request += "\r\nProxy-Connection: keep-alive"; + } + if (m_first_request || using_proxy) { + request += "\r\nConnection: keep-alive"; + } + } + + // -------------------------- + // RECEIVE DATA + // -------------------------- + + void web_connection_base::get_specific_peer_info(peer_info& p) const + { + if (is_interesting()) p.flags |= peer_info::interesting; + if (is_choked()) p.flags |= peer_info::choked; + if (!is_connecting() && m_server_string.empty()) + p.flags |= peer_info::handshake; + if (is_connecting() && !is_queued()) p.flags |= peer_info::connecting; + if (is_queued()) p.flags |= peer_info::queued; + + p.client = m_server_string; + } + + bool web_connection_base::in_handshake() const + { + return m_server_string.empty(); + } + + void web_connection_base::on_sent(error_code const& error + , std::size_t bytes_transferred) + { + INVARIANT_CHECK; + + if (error) return; + m_statistics.sent_bytes(0, bytes_transferred); + } + + +#if TORRENT_USE_INVARIANT_CHECKS + void web_connection_base::check_invariant() const + { +/* + TORRENT_ASSERT(m_num_pieces == std::count( + m_have_piece.begin() + , m_have_piece.end() + , true)); +*/ } +#endif + +} + diff --git a/apps/Launcher/ext/libtorrent/src/web_peer_connection.cpp b/apps/Launcher/ext/libtorrent/src/web_peer_connection.cpp new file mode 100644 index 0000000000..bf3a6e79fc --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/web_peer_connection.cpp @@ -0,0 +1,1050 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include + +#include "libtorrent/web_peer_connection.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/identify_client.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/version.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/parse_url.hpp" +#include "libtorrent/peer_info.hpp" + +using boost::shared_ptr; +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + enum + { + request_size_overhead = 5000 + }; + + web_peer_connection::web_peer_connection( + session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote + , web_seed_entry& web) + : web_connection_base(ses, t, s, remote, web) + , m_url(web.url) + , m_web(&web) + , m_received_body(0) + , m_range_pos(0) + , m_block_pos(0) + , m_chunk_pos(0) + , m_partial_chunk_header(0) + , m_num_responses(0) + { + INVARIANT_CHECK; + + if (!ses.settings().report_web_seed_downloads) + ignore_stats(true); + + shared_ptr tor = t.lock(); + TORRENT_ASSERT(tor); + + // we always prefer downloading 1 MiB chunks + // from web seeds, or whole pieces if pieces + // are larger than a MiB + int preferred_size = 1024 * 1024; + + // if the web server is known not to support keep-alive. + // request even larger blocks at a time + if (!web.supports_keepalive) preferred_size *= 4; + + prefer_whole_pieces((std::max)(preferred_size / tor->torrent_file().piece_length(), 1)); + + // we want large blocks as well, so + // we can request more bytes at once + // this setting will merge adjacent requests + // into single larger ones + request_large_blocks(true); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** web_peer_connection %s", web.url.c_str()); +#endif + } + + void web_peer_connection::on_connected() + { + incoming_have_all(); + if (m_web->restart_request.piece != -1) + { + // increase the chances of requesting the block + // we have partial data for already, to finish it + incoming_suggest(m_web->restart_request.piece); + } + web_connection_base::on_connected(); + } + + void web_peer_connection::disconnect(error_code const& ec, int error) + { + if (is_disconnecting()) return; + + boost::shared_ptr t = associated_torrent().lock(); + + if (!m_requests.empty() && !m_file_requests.empty() + && !m_piece.empty() && m_web) + { +#if 0 + std::cerr << this << " SAVE-RESTART-DATA: data: " << m_piece.size() + << " req: " << m_requests.front().piece + << " off: " << m_requests.front().start + << std::endl; +#endif + m_web->restart_request = m_requests.front(); + if (!m_web->restart_piece.empty()) + { + // we're about to replace a different restart piece + // buffer. So it was wasted download + if (t) t->add_redundant_bytes(m_web->restart_piece.size() + , torrent::piece_closing); + } + m_web->restart_piece.swap(m_piece); + + // we have to do this to not count this data as redundant. The + // upper layer will call downloading_piece_progress and assume + // it's all wasted download. Since we're saving it here, it isn't. + m_requests.clear(); + m_block_pos = 0; + } + + if (m_web && !m_web->supports_keepalive && error == 0) + { + // if the web server doesn't support keepalive and we were + // disconnected as a graceful EOF, reconnect right away + if (t) t->session().m_io_service.post( + boost::bind(&torrent::maybe_connect_web_seeds, t)); + } + peer_connection::disconnect(ec, error); + if (t) t->disconnect_web_seed(this); + } + + boost::optional + web_peer_connection::downloading_piece_progress() const + { + if (m_requests.empty()) + return boost::optional(); + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + piece_block_progress ret; + + ret.piece_index = m_requests.front().piece; + ret.bytes_downloaded = m_block_pos % t->block_size(); + // this is used to make sure that the block_index stays within + // bounds. If the entire piece is downloaded, the block_index + // would otherwise point to one past the end + int correction = m_block_pos ? -1 : 0; + ret.block_index = (m_requests.front().start + m_block_pos + correction) / t->block_size(); + TORRENT_ASSERT(ret.block_index < int(piece_block::invalid.block_index)); + TORRENT_ASSERT(ret.piece_index < int(piece_block::invalid.piece_index)); + + ret.full_block_bytes = t->block_size(); + const int last_piece = t->torrent_file().num_pieces() - 1; + if (ret.piece_index == last_piece && ret.block_index + == t->torrent_file().piece_size(last_piece) / t->block_size()) + ret.full_block_bytes = t->torrent_file().piece_size(last_piece) % t->block_size(); + return ret; + } + + void web_peer_connection::write_request(peer_request const& r) + { + INVARIANT_CHECK; + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + TORRENT_ASSERT(t->valid_metadata()); + + bool single_file_request = t->torrent_file().num_files() == 1; + + if (!single_file_request) + { + // handle incorrect .torrent files which are multi-file + // but have web seeds not ending with a slash + if (m_path.empty() || m_path[m_path.size() - 1] != '/') m_path += "/"; + if (m_url.empty() || m_url[m_url.size() - 1] != '/') m_url += "/"; + } + else + { + // handle .torrent files that don't include the filename in the url + if (m_path.empty()) m_path += "/" + t->torrent_file().name(); + else if (m_path[m_path.size() - 1] == '/') + { + std::string tmp = t->torrent_file().files().at(0).path; +#ifdef TORRENT_WINDOWS + convert_path_to_posix(tmp); +#endif + m_path += tmp; + } + else if (!m_url.empty() && m_url[m_url.size() - 1] == '/') + { + std::string tmp = t->torrent_file().files().at(0).path; +#ifdef TORRENT_WINDOWS + convert_path_to_posix(tmp); +#endif + m_url += tmp; + } + } + + torrent_info const& info = t->torrent_file(); + peer_request req = r; + + std::string request; + request.reserve(400); + + int size = r.length; + const int block_size = t->block_size(); + const int piece_size = t->torrent_file().piece_length(); + peer_request pr; + while (size > 0) + { + int request_offset = r.start + r.length - size; + pr.start = request_offset % piece_size; + pr.length = (std::min)(block_size, size); + pr.piece = r.piece + request_offset / piece_size; + m_requests.push_back(pr); + size -= pr.length; + if (m_web->restart_request == m_requests.front()) + { + m_piece.swap(m_web->restart_piece); + m_block_pos += m_piece.size(); + peer_request& front = m_requests.front(); + TORRENT_ASSERT(front.length > int(m_piece.size())); + +#if 0 + std::cerr << this << " RESTART-DATA: data: " << m_piece.size() + << " req: ( " << front.piece << ", " << front.start + << ", " << (front.start + front.length - 1) << ")" + << std::endl; +#endif + + req.start += m_piece.size(); + req.length -= m_piece.size(); + + // just to keep the accounting straight for the upper layer. + // it doesn't know we just re-wrote the request + incoming_piece_fragment(m_piece.size()); + m_web->restart_request.piece = -1; + } + +#if 0 + std::cerr << this << " REQ: p: " << pr.piece << " " << pr.start << std::endl; +#endif + } + + proxy_settings const& ps = m_ses.proxy(); + bool using_proxy = (ps.type == proxy_settings::http + || ps.type == proxy_settings::http_pw) && !m_ssl; + + if (single_file_request) + { + request += "GET "; + // do not encode single file paths, they are + // assumed to be encoded in the torrent file + request += using_proxy ? m_url : m_path; + request += " HTTP/1.1\r\n"; + add_headers(request, ps, using_proxy); + request += "\r\nRange: bytes="; + request += to_string(size_type(req.piece) * info.piece_length() + + req.start).elems; + request += "-"; + request += to_string(size_type(req.piece) * info.piece_length() + + req.start + req.length - 1).elems; + request += "\r\n\r\n"; + m_first_request = false; + m_file_requests.push_back(0); + } + else + { + std::vector files = info.orig_files().map_block( + req.piece, req.start, req.length); + + for (std::vector::iterator i = files.begin(); + i != files.end(); ++i) + { + file_slice const& f = *i; + if (info.orig_files().pad_file_at(f.file_index)) + { + m_file_requests.push_back(f.file_index); + continue; + } + request += "GET "; + if (using_proxy) + { + // m_url is already a properly escaped URL + // with the correct slashes. Don't encode it again + request += m_url; + std::string path = info.orig_files().file_path(f.file_index); +#ifdef TORRENT_WINDOWS + convert_path_to_posix(path); +#endif + request += escape_path(path.c_str(), path.length()); + } + else + { + // m_path is already a properly escaped URL + // with the correct slashes. Don't encode it again + request += m_path; + + std::string path = info.orig_files().file_path(f.file_index); +#ifdef TORRENT_WINDOWS + convert_path_to_posix(path); +#endif + request += escape_path(path.c_str(), path.length()); + } + request += " HTTP/1.1\r\n"; + add_headers(request, ps, using_proxy); + request += "\r\nRange: bytes="; + request += to_string(f.offset).elems; + request += "-"; + request += to_string(f.offset + f.size - 1).elems; + request += "\r\n\r\n"; + m_first_request = false; + +#if 0 + std::cerr << this << " SEND-REQUEST: f: " << f.file_index + << " s: " << f.offset + << " e: " << (f.offset + f.size - 1) << std::endl; +#endif + TORRENT_ASSERT(f.file_index >= 0); + m_file_requests.push_back(f.file_index); + } + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> %s", request.c_str()); +#endif + + // in case the first file on this series of requests is a padfile + // we need to handle it right now, and pretend that we got a response + // with zeros. + buffer::const_interval recv_buffer = receive_buffer(); + handle_padfile(recv_buffer); + if (associated_torrent().expired()) return; + + send_buffer(request.c_str(), request.size(), message_type_request); + } + + // -------------------------- + // RECEIVE DATA + // -------------------------- + + namespace + { + bool range_contains(peer_request const& range, peer_request const& req, int piece_size) + { + size_type range_start = size_type(range.piece) * piece_size + range.start; + size_type req_start = size_type(req.piece) * piece_size + req.start; + return range_start <= req_start + && range_start + range.length >= req_start + req.length; + } + } + + bool web_peer_connection::maybe_harvest_block() + { + peer_request const& front_request = m_requests.front(); + + if (int(m_piece.size()) < front_request.length) return false; + TORRENT_ASSERT(int(m_piece.size()) == front_request.length); + + // each call to incoming_piece() may result in us becoming + // a seed. If we become a seed, all seeds we're connected to + // will be disconnected, including this web seed. We need to + // check for the disconnect condition after the call. + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + buffer::const_interval recv_buffer = receive_buffer(); + + incoming_piece(front_request, &m_piece[0]); + m_requests.pop_front(); + if (associated_torrent().expired()) return false; + TORRENT_ASSERT(m_block_pos >= front_request.length); + m_block_pos -= front_request.length; + cut_receive_buffer(m_body_start, t->block_size() + request_size_overhead); + m_body_start = 0; + recv_buffer = receive_buffer(); +// TORRENT_ASSERT(m_received_body <= range_end - range_start); + m_piece.clear(); + TORRENT_ASSERT(m_piece.empty()); + return true; + } + + bool web_peer_connection::received_invalid_data(int index, bool single_peer) + { + if (!single_peer) return peer_connection::received_invalid_data(index, single_peer); + + // when a web seed fails a hash check, do the following: + // 1. if the whole piece only overlaps a single file, mark that file as not + // have for this peer + // 2. if the piece overlaps more than one file, mark the piece as not have + // for this peer + // 3. if it's a single file torrent, just ban it right away + // this handles the case where web seeds may have some files updated but not other + + boost::shared_ptr t = associated_torrent().lock(); + file_storage const& fs = t->torrent_file().files(); + + // single file torrent + if (fs.num_files() == 1) return peer_connection::received_invalid_data(index, single_peer); + + std::vector files = fs.map_block(index, 0, fs.piece_size(index)); + + if (files.size() == 1) + { + // assume the web seed has a different copy of this specific file + // than what we expect, and pretend not to have it. + int fi = files[0].file_index; + int first_piece = int(fs.file_offset(fi) / fs.piece_length()); + // one past last piece + int end_piece = int((fs.file_offset(fi) + fs.file_size(fi) + 1) / fs.piece_length()); + for (int i = first_piece; i < end_piece; ++i) + incoming_dont_have(i); + } + else + { + incoming_dont_have(index); + } + + peer_connection::received_invalid_data(index, single_peer); + + // if we don't think we have any of the files, allow banning the web seed + if (num_have_pieces() == 0) return true; + + // don't disconnect, we won't request anything from this file again + return false; + } + + void web_peer_connection::on_receive(error_code const& error + , std::size_t bytes_transferred) + { + INVARIANT_CHECK; + +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + bytes_transferred < size_t(INT_MAX)); + int dl_target = m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + bytes_transferred; +#endif + + if (error) + { + m_statistics.received_bytes(0, bytes_transferred); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** web_peer_connection error: %s", error.message().c_str()); +#endif +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + return; + } + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + for (;;) + { +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + int(bytes_transferred) + == dl_target); +#endif + + buffer::const_interval recv_buffer = receive_buffer(); + + int payload; + int protocol; + bool header_finished = m_parser.header_finished(); + if (!header_finished) + { + bool failed = false; + boost::tie(payload, protocol) = m_parser.incoming(recv_buffer, failed); + m_statistics.received_bytes(0, protocol); + TORRENT_ASSERT(int(bytes_transferred) >= protocol); + bytes_transferred -= protocol; + + if (failed) + { + m_statistics.received_bytes(0, bytes_transferred); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** %s", std::string(recv_buffer.begin, recv_buffer.end).c_str()); +#endif + disconnect(errors::http_parse_error, 2); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + return; + } + + TORRENT_ASSERT(recv_buffer.left() == 0 || *recv_buffer.begin == 'H'); + + TORRENT_ASSERT(recv_buffer.left() <= packet_size()); + + // this means the entire status line hasn't been received yet + if (m_parser.status_code() == -1) + { + TORRENT_ASSERT(payload == 0); + TORRENT_ASSERT(bytes_transferred == 0); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + int(bytes_transferred) + == dl_target); +#endif + break; + } + + if (!m_parser.header_finished()) + { + TORRENT_ASSERT(payload == 0); + TORRENT_ASSERT(bytes_transferred == 0); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + int(bytes_transferred) + == dl_target); +#endif + break; + } + + m_body_start = m_parser.body_start(); + m_received_body = 0; + } + + // we just completed reading the header + if (!header_finished) + { + ++m_num_responses; + + if (m_parser.connection_close()) + { + incoming_choke(); + if (m_num_responses == 1) + m_web->supports_keepalive = false; + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** STATUS: %d %s", m_parser.status_code(), m_parser.message().c_str()); + std::multimap const& headers = m_parser.headers(); + for (std::multimap::const_iterator i = headers.begin() + , end(headers.end()); i != end; ++i) + peer_log(" %s: %s", i->first.c_str(), i->second.c_str()); +#endif + // if the status code is not one of the accepted ones, abort + if (!is_ok_status(m_parser.status_code())) + { + // TODO: 3 just make this peer not have the pieces + // associated with the file we just requested. Only + // when it doesn't have any of the file do the following + int retry_time = atoi(m_parser.header("retry-after").c_str()); + if (retry_time <= 0) retry_time = m_ses.settings().urlseed_wait_retry; + // temporarily unavailable, retry later + t->retry_web_seed(this, retry_time); + std::string error_msg = to_string(m_parser.status_code()).elems + + (" " + m_parser.message()); + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(url_seed_alert(t->get_handle(), m_url + , error_msg)); + } + m_statistics.received_bytes(0, bytes_transferred); + disconnect(error_code(m_parser.status_code(), get_http_category()), 1); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + return; + } + if (is_redirect(m_parser.status_code())) + { + // this means we got a redirection request + // look for the location header + std::string location = m_parser.header("location"); + m_statistics.received_bytes(0, bytes_transferred); + + if (location.empty()) + { + // we should not try this server again. + t->remove_web_seed(this); + m_web = NULL; + disconnect(errors::missing_location, 2); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + return; + } + + bool single_file_request = false; + if (!m_path.empty() && m_path[m_path.size() - 1] != '/') + single_file_request = true; + + // add the redirected url and remove the current one + if (!single_file_request) + { + TORRENT_ASSERT(!m_file_requests.empty()); + int file_index = m_file_requests.front(); + +// TODO: 2 create a mapping of file-index to redirection URLs. Use that to form +// URLs instead. Support to reconnect to a new server without destructing this +// peer_connection + torrent_info const& info = t->torrent_file(); + std::string path = info.orig_files().file_path(file_index); +#ifdef TORRENT_WINDOWS + convert_path_to_posix(path); +#endif + path = escape_path(path.c_str(), path.length()); + size_t i = location.rfind(path); + if (i == std::string::npos) + { + t->remove_web_seed(this); + m_web = NULL; + disconnect(errors::invalid_redirection, 2); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + return; + } + location.resize(i); + } + else + { + location = resolve_redirect_location(m_url, location); + } + t->add_web_seed(location, web_seed_entry::url_seed + , m_external_auth, m_extra_headers); + t->remove_web_seed(this); + m_web = NULL; + disconnect(errors::redirecting, 2); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + return; + } + + std::string const& server_version = m_parser.header("server"); + if (!server_version.empty()) + { + m_server_string = "URL seed @ "; + m_server_string += m_host; + m_server_string += " ("; + m_server_string += server_version; + m_server_string += ")"; + } + + m_body_start = m_parser.body_start(); + m_received_body = 0; + m_range_pos = 0; + } + + recv_buffer.begin += m_body_start; + + // we only received the header, no data + if (recv_buffer.left() == 0) + { +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + break; + } + + size_type range_start; + size_type range_end; + if (m_parser.status_code() == 206) + { + boost::tie(range_start, range_end) = m_parser.content_range(); + if (range_start < 0 || range_end < range_start) + { + m_statistics.received_bytes(0, bytes_transferred); + // we should not try this server again. + t->remove_web_seed(this); + m_web = NULL; + disconnect(errors::invalid_range); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + return; + } + // the http range is inclusive + range_end++; + } + else + { + range_start = 0; + range_end = m_parser.content_length(); + if (range_end == -1) + { + m_statistics.received_bytes(0, bytes_transferred); + // we should not try this server again. + t->remove_web_seed(this); + m_web = NULL; + disconnect(errors::no_content_length, 2); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + return; + } + } + + // ========================= + // === CHUNKED ENCODING === + // ========================= + while (m_parser.chunked_encoding() + && m_chunk_pos >= 0 + && m_chunk_pos < recv_buffer.left()) + { + int header_size = 0; + size_type chunk_size = 0; + buffer::const_interval chunk_start = recv_buffer; + chunk_start.begin += m_chunk_pos; + TORRENT_ASSERT(chunk_start.begin[0] == '\r' || is_hex(chunk_start.begin, 1)); + bool ret = m_parser.parse_chunk_header(chunk_start, &chunk_size, &header_size); + if (!ret) + { + TORRENT_ASSERT(int(bytes_transferred) >= chunk_start.left() - m_partial_chunk_header); + bytes_transferred -= chunk_start.left() - m_partial_chunk_header; + m_statistics.received_bytes(0, chunk_start.left() - m_partial_chunk_header); + m_partial_chunk_header = chunk_start.left(); + if (bytes_transferred == 0) return; + break; + } + else + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** parsed chunk: %d header_size: %d", chunk_size, header_size); +#endif + TORRENT_ASSERT(int(bytes_transferred) >= header_size - m_partial_chunk_header); + bytes_transferred -= header_size - m_partial_chunk_header; + m_statistics.received_bytes(0, header_size - m_partial_chunk_header); + m_partial_chunk_header = 0; + TORRENT_ASSERT(chunk_size != 0 || chunk_start.left() <= header_size || chunk_start.begin[header_size] == 'H'); + // cut out the chunk header from the receive buffer + TORRENT_ASSERT(m_body_start + m_chunk_pos < INT_MAX); + cut_receive_buffer(header_size, t->block_size() + request_size_overhead, int(m_body_start + m_chunk_pos)); + recv_buffer = receive_buffer(); + recv_buffer.begin += m_body_start; + m_chunk_pos += chunk_size; + if (chunk_size == 0) + { +#ifdef TORRENT_DEBUG + chunk_start = recv_buffer; + chunk_start.begin += m_chunk_pos; + TORRENT_ASSERT(chunk_start.left() == 0 || chunk_start.begin[0] == 'H'); +#endif + m_chunk_pos = -1; + } + // if all of hte receive buffer was just consumed as chunk + // header, we're done + if (bytes_transferred == 0) return; + } + } + + if (m_requests.empty() || m_file_requests.empty()) + { + m_statistics.received_bytes(0, bytes_transferred); + disconnect(errors::http_error, 2); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + return; + } + + size_type left_in_response = range_end - range_start - m_range_pos; + int payload_transferred = int((std::min)(left_in_response, size_type(bytes_transferred))); + + torrent_info const& info = t->torrent_file(); + + peer_request front_request = m_requests.front(); + + TORRENT_ASSERT(m_block_pos >= 0); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** payload_transferred: %d [ %d:%d = %d ]" + , payload_transferred, front_request.piece + , front_request.start, front_request.length); +#endif + m_statistics.received_bytes(payload_transferred, 0); + TORRENT_ASSERT(int(bytes_transferred) >= payload_transferred); + bytes_transferred -= payload_transferred; + m_range_pos += payload_transferred; + m_block_pos += payload_transferred; + if (m_range_pos > range_end - range_start) m_range_pos = range_end - range_start; + + int file_index = m_file_requests.front(); + peer_request in_range = info.orig_files().map_file(file_index, range_start + , int(range_end - range_start)); + + // request start + size_type rs = size_type(in_range.piece) * info.piece_length() + in_range.start; + // request end + size_type re = rs + in_range.length; + // file start + size_type fs = size_type(front_request.piece) * info.piece_length() + front_request.start; + + // the http response body consists of 3 parts + // 1. the middle of a block or the ending of a block + // 2. a number of whole blocks + // 3. the start of a block + // in that order, these parts are parsed. + + bool range_overlaps_request = re >= fs + int(m_piece.size()); + + if (!range_overlaps_request) + { + incoming_piece_fragment((std::min)(payload_transferred + , front_request.length - m_block_pos)); + m_statistics.received_bytes(0, bytes_transferred); + // this means the end of the incoming request ends _before_ the + // first expected byte (fs + m_piece.size()) + + disconnect(errors::invalid_range, 2); + return; + } + + // if the request is contained in the range (i.e. the entire request + // fits in the range) we should not start a partial piece, since we soon + // will receive enough to call incoming_piece() and pass the read buffer + // directly (in the next loop below). + if (range_overlaps_request + && !range_contains(in_range, front_request, info.piece_length())) + { + // the start of the next block to receive is stored + // in m_piece. We need to append the rest of that + // block from the http receive buffer and then + // (if it completed) call incoming_piece() with + // m_piece as buffer. + + int piece_size = int(m_piece.size()); + int copy_size = (std::min)((std::min)(front_request.length - piece_size + , recv_buffer.left()), int(range_end - range_start - m_received_body)); + if (copy_size > m_chunk_pos && m_chunk_pos > 0) copy_size = m_chunk_pos; + if (copy_size > 0) + { + TORRENT_ASSERT(int(m_piece.size()) == m_received_in_piece); + m_piece.resize(piece_size + copy_size); + std::memcpy(&m_piece[0] + piece_size, recv_buffer.begin, copy_size); + TORRENT_ASSERT(int(m_piece.size()) <= front_request.length); + recv_buffer.begin += copy_size; + m_received_body += copy_size; + m_body_start += copy_size; + if (m_chunk_pos > 0) + { + TORRENT_ASSERT(m_chunk_pos >= copy_size); + m_chunk_pos -= copy_size; + } + TORRENT_ASSERT(m_received_body <= range_end - range_start); + TORRENT_ASSERT(int(m_piece.size()) <= front_request.length); + incoming_piece_fragment(copy_size); + TORRENT_ASSERT(int(m_piece.size()) == m_received_in_piece); + } + + if (maybe_harvest_block()) + recv_buffer = receive_buffer(); + if (associated_torrent().expired()) return; + } + + // report all received blocks to the bittorrent engine + while (!m_requests.empty() + && range_contains(in_range, m_requests.front(), info.piece_length()) + && m_block_pos >= m_requests.front().length) + { + peer_request r = m_requests.front(); + TORRENT_ASSERT(recv_buffer.left() >= r.length); + + incoming_piece_fragment(r.length); + incoming_piece(r, recv_buffer.begin); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== POP REQUEST [ piece: %d start: %d len: %d ]" + , r.piece, r.start, r.length); +#endif + m_requests.pop_front(); + if (associated_torrent().expired()) return; + TORRENT_ASSERT(m_block_pos >= r.length); + m_block_pos -= r.length; + m_received_body += r.length; + TORRENT_ASSERT(receive_buffer().begin + m_body_start == recv_buffer.begin); + TORRENT_ASSERT(m_received_body <= range_end - range_start); + cut_receive_buffer(m_body_start + r.length, t->block_size() + request_size_overhead); + if (m_chunk_pos > 0) + { + TORRENT_ASSERT(m_chunk_pos >= r.length); + m_chunk_pos -= r.length; + } + m_body_start = 0; + recv_buffer = receive_buffer(); + } + + if (!m_requests.empty()) + { + if (in_range.start + in_range.length < m_requests.front().start + m_requests.front().length + && (m_received_body + recv_buffer.left() >= range_end - range_start)) + { + int piece_size = int(m_piece.size()); + int copy_size = (std::min)((std::min)(m_requests.front().length - piece_size + , recv_buffer.left()), int(range_end - range_start - m_received_body)); + TORRENT_ASSERT(copy_size >= 0); + if (copy_size > 0) + { + TORRENT_ASSERT(int(m_piece.size()) == m_received_in_piece); + m_piece.resize(piece_size + copy_size); + std::memcpy(&m_piece[0] + piece_size, recv_buffer.begin, copy_size); + recv_buffer.begin += copy_size; + m_received_body += copy_size; + m_body_start += copy_size; + incoming_piece_fragment(copy_size); + TORRENT_ASSERT(int(m_piece.size()) == m_received_in_piece); + } + TORRENT_ASSERT(m_received_body == range_end - range_start); + } + } + + TORRENT_ASSERT(m_received_body <= range_end - range_start); + // if we're in chunked encoding mode, we have to wait for the complete + // tail header before we can consider have received the block, otherwise + // we'll get out of sync with the next http response. m_chunk_pos is set + // to -1 when the tail header has been received + if (m_received_body == range_end - range_start + && (!m_parser.chunked_encoding() || m_chunk_pos == -1)) + { + int size_to_cut = recv_buffer.begin - receive_buffer().begin; + + TORRENT_ASSERT(receive_buffer().left() < size_to_cut + 1 + || receive_buffer()[size_to_cut] == 'H'); + + cut_receive_buffer(size_to_cut, t->block_size() + request_size_overhead); + if (m_chunk_pos > 0) + { + TORRENT_ASSERT(m_chunk_pos >= size_to_cut); + m_chunk_pos -= size_to_cut; + } + recv_buffer = receive_buffer(); + m_file_requests.pop_front(); + m_parser.reset(); + m_body_start = 0; + m_received_body = 0; + m_chunk_pos = 0; + m_partial_chunk_header = 0; + + handle_padfile(recv_buffer); + if (associated_torrent().expired()) return; + continue; + } + + if (bytes_transferred == 0 || payload_transferred == 0) + { +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + break; + } + TORRENT_ASSERT(payload_transferred > 0); + } + TORRENT_ASSERT(bytes_transferred == 0); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() == dl_target); +#endif + } + + void web_peer_connection::get_specific_peer_info(peer_info& p) const + { + web_connection_base::get_specific_peer_info(p); + p.flags |= peer_info::local_connection; + p.connection_type = peer_info::web_seed; + } + + void web_peer_connection::handle_padfile(buffer::const_interval& recv_buffer) + { + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + torrent_info const& info = t->torrent_file(); + + while (!m_file_requests.empty() + && info.orig_files().pad_file_at(m_file_requests.front())) + { + // the next file is a pad file. We didn't actually send + // a request for this since it most likely doesn't exist on + // the web server anyway. Just pretend that we received a + // bunch of zeroes here and pop it again + int file_index = m_file_requests.front(); + m_file_requests.pop_front(); + size_type file_size = info.orig_files().file_size(file_index); + + peer_request front_request = m_requests.front(); + + TORRENT_ASSERT(m_block_pos < front_request.length); + int pad_size = int((std::min)(file_size, size_type(front_request.length - m_block_pos))); + + // insert zeroes to represent the pad file + m_piece.resize(m_piece.size() + size_t(pad_size), 0); + m_block_pos += pad_size; + incoming_piece_fragment(pad_size); + + if (maybe_harvest_block()) + recv_buffer = receive_buffer(); + if (associated_torrent().expired()) return; + } + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/xml_parse.cpp b/apps/Launcher/ext/libtorrent/src/xml_parse.cpp new file mode 100644 index 0000000000..83f3447014 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/xml_parse.cpp @@ -0,0 +1,218 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + + +#include "libtorrent/xml_parse.hpp" + +namespace libtorrent +{ + + TORRENT_EXTRA_EXPORT void xml_parse(char* p, char* end + , boost::function callback) + { + for(;p != end; ++p) + { + char const* start = p; + char const* val_start = 0; + int token; + // look for tag start + for(; p != end && *p != '<'; ++p); + + if (p != start) + { + if (p != end) + { + TORRENT_ASSERT(*p == '<'); + *p = 0; + } + token = xml_string; + callback(token, start, val_start); + if (p != end) *p = '<'; + } + + if (p == end) break; + + // skip '<' + ++p; + if (p != end && p+8 < end && string_begins_no_case("![CDATA[", p)) + { + // CDATA. match '![CDATA[' + p += 8; + start = p; + while (p != end && !string_begins_no_case("]]>", p-2)) ++p; + + // parse error + if (p == end) + { + token = xml_parse_error; + start = "unexpected end of file"; + callback(token, start, val_start); + break; + } + + token = xml_string; + char tmp = p[-2]; + p[-2] = 0; + callback(token, start, val_start); + p[-2] = tmp; + continue; + } + + // parse the name of the tag. + for (start = p; p != end && *p != '>' && !is_space(*p); ++p); + + char* tag_name_end = p; + + // skip the attributes for now + for (; p != end && *p != '>'; ++p); + + // parse error + if (p == end) + { + token = xml_parse_error; + start = "unexpected end of file"; + callback(token, start, val_start); + break; + } + + TORRENT_ASSERT(*p == '>'); + // save the character that terminated the tag name + // it could be both '>' and ' '. + char save = *tag_name_end; + *tag_name_end = 0; + + char* tag_end = p; + if (*start == '/') + { + ++start; + token = xml_end_tag; + callback(token, start, val_start); + } + else if (*(p-1) == '/') + { + *(p-1) = 0; + token = xml_empty_tag; + callback(token, start, val_start); + *(p-1) = '/'; + tag_end = p - 1; + } + else if (*start == '?' && *(p-1) == '?') + { + *(p-1) = 0; + ++start; + token = xml_declaration_tag; + callback(token, start, val_start); + *(p-1) = '?'; + tag_end = p - 1; + } + else if (start + 5 < p && std::memcmp(start, "!--", 3) == 0 && std::memcmp(p-2, "--", 2) == 0) + { + start += 3; + *(p-2) = 0; + token = xml_comment; + callback(token, start, val_start); + *(p-2) = '-'; + tag_end = p - 2; + continue; + } + else + { + token = xml_start_tag; + callback(token, start, val_start); + } + + *tag_name_end = save; + + // parse attributes + for (char* i = tag_name_end; i < tag_end; ++i) + { + // find start of attribute name + for (; i != tag_end && is_space(*i); ++i); + if (i == tag_end) break; + start = i; + // find end of attribute name + for (; i != tag_end && *i != '=' && !is_space(*i); ++i); + char* name_end = i; + + // look for equality sign + for (; i != tag_end && *i != '='; ++i); + + // no equality sign found. Report this as xml_tag_content + // instead of a series of key value pairs + if (i == tag_end) + { + char tmp = *i; + *i = 0; // null terminate the content string + token = xml_tag_content; + val_start = 0; + callback(token, start, val_start); + *i = tmp; + break; + } + + ++i; + for (; i != tag_end && is_space(*i); ++i); + // check for parse error (values must be quoted) + if (i == tag_end || (*i != '\'' && *i != '\"')) + { + token = xml_parse_error; + val_start = 0; + start = "unquoted attribute value"; + callback(token, start, val_start); + break; + } + char quote = *i; + ++i; + val_start = i; + for (; i != tag_end && *i != quote; ++i); + // parse error (missing end quote) + if (i == tag_end) + { + token = xml_parse_error; + val_start = 0; + start = "missing end quote on attribute"; + callback(token, start, val_start); + break; + } + save = *i; + *i = 0; + *name_end = 0; + token = xml_attribute; + callback(token, start, val_start); + *name_end = '='; + *i = save; + } + } + } + +} + diff --git a/support/cmake/handle_external_library.cmake b/support/cmake/handle_external_library.cmake index bd75cce586..cc0d6e543a 100644 --- a/support/cmake/handle_external_library.cmake +++ b/support/cmake/handle_external_library.cmake @@ -31,7 +31,7 @@ function (include_external_library target_name library_name path) add_subdirectory(${path}) get_property(INCLUDE_DIR TARGET ${target_name} PROPERTY INTERFACE_INCLUDE_DIRECTORIES) target_link_libraries(${target_name} ${library_name}) - target_include_directories(${target_name} PUBLIC ${INCLUDE_DIR}) + target_include_directories(${target_name} PUBLIC SYSTEM ${INCLUDE_DIR}) set_property(TARGET ${library_name} PROPERTY FOLDER "External") if (OPENSPACE_DISABLE_EXTERNAL_WARNINGS) if (MSVC) From f369d0e1f33608cf01b7575545c479a34907291f Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 10 Jun 2015 20:35:11 +0200 Subject: [PATCH 119/329] Moved selection of scenes to sync in Launcher into syncwidget --- apps/Launcher/mainwindow.cpp | 3 +- apps/Launcher/syncwidget.cpp | 67 ++++++++++++++++++++++++++++-------- apps/Launcher/syncwidget.h | 15 +++++++- ext/ghoul | 2 +- 4 files changed, 69 insertions(+), 18 deletions(-) diff --git a/apps/Launcher/mainwindow.cpp b/apps/Launcher/mainwindow.cpp index 138a083a13..a38594d25d 100644 --- a/apps/Launcher/mainwindow.cpp +++ b/apps/Launcher/mainwindow.cpp @@ -152,6 +152,7 @@ void MainWindow::initialize() { _sceneFiles.insert(i.fileName(), i.absoluteFilePath()); _scenes->addItem(i.fileName()); } + _syncWidget.setSceneFiles(_sceneFiles); } void MainWindow::shortcutButtonPressed() { @@ -159,8 +160,6 @@ void MainWindow::shortcutButtonPressed() { } void MainWindow::syncButtonPressed() { - QString currentScene = _scenes->currentText(); - _syncWidget.setSceneFile(_sceneFiles[currentScene]); _syncWidget.show(); } diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 7d6371a1b6..fc12bc950a 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -30,20 +30,40 @@ #include #include +#include #include #include +#include +#include +#include #include +#include namespace { + const int nColumns = 3; } SyncWidget::SyncWidget(QWidget* parent) : QWidget(parent) + , _sceneLayout(nullptr) { setFixedSize(500, 500); - ghoul::initialize(); + QBoxLayout* layout = new QVBoxLayout; + { + QGroupBox* sceneBox = new QGroupBox; + _sceneLayout = new QGridLayout; + sceneBox->setLayout(_sceneLayout); + layout->addWidget(sceneBox); + } + { + QPushButton* syncButton = new QPushButton("Synchronize Scenes"); + layout->addWidget(syncButton); + } + setLayout(layout); + + ghoul::initialize(); openspace::DownloadManager::initialize("http://openspace.itn.liu.se/request.cgi"); } @@ -52,10 +72,38 @@ SyncWidget::~SyncWidget() { ghoul::deinitialize(); } -void SyncWidget::setSceneFile(QString scene) { +void SyncWidget::setSceneFiles(QMap sceneFiles) { + _sceneFiles = std::move(sceneFiles); + qDebug() << _sceneFiles; + QStringList keys = _sceneFiles.keys(); + qDebug() << keys; + for (int i = 0; i < keys.size(); ++i) { + const QString& sceneName = keys[i]; + + QCheckBox* checkbox = new QCheckBox(sceneName); + + _sceneLayout->addWidget(checkbox, i / nColumns, i % nColumns); + } +} + +void SyncWidget::clear() { + +} + +void SyncWidget::handleDirectFiles(QString module, QStringList files) { + qDebug() << files; +} + +void SyncWidget::handleTorrentFiles(QString module, QStringList torrents) { + qDebug() << torrents; +} + +void SyncWidget::syncButtonPressed() { clear(); - qDebug() << scene; + + + qDebug() << _sceneFiles; ghoul::Dictionary sceneDictionary; ghoul::lua::loadDictionaryFromFile( @@ -100,7 +148,7 @@ void SyncWidget::setSceneFile(QString scene) { } handleDirectFiles(module, files); } - + found = dataDictionary.getValue("Torrents", torrentFiles); if (found) { QStringList torrents; @@ -114,15 +162,6 @@ void SyncWidget::setSceneFile(QString scene) { } } -void SyncWidget::clear() { - +QStringList SyncWidget::selectedScenes() const { } - -void SyncWidget::handleDirectFiles(QString module, QStringList files) { - qDebug() << files; -} - -void SyncWidget::handleTorrentFiles(QString module, QStringList torrents) { - qDebug() << torrents; -} diff --git a/apps/Launcher/syncwidget.h b/apps/Launcher/syncwidget.h index be78e6a332..4970488523 100644 --- a/apps/Launcher/syncwidget.h +++ b/apps/Launcher/syncwidget.h @@ -27,17 +27,30 @@ #include +#include + +class QGridLayout; + class SyncWidget : public QWidget { +Q_OBJECT public: SyncWidget(QWidget* parent); ~SyncWidget(); - void setSceneFile(QString scene); + void setSceneFiles(QMap sceneFiles); + +private slots: + void syncButtonPressed(); private: void clear(); + QStringList selectedScenes() const; + void handleDirectFiles(QString module, QStringList files); void handleTorrentFiles(QString module, QStringList torrents); + + QMap _sceneFiles; + QGridLayout* _sceneLayout; }; #endif // __SYNCWIDGET_H__ diff --git a/ext/ghoul b/ext/ghoul index eb66778e0a..0dbde3f6ee 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit eb66778e0aaf1809fff1a1476872594a24ac0ac3 +Subproject commit 0dbde3f6ee4817a0a8a947d8790e856c7de7d74b From 549992b5e3e7b609156e63a215cb44cad791d3df Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 11 Jun 2015 02:03:50 +0200 Subject: [PATCH 120/329] Work to using the request mechanism for downloading --- apps/Launcher/mainwindow.cpp | 1 + apps/Launcher/syncwidget.cpp | 184 +++++++++++++++------ apps/Launcher/syncwidget.h | 29 +++- include/openspace/engine/downloadmanager.h | 22 ++- src/engine/downloadmanager.cpp | 38 ++++- src/engine/openspaceengine.cpp | 3 +- 6 files changed, 215 insertions(+), 62 deletions(-) diff --git a/apps/Launcher/mainwindow.cpp b/apps/Launcher/mainwindow.cpp index a38594d25d..2efa0fc3eb 100644 --- a/apps/Launcher/mainwindow.cpp +++ b/apps/Launcher/mainwindow.cpp @@ -153,6 +153,7 @@ void MainWindow::initialize() { _scenes->addItem(i.fileName()); } _syncWidget.setSceneFiles(_sceneFiles); + _syncWidget.setModulesDirectory(ModulesDirectory); } void MainWindow::shortcutButtonPressed() { diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index fc12bc950a..c984031480 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -41,6 +42,17 @@ namespace { const int nColumns = 3; + + const int DownloadApplicationVersion = 1; + + const std::string FileDownloadKey = "FileDownload"; + const std::string FileRequestKey = "FileRequest"; + const std::string TorrentFilesKey = "TorrentFiles"; + + const std::string UrlKey = "URL"; + const std::string DestinationKey = "Destination"; + const std::string IdentifierKey = "Identifier"; + const std::string VersionKey = "Version"; } SyncWidget::SyncWidget(QWidget* parent) @@ -58,13 +70,18 @@ SyncWidget::SyncWidget(QWidget* parent) } { QPushButton* syncButton = new QPushButton("Synchronize Scenes"); + QObject::connect( + syncButton, SIGNAL(clicked(bool)), + this, SLOT(syncButtonPressed()) + ); + layout->addWidget(syncButton); } setLayout(layout); ghoul::initialize(); - openspace::DownloadManager::initialize("http://openspace.itn.liu.se/request.cgi"); + openspace::DownloadManager::initialize("http://openspace.itn.liu.se/data/request", DownloadApplicationVersion); } SyncWidget::~SyncWidget() { @@ -86,82 +103,147 @@ void SyncWidget::setSceneFiles(QMap sceneFiles) { } } +void SyncWidget::setModulesDirectory(QString modulesDirectory) { + _modulesDirectory = std::move(modulesDirectory); +} + void SyncWidget::clear() { } -void SyncWidget::handleDirectFiles(QString module, QStringList files) { - qDebug() << files; +void SyncWidget::handleDirectFiles(QString module, DirectFiles files) { + qDebug() << "Direct Files"; + for (const DirectFile& f : files) { + qDebug() << f.url << " -> " << f.destination; + + DlManager.downloadFile( + f.url.toStdString(), + fullPath(module, f.destination).toStdString(), + [](const ghoul::filesystem::File& f) { qDebug() << QString::fromStdString(f.filename()) << "finished";} + ); + } } -void SyncWidget::handleTorrentFiles(QString module, QStringList torrents) { - qDebug() << torrents; +void SyncWidget::handleFileRequest(QString module, FileRequests files) { + qDebug() << "File Requests"; + for (const FileRequest& f : files) { + qDebug() << f.identifier << " (" << f.version << ")" << " -> " << f.destination; + + + } +} + +void SyncWidget::handleTorrentFiles(QString module, TorrentFiles files) { + qDebug() << "Torrent Files"; + for (const TorrentFile& f : files) { + qDebug() << f.file; + } } void SyncWidget::syncButtonPressed() { clear(); - + for (const QString& scene : selectedScenes()) { + qDebug() << scene; + ghoul::Dictionary sceneDictionary; + ghoul::lua::loadDictionaryFromFile( + scene.toStdString(), + sceneDictionary + ); - qDebug() << _sceneFiles; + ghoul::Dictionary modules; + bool success = sceneDictionary.getValue("Modules", modules); + qDebug() << success; - ghoul::Dictionary sceneDictionary; - ghoul::lua::loadDictionaryFromFile( - scene.toStdString(), - sceneDictionary - ); + QStringList modulesList; + for (int i = 1; i <= modules.size(); ++i) { + std::string module = modules.value(std::to_string(i)); + modulesList.append(QString::fromStdString(module)); + } + qDebug() << modulesList; - ghoul::Dictionary modules; - bool success = sceneDictionary.getValue("Modules", modules); - qDebug() << success; + QDir sceneDir(scene); + sceneDir.cdUp(); + for (QString module : modulesList) { + QString dataFile = sceneDir.absoluteFilePath(module + "/" + module + ".data"); - QStringList modulesList; - for (int i = 1; i < modules.size(); ++i) { - std::string module = modules.value(std::to_string(i)); - modulesList.append(QString::fromStdString(module)); - } - qDebug() << modulesList; + qDebug() << module; + qDebug() << dataFile << QFileInfo(dataFile).exists(); - QDir sceneDir(scene); - sceneDir.cdUp(); - for (QString module : modulesList) { - QString moduleFile = sceneDir.absoluteFilePath(module + "/" + module + ".mod"); - QString dataFile = sceneDir.absoluteFilePath(module + "/" + module + ".data"); + if (QFileInfo(dataFile).exists()) { + ghoul::Dictionary dataDictionary; + ghoul::lua::loadDictionaryFromFile(dataFile.toStdString(), dataDictionary); - qDebug() << module; - qDebug() << moduleFile << QFileInfo(moduleFile).exists(); - qDebug() << dataFile << QFileInfo(dataFile).exists(); + ghoul::Dictionary directDownloadFiles; + ghoul::Dictionary fileRequests; + ghoul::Dictionary torrentFiles; - if (QFileInfo(dataFile).exists()) { - ghoul::Dictionary dataDictionary; - ghoul::lua::loadDictionaryFromFile(dataFile.toStdString(), dataDictionary); + bool found = dataDictionary.getValue(FileDownloadKey, directDownloadFiles); + if (found) { + DirectFiles files; + //QStringList files; + for (int i = 1; i <= directDownloadFiles.size(); ++i) { + ghoul::Dictionary d = directDownloadFiles.value(std::to_string(i)); + std::string url = d.value(UrlKey); + std::string dest = d.value(DestinationKey); - ghoul::Dictionary directFiles; - ghoul::Dictionary torrentFiles; - - bool found = dataDictionary.getValue("Files", directFiles); - if (found) { - QStringList files; - for (int i = 1; i < directFiles.size(); ++i) { - std::string f = directFiles.value(std::to_string(i)); - files.append(QString::fromStdString(f)); + files.append({ + QString::fromStdString(url), + QString::fromStdString(dest) + }); + } + handleDirectFiles(module, files); } - handleDirectFiles(module, files); - } - found = dataDictionary.getValue("Torrents", torrentFiles); - if (found) { - QStringList torrents; - for (int i = 1; i < torrentFiles.size(); ++i) { - std::string f = torrentFiles.value(std::to_string(i)); - torrents.append(QString::fromStdString(f)); + found = dataDictionary.getValue(FileRequestKey, fileRequests); + if (found) { + FileRequests files; + for (int i = 1; i <= fileRequests.size(); ++i) { + ghoul::Dictionary d = fileRequests.value(std::to_string(i)); + std::string url = d.value(IdentifierKey); + std::string dest = d.value(DestinationKey); + int version = static_cast(d.value(VersionKey)); + + + files.append({ + QString::fromStdString(url), + QString::fromStdString(dest), + version + }); + } + handleFileRequest(module, files); + } + + found = dataDictionary.getValue(TorrentFilesKey, torrentFiles); + if (found) { + TorrentFiles torrents; + for (int i = 1; i <= torrentFiles.size(); ++i) { + std::string f = torrentFiles.value(std::to_string(i)); + + torrents.append({QString::fromStdString(f)}); + } + handleTorrentFiles(module, torrents); } - handleTorrentFiles(module, torrents); } } } } QStringList SyncWidget::selectedScenes() const { - + QStringList result; + int nChildren = _sceneLayout->count(); + for (int i = 0; i < nChildren; ++i) { + QWidget* w = _sceneLayout->itemAt(i)->widget(); + QCheckBox* c = static_cast(w); + if (c->isChecked()) { + QString t = c->text(); + result.append(_sceneFiles[t]); + } + } + qDebug() << result; + return result; +} + +QString SyncWidget::fullPath(QString module, QString destination) const { + return _modulesDirectory + "/" + module + "/" + destination; } diff --git a/apps/Launcher/syncwidget.h b/apps/Launcher/syncwidget.h index 4970488523..0353409a3a 100644 --- a/apps/Launcher/syncwidget.h +++ b/apps/Launcher/syncwidget.h @@ -37,19 +37,42 @@ public: SyncWidget(QWidget* parent); ~SyncWidget(); - void setSceneFiles(QMap sceneFiles); + void setSceneFiles(QMap sceneFiles); + void setModulesDirectory(QString modulesDirectory); private slots: void syncButtonPressed(); private: + struct DirectFile { + QString url; + QString destination; + }; + typedef QList DirectFiles; + + struct FileRequest { + QString identifier; + QString destination; + int version; + }; + typedef QList FileRequests; + + struct TorrentFile { + QString file; + }; + typedef QList TorrentFiles; + void clear(); QStringList selectedScenes() const; - void handleDirectFiles(QString module, QStringList files); - void handleTorrentFiles(QString module, QStringList torrents); + QString fullPath(QString module, QString destination) const; + + void handleDirectFiles(QString module, DirectFiles files); + void handleFileRequest(QString module, FileRequests files); + void handleTorrentFiles(QString module, TorrentFiles files); QMap _sceneFiles; + QString _modulesDirectory; QGridLayout* _sceneLayout; }; diff --git a/include/openspace/engine/downloadmanager.h b/include/openspace/engine/downloadmanager.h index c72ef39c34..13f1f1f7e4 100644 --- a/include/openspace/engine/downloadmanager.h +++ b/include/openspace/engine/downloadmanager.h @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -34,18 +35,33 @@ namespace openspace { class DownloadManager : public ghoul::Singleton { public: - typedef std::function DownloadFinishedCallback; + typedef std::function < + void(const ghoul::filesystem::File&, float progress) + > DownloadProgressCallback; + typedef std::function< + void (const ghoul::filesystem::File&) + > DownloadFinishedCallback; - DownloadManager(std::string requestURL); + DownloadManager(std::string requestURL, int applicationVersion); bool downloadFile( const std::string& url, const ghoul::filesystem::File& file, - DownloadFinishedCallback callback = DownloadFinishedCallback() + DownloadFinishedCallback finishedCallback = DownloadFinishedCallback(), + DownloadProgressCallback progressCallback = DownloadProgressCallback() + ); + + bool downloadRequestFiles( + const std::string& identifier, + const ghoul::filesystem::Directory& destination, + int version, + DownloadFinishedCallback finishedCallback = DownloadFinishedCallback(), + DownloadProgressCallback progressCallback = DownloadProgressCallback() ); private: std::string _requestURL; + int _applicationVersion; }; #define DlManager (openspace::DownloadManager::ref()) diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp index 023142cdfb..f6be8283d5 100644 --- a/src/engine/downloadmanager.cpp +++ b/src/engine/downloadmanager.cpp @@ -33,6 +33,10 @@ namespace { const std::string _loggerCat = "DownloadManager"; + + const std::string RequestIdentifier = "identifier"; + const std::string RequestFileVersion = "file_version"; + const std::string RequestApplicationVersion = "application_version"; size_t writeData(void* ptr, size_t size, size_t nmemb, FILE* stream) { size_t written; @@ -43,8 +47,9 @@ namespace { namespace openspace { -DownloadManager::DownloadManager(std::string requestURL) +DownloadManager::DownloadManager(std::string requestURL, int applicationVersion) : _requestURL(std::move(requestURL)) + , _applicationVersion(std::move(applicationVersion)) { curl_global_init(CURL_GLOBAL_ALL); // Check if URL is accessible @@ -53,7 +58,8 @@ DownloadManager::DownloadManager(std::string requestURL) bool DownloadManager::downloadFile( const std::string& url, const ghoul::filesystem::File& file, - DownloadFinishedCallback callback) + DownloadFinishedCallback finishedCallback, + DownloadProgressCallback progressCallback) { CURL* curl = curl_easy_init(); if (curl) { @@ -62,20 +68,44 @@ bool DownloadManager::downloadFile( curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData); curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); + LDEBUG("Starting download for file: '" << url << + "' into file '" << file.filename() << "'"); CURLcode res = curl_easy_perform(curl); curl_easy_cleanup(curl); fclose(fp); + // TODO: incorporate progressCallback ---abock + // http://curl.haxx.se/libcurl/c/progressfunc.html + if (res != CURLE_OK) { LERROR("Error downloading file 'url': " << curl_easy_strerror(res)); return false; } - if (callback) - callback(file); + if (finishedCallback) + finishedCallback(file); return true; } } +bool DownloadManager::downloadRequestFiles( + const std::string& identifier, + const ghoul::filesystem::Directory& destination, + int version, + DownloadFinishedCallback finishedCallback, + DownloadProgressCallback progressCallback) +{ + // Escaping is necessary ---abock + const std::string fullRequest =_requestURL + "?" + + RequestIdentifier + "=" + identifier + "&" + + RequestFileVersion + "=" + std::to_string(version) + "&" + + RequestApplicationVersion = "=" + std::to_string(_applicationVersion); + + + + + return true; +} + } // namespace openspace diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index e2fff913d9..0b9ec647ba 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -87,6 +87,7 @@ namespace { const std::string _sgctConfigArgumentCommand = "-config"; const int CacheVersion = 1; + const int DownloadVersion = 1; struct { std::string configurationName; @@ -292,7 +293,7 @@ bool OpenSpaceEngine::initialize() { std::string requestURL = ""; bool success = configurationManager()->getValue(ConfigurationManager::KeyDownloadRequestURL, requestURL); if (success) - DownloadManager::initialize(requestURL); + DownloadManager::initialize(requestURL, DownloadVersion); // Load SPICE time kernel success = loadSpiceKernels(); From d812d292327446d42637a4568a4fa56a43352d82 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 11 Jun 2015 02:36:28 +0200 Subject: [PATCH 121/329] Making request-based downloading work --- apps/Launcher/syncwidget.cpp | 6 ++++++ src/engine/downloadmanager.cpp | 36 +++++++++++++++++++++++++++++++--- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index c984031480..a444f4c058 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -128,6 +128,12 @@ void SyncWidget::handleFileRequest(QString module, FileRequests files) { qDebug() << "File Requests"; for (const FileRequest& f : files) { qDebug() << f.identifier << " (" << f.version << ")" << " -> " << f.destination; + DlManager.downloadRequestFiles( + f.identifier.toStdString(), + fullPath(module, f.destination).toStdString(), + f.version, + [](const ghoul::filesystem::File& f) { qDebug() << "finished"; } + ); } diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp index f6be8283d5..b40afb8851 100644 --- a/src/engine/downloadmanager.cpp +++ b/src/engine/downloadmanager.cpp @@ -27,6 +27,8 @@ #include #include +#include + #ifdef OPENSPACE_CURL_ENABLED #include #endif @@ -69,7 +71,7 @@ bool DownloadManager::downloadFile( curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData); curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); LDEBUG("Starting download for file: '" << url << - "' into file '" << file.filename() << "'"); + "' into file '" << file.path() << "'"); CURLcode res = curl_easy_perform(curl); curl_easy_cleanup(curl); fclose(fp); @@ -95,12 +97,40 @@ bool DownloadManager::downloadRequestFiles( DownloadFinishedCallback finishedCallback, DownloadProgressCallback progressCallback) { - // Escaping is necessary ---abock + bool s = FileSys.createDirectory(destination, true); + // TODO: Check s ---abock + // TODO: Escaping is necessary ---abock const std::string fullRequest =_requestURL + "?" + RequestIdentifier + "=" + identifier + "&" + RequestFileVersion + "=" + std::to_string(version) + "&" + - RequestApplicationVersion = "=" + std::to_string(_applicationVersion); + RequestApplicationVersion + "=" + std::to_string(_applicationVersion); + LDEBUG("Request: " << fullRequest); + std::string requestFile = absPath("${TEMPORARY}/" + identifier); + LDEBUG("Request File: " << requestFile); + + bool success = downloadFile( + fullRequest, + requestFile, + [destination](const ghoul::filesystem::File& f) { + LDEBUG("Finished: " << f.path()); + std::ifstream temporary(f.path()); + std::string line; + int nFiles = 0; + int nFinished = 0; + while (std::getline(temporary, line)) { + ++nFiles; + std::string file = ghoul::filesystem::File(line).filename(); + + LDEBUG("\tLine: " << line << " ; Dest: " << destination.path() + "/" + file); + bool success = DlManager.downloadFile( + line, + destination.path() + "/" + file, + [&nFinished](const ghoul::filesystem::File& f) { ++nFinished; } + ); + } + } + ); From d396c29f89f0b77fa08a5140bd49d2c1ffaa1623 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 12 Jun 2015 14:08:29 +0200 Subject: [PATCH 122/329] Making libtorrent work with Launcher application --- apps/Launcher/CMakeLists.txt | 32 ++++++++- apps/Launcher/ext/libtorrent/CMakeLists.txt | 74 +++++---------------- apps/Launcher/syncwidget.cpp | 27 ++++++++ ext/ghoul | 2 +- support/cmake/handle_external_library.cmake | 1 - 5 files changed, 75 insertions(+), 61 deletions(-) diff --git a/apps/Launcher/CMakeLists.txt b/apps/Launcher/CMakeLists.txt index 8c58247d0c..706ab201f4 100644 --- a/apps/Launcher/CMakeLists.txt +++ b/apps/Launcher/CMakeLists.txt @@ -67,9 +67,35 @@ if (APPLE) " COMPONENT Runtime) endif () +# if(NOT DEFINED Boost_INCLUDE_DIR OR NOT DEFINED Boost_LIBRARIES) +# FIND_PACKAGE( Boost COMPONENTS system thread date_time chrono) +# endif() +# include_directories(${Boost_INCLUDE_DIR}) +# target_link_libraries(${APPLICATION_NAME} ${Boost_LIBRARIES}) + # Libtorrent -set(encryption OFF CACHE BOOL "") -set(shared OFF CACHE BOOL "") -include_external_library(${APPLICATION_NAME} torrent-rasterbar ${application_path}/ext/libtorrent) +add_subdirectory(${application_path}/ext/libtorrent) +target_link_libraries(${APPLICATION_NAME} libtorrent) +target_compile_definitions(${APPLICATION_NAME} PUBLIC BOOST_ASIO_SEPARATE_COMPILATION) + + +# get_property(INCLUDE_DIR TARGET ${target_name} PROPERTY INTERFACE_INCLUDE_DIRECTORIES) +# target_link_libraries(${target_name} ${library_name}) +target_include_directories(${APPLICATION_NAME} PUBLIC SYSTEM ${application_path}/ext/libtorrent/include) +set_property(TARGET libtorrent PROPERTY FOLDER "External") +if (OPENSPACE_DISABLE_EXTERNAL_WARNINGS) + if (MSVC) + target_compile_options(libtorrent PUBLIC "/W0" "/MP") + else () + target_compile_options(libtorrent PUBLIC "-w") + endif () +endif () + +# include_external_library(${APPLICATION_NAME} libtorrent ${application_path}/ext/libtorrent) +# We have to set it manually since the libtorrent doesn't use the new system that can be queried +# target_include_directories(${APPLICATION_NAME} PUBLIC SYSTEM ${application_path}/ext/libtorrent/include) + +target_compile_definitions(${APPLICATION_NAME} PUBLIC BOOST_LIB_DIAGNOSTIC) +target_compile_definitions(libtorrent PUBLIC BOOST_LIB_DIAGNOSTIC) copy_files(${APPLICATION_NAME} "${CURL_ROOT_DIR}/lib/libcurl.dll") diff --git a/apps/Launcher/ext/libtorrent/CMakeLists.txt b/apps/Launcher/ext/libtorrent/CMakeLists.txt index 9bb7b2cd6f..90015a22bb 100644 --- a/apps/Launcher/ext/libtorrent/CMakeLists.txt +++ b/apps/Launcher/ext/libtorrent/CMakeLists.txt @@ -124,7 +124,6 @@ option(shared "build libtorrent as a shared library" ON) option(static_runtime "build libtorrent with static runtime" OFF) option(tcmalloc "link against google performance tools tcmalloc" OFF) option(pool-allocators "Uses a pool allocator for disk and piece buffers" ON) -option(encryption "link against openssl and enable encryption" ON) option(geoip "link against LGPL GeoIP code from Maxmind, to enable geoip database support" OFF) option(dht "enable support for Mainline DHT" ON) option(resolve-countries "enable support for resolving countries from peer IPs" ON) @@ -133,7 +132,6 @@ option(deprecated-functions "enable deprecated functions for backwards compatibi option(exceptions "build with exception support" ON) option(logging "build with logging" OFF) option(verbose-logging "build with verbose logging" OFF) -option(build_tests "build tests" OFF) set(CMAKE_CONFIGURATION_TYPES Debug Release RelWithDebInfo) @@ -148,25 +146,10 @@ if(UNIX) set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") endif() -if (build_tests) - # this will make some internal functions available in the - # DLL interface, for the tests to access - add_definitions(-DTORRENT_EXPORT_EXTRA) -endif (build_tests) - include_directories(${includes}) -if (encryption) - list(APPEND sources pe_crypto asio_ssl) - if(NOT DEFINED OPENSSL_INCLUDE_DIR OR NOT DEFINED OPENSSL_LIBRARIES) - FIND_PACKAGE(OpenSSL REQUIRED) - endif() - add_definitions(-DTORRENT_USE_OPENSSL) - include_directories(${OPENSSL_INCLUDE_DIR}) -else() - add_definitions(-DTORRENT_DISABLE_ENCRYPTION) - list(APPEND sources sha1) -endif (encryption) +add_definitions(-DTORRENT_DISABLE_ENCRYPTION) +list(APPEND sources sha1) if (logging) add_definitions(-DTORRENT_LOGGING) @@ -197,7 +180,7 @@ endif() if (shared) add_definitions(-DTORRENT_BUILDING_SHARED) - add_library(torrent-rasterbar SHARED ${sources2}) + add_library(libtorrent SHARED ${sources2}) else (shared) if(static_runtime) # fix /MT flag: @@ -215,37 +198,38 @@ else (shared) string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") endforeach() endif() - add_library(torrent-rasterbar STATIC ${sources2}) + add_library(libtorrent STATIC ${sources2}) endif() # Boost +set(Boost_USE_STATIC_LIBS ON) if(NOT DEFINED Boost_INCLUDE_DIR OR NOT DEFINED Boost_LIBRARIES) FIND_PACKAGE( Boost COMPONENTS system thread date_time chrono) endif() include_directories(${Boost_INCLUDE_DIR}) -target_link_libraries(torrent-rasterbar ${Boost_LIBRARIES}) +target_link_libraries(libtorrent ${Boost_LIBRARIES}) + +if (WIN32) + target_link_libraries(libtorrent Iphlpapi.lib) +endif () # this works around a bug in asio in boost-1.39 #add_definitions(-DBOOST_ASIO_HASH_MAP_BUCKETS=1021 -D__USE_W32_SOCKETS -DWIN32_LEAN_AND_MEAN ) -if(NOT static_runtime) - add_definitions(-DBOOST_ASIO_DYN_LINK -DBOOST_DATE_TIME_DYN_LINK -DBOOST_THREAD_DYN_LINK -DBOOST_SYSTEM_DYN_LINK -DBOOST_CHRONO_DYN_LINK) -else() +# if(NOT static_runtime) +# add_definitions(-DBOOST_ASIO_DYN_LINK -DBOOST_DATE_TIME_DYN_LINK -DBOOST_THREAD_DYN_LINK -DBOOST_SYSTEM_DYN_LINK -DBOOST_CHRONO_DYN_LINK) +# else() add_definitions(-DBOOST_ASIO_SEPARATE_COMPILATION) -endif() +# endif() if (WIN32) - target_link_libraries(torrent-rasterbar wsock32 ws2_32) + target_link_libraries(libtorrent wsock32 ws2_32) add_definitions(-D_WIN32_WINNT=0x0600) if (MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") # multicore compilation endif() endif() -if (encryption) - target_link_libraries(torrent-rasterbar ${OPENSSL_LIBRARIES}) -endif() - if (NOT pool-allocators) add_definitions(-DTORRENT_DISABLE_POOL_ALLOCATOR) endif() @@ -303,10 +287,10 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") endif() if (tcmalloc) - target_link_libraries(torrent-rasterbar tcmalloc) + target_link_libraries(libtorrent tcmalloc) endif() -set_target_properties(torrent-rasterbar PROPERTIES +set_target_properties(libtorrent PROPERTIES SOVERSION ${SOVERSION}) get_property (COMPILETIME_OPTIONS_LIST @@ -327,7 +311,7 @@ else() set (LIBDIR "lib") endif() -install(TARGETS torrent-rasterbar DESTINATION ${LIBDIR}) +install(TARGETS libtorrent DESTINATION ${LIBDIR}) install(DIRECTORY include/libtorrent DESTINATION include PATTERN ".svn" EXCLUDE) @@ -337,25 +321,3 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libtorrent-rasterbar.pc DESTINATION ${ #file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/examples) #configure_file(examples/run_cmake.sh.in examples/run_cmake.sh) # to build the examples, run examples/run_cmake.sh after building libtorrent - -# === build tests === -if(build_tests) - FILE(GLOB tests RELATIVE "${PROJECT_SOURCE_DIR}" "test/test_*.cpp") - add_library(test_common STATIC test/main.cpp test/setup_transfer.cpp - test/dht_server.cpp test/udp_tracker.cpp test/peer_server.cpp - test/web_seed_suite.cpp) - enable_testing() - - foreach(s ${tests}) - get_filename_component (sn ${s} NAME_WE) - add_executable(${sn} ${s}) - target_link_libraries(${sn} torrent-rasterbar test_common) - add_test(${sn} ${s}) - endforeach(s) - -# add_executable(test_upnp test/test_upnp.cpp) -# target_link_libraries(test_upnp torrent-rasterbar) - -# add_executable(test_natpmp test/test_natpmp.cpp) -# target_link_libraries(test_natpmp torrent-rasterbar) -endif() diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index a444f4c058..1fa77733a1 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -40,6 +40,10 @@ #include #include +#include +#include +#include + namespace { const int nColumns = 3; @@ -112,6 +116,7 @@ void SyncWidget::clear() { } void SyncWidget::handleDirectFiles(QString module, DirectFiles files) { + return; qDebug() << "Direct Files"; for (const DirectFile& f : files) { qDebug() << f.url << " -> " << f.destination; @@ -125,6 +130,7 @@ void SyncWidget::handleDirectFiles(QString module, DirectFiles files) { } void SyncWidget::handleFileRequest(QString module, FileRequests files) { + return; qDebug() << "File Requests"; for (const FileRequest& f : files) { qDebug() << f.identifier << " (" << f.version << ")" << " -> " << f.destination; @@ -143,6 +149,27 @@ void SyncWidget::handleTorrentFiles(QString module, TorrentFiles files) { qDebug() << "Torrent Files"; for (const TorrentFile& f : files) { qDebug() << f.file; + + libtorrent::session s; + libtorrent::error_code ec; + s.listen_on(std::make_pair(6881, 6889), ec); + if (ec) { + qDebug() << "Failed to open socket: " << QString::fromStdString(ec.message()); + return; + } + + libtorrent::add_torrent_params p; + p.save_path = fullPath(module, ".").toStdString(); + p.ti = new libtorrent::torrent_info(f.file.toStdString(), ec); + if (ec) { + qDebug() << QString::fromStdString(ec.message()); + return; + } + s.add_torrent(p, ec); + if (ec) { + qDebug() << QString::fromStdString(ec.message()); + return; + } } } diff --git a/ext/ghoul b/ext/ghoul index 0dbde3f6ee..f38a8ada58 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 0dbde3f6ee4817a0a8a947d8790e856c7de7d74b +Subproject commit f38a8ada5843b0350610b62120e4a9579e8f96a5 diff --git a/support/cmake/handle_external_library.cmake b/support/cmake/handle_external_library.cmake index cc0d6e543a..27cd7af07f 100644 --- a/support/cmake/handle_external_library.cmake +++ b/support/cmake/handle_external_library.cmake @@ -41,5 +41,4 @@ function (include_external_library target_name library_name path) endif () endif () endif () - endfunction () \ No newline at end of file From da812f3564137d2067a7baaecc8bfb852c7d12eb Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 12 Jun 2015 23:48:11 +0200 Subject: [PATCH 123/329] More cleanup of Launcher Added Infowidget --- apps/Launcher/CMakeLists.txt | 33 +- apps/Launcher/ext/libtorrent/CMakeLists.txt | 440 ++++++++------------ apps/Launcher/infowidget.cpp | 68 +++ apps/Launcher/infowidget.h | 52 +++ apps/Launcher/syncwidget.cpp | 112 ++++- apps/Launcher/syncwidget.h | 16 + openspace.cfg | 2 +- support/cmake/support_macros.cmake | 3 + 8 files changed, 419 insertions(+), 307 deletions(-) create mode 100644 apps/Launcher/infowidget.cpp create mode 100644 apps/Launcher/infowidget.h diff --git a/apps/Launcher/CMakeLists.txt b/apps/Launcher/CMakeLists.txt index 706ab201f4..ecaf7a75b6 100644 --- a/apps/Launcher/CMakeLists.txt +++ b/apps/Launcher/CMakeLists.txt @@ -31,12 +31,14 @@ set(application_path ${OPENSPACE_APPS_DIR}/Launcher) set(SOURCE_FILES ${application_path}/main.cpp + ${application_path}/infowidget.cpp ${application_path}/mainwindow.cpp ${application_path}/shortcutwidget.cpp ${application_path}/syncwidget.cpp ) set(HEADER_FILES + ${application_path}/infowidget.h ${application_path}/mainwindow.h ${application_path}/shortcutwidget.h ${application_path}/syncwidget.h @@ -67,35 +69,6 @@ if (APPLE) " COMPONENT Runtime) endif () -# if(NOT DEFINED Boost_INCLUDE_DIR OR NOT DEFINED Boost_LIBRARIES) -# FIND_PACKAGE( Boost COMPONENTS system thread date_time chrono) -# endif() -# include_directories(${Boost_INCLUDE_DIR}) -# target_link_libraries(${APPLICATION_NAME} ${Boost_LIBRARIES}) - # Libtorrent -add_subdirectory(${application_path}/ext/libtorrent) -target_link_libraries(${APPLICATION_NAME} libtorrent) -target_compile_definitions(${APPLICATION_NAME} PUBLIC BOOST_ASIO_SEPARATE_COMPILATION) - - -# get_property(INCLUDE_DIR TARGET ${target_name} PROPERTY INTERFACE_INCLUDE_DIRECTORIES) -# target_link_libraries(${target_name} ${library_name}) +include_external_library(${APPLICATION_NAME} libtorrent ${application_path}/ext/libtorrent) target_include_directories(${APPLICATION_NAME} PUBLIC SYSTEM ${application_path}/ext/libtorrent/include) -set_property(TARGET libtorrent PROPERTY FOLDER "External") -if (OPENSPACE_DISABLE_EXTERNAL_WARNINGS) - if (MSVC) - target_compile_options(libtorrent PUBLIC "/W0" "/MP") - else () - target_compile_options(libtorrent PUBLIC "-w") - endif () -endif () - -# include_external_library(${APPLICATION_NAME} libtorrent ${application_path}/ext/libtorrent) -# We have to set it manually since the libtorrent doesn't use the new system that can be queried -# target_include_directories(${APPLICATION_NAME} PUBLIC SYSTEM ${application_path}/ext/libtorrent/include) - -target_compile_definitions(${APPLICATION_NAME} PUBLIC BOOST_LIB_DIAGNOSTIC) -target_compile_definitions(libtorrent PUBLIC BOOST_LIB_DIAGNOSTIC) - -copy_files(${APPLICATION_NAME} "${CURL_ROOT_DIR}/lib/libcurl.dll") diff --git a/apps/Launcher/ext/libtorrent/CMakeLists.txt b/apps/Launcher/ext/libtorrent/CMakeLists.txt index 90015a22bb..6e8c73a9da 100644 --- a/apps/Launcher/ext/libtorrent/CMakeLists.txt +++ b/apps/Launcher/ext/libtorrent/CMakeLists.txt @@ -4,320 +4,242 @@ set (SOVERSION "8") set (VERSION "1.0.5") set(sources - web_connection_base - alert - alert_manager - allocator - asio - assert - bandwidth_limit - bandwidth_manager - bandwidth_queue_entry - bloom_filter - chained_buffer - connection_queue - create_torrent - disk_buffer_holder - entry - error_code - file_storage - lazy_bdecode - escape_string - string_util - file - gzip - hasher - http_connection - http_stream - http_parser - i2p_stream - identify_client - ip_filter - ip_voter - peer_connection - bt_peer_connection - web_peer_connection - http_seed_connection - instantiate_connection - natpmp - packet_buffer - piece_picker - policy - puff - random - rss - session - session_impl - settings - socket_io - socket_type - socks5_stream - stat - storage - time - timestamp_history - torrent - torrent_handle - torrent_info - tracker_manager - http_tracker_connection - utf8 - udp_tracker_connection - udp_socket - upnp - utp_socket_manager - utp_stream - logger - file_pool - lsd - disk_buffer_pool - disk_io_thread - enum_net - broadcast_socket - magnet_uri - parse_url - ConvertUTF - thread - xml_parse + web_connection_base + alert + alert_manager + allocator + asio + assert + bandwidth_limit + bandwidth_manager + bandwidth_queue_entry + bloom_filter + chained_buffer + connection_queue + create_torrent + disk_buffer_holder + entry + error_code + file_storage + lazy_bdecode + escape_string + string_util + file + gzip + hasher + http_connection + http_stream + http_parser + i2p_stream + identify_client + ip_filter + ip_voter + peer_connection + bt_peer_connection + web_peer_connection + http_seed_connection + instantiate_connection + natpmp + packet_buffer + piece_picker + policy + puff + random + rss + session + session_impl + settings + socket_io + socket_type + socks5_stream + stat + storage + time + timestamp_history + torrent + torrent_handle + torrent_info + tracker_manager + http_tracker_connection + utf8 + udp_tracker_connection + udp_socket + upnp + utp_socket_manager + utp_stream + logger + file_pool + lsd + disk_buffer_pool + disk_io_thread + enum_net + broadcast_socket + magnet_uri + parse_url + ConvertUTF + thread + xml_parse # -- extensions -- - metadata_transfer - ut_pex - ut_metadata - smart_ban - lt_trackers + metadata_transfer + ut_pex + ut_metadata + smart_ban + lt_trackers ) # -- kademlia -- set(kademlia_sources - dht_tracker - node - refresh - rpc_manager - find_data - node_id - routing_table - traversal_algorithm - logging - item - get_peers - get_item + dht_tracker + node + refresh + rpc_manager + find_data + node_id + routing_table + traversal_algorithm + logging + item + get_peers + get_item ) # -- ed25519 -- set(ed25519_sources - add_scalar - fe - ge - key_exchange - keypair - sc - seed - sha512 - sign - verify + add_scalar + fe + ge + key_exchange + keypair + sc + seed + sha512 + sign + verify ) set(includes include ed25519/src) -option(shared "build libtorrent as a shared library" ON) -option(static_runtime "build libtorrent with static runtime" OFF) -option(tcmalloc "link against google performance tools tcmalloc" OFF) -option(pool-allocators "Uses a pool allocator for disk and piece buffers" ON) -option(geoip "link against LGPL GeoIP code from Maxmind, to enable geoip database support" OFF) -option(dht "enable support for Mainline DHT" ON) -option(resolve-countries "enable support for resolving countries from peer IPs" ON) -option(unicode "enable unicode support" ON) -option(deprecated-functions "enable deprecated functions for backwards compatibility" ON) -option(exceptions "build with exception support" ON) -option(logging "build with logging" OFF) -option(verbose-logging "build with verbose logging" OFF) +option(LIBTORRENT_shared "build libtorrent as a shared library" ON) +option(LIBTORRENT_pool-allocators "Uses a pool allocator for disk and piece buffers" ON) +option(LIBTORRENT_dht "enable support for Mainline DHT" OFF) +option(LIBTORRENT_unicode "enable unicode support" ON) set(CMAKE_CONFIGURATION_TYPES Debug Release RelWithDebInfo) if (NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release FORCE) -endif() + set(CMAKE_BUILD_TYPE Release FORCE) +endif () # add_definitions() doesn't seem to let you say wich build type to apply it to set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DTORRENT_DEBUG") -if(UNIX) - set(CMAKE_C_FLAGS_RELWITHDEBINFO "-Os -g") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") -endif() +if (UNIX) + set(CMAKE_C_FLAGS_RELWITHDEBINFO "-Os -g") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") +endif () -include_directories(${includes}) - -add_definitions(-DTORRENT_DISABLE_ENCRYPTION) list(APPEND sources sha1) -if (logging) - add_definitions(-DTORRENT_LOGGING) -endif() -if (verbose-logging) - add_definitions(-DTORRENT_VERBOSE_LOGGING) -endif() - foreach(s ${sources}) - list(APPEND sources2 src/${s}) -endforeach(s) + list(APPEND sources2 src/${s}) +endforeach () -if (dht) - foreach(s ${kademlia_sources}) - list(APPEND sources2 src/kademlia/${s}) - endforeach(s) - foreach(s ${ed25519_sources}) - list(APPEND sources2 ed25519/src/${s}) - endforeach(s) -else() - add_definitions(-DTORRENT_DISABLE_DHT) +if (LIBTORRENT_dht) + foreach(s ${kademlia_sources}) + list(APPEND sources2 src/kademlia/${s}) + endforeach(s) + foreach(s ${ed25519_sources}) + list(APPEND sources2 ed25519/src/${s}) + endforeach(s) +endif () + +if (LIBTORRENT_shared) + add_definitions(-DTORRENT_BUILDING_SHARED) + add_library(libtorrent SHARED ${sources2}) +else () + add_library(libtorrent STATIC ${sources2}) +endif () + +target_include_directories(libtorrent PUBLIC ${includes}) +target_compile_definitions(libtorrent PUBLIC + TORRENT_DISABLE_ENCRYPTION + TORRENT_DISABLE_RESOLVE_COUNTRIES + TORRENT_DISABLE_GEO_IP + BOOST_ASIO_SEPARATE_COMPILATION + BOOST_EXCEPTION_DISABLE + BOOST_ASIO_ENABLE_CANCELIO + _FILE_OFFSET_BITS=64 +) + +if (NOT LIBTORRENT_dht) + target_compile_definitions(libtorrent PUBLIC TORRENT_DISABLE_DHT) +endif () + +if (NOT LIBTORRENT_pool-allocators) + target_compile_definitions(libtorrent PUBLIC TORRENT_DISABLE_POOL_ALLOCATOR) endif() -if(NOT MSVC) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") - set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -fvisibility-inlines-hidden") +if (LIBTORRENT_unicode) + add_definitions(-DUNICODE -D_UNICODE) endif() -if (shared) - add_definitions(-DTORRENT_BUILDING_SHARED) - add_library(libtorrent SHARED ${sources2}) -else (shared) - if(static_runtime) - # fix /MT flag: - set(CompilerFlags - CMAKE_CXX_FLAGS - CMAKE_CXX_FLAGS_DEBUG - CMAKE_CXX_FLAGS_RELWITHDEBINFO - CMAKE_CXX_FLAGS_RELEASE - CMAKE_C_FLAGS - CMAKE_C_FLAGS_DEBUG - CMAKE_CXX_FLAGS_RELWITHDEBINFO - CMAKE_C_FLAGS_RELEASE - ) - foreach(CompilerFlag ${CompilerFlags}) - string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") - endforeach() - endif() - add_library(libtorrent STATIC ${sources2}) -endif() + +if (NOT MSVC) + target_compile_options(libtorrent PUBLIC "-fvisibility=hidden" "-fvisibility-inlines-hidden") + # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") + # set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -fvisibility-inlines-hidden") +endif () # Boost -set(Boost_USE_STATIC_LIBS ON) -if(NOT DEFINED Boost_INCLUDE_DIR OR NOT DEFINED Boost_LIBRARIES) - FIND_PACKAGE( Boost COMPONENTS system thread date_time chrono) -endif() -include_directories(${Boost_INCLUDE_DIR}) +set(Boost_USE_STATIC_LIBS ON) +if (NOT DEFINED Boost_INCLUDE_DIR OR NOT DEFINED Boost_LIBRARIES) + find_package(Boost COMPONENTS system thread date_time chrono) +endif () +target_include_directories(libtorrent PUBLIC ${Boost_INCLUDE_DIR}) target_link_libraries(libtorrent ${Boost_LIBRARIES}) if (WIN32) - target_link_libraries(libtorrent Iphlpapi.lib) + target_link_libraries(libtorrent wsock32 ws2_32 Iphlpapi.lib) + target_compile_definitions(libtorrent PUBLIC "_WIN32_WINNT=0x0600") endif () # this works around a bug in asio in boost-1.39 #add_definitions(-DBOOST_ASIO_HASH_MAP_BUCKETS=1021 -D__USE_W32_SOCKETS -DWIN32_LEAN_AND_MEAN ) -# if(NOT static_runtime) -# add_definitions(-DBOOST_ASIO_DYN_LINK -DBOOST_DATE_TIME_DYN_LINK -DBOOST_THREAD_DYN_LINK -DBOOST_SYSTEM_DYN_LINK -DBOOST_CHRONO_DYN_LINK) -# else() - add_definitions(-DBOOST_ASIO_SEPARATE_COMPILATION) -# endif() - -if (WIN32) - target_link_libraries(libtorrent wsock32 ws2_32) - add_definitions(-D_WIN32_WINNT=0x0600) - if (MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") # multicore compilation - endif() -endif() - -if (NOT pool-allocators) - add_definitions(-DTORRENT_DISABLE_POOL_ALLOCATOR) -endif() - -if (NOT geoip) - add_definitions(-DTORRENT_DISABLE_GEO_IP) -endif() - -if (NOT resolve-countries) - add_definitions(-DTORRENT_DISABLE_RESOLVE_COUNTRIES) -endif() - -if (unicode) - add_definitions(-DUNICODE -D_UNICODE) -endif() - -if (NOT deprecated-functions) - add_definitions(-DTORRENT_NO_DEPRECATE) -endif() - -if (exceptions) - if (MSVC) - add_definitions(/EHsc) - else (MSVC) - add_definitions(-fexceptions) - endif (MSVC) -else() - if (MSVC) - add_definitions(-D_HAS_EXCEPTIONS=0) - else (MSVC) - add_definitions(-fno-exceptions) - endif (MSVC) -endif() - if (MSVC) -# disable bogus deprecation warnings on msvc8 - add_definitions(-D_SCL_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_DEPRECATE) -# these compiler settings just makes the compiler standard conforming - add_definitions(/Zc:wchar_t /Zc:forScope) -# for multi-core compilation - add_definitions(/MP) + target_compile_options(libtorrent PUBLIC "/EHsc" "/Zc:wchar_t" "/Zc:forScope" "/MP") + target_compile_definitions(libtorrent PUBLIC _SCL_SECURE_NO_DEPRECATE _CRT_SECURE_NO_DEPRECATE) +else () + target_compile_options(libtorrent PUBLIC "-fexceptions" "-Wno-c++11-extensions") +endif () -#$(SolutionDir)msvc,release:/OPT:ICF=5 -#$(SolutionDir)msvc,release:/OPT:REF -else() - add_definitions(-Wno-c++11-extensions) -endif() - -add_definitions(-D_FILE_OFFSET_BITS=64) -add_definitions(-DBOOST_EXCEPTION_DISABLE) -add_definitions(-DBOOST_ASIO_ENABLE_CANCELIO) - -if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - add_definitions(-fcolor-diagnostics) -endif() - -if (tcmalloc) - target_link_libraries(libtorrent tcmalloc) -endif() +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + target_compile_options(libtorrent PUBLIC "-fcolor-diagnostics") +endif () set_target_properties(libtorrent PROPERTIES - SOVERSION ${SOVERSION}) + SOVERSION ${SOVERSION}) -get_property (COMPILETIME_OPTIONS_LIST - DIRECTORY ${CMAKE_CURRENT_SOURCE_DIRECTORY} - PROPERTY COMPILE_DEFINITIONS - ) +get_property(COMPILETIME_OPTIONS_LIST + DIRECTORY ${CMAKE_CURRENT_SOURCE_DIRECTORY} + PROPERTY COMPILE_DEFINITIONS + ) foreach (s ${COMPILETIME_OPTIONS_LIST}) - set (COMPILETIME_OPTIONS "${COMPILETIME_OPTIONS} -D${s}") -endforeach (s) + set (COMPILETIME_OPTIONS "${COMPILETIME_OPTIONS} -D${s}") +endforeach () configure_file(libtorrent-rasterbar-cmake.pc.in libtorrent-rasterbar.pc) string (COMPARE EQUAL "${CMAKE_SIZEOF_VOID_P}" "8" IS64BITS) if (IS64BITS AND RESPECTLIB64) - set (LIBDIR "lib64") + set(LIBDIR "lib64") else() - set (LIBDIR "lib") + set(LIBDIR "lib") endif() install(TARGETS libtorrent DESTINATION ${LIBDIR}) install(DIRECTORY include/libtorrent - DESTINATION include - PATTERN ".svn" EXCLUDE) + DESTINATION include + PATTERN ".svn" EXCLUDE) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libtorrent-rasterbar.pc DESTINATION ${LIBDIR}/pkgconfig) - -# === set up examples directory as an independent project === -#file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/examples) -#configure_file(examples/run_cmake.sh.in examples/run_cmake.sh) -# to build the examples, run examples/run_cmake.sh after building libtorrent diff --git a/apps/Launcher/infowidget.cpp b/apps/Launcher/infowidget.cpp new file mode 100644 index 0000000000..ddb523355e --- /dev/null +++ b/apps/Launcher/infowidget.cpp @@ -0,0 +1,68 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include "infowidget.h" + +#include +#include +#include + +InfoWidget::InfoWidget(QString name, int totalBytes) + : QWidget(nullptr) + , _name(nullptr) + , _bytes(nullptr) + , _progress(nullptr) + , _messages(nullptr) + , _totalBytes(totalBytes) +{ + QGridLayout* layout = new QGridLayout; + + _name = new QLabel(name); + layout->addWidget(_name, 0, 0); + + _bytes = new QLabel(""); + layout->addWidget(_bytes, 0, 1); + + _progress = new QProgressBar; + layout->addWidget(_progress, 0, 2); + + _messages = new QLabel(""); + layout->addWidget(_messages, 1, 0, 1, 3); + + setLayout(layout); + + update(0, 0.f); +} + +void InfoWidget::update(int currentBytes, float progress) { + _bytes->setText( + QString("%1 / %2").arg(currentBytes).arg(_totalBytes) + ); + + _progress->setValue(static_cast(progress * 100)); +} + +void InfoWidget::error(QString message) { + _messages->setText(message); +} diff --git a/apps/Launcher/infowidget.h b/apps/Launcher/infowidget.h new file mode 100644 index 0000000000..c60a96d9b0 --- /dev/null +++ b/apps/Launcher/infowidget.h @@ -0,0 +1,52 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __INFOWIDGET_H__ +#define __INFOWIDGET_H__ + +#include + +class QLabel; +class QProgressBar; + +class InfoWidget : public QWidget { +Q_OBJECT +public: + InfoWidget(QString name, int totalBytes); + + void update(int currentBytes, float progress); + void error(QString message); + +private: + InfoWidget(const InfoWidget& rhs) = delete; + + QLabel* _name; + QLabel* _bytes; + QProgressBar* _progress; + QLabel* _messages; + + int _totalBytes; +}; + +#endif // __INFOWIDGET_H__ diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 1fa77733a1..617a5d7fd0 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -24,9 +24,12 @@ #include "syncwidget.h" +#include "infowidget.h" + #include #include +#include #include #include #include @@ -38,11 +41,14 @@ #include #include #include +#include +#include #include #include #include #include +#include namespace { const int nColumns = 3; @@ -62,6 +68,7 @@ namespace { SyncWidget::SyncWidget(QWidget* parent) : QWidget(parent) , _sceneLayout(nullptr) + , _session(new libtorrent::session) { setFixedSize(500, 500); @@ -82,15 +89,37 @@ SyncWidget::SyncWidget(QWidget* parent) layout->addWidget(syncButton); } + { + QGroupBox* downloadBox = new QGroupBox; + _downloadLayout = new QVBoxLayout; + + downloadBox->setLayout(_downloadLayout); + layout->addWidget(downloadBox); + } + setLayout(layout); ghoul::initialize(); openspace::DownloadManager::initialize("http://openspace.itn.liu.se/data/request", DownloadApplicationVersion); + + libtorrent::error_code ec; + _session->listen_on(std::make_pair(6881, 6889), ec); + if (ec) { + qDebug() << "Failed to open socket: " << QString::fromStdString(ec.message()); + _session = nullptr; + return; + } + _session->start_upnp(); + + QTimer* timer = new QTimer(this); + QObject::connect(timer, SIGNAL(timeout()), this, SLOT(handleTimer())); + timer->start(100); } SyncWidget::~SyncWidget() { openspace::DownloadManager::deinitialize(); ghoul::deinitialize(); + delete _session; } void SyncWidget::setSceneFiles(QMap sceneFiles) { @@ -148,28 +177,31 @@ void SyncWidget::handleFileRequest(QString module, FileRequests files) { void SyncWidget::handleTorrentFiles(QString module, TorrentFiles files) { qDebug() << "Torrent Files"; for (const TorrentFile& f : files) { - qDebug() << f.file; + QString file = QString::fromStdString(absPath(fullPath(module, f.file).toStdString())); + qDebug() << file; + + //libtorrent::bdecode() - libtorrent::session s; libtorrent::error_code ec; - s.listen_on(std::make_pair(6881, 6889), ec); + libtorrent::add_torrent_params p; + p.save_path = absPath(fullPath(module, ".").toStdString()); + qDebug() << QString::fromStdString(p.save_path); + p.ti = new libtorrent::torrent_info(file.toStdString(), ec); + p.name = f.file.toStdString(); + p.storage_mode = libtorrent::storage_mode_allocate; + p.auto_managed = true; if (ec) { - qDebug() << "Failed to open socket: " << QString::fromStdString(ec.message()); - return; + qDebug() << QString::fromStdString(ec.message()); + //return; + } + libtorrent::torrent_handle h = _session->add_torrent(p, ec); + if (ec) { + qDebug() << QString::fromStdString(ec.message()); + //return; } - libtorrent::add_torrent_params p; - p.save_path = fullPath(module, ".").toStdString(); - p.ti = new libtorrent::torrent_info(f.file.toStdString(), ec); - if (ec) { - qDebug() << QString::fromStdString(ec.message()); - return; - } - s.add_torrent(p, ec); - if (ec) { - qDebug() << QString::fromStdString(ec.message()); - return; - } + InfoWidget* w = new InfoWidget(f.file, h.status().total_done); + _downloadLayout->addWidget(w); } } @@ -280,3 +312,49 @@ QStringList SyncWidget::selectedScenes() const { QString SyncWidget::fullPath(QString module, QString destination) const { return _modulesDirectory + "/" + module + "/" + destination; } + +void SyncWidget::handleTimer() { + //using namespace libtorrent; + + //_session->post_torrent_updates(); + + //qDebug() << "Session"; + //qDebug() << "nPeers: " << _session->status().num_peers; + //qDebug() << "==="; + + //qDebug() << "Alerts"; + //std::deque alerts; + //_session->pop_alerts(&alerts); + //for (alert* a : alerts) { + // qDebug() << QString::fromStdString(a->message()); + + // //if (a->category() == alert::status_notification) { + // // state_update_alert* sua = static_cast(a); + // // for (torrent_status s ) + // //} + //} + //qDebug() << "==="; + + + //std::vector handles = _session->get_torrents(); + //for (torrent_handle h : handles) { + // //qDebug() << "Name: " << QString::fromStdString(h.name()); + // //torrent_status s = h.status(); + + // //qDebug() << "Error: " << QString::fromStdString(s.error); + + // //qDebug() << "Total Wanted: " << s.total_wanted; + // //qDebug() << "Total Wanted Done: " << s.total_wanted_done; + // //qDebug() << "Has Incoming: " << s.has_incoming; + // //qDebug() << "Connect Candidates: " << s.connect_candidates; + // //qDebug() << "Last Seen Complete: " << s.last_seen_complete; + // //qDebug() << "List Peers: " << s.list_peers; + // //qDebug() << "Num Pieces: " << s.num_pieces; + // //qDebug() << "Download Rate: " << s.download_rate; + // //qDebug() << "List Seeds: " << s.list_seeds; + // //qDebug() << "Paused: " << s.paused; + // //qDebug() << "Progress: " << s.progress; + + // qDebug() << ""; + //} +} diff --git a/apps/Launcher/syncwidget.h b/apps/Launcher/syncwidget.h index 0353409a3a..a85882ff8c 100644 --- a/apps/Launcher/syncwidget.h +++ b/apps/Launcher/syncwidget.h @@ -29,8 +29,18 @@ #include +#include + +class QBoxLayout; class QGridLayout; +class InfoWidget; + +namespace libtorrent { + class session; + class torrent_handle; +} + class SyncWidget : public QWidget { Q_OBJECT public: @@ -42,6 +52,7 @@ public: private slots: void syncButtonPressed(); + void handleTimer(); private: struct DirectFile { @@ -74,6 +85,11 @@ private: QMap _sceneFiles; QString _modulesDirectory; QGridLayout* _sceneLayout; + QBoxLayout* _downloadLayout; + + libtorrent::session* _session; + //QMap _infoWidgetMap; + QMap _infoWidgetMap; }; #endif // __SYNCWIDGET_H__ diff --git a/openspace.cfg b/openspace.cfg index bd72416a0c..1ff91c22a1 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -7,7 +7,7 @@ return { -- Sets the scene that is to be loaded by OpenSpace. A scene file is a description -- of all entities that will be visible during an instance of OpenSpace - Scene = "${OPENSPACE_DATA}/scene/default_nh.scene", + Scene = "${OPENSPACE_DATA}/scene/default.scene", Paths = { SGCT = "${BASE_PATH}/config/sgct", diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 4ee6a9c93f..faff599d9e 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -229,6 +229,9 @@ function (handle_applications) target_link_libraries(${APPLICATION_NAME} Ghoul) target_link_libraries(${APPLICATION_NAME} libOpenSpace) + + copy_files(${APPLICATION_NAME} "${CURL_ROOT_DIR}/lib/libcurl.dll") + endif () list(APPEND applications ${APPLICATION_NAME}) From 25911736bf73e090f97ac7c47684f92a2cd5364d Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 13 Jun 2015 01:17:53 +0200 Subject: [PATCH 124/329] Some more work on Launcher --- apps/Launcher/infowidget.cpp | 9 ++++-- apps/Launcher/infowidget.h | 2 +- apps/Launcher/syncwidget.cpp | 59 +++++++++++++++++++++--------------- apps/Launcher/syncwidget.h | 3 +- 4 files changed, 43 insertions(+), 30 deletions(-) diff --git a/apps/Launcher/infowidget.cpp b/apps/Launcher/infowidget.cpp index ddb523355e..38dc029528 100644 --- a/apps/Launcher/infowidget.cpp +++ b/apps/Launcher/infowidget.cpp @@ -52,14 +52,17 @@ InfoWidget::InfoWidget(QString name, int totalBytes) setLayout(layout); - update(0, 0.f); + update(0); } -void InfoWidget::update(int currentBytes, float progress) { +void InfoWidget::update(int currentBytes) { _bytes->setText( - QString("%1 / %2").arg(currentBytes).arg(_totalBytes) + QString("%1 / %2") + .arg(currentBytes) + .arg(_totalBytes) ); + float progress = static_cast(currentBytes) / static_cast(_totalBytes); _progress->setValue(static_cast(progress * 100)); } diff --git a/apps/Launcher/infowidget.h b/apps/Launcher/infowidget.h index c60a96d9b0..685430d9b6 100644 --- a/apps/Launcher/infowidget.h +++ b/apps/Launcher/infowidget.h @@ -35,7 +35,7 @@ Q_OBJECT public: InfoWidget(QString name, int totalBytes); - void update(int currentBytes, float progress); + void update(int currentBytes); void error(QString message); private: diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 617a5d7fd0..74f49fb375 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -110,6 +111,7 @@ SyncWidget::SyncWidget(QWidget* parent) return; } _session->start_upnp(); + _session->start_dht(); QTimer* timer = new QTimer(this); QObject::connect(timer, SIGNAL(timeout()), this, SLOT(handleTimer())); @@ -180,7 +182,10 @@ void SyncWidget::handleTorrentFiles(QString module, TorrentFiles files) { QString file = QString::fromStdString(absPath(fullPath(module, f.file).toStdString())); qDebug() << file; - //libtorrent::bdecode() + if (!QFileInfo(file).exists()) { + qDebug() << file << " does not exist"; + continue; + } libtorrent::error_code ec; libtorrent::add_torrent_params p; @@ -192,16 +197,18 @@ void SyncWidget::handleTorrentFiles(QString module, TorrentFiles files) { p.auto_managed = true; if (ec) { qDebug() << QString::fromStdString(ec.message()); - //return; + continue; } libtorrent::torrent_handle h = _session->add_torrent(p, ec); if (ec) { qDebug() << QString::fromStdString(ec.message()); - //return; + continue; } - InfoWidget* w = new InfoWidget(f.file, h.status().total_done); + libtorrent::size_type s = h.status().total_wanted; + InfoWidget* w = new InfoWidget(f.file, h.status().total_wanted); _downloadLayout->addWidget(w); + _infoWidgetMap[h] = w; } } @@ -246,7 +253,6 @@ void SyncWidget::syncButtonPressed() { bool found = dataDictionary.getValue(FileDownloadKey, directDownloadFiles); if (found) { DirectFiles files; - //QStringList files; for (int i = 1; i <= directDownloadFiles.size(); ++i) { ghoul::Dictionary d = directDownloadFiles.value(std::to_string(i)); std::string url = d.value(UrlKey); @@ -314,7 +320,7 @@ QString SyncWidget::fullPath(QString module, QString destination) const { } void SyncWidget::handleTimer() { - //using namespace libtorrent; + using namespace libtorrent; //_session->post_torrent_updates(); @@ -336,25 +342,30 @@ void SyncWidget::handleTimer() { //qDebug() << "==="; - //std::vector handles = _session->get_torrents(); - //for (torrent_handle h : handles) { - // //qDebug() << "Name: " << QString::fromStdString(h.name()); - // //torrent_status s = h.status(); + std::vector handles = _session->get_torrents(); + for (torrent_handle h : handles) { + torrent_status s = h.status(); + InfoWidget* w = _infoWidgetMap[h]; - // //qDebug() << "Error: " << QString::fromStdString(s.error); + w->update(s.total_wanted_done); - // //qDebug() << "Total Wanted: " << s.total_wanted; - // //qDebug() << "Total Wanted Done: " << s.total_wanted_done; - // //qDebug() << "Has Incoming: " << s.has_incoming; - // //qDebug() << "Connect Candidates: " << s.connect_candidates; - // //qDebug() << "Last Seen Complete: " << s.last_seen_complete; - // //qDebug() << "List Peers: " << s.list_peers; - // //qDebug() << "Num Pieces: " << s.num_pieces; - // //qDebug() << "Download Rate: " << s.download_rate; - // //qDebug() << "List Seeds: " << s.list_seeds; - // //qDebug() << "Paused: " << s.paused; - // //qDebug() << "Progress: " << s.progress; + qDebug() << "Name: " << QString::fromStdString(h.name()); + //torrent_status s = h.status(); - // qDebug() << ""; - //} + qDebug() << "Error: " << QString::fromStdString(s.error); + + qDebug() << "Total Wanted: " << s.total_wanted; + qDebug() << "Total Wanted Done: " << s.total_wanted_done; + qDebug() << "Has Incoming: " << s.has_incoming; + qDebug() << "Connect Candidates: " << s.connect_candidates; + qDebug() << "Last Seen Complete: " << s.last_seen_complete; + qDebug() << "List Peers: " << s.list_peers; + qDebug() << "Num Pieces: " << s.num_pieces; + qDebug() << "Download Rate: " << s.download_rate; + qDebug() << "List Seeds: " << s.list_seeds; + qDebug() << "Paused: " << s.paused; + qDebug() << "Progress: " << s.progress; + + qDebug() << ""; + } } diff --git a/apps/Launcher/syncwidget.h b/apps/Launcher/syncwidget.h index a85882ff8c..06e2b9e122 100644 --- a/apps/Launcher/syncwidget.h +++ b/apps/Launcher/syncwidget.h @@ -38,7 +38,7 @@ class InfoWidget; namespace libtorrent { class session; - class torrent_handle; + struct torrent_handle; } class SyncWidget : public QWidget { @@ -88,7 +88,6 @@ private: QBoxLayout* _downloadLayout; libtorrent::session* _session; - //QMap _infoWidgetMap; QMap _infoWidgetMap; }; From de4a6edc4fb171ca69a42218a1bd98a95c4f29bb Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 13 Jun 2015 17:06:57 +0200 Subject: [PATCH 125/329] Make torrent capabilitiy for Launcher work --- apps/Launcher/syncwidget.cpp | 37 +++++++++++++++++++++--------------- data | 2 +- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 74f49fb375..59502833a5 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -112,6 +112,7 @@ SyncWidget::SyncWidget(QWidget* parent) } _session->start_upnp(); _session->start_dht(); + QTimer* timer = new QTimer(this); QObject::connect(timer, SIGNAL(timeout()), this, SLOT(handleTimer())); @@ -322,24 +323,30 @@ QString SyncWidget::fullPath(QString module, QString destination) const { void SyncWidget::handleTimer() { using namespace libtorrent; - //_session->post_torrent_updates(); + _session->post_torrent_updates(); + libtorrent::session_settings settings = _session->settings(); - //qDebug() << "Session"; - //qDebug() << "nPeers: " << _session->status().num_peers; - //qDebug() << "==="; + qDebug() << "Session"; + qDebug() << "nPeers: " << _session->status().num_peers; + qDebug() << "DHT: " << _session->is_dht_running(); + qDebug() << "Incoming TCP" << settings.enable_incoming_tcp; + qDebug() << "Outgoing TCP" << settings.enable_outgoing_tcp; + qDebug() << "Incoming UTP" << settings.enable_incoming_utp; + qDebug() << "Outgoing UTP" << settings.enable_outgoing_utp; + qDebug() << "==="; - //qDebug() << "Alerts"; - //std::deque alerts; - //_session->pop_alerts(&alerts); - //for (alert* a : alerts) { - // qDebug() << QString::fromStdString(a->message()); + qDebug() << "Alerts"; + std::deque alerts; + _session->pop_alerts(&alerts); + for (alert* a : alerts) { + qDebug() << QString::fromStdString(a->message()); - // //if (a->category() == alert::status_notification) { - // // state_update_alert* sua = static_cast(a); - // // for (torrent_status s ) - // //} - //} - //qDebug() << "==="; + //if (a->category() == alert::status_notification) { + // state_update_alert* sua = static_cast(a); + // for (torrent_status s ) + //} + } + qDebug() << "==="; std::vector handles = _session->get_torrents(); diff --git a/data b/data index f3928948f2..651275ac9a 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit f3928948f25ac520240a4c7a6ac280b278ddf3b7 +Subproject commit 651275ac9aac3f67e9c4677663d2e1bd6a05716b From c7b041e98ae0770c6682e315884236b4b872abe9 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 13 Jun 2015 18:40:33 +0200 Subject: [PATCH 126/329] Fix port to 20285 and list all connected seeds --- apps/Launcher/syncwidget.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 59502833a5..f5b953f8b9 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -104,7 +104,7 @@ SyncWidget::SyncWidget(QWidget* parent) openspace::DownloadManager::initialize("http://openspace.itn.liu.se/data/request", DownloadApplicationVersion); libtorrent::error_code ec; - _session->listen_on(std::make_pair(6881, 6889), ec); + _session->listen_on(std::make_pair(20285, 20285), ec); if (ec) { qDebug() << "Failed to open socket: " << QString::fromStdString(ec.message()); _session = nullptr; @@ -367,6 +367,7 @@ void SyncWidget::handleTimer() { qDebug() << "Connect Candidates: " << s.connect_candidates; qDebug() << "Last Seen Complete: " << s.last_seen_complete; qDebug() << "List Peers: " << s.list_peers; + qDebug() << "List Seeds: " << s.list_seeds; qDebug() << "Num Pieces: " << s.num_pieces; qDebug() << "Download Rate: " << s.download_rate; qDebug() << "List Seeds: " << s.list_seeds; From f327ba3014cee11695aa4ceff839bdacb6bd46e3 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 14 Jun 2015 18:21:10 +0200 Subject: [PATCH 127/329] Remove warning in CMakeLists Enable download and request-download functionality --- apps/Launcher/ext/libtorrent/CMakeLists.txt | 2 +- apps/Launcher/syncwidget.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/Launcher/ext/libtorrent/CMakeLists.txt b/apps/Launcher/ext/libtorrent/CMakeLists.txt index 6e8c73a9da..d9050522fc 100644 --- a/apps/Launcher/ext/libtorrent/CMakeLists.txt +++ b/apps/Launcher/ext/libtorrent/CMakeLists.txt @@ -213,7 +213,7 @@ else () target_compile_options(libtorrent PUBLIC "-fexceptions" "-Wno-c++11-extensions") endif () -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") +if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") target_compile_options(libtorrent PUBLIC "-fcolor-diagnostics") endif () diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index f5b953f8b9..b72f953458 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -148,7 +148,7 @@ void SyncWidget::clear() { } void SyncWidget::handleDirectFiles(QString module, DirectFiles files) { - return; + //return; qDebug() << "Direct Files"; for (const DirectFile& f : files) { qDebug() << f.url << " -> " << f.destination; @@ -162,7 +162,7 @@ void SyncWidget::handleDirectFiles(QString module, DirectFiles files) { } void SyncWidget::handleFileRequest(QString module, FileRequests files) { - return; + //return; qDebug() << "File Requests"; for (const FileRequest& f : files) { qDebug() << f.identifier << " (" << f.version << ")" << " -> " << f.destination; From 41ab893240eeec591f7ace31aed3643d03e579c2 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 14 Jun 2015 18:23:33 +0200 Subject: [PATCH 128/329] Update data --- data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data b/data index 651275ac9a..3879f4b889 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 651275ac9aac3f67e9c4677663d2e1bd6a05716b +Subproject commit 3879f4b8896fc417987a93640049d5aa6faf2337 From e212d0b34f4767900399b3c4fc4977f0aeae6279 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 14 Jun 2015 20:17:09 +0200 Subject: [PATCH 129/329] Making SyncWidget and ShortcutWidget modal windows Enable correct progress callback for DownloadManager --- apps/Launcher/mainwindow.cpp | 20 ++++-- apps/Launcher/mainwindow.h | 10 +-- apps/Launcher/shortcutwidget.cpp | 4 +- apps/Launcher/shortcutwidget.h | 2 +- apps/Launcher/syncwidget.cpp | 116 +++++++++++++++++++------------ apps/Launcher/syncwidget.h | 2 +- data | 2 +- openspace.cfg | 1 + src/engine/downloadmanager.cpp | 62 +++++++++++++++-- 9 files changed, 155 insertions(+), 64 deletions(-) diff --git a/apps/Launcher/mainwindow.cpp b/apps/Launcher/mainwindow.cpp index 2efa0fc3eb..9f7fd7f55a 100644 --- a/apps/Launcher/mainwindow.cpp +++ b/apps/Launcher/mainwindow.cpp @@ -24,6 +24,9 @@ #include "mainwindow.h" +#include "shortcutwidget.h" +#include "syncwidget.h" + #include #include #include @@ -141,8 +144,13 @@ void MainWindow::initialize() { this, SLOT(newsNetworkError()) ); - _shortcutWidget.hide(); - _syncWidget.hide(); + _shortcutWidget = new ShortcutWidget(this, Qt::Popup | Qt::Dialog); + _shortcutWidget->setWindowModality(Qt::WindowModal); + _shortcutWidget->hide(); + + _syncWidget = new SyncWidget(this, Qt::Popup | Qt::Dialog); + _syncWidget->setWindowModality(Qt::WindowModal); + _syncWidget->hide(); QDir d(ModulesDirectory); d.setFilter(QDir::Files); @@ -152,16 +160,16 @@ void MainWindow::initialize() { _sceneFiles.insert(i.fileName(), i.absoluteFilePath()); _scenes->addItem(i.fileName()); } - _syncWidget.setSceneFiles(_sceneFiles); - _syncWidget.setModulesDirectory(ModulesDirectory); + _syncWidget->setSceneFiles(_sceneFiles); + _syncWidget->setModulesDirectory(ModulesDirectory); } void MainWindow::shortcutButtonPressed() { - _shortcutWidget.show(); + _shortcutWidget->show(); } void MainWindow::syncButtonPressed() { - _syncWidget.show(); + _syncWidget->show(); } void MainWindow::startButtonPressed() { diff --git a/apps/Launcher/mainwindow.h b/apps/Launcher/mainwindow.h index 109bc4b0b4..3c11b3a71b 100644 --- a/apps/Launcher/mainwindow.h +++ b/apps/Launcher/mainwindow.h @@ -27,9 +27,6 @@ #include -#include "shortcutwidget.h" -#include "syncwidget.h" - #include #include #include @@ -38,6 +35,9 @@ class QComboBox; class QNetworkAccessManager; +class ShortcutWidget; +class SyncWidget; + class MainWindow : public QWidget { Q_OBJECT public: @@ -62,8 +62,8 @@ private: QComboBox* _scenes; QMap _sceneFiles; - ShortcutWidget _shortcutWidget; - SyncWidget _syncWidget; + ShortcutWidget* _shortcutWidget; + SyncWidget* _syncWidget; QNetworkAccessManager _networkManager; diff --git a/apps/Launcher/shortcutwidget.cpp b/apps/Launcher/shortcutwidget.cpp index 899b60c82e..280da6b81f 100644 --- a/apps/Launcher/shortcutwidget.cpp +++ b/apps/Launcher/shortcutwidget.cpp @@ -24,6 +24,6 @@ #include "shortcutwidget.h" -ShortcutWidget::ShortcutWidget(QWidget* parent) - : QWidget(parent) +ShortcutWidget::ShortcutWidget(QWidget* parent, Qt::WindowFlags f) + : QWidget(parent, f) {} diff --git a/apps/Launcher/shortcutwidget.h b/apps/Launcher/shortcutwidget.h index 5a06a0d388..d488281861 100644 --- a/apps/Launcher/shortcutwidget.h +++ b/apps/Launcher/shortcutwidget.h @@ -30,7 +30,7 @@ class ShortcutWidget : public QWidget { Q_OBJECT public: - ShortcutWidget(QWidget* parent); + ShortcutWidget(QWidget* parent, Qt::WindowFlags f = 0); }; #endif // __SHORTCUTWIDGET_H__ diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index b72f953458..0d391e11d3 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -64,10 +64,12 @@ namespace { const std::string DestinationKey = "Destination"; const std::string IdentifierKey = "Identifier"; const std::string VersionKey = "Version"; + + const QString DefaultSceneName = "default.scene"; } -SyncWidget::SyncWidget(QWidget* parent) - : QWidget(parent) +SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) + : QWidget(parent, f) , _sceneLayout(nullptr) , _session(new libtorrent::session) { @@ -135,6 +137,9 @@ void SyncWidget::setSceneFiles(QMap sceneFiles) { QCheckBox* checkbox = new QCheckBox(sceneName); + if (sceneName == DefaultSceneName) + checkbox->setChecked(true); + _sceneLayout->addWidget(checkbox, i / nColumns, i % nColumns); } } @@ -148,29 +153,44 @@ void SyncWidget::clear() { } void SyncWidget::handleDirectFiles(QString module, DirectFiles files) { - //return; qDebug() << "Direct Files"; for (const DirectFile& f : files) { qDebug() << f.url << " -> " << f.destination; + auto finishedCallback = + [](const ghoul::filesystem::File& f) { + qDebug() << QString::fromStdString(f.filename()) << "finished"; + }; + auto progressCallback = + [](const ghoul::filesystem::File& f, float progress) { + qDebug() << QString::fromStdString(f.filename()) << ": " << progress; + }; + DlManager.downloadFile( f.url.toStdString(), fullPath(module, f.destination).toStdString(), - [](const ghoul::filesystem::File& f) { qDebug() << QString::fromStdString(f.filename()) << "finished";} + finishedCallback, + progressCallback ); } } void SyncWidget::handleFileRequest(QString module, FileRequests files) { - //return; + return; qDebug() << "File Requests"; for (const FileRequest& f : files) { + auto progressCallback = + [](const ghoul::filesystem::File& f, float progress) { + qDebug() << QString::fromStdString(f.filename()) << ": " << progress; + }; + qDebug() << f.identifier << " (" << f.version << ")" << " -> " << f.destination; DlManager.downloadRequestFiles( f.identifier.toStdString(), fullPath(module, f.destination).toStdString(), f.version, - [](const ghoul::filesystem::File& f) { qDebug() << "finished"; } + [](const ghoul::filesystem::File& f) { qDebug() << "finished"; }, + progressCallback ); @@ -255,8 +275,18 @@ void SyncWidget::syncButtonPressed() { if (found) { DirectFiles files; for (int i = 1; i <= directDownloadFiles.size(); ++i) { + if (!directDownloadFiles.hasKeyAndValue(std::to_string(i))) { + qDebug() << QString::fromStdString(FileDownloadKey) << " is not a dictionary"; + continue; + } ghoul::Dictionary d = directDownloadFiles.value(std::to_string(i)); + if (!directDownloadFiles.hasKeyAndValue(UrlKey)) { + qDebug() << "No '" << QString::fromStdString(UrlKey); + } std::string url = d.value(UrlKey); + if (!directDownloadFiles.hasKeyAndValue(DestinationKey)) { + qDebug() << "No '" << QString::fromStdString(DestinationKey); + } std::string dest = d.value(DestinationKey); files.append({ @@ -323,30 +353,30 @@ QString SyncWidget::fullPath(QString module, QString destination) const { void SyncWidget::handleTimer() { using namespace libtorrent; - _session->post_torrent_updates(); - libtorrent::session_settings settings = _session->settings(); + //_session->post_torrent_updates(); + //libtorrent::session_settings settings = _session->settings(); - qDebug() << "Session"; - qDebug() << "nPeers: " << _session->status().num_peers; - qDebug() << "DHT: " << _session->is_dht_running(); - qDebug() << "Incoming TCP" << settings.enable_incoming_tcp; - qDebug() << "Outgoing TCP" << settings.enable_outgoing_tcp; - qDebug() << "Incoming UTP" << settings.enable_incoming_utp; - qDebug() << "Outgoing UTP" << settings.enable_outgoing_utp; - qDebug() << "==="; + //qDebug() << "Session"; + //qDebug() << "nPeers: " << _session->status().num_peers; + //qDebug() << "DHT: " << _session->is_dht_running(); + //qDebug() << "Incoming TCP" << settings.enable_incoming_tcp; + //qDebug() << "Outgoing TCP" << settings.enable_outgoing_tcp; + //qDebug() << "Incoming UTP" << settings.enable_incoming_utp; + //qDebug() << "Outgoing UTP" << settings.enable_outgoing_utp; + //qDebug() << "==="; - qDebug() << "Alerts"; - std::deque alerts; - _session->pop_alerts(&alerts); - for (alert* a : alerts) { - qDebug() << QString::fromStdString(a->message()); + //qDebug() << "Alerts"; + //std::deque alerts; + //_session->pop_alerts(&alerts); + //for (alert* a : alerts) { + // qDebug() << QString::fromStdString(a->message()); - //if (a->category() == alert::status_notification) { - // state_update_alert* sua = static_cast(a); - // for (torrent_status s ) - //} - } - qDebug() << "==="; + // //if (a->category() == alert::status_notification) { + // // state_update_alert* sua = static_cast(a); + // // for (torrent_status s ) + // //} + //} + //qDebug() << "==="; std::vector handles = _session->get_torrents(); @@ -356,24 +386,24 @@ void SyncWidget::handleTimer() { w->update(s.total_wanted_done); - qDebug() << "Name: " << QString::fromStdString(h.name()); - //torrent_status s = h.status(); + // qDebug() << "Name: " << QString::fromStdString(h.name()); + // //torrent_status s = h.status(); - qDebug() << "Error: " << QString::fromStdString(s.error); + // qDebug() << "Error: " << QString::fromStdString(s.error); - qDebug() << "Total Wanted: " << s.total_wanted; - qDebug() << "Total Wanted Done: " << s.total_wanted_done; - qDebug() << "Has Incoming: " << s.has_incoming; - qDebug() << "Connect Candidates: " << s.connect_candidates; - qDebug() << "Last Seen Complete: " << s.last_seen_complete; - qDebug() << "List Peers: " << s.list_peers; - qDebug() << "List Seeds: " << s.list_seeds; - qDebug() << "Num Pieces: " << s.num_pieces; - qDebug() << "Download Rate: " << s.download_rate; - qDebug() << "List Seeds: " << s.list_seeds; - qDebug() << "Paused: " << s.paused; - qDebug() << "Progress: " << s.progress; + // qDebug() << "Total Wanted: " << s.total_wanted; + // qDebug() << "Total Wanted Done: " << s.total_wanted_done; + // qDebug() << "Has Incoming: " << s.has_incoming; + // qDebug() << "Connect Candidates: " << s.connect_candidates; + // qDebug() << "Last Seen Complete: " << s.last_seen_complete; + // qDebug() << "List Peers: " << s.list_peers; + // qDebug() << "List Seeds: " << s.list_seeds; + // qDebug() << "Num Pieces: " << s.num_pieces; + // qDebug() << "Download Rate: " << s.download_rate; + // qDebug() << "List Seeds: " << s.list_seeds; + // qDebug() << "Paused: " << s.paused; + // qDebug() << "Progress: " << s.progress; - qDebug() << ""; + // qDebug() << ""; } } diff --git a/apps/Launcher/syncwidget.h b/apps/Launcher/syncwidget.h index 06e2b9e122..fe9da6550c 100644 --- a/apps/Launcher/syncwidget.h +++ b/apps/Launcher/syncwidget.h @@ -44,7 +44,7 @@ namespace libtorrent { class SyncWidget : public QWidget { Q_OBJECT public: - SyncWidget(QWidget* parent); + SyncWidget(QWidget* parent, Qt::WindowFlags f = 0); ~SyncWidget(); void setSceneFiles(QMap sceneFiles); diff --git a/data b/data index 3879f4b889..9de5bb5767 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 3879f4b8896fc417987a93640049d5aa6faf2337 +Subproject commit 9de5bb57678b171f1bc53a176ab63f380ce64ed9 diff --git a/openspace.cfg b/openspace.cfg index 1ff91c22a1..5ccbed0285 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -54,4 +54,5 @@ return { File = "${BASE_PATH}/Properties.txt" }, DownloadRequestURL = "http://openspace.itn.liu.se/request.cgi", + RenderingMethod = "ABufferFrameBuffer" } \ No newline at end of file diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp index b40afb8851..5821f2ac4e 100644 --- a/src/engine/downloadmanager.cpp +++ b/src/engine/downloadmanager.cpp @@ -45,6 +45,51 @@ namespace { written = fwrite(ptr, size, nmemb, stream); return written; } + + struct Progress { + CURL* curl; + const ghoul::filesystem::File* file; + openspace::DownloadManager::DownloadProgressCallback* callback; + }; + + int xferinfo(void *p, + curl_off_t dltotal, curl_off_t dlnow, + curl_off_t ultotal, curl_off_t ulnow) + { + Progress* myp = static_cast(p); + + if (myp->callback && *(myp->callback)) { + (*(myp->callback))( + *(myp->file), + static_cast(dlnow) / static_cast(dltotal) + ); + } + + + //if (*(myp->callback)) { + // *(myp->callback)( + // myp->file, + // static_cast(dlnow) / static_cast(dltotal) + // ); + //} + //CURL* curl = myp->curl; + + + + //double curtime = 0; + + //curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &curtime); + + //fprintf(stderr, "UP: %" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T + // " DOWN: %" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T + // "\r\n", + // ulnow, ultotal, dlnow, dltotal); + + return 0; + } + + + } namespace openspace { @@ -65,20 +110,26 @@ bool DownloadManager::downloadFile( { CURL* curl = curl_easy_init(); if (curl) { + FILE* fp = fopen(file.path().c_str(), "wb"); curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData); curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); + + if (progressCallback) { + Progress p = { curl, &file, &progressCallback }; + curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferinfo); + curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &p); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); + } + LDEBUG("Starting download for file: '" << url << "' into file '" << file.path() << "'"); CURLcode res = curl_easy_perform(curl); curl_easy_cleanup(curl); fclose(fp); - // TODO: incorporate progressCallback ---abock - // http://curl.haxx.se/libcurl/c/progressfunc.html - if (res != CURLE_OK) { LERROR("Error downloading file 'url': " << curl_easy_strerror(res)); return false; @@ -112,7 +163,7 @@ bool DownloadManager::downloadRequestFiles( bool success = downloadFile( fullRequest, requestFile, - [destination](const ghoul::filesystem::File& f) { + [destination, &progressCallback](const ghoul::filesystem::File& f) { LDEBUG("Finished: " << f.path()); std::ifstream temporary(f.path()); std::string line; @@ -126,7 +177,8 @@ bool DownloadManager::downloadRequestFiles( bool success = DlManager.downloadFile( line, destination.path() + "/" + file, - [&nFinished](const ghoul::filesystem::File& f) { ++nFinished; } + [&nFinished](const ghoul::filesystem::File& f) { ++nFinished; }, + [&progressCallback](const ghoul::filesystem::File& f, float progress) { progressCallback(f, progress); } ); } } From b7412e2e39add371de7d7894a7626310642a925e Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 14 Jun 2015 20:40:44 +0200 Subject: [PATCH 130/329] Making download multi-threaded --- apps/Launcher/syncwidget.cpp | 40 ++++++++++++++++++++++------------ apps/Launcher/syncwidget.h | 4 ++++ src/engine/downloadmanager.cpp | 21 +++++++++++++----- 3 files changed, 45 insertions(+), 20 deletions(-) diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 0d391e11d3..d5d06d1c1f 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -166,17 +166,23 @@ void SyncWidget::handleDirectFiles(QString module, DirectFiles files) { qDebug() << QString::fromStdString(f.filename()) << ": " << progress; }; - DlManager.downloadFile( - f.url.toStdString(), - fullPath(module, f.destination).toStdString(), - finishedCallback, - progressCallback + std::string url = f.url.toStdString(); + std::string path = fullPath(module, f.destination).toStdString(); + + _threads.push_back( + std::thread([url, path, finishedCallback, progressCallback](){ + DlManager.downloadFile( + url, + path, + finishedCallback, + progressCallback + ); + }) ); } } void SyncWidget::handleFileRequest(QString module, FileRequests files) { - return; qDebug() << "File Requests"; for (const FileRequest& f : files) { auto progressCallback = @@ -185,15 +191,21 @@ void SyncWidget::handleFileRequest(QString module, FileRequests files) { }; qDebug() << f.identifier << " (" << f.version << ")" << " -> " << f.destination; - DlManager.downloadRequestFiles( - f.identifier.toStdString(), - fullPath(module, f.destination).toStdString(), - f.version, - [](const ghoul::filesystem::File& f) { qDebug() << "finished"; }, - progressCallback + + std::string identifier = f.identifier.toStdString(); + std::string path = fullPath(module, f.destination).toStdString(); + int version = f.version; + _threads.push_back( + std::thread([identifier, path, version, progressCallback](){ + DlManager.downloadRequestFiles( + identifier, + path, + version, + [](const ghoul::filesystem::File& f) { qDebug() << "finished"; }, + progressCallback + ); + }) ); - - } } diff --git a/apps/Launcher/syncwidget.h b/apps/Launcher/syncwidget.h index fe9da6550c..d2a510fb74 100644 --- a/apps/Launcher/syncwidget.h +++ b/apps/Launcher/syncwidget.h @@ -31,6 +31,8 @@ #include +#include + class QBoxLayout; class QGridLayout; @@ -89,6 +91,8 @@ private: libtorrent::session* _session; QMap _infoWidgetMap; + + std::vector _threads; }; #endif // __SYNCWIDGET_H__ diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp index 5821f2ac4e..9eae4bfe2d 100644 --- a/src/engine/downloadmanager.cpp +++ b/src/engine/downloadmanager.cpp @@ -28,6 +28,7 @@ #include #include +#include #ifdef OPENSPACE_CURL_ENABLED #include @@ -160,10 +161,12 @@ bool DownloadManager::downloadRequestFiles( std::string requestFile = absPath("${TEMPORARY}/" + identifier); LDEBUG("Request File: " << requestFile); + std::vector threads; + bool success = downloadFile( fullRequest, requestFile, - [destination, &progressCallback](const ghoul::filesystem::File& f) { + [destination, &progressCallback, &threads](const ghoul::filesystem::File& f) { LDEBUG("Finished: " << f.path()); std::ifstream temporary(f.path()); std::string line; @@ -174,16 +177,22 @@ bool DownloadManager::downloadRequestFiles( std::string file = ghoul::filesystem::File(line).filename(); LDEBUG("\tLine: " << line << " ; Dest: " << destination.path() + "/" + file); - bool success = DlManager.downloadFile( - line, - destination.path() + "/" + file, - [&nFinished](const ghoul::filesystem::File& f) { ++nFinished; }, - [&progressCallback](const ghoul::filesystem::File& f, float progress) { progressCallback(f, progress); } + threads.push_back( + std::thread([&nFinished, line, destination, file, progressCallback](){ + DlManager.downloadFile( + line, + destination.path() + "/" + file, + [&nFinished](const ghoul::filesystem::File& f) { ++nFinished; }, + [&progressCallback](const ghoul::filesystem::File& f, float progress) { progressCallback(f, progress); } + );}) ); } } ); + for (std::thread& t : threads) + t.join(); + return true; From 0459f0472bf1077b63a0b8405c8da652b677e223 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 14 Jun 2015 21:04:42 +0200 Subject: [PATCH 131/329] Some fixes for multithreaded downloads --- apps/Launcher/infowidget.cpp | 5 +++++ apps/Launcher/infowidget.h | 4 +++- apps/Launcher/syncwidget.cpp | 36 +++++++++++++++++++++++++++--------- apps/Launcher/syncwidget.h | 3 ++- 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/apps/Launcher/infowidget.cpp b/apps/Launcher/infowidget.cpp index 38dc029528..7639f6bc1d 100644 --- a/apps/Launcher/infowidget.cpp +++ b/apps/Launcher/infowidget.cpp @@ -66,6 +66,11 @@ void InfoWidget::update(int currentBytes) { _progress->setValue(static_cast(progress * 100)); } +void InfoWidget::update(float progress) { + _bytes->setText(""); + _progress->setValue(progress); +} + void InfoWidget::error(QString message) { _messages->setText(message); } diff --git a/apps/Launcher/infowidget.h b/apps/Launcher/infowidget.h index 685430d9b6..7f7f1d086d 100644 --- a/apps/Launcher/infowidget.h +++ b/apps/Launcher/infowidget.h @@ -33,9 +33,11 @@ class QProgressBar; class InfoWidget : public QWidget { Q_OBJECT public: - InfoWidget(QString name, int totalBytes); + InfoWidget(QString name, int totalBytes = -1); void update(int currentBytes); + void update(float progress); + void error(QString message); private: diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index d5d06d1c1f..4acff9cf59 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -93,11 +94,15 @@ SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) } { - QGroupBox* downloadBox = new QGroupBox; + QWidget* downloadBox = new QGroupBox; + downloadBox->setFixedSize(500, 500); _downloadLayout = new QVBoxLayout; - downloadBox->setLayout(_downloadLayout); - layout->addWidget(downloadBox); + + QScrollArea* area = new QScrollArea; + area->setWidget(downloadBox); + + layout->addWidget(area); } setLayout(layout); @@ -155,15 +160,23 @@ void SyncWidget::clear() { void SyncWidget::handleDirectFiles(QString module, DirectFiles files) { qDebug() << "Direct Files"; for (const DirectFile& f : files) { + InfoWidget* w = new InfoWidget(f.destination); + _downloadLayout->addWidget(w); + //_stringInfoWidgetMap[f.destination] = w; + qDebug() << f.url << " -> " << f.destination; + + auto finishedCallback = - [](const ghoul::filesystem::File& f) { + [w](const ghoul::filesystem::File& f) { qDebug() << QString::fromStdString(f.filename()) << "finished"; + //delete w; }; auto progressCallback = - [](const ghoul::filesystem::File& f, float progress) { + [w](const ghoul::filesystem::File& f, float progress) { qDebug() << QString::fromStdString(f.filename()) << ": " << progress; + w->update(progress); }; std::string url = f.url.toStdString(); @@ -185,9 +198,14 @@ void SyncWidget::handleDirectFiles(QString module, DirectFiles files) { void SyncWidget::handleFileRequest(QString module, FileRequests files) { qDebug() << "File Requests"; for (const FileRequest& f : files) { + InfoWidget* w = new InfoWidget(f.identifier, -1); + _downloadLayout->addWidget(w); + //_stringInfoWidgetMap[f.identifier] = w; + auto progressCallback = - [](const ghoul::filesystem::File& f, float progress) { + [w](const ghoul::filesystem::File& f, float progress) { qDebug() << QString::fromStdString(f.filename()) << ": " << progress; + w->update(progress); }; qDebug() << f.identifier << " (" << f.version << ")" << " -> " << f.destination; @@ -241,7 +259,7 @@ void SyncWidget::handleTorrentFiles(QString module, TorrentFiles files) { libtorrent::size_type s = h.status().total_wanted; InfoWidget* w = new InfoWidget(f.file, h.status().total_wanted); _downloadLayout->addWidget(w); - _infoWidgetMap[h] = w; + _torrentInfoWidgetMap[h] = w; } } @@ -394,9 +412,9 @@ void SyncWidget::handleTimer() { std::vector handles = _session->get_torrents(); for (torrent_handle h : handles) { torrent_status s = h.status(); - InfoWidget* w = _infoWidgetMap[h]; + InfoWidget* w = _torrentInfoWidgetMap[h]; - w->update(s.total_wanted_done); + w->update(static_cast(s.total_wanted_done)); // qDebug() << "Name: " << QString::fromStdString(h.name()); // //torrent_status s = h.status(); diff --git a/apps/Launcher/syncwidget.h b/apps/Launcher/syncwidget.h index d2a510fb74..720a9d913a 100644 --- a/apps/Launcher/syncwidget.h +++ b/apps/Launcher/syncwidget.h @@ -90,7 +90,8 @@ private: QBoxLayout* _downloadLayout; libtorrent::session* _session; - QMap _infoWidgetMap; + QMap _torrentInfoWidgetMap; + //QMap _stringInfoWidgetMap; std::vector _threads; }; From 0a02906d8eaaee7589ae9628ee687098b0cbefbe Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 15 Jun 2015 00:37:48 +0200 Subject: [PATCH 132/329] More restructuring --- apps/Launcher/infowidget.cpp | 4 +- apps/Launcher/syncwidget.cpp | 104 ++++++++++++--------- apps/Launcher/syncwidget.h | 19 ++-- include/openspace/engine/downloadmanager.h | 4 + src/engine/downloadmanager.cpp | 17 ++-- 5 files changed, 85 insertions(+), 63 deletions(-) diff --git a/apps/Launcher/infowidget.cpp b/apps/Launcher/infowidget.cpp index 7639f6bc1d..d15fc1d506 100644 --- a/apps/Launcher/infowidget.cpp +++ b/apps/Launcher/infowidget.cpp @@ -36,6 +36,8 @@ InfoWidget::InfoWidget(QString name, int totalBytes) , _messages(nullptr) , _totalBytes(totalBytes) { + setFixedHeight(100); + QGridLayout* layout = new QGridLayout; _name = new QLabel(name); @@ -68,7 +70,7 @@ void InfoWidget::update(int currentBytes) { void InfoWidget::update(float progress) { _bytes->setText(""); - _progress->setValue(progress); + _progress->setValue(static_cast(progress * 100)); } void InfoWidget::error(QString message) { diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 4acff9cf59..668194ee51 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -95,7 +96,7 @@ SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) { QWidget* downloadBox = new QGroupBox; - downloadBox->setFixedSize(500, 500); + downloadBox->setMinimumSize(450, 1000); _downloadLayout = new QVBoxLayout; downloadBox->setLayout(_downloadLayout); @@ -157,80 +158,76 @@ void SyncWidget::clear() { } -void SyncWidget::handleDirectFiles(QString module, DirectFiles files) { +void SyncWidget::handleDirectFiles() { qDebug() << "Direct Files"; - for (const DirectFile& f : files) { + for (const DirectFile& f : _directFiles) { InfoWidget* w = new InfoWidget(f.destination); _downloadLayout->addWidget(w); //_stringInfoWidgetMap[f.destination] = w; qDebug() << f.url << " -> " << f.destination; - - auto finishedCallback = [w](const ghoul::filesystem::File& f) { qDebug() << QString::fromStdString(f.filename()) << "finished"; - //delete w; + delete w; + qApp->processEvents(); }; auto progressCallback = [w](const ghoul::filesystem::File& f, float progress) { qDebug() << QString::fromStdString(f.filename()) << ": " << progress; w->update(progress); + qApp->processEvents(); }; - std::string url = f.url.toStdString(); - std::string path = fullPath(module, f.destination).toStdString(); - - _threads.push_back( - std::thread([url, path, finishedCallback, progressCallback](){ - DlManager.downloadFile( - url, - path, - finishedCallback, - progressCallback - ); - }) + DlManager.downloadFile( + f.url.toStdString(), + fullPath(f.module, f.destination).toStdString(), + finishedCallback, + progressCallback ); } } -void SyncWidget::handleFileRequest(QString module, FileRequests files) { +void SyncWidget::handleFileRequest() { qDebug() << "File Requests"; - for (const FileRequest& f : files) { + for (const FileRequest& f : _fileRequests) { InfoWidget* w = new InfoWidget(f.identifier, -1); _downloadLayout->addWidget(w); - //_stringInfoWidgetMap[f.identifier] = w; + + auto finishedCallback = + [w](const ghoul::filesystem::File& f) { + qDebug() << QString::fromStdString(f.filename()) << "finished"; + delete w; + qApp->processEvents(); + }; auto progressCallback = [w](const ghoul::filesystem::File& f, float progress) { qDebug() << QString::fromStdString(f.filename()) << ": " << progress; w->update(progress); + qApp->processEvents(); }; qDebug() << f.identifier << " (" << f.version << ")" << " -> " << f.destination; std::string identifier = f.identifier.toStdString(); - std::string path = fullPath(module, f.destination).toStdString(); + std::string path = fullPath(f.module, f.destination).toStdString(); int version = f.version; - _threads.push_back( - std::thread([identifier, path, version, progressCallback](){ - DlManager.downloadRequestFiles( - identifier, - path, - version, - [](const ghoul::filesystem::File& f) { qDebug() << "finished"; }, - progressCallback - ); - }) + DlManager.downloadRequestFiles( + identifier, + path, + version, + finishedCallback, + progressCallback ); } } -void SyncWidget::handleTorrentFiles(QString module, TorrentFiles files) { +void SyncWidget::handleTorrentFiles() { qDebug() << "Torrent Files"; - for (const TorrentFile& f : files) { - QString file = QString::fromStdString(absPath(fullPath(module, f.file).toStdString())); + for (const TorrentFile& f : _torrentFiles) { + QString file = QString::fromStdString(absPath(fullPath(f.module, f.file).toStdString())); qDebug() << file; if (!QFileInfo(file).exists()) { @@ -240,7 +237,7 @@ void SyncWidget::handleTorrentFiles(QString module, TorrentFiles files) { libtorrent::error_code ec; libtorrent::add_torrent_params p; - p.save_path = absPath(fullPath(module, ".").toStdString()); + p.save_path = absPath(fullPath(f.module, ".").toStdString()); qDebug() << QString::fromStdString(p.save_path); p.ti = new libtorrent::torrent_info(file.toStdString(), ec); p.name = f.file.toStdString(); @@ -303,7 +300,6 @@ void SyncWidget::syncButtonPressed() { bool found = dataDictionary.getValue(FileDownloadKey, directDownloadFiles); if (found) { - DirectFiles files; for (int i = 1; i <= directDownloadFiles.size(); ++i) { if (!directDownloadFiles.hasKeyAndValue(std::to_string(i))) { qDebug() << QString::fromStdString(FileDownloadKey) << " is not a dictionary"; @@ -319,17 +315,16 @@ void SyncWidget::syncButtonPressed() { } std::string dest = d.value(DestinationKey); - files.append({ + _directFiles.append({ + module, QString::fromStdString(url), QString::fromStdString(dest) }); } - handleDirectFiles(module, files); } found = dataDictionary.getValue(FileRequestKey, fileRequests); if (found) { - FileRequests files; for (int i = 1; i <= fileRequests.size(); ++i) { ghoul::Dictionary d = fileRequests.value(std::to_string(i)); std::string url = d.value(IdentifierKey); @@ -337,28 +332,47 @@ void SyncWidget::syncButtonPressed() { int version = static_cast(d.value(VersionKey)); - files.append({ + _fileRequests.append({ + module, QString::fromStdString(url), QString::fromStdString(dest), version }); } - handleFileRequest(module, files); } found = dataDictionary.getValue(TorrentFilesKey, torrentFiles); if (found) { - TorrentFiles torrents; for (int i = 1; i <= torrentFiles.size(); ++i) { std::string f = torrentFiles.value(std::to_string(i)); - torrents.append({QString::fromStdString(f)}); + _torrentFiles.append({ + module, + QString::fromStdString(f) + }); } - handleTorrentFiles(module, torrents); } } } } + + //// Make the lists unique + //{ + // QSet s = _directFiles.toSet(); + // _directFiles = QList::fromSet(s); + //} + //{ + // QSet s = _fileRequests.toSet(); + // _fileRequests = QList::fromSet(s); + //} + //{ + // QSet s = _torrentFiles.toSet(); + // _torrentFiles = QList::fromSet(s); + //} + + handleDirectFiles(); + handleFileRequest(); + handleTorrentFiles(); } QStringList SyncWidget::selectedScenes() const { diff --git a/apps/Launcher/syncwidget.h b/apps/Launcher/syncwidget.h index 720a9d913a..c850586188 100644 --- a/apps/Launcher/syncwidget.h +++ b/apps/Launcher/syncwidget.h @@ -31,7 +31,7 @@ #include -#include +//#include class QBoxLayout; class QGridLayout; @@ -58,31 +58,31 @@ private slots: private: struct DirectFile { + QString module; QString url; QString destination; }; - typedef QList DirectFiles; struct FileRequest { + QString module; QString identifier; QString destination; int version; }; - typedef QList FileRequests; struct TorrentFile { + QString module; QString file; }; - typedef QList TorrentFiles; void clear(); QStringList selectedScenes() const; QString fullPath(QString module, QString destination) const; - void handleDirectFiles(QString module, DirectFiles files); - void handleFileRequest(QString module, FileRequests files); - void handleTorrentFiles(QString module, TorrentFiles files); + void handleDirectFiles(); + void handleFileRequest(); + void handleTorrentFiles(); QMap _sceneFiles; QString _modulesDirectory; @@ -91,9 +91,10 @@ private: libtorrent::session* _session; QMap _torrentInfoWidgetMap; - //QMap _stringInfoWidgetMap; - std::vector _threads; + QList _directFiles; + QList _fileRequests; + QList _torrentFiles; }; #endif // __SYNCWIDGET_H__ diff --git a/include/openspace/engine/downloadmanager.h b/include/openspace/engine/downloadmanager.h index 13f1f1f7e4..b000620b6e 100644 --- a/include/openspace/engine/downloadmanager.h +++ b/include/openspace/engine/downloadmanager.h @@ -42,6 +42,10 @@ public: void (const ghoul::filesystem::File&) > DownloadFinishedCallback; + //struct FileFuture { + + //}; + DownloadManager(std::string requestURL, int applicationVersion); bool downloadFile( diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp index 9eae4bfe2d..b05bd42bcc 100644 --- a/src/engine/downloadmanager.cpp +++ b/src/engine/downloadmanager.cpp @@ -28,7 +28,7 @@ #include #include -#include +//#include #ifdef OPENSPACE_CURL_ENABLED #include @@ -161,12 +161,12 @@ bool DownloadManager::downloadRequestFiles( std::string requestFile = absPath("${TEMPORARY}/" + identifier); LDEBUG("Request File: " << requestFile); - std::vector threads; + //std::vector threads; bool success = downloadFile( fullRequest, requestFile, - [destination, &progressCallback, &threads](const ghoul::filesystem::File& f) { + [destination, &progressCallback](const ghoul::filesystem::File& f) { LDEBUG("Finished: " << f.path()); std::ifstream temporary(f.path()); std::string line; @@ -177,21 +177,22 @@ bool DownloadManager::downloadRequestFiles( std::string file = ghoul::filesystem::File(line).filename(); LDEBUG("\tLine: " << line << " ; Dest: " << destination.path() + "/" + file); - threads.push_back( - std::thread([&nFinished, line, destination, file, progressCallback](){ + //threads.push_back( + //std::thread([&nFinished, line, destination, file, progressCallback](){ DlManager.downloadFile( line, destination.path() + "/" + file, [&nFinished](const ghoul::filesystem::File& f) { ++nFinished; }, [&progressCallback](const ghoul::filesystem::File& f, float progress) { progressCallback(f, progress); } - );}) + //);} + //) ); } } ); - for (std::thread& t : threads) - t.join(); + //for (std::thread& t : threads) + //t.join(); From fb7f49a6bba5d5213fb9237d9188b5d6019c8a42 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 15 Jun 2015 02:05:56 +0200 Subject: [PATCH 133/329] Making single file download fully (and correctly) multithreaded --- apps/Launcher/syncwidget.cpp | 32 +-- include/openspace/engine/downloadmanager.h | 36 ++-- src/engine/downloadmanager.cpp | 238 +++++++++++---------- 3 files changed, 163 insertions(+), 143 deletions(-) diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 668194ee51..4158ba6c79 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -168,16 +168,18 @@ void SyncWidget::handleDirectFiles() { qDebug() << f.url << " -> " << f.destination; auto finishedCallback = - [w](const ghoul::filesystem::File& f) { - qDebug() << QString::fromStdString(f.filename()) << "finished"; - delete w; - qApp->processEvents(); + [w](const openspace::DownloadManager::FileFuture& f) { + std::cout << f.filePath << ": Finished" << std::endl; + //qDebug() << QString::fromStdString(f.file.filename()) << "finished"; + //delete w; + //qApp->processEvents(); }; auto progressCallback = - [w](const ghoul::filesystem::File& f, float progress) { - qDebug() << QString::fromStdString(f.filename()) << ": " << progress; - w->update(progress); - qApp->processEvents(); + [w](const openspace::DownloadManager::FileFuture& f) { + std::cout << f.filePath << ": " << f.progress << std::endl; + //qDebug() << QString::fromStdString(f.file.filename()) << ": " << f.progress; + //w->update(f.progress); + //qApp->processEvents(); }; DlManager.downloadFile( @@ -214,13 +216,13 @@ void SyncWidget::handleFileRequest() { std::string identifier = f.identifier.toStdString(); std::string path = fullPath(f.module, f.destination).toStdString(); int version = f.version; - DlManager.downloadRequestFiles( - identifier, - path, - version, - finishedCallback, - progressCallback - ); + //DlManager.downloadRequestFiles( + // identifier, + // path, + // version, + // finishedCallback, + // progressCallback + //); } } diff --git a/include/openspace/engine/downloadmanager.h b/include/openspace/engine/downloadmanager.h index b000620b6e..b3f651b038 100644 --- a/include/openspace/engine/downloadmanager.h +++ b/include/openspace/engine/downloadmanager.h @@ -28,40 +28,44 @@ #include #include #include + +#include #include #include namespace openspace { +// Multithreaded class DownloadManager : public ghoul::Singleton { public: - typedef std::function < - void(const ghoul::filesystem::File&, float progress) - > DownloadProgressCallback; - typedef std::function< - void (const ghoul::filesystem::File&) - > DownloadFinishedCallback; + struct FileFuture { + FileFuture(std::string file); - //struct FileFuture { + float progress; // [0,1] + std::string filePath; + std::string errorMessage; + }; - //}; + typedef std::function DownloadProgressCallback; + typedef std::function DownloadFinishedCallback; DownloadManager(std::string requestURL, int applicationVersion); - bool downloadFile( + // callers responsibility to delete + FileFuture* downloadFile( const std::string& url, const ghoul::filesystem::File& file, DownloadFinishedCallback finishedCallback = DownloadFinishedCallback(), DownloadProgressCallback progressCallback = DownloadProgressCallback() ); - bool downloadRequestFiles( - const std::string& identifier, - const ghoul::filesystem::Directory& destination, - int version, - DownloadFinishedCallback finishedCallback = DownloadFinishedCallback(), - DownloadProgressCallback progressCallback = DownloadProgressCallback() - ); + //bool downloadRequestFiles( + // const std::string& identifier, + // const ghoul::filesystem::Directory& destination, + // int version, + // DownloadFinishedCallback finishedCallback = DownloadFinishedCallback(), + // DownloadProgressCallback progressCallback = DownloadProgressCallback() + //); private: std::string _requestURL; diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp index b05bd42bcc..68fc47aae3 100644 --- a/src/engine/downloadmanager.cpp +++ b/src/engine/downloadmanager.cpp @@ -28,7 +28,7 @@ #include #include -//#include +#include #ifdef OPENSPACE_CURL_ENABLED #include @@ -41,46 +41,47 @@ namespace { const std::string RequestFileVersion = "file_version"; const std::string RequestApplicationVersion = "application_version"; + struct ProgressInformation { + CURL* curl; + openspace::DownloadManager::FileFuture* future; + const openspace::DownloadManager::DownloadProgressCallback* callback; + }; + size_t writeData(void* ptr, size_t size, size_t nmemb, FILE* stream) { size_t written; written = fwrite(ptr, size, nmemb, stream); return written; } - struct Progress { - CURL* curl; - const ghoul::filesystem::File* file; - openspace::DownloadManager::DownloadProgressCallback* callback; - }; - int xferinfo(void *p, + int xferinfo(void* p, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { - Progress* myp = static_cast(p); + if (dltotal == 0) + return 0; - if (myp->callback && *(myp->callback)) { - (*(myp->callback))( - *(myp->file), - static_cast(dlnow) / static_cast(dltotal) - ); + ghoul_assert(p, "Passed progress information is nullptr"); + ProgressInformation* i = static_cast(p); + ghoul_assert(i, "Passed pointer is not a ProgressInformation"); + ghoul_assert(i->curl, "CURL pointer is nullptr"); + ghoul_assert(i->future, "FileFuture is not initialized"); + ghoul_assert(i->callback, "Callback pointer is nullptr"); + + ghoul_assert(dltotal, "Download total is 0"); + ghoul_assert(dlnow <= dltotal, "Downloaded filesize is bigger then total size"); + i->future->progress = static_cast(dlnow) / static_cast(dltotal); + + if (*(i->callback)) { + // The callback function is a pointer to an std::function; that is the reason + // for the excessive referencing + (*(i->callback))(*(i->future)); } - //if (*(myp->callback)) { - // *(myp->callback)( - // myp->file, - // static_cast(dlnow) / static_cast(dltotal) - // ); - //} //CURL* curl = myp->curl; - - - //double curtime = 0; - //curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &curtime); - //fprintf(stderr, "UP: %" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T // " DOWN: %" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T // "\r\n", @@ -88,9 +89,6 @@ namespace { return 0; } - - - } namespace openspace { @@ -100,104 +98,120 @@ DownloadManager::DownloadManager(std::string requestURL, int applicationVersion) , _applicationVersion(std::move(applicationVersion)) { curl_global_init(CURL_GLOBAL_ALL); - // Check if URL is accessible + // TODO: Check if URL is accessible ---abock + // TODO: Allow for multiple requestURLs } -bool DownloadManager::downloadFile( +DownloadManager::FileFuture* DownloadManager::downloadFile( const std::string& url, const ghoul::filesystem::File& file, DownloadFinishedCallback finishedCallback, DownloadProgressCallback progressCallback) { - CURL* curl = curl_easy_init(); - if (curl) { - - FILE* fp = fopen(file.path().c_str(), "wb"); - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); - - if (progressCallback) { - Progress p = { curl, &file, &progressCallback }; - curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferinfo); - curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &p); - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); - } - - LDEBUG("Starting download for file: '" << url << - "' into file '" << file.path() << "'"); - CURLcode res = curl_easy_perform(curl); - curl_easy_cleanup(curl); - fclose(fp); - - if (res != CURLE_OK) { - LERROR("Error downloading file 'url': " << curl_easy_strerror(res)); - return false; - } - - if (finishedCallback) - finishedCallback(file); - return true; - } -} - -bool DownloadManager::downloadRequestFiles( - const std::string& identifier, - const ghoul::filesystem::Directory& destination, - int version, - DownloadFinishedCallback finishedCallback, - DownloadProgressCallback progressCallback) -{ - bool s = FileSys.createDirectory(destination, true); - // TODO: Check s ---abock - // TODO: Escaping is necessary ---abock - const std::string fullRequest =_requestURL + "?" + - RequestIdentifier + "=" + identifier + "&" + - RequestFileVersion + "=" + std::to_string(version) + "&" + - RequestApplicationVersion + "=" + std::to_string(_applicationVersion); - LDEBUG("Request: " << fullRequest); - - std::string requestFile = absPath("${TEMPORARY}/" + identifier); - LDEBUG("Request File: " << requestFile); - - //std::vector threads; - - bool success = downloadFile( - fullRequest, - requestFile, - [destination, &progressCallback](const ghoul::filesystem::File& f) { - LDEBUG("Finished: " << f.path()); - std::ifstream temporary(f.path()); - std::string line; - int nFiles = 0; - int nFinished = 0; - while (std::getline(temporary, line)) { - ++nFiles; - std::string file = ghoul::filesystem::File(line).filename(); - - LDEBUG("\tLine: " << line << " ; Dest: " << destination.path() + "/" + file); - //threads.push_back( - //std::thread([&nFinished, line, destination, file, progressCallback](){ - DlManager.downloadFile( - line, - destination.path() + "/" + file, - [&nFinished](const ghoul::filesystem::File& f) { ++nFinished; }, - [&progressCallback](const ghoul::filesystem::File& f, float progress) { progressCallback(f, progress); } - //);} - //) - ); - } - } + FileFuture* future = new FileFuture( + file.filename() ); + FILE* fp = fopen(file.path().c_str(), "wb"); - //for (std::thread& t : threads) - //t.join(); + LDEBUG("Starting download for file: '" << url << + "' into file '" << file.path() << "'"); + std::thread([url, finishedCallback, progressCallback, future, fp]() { + CURL* curl = curl_easy_init(); + if (curl) { + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData); + if (progressCallback) { + ProgressInformation p = { + curl, + future, + &progressCallback + }; + curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferinfo); + curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &p); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); + } + CURLcode res = curl_easy_perform(curl); + curl_easy_cleanup(curl); + fclose(fp); - return true; + if (res != CURLE_OK) { + future->errorMessage = curl_easy_strerror(res); + } + + if (finishedCallback) + finishedCallback(*future); + } + }).detach(); + + return future; } +//bool DownloadManager::downloadRequestFiles( +// const std::string& identifier, +// const ghoul::filesystem::Directory& destination, +// int version, +// DownloadFinishedCallback finishedCallback, +// DownloadProgressCallback progressCallback) +//{ +// bool s = FileSys.createDirectory(destination, true); +// // TODO: Check s ---abock +// // TODO: Escaping is necessary ---abock +// const std::string fullRequest =_requestURL + "?" + +// RequestIdentifier + "=" + identifier + "&" + +// RequestFileVersion + "=" + std::to_string(version) + "&" + +// RequestApplicationVersion + "=" + std::to_string(_applicationVersion); +// LDEBUG("Request: " << fullRequest); +// +// std::string requestFile = absPath("${TEMPORARY}/" + identifier); +// LDEBUG("Request File: " << requestFile); +// +// //std::vector threads; +// +// bool success = downloadFile( +// fullRequest, +// requestFile, +// [destination, &progressCallback](const ghoul::filesystem::File& f) { +// LDEBUG("Finished: " << f.path()); +// std::ifstream temporary(f.path()); +// std::string line; +// int nFiles = 0; +// int nFinished = 0; +// while (std::getline(temporary, line)) { +// ++nFiles; +// std::string file = ghoul::filesystem::File(line).filename(); +// +// LDEBUG("\tLine: " << line << " ; Dest: " << destination.path() + "/" + file); +// //threads.push_back( +// //std::thread([&nFinished, line, destination, file, progressCallback](){ +// DlManager.downloadFile( +// line, +// destination.path() + "/" + file, +// [&nFinished](const ghoul::filesystem::File& f) { ++nFinished; }, +// [&progressCallback](const ghoul::filesystem::File& f, float progress) { progressCallback(f, progress); } +// //);} +// //) +// ); +// } +// } +// ); +// +// //for (std::thread& t : threads) +// //t.join(); +// +// +// +// return true; +//} + + + +DownloadManager::FileFuture::FileFuture(std::string file) + : progress(0.f) + , filePath(std::move(file)) +{} } // namespace openspace From 0a233eb6bcba02a162d9dbd0808ab59a297815ef Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 15 Jun 2015 02:25:54 +0200 Subject: [PATCH 134/329] More work on correct displaying status of single download files --- apps/Launcher/syncwidget.cpp | 32 ++++++++-------------- apps/Launcher/syncwidget.h | 5 ++++ include/openspace/engine/downloadmanager.h | 2 ++ src/engine/downloadmanager.cpp | 29 +++++++++++--------- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 4158ba6c79..f3582a6644 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -26,7 +26,6 @@ #include "infowidget.h" -#include #include #include @@ -163,31 +162,15 @@ void SyncWidget::handleDirectFiles() { for (const DirectFile& f : _directFiles) { InfoWidget* w = new InfoWidget(f.destination); _downloadLayout->addWidget(w); - //_stringInfoWidgetMap[f.destination] = w; qDebug() << f.url << " -> " << f.destination; - auto finishedCallback = - [w](const openspace::DownloadManager::FileFuture& f) { - std::cout << f.filePath << ": Finished" << std::endl; - //qDebug() << QString::fromStdString(f.file.filename()) << "finished"; - //delete w; - //qApp->processEvents(); - }; - auto progressCallback = - [w](const openspace::DownloadManager::FileFuture& f) { - std::cout << f.filePath << ": " << f.progress << std::endl; - //qDebug() << QString::fromStdString(f.file.filename()) << ": " << f.progress; - //w->update(f.progress); - //qApp->processEvents(); - }; - - DlManager.downloadFile( + openspace::DownloadManager::FileFuture* future = DlManager.downloadFile( f.url.toStdString(), - fullPath(f.module, f.destination).toStdString(), - finishedCallback, - progressCallback + fullPath(f.module, f.destination).toStdString() ); + _futures.push_back(future); + _futureInfoWidgetMap[future] = w; } } @@ -398,6 +381,13 @@ QString SyncWidget::fullPath(QString module, QString destination) const { void SyncWidget::handleTimer() { using namespace libtorrent; + using FileFuture = openspace::DownloadManager::FileFuture; + + for (FileFuture* f : _futures) { + InfoWidget* w = _futureInfoWidgetMap[f]; + + w->update(f->progress); + } //_session->post_torrent_updates(); //libtorrent::session_settings settings = _session->settings(); diff --git a/apps/Launcher/syncwidget.h b/apps/Launcher/syncwidget.h index c850586188..0aecfc0761 100644 --- a/apps/Launcher/syncwidget.h +++ b/apps/Launcher/syncwidget.h @@ -29,6 +29,8 @@ #include +#include + #include //#include @@ -95,6 +97,9 @@ private: QList _directFiles; QList _fileRequests; QList _torrentFiles; + + std::vector _futures; + std::map _futureInfoWidgetMap; }; #endif // __SYNCWIDGET_H__ diff --git a/include/openspace/engine/downloadmanager.h b/include/openspace/engine/downloadmanager.h index b3f651b038..cc5d57219f 100644 --- a/include/openspace/engine/downloadmanager.h +++ b/include/openspace/engine/downloadmanager.h @@ -41,7 +41,9 @@ public: struct FileFuture { FileFuture(std::string file); + long long totalSize; float progress; // [0,1] + bool isFinished; std::string filePath; std::string errorMessage; }; diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp index 68fc47aae3..ab80bd36b5 100644 --- a/src/engine/downloadmanager.cpp +++ b/src/engine/downloadmanager.cpp @@ -70,6 +70,7 @@ namespace { ghoul_assert(dltotal, "Download total is 0"); ghoul_assert(dlnow <= dltotal, "Downloaded filesize is bigger then total size"); + i->future->totalSize = dltotal; i->future->progress = static_cast(dlnow) / static_cast(dltotal); if (*(i->callback)) { @@ -123,24 +124,23 @@ DownloadManager::FileFuture* DownloadManager::downloadFile( curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData); - if (progressCallback) { - ProgressInformation p = { - curl, - future, - &progressCallback - }; - curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferinfo); - curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &p); - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); - } + ProgressInformation p = { + curl, + future, + &progressCallback + }; + curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferinfo); + curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &p); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); CURLcode res = curl_easy_perform(curl); curl_easy_cleanup(curl); fclose(fp); - if (res != CURLE_OK) { + if (res == CURLE_OK) + future->isFinished = true; + else future->errorMessage = curl_easy_strerror(res); - } if (finishedCallback) finishedCallback(*future); @@ -210,8 +210,11 @@ DownloadManager::FileFuture* DownloadManager::downloadFile( DownloadManager::FileFuture::FileFuture(std::string file) - : progress(0.f) + : totalSize(-1) + , progress(0.f) + , isFinished(false) , filePath(std::move(file)) + , errorMessage("") {} } // namespace openspace From 13bee28974324b0202db711a4ea1fbcb0628405d Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 15 Jun 2015 02:41:27 +0200 Subject: [PATCH 135/329] Enable multi-threaded download support for filerequests --- apps/Launcher/syncwidget.cpp | 38 +++----- include/openspace/engine/downloadmanager.h | 14 +-- src/engine/downloadmanager.cpp | 106 ++++++++++----------- 3 files changed, 71 insertions(+), 87 deletions(-) diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index f3582a6644..d0b020eaa6 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -177,35 +177,25 @@ void SyncWidget::handleDirectFiles() { void SyncWidget::handleFileRequest() { qDebug() << "File Requests"; for (const FileRequest& f : _fileRequests) { - InfoWidget* w = new InfoWidget(f.identifier, -1); - _downloadLayout->addWidget(w); - - auto finishedCallback = - [w](const ghoul::filesystem::File& f) { - qDebug() << QString::fromStdString(f.filename()) << "finished"; - delete w; - qApp->processEvents(); - }; - - auto progressCallback = - [w](const ghoul::filesystem::File& f, float progress) { - qDebug() << QString::fromStdString(f.filename()) << ": " << progress; - w->update(progress); - qApp->processEvents(); - }; - qDebug() << f.identifier << " (" << f.version << ")" << " -> " << f.destination; std::string identifier = f.identifier.toStdString(); std::string path = fullPath(f.module, f.destination).toStdString(); int version = f.version; - //DlManager.downloadRequestFiles( - // identifier, - // path, - // version, - // finishedCallback, - // progressCallback - //); + std::vector futures = + DlManager.downloadRequestFiles( + identifier, + path, + version + ); + + _futures.insert(_futures.end(), futures.begin(), futures.end()); + for (openspace::DownloadManager::FileFuture* f : futures) { + InfoWidget* w = new InfoWidget(QString::fromStdString(f->filePath), -1); + _downloadLayout->addWidget(w); + + _futureInfoWidgetMap[f] = w; + } } } diff --git a/include/openspace/engine/downloadmanager.h b/include/openspace/engine/downloadmanager.h index cc5d57219f..95006d5a94 100644 --- a/include/openspace/engine/downloadmanager.h +++ b/include/openspace/engine/downloadmanager.h @@ -61,13 +61,13 @@ public: DownloadProgressCallback progressCallback = DownloadProgressCallback() ); - //bool downloadRequestFiles( - // const std::string& identifier, - // const ghoul::filesystem::Directory& destination, - // int version, - // DownloadFinishedCallback finishedCallback = DownloadFinishedCallback(), - // DownloadProgressCallback progressCallback = DownloadProgressCallback() - //); + std::vector downloadRequestFiles( + const std::string& identifier, + const ghoul::filesystem::Directory& destination, + int version, + DownloadFinishedCallback finishedCallback = DownloadFinishedCallback(), + DownloadProgressCallback progressCallback = DownloadProgressCallback() + ); private: std::string _requestURL; diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp index ab80bd36b5..a15bb27c4f 100644 --- a/src/engine/downloadmanager.cpp +++ b/src/engine/downloadmanager.cpp @@ -150,64 +150,58 @@ DownloadManager::FileFuture* DownloadManager::downloadFile( return future; } -//bool DownloadManager::downloadRequestFiles( -// const std::string& identifier, -// const ghoul::filesystem::Directory& destination, -// int version, -// DownloadFinishedCallback finishedCallback, -// DownloadProgressCallback progressCallback) -//{ -// bool s = FileSys.createDirectory(destination, true); -// // TODO: Check s ---abock -// // TODO: Escaping is necessary ---abock -// const std::string fullRequest =_requestURL + "?" + -// RequestIdentifier + "=" + identifier + "&" + -// RequestFileVersion + "=" + std::to_string(version) + "&" + -// RequestApplicationVersion + "=" + std::to_string(_applicationVersion); -// LDEBUG("Request: " << fullRequest); -// -// std::string requestFile = absPath("${TEMPORARY}/" + identifier); -// LDEBUG("Request File: " << requestFile); -// -// //std::vector threads; -// -// bool success = downloadFile( -// fullRequest, -// requestFile, -// [destination, &progressCallback](const ghoul::filesystem::File& f) { -// LDEBUG("Finished: " << f.path()); -// std::ifstream temporary(f.path()); -// std::string line; -// int nFiles = 0; -// int nFinished = 0; -// while (std::getline(temporary, line)) { -// ++nFiles; -// std::string file = ghoul::filesystem::File(line).filename(); -// -// LDEBUG("\tLine: " << line << " ; Dest: " << destination.path() + "/" + file); -// //threads.push_back( -// //std::thread([&nFinished, line, destination, file, progressCallback](){ -// DlManager.downloadFile( -// line, -// destination.path() + "/" + file, -// [&nFinished](const ghoul::filesystem::File& f) { ++nFinished; }, -// [&progressCallback](const ghoul::filesystem::File& f, float progress) { progressCallback(f, progress); } -// //);} -// //) -// ); -// } -// } -// ); -// -// //for (std::thread& t : threads) -// //t.join(); -// -// -// -// return true; -//} +std::vector DownloadManager::downloadRequestFiles( + const std::string& identifier, + const ghoul::filesystem::Directory& destination, + int version, + DownloadFinishedCallback finishedCallback, + DownloadProgressCallback progressCallback) +{ + std::vector futures; + bool s = FileSys.createDirectory(destination, true); + // TODO: Check s ---abock + // TODO: Escaping is necessary ---abock + const std::string fullRequest =_requestURL + "?" + + RequestIdentifier + "=" + identifier + "&" + + RequestFileVersion + "=" + std::to_string(version) + "&" + + RequestApplicationVersion + "=" + std::to_string(_applicationVersion); + LDEBUG("Request: " << fullRequest); + std::string requestFile = absPath("${TEMPORARY}/" + identifier); + LDEBUG("Request File: " << requestFile); + bool isFinished = false; + auto callback = [&futures, destination, &progressCallback, &isFinished, requestFile](const FileFuture& f) { + LDEBUG("Finished: " << requestFile); + std::ifstream temporary(requestFile); + std::string line; + int nFiles = 0; + int nFinished = 0; + while (std::getline(temporary, line)) { + ++nFiles; + std::string file = ghoul::filesystem::File(line).filename(); + + LDEBUG("\tLine: " << line << " ; Dest: " << destination.path() + "/" + file); + + FileFuture* future = DlManager.downloadFile( + line, + destination.path() + "/" + file + ); + futures.push_back(future); + } + isFinished = true; + }; + + FileFuture* f = downloadFile( + fullRequest, + requestFile, + callback + ); + + while (!isFinished) {} + + return futures; +} DownloadManager::FileFuture::FileFuture(std::string file) : totalSize(-1) From 5967063754de30d9b42cc688ccf465bb2a0e6061 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 15 Jun 2015 19:37:06 +0200 Subject: [PATCH 136/329] More file cleanups --- apps/Launcher/mainwindow.cpp | 19 +++++++++++++++++++ apps/Launcher/syncwidget.cpp | 12 +++++++++++- data | 2 +- include/openspace/engine/downloadmanager.h | 3 +++ src/engine/downloadmanager.cpp | 4 ++-- 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/apps/Launcher/mainwindow.cpp b/apps/Launcher/mainwindow.cpp index 9f7fd7f55a..440f5725a7 100644 --- a/apps/Launcher/mainwindow.cpp +++ b/apps/Launcher/mainwindow.cpp @@ -27,6 +27,10 @@ #include "shortcutwidget.h" #include "syncwidget.h" +#include +#include +#include + #include #include #include @@ -49,6 +53,17 @@ namespace { #else const QString OpenSpaceExecutable = "OpenSpace"; #endif + + class QLog : public ghoul::logging::Log { + public: + void log( + ghoul::logging::LogManager::LogLevel level, + const std::string& category, + const std::string& message + ) { + //qDebug() << QString::fromStdString(category) << ": " << QString::fromStdString(message); + } + }; } MainWindow::MainWindow() @@ -162,6 +177,10 @@ void MainWindow::initialize() { } _syncWidget->setSceneFiles(_sceneFiles); _syncWidget->setModulesDirectory(ModulesDirectory); + + ghoul::logging::LogManager::initialize(ghoul::logging::LogManager::LogLevel::Debug); + LogMgr.addLog(new ghoul::logging::ConsoleLog); + LogMgr.addLog(new QLog); } void MainWindow::shortcutButtonPressed() { diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index d0b020eaa6..aae73d94ab 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -373,12 +373,22 @@ void SyncWidget::handleTimer() { using namespace libtorrent; using FileFuture = openspace::DownloadManager::FileFuture; + std::vector toRemove; for (FileFuture* f : _futures) { InfoWidget* w = _futureInfoWidgetMap[f]; - w->update(f->progress); + if (f->isFinished) { + toRemove.push_back(f); + _downloadLayout->removeWidget(w); + delete w; + } + else + w->update(f->progress); } + for (FileFuture* f : toRemove) + _futures.erase(std::remove(_futures.begin(), _futures.end(), f), _futures.end()); + //_session->post_torrent_updates(); //libtorrent::session_settings settings = _session->settings(); diff --git a/data b/data index 9de5bb5767..ef2707d9b9 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 9de5bb57678b171f1bc53a176ab63f380ce64ed9 +Subproject commit ef2707d9b919683b5d62778792d7da29ab2ff7cd diff --git a/include/openspace/engine/downloadmanager.h b/include/openspace/engine/downloadmanager.h index 95006d5a94..c6423f4aee 100644 --- a/include/openspace/engine/downloadmanager.h +++ b/include/openspace/engine/downloadmanager.h @@ -69,6 +69,9 @@ public: DownloadProgressCallback progressCallback = DownloadProgressCallback() ); + // TODO: Add async version of downloadRequestFiles that returns a filefuture + // that can be used to call an additional function + private: std::string _requestURL; int _applicationVersion; diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp index a15bb27c4f..764940012e 100644 --- a/src/engine/downloadmanager.cpp +++ b/src/engine/downloadmanager.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -47,6 +48,7 @@ namespace { const openspace::DownloadManager::DownloadProgressCallback* callback; }; + size_t writeData(void* ptr, size_t size, size_t nmemb, FILE* stream) { size_t written; written = fwrite(ptr, size, nmemb, stream); @@ -68,8 +70,6 @@ namespace { ghoul_assert(i->future, "FileFuture is not initialized"); ghoul_assert(i->callback, "Callback pointer is nullptr"); - ghoul_assert(dltotal, "Download total is 0"); - ghoul_assert(dlnow <= dltotal, "Downloaded filesize is bigger then total size"); i->future->totalSize = dltotal; i->future->progress = static_cast(dlnow) / static_cast(dltotal); From 886c8d2da6da5e47f3ab018316eed63840f7d688 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 15 Jun 2015 19:58:23 +0200 Subject: [PATCH 137/329] Reenable bootstrapping of torrent DHTs Enable setting of overwrite settings for download files --- apps/Launcher/syncwidget.cpp | 23 +++++++++++++++------- include/openspace/engine/downloadmanager.h | 2 ++ src/engine/downloadmanager.cpp | 14 ++++++++++--- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index aae73d94ab..a9b5fc615e 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -119,7 +119,11 @@ SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) } _session->start_upnp(); _session->start_dht(); - + + _session->add_dht_router({ "dht.transmissionbt.com", 6881}); + _session->add_dht_router({ "router.bittorrent.com", 6881}); + _session->add_dht_router({ "router.utorrent.com", 6881 }); + _session->add_dht_router({ "router.bitcomet.com", 6881 }); QTimer* timer = new QTimer(this); QObject::connect(timer, SIGNAL(timeout()), this, SLOT(handleTimer())); @@ -160,17 +164,21 @@ void SyncWidget::clear() { void SyncWidget::handleDirectFiles() { qDebug() << "Direct Files"; for (const DirectFile& f : _directFiles) { - InfoWidget* w = new InfoWidget(f.destination); - _downloadLayout->addWidget(w); qDebug() << f.url << " -> " << f.destination; openspace::DownloadManager::FileFuture* future = DlManager.downloadFile( f.url.toStdString(), - fullPath(f.module, f.destination).toStdString() + fullPath(f.module, f.destination).toStdString(), + false ); - _futures.push_back(future); - _futureInfoWidgetMap[future] = w; + if (future) { + InfoWidget* w = new InfoWidget(f.destination); + _downloadLayout->addWidget(w); + + _futures.push_back(future); + _futureInfoWidgetMap[future] = w; + } } } @@ -186,7 +194,8 @@ void SyncWidget::handleFileRequest() { DlManager.downloadRequestFiles( identifier, path, - version + version, + false ); _futures.insert(_futures.end(), futures.begin(), futures.end()); diff --git a/include/openspace/engine/downloadmanager.h b/include/openspace/engine/downloadmanager.h index c6423f4aee..460f77581b 100644 --- a/include/openspace/engine/downloadmanager.h +++ b/include/openspace/engine/downloadmanager.h @@ -57,6 +57,7 @@ public: FileFuture* downloadFile( const std::string& url, const ghoul::filesystem::File& file, + bool overrideFile = true, DownloadFinishedCallback finishedCallback = DownloadFinishedCallback(), DownloadProgressCallback progressCallback = DownloadProgressCallback() ); @@ -65,6 +66,7 @@ public: const std::string& identifier, const ghoul::filesystem::Directory& destination, int version, + bool overrideFiles = true, DownloadFinishedCallback finishedCallback = DownloadFinishedCallback(), DownloadProgressCallback progressCallback = DownloadProgressCallback() ); diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp index 764940012e..5a501faadb 100644 --- a/src/engine/downloadmanager.cpp +++ b/src/engine/downloadmanager.cpp @@ -106,9 +106,13 @@ DownloadManager::DownloadManager(std::string requestURL, int applicationVersion) DownloadManager::FileFuture* DownloadManager::downloadFile( const std::string& url, const ghoul::filesystem::File& file, + bool overrideFile, DownloadFinishedCallback finishedCallback, DownloadProgressCallback progressCallback) { + if (!overrideFile && FileSys.fileExists(file)) + return nullptr; + FileFuture* future = new FileFuture( file.filename() ); @@ -154,6 +158,7 @@ std::vector DownloadManager::downloadRequestFiles( const std::string& identifier, const ghoul::filesystem::Directory& destination, int version, + bool overrideFiles, DownloadFinishedCallback finishedCallback, DownloadProgressCallback progressCallback) { @@ -171,7 +176,7 @@ std::vector DownloadManager::downloadRequestFiles( LDEBUG("Request File: " << requestFile); bool isFinished = false; - auto callback = [&futures, destination, &progressCallback, &isFinished, requestFile](const FileFuture& f) { + auto callback = [&futures, destination, &progressCallback, &isFinished, requestFile, overrideFiles](const FileFuture& f) { LDEBUG("Finished: " << requestFile); std::ifstream temporary(requestFile); std::string line; @@ -185,9 +190,11 @@ std::vector DownloadManager::downloadRequestFiles( FileFuture* future = DlManager.downloadFile( line, - destination.path() + "/" + file + destination.path() + "/" + file, + overrideFiles ); - futures.push_back(future); + if (future) + futures.push_back(future); } isFinished = true; }; @@ -195,6 +202,7 @@ std::vector DownloadManager::downloadRequestFiles( FileFuture* f = downloadFile( fullRequest, requestFile, + true, callback ); From 2122369d92f3e70a936fb194544b0c85bc7d4601 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 16 Jun 2015 02:58:16 +0200 Subject: [PATCH 138/329] Enable to abort downloads --- apps/Launcher/mainwindow.cpp | 2 +- apps/Launcher/syncwidget.cpp | 17 ++++++++++++---- include/openspace/engine/downloadmanager.h | 8 ++++++++ src/engine/downloadmanager.cpp | 23 ++++++++++++++-------- 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/apps/Launcher/mainwindow.cpp b/apps/Launcher/mainwindow.cpp index 440f5725a7..f9462906f6 100644 --- a/apps/Launcher/mainwindow.cpp +++ b/apps/Launcher/mainwindow.cpp @@ -178,7 +178,7 @@ void MainWindow::initialize() { _syncWidget->setSceneFiles(_sceneFiles); _syncWidget->setModulesDirectory(ModulesDirectory); - ghoul::logging::LogManager::initialize(ghoul::logging::LogManager::LogLevel::Debug); + ghoul::logging::LogManager::initialize(ghoul::logging::LogManager::LogLevel::Error); LogMgr.addLog(new ghoul::logging::ConsoleLog); LogMgr.addLog(new QLog); } diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index a9b5fc615e..a64f9b2b3b 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -67,6 +67,8 @@ namespace { const std::string VersionKey = "Version"; const QString DefaultSceneName = "default.scene"; + + const bool OverwriteFiles = true; } SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) @@ -158,7 +160,11 @@ void SyncWidget::setModulesDirectory(QString modulesDirectory) { } void SyncWidget::clear() { - + for (openspace::DownloadManager::FileFuture* f : _futures) + f->abortDownload = true; + _directFiles.clear(); + _fileRequests.clear(); + _torrentFiles.clear(); } void SyncWidget::handleDirectFiles() { @@ -170,7 +176,7 @@ void SyncWidget::handleDirectFiles() { openspace::DownloadManager::FileFuture* future = DlManager.downloadFile( f.url.toStdString(), fullPath(f.module, f.destination).toStdString(), - false + OverwriteFiles ); if (future) { InfoWidget* w = new InfoWidget(f.destination); @@ -183,6 +189,7 @@ void SyncWidget::handleDirectFiles() { } void SyncWidget::handleFileRequest() { + return; qDebug() << "File Requests"; for (const FileRequest& f : _fileRequests) { qDebug() << f.identifier << " (" << f.version << ")" << " -> " << f.destination; @@ -195,7 +202,7 @@ void SyncWidget::handleFileRequest() { identifier, path, version, - false + OverwriteFiles ); _futures.insert(_futures.end(), futures.begin(), futures.end()); @@ -209,6 +216,7 @@ void SyncWidget::handleFileRequest() { } void SyncWidget::handleTorrentFiles() { + return; qDebug() << "Torrent Files"; for (const TorrentFile& f : _torrentFiles) { QString file = QString::fromStdString(absPath(fullPath(f.module, f.file).toStdString())); @@ -386,9 +394,10 @@ void SyncWidget::handleTimer() { for (FileFuture* f : _futures) { InfoWidget* w = _futureInfoWidgetMap[f]; - if (f->isFinished) { + if (f->isFinished || f->isAborted) { toRemove.push_back(f); _downloadLayout->removeWidget(w); + _futureInfoWidgetMap.erase(f); delete w; } else diff --git a/include/openspace/engine/downloadmanager.h b/include/openspace/engine/downloadmanager.h index 460f77581b..b17fc2ede5 100644 --- a/include/openspace/engine/downloadmanager.h +++ b/include/openspace/engine/downloadmanager.h @@ -39,13 +39,21 @@ namespace openspace { class DownloadManager : public ghoul::Singleton { public: struct FileFuture { + // Since the FileFuture object will be used from multiple threads, we have to be + // careful about the access pattern, that is, no values should be read and written + // by both the DownloadManager and the outside threads. FileFuture(std::string file); + // Values that are written by the DownloadManager to be consumed by others long long totalSize; float progress; // [0,1] bool isFinished; std::string filePath; std::string errorMessage; + bool isAborted; + + // Values set by others to be consumed by the DownloadManager + bool abortDownload; }; typedef std::function DownloadProgressCallback; diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp index 5a501faadb..5e6e06584a 100644 --- a/src/engine/downloadmanager.cpp +++ b/src/engine/downloadmanager.cpp @@ -69,6 +69,11 @@ namespace { ghoul_assert(i->curl, "CURL pointer is nullptr"); ghoul_assert(i->future, "FileFuture is not initialized"); ghoul_assert(i->callback, "Callback pointer is nullptr"); + + if (i->future->abortDownload) { + i->future->isAborted = true; + return 1; + } i->future->totalSize = dltotal; i->future->progress = static_cast(dlnow) / static_cast(dltotal); @@ -94,6 +99,16 @@ namespace { namespace openspace { +DownloadManager::FileFuture::FileFuture(std::string file) + : totalSize(-1) + , progress(0.f) + , isFinished(false) + , filePath(std::move(file)) + , errorMessage("") + , isAborted(false) + , abortDownload(false) +{} + DownloadManager::DownloadManager(std::string requestURL, int applicationVersion) : _requestURL(std::move(requestURL)) , _applicationVersion(std::move(applicationVersion)) @@ -211,12 +226,4 @@ std::vector DownloadManager::downloadRequestFiles( return futures; } -DownloadManager::FileFuture::FileFuture(std::string file) - : totalSize(-1) - , progress(0.f) - , isFinished(false) - , filePath(std::move(file)) - , errorMessage("") -{} - } // namespace openspace From ee85c237e6f7654d45099c32bd9f1b8f9b05f4ab Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 16 Jun 2015 03:05:27 +0200 Subject: [PATCH 139/329] Reenable downloading Correctly clear torrent downloads --- apps/Launcher/syncwidget.cpp | 14 ++++++++++++-- data | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index a64f9b2b3b..d4abf112fd 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -162,6 +162,18 @@ void SyncWidget::setModulesDirectory(QString modulesDirectory) { void SyncWidget::clear() { for (openspace::DownloadManager::FileFuture* f : _futures) f->abortDownload = true; + + using libtorrent::torrent_handle; + for (QMap::iterator i = _torrentInfoWidgetMap.begin(); + i != _torrentInfoWidgetMap.end(); + ++i) + { + delete i.value(); + } + _torrentInfoWidgetMap.clear(); + _session->abort(); + + _directFiles.clear(); _fileRequests.clear(); _torrentFiles.clear(); @@ -189,7 +201,6 @@ void SyncWidget::handleDirectFiles() { } void SyncWidget::handleFileRequest() { - return; qDebug() << "File Requests"; for (const FileRequest& f : _fileRequests) { qDebug() << f.identifier << " (" << f.version << ")" << " -> " << f.destination; @@ -216,7 +227,6 @@ void SyncWidget::handleFileRequest() { } void SyncWidget::handleTorrentFiles() { - return; qDebug() << "Torrent Files"; for (const TorrentFile& f : _torrentFiles) { QString file = QString::fromStdString(absPath(fullPath(f.module, f.file).toStdString())); diff --git a/data b/data index ef2707d9b9..dfc64c3319 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit ef2707d9b919683b5d62778792d7da29ab2ff7cd +Subproject commit dfc64c3319ff9108de5a0bce9cbcb4caf9ebddf1 From 52161cbe41f0c0674956097f1875210a52f62639 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Tue, 16 Jun 2015 08:04:38 +0200 Subject: [PATCH 140/329] changed default scene --- openspace.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openspace.cfg b/openspace.cfg index 7b156a7b68..ace1740b8f 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -7,7 +7,7 @@ return { -- Sets the scene that is to be loaded by OpenSpace. A scene file is a description -- of all entities that will be visible during an instance of OpenSpace - Scene = "${OPENSPACE_DATA}/scene/default_nh.scene", + Scene = "${OPENSPACE_DATA}/scene/default.scene", Paths = { SGCT = "${BASE_PATH}/config/sgct", From 57fb1a1dd49d353e421a1e59115e42329df7b8c6 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Tue, 16 Jun 2015 08:20:25 +0200 Subject: [PATCH 141/329] changed default scene from NH to standard scene and commented out a printf. --- openspace.cfg | 2 +- src/interaction/remotecontroller.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openspace.cfg b/openspace.cfg index 7b156a7b68..ace1740b8f 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -7,7 +7,7 @@ return { -- Sets the scene that is to be loaded by OpenSpace. A scene file is a description -- of all entities that will be visible during an instance of OpenSpace - Scene = "${OPENSPACE_DATA}/scene/default_nh.scene", + Scene = "${OPENSPACE_DATA}/scene/default.scene", Paths = { SGCT = "${BASE_PATH}/config/sgct", diff --git a/src/interaction/remotecontroller.cpp b/src/interaction/remotecontroller.cpp index 7d770ebb72..6417a8226b 100644 --- a/src/interaction/remotecontroller.cpp +++ b/src/interaction/remotecontroller.cpp @@ -52,7 +52,7 @@ namespace interaction { std::string write = std::to_string(kf._position.vec4().x) + "\t" + std::to_string(kf._position.vec4().y) + "\t" + std::to_string(kf._position.vec4().z) + "\t" + std::to_string(kf._position.vec4().w) + "\n"; //write += - printf("%s", write.c_str()); + //printf("%s", write.c_str()); } void RemoteController::sendKeyFrame(){ From 63cce6226ea70a562688984db5cc012ef492ad38 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Tue, 16 Jun 2015 11:28:34 +0200 Subject: [PATCH 142/329] Removed redefinition of BOOST_ROOT from cmakelists --- modules/kameleon/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/kameleon/CMakeLists.txt b/modules/kameleon/CMakeLists.txt index 724e7a40a9..edc593869b 100644 --- a/modules/kameleon/CMakeLists.txt +++ b/modules/kameleon/CMakeLists.txt @@ -49,8 +49,7 @@ create_new_module( mark_as_advanced(BUILD_SHARED_LIBS) # Change to set instead of option option(KAMELEON_USE_HDF5 "Kameleon use HDF5" OFF) set(KAMELEON_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/kameleon) - set(KAMELEON_INCLUDES ${KAMELEON_ROOT_DIR}/src) - set(BOOST_ROOT "${OPENSPACE_EXT_DIR}/ghoul/ext/boost") + set(KAMELEON_INCLUDES ${KAMELEON_ROOT_DIR}/src) add_subdirectory(${KAMELEON_ROOT_DIR}) target_include_directories(${kameleon_module} SYSTEM PUBLIC ${KAMELEON_INCLUDES}) target_link_libraries(${kameleon_module} ccmc) From e156e6f21aa6195289f8eb8af1506cff82cc2135 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Tue, 16 Jun 2015 11:31:34 +0200 Subject: [PATCH 143/329] Removed redefinition of BOOST_ROOT --- modules/kameleon/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/kameleon/CMakeLists.txt b/modules/kameleon/CMakeLists.txt index 724e7a40a9..0e95aabdc5 100644 --- a/modules/kameleon/CMakeLists.txt +++ b/modules/kameleon/CMakeLists.txt @@ -50,7 +50,6 @@ create_new_module( option(KAMELEON_USE_HDF5 "Kameleon use HDF5" OFF) set(KAMELEON_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/kameleon) set(KAMELEON_INCLUDES ${KAMELEON_ROOT_DIR}/src) - set(BOOST_ROOT "${OPENSPACE_EXT_DIR}/ghoul/ext/boost") add_subdirectory(${KAMELEON_ROOT_DIR}) target_include_directories(${kameleon_module} SYSTEM PUBLIC ${KAMELEON_INCLUDES}) target_link_libraries(${kameleon_module} ccmc) From 2d3ef442fb5a7fceee70755ed327ab1f58e69eeb Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Tue, 16 Jun 2015 13:56:34 +0200 Subject: [PATCH 144/329] adding parallelconnection --- include/openspace/engine/openspaceengine.h | 6 + .../openspace/network/osparallelconnection.h | 126 ++++++++++++ src/CMakeLists.txt | 3 + src/engine/openspaceengine.cpp | 9 + src/network/osparallelconnection.cpp | 180 ++++++++++++++++++ src/network/osparallelconnection_lua.inl | 55 ++++++ 6 files changed, 379 insertions(+) create mode 100644 include/openspace/network/osparallelconnection.h create mode 100644 src/network/osparallelconnection.cpp create mode 100644 src/network/osparallelconnection_lua.inl diff --git a/include/openspace/engine/openspaceengine.h b/include/openspace/engine/openspaceengine.h index 08a25948d9..c21d6d59a2 100644 --- a/include/openspace/engine/openspaceengine.h +++ b/include/openspace/engine/openspaceengine.h @@ -56,6 +56,10 @@ namespace gui { namespace scripting { class ScriptEngine; } + +namespace network{ + class OSParallelConnection; +} class OpenSpaceEngine { public: @@ -77,6 +81,7 @@ public: NetworkEngine* networkEngine(); LuaConsole* console(); ModuleEngine* moduleEngine(); + network::OSParallelConnection* parallelConnection(); gui::GUI* gui(); @@ -124,6 +129,7 @@ private: LuaConsole* _console; ModuleEngine* _moduleEngine; gui::GUI* _gui; + network::OSParallelConnection* _parallelConnection; bool _isMaster; SyncBuffer* _syncBuffer; diff --git a/include/openspace/network/osparallelconnection.h b/include/openspace/network/osparallelconnection.h new file mode 100644 index 0000000000..db8a4afdc8 --- /dev/null +++ b/include/openspace/network/osparallelconnection.h @@ -0,0 +1,126 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OSPARALLELCONNECTION_H__ +#define __OSPARALLELCONNECTION_H__ + +//openspace includes +#include +#include +#include + +//std includes +#include +#include +#include +#include + +#ifdef __WIN32__ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#include +#endif + +#if defined(__WIN32__) || defined(__MING32__) || defined(__MING64__) +typedef size_t _SOCKET; +#else +typedef int _SOCKET; +#include +#endif + +namespace openspace{ + + namespace network{ + + class OSParallelConnection : public properties::PropertyOwner { + public: + + OSParallelConnection(); + + ~OSParallelConnection(); + + void connect(); + + void setPort(const std::string &port); + + std::string port(); + + void setAddress(const std::string &address); + + std::string address(); + + void setName(const std::string& name); + + std::string name(); + + void setSocket(_SOCKET socket); + + _SOCKET socket(); + + void setHost(bool host); + + bool isHost(); + + bool isRunning(); + + void requestHostship(); + + enum MessageTypes{ + Authentication=0, + Initialization, + Data, + HostInfo, + InitRequest + }; + + /** + * Returns the Lua library that contains all Lua functions available to affect the + * remote OS parallel connection. The functions contained are + * - + * \return The Lua library that contains all Lua functions available to affect the + * interaction + */ + static scripting::ScriptEngine::LuaLibrary luaLibrary(); + + protected: + + private: + uint32_t _passCode; + std::string _password; + std::string _port; + std::string _address; + std::string _name; + _SOCKET _clientSocket; + std::thread *_thread; + std::atomic _isRunning; + std::atomic _isHost; + }; + } // namespace network + +} // namespace openspace + +#endif // __OSPARALLELCONNECTION_H__ \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5842a16686..22c2bf60b7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -50,6 +50,8 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/interaction/externalcontrol/pythonexternalcontrol.cpp ${OPENSPACE_BASE_DIR}/src/interaction/externalcontrol/randomexternalcontrol.cpp ${OPENSPACE_BASE_DIR}/src/network/networkengine.cpp + ${OPENSPACE_BASE_DIR}/src/network/osparallelconnection.cpp + ${OPENSPACE_BASE_DIR}/src/network/osparallelconnection_lua.inl ${OPENSPACE_BASE_DIR}/src/properties/matrixproperty.cpp ${OPENSPACE_BASE_DIR}/src/properties/optionproperty.cpp ${OPENSPACE_BASE_DIR}/src/properties/property.cpp @@ -111,6 +113,7 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/interaction/externalcontrol/pythonexternalcontrol.h ${OPENSPACE_BASE_DIR}/include/openspace/interaction/externalcontrol/randomexternalcontrol.h ${OPENSPACE_BASE_DIR}/include/openspace/network/networkengine.h + ${OPENSPACE_BASE_DIR}/include/openspace/network/osparallelconnection.h ${OPENSPACE_BASE_DIR}/include/openspace/properties/matrixproperty.h ${OPENSPACE_BASE_DIR}/include/openspace/properties/numericalproperty.h ${OPENSPACE_BASE_DIR}/include/openspace/properties/numericalproperty.inl diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index f22143dd57..ba6c172af3 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -110,6 +111,7 @@ OpenSpaceEngine::OpenSpaceEngine(std::string programName) , _gui(new gui::GUI) , _isMaster(false) , _syncBuffer(nullptr) + , _parallelConnection(new network::OSParallelConnection) { FactoryManager::initialize(); SpiceManager::initialize(); @@ -129,6 +131,7 @@ OpenSpaceEngine::~OpenSpaceEngine() { delete _console; delete _moduleEngine; delete _gui; + delete _parallelConnection; if(_syncBuffer) delete _syncBuffer; @@ -302,6 +305,7 @@ bool OpenSpaceEngine::initialize() { _scriptEngine->addLibrary(interaction::InteractionHandler::luaLibrary()); _scriptEngine->addLibrary(LuaConsole::luaLibrary()); _scriptEngine->addLibrary(gui::GUI::luaLibrary()); + _scriptEngine->addLibrary(network::OSParallelConnection::luaLibrary()); // TODO: Maybe move all scenegraph and renderengine stuff to initializeGL scriptEngine()->initialize(); @@ -758,5 +762,10 @@ NetworkEngine* OpenSpaceEngine::networkEngine() { ModuleEngine* OpenSpaceEngine::moduleEngine() { return _moduleEngine; } + +network::OSParallelConnection* OpenSpaceEngine::parallelConnection() { + ghoul_assert(_parallelConnection != nullptr, "ParallelConnection is nullptr"); + return _parallelConnection; +} } // namespace openspace diff --git a/src/network/osparallelconnection.cpp b/src/network/osparallelconnection.cpp new file mode 100644 index 0000000000..7285dc74da --- /dev/null +++ b/src/network/osparallelconnection.cpp @@ -0,0 +1,180 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include +#include + +#include "osparallelconnection_lua.inl" + +namespace openspace { + namespace network{ + + OSParallelConnection::OSParallelConnection(): + _passCode(0), + _port(""), + _address(""), + _name("No name"), + _password(""), + _clientSocket(0), + _thread(nullptr), + _isRunning(false), + _isHost(false) + { + + } + + OSParallelConnection::~OSParallelConnection(){ + + } + + void OSParallelConnection::connect(){ + + } + + void OSParallelConnection::setPort(const std::string &port){ + _port = port; + } + + std::string OSParallelConnection::port(){ + return _port; + } + + void OSParallelConnection::setAddress(const std::string &address){ + _address = address; + } + + std::string OSParallelConnection::address(){ + return _address; + } + + void OSParallelConnection::setName(const std::string& name){ + _name = name; + } + + std::string OSParallelConnection::name(){ + return _name; + } + + void OSParallelConnection::setSocket(_SOCKET socket){ + _clientSocket = socket; + } + + _SOCKET OSParallelConnection::socket(){ + return _clientSocket; + } + + void OSParallelConnection::setHost(bool host){ + _isHost.store(host); + } + + bool OSParallelConnection::isHost(){ + return _isHost.load(); + } + + bool OSParallelConnection::isRunning(){ + return _isRunning.load(); + } + + void OSParallelConnection::requestHostship(){ + + } + + scripting::ScriptEngine::LuaLibrary OSParallelConnection::luaLibrary() { + return { + "", + { + { + "setPort", + &luascriptfunctions::setPort, + "string", + "Set the port for the parallel connection" + }, +// { +// "clearKeys", +// &luascriptfunctions::clearKeys, +// "", +// "Clear all key bindings" +// }, +// { +// "bindKey", +// &luascriptfunctions::bindKey, +// "string, string", +// "Binds a key by name to a lua string command" +// }, +// { +// "dt", +// &luascriptfunctions::dt, +// "", +// "Get current frame time" +// }, +// { +// "distance", +// &luascriptfunctions::distance, +// "number", +// "Change distance to origin" +// }, +// { +// "setInteractionSensitivity", +// &luascriptfunctions::setInteractionSensitivity, +// "number", +// "Sets the global interaction sensitivity" +// }, +// { +// "interactionSensitivity", +// &luascriptfunctions::interactionSensitivity, +// "", +// "Gets the current global interaction sensitivity" +// }, +// { +// "setInvertRoll", +// &luascriptfunctions::setInvertRoll, +// "bool", +// "Sets the setting if roll movements are inverted" +// }, +// { +// "invertRoll", +// &luascriptfunctions::invertRoll, +// "", +// "Returns the status of roll movement inversion" +// }, +// { +// "setInvertRotation", +// &luascriptfunctions::setInvertRotation, +// "bool", +// "Sets the setting if rotation movements are inverted" +// }, +// { +// "invertRotation", +// &luascriptfunctions::invertRotation, +// "", +// "Returns the status of rotation movement inversion" +// } + + } + }; + } + + } // namespace network + +} // namespace openspace diff --git a/src/network/osparallelconnection_lua.inl b/src/network/osparallelconnection_lua.inl new file mode 100644 index 0000000000..d7ddd4d103 --- /dev/null +++ b/src/network/osparallelconnection_lua.inl @@ -0,0 +1,55 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * 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. * + ****************************************************************************************/ + +namespace openspace { + +namespace luascriptfunctions { + +/** + * \ingroup LuaScripts + * setPort(): + * Set the port for parallel connection + */ +int setPort(lua_State* L) { + using ghoul::lua::luaTypeToString; + const std::string _loggerCat = "lua.setPort"; + + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + + const int type = lua_type(L, -1); + if (type != LUA_TSTRING) + return luaL_error(L, "Expected string, got %i", type); + + std::string s = luaL_checkstring(L, -1); + + OsEng.parallelConnection()->setPort(s); + + return 0; +} + +} // namespace luascriptfunctions + +} // namespace openspace From 8f05d643167ead29079fcc1431a21945debf0667 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Tue, 16 Jun 2015 15:45:00 +0200 Subject: [PATCH 145/329] added lua scripting functionality and fixed authentication to server --- .../openspace/network/osparallelconnection.h | 41 +- src/network/osparallelconnection.cpp | 394 ++++++++++++++---- src/network/osparallelconnection_lua.inl | 109 ++++- 3 files changed, 455 insertions(+), 89 deletions(-) diff --git a/include/openspace/network/osparallelconnection.h b/include/openspace/network/osparallelconnection.h index db8a4afdc8..0721ef96fd 100644 --- a/include/openspace/network/osparallelconnection.h +++ b/include/openspace/network/osparallelconnection.h @@ -63,7 +63,7 @@ namespace openspace{ ~OSParallelConnection(); - void connect(); + void clientConnect(); void setPort(const std::string &port); @@ -79,7 +79,7 @@ namespace openspace{ void setSocket(_SOCKET socket); - _SOCKET socket(); + _SOCKET clientSocket(); void setHost(bool host); @@ -88,6 +88,8 @@ namespace openspace{ bool isRunning(); void requestHostship(); + + void setPassword(const std::string &password); enum MessageTypes{ Authentication=0, @@ -109,8 +111,39 @@ namespace openspace{ protected: private: - uint32_t _passCode; - std::string _password; + //@TODO change this into the ghoul hasher for client AND server + uint32_t hash(const std::string &val){ + uint32_t hashVal = 0, i; + size_t len = val.length(); + + for (hashVal = i = 0; i < len; ++i){ + hashVal += val.c_str()[i]; + hashVal += (hashVal << 10); + hashVal ^= (hashVal >> 6); + } + + hashVal += (hashVal << 3); + hashVal ^= (hashVal >> 11); + hashVal += (hashVal << 15); + + return hashVal; + }; + + void disconnect(); + + void closeSocket(); + + bool initNetworkAPI(); + + void connection(addrinfo *info); + + void authenticate(); + + void communicate(); + + int receiveData(_SOCKET & socket, std::vector &buffer, int length, int flags); + + uint32_t _passCode; std::string _port; std::string _address; std::string _name; diff --git a/src/network/osparallelconnection.cpp b/src/network/osparallelconnection.cpp index 7285dc74da..8376a75cd0 100644 --- a/src/network/osparallelconnection.cpp +++ b/src/network/osparallelconnection.cpp @@ -22,21 +22,59 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ +//@TODO CHANGE THIS! +const int headerSize = 8; + +#ifdef __WIN32__ +#ifndef _ERRNO +#define _ERRNO WSAGetLastError() +#endif +#else //Use BSD sockets +#ifdef _XCODE +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#ifndef SOCKET_ERROR +#define SOCKET_ERROR (-1) +#endif + +#ifndef INVALID_SOCKET +#define INVALID_SOCKET (_SOCKET)(~0) +#endif + +#ifndef NO_ERROR +#define NO_ERROR 0L +#endif + +#ifndef _ERRNO +#define _ERRNO errno +#endif +#endif + #include #include #include "osparallelconnection_lua.inl" +namespace{ + const std::string _loggerCat = "Parallel"; +} + namespace openspace { namespace network{ OSParallelConnection::OSParallelConnection(): _passCode(0), - _port(""), - _address(""), + _port("20501"), + _address("127.0.0.1"), _name("No name"), - _password(""), - _clientSocket(0), + _clientSocket(INVALID_SOCKET), _thread(nullptr), _isRunning(false), _isHost(false) @@ -45,12 +83,195 @@ namespace openspace { } OSParallelConnection::~OSParallelConnection(){ - + _isRunning.store(false); + + disconnect(); + + #if defined(__WIN32__) + WSACleanup(); + #endif } - void OSParallelConnection::connect(){ - + void OSParallelConnection::clientConnect(){ + if (!initNetworkAPI()){ + //error, handle this + } + + struct addrinfo *addresult = NULL, *ptr = NULL, hints; + #ifdef __WIN32__ //WinSock + ZeroMemory(&hints, sizeof(hints)); + #else + memset(&hints, 0, sizeof(hints)); + #endif + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_PASSIVE; + + int result; + + // Resolve the local address and port to be used by the server + result = getaddrinfo(_address.c_str(), _port.c_str(), &hints, &addresult); + if (result != 0) + { + #if defined(__WIN32__) + WSACleanup(); + #endif + std::cerr << "Failed to parse hints for connection!" << std::endl; + } + + // Attempt to connect to the first address returned by + // the call to getaddrinfo + ptr = addresult; + + std::cout << "Client started on port " << _port << std::endl; + + //start accept connections thread + _isRunning.store(true); + _thread = new (std::nothrow) std::thread(&OSParallelConnection::connection, this, addresult); + } + + void OSParallelConnection::connection(addrinfo *info){ + int result; + + while (_isRunning.load()){ + _clientSocket = socket(info->ai_family, info->ai_socktype, info->ai_protocol); + + if (_clientSocket == INVALID_SOCKET){ + freeaddrinfo(info); + #if defined(__WIN32__) + WSACleanup(); + #endif + std::cerr << "Failed to init client socket!" << std::endl; + return; + } + + int flag = 1; + int result; + + //set send timeout + int timeout = 0; //infinite + result = setsockopt( + _clientSocket, + SOL_SOCKET, + SO_SNDTIMEO, + (char *)&timeout, + sizeof(timeout)); + + //set receive timeout + result = setsockopt( + _clientSocket, + SOL_SOCKET, + SO_RCVTIMEO, + (char *)&timeout, + sizeof(timeout)); + + result = setsockopt(_clientSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&flag, sizeof(int)); + if (result == SOCKET_ERROR) + std::cout << "Failed to set reuse address with error:" << _ERRNO << std::endl; + + result = setsockopt(_clientSocket, SOL_SOCKET, SO_KEEPALIVE, (char*)&flag, sizeof(int)); + if (result == SOCKET_ERROR) + std::cout << "Failed to set keep alive with error: " << _ERRNO << std::endl; + + result = connect(_clientSocket, info->ai_addr, (int)info->ai_addrlen); + if (result != SOCKET_ERROR) + { + //send authentication + authenticate(); + + //start listening for communication + communicate(); + } + + //one sec sleep + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + //cleanup + freeaddrinfo(info); + } + + void OSParallelConnection::authenticate(){ + int pos = 4; + uint16_t namelen = static_cast(_name.length()); + int size = headerSize + sizeof(uint32_t) + sizeof(uint16_t) + static_cast(namelen); + std::vector buffer; + buffer.reserve(size); + + //version + buffer.insert(buffer.end(), 'O'); + buffer.insert(buffer.end(), 'S'); + buffer.insert(buffer.end(), 0); + buffer.insert(buffer.end(), 0); + + //msg type, 0 = auth + int type = 0; + buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(int)); + + //passcode + buffer.insert(buffer.end(), reinterpret_cast(&_passCode), reinterpret_cast(&_passCode) + sizeof(uint32_t)); + + //name length + buffer.insert(buffer.end(), reinterpret_cast(&namelen), reinterpret_cast(&namelen) + sizeof(uint16_t)); + + //name + buffer.insert(buffer.end(), _name.begin(), _name.end()); + + int result = send(_clientSocket, buffer.data(), size, 0); + + if (result == SOCKET_ERROR){ + //failed to send auth msg. + std::cerr << "Failed to send authentication message!" << std::endl; + } + } + + void OSParallelConnection::communicate(){ + + std::vector buffer; + buffer.resize(8); + int result; + + while (_isRunning.load()){ + result = receiveData(_clientSocket, buffer, headerSize, 0); + + if (result > 0){ + int i = 0; + } + else{ + if (result == 0){ + //connection rejected + _isRunning.store(false); + } + else{ + std::cerr << "Error " << _ERRNO << " detected in connection!" << std::endl; + } + break; + } + } + + } + + int OSParallelConnection::receiveData(_SOCKET & socket, std::vector &buffer, int length, int flags){ + int result = 0; + int received = 0; + while (result < length){ + received = recv(socket, buffer.data() + result, length - result, flags); + + if (received > 0){ + result += received; + received = 0; + } + else{ + //error receiving + result = received; + break; + } + } + + return result; + } void OSParallelConnection::setPort(const std::string &port){ _port = port; @@ -80,7 +301,7 @@ namespace openspace { _clientSocket = socket; } - _SOCKET OSParallelConnection::socket(){ + _SOCKET OSParallelConnection::clientSocket(){ return _clientSocket; } @@ -99,78 +320,107 @@ namespace openspace { void OSParallelConnection::requestHostship(){ } - + + void OSParallelConnection::setPassword(const std::string& pwd){ + _passCode = hash(pwd); + } + + void OSParallelConnection::disconnect(){ + closeSocket(); + + if (_thread != nullptr){ + _thread->join(); + delete _thread; + _thread = nullptr; + } + } + + void OSParallelConnection::closeSocket(){ + if (_clientSocket != INVALID_SOCKET) + { + /* + Windows shutdown options + * SD_RECIEVE + * SD_SEND + * SD_BOTH + + Linux & Mac shutdown options + * SHUT_RD (Disables further receive operations) + * SHUT_WR (Disables further send operations) + * SHUT_RDWR (Disables further send and receive operations) + */ + + #ifdef __WIN32__ + shutdown(_clientSocket, SD_BOTH); + closesocket(_clientSocket); + #else + shutdown(_clientSocket, SHUT_RDWR); + #endif + + _clientSocket = INVALID_SOCKET; + } + } + + bool OSParallelConnection::initNetworkAPI(){ + #if defined(__WIN32__) + WSADATA wsaData; + WORD version; + int error; + + version = MAKEWORD(2, 2); + + error = WSAStartup(version, &wsaData); + + if (error != 0 || + LOBYTE(wsaData.wVersion) != 2 || + HIBYTE(wsaData.wVersion) != 2) + { + /* incorrect WinSock version */ + std::cerr << "Failed to init winsock API!" << std::endl; + WSACleanup(); + return false; + } + #else + //No init needed on unix + #endif + + return true; + } + scripting::ScriptEngine::LuaLibrary OSParallelConnection::luaLibrary() { return { - "", + "parallel", { { "setPort", &luascriptfunctions::setPort, - "string", + "number", "Set the port for the parallel connection" }, -// { -// "clearKeys", -// &luascriptfunctions::clearKeys, -// "", -// "Clear all key bindings" -// }, -// { -// "bindKey", -// &luascriptfunctions::bindKey, -// "string, string", -// "Binds a key by name to a lua string command" -// }, -// { -// "dt", -// &luascriptfunctions::dt, -// "", -// "Get current frame time" -// }, -// { -// "distance", -// &luascriptfunctions::distance, -// "number", -// "Change distance to origin" -// }, -// { -// "setInteractionSensitivity", -// &luascriptfunctions::setInteractionSensitivity, -// "number", -// "Sets the global interaction sensitivity" -// }, -// { -// "interactionSensitivity", -// &luascriptfunctions::interactionSensitivity, -// "", -// "Gets the current global interaction sensitivity" -// }, -// { -// "setInvertRoll", -// &luascriptfunctions::setInvertRoll, -// "bool", -// "Sets the setting if roll movements are inverted" -// }, -// { -// "invertRoll", -// &luascriptfunctions::invertRoll, -// "", -// "Returns the status of roll movement inversion" -// }, -// { -// "setInvertRotation", -// &luascriptfunctions::setInvertRotation, -// "bool", -// "Sets the setting if rotation movements are inverted" -// }, -// { -// "invertRotation", -// &luascriptfunctions::invertRotation, -// "", -// "Returns the status of rotation movement inversion" -// } - + { + "setAddress", + &luascriptfunctions::setAddress, + "string", + "Set the address for the parallel connection" + }, + { + "setPassword", + &luascriptfunctions::setPassword, + "string", + "Set the password for the parallel connection" + }, + { + "setDisplayName", + &luascriptfunctions::setDisplayName, + "string", + "Set your display name for the parallel connection" + }, + { + "connect", + &luascriptfunctions::connect, + "", + "Connect to parallel" + }, } }; } diff --git a/src/network/osparallelconnection_lua.inl b/src/network/osparallelconnection_lua.inl index d7ddd4d103..a247ffa536 100644 --- a/src/network/osparallelconnection_lua.inl +++ b/src/network/osparallelconnection_lua.inl @@ -32,24 +32,107 @@ namespace luascriptfunctions { * Set the port for parallel connection */ int setPort(lua_State* L) { - using ghoul::lua::luaTypeToString; - const std::string _loggerCat = "lua.setPort"; + const bool isFunction = (lua_isfunction(L, -1) != 0); + if (isFunction) { + // If the top of the stack is a function, it is ourself + const char* msg = lua_pushfstring(L, "method called without argument"); + return luaL_error(L, "bad argument (%s)", msg); + } - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - const int type = lua_type(L, -1); - if (type != LUA_TSTRING) - return luaL_error(L, "Expected string, got %i", type); - - std::string s = luaL_checkstring(L, -1); - - OsEng.parallelConnection()->setPort(s); + const bool isNumber = (lua_isnumber(L, -1) != 0); + if (isNumber) { + int value = lua_tonumber(L, -1); + std::string port = std::to_string(value); + OsEng.parallelConnection()->setPort(port); + return 0; + } + else { + const char* msg = lua_pushfstring(L, "%s expected, got %s", + lua_typename(L, LUA_TNUMBER), luaL_typename(L, -1)); + return luaL_error(L, "bad argument #%d (%s)", 1, msg); + } return 0; } +int setAddress(lua_State* L) { + const bool isFunction = (lua_isfunction(L, -1) != 0); + if (isFunction) { + // If the top of the stack is a function, it is ourself + const char* msg = lua_pushfstring(L, "method called without argument"); + return luaL_error(L, "bad argument (%s)", msg); + } + + const int type = lua_type(L, -1); + if (type == LUA_TSTRING) { + std::string address = luaL_checkstring(L, -1); + OsEng.parallelConnection()->setAddress(address); + return 0; + } + else { + const char* msg = lua_pushfstring(L, "%s expected, got %s", + lua_typename(L, LUA_TSTRING), luaL_typename(L, -1)); + return luaL_error(L, "bad argument #%d (%s)", 1, msg); + } + + return 0; +} + +int setPassword(lua_State* L) { + const bool isFunction = (lua_isfunction(L, -1) != 0); + if (isFunction) { + // If the top of the stack is a function, it is ourself + const char* msg = lua_pushfstring(L, "method called without argument"); + return luaL_error(L, "bad argument (%s)", msg); + } + + const int type = lua_type(L, -1); + if (type == LUA_TSTRING) { + std::string pwd = luaL_checkstring(L, -1); + OsEng.parallelConnection()->setPassword(pwd); + return 0; + } + else { + const char* msg = lua_pushfstring(L, "%s expected, got %s", + lua_typename(L, LUA_TSTRING), luaL_typename(L, -1)); + return luaL_error(L, "bad argument #%d (%s)", 1, msg); + } + + return 0; +} + +int setDisplayName(lua_State* L) { + const bool isFunction = (lua_isfunction(L, -1) != 0); + if (isFunction) { + // If the top of the stack is a function, it is ourself + const char* msg = lua_pushfstring(L, "method called without argument"); + return luaL_error(L, "bad argument (%s)", msg); + } + + const int type = lua_type(L, -1); + if (type == LUA_TSTRING) { + std::string name = luaL_checkstring(L, -1); + OsEng.parallelConnection()->setName(name); + return 0; + } + else { + const char* msg = lua_pushfstring(L, "%s expected, got %s", + lua_typename(L, LUA_TSTRING), luaL_typename(L, -1)); + return luaL_error(L, "bad argument #%d (%s)", 1, msg); + } + + return 0; +} + +int connect(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 0) + return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); + OsEng.parallelConnection()->clientConnect(); + return 0; +} + + } // namespace luascriptfunctions } // namespace openspace From 8da76f6d1e3eb6393cfe53c75f96e0cb93285cd2 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Tue, 16 Jun 2015 16:37:07 +0200 Subject: [PATCH 146/329] added message specific decoding functions --- .../openspace/network/osparallelconnection.h | 14 +++- src/network/osparallelconnection.cpp | 76 ++++++++++++++++++- 2 files changed, 87 insertions(+), 3 deletions(-) diff --git a/include/openspace/network/osparallelconnection.h b/include/openspace/network/osparallelconnection.h index 0721ef96fd..d6a8b101b5 100644 --- a/include/openspace/network/osparallelconnection.h +++ b/include/openspace/network/osparallelconnection.h @@ -96,7 +96,7 @@ namespace openspace{ Initialization, Data, HostInfo, - InitRequest + InitializationRequest }; /** @@ -141,6 +141,18 @@ namespace openspace{ void communicate(); + void delegateDecoding(int type); + + void decodeAuthenticationMessage(); + + void decodeInitializationMessage(); + + void decodeDataMessage(); + + void decodeHostInfoMessage(); + + void decodeInitializationRequestMessage(); + int receiveData(_SOCKET & socket, std::vector &buffer, int length, int flags); uint32_t _passCode; diff --git a/src/network/osparallelconnection.cpp b/src/network/osparallelconnection.cpp index 8376a75cd0..6370581967 100644 --- a/src/network/osparallelconnection.cpp +++ b/src/network/osparallelconnection.cpp @@ -59,6 +59,8 @@ const int headerSize = 8; #include #include +#include +#include #include "osparallelconnection_lua.inl" @@ -207,7 +209,7 @@ namespace openspace { buffer.insert(buffer.end(), 0); //msg type, 0 = auth - int type = 0; + int type = MessageTypes::Authentication; buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(int)); //passcode @@ -227,6 +229,67 @@ namespace openspace { } } + void OSParallelConnection::delegateDecoding(int type){ + switch (type){ + case MessageTypes::Authentication: + decodeAuthenticationMessage(); + break; + case MessageTypes::Initialization: + decodeInitializationMessage(); + break; + case MessageTypes::Data: + decodeDataMessage(); + break; + case MessageTypes::HostInfo: + decodeHostInfoMessage(); + break; + case MessageTypes::InitializationRequest: + decodeInitializationRequestMessage(); + break; + default: + //unknown message type + break; + } + } + + void OSParallelConnection::decodeAuthenticationMessage(){ + printf("Auth OK!\n"); //more stuff here later + } + + void OSParallelConnection::decodeInitializationMessage(){ + printf("Init message received!\n"); + } + + void OSParallelConnection::decodeDataMessage(){ + printf("Data message received!\n"); + } + + void OSParallelConnection::decodeHostInfoMessage(){ + std::vector hostflag; + hostflag.resize(1); + int result = receiveData(_clientSocket, hostflag, 1, 0); + + if (result > 0){ + if (hostflag.at(0) == 1){ + printf("IM MASTER!\n"); + _isHost.store(true); + } + else{ + printf("IM A SLAVE!\n"); + _isHost.store(false); + } + + } + else{ + std::cerr << "Error " << _ERRNO << " detected in connection!" << std::endl; + disconnect(); + } + } + + void OSParallelConnection::decodeInitializationRequestMessage(){ + printf("InitRequest message received!\n"); + } + void OSParallelConnection::communicate(){ std::vector buffer; @@ -237,7 +300,16 @@ namespace openspace { result = receiveData(_clientSocket, buffer, headerSize, 0); if (result > 0){ - int i = 0; + if (buffer[0] == 'O' && //Open + buffer[1] == 'S' && //Space + buffer[2] == 0 && //version + buffer[3] == 0 //version + ) + { + //parse type + int type = (*(reinterpret_cast(&buffer[4]))); + delegateDecoding(type); + } } else{ if (result == 0){ From 77b3b958305ee7a087c17f18476fafc2766f705e Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 16 Jun 2015 20:33:40 +0200 Subject: [PATCH 147/329] Keep the Launcher responsible during downloading Remove duplicate downloads --- apps/Launcher/syncwidget.cpp | 122 +++++++++++++++++++++++++++++------ 1 file changed, 102 insertions(+), 20 deletions(-) diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index d4abf112fd..f6a81ed30b 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -223,6 +223,8 @@ void SyncWidget::handleFileRequest() { _futureInfoWidgetMap[f] = w; } + + qApp->processEvents(); } } @@ -259,6 +261,8 @@ void SyncWidget::handleTorrentFiles() { InfoWidget* w = new InfoWidget(f.file, h.status().total_wanted); _downloadLayout->addWidget(w); _torrentInfoWidgetMap[h] = w; + + qApp->processEvents(); } } @@ -359,18 +363,75 @@ void SyncWidget::syncButtonPressed() { } //// Make the lists unique - //{ - // QSet s = _directFiles.toSet(); - // _directFiles = QList::fromSet(s); - //} - //{ - // QSet s = _fileRequests.toSet(); - // _fileRequests = QList::fromSet(s); - //} - //{ - // QSet s = _torrentFiles.toSet(); - // _torrentFiles = QList::fromSet(s); - //} + { + auto equal = [](const DirectFile& lhs, const DirectFile& rhs) -> bool { + return lhs.module == rhs.module && lhs.url == rhs.url && lhs.destination == rhs.destination; + }; + + QList files; + for (const DirectFile& f : _directFiles) { + bool found = false; + for (const DirectFile& g : files) { + if (equal(g, f)) { + found = true; + break; + } + } + + if (!found) + files.append(f); + } + + _directFiles = files; + } + { + auto equal = [](const FileRequest& lhs, const FileRequest& rhs) -> bool { + return + lhs.module == rhs.module && + lhs.identifier == rhs.identifier && + lhs.destination == rhs.destination && + lhs.version == rhs.version; + }; + + QList files; + for (const FileRequest& f : _fileRequests) { + bool found = false; + for (const FileRequest& g : files) { + if (equal(g, f)) { + found = true; + break; + } + } + + if (!found) + files.append(f); + } + + _fileRequests = files; + } + { + auto equal = [](const TorrentFile& lhs, const TorrentFile& rhs) -> bool { + return + lhs.module == rhs.module && + lhs.file == rhs.file; + }; + + QList files; + for (const TorrentFile& f : _torrentFiles) { + bool found = false; + for (const TorrentFile& g : files) { + if (equal(g, f)) { + found = true; + break; + } + } + + if (!found) + files.append(f); + } + + _torrentFiles = files; + } handleDirectFiles(); handleFileRequest(); @@ -417,6 +478,35 @@ void SyncWidget::handleTimer() { for (FileFuture* f : toRemove) _futures.erase(std::remove(_futures.begin(), _futures.end(), f), _futures.end()); + std::vector handles = _session->get_torrents(); + for (torrent_handle h : handles) { + torrent_status s = h.status(); + InfoWidget* w = _torrentInfoWidgetMap[h]; + + w->update(static_cast(s.total_wanted_done)); + + if (s.state == torrent_status::finished || s.state == torrent_status::seeding) { + //_session->remove_torrent(h); + _torrentInfoWidgetMap.remove(h); + delete w; + } + } + + // Only close every torrent if all torrents are finished + bool allSeeding = true; + for (torrent_handle h : handles) { + torrent_status s = h.status(); + allSeeding &= (s.state == torrent_status::seeding); + } + + if (allSeeding) { + for (torrent_handle h : handles) + _session->remove_torrent(h); + } + + + + //_session->post_torrent_updates(); //libtorrent::session_settings settings = _session->settings(); @@ -443,13 +533,6 @@ void SyncWidget::handleTimer() { //qDebug() << "==="; - std::vector handles = _session->get_torrents(); - for (torrent_handle h : handles) { - torrent_status s = h.status(); - InfoWidget* w = _torrentInfoWidgetMap[h]; - - w->update(static_cast(s.total_wanted_done)); - // qDebug() << "Name: " << QString::fromStdString(h.name()); // //torrent_status s = h.status(); @@ -469,5 +552,4 @@ void SyncWidget::handleTimer() { // qDebug() << "Progress: " << s.progress; // qDebug() << ""; - } } From 613b6b3193e4a0e92c33cc14e9d8d1134354b47b Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 17 Jun 2015 00:23:57 +0200 Subject: [PATCH 148/329] Move Spice kernels to new location Enable loading of openspace.cfg in Loader Make save locations based on FileSys tokens Disable LIBCMT linking of applications that link against libOpenSpace --- apps/Launcher/mainwindow.cpp | 28 ++++++++++++++---- apps/Launcher/mainwindow.h | 6 +++- apps/Launcher/syncwidget.cpp | 47 ++++++++++++++++++++---------- apps/Launcher/syncwidget.h | 4 +-- data | 2 +- openspace.cfg | 12 ++++---- support/cmake/support_macros.cmake | 7 +++++ 7 files changed, 73 insertions(+), 33 deletions(-) diff --git a/apps/Launcher/mainwindow.cpp b/apps/Launcher/mainwindow.cpp index f9462906f6..ac0a5accc3 100644 --- a/apps/Launcher/mainwindow.cpp +++ b/apps/Launcher/mainwindow.cpp @@ -27,6 +27,10 @@ #include "shortcutwidget.h" #include "syncwidget.h" +#include +#include + +#include #include #include #include @@ -46,7 +50,7 @@ namespace { const QString NewsURL = "http://openspace.itn.liu.se/news.txt"; - const QString ModulesDirectory = "../data/scene"; // temporary ---abock + const std::string _configurationFile = "openspace.cfg"; #ifdef WIN32 const QString OpenSpaceExecutable = "OpenSpace.exe"; @@ -167,7 +171,23 @@ void MainWindow::initialize() { _syncWidget->setWindowModality(Qt::WindowModal); _syncWidget->hide(); - QDir d(ModulesDirectory); + ghoul::logging::LogManager::initialize(ghoul::logging::LogManager::LogLevel::Error); + LogMgr.addLog(new ghoul::logging::ConsoleLog); + LogMgr.addLog(new QLog); + + std::string configurationFile = _configurationFile; + bool found = openspace::OpenSpaceEngine::findConfiguration(configurationFile); + if (!found) { + } + + _configuration = new openspace::ConfigurationManager; + _configuration->loadFromFile(configurationFile); + + + QString modulesDirectory = QString::fromStdString( + absPath("${SCENE}") + ); + QDir d(modulesDirectory); d.setFilter(QDir::Files); QFileInfoList list = d.entryInfoList(); @@ -176,11 +196,7 @@ void MainWindow::initialize() { _scenes->addItem(i.fileName()); } _syncWidget->setSceneFiles(_sceneFiles); - _syncWidget->setModulesDirectory(ModulesDirectory); - ghoul::logging::LogManager::initialize(ghoul::logging::LogManager::LogLevel::Error); - LogMgr.addLog(new ghoul::logging::ConsoleLog); - LogMgr.addLog(new QLog); } void MainWindow::shortcutButtonPressed() { diff --git a/apps/Launcher/mainwindow.h b/apps/Launcher/mainwindow.h index 3c11b3a71b..f131a8aa25 100644 --- a/apps/Launcher/mainwindow.h +++ b/apps/Launcher/mainwindow.h @@ -38,6 +38,10 @@ class QNetworkAccessManager; class ShortcutWidget; class SyncWidget; +namespace openspace { + class ConfigurationManager; +} + class MainWindow : public QWidget { Q_OBJECT public: @@ -65,8 +69,8 @@ private: ShortcutWidget* _shortcutWidget; SyncWidget* _syncWidget; - QNetworkAccessManager _networkManager; + openspace::ConfigurationManager* _configuration; }; //class MainWindow : public QWidget { diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index f6a81ed30b..4904c41523 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -62,6 +62,7 @@ namespace { const std::string TorrentFilesKey = "TorrentFiles"; const std::string UrlKey = "URL"; + const std::string FileKey = "File"; const std::string DestinationKey = "Destination"; const std::string IdentifierKey = "Identifier"; const std::string VersionKey = "Version"; @@ -155,10 +156,6 @@ void SyncWidget::setSceneFiles(QMap sceneFiles) { } } -void SyncWidget::setModulesDirectory(QString modulesDirectory) { - _modulesDirectory = std::move(modulesDirectory); -} - void SyncWidget::clear() { for (openspace::DownloadManager::FileFuture* f : _futures) f->abortDownload = true; @@ -187,7 +184,7 @@ void SyncWidget::handleDirectFiles() { openspace::DownloadManager::FileFuture* future = DlManager.downloadFile( f.url.toStdString(), - fullPath(f.module, f.destination).toStdString(), + absPath("${SCENE}/" + f.module.toStdString() + "/" + f.destination.toStdString()), OverwriteFiles ); if (future) { @@ -206,7 +203,7 @@ void SyncWidget::handleFileRequest() { qDebug() << f.identifier << " (" << f.version << ")" << " -> " << f.destination; std::string identifier = f.identifier.toStdString(); - std::string path = fullPath(f.module, f.destination).toStdString(); + std::string path = absPath("${SCENE}/" + f.module.toStdString() + "/" + f.destination.toStdString()); int version = f.version; std::vector futures = DlManager.downloadRequestFiles( @@ -231,7 +228,11 @@ void SyncWidget::handleFileRequest() { void SyncWidget::handleTorrentFiles() { qDebug() << "Torrent Files"; for (const TorrentFile& f : _torrentFiles) { - QString file = QString::fromStdString(absPath(fullPath(f.module, f.file).toStdString())); + ghoul::filesystem::Directory d = FileSys.currentDirectory(); + std::string thisDirectory = absPath("${SCENE}/" + f.module.toStdString() + "/"); + FileSys.setCurrentDirectory(thisDirectory); + + QString file = QString::fromStdString(absPath(f.file.toStdString())); qDebug() << file; if (!QFileInfo(file).exists()) { @@ -241,7 +242,14 @@ void SyncWidget::handleTorrentFiles() { libtorrent::error_code ec; libtorrent::add_torrent_params p; - p.save_path = absPath(fullPath(f.module, ".").toStdString()); + + //if (f.destination.isEmpty()) + //p.save_path = absPath(fullPath(f.module, ".").toStdString()); + //else + //p.save_path = + p.save_path = absPath(f.destination.toStdString()); + + qDebug() << QString::fromStdString(p.save_path); p.ti = new libtorrent::torrent_info(file.toStdString(), ec); p.name = f.file.toStdString(); @@ -262,6 +270,7 @@ void SyncWidget::handleTorrentFiles() { _downloadLayout->addWidget(w); _torrentInfoWidgetMap[h] = w; + FileSys.setCurrentDirectory(d); qApp->processEvents(); } } @@ -350,11 +359,19 @@ void SyncWidget::syncButtonPressed() { found = dataDictionary.getValue(TorrentFilesKey, torrentFiles); if (found) { for (int i = 1; i <= torrentFiles.size(); ++i) { - std::string f = torrentFiles.value(std::to_string(i)); + ghoul::Dictionary d = torrentFiles.value(std::to_string(i)); + std::string file = d.value(FileKey); + + std::string dest; + if (d.hasKeyAndValue(DestinationKey)) + dest = d.value(DestinationKey); + else + dest = ""; _torrentFiles.append({ module, - QString::fromStdString(f) + QString::fromStdString(file), + QString::fromStdString(dest) }); } } @@ -413,7 +430,8 @@ void SyncWidget::syncButtonPressed() { auto equal = [](const TorrentFile& lhs, const TorrentFile& rhs) -> bool { return lhs.module == rhs.module && - lhs.file == rhs.file; + lhs.file == rhs.file && + lhs.destination == rhs.destination; }; QList files; @@ -453,10 +471,6 @@ QStringList SyncWidget::selectedScenes() const { return result; } -QString SyncWidget::fullPath(QString module, QString destination) const { - return _modulesDirectory + "/" + module + "/" + destination; -} - void SyncWidget::handleTimer() { using namespace libtorrent; using FileFuture = openspace::DownloadManager::FileFuture; @@ -483,7 +497,8 @@ void SyncWidget::handleTimer() { torrent_status s = h.status(); InfoWidget* w = _torrentInfoWidgetMap[h]; - w->update(static_cast(s.total_wanted_done)); + if (w) + w->update(static_cast(s.total_wanted_done)); if (s.state == torrent_status::finished || s.state == torrent_status::seeding) { //_session->remove_torrent(h); diff --git a/apps/Launcher/syncwidget.h b/apps/Launcher/syncwidget.h index 0aecfc0761..c8c856c04b 100644 --- a/apps/Launcher/syncwidget.h +++ b/apps/Launcher/syncwidget.h @@ -52,7 +52,6 @@ public: ~SyncWidget(); void setSceneFiles(QMap sceneFiles); - void setModulesDirectory(QString modulesDirectory); private slots: void syncButtonPressed(); @@ -75,13 +74,12 @@ private: struct TorrentFile { QString module; QString file; + QString destination; }; void clear(); QStringList selectedScenes() const; - QString fullPath(QString module, QString destination) const; - void handleDirectFiles(); void handleFileRequest(); void handleTorrentFiles(); diff --git a/data b/data index dfc64c3319..1e33b0ba5d 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit dfc64c3319ff9108de5a0bce9cbcb4caf9ebddf1 +Subproject commit 1e33b0ba5dca3dfa358a98aa64fe6e386eecc425 diff --git a/openspace.cfg b/openspace.cfg index 5ccbed0285..a4372eecae 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -7,7 +7,7 @@ return { -- Sets the scene that is to be loaded by OpenSpace. A scene file is a description -- of all entities that will be visible during an instance of OpenSpace - Scene = "${OPENSPACE_DATA}/scene/default.scene", + Scene = "${SCENE}/default_nh.scene", Paths = { SGCT = "${BASE_PATH}/config/sgct", @@ -15,18 +15,18 @@ return { SHADERS = "${BASE_PATH}/shaders", SHADERS_GENERATED = "${SHADERS}/generated", OPENSPACE_DATA = "${BASE_PATH}/data", + SCENE = "${OPENSPACE_DATA}/scene", + SPICE = "${OPENSPACE_DATA}/spice", MODULES = "${BASE_PATH}/modules", TESTDIR = "${BASE_PATH}/tests", CONFIG = "${BASE_PATH}/config", CACHE = "${BASE_PATH}/cache", FONTS = "${OPENSPACE_DATA}/fonts", - PLUTO_KERNELS = "${OPENSPACE_DATA}/spice/Pluto", - JP_KERNELS = "${OPENSPACE_DATA}/spice/JP_KERNELS" }, SpiceKernel = { - Time = "${OPENSPACE_DATA}/spice/naif0010.tls", - LeapSecond = "${OPENSPACE_DATA}/spice/pck00010.tpc", - NewHorizons = "${OPENSPACE_DATA}/spice/nhmeta.tm" + Time = "${SPICE}/naif0010.tls", + LeapSecond = "${SPICE}/pck00010.tpc", + NewHorizons = "${SPICE}/nhmeta.tm" }, Fonts = { Mono = "${FONTS}/Droid_Sans_Mono/DroidSansMono.ttf", diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index faff599d9e..09a480f4f7 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -230,6 +230,13 @@ function (handle_applications) target_link_libraries(${APPLICATION_NAME} Ghoul) target_link_libraries(${APPLICATION_NAME} libOpenSpace) + if (MSVC) + set_target_properties(${APPLICATION_NAME} PROPERTIES LINK_FLAGS + "/NODEFAULTLIB:LIBCMTD.lib /NODEFAULTLIB:LIBCMT.lib" + ) + endif () + + copy_files(${APPLICATION_NAME} "${CURL_ROOT_DIR}/lib/libcurl.dll") endif () From 72c520e1d1f7da4c8fddabf6a551b5a1bb3c5988 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 17 Jun 2015 00:36:54 +0200 Subject: [PATCH 149/329] Close memory leak --- apps/Launcher/syncwidget.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 4904c41523..1947dacd03 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -489,8 +489,10 @@ void SyncWidget::handleTimer() { w->update(f->progress); } - for (FileFuture* f : toRemove) + for (FileFuture* f : toRemove) { _futures.erase(std::remove(_futures.begin(), _futures.end(), f), _futures.end()); + delete f; + } std::vector handles = _session->get_torrents(); for (torrent_handle h : handles) { From 850d9d3789c61562fe00ff38fe6ad8a506ea3bc6 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 17 Jun 2015 01:02:07 +0200 Subject: [PATCH 150/329] Multithreading the downloadrequests --- apps/Launcher/syncwidget.cpp | 60 +++++++++++++++++----- apps/Launcher/syncwidget.h | 6 +++ include/openspace/engine/downloadmanager.h | 10 ++++ src/engine/downloadmanager.cpp | 20 ++++++++ 4 files changed, 84 insertions(+), 12 deletions(-) diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 1947dacd03..f02363257b 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -131,6 +131,8 @@ SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) QTimer* timer = new QTimer(this); QObject::connect(timer, SIGNAL(timeout()), this, SLOT(handleTimer())); timer->start(100); + + _mutex.clear(); } SyncWidget::~SyncWidget() { @@ -205,23 +207,30 @@ void SyncWidget::handleFileRequest() { std::string identifier = f.identifier.toStdString(); std::string path = absPath("${SCENE}/" + f.module.toStdString() + "/" + f.destination.toStdString()); int version = f.version; - std::vector futures = - DlManager.downloadRequestFiles( + + DlManager.downloadRequestFilesAsync( identifier, path, version, - OverwriteFiles - ); + OverwriteFiles, + std::bind(&SyncWidget::handleFileFutureAddition, this, std::placeholders::_1) + ); - _futures.insert(_futures.end(), futures.begin(), futures.end()); - for (openspace::DownloadManager::FileFuture* f : futures) { - InfoWidget* w = new InfoWidget(QString::fromStdString(f->filePath), -1); - _downloadLayout->addWidget(w); + //std::vector futures = + // DlManager.downloadRequestFiles( + // identifier, + // path, + // version, + // OverwriteFiles + // ); - _futureInfoWidgetMap[f] = w; - } + //_futures.insert(_futures.end(), futures.begin(), futures.end()); + //for (openspace::DownloadManager::FileFuture* f : futures) { + // InfoWidget* w = new InfoWidget(QString::fromStdString(f->filePath), -1); + // _downloadLayout->addWidget(w); - qApp->processEvents(); + // _futureInfoWidgetMap[f] = w; + //} } } @@ -271,7 +280,6 @@ void SyncWidget::handleTorrentFiles() { _torrentInfoWidgetMap[h] = w; FileSys.setCurrentDirectory(d); - qApp->processEvents(); } } @@ -494,6 +502,18 @@ void SyncWidget::handleTimer() { delete f; } + while (_mutex.test_and_set()) {} + for (openspace::DownloadManager::FileFuture* f : _futuresToAdd) { + InfoWidget* w = new InfoWidget(QString::fromStdString(f->filePath), -1); + _downloadLayout->addWidget(w); + + _futureInfoWidgetMap[f] = w; + _futures.push_back(f); + } + _futuresToAdd.clear(); + _mutex.clear(); + + std::vector handles = _session->get_torrents(); for (torrent_handle h : handles) { torrent_status s = h.status(); @@ -570,3 +590,19 @@ void SyncWidget::handleTimer() { // qDebug() << ""; } + +void SyncWidget::handleFileFutureAddition( + const std::vector& futures) +{ + while (_mutex.test_and_set()) {} + _futuresToAdd.insert(_futuresToAdd.end(), futures.begin(), futures.end()); + _mutex.clear(); + //_futures.insert(_futures.end(), futures.begin(), futures.end()); + //for (openspace::DownloadManager::FileFuture* f : futures) { + // InfoWidget* w = new InfoWidget(QString::fromStdString(f->filePath), -1); + // _downloadLayout->addWidget(w); + + // _futureInfoWidgetMap[f] = w; + //} + +} diff --git a/apps/Launcher/syncwidget.h b/apps/Launcher/syncwidget.h index c8c856c04b..e1e560b021 100644 --- a/apps/Launcher/syncwidget.h +++ b/apps/Launcher/syncwidget.h @@ -33,6 +33,7 @@ #include +#include //#include class QBoxLayout; @@ -80,6 +81,8 @@ private: void clear(); QStringList selectedScenes() const; + void handleFileFutureAddition(const std::vector& futures); + void handleDirectFiles(); void handleFileRequest(); void handleTorrentFiles(); @@ -98,6 +101,9 @@ private: std::vector _futures; std::map _futureInfoWidgetMap; + + std::vector _futuresToAdd; + std::atomic_flag _mutex; }; #endif // __SYNCWIDGET_H__ diff --git a/include/openspace/engine/downloadmanager.h b/include/openspace/engine/downloadmanager.h index b17fc2ede5..4e61136bd9 100644 --- a/include/openspace/engine/downloadmanager.h +++ b/include/openspace/engine/downloadmanager.h @@ -58,10 +58,12 @@ public: typedef std::function DownloadProgressCallback; typedef std::function DownloadFinishedCallback; + typedef std::function&)> AsyncDownloadFinishedCallback; DownloadManager(std::string requestURL, int applicationVersion); // callers responsibility to delete + // callbacks happen on a different thread FileFuture* downloadFile( const std::string& url, const ghoul::filesystem::File& file, @@ -82,6 +84,14 @@ public: // TODO: Add async version of downloadRequestFiles that returns a filefuture // that can be used to call an additional function + void downloadRequestFilesAsync( + const std::string& identifier, + const ghoul::filesystem::Directory& destination, + int version, + bool overrideFiles, + AsyncDownloadFinishedCallback callback + ); + private: std::string _requestURL; int _applicationVersion; diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp index 5e6e06584a..edddf7a027 100644 --- a/src/engine/downloadmanager.cpp +++ b/src/engine/downloadmanager.cpp @@ -226,4 +226,24 @@ std::vector DownloadManager::downloadRequestFiles( return futures; } +void DownloadManager::downloadRequestFilesAsync( + const std::string& identifier, + const ghoul::filesystem::Directory& destination, + int version, + bool overrideFiles, + AsyncDownloadFinishedCallback callback) +{ + std::thread([this, identifier, destination, version, overrideFiles, callback](){ + std::vector f = downloadRequestFiles( + identifier, + destination, + version, + overrideFiles + ); + + callback(f); + }).detach(); + +} + } // namespace openspace From 54186e5dcbc84636abe205f8e25483d982433095 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 17 Jun 2015 01:43:01 +0200 Subject: [PATCH 151/329] Fix layout issues --- apps/Launcher/syncwidget.cpp | 39 +++++++++++++++++----- include/openspace/engine/downloadmanager.h | 3 -- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index f02363257b..e2810681d4 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -97,13 +97,31 @@ SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) } { - QWidget* downloadBox = new QGroupBox; - downloadBox->setMinimumSize(450, 1000); - _downloadLayout = new QVBoxLayout; - downloadBox->setLayout(_downloadLayout); - QScrollArea* area = new QScrollArea; - area->setWidget(downloadBox); + area->setWidgetResizable(true); + + QWidget* w = new QWidget; + area->setWidget(w); + + _downloadLayout = new QVBoxLayout(w); + _downloadLayout->addStretch(100); + + + + //QGroupBox* downloadBox = new QGroupBox; + + //QWidget* + + //_downloadLayout = new QVBoxLayout(area); + //area->setWidget(_downloadLayout) + + + + ////downloadBox->setMinimumSize(450, 1000); + //downloadBox->setLayout(_downloadLayout); + + //QScrollArea* area = new QScrollArea; + //area->setWidget(downloadBox); layout->addWidget(area); } @@ -191,7 +209,8 @@ void SyncWidget::handleDirectFiles() { ); if (future) { InfoWidget* w = new InfoWidget(f.destination); - _downloadLayout->addWidget(w); + _downloadLayout->insertWidget(_downloadLayout->count() - 1, w); + //_downloadLayout->addWidget(w); _futures.push_back(future); _futureInfoWidgetMap[future] = w; @@ -276,7 +295,8 @@ void SyncWidget::handleTorrentFiles() { libtorrent::size_type s = h.status().total_wanted; InfoWidget* w = new InfoWidget(f.file, h.status().total_wanted); - _downloadLayout->addWidget(w); + _downloadLayout->insertWidget(_downloadLayout->count() - 1, w); + //_downloadLayout->addWidget(w); _torrentInfoWidgetMap[h] = w; FileSys.setCurrentDirectory(d); @@ -505,7 +525,8 @@ void SyncWidget::handleTimer() { while (_mutex.test_and_set()) {} for (openspace::DownloadManager::FileFuture* f : _futuresToAdd) { InfoWidget* w = new InfoWidget(QString::fromStdString(f->filePath), -1); - _downloadLayout->addWidget(w); + _downloadLayout->insertWidget(_downloadLayout->count() - 1, w); + //_downloadLayout->addWidget(w); _futureInfoWidgetMap[f] = w; _futures.push_back(f); diff --git a/include/openspace/engine/downloadmanager.h b/include/openspace/engine/downloadmanager.h index 4e61136bd9..7aeb3f32ed 100644 --- a/include/openspace/engine/downloadmanager.h +++ b/include/openspace/engine/downloadmanager.h @@ -81,9 +81,6 @@ public: DownloadProgressCallback progressCallback = DownloadProgressCallback() ); - // TODO: Add async version of downloadRequestFiles that returns a filefuture - // that can be used to call an additional function - void downloadRequestFilesAsync( const std::string& identifier, const ghoul::filesystem::Directory& destination, From f35ec6f8d44d0091fedea6a5f3d00da5b8457c77 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 17 Jun 2015 08:38:25 +0200 Subject: [PATCH 152/329] Removed remotecontroller class --- .../openspace/interaction/remotecontroller.h | 58 ---------------- src/CMakeLists.txt | 2 - src/interaction/remotecontroller.cpp | 68 ------------------- 3 files changed, 128 deletions(-) delete mode 100644 include/openspace/interaction/remotecontroller.h delete mode 100644 src/interaction/remotecontroller.cpp diff --git a/include/openspace/interaction/remotecontroller.h b/include/openspace/interaction/remotecontroller.h deleted file mode 100644 index e23f020c75..0000000000 --- a/include/openspace/interaction/remotecontroller.h +++ /dev/null @@ -1,58 +0,0 @@ -/***************************************************************************************** -* * -* OpenSpace * -* * -* Copyright (c) 2014-2015 * -* * -* Permission is hereby granted, free of charge, to any person obtaining a copy of this * -* software and associated documentation files (the "Software"), to deal in the Software * -* without restriction, including without limitation the rights to use, copy, modify, * -* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * -* permit persons to whom the Software is furnished to do so, subject to the following * -* conditions: * -* * -* The above copyright notice and this permission notice shall be included in all copies * -* or substantial portions of the Software. * -* * -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * -* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * -* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * -* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * -* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * -* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * -****************************************************************************************/ - -#ifndef __REMOTECONTROLLER_H__ -#define __REMOTECONTROLLER_H__ - -#include -#include - -#include - -namespace openspace { - namespace interaction { - - struct ControllerKeyFrame{ - glm::mat4 _viewRotationMatrix; - psc _position; - double _timeStamp; - }; - - class RemoteController : public Controller { - public: - RemoteController(); - virtual ~RemoteController(); - virtual void update(const double& dt); - virtual void sendKeyFrame(); - virtual void keyFrameReceived(const ControllerKeyFrame& keyframe); - protected: - bool _isBroadcasting; - double _lastTimeStap; - std::ifstream ff; - }; - - } // namespace interaction -} // namespace openspace - -#endif // __REMOTECONTROLLER_H__ \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 22c2bf60b7..9ec41ef3cc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,7 +34,6 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/engine/moduleengine.cpp ${OPENSPACE_BASE_DIR}/src/engine/openspaceengine.cpp ${OPENSPACE_BASE_DIR}/src/interaction/controller.cpp - ${OPENSPACE_BASE_DIR}/src/interaction/remotecontroller.cpp ${OPENSPACE_BASE_DIR}/src/interaction/deviceidentifier.cpp ${OPENSPACE_BASE_DIR}/src/interaction/interactionhandler.cpp ${OPENSPACE_BASE_DIR}/src/interaction/interactionhandler_lua.inl @@ -98,7 +97,6 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/engine/moduleengine.h ${OPENSPACE_BASE_DIR}/include/openspace/engine/openspaceengine.h ${OPENSPACE_BASE_DIR}/include/openspace/interaction/controller.h - ${OPENSPACE_BASE_DIR}/include/openspace/interaction/remotecontroller.h ${OPENSPACE_BASE_DIR}/include/openspace/interaction/deviceidentifier.h ${OPENSPACE_BASE_DIR}/include/openspace/interaction/interactionhandler.h ${OPENSPACE_BASE_DIR}/include/openspace/interaction/keyboardcontroller.h diff --git a/src/interaction/remotecontroller.cpp b/src/interaction/remotecontroller.cpp deleted file mode 100644 index 6417a8226b..0000000000 --- a/src/interaction/remotecontroller.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2015 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#include - -#include - -#include - -#include - -namespace openspace { -namespace interaction { - - RemoteController::RemoteController() - : _isBroadcasting(false), - _lastTimeStap(0.0) - { - //ff.open("path.txt"); - } - - RemoteController::~RemoteController(){ - - } - - void RemoteController::update(const double& dt){ - ControllerKeyFrame kf; - kf._position = _handler->camera()->position(); - kf._viewRotationMatrix = _handler->camera()->viewRotationMatrix(); - kf._timeStamp = Time::ref().currentTime(); - - std::string write = std::to_string(kf._position.vec4().x) + "\t" + std::to_string(kf._position.vec4().y) + "\t" + std::to_string(kf._position.vec4().z) + "\t" + std::to_string(kf._position.vec4().w) + "\n"; - //write += - //printf("%s", write.c_str()); - } - - void RemoteController::sendKeyFrame(){ - - } - - void RemoteController::keyFrameReceived(const ControllerKeyFrame& keyframe){ - - } - - -} // namespace interaction -} // namespace openspace From cce351af690908846b0582b82afb39132b4bb0eb Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 17 Jun 2015 08:39:02 +0200 Subject: [PATCH 153/329] removed all references to remotecontroller --- include/openspace/interaction/interactionhandler.h | 3 --- src/engine/openspaceengine.cpp | 3 --- src/interaction/interactionhandler.cpp | 9 --------- 3 files changed, 15 deletions(-) diff --git a/include/openspace/interaction/interactionhandler.h b/include/openspace/interaction/interactionhandler.h index 183ae0c0ca..2b56ebe881 100644 --- a/include/openspace/interaction/interactionhandler.h +++ b/include/openspace/interaction/interactionhandler.h @@ -76,7 +76,6 @@ #include #include -#include #include @@ -95,7 +94,6 @@ public: void setKeyboardController(KeyboardController* controller); void setMouseController(MouseController* controller); - void setRemoteController(RemoteController* controller); void addController(Controller* controller); void lockControls(); @@ -173,7 +171,6 @@ private: KeyboardController* _keyboardController; MouseController* _mouseController; - RemoteController* _remoteController; std::vector _controllers; }; diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index ba6c172af3..28996a2cf3 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -353,9 +353,6 @@ bool OpenSpaceEngine::initialize() { //_interactionHandler.setMouseController(new interaction::TrackballMouseController); _interactionHandler->setMouseController(new interaction::OrbitalMouseController); - //@TODO fix this -JK - _interactionHandler->setRemoteController(new interaction::RemoteController); - // Run start up scripts runStartupScripts(); diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index 3c4ae90b30..11148e379f 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -253,7 +253,6 @@ InteractionHandler::InteractionHandler() , _invertRotation(false) , _keyboardController(nullptr) , _mouseController(nullptr) - , _remoteController(nullptr) { } @@ -278,13 +277,6 @@ void InteractionHandler::setMouseController(MouseController* controller) { _mouseController->setHandler(this); } -void InteractionHandler::setRemoteController(RemoteController* controller) { - assert(controller); - delete _remoteController; - _remoteController = controller; - _remoteController->setHandler(this); -} - void InteractionHandler::addController(Controller* controller) { assert(controller); _controllers.push_back(controller); @@ -355,7 +347,6 @@ void InteractionHandler::unlockControls() { void InteractionHandler::update(double deltaTime) { _deltaTime = deltaTime; _mouseController->update(deltaTime); - _remoteController->update(deltaTime); } void InteractionHandler::setFocusNode(SceneGraphNode* node) { From 91247d4ac9d784fb1cecf8d382d1a1ff21169b08 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 17 Jun 2015 08:39:34 +0200 Subject: [PATCH 154/329] added keyframe struct and functionality to broadcast keyframes --- .../openspace/network/osparallelconnection.h | 65 +++++++++- src/network/osparallelconnection.cpp | 115 +++++++++++++++--- 2 files changed, 165 insertions(+), 15 deletions(-) diff --git a/include/openspace/network/osparallelconnection.h b/include/openspace/network/osparallelconnection.h index d6a8b101b5..8169e03c2e 100644 --- a/include/openspace/network/osparallelconnection.h +++ b/include/openspace/network/osparallelconnection.h @@ -29,12 +29,15 @@ #include #include #include +#include +#include //std includes #include #include #include #include +#include #ifdef __WIN32__ #ifndef WIN32_LEAN_AND_MEAN @@ -56,6 +59,63 @@ namespace openspace{ namespace network{ + struct Keyframe{ + glm::quat _viewRotationQuat; + psc _position; + double _timeStamp; + + std::string to_string(){ + std::stringstream ss; + //position + ss << _position.dvec4().x; + ss << "\t"; + ss << _position.dvec4().y; + ss << "\t"; + ss << _position.dvec4().z; + ss << "\t"; + ss << _position.dvec4().w; + ss << "\t"; + + ss << "\n"; + //orientation + ss << _viewRotationQuat.x; + ss << "\t"; + ss << _viewRotationQuat.y; + ss << "\t"; + ss << _viewRotationQuat.z; + ss << "\t"; + ss << _viewRotationQuat.w; + + ss << "\n"; + //timestamp + ss << _timeStamp; + + return ss.str(); + } + + void from_string(std::string &val){ + std::stringstream ss(val); + double x, y, z, w; + + //position + ss >> x; + ss >> y; + ss >> z; + ss >> w; + _position = psc(x, y, z, w); + + //orientation + ss >> x; + ss >> y; + ss >> z; + ss >> w; + _viewRotationQuat = glm::quat(x, y, z, w); + + //timestamp + ss >> _timeStamp; + } + }; + class OSParallelConnection : public properties::PropertyOwner { public: @@ -153,6 +213,8 @@ namespace openspace{ void decodeInitializationRequestMessage(); + void broadcast(); + int receiveData(_SOCKET & socket, std::vector &buffer, int length, int flags); uint32_t _passCode; @@ -160,7 +222,8 @@ namespace openspace{ std::string _address; std::string _name; _SOCKET _clientSocket; - std::thread *_thread; + std::thread *_connectionThread; + std::thread *_broadcastThread; std::atomic _isRunning; std::atomic _isHost; }; diff --git a/src/network/osparallelconnection.cpp b/src/network/osparallelconnection.cpp index 6370581967..a53bfd83ac 100644 --- a/src/network/osparallelconnection.cpp +++ b/src/network/osparallelconnection.cpp @@ -60,7 +60,7 @@ const int headerSize = 8; #include #include #include -#include +#include #include "osparallelconnection_lua.inl" @@ -75,9 +75,10 @@ namespace openspace { _passCode(0), _port("20501"), _address("127.0.0.1"), - _name("No name"), + _name("Local Connection"), _clientSocket(INVALID_SOCKET), - _thread(nullptr), + _connectionThread(nullptr), + _broadcastThread(nullptr), _isRunning(false), _isHost(false) { @@ -130,7 +131,7 @@ namespace openspace { //start accept connections thread _isRunning.store(true); - _thread = new (std::nothrow) std::thread(&OSParallelConnection::connection, this, addresult); + _connectionThread = new (std::nothrow) std::thread(&OSParallelConnection::connection, this, addresult); } @@ -262,6 +263,27 @@ namespace openspace { void OSParallelConnection::decodeDataMessage(){ printf("Data message received!\n"); + int result; + uint16_t msglen; + std::vector buffer; + buffer.reserve(sizeof(msglen)); + result = receiveData(_clientSocket, buffer, sizeof(msglen), 0); + + if (result <= 0){ + //error + return; + } + + msglen = (*(reinterpret_cast(buffer.data()))); + + buffer.clear(); + buffer.reserve(msglen); + + result = receiveData(_clientSocket, buffer, msglen, 0); + if (result <= 0){ + //error + return; + } } void OSParallelConnection::decodeHostInfoMessage(){ @@ -271,14 +293,30 @@ namespace openspace { if (result > 0){ if (hostflag.at(0) == 1){ - printf("IM MASTER!\n"); - _isHost.store(true); + //we're already host, do nothing (dummy check) + if (_isHost.load()){ + return; + } + else{ + //start broadcasting + _isHost.store(true); + _broadcastThread = new (std::nothrow) std::thread(&OSParallelConnection::broadcast, this); + } } else{ - printf("IM A SLAVE!\n"); - _isHost.store(false); + //we were broadcasting but should stop now + if (_isHost.load()){ + _isHost.store(false); + if (_broadcastThread != nullptr){ + _broadcastThread->join(); + delete _broadcastThread; + _broadcastThread = nullptr; + } + } + else{ + //we were not host so nothing to do + } } - } else{ std::cerr << "Error " << _ERRNO << " detected in connection!" << std::endl; @@ -398,13 +436,23 @@ namespace openspace { } void OSParallelConnection::disconnect(){ - closeSocket(); + _isHost.store(false); - if (_thread != nullptr){ - _thread->join(); - delete _thread; - _thread = nullptr; + if (_broadcastThread != nullptr){ + _broadcastThread->join(); + delete _broadcastThread; + _broadcastThread = nullptr; } + + _isRunning.store(false); + + if (_connectionThread != nullptr){ + _connectionThread->join(); + delete _connectionThread; + _connectionThread = nullptr; + } + + closeSocket(); } void OSParallelConnection::closeSocket(){ @@ -459,6 +507,45 @@ namespace openspace { return true; } + void OSParallelConnection::broadcast(){ + + while (_isHost.load()){ + + network::Keyframe kf; + kf._position = OsEng.interactionHandler()->camera()->position(); + kf._viewRotationQuat = glm::quat_cast(OsEng.interactionHandler()->camera()->viewRotationMatrix()); + kf._timeStamp = Time::ref().currentTime(); + + std::string msg = kf.to_string(); + + uint16_t msglen = static_cast(msg.length()); + std::vector buffer; + buffer.reserve(headerSize + sizeof(msglen) + msglen); + + //header + buffer.insert(buffer.end(), 'O'); + buffer.insert(buffer.end(), 'S'); + buffer.insert(buffer.end(), 0); + buffer.insert(buffer.end(), 0); + + //type of message + int type = OSParallelConnection::MessageTypes::Data; + buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(type)); + + //size of message + buffer.insert(buffer.end(), reinterpret_cast(&msglen), reinterpret_cast(&msglen) + sizeof(msglen)); + + //actual message + buffer.insert(buffer.end(), msg.begin(), msg.end()); + + //send message + send(_clientSocket, buffer.data(), buffer.size(), 0); + + //100 ms sleep + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + } + scripting::ScriptEngine::LuaLibrary OSParallelConnection::luaLibrary() { return { "parallel", From 74ab05ec37ce80fe08bc9af33d39911de40c2625 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 17 Jun 2015 08:39:49 +0200 Subject: [PATCH 155/329] rename of mutex --- include/openspace/util/camera.h | 5 ++--- src/util/camera.cpp | 16 ++++++++-------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/include/openspace/util/camera.h b/include/openspace/util/camera.h index 69a45e96e2..39922715fb 100644 --- a/include/openspace/util/camera.h +++ b/include/openspace/util/camera.h @@ -159,9 +159,8 @@ private: glm::vec3 _lookUp; - //cluster variables - std::mutex _syncMutex; - + std::mutex _mutex; + //local variables glm::mat4 _localViewRotationMatrix; glm::vec2 _localScaling; diff --git a/src/util/camera.cpp b/src/util/camera.cpp index 3ee138bfd4..e99d7eaf29 100644 --- a/src/util/camera.cpp +++ b/src/util/camera.cpp @@ -219,43 +219,43 @@ const glm::vec3& Camera::lookUpVector() const } void Camera::serialize(SyncBuffer* syncBuffer){ - _syncMutex.lock(); + _mutex.lock(); syncBuffer->encode(_sharedViewRotationMatrix); syncBuffer->encode(_sharedPosition); syncBuffer->encode(_sharedScaling); - _syncMutex.unlock(); + _mutex.unlock(); } void Camera::deserialize(SyncBuffer* syncBuffer){ - _syncMutex.lock(); + _mutex.lock(); syncBuffer->decode(_sharedViewRotationMatrix); syncBuffer->decode(_sharedPosition); syncBuffer->decode(_sharedScaling); - _syncMutex.unlock(); + _mutex.unlock(); } void Camera::postSynchronizationPreDraw(){ - _syncMutex.lock(); + _mutex.lock(); _syncedViewRotationMatrix = _sharedViewRotationMatrix; _syncedPosition = _sharedPosition; _syncedScaling = _sharedScaling; - _syncMutex.unlock(); + _mutex.unlock(); } void Camera::preSynchronization(){ - _syncMutex.lock(); + _mutex.lock(); _sharedViewRotationMatrix = _localViewRotationMatrix; _sharedPosition = _localPosition; _sharedScaling = _localScaling; - _syncMutex.unlock(); + _mutex.unlock(); } // From 9d6b9a3ad151d566a213e3610c41f0cb2d23e493 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 17 Jun 2015 09:52:12 +0200 Subject: [PATCH 156/329] adding first version of keyframe functionality for interactionhandler --- include/openspace/interaction/interactionhandler.h | 7 +++++++ src/interaction/interactionhandler.cpp | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/include/openspace/interaction/interactionhandler.h b/include/openspace/interaction/interactionhandler.h index 2b56ebe881..0a0a9fa71e 100644 --- a/include/openspace/interaction/interactionhandler.h +++ b/include/openspace/interaction/interactionhandler.h @@ -76,6 +76,7 @@ #include #include +#include #include @@ -139,6 +140,8 @@ public: void setInvertRotation(bool invert); bool invertRotation() const; + void addKeyframe(const network::Keyframe &kf); + /** * Returns the Lua library that contains all Lua functions available to affect the * interaction. The functions contained are @@ -149,6 +152,7 @@ public: static scripting::ScriptEngine::LuaLibrary luaLibrary(); private: + friend class Controller; InteractionHandler(const InteractionHandler&) = delete; @@ -172,6 +176,9 @@ private: KeyboardController* _keyboardController; MouseController* _mouseController; std::vector _controllers; + + //remote controller + std::vector _keyframes; }; } // namespace interaction diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index 11148e379f..ddf5f6fda0 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -254,6 +254,9 @@ InteractionHandler::InteractionHandler() , _keyboardController(nullptr) , _mouseController(nullptr) { + network::Keyframe kf; + kf._timeStamp = std::numeric_limits::min(); + _keyframes.assign(4, kf); } InteractionHandler::~InteractionHandler() { @@ -347,6 +350,7 @@ void InteractionHandler::unlockControls() { void InteractionHandler::update(double deltaTime) { _deltaTime = deltaTime; _mouseController->update(deltaTime); + printf("Current keys:\n, %s\n %s\n %s\n %s\n\n\n", _keyframes[0].to_string(), _keyframes[1].to_string(), _keyframes[2].to_string(), _keyframes[3].to_string()); } void InteractionHandler::setFocusNode(SceneGraphNode* node) { @@ -919,6 +923,11 @@ bool InteractionHandler::invertRotation() const { return _invertRotation; } +void InteractionHandler::addKeyframe(const network::Keyframe &kf){ + _keyframes.push_back(kf); + _keyframes.erase(_keyframes.begin()); +} + } // namespace interaction //>>>>>>> feature/interactionhandler } // namespace openspace From 4e4cf4cd6a7ac02b5da12f9eda1a5d148ae4f2a2 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 17 Jun 2015 11:18:50 +0200 Subject: [PATCH 157/329] temporarily removed references to keyframes in interactionhandler. changed how keyframes are encoded/decoded and sent in osparallelconnection --- .../interaction/interactionhandler.h | 5 +- .../openspace/network/osparallelconnection.h | 80 +++++++------------ src/interaction/interactionhandler.cpp | 21 +++-- src/network/osparallelconnection.cpp | 21 +++-- 4 files changed, 59 insertions(+), 68 deletions(-) diff --git a/include/openspace/interaction/interactionhandler.h b/include/openspace/interaction/interactionhandler.h index 0a0a9fa71e..862386695b 100644 --- a/include/openspace/interaction/interactionhandler.h +++ b/include/openspace/interaction/interactionhandler.h @@ -76,7 +76,6 @@ #include #include -#include #include @@ -140,7 +139,7 @@ public: void setInvertRotation(bool invert); bool invertRotation() const; - void addKeyframe(const network::Keyframe &kf); +// void addKeyframe(const network::Keyframe &kf); /** * Returns the Lua library that contains all Lua functions available to affect the @@ -178,7 +177,7 @@ private: std::vector _controllers; //remote controller - std::vector _keyframes; +// std::vector _keyframes; }; } // namespace interaction diff --git a/include/openspace/network/osparallelconnection.h b/include/openspace/network/osparallelconnection.h index 8169e03c2e..73fb2c5b37 100644 --- a/include/openspace/network/osparallelconnection.h +++ b/include/openspace/network/osparallelconnection.h @@ -64,56 +64,36 @@ namespace openspace{ psc _position; double _timeStamp; - std::string to_string(){ - std::stringstream ss; - //position - ss << _position.dvec4().x; - ss << "\t"; - ss << _position.dvec4().y; - ss << "\t"; - ss << _position.dvec4().z; - ss << "\t"; - ss << _position.dvec4().w; - ss << "\t"; - - ss << "\n"; - //orientation - ss << _viewRotationQuat.x; - ss << "\t"; - ss << _viewRotationQuat.y; - ss << "\t"; - ss << _viewRotationQuat.z; - ss << "\t"; - ss << _viewRotationQuat.w; - - ss << "\n"; - //timestamp - ss << _timeStamp; - - return ss.str(); - } - - void from_string(std::string &val){ - std::stringstream ss(val); - double x, y, z, w; - - //position - ss >> x; - ss >> y; - ss >> z; - ss >> w; - _position = psc(x, y, z, w); - - //orientation - ss >> x; - ss >> y; - ss >> z; - ss >> w; - _viewRotationQuat = glm::quat(x, y, z, w); - - //timestamp - ss >> _timeStamp; - } + void serialize(std::vector &buffer){ + //add position + buffer.insert(buffer.end(), reinterpret_cast(&_position), reinterpret_cast(&_position) + sizeof(_position)); + + //add orientation + buffer.insert(buffer.end(), reinterpret_cast(&_viewRotationQuat), reinterpret_cast(&_viewRotationQuat) + sizeof(_viewRotationQuat)); + + //add timestamp + buffer.insert(buffer.end(), reinterpret_cast(&_timeStamp), reinterpret_cast(&_timeStamp) + sizeof(_timeStamp)); + }; + + void deserialize(const std::vector &buffer){ + int offset = 0; + int size = 0; + + //position + size = sizeof(_position); + memcpy(&_position, buffer.data() + offset, size); + offset += size; + + //orientation + size = sizeof(_viewRotationQuat); + memcpy(&_viewRotationQuat, buffer.data() + offset, size); + offset += size; + + //timestamp + size = sizeof(_timeStamp); + memcpy(&_timeStamp, buffer.data() + offset, size); + offset += size; + }; }; class OSParallelConnection : public properties::PropertyOwner { diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index ddf5f6fda0..4b4ea62a7f 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -254,9 +254,9 @@ InteractionHandler::InteractionHandler() , _keyboardController(nullptr) , _mouseController(nullptr) { - network::Keyframe kf; - kf._timeStamp = std::numeric_limits::min(); - _keyframes.assign(4, kf); +// network::Keyframe kf; +// kf._timeStamp = -std::numeric_limits::max(); +// _keyframes.assign(4, kf); } InteractionHandler::~InteractionHandler() { @@ -350,7 +350,9 @@ void InteractionHandler::unlockControls() { void InteractionHandler::update(double deltaTime) { _deltaTime = deltaTime; _mouseController->update(deltaTime); - printf("Current keys:\n, %s\n %s\n %s\n %s\n\n\n", _keyframes[0].to_string(), _keyframes[1].to_string(), _keyframes[2].to_string(), _keyframes[3].to_string()); +// printf("%f\n", _keyframes[0]._timeStamp); +// printf("%f\n %f\n %f\n %f\n", _keyframes[0]._timeStamp, _keyframes[1]._timeStamp, _keyframes[2]._timeStamp, _keyframes[3]._timeStamp); +// printf("Current keys:\n, %s\n %s\n %s\n %s\n\n\n", _keyframes[0].to_string().c_str(), _keyframes[1].to_string().c_str(), _keyframes[2].to_string().c_str(), _keyframes[3].to_string().c_str()); } void InteractionHandler::setFocusNode(SceneGraphNode* node) { @@ -923,10 +925,13 @@ bool InteractionHandler::invertRotation() const { return _invertRotation; } -void InteractionHandler::addKeyframe(const network::Keyframe &kf){ - _keyframes.push_back(kf); - _keyframes.erase(_keyframes.begin()); -} +//void InteractionHandler::addKeyframe(const network::Keyframe &kf){ +//// _keyframes.erase(_keyframes.begin()); +// _keyframes.clear(); +// _keyframes.resize(1); +// _keyframes.push_back(kf); +// printf("%f\n", kf._timeStamp); +//} } // namespace interaction //>>>>>>> feature/interactionhandler diff --git a/src/network/osparallelconnection.cpp b/src/network/osparallelconnection.cpp index a53bfd83ac..64e1d43437 100644 --- a/src/network/osparallelconnection.cpp +++ b/src/network/osparallelconnection.cpp @@ -262,11 +262,11 @@ namespace openspace { } void OSParallelConnection::decodeDataMessage(){ - printf("Data message received!\n"); +// printf("Data message received!\n"); int result; uint16_t msglen; std::vector buffer; - buffer.reserve(sizeof(msglen)); + buffer.resize(sizeof(msglen)); result = receiveData(_clientSocket, buffer, sizeof(msglen), 0); if (result <= 0){ @@ -277,13 +277,19 @@ namespace openspace { msglen = (*(reinterpret_cast(buffer.data()))); buffer.clear(); - buffer.reserve(msglen); + buffer.resize(msglen); result = receiveData(_clientSocket, buffer, msglen, 0); if (result <= 0){ //error return; } + + network::Keyframe kf; + kf.deserialize(buffer); + printf("--- %f ---\n", kf._timeStamp); +// OsEng.interactionHandler()->addKeyframe(kf); + } void OSParallelConnection::decodeHostInfoMessage(){ @@ -516,9 +522,10 @@ namespace openspace { kf._viewRotationQuat = glm::quat_cast(OsEng.interactionHandler()->camera()->viewRotationMatrix()); kf._timeStamp = Time::ref().currentTime(); - std::string msg = kf.to_string(); - - uint16_t msglen = static_cast(msg.length()); + std::vector kfBuffer; + kf.serialize(kfBuffer); + + uint16_t msglen = static_cast(kfBuffer.size()); std::vector buffer; buffer.reserve(headerSize + sizeof(msglen) + msglen); @@ -536,7 +543,7 @@ namespace openspace { buffer.insert(buffer.end(), reinterpret_cast(&msglen), reinterpret_cast(&msglen) + sizeof(msglen)); //actual message - buffer.insert(buffer.end(), msg.begin(), msg.end()); + buffer.insert(buffer.end(), kfBuffer.begin(), kfBuffer.end()); //send message send(_clientSocket, buffer.data(), buffer.size(), 0); From 8297f2e683619afa6b0be9f1dbb96f42e32ea51e Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 17 Jun 2015 13:10:19 +0200 Subject: [PATCH 158/329] First test of actual remote controlling --- .../interaction/interactionhandler.h | 5 +++-- .../openspace/network/osparallelconnection.h | 12 +++++------ src/interaction/interactionhandler.cpp | 21 +++++++++---------- src/network/osparallelconnection.cpp | 6 +++--- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/include/openspace/interaction/interactionhandler.h b/include/openspace/interaction/interactionhandler.h index 862386695b..0a0a9fa71e 100644 --- a/include/openspace/interaction/interactionhandler.h +++ b/include/openspace/interaction/interactionhandler.h @@ -76,6 +76,7 @@ #include #include +#include #include @@ -139,7 +140,7 @@ public: void setInvertRotation(bool invert); bool invertRotation() const; -// void addKeyframe(const network::Keyframe &kf); + void addKeyframe(const network::Keyframe &kf); /** * Returns the Lua library that contains all Lua functions available to affect the @@ -177,7 +178,7 @@ private: std::vector _controllers; //remote controller -// std::vector _keyframes; + std::vector _keyframes; }; } // namespace interaction diff --git a/include/openspace/network/osparallelconnection.h b/include/openspace/network/osparallelconnection.h index 73fb2c5b37..8a84120fbc 100644 --- a/include/openspace/network/osparallelconnection.h +++ b/include/openspace/network/osparallelconnection.h @@ -59,11 +59,11 @@ namespace openspace{ namespace network{ - struct Keyframe{ - glm::quat _viewRotationQuat; - psc _position; - double _timeStamp; - + struct Keyframe{ + glm::quat _viewRotationQuat; + psc _position; + double _timeStamp; + void serialize(std::vector &buffer){ //add position buffer.insert(buffer.end(), reinterpret_cast(&_position), reinterpret_cast(&_position) + sizeof(_position)); @@ -94,7 +94,7 @@ namespace openspace{ memcpy(&_timeStamp, buffer.data() + offset, size); offset += size; }; - }; + }; class OSParallelConnection : public properties::PropertyOwner { public: diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index 4b4ea62a7f..c066e2c391 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -254,9 +254,9 @@ InteractionHandler::InteractionHandler() , _keyboardController(nullptr) , _mouseController(nullptr) { -// network::Keyframe kf; -// kf._timeStamp = -std::numeric_limits::max(); -// _keyframes.assign(4, kf); + network::Keyframe kf; + kf._timeStamp = -std::numeric_limits::max(); + _keyframes.assign(4, kf); } InteractionHandler::~InteractionHandler() { @@ -350,7 +350,9 @@ void InteractionHandler::unlockControls() { void InteractionHandler::update(double deltaTime) { _deltaTime = deltaTime; _mouseController->update(deltaTime); -// printf("%f\n", _keyframes[0]._timeStamp); + + _camera->setPosition(_keyframes[3]._position); + _camera->setViewRotationMatrix(glm::mat4_cast(_keyframes[3]._viewRotationQuat)); // printf("%f\n %f\n %f\n %f\n", _keyframes[0]._timeStamp, _keyframes[1]._timeStamp, _keyframes[2]._timeStamp, _keyframes[3]._timeStamp); // printf("Current keys:\n, %s\n %s\n %s\n %s\n\n\n", _keyframes[0].to_string().c_str(), _keyframes[1].to_string().c_str(), _keyframes[2].to_string().c_str(), _keyframes[3].to_string().c_str()); } @@ -925,13 +927,10 @@ bool InteractionHandler::invertRotation() const { return _invertRotation; } -//void InteractionHandler::addKeyframe(const network::Keyframe &kf){ -//// _keyframes.erase(_keyframes.begin()); -// _keyframes.clear(); -// _keyframes.resize(1); -// _keyframes.push_back(kf); -// printf("%f\n", kf._timeStamp); -//} +void InteractionHandler::addKeyframe(const network::Keyframe &kf){ + _keyframes.erase(_keyframes.begin()); + _keyframes.push_back(kf); +} } // namespace interaction //>>>>>>> feature/interactionhandler diff --git a/src/network/osparallelconnection.cpp b/src/network/osparallelconnection.cpp index 64e1d43437..e7c14a2fb9 100644 --- a/src/network/osparallelconnection.cpp +++ b/src/network/osparallelconnection.cpp @@ -262,7 +262,7 @@ namespace openspace { } void OSParallelConnection::decodeDataMessage(){ -// printf("Data message received!\n"); + int result; uint16_t msglen; std::vector buffer; @@ -287,8 +287,8 @@ namespace openspace { network::Keyframe kf; kf.deserialize(buffer); - printf("--- %f ---\n", kf._timeStamp); -// OsEng.interactionHandler()->addKeyframe(kf); +// printf("--- %f ---\n", kf._timeStamp); + OsEng.interactionHandler()->addKeyframe(kf); } From 206b47d675ec9858ad42d45ed15ab493c39f2e23 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 17 Jun 2015 13:21:29 +0200 Subject: [PATCH 159/329] quick fix for testing --- src/interaction/interactionhandler.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index c066e2c391..1a49664463 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -351,8 +351,10 @@ void InteractionHandler::update(double deltaTime) { _deltaTime = deltaTime; _mouseController->update(deltaTime); - _camera->setPosition(_keyframes[3]._position); - _camera->setViewRotationMatrix(glm::mat4_cast(_keyframes[3]._viewRotationQuat)); + if(_keyframes[0]._timeStamp > -std::numeric_limits::max()){ + _camera->setPosition(_keyframes[3]._position); + _camera->setViewRotationMatrix(glm::mat4_cast(_keyframes[3]._viewRotationQuat)); + } // printf("%f\n %f\n %f\n %f\n", _keyframes[0]._timeStamp, _keyframes[1]._timeStamp, _keyframes[2]._timeStamp, _keyframes[3]._timeStamp); // printf("Current keys:\n, %s\n %s\n %s\n %s\n\n\n", _keyframes[0].to_string().c_str(), _keyframes[1].to_string().c_str(), _keyframes[2].to_string().c_str(), _keyframes[3].to_string().c_str()); } From 78f170048e846682ee83f84a09f04ebe69560e35 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 17 Jun 2015 15:04:55 +0200 Subject: [PATCH 160/329] moved deleteion of osparallelconnection --- src/engine/openspaceengine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 28996a2cf3..bae0532874 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -122,6 +122,7 @@ OpenSpaceEngine::OpenSpaceEngine(std::string programName) OpenSpaceEngine::~OpenSpaceEngine() { _gui->deinitializeGL(); + delete _parallelConnection; delete _configurationManager; delete _interactionHandler; delete _renderEngine; @@ -130,8 +131,7 @@ OpenSpaceEngine::~OpenSpaceEngine() { delete _commandlineParser; delete _console; delete _moduleEngine; - delete _gui; - delete _parallelConnection; + delete _gui; if(_syncBuffer) delete _syncBuffer; From e42d160773136354dc79967ea7ef10cf3e8dd716 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 17 Jun 2015 15:11:34 +0200 Subject: [PATCH 161/329] added interpolation and mutex protection changed TCP protocol to no delay --- .../interaction/interactionhandler.h | 2 ++ src/interaction/interactionhandler.cpp | 29 +++++++++++++++++-- src/network/osparallelconnection.cpp | 16 +++++++--- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/include/openspace/interaction/interactionhandler.h b/include/openspace/interaction/interactionhandler.h index 0a0a9fa71e..80c8651929 100644 --- a/include/openspace/interaction/interactionhandler.h +++ b/include/openspace/interaction/interactionhandler.h @@ -179,6 +179,8 @@ private: //remote controller std::vector _keyframes; + double _currentKeyframeTime; + std::mutex _keyframeMutex; }; } // namespace interaction diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index 1a49664463..cd3ba3cd50 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -32,6 +32,7 @@ #include #include +#include namespace { const std::string _loggerCat = "InteractionHandler"; @@ -351,10 +352,31 @@ void InteractionHandler::update(double deltaTime) { _deltaTime = deltaTime; _mouseController->update(deltaTime); - if(_keyframes[0]._timeStamp > -std::numeric_limits::max()){ - _camera->setPosition(_keyframes[3]._position); + _keyframeMutex.lock(); + //if the target keyframe has a valid timestamp and time within range + if (_keyframes[1]._timeStamp > 0.0){// && _currentKeyframeTime <= _keyframes[2]._timeStamp){ + + ghoul::Interpolator positionInterpCR; + ghoul::Interpolator positionInterpLin; + double t0 = _keyframes[1]._timeStamp; + double t1 = _keyframes[2]._timeStamp; + double fact = (_currentKeyframeTime - t0) / (t1 - t0); + + glm::dvec4 v = positionInterpCR.interpolate(fact, _keyframes[0]._position.dvec4(), _keyframes[1]._position.dvec4(), _keyframes[2]._position.dvec4(), _keyframes[3]._position.dvec4()); + //glm::dvec4 v = positionInterpLin.interpolate(fact, _keyframes[1]._position.dvec4(), _keyframes[2]._position.dvec4()); + psc pos(v.x, v.y, v.z, v.w); + + //printf("---fact: %f\nT0: %f, T1%f\n, CURR:%f---\n", fact, t0, t1, _currentKeyframeTime); + //printf("Fact: %f\n", fact); + + _camera->setPosition(pos); _camera->setViewRotationMatrix(glm::mat4_cast(_keyframes[3]._viewRotationQuat)); + + _currentKeyframeTime += deltaTime; + _currentKeyframeTime = std::fmin(_currentKeyframeTime, t1); } + _keyframeMutex.unlock(); + // printf("%f\n %f\n %f\n %f\n", _keyframes[0]._timeStamp, _keyframes[1]._timeStamp, _keyframes[2]._timeStamp, _keyframes[3]._timeStamp); // printf("Current keys:\n, %s\n %s\n %s\n %s\n\n\n", _keyframes[0].to_string().c_str(), _keyframes[1].to_string().c_str(), _keyframes[2].to_string().c_str(), _keyframes[3].to_string().c_str()); } @@ -930,8 +952,11 @@ bool InteractionHandler::invertRotation() const { } void InteractionHandler::addKeyframe(const network::Keyframe &kf){ + _keyframeMutex.lock(); _keyframes.erase(_keyframes.begin()); _keyframes.push_back(kf); + _currentKeyframeTime = _keyframes[1]._timeStamp; + _keyframeMutex.unlock(); } } // namespace interaction diff --git a/src/network/osparallelconnection.cpp b/src/network/osparallelconnection.cpp index e7c14a2fb9..3db55c5456 100644 --- a/src/network/osparallelconnection.cpp +++ b/src/network/osparallelconnection.cpp @@ -153,6 +153,13 @@ namespace openspace { int flag = 1; int result; + //set no delay + result = setsockopt(_clientSocket, /* socket affected */ + IPPROTO_TCP, /* set option at TCP level */ + TCP_NODELAY, /* name of option */ + (char *)&flag, /* the cast is historical cruft */ + sizeof(int)); /* length of option value */ + //set send timeout int timeout = 0; //infinite result = setsockopt( @@ -287,9 +294,7 @@ namespace openspace { network::Keyframe kf; kf.deserialize(buffer); -// printf("--- %f ---\n", kf._timeStamp); - OsEng.interactionHandler()->addKeyframe(kf); - + OsEng.interactionHandler()->addKeyframe(kf); } void OSParallelConnection::decodeHostInfoMessage(){ @@ -520,7 +525,10 @@ namespace openspace { network::Keyframe kf; kf._position = OsEng.interactionHandler()->camera()->position(); kf._viewRotationQuat = glm::quat_cast(OsEng.interactionHandler()->camera()->viewRotationMatrix()); - kf._timeStamp = Time::ref().currentTime(); + + //@TODO, implement method in openspace engine for this + kf._timeStamp = sgct::Engine::getTime(); + std::vector kfBuffer; kf.serialize(kfBuffer); From be21b8e43dbd594f6299c3f111a8dee053fb4c1b Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 17 Jun 2015 16:03:42 +0200 Subject: [PATCH 162/329] added quaternion interpolation --- src/interaction/interactionhandler.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index cd3ba3cd50..5a0532e996 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -358,19 +358,25 @@ void InteractionHandler::update(double deltaTime) { ghoul::Interpolator positionInterpCR; ghoul::Interpolator positionInterpLin; + ghoul::Interpolator quatInterpLin; double t0 = _keyframes[1]._timeStamp; double t1 = _keyframes[2]._timeStamp; double fact = (_currentKeyframeTime - t0) / (t1 - t0); - - glm::dvec4 v = positionInterpCR.interpolate(fact, _keyframes[0]._position.dvec4(), _keyframes[1]._position.dvec4(), _keyframes[2]._position.dvec4(), _keyframes[3]._position.dvec4()); - //glm::dvec4 v = positionInterpLin.interpolate(fact, _keyframes[1]._position.dvec4(), _keyframes[2]._position.dvec4()); + if (fact > 1.0){ + printf("%f\n", fact); + fact = fmin(1.0, fact); + } + //glm::dvec4 v = positionInterpCR.interpolate(fact, _keyframes[0]._position.dvec4(), _keyframes[1]._position.dvec4(), _keyframes[2]._position.dvec4(), _keyframes[3]._position.dvec4()); + glm::dvec4 v = positionInterpLin.interpolate(fact, _keyframes[1]._position.dvec4(), _keyframes[2]._position.dvec4()); psc pos(v.x, v.y, v.z, v.w); + glm::quat q = quatInterpLin.interpolate(fact, _keyframes[1]._viewRotationQuat, _keyframes[2]._viewRotationQuat); + //printf("---fact: %f\nT0: %f, T1%f\n, CURR:%f---\n", fact, t0, t1, _currentKeyframeTime); //printf("Fact: %f\n", fact); _camera->setPosition(pos); - _camera->setViewRotationMatrix(glm::mat4_cast(_keyframes[3]._viewRotationQuat)); + _camera->setViewRotationMatrix(glm::mat4_cast(q)); _currentKeyframeTime += deltaTime; _currentKeyframeTime = std::fmin(_currentKeyframeTime, t1); From b723751c1bccc2c2eedc5bf4ad41eebafbdc73e7 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Thu, 18 Jun 2015 09:10:15 +0200 Subject: [PATCH 163/329] added functionality to disconnect and request hostship via LUA. fixed bug in threading --- .../openspace/network/osparallelconnection.h | 7 +- src/network/osparallelconnection.cpp | 179 ++++++++++-------- src/network/osparallelconnection_lua.inl | 15 ++ 3 files changed, 123 insertions(+), 78 deletions(-) diff --git a/include/openspace/network/osparallelconnection.h b/include/openspace/network/osparallelconnection.h index 8a84120fbc..d17c5bd80d 100644 --- a/include/openspace/network/osparallelconnection.h +++ b/include/openspace/network/osparallelconnection.h @@ -105,6 +105,8 @@ namespace openspace{ void clientConnect(); + void disconnect(); + void setPort(const std::string &port); std::string port(); @@ -136,7 +138,8 @@ namespace openspace{ Initialization, Data, HostInfo, - InitializationRequest + InitializationRequest, + HostshipRequest }; /** @@ -169,8 +172,6 @@ namespace openspace{ return hashVal; }; - void disconnect(); - void closeSocket(); bool initNetworkAPI(); diff --git a/src/network/osparallelconnection.cpp b/src/network/osparallelconnection.cpp index 3db55c5456..58d3e46112 100644 --- a/src/network/osparallelconnection.cpp +++ b/src/network/osparallelconnection.cpp @@ -86,13 +86,7 @@ namespace openspace { } OSParallelConnection::~OSParallelConnection(){ - _isRunning.store(false); - - disconnect(); - - #if defined(__WIN32__) - WSACleanup(); - #endif + disconnect(); } void OSParallelConnection::clientConnect(){ @@ -136,55 +130,56 @@ namespace openspace { } void OSParallelConnection::connection(addrinfo *info){ - int result; + _clientSocket = socket(info->ai_family, info->ai_socktype, info->ai_protocol); + + if (_clientSocket == INVALID_SOCKET){ + freeaddrinfo(info); +#if defined(__WIN32__) + WSACleanup(); +#endif + std::cerr << "Failed to InitializationRequest client socket!" << std::endl; + return; + } + + int flag = 1; + int result; + + //set no delay + result = setsockopt(_clientSocket, /* socket affected */ + IPPROTO_TCP, /* set option at TCP level */ + TCP_NODELAY, /* name of option */ + (char *)&flag, /* the cast is historical cruft */ + sizeof(int)); /* length of option value */ + + //set send timeout + int timeout = 0; //infinite + result = setsockopt( + _clientSocket, + SOL_SOCKET, + SO_SNDTIMEO, + (char *)&timeout, + sizeof(timeout)); + + //set receive timeout + result = setsockopt( + _clientSocket, + SOL_SOCKET, + SO_RCVTIMEO, + (char *)&timeout, + sizeof(timeout)); + + result = setsockopt(_clientSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&flag, sizeof(int)); + if (result == SOCKET_ERROR) + std::cout << "Failed to set reuse address with error:" << _ERRNO << std::endl; + + result = setsockopt(_clientSocket, SOL_SOCKET, SO_KEEPALIVE, (char*)&flag, sizeof(int)); + if (result == SOCKET_ERROR) + std::cout << "Failed to set keep alive with error: " << _ERRNO << std::endl; + + //try to connect to server while (_isRunning.load()){ - _clientSocket = socket(info->ai_family, info->ai_socktype, info->ai_protocol); - - if (_clientSocket == INVALID_SOCKET){ - freeaddrinfo(info); - #if defined(__WIN32__) - WSACleanup(); - #endif - std::cerr << "Failed to init client socket!" << std::endl; - return; - } - - int flag = 1; - int result; - - //set no delay - result = setsockopt(_clientSocket, /* socket affected */ - IPPROTO_TCP, /* set option at TCP level */ - TCP_NODELAY, /* name of option */ - (char *)&flag, /* the cast is historical cruft */ - sizeof(int)); /* length of option value */ - - //set send timeout - int timeout = 0; //infinite - result = setsockopt( - _clientSocket, - SOL_SOCKET, - SO_SNDTIMEO, - (char *)&timeout, - sizeof(timeout)); - - //set receive timeout - result = setsockopt( - _clientSocket, - SOL_SOCKET, - SO_RCVTIMEO, - (char *)&timeout, - sizeof(timeout)); - - result = setsockopt(_clientSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&flag, sizeof(int)); - if (result == SOCKET_ERROR) - std::cout << "Failed to set reuse address with error:" << _ERRNO << std::endl; - - result = setsockopt(_clientSocket, SOL_SOCKET, SO_KEEPALIVE, (char*)&flag, sizeof(int)); - if (result == SOCKET_ERROR) - std::cout << "Failed to set keep alive with error: " << _ERRNO << std::endl; - + result = connect(_clientSocket, info->ai_addr, (int)info->ai_addrlen); if (result != SOCKET_ERROR) { @@ -198,7 +193,12 @@ namespace openspace { //one sec sleep std::this_thread::sleep_for(std::chrono::seconds(1)); } - + //make sure to join the broadcast thread if started + //dont delete it, that will be done in disconnect() function + if(_broadcastThread != nullptr && _isHost.load()){ + _isHost.store(false); + _broadcastThread->join(); + } //cleanup freeaddrinfo(info); } @@ -269,7 +269,6 @@ namespace openspace { } void OSParallelConnection::decodeDataMessage(){ - int result; uint16_t msglen; std::vector buffer; @@ -312,7 +311,7 @@ namespace openspace { //start broadcasting _isHost.store(true); _broadcastThread = new (std::nothrow) std::thread(&OSParallelConnection::broadcast, this); - } + } } else{ //we were broadcasting but should stop now @@ -439,7 +438,20 @@ namespace openspace { } void OSParallelConnection::requestHostship(){ + std::vector buffer; + buffer.reserve(headerSize + sizeof(int)); + //header + buffer.insert(buffer.end(), 'O'); + buffer.insert(buffer.end(), 'S'); + buffer.insert(buffer.end(), 0); + buffer.insert(buffer.end(), 0); + //type of message + int type = OSParallelConnection::MessageTypes::HostshipRequest; + buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(type)); + + //send message + send(_clientSocket, buffer.data(), buffer.size(), 0); } void OSParallelConnection::setPassword(const std::string& pwd){ @@ -447,23 +459,28 @@ namespace openspace { } void OSParallelConnection::disconnect(){ - _isHost.store(false); + //must be run before trying to join communication threads, else the threads are stuck trying to receive data + closeSocket(); + + _isRunning.store(false); + _isHost.store(false); + if (_connectionThread != nullptr){ + _connectionThread->join(); + delete _connectionThread; + _connectionThread = nullptr; + } + + if (_broadcastThread != nullptr){ + _broadcastThread->join(); + delete _broadcastThread; + _broadcastThread = nullptr; + } + - if (_broadcastThread != nullptr){ - _broadcastThread->join(); - delete _broadcastThread; - _broadcastThread = nullptr; - } - - _isRunning.store(false); - - if (_connectionThread != nullptr){ - _connectionThread->join(); - delete _connectionThread; - _connectionThread = nullptr; - } - - closeSocket(); + +#if defined(__WIN32__) + WSACleanup(); +#endif } void OSParallelConnection::closeSocket(){ @@ -556,8 +573,8 @@ namespace openspace { //send message send(_clientSocket, buffer.data(), buffer.size(), 0); - //100 ms sleep - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + //200 ms sleep + std::this_thread::sleep_for(std::chrono::milliseconds(200)); } } @@ -595,6 +612,18 @@ namespace openspace { "", "Connect to parallel" }, + { + "disconnect", + &luascriptfunctions::disconnect, + "", + "Disconnect from parallel" + }, + { + "requestHostship", + &luascriptfunctions::requestHostship, + "", + "Request to be the host for this session" + }, } }; } diff --git a/src/network/osparallelconnection_lua.inl b/src/network/osparallelconnection_lua.inl index a247ffa536..3237675c1a 100644 --- a/src/network/osparallelconnection_lua.inl +++ b/src/network/osparallelconnection_lua.inl @@ -132,6 +132,21 @@ int connect(lua_State* L) { return 0; } +int disconnect(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 0) + return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); + OsEng.parallelConnection()->disconnect(); + return 0; +} + +int requestHostship(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 0) + return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); + OsEng.parallelConnection()->requestHostship(); + return 0; +} } // namespace luascriptfunctions From ca3628e505ccfd289f6e2b856d9e7dda54c6d31a Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Thu, 18 Jun 2015 10:44:38 +0200 Subject: [PATCH 164/329] added buffering of samples and changed how samples are handled when received. fixed a bug where client did not regain control after host disconnected --- src/interaction/interactionhandler.cpp | 37 +++++++++++++++++--------- src/network/osparallelconnection.cpp | 7 +++++ 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index 5a0532e996..b0129794cb 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -254,7 +254,9 @@ InteractionHandler::InteractionHandler() , _invertRotation(false) , _keyboardController(nullptr) , _mouseController(nullptr) + , _currentKeyframeTime(-1.0) { + //add 4 dummy keyframes network::Keyframe kf; kf._timeStamp = -std::numeric_limits::max(); _keyframes.assign(4, kf); @@ -353,18 +355,23 @@ void InteractionHandler::update(double deltaTime) { _mouseController->update(deltaTime); _keyframeMutex.lock(); - //if the target keyframe has a valid timestamp and time within range - if (_keyframes[1]._timeStamp > 0.0){// && _currentKeyframeTime <= _keyframes[2]._timeStamp){ - + + if (_keyframes.size() > 4){ //wait until enough samples are buffered ghoul::Interpolator positionInterpCR; ghoul::Interpolator positionInterpLin; ghoul::Interpolator quatInterpLin; + + //interval check + if (_currentKeyframeTime < _keyframes[1]._timeStamp){ + _currentKeyframeTime = _keyframes[1]._timeStamp; + } + double t0 = _keyframes[1]._timeStamp; double t1 = _keyframes[2]._timeStamp; double fact = (_currentKeyframeTime - t0) / (t1 - t0); if (fact > 1.0){ printf("%f\n", fact); - fact = fmin(1.0, fact); + //fact = fmin(1.0, fact); } //glm::dvec4 v = positionInterpCR.interpolate(fact, _keyframes[0]._position.dvec4(), _keyframes[1]._position.dvec4(), _keyframes[2]._position.dvec4(), _keyframes[3]._position.dvec4()); glm::dvec4 v = positionInterpLin.interpolate(fact, _keyframes[1]._position.dvec4(), _keyframes[2]._position.dvec4()); @@ -372,19 +379,19 @@ void InteractionHandler::update(double deltaTime) { glm::quat q = quatInterpLin.interpolate(fact, _keyframes[1]._viewRotationQuat, _keyframes[2]._viewRotationQuat); - //printf("---fact: %f\nT0: %f, T1%f\n, CURR:%f---\n", fact, t0, t1, _currentKeyframeTime); - //printf("Fact: %f\n", fact); - _camera->setPosition(pos); _camera->setViewRotationMatrix(glm::mat4_cast(q)); _currentKeyframeTime += deltaTime; - _currentKeyframeTime = std::fmin(_currentKeyframeTime, t1); + + //we're done with this sample interval + if (_currentKeyframeTime >= _keyframes[2]._timeStamp){ + _keyframes.erase(_keyframes.begin()); + _currentKeyframeTime = _keyframes[1]._timeStamp; + } } + _keyframeMutex.unlock(); - -// printf("%f\n %f\n %f\n %f\n", _keyframes[0]._timeStamp, _keyframes[1]._timeStamp, _keyframes[2]._timeStamp, _keyframes[3]._timeStamp); -// printf("Current keys:\n, %s\n %s\n %s\n %s\n\n\n", _keyframes[0].to_string().c_str(), _keyframes[1].to_string().c_str(), _keyframes[2].to_string().c_str(), _keyframes[3].to_string().c_str()); } void InteractionHandler::setFocusNode(SceneGraphNode* node) { @@ -959,9 +966,13 @@ bool InteractionHandler::invertRotation() const { void InteractionHandler::addKeyframe(const network::Keyframe &kf){ _keyframeMutex.lock(); - _keyframes.erase(_keyframes.begin()); + + //save a maximum of 10 samples (1 seconds of buffer) + if (_keyframes.size() >= 10){ + _keyframes.erase(_keyframes.begin()); + } _keyframes.push_back(kf); - _currentKeyframeTime = _keyframes[1]._timeStamp; + _keyframeMutex.unlock(); } diff --git a/src/network/osparallelconnection.cpp b/src/network/osparallelconnection.cpp index 58d3e46112..dd5f9cd7e0 100644 --- a/src/network/osparallelconnection.cpp +++ b/src/network/osparallelconnection.cpp @@ -312,6 +312,13 @@ namespace openspace { _isHost.store(true); _broadcastThread = new (std::nothrow) std::thread(&OSParallelConnection::broadcast, this); } + //@TODO fix this to a more permanent solution + Keyframe kf; + kf._timeStamp = -1.0; + OsEng.interactionHandler()->addKeyframe(kf); + OsEng.interactionHandler()->addKeyframe(kf); + OsEng.interactionHandler()->addKeyframe(kf); + OsEng.interactionHandler()->addKeyframe(kf); } else{ //we were broadcasting but should stop now From 8f1f2dafa9531a12c3003cb21a22b0a6fbd9f05f Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Thu, 18 Jun 2015 10:45:36 +0200 Subject: [PATCH 165/329] changed so that 10 samples per second are sent --- src/network/osparallelconnection.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network/osparallelconnection.cpp b/src/network/osparallelconnection.cpp index 58d3e46112..87cb9530cf 100644 --- a/src/network/osparallelconnection.cpp +++ b/src/network/osparallelconnection.cpp @@ -573,8 +573,8 @@ namespace openspace { //send message send(_clientSocket, buffer.data(), buffer.size(), 0); - //200 ms sleep - std::this_thread::sleep_for(std::chrono::milliseconds(200)); + //100 ms sleep + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } From 4c06a76eaee7f3167df616819109939bfebfcc96 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Thu, 18 Jun 2015 11:04:34 +0200 Subject: [PATCH 166/329] removed dummy frames --- src/interaction/interactionhandler.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index b0129794cb..672e345cc5 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -256,10 +256,6 @@ InteractionHandler::InteractionHandler() , _mouseController(nullptr) , _currentKeyframeTime(-1.0) { - //add 4 dummy keyframes - network::Keyframe kf; - kf._timeStamp = -std::numeric_limits::max(); - _keyframes.assign(4, kf); } InteractionHandler::~InteractionHandler() { From ba49755d831777ca21d173998542475239b89dd2 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Thu, 18 Jun 2015 11:36:38 +0200 Subject: [PATCH 167/329] removed dummy frames --- src/network/osparallelconnection.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/network/osparallelconnection.cpp b/src/network/osparallelconnection.cpp index 4ce39cdbb2..87cb9530cf 100644 --- a/src/network/osparallelconnection.cpp +++ b/src/network/osparallelconnection.cpp @@ -312,13 +312,6 @@ namespace openspace { _isHost.store(true); _broadcastThread = new (std::nothrow) std::thread(&OSParallelConnection::broadcast, this); } - //@TODO fix this to a more permanent solution - Keyframe kf; - kf._timeStamp = -1.0; - OsEng.interactionHandler()->addKeyframe(kf); - OsEng.interactionHandler()->addKeyframe(kf); - OsEng.interactionHandler()->addKeyframe(kf); - OsEng.interactionHandler()->addKeyframe(kf); } else{ //we were broadcasting but should stop now From f58d6d91b5b681b64ef4278d7cdf4d888a0d2877 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 18 Jun 2015 14:29:34 +0200 Subject: [PATCH 168/329] Enable setting the capabilities verbosity using the configuration file --- apps/OpenSpace/main.cpp | 2 +- include/openspace/engine/configurationmanager.h | 1 + modules/newhorizons/util/imagesequencer2.cpp | 1 + src/engine/configurationmanager.cpp | 1 + src/engine/openspaceengine.cpp | 16 +++++++++++++++- 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/apps/OpenSpace/main.cpp b/apps/OpenSpace/main.cpp index 0edbe2edc9..3b36a36c76 100644 --- a/apps/OpenSpace/main.cpp +++ b/apps/OpenSpace/main.cpp @@ -53,7 +53,7 @@ std::pair supportedOpenGLVersion () { //On OS X we need to explicitly set the version and specify that we are using CORE profile //to be able to use glGetIntegerv(GL_MAJOR_VERSION, &major) and glGetIntegerv(GL_MINOR_VERSION, &minor) //explicitly setting to OGL 3.3 CORE works since all Mac's now support at least 3.3 -#if __APPLE__ +#ifdef __APPLE__ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); diff --git a/include/openspace/engine/configurationmanager.h b/include/openspace/engine/configurationmanager.h index bd11a2dd23..c87c9183ab 100644 --- a/include/openspace/engine/configurationmanager.h +++ b/include/openspace/engine/configurationmanager.h @@ -49,6 +49,7 @@ public: static const std::string KeyLogLevel; static const std::string KeyLogImmediateFlush; static const std::string KeyLogs; + static const std::string KeyCapabilitiesVerbosity; static const std::string KeyDisableMasterRendering; static const std::string KeyDownloadRequestURL; diff --git a/modules/newhorizons/util/imagesequencer2.cpp b/modules/newhorizons/util/imagesequencer2.cpp index aea8dd4238..cc880b7214 100644 --- a/modules/newhorizons/util/imagesequencer2.cpp +++ b/modules/newhorizons/util/imagesequencer2.cpp @@ -306,6 +306,7 @@ void ImageSequencer2::runSequenceParser(SequenceParser* parser){ std::vector in5 = parser->getCaptureProgression(); // check for sanity + // TODO: This cannot crash the program! ---abock ghoul_assert(in1.size() > 0, "Sequencer failed to load Translation" ); ghoul_assert(in2.size() > 0, "Sequencer failed to load Image data" ); ghoul_assert(in3.size() > 0, "Sequencer failed to load Instrument Switching schedule"); diff --git a/src/engine/configurationmanager.cpp b/src/engine/configurationmanager.cpp index 5c2c9bf3ed..e8a423df53 100644 --- a/src/engine/configurationmanager.cpp +++ b/src/engine/configurationmanager.cpp @@ -57,6 +57,7 @@ const std::string ConfigurationManager::KeySpiceLeapsecondKernel = "SpiceKernel. const std::string ConfigurationManager::KeyLogLevel = "Logging.LogLevel"; const std::string ConfigurationManager::KeyLogImmediateFlush = "Logging.ImmediateFlush"; const std::string ConfigurationManager::KeyLogs = "Logging.Logs"; +const std::string ConfigurationManager::KeyCapabilitiesVerbosity = "Logging.CapabilitiesVerbosity"; const std::string ConfigurationManager::KeyDisableMasterRendering = "DisableRenderingOnMaster"; const std::string ConfigurationManager::KeyDownloadRequestURL = "DownloadRequestURL"; diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 0b9ec647ba..2e1421fe2e 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -288,7 +288,21 @@ bool OpenSpaceEngine::initialize() { SysCap.addComponent(new ghoul::systemcapabilities::GeneralCapabilitiesComponent); SysCap.addComponent(new ghoul::systemcapabilities::OpenGLCapabilitiesComponent); SysCap.detectCapabilities(); - SysCap.logCapabilities(); + + using Verbosity = ghoul::systemcapabilities::SystemCapabilitiesComponent::Verbosity; + Verbosity verbosity = Verbosity::Default; + if (configurationManager()->hasKeyAndValue(ConfigurationManager::KeyCapabilitiesVerbosity)) { + std::map verbosityMap = { + { "Minimal", Verbosity::Minimal }, + { "Default", Verbosity::Default }, + { "Full", Verbosity::Full } + }; + + std::string v = configurationManager()->value(ConfigurationManager::KeyCapabilitiesVerbosity); + if (verbosityMap.find(v) != verbosityMap.end()) + verbosity = verbosityMap[v]; + } + SysCap.logCapabilities(verbosity); std::string requestURL = ""; bool success = configurationManager()->getValue(ConfigurationManager::KeyDownloadRequestURL, requestURL); From 7401b7d48bbd2b917ec23e5377024a601a3a6a8f Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 18 Jun 2015 15:42:41 +0200 Subject: [PATCH 169/329] Enable FrameBuffer-based rendering on Windows --- include/openspace/abuffer/abuffer.h | 4 +- .../openspace/abuffer/abufferframebuffer.h | 14 ++- include/openspace/rendering/renderengine.h | 15 ++- src/abuffer/abuffer.cpp | 8 -- src/abuffer/abufferframebuffer.cpp | 25 +++++ src/rendering/renderengine.cpp | 99 ++++++++++--------- src/scene/scene.cpp | 4 +- src/scene/scenegraph.cpp | 51 +++++----- 8 files changed, 129 insertions(+), 91 deletions(-) diff --git a/include/openspace/abuffer/abuffer.h b/include/openspace/abuffer/abuffer.h index f8e1550a29..86de8676d7 100644 --- a/include/openspace/abuffer/abuffer.h +++ b/include/openspace/abuffer/abuffer.h @@ -74,7 +74,7 @@ public: protected: virtual bool reinitializeInternal() = 0; - bool initializeABuffer(); + virtual bool initializeABuffer(); void generateShaderSource(); bool updateShader(); @@ -86,8 +86,6 @@ protected: unsigned int _width, _height, _totalPixels; -private: - void updateDimensions(); GLuint _screenQuad; diff --git a/include/openspace/abuffer/abufferframebuffer.h b/include/openspace/abuffer/abufferframebuffer.h index c348c777f6..f84e01cd2b 100644 --- a/include/openspace/abuffer/abufferframebuffer.h +++ b/include/openspace/abuffer/abufferframebuffer.h @@ -34,15 +34,19 @@ public: ABufferFramebuffer(); virtual ~ABufferFramebuffer(); - virtual bool initialize(); + bool initialize(); - virtual void clear(); - virtual void preRender(); - virtual void postRender(); + void clear(); + void preRender(); + void postRender(); + void resolve(); std::vector pixelData(); protected: - virtual bool reinitializeInternal(); + bool reinitializeInternal(); + + bool initializeABuffer(); + private: diff --git a/include/openspace/rendering/renderengine.h b/include/openspace/rendering/renderengine.h index 4056dc2e56..cde17ace1b 100644 --- a/include/openspace/rendering/renderengine.h +++ b/include/openspace/rendering/renderengine.h @@ -46,6 +46,14 @@ class ScreenLog; class RenderEngine { public: + enum class ABufferImplementation { + FrameBuffer = 0, + SingleLinked, + Fixed, + Dynamic, + Invalid + }; + static const std::string PerformanceMeasurementSharedData; RenderEngine(); @@ -57,7 +65,8 @@ public: Scene* scene(); Camera* camera() const; - ABuffer* abuffer() const; + ABuffer* aBuffer() const; + ABufferImplementation aBufferImplementation() const; // sgct wrapped functions bool initializeGL(); @@ -108,12 +117,14 @@ public: } _onScreenInformation; private: + ABufferImplementation aBufferFromString(const std::string& impl); + void storePerformanceMeasurements(); Camera* _mainCamera; Scene* _sceneGraph; ABuffer* _abuffer; - int _abufferImplementation; + ABufferImplementation _abufferImplementation; ScreenLog* _log; bool _showInfo; diff --git a/src/abuffer/abuffer.cpp b/src/abuffer/abuffer.cpp index 700413430d..40944fcfd9 100644 --- a/src/abuffer/abuffer.cpp +++ b/src/abuffer/abuffer.cpp @@ -89,7 +89,6 @@ bool ABuffer::initializeABuffer() { return false; _resolveShader->setProgramObjectCallback(shaderCallback); -#ifndef __APPLE__ // ============================ // GEOMETRY (quad) // ============================ @@ -111,7 +110,6 @@ bool ABuffer::initializeABuffer() { glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*4, reinterpret_cast(0)); glEnableVertexAttribArray(0); -#endif return true; } @@ -123,7 +121,6 @@ bool ABuffer::reinitialize() { } void ABuffer::resolve() { -#ifndef __APPLE__ if( ! _validShader) { generateShaderSource(); updateShader(); @@ -158,7 +155,6 @@ void ABuffer::resolve() { glDrawArrays(GL_TRIANGLES, 0, 6); _resolveShader->deactivate(); -#endif } void ABuffer::addVolume(const std::string& tag,ghoul::opengl::Texture* volume) { @@ -173,7 +169,6 @@ int ABuffer::addSamplerfile(const std::string& filename) { if( ! FileSys.fileExists(filename)) return -1; -#ifndef __APPLE__ auto fileCallback = [this](const ghoul::filesystem::File& file) { _validShader = false; }; @@ -185,9 +180,6 @@ int ABuffer::addSamplerfile(const std::string& filename) { // ID is one more than "actual" position since ID=0 is considered geometry //return 1 << (_samplers.size()-1); return static_cast(_samplers.size()); -#else - return 0; -#endif } bool ABuffer::updateShader() { diff --git a/src/abuffer/abufferframebuffer.cpp b/src/abuffer/abufferframebuffer.cpp index e2c13d7cae..3dae2f4876 100644 --- a/src/abuffer/abufferframebuffer.cpp +++ b/src/abuffer/abufferframebuffer.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -64,5 +65,29 @@ std::vector ABufferFramebuffer::pixelData() { return std::vector(); } +bool ABufferFramebuffer::initializeABuffer() { + // ============================ + // SHADERS + // ============================ + auto shaderCallback = [this](ghoul::opengl::ProgramObject* program) { + // Error for visibility in log + _validShader = false; + }; + + generateShaderSource(); + _resolveShader = ghoul::opengl::ProgramObject::Build( + "ABufferResolve", + "${SHADERS}/ABuffer/abufferResolveVertex.glsl", + "${SHADERS}/ABuffer/abufferResolveFragment.glsl"); + if (!_resolveShader) + return false; + _resolveShader->setProgramObjectCallback(shaderCallback); +} + +void ABufferFramebuffer::resolve() +{ + +} + } // openspace \ No newline at end of file diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 75e1f7ed9a..1284f8ff91 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -81,13 +81,6 @@ namespace { const std::string KeyRenderingMethod = "RenderingMethod"; const std::string DefaultRenderingMethod = "ABufferSingleLinked"; - - const std::map RenderingMethods = { - { "ABufferFrameBuffer", ABUFFER_FRAMEBUFFER}, - { "ABufferSingleLinked", ABUFFER_SINGLE_LINKED }, - { "ABufferFixed", ABUFFER_FIXED }, - { "ABufferDynamic", ABUFFER_DYNAMIC } - }; } namespace openspace { @@ -99,7 +92,7 @@ RenderEngine::RenderEngine() : _mainCamera(nullptr) , _sceneGraph(nullptr) , _abuffer(nullptr) - , _abufferImplementation(-1) + , _abufferImplementation(ABufferImplementation::Invalid) , _log(nullptr) , _showInfo(true) , _showScreenLog(true) @@ -153,33 +146,29 @@ bool RenderEngine::initialize() { } } - auto it = RenderingMethods.find(renderingMethod); - if (it == RenderingMethods.end()) { + _abufferImplementation = aBufferFromString(renderingMethod); + switch (_abufferImplementation) { + case ABufferImplementation::FrameBuffer: + LINFO("Creating ABufferFramebuffer implementation"); + _abuffer = new ABufferFramebuffer; + break; + case ABufferImplementation::SingleLinked: + LINFO("Creating ABufferSingleLinked implementation"); + _abuffer = new ABufferSingleLinked(); + break; + case ABufferImplementation::Fixed: + LINFO("Creating ABufferFixed implementation"); + _abuffer = new ABufferFixed(); + break; + case ABufferImplementation::Dynamic: + LINFO("Creating ABufferDynamic implementation"); + _abuffer = new ABufferDynamic(); + break; + case ABufferImplementation::Invalid: LFATAL("Rendering method '" << renderingMethod << "' not among the available " << "rendering methods"); return false; } - else { - _abufferImplementation = it->second; - switch (_abufferImplementation) { - case ABUFFER_FRAMEBUFFER: - LINFO("Creating ABufferFramebuffer implementation"); - _abuffer = new ABufferFramebuffer; - break; - case ABUFFER_SINGLE_LINKED: - LINFO("Creating ABufferSingleLinked implementation"); - _abuffer = new ABufferSingleLinked(); - break; - case ABUFFER_FIXED: - LINFO("Creating ABufferFixed implementation"); - _abuffer = new ABufferFixed(); - break; - case ABUFFER_DYNAMIC: - LINFO("Creating ABufferDynamic implementation"); - _abuffer = new ABufferDynamic(); - break; - } - } generateGlslConfig(); @@ -363,18 +352,20 @@ void RenderEngine::render(const glm::mat4 &projectionMatrix, const glm::mat4 &vi _abuffer->clear(); // SGCT resets certain settings -#ifndef __APPLE__ - glDisable(GL_DEPTH_TEST); - glDisable(GL_CULL_FACE); - glDisable(GL_BLEND); -#else - glEnable(GL_DEPTH_TEST); -// glDisable(GL_CULL_FACE); - glEnable(GL_CULL_FACE); -// glDisable(GL_BLEND); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -#endif + + if (_abufferImplementation == ABufferImplementation::FrameBuffer) { + glEnable(GL_DEPTH_TEST); + // glDisable(GL_CULL_FACE); + glEnable(GL_CULL_FACE); + // glDisable(GL_BLEND); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + else { + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_BLEND); + } // setup the camera for the current frame _mainCamera->setViewMatrix( @@ -692,10 +683,14 @@ Camera* RenderEngine::camera() const { return _mainCamera; } -ABuffer* RenderEngine::abuffer() const { +ABuffer* RenderEngine::aBuffer() const { return _abuffer; } +RenderEngine::ABufferImplementation RenderEngine::aBufferImplementation() const { + return _abufferImplementation; +} + float RenderEngine::globalBlackOutFactor() { return _globalBlackOutFactor; } @@ -729,7 +724,7 @@ void RenderEngine::generateGlslConfig() { << "#define ABUFFER_SINGLE_LINKED " << ABUFFER_SINGLE_LINKED << "\n" << "#define ABUFFER_FIXED " << ABUFFER_FIXED << "\n" << "#define ABUFFER_DYNAMIC " << ABUFFER_DYNAMIC << "\n" - << "#define ABUFFER_IMPLEMENTATION " << _abufferImplementation << "\n"; + << "#define ABUFFER_IMPLEMENTATION " << int(_abufferImplementation) << "\n"; // System specific #ifdef WIN32 os << "#define WIN32\n"; @@ -1247,4 +1242,18 @@ void RenderEngine::setDisableRenderingOnMaster(bool enabled) { _disableMasterRendering = enabled; } +RenderEngine::ABufferImplementation RenderEngine::aBufferFromString(const std::string& impl) { + const std::map RenderingMethods = { + { "ABufferFrameBuffer", ABufferImplementation::FrameBuffer }, + { "ABufferSingleLinked", ABufferImplementation::SingleLinked }, + { "ABufferFixed", ABufferImplementation::Fixed }, + { "ABufferDynamic", ABufferImplementation::Dynamic } + }; + + if (RenderingMethods.find(impl) != RenderingMethods.end()) + return RenderingMethods.at(impl); + else + return ABufferImplementation::Invalid; +} + }// namespace openspace diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 8e6b0c58bc..27db64793a 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -149,9 +149,7 @@ void Scene::update(const UpdateData& data) { _sceneGraphToLoad = ""; if (!success) return; -#ifndef __APPLE__ - OsEng.renderEngine()->abuffer()->invalidateABuffer(); -#endif + OsEng.renderEngine()->aBuffer()->invalidateABuffer(); } for (SceneGraphNode* node : _graph.nodes()) node->update(data); diff --git a/src/scene/scenegraph.cpp b/src/scene/scenegraph.cpp index a26ec95e0c..33a9bf607d 100644 --- a/src/scene/scenegraph.cpp +++ b/src/scene/scenegraph.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -335,31 +336,31 @@ bool SceneGraph::sortTopologically() { } } - -#ifdef __APPLE__ - auto it = std::find_if( - _topologicalSortedNodes.begin(), - _topologicalSortedNodes.end(), - [](SceneGraphNode* node) { - return node->name() == "Stars"; - } - ); - SceneGraphNode* n = *it; - _topologicalSortedNodes.erase(it); - _topologicalSortedNodes.insert(_topologicalSortedNodes.begin() + 3, n); - - it = std::find_if( - _topologicalSortedNodes.begin(), - _topologicalSortedNodes.end(), - [](SceneGraphNode* node) { - return node->name() == "MilkyWay"; - } - ); - n = *it; - _topologicalSortedNodes.erase(it); - _topologicalSortedNodes.insert(_topologicalSortedNodes.begin() + 2, n); -#endif - + + RenderEngine::ABufferImplementation i = OsEng.renderEngine()->aBufferImplementation(); + if (i == RenderEngine::ABufferImplementation::FrameBuffer) { + auto it = std::find_if( + _topologicalSortedNodes.begin(), + _topologicalSortedNodes.end(), + [](SceneGraphNode* node) { + return node->name() == "Stars"; + } + ); + SceneGraphNode* n = *it; + _topologicalSortedNodes.erase(it); + _topologicalSortedNodes.insert(_topologicalSortedNodes.begin() + 3, n); + + it = std::find_if( + _topologicalSortedNodes.begin(), + _topologicalSortedNodes.end(), + [](SceneGraphNode* node) { + return node->name() == "MilkyWay"; + } + ); + n = *it; + _topologicalSortedNodes.erase(it); + _topologicalSortedNodes.insert(_topologicalSortedNodes.begin() + 2, n); + } return true; From 4411a4e4b2700e6c22ee12c86ebc785ad513a207 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 18 Jun 2015 16:19:25 +0200 Subject: [PATCH 170/329] Disable postFX for global blackout --- apps/OpenSpace/main.cpp | 32 -------------------------------- openspace.cfg | 3 ++- 2 files changed, 2 insertions(+), 33 deletions(-) diff --git a/apps/OpenSpace/main.cpp b/apps/OpenSpace/main.cpp index 3b36a36c76..2854a29e94 100644 --- a/apps/OpenSpace/main.cpp +++ b/apps/OpenSpace/main.cpp @@ -73,12 +73,6 @@ std::pair supportedOpenGLVersion () { return { major, minor }; } -//temporary post-FX functions, TODO make a more permanent solution to this @JK -void postFXPass(); -void setupPostFX(); -GLint _postFXTexLoc; -GLint _postFXOpacityLoc; - #include namespace { @@ -195,9 +189,6 @@ void mainInitFunc() { std::cin.ignore(100); exit(EXIT_FAILURE); } - - //temporary post-FX solution, TODO add a more permanent solution @JK - setupPostFX(); } void mainPreSyncFunc() { @@ -272,26 +263,3 @@ void mainLogCallback(const char* msg){ // Remove the trailing \n that is passed along LINFOC("SGCT", message.substr(0, std::max(message.size() - 1, 0))); } - -void postFXPass(){ - glUniform1i(_postFXTexLoc, 0); - if (OsEng.isMaster()) - glUniform1f(_postFXOpacityLoc, 1.f); - else - glUniform1f(_postFXOpacityLoc, OsEng.renderEngine()->globalBlackOutFactor()); -} - -void setupPostFX(){ -#ifndef __APPLE__ - sgct::PostFX fx[1]; - sgct::ShaderProgram *shader; - fx[0].init("OpacityControl", absPath("${SHADERS}/postFX_vs.glsl"), absPath("${SHADERS}/postFX_fs.glsl")); - fx[0].setUpdateUniformsFunction(postFXPass); - shader = fx[0].getShaderProgram(); - shader->bind(); - _postFXTexLoc = shader->getUniformLocation("Tex"); - _postFXOpacityLoc = shader->getUniformLocation("Opacity"); - shader->unbind(); - _sgctEngine->addPostFX(fx[0]); -#endif -} diff --git a/openspace.cfg b/openspace.cfg index a4372eecae..c41cecff48 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -43,7 +43,8 @@ return { ImmediateFlush = true, Logs = { { Type = "HTML", FileName = "${BASE_PATH}/log.html", Append = false } - } + }, + CapabilitiesVerbosity = "Full" }, LuaDocumentationFile = { Type = "text", From bab7b5f4904ced04f70e69b4a63dab266114f633 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 18 Jun 2015 16:51:37 +0200 Subject: [PATCH 171/329] Move renderings from Anisotropic filtering to Linear filtering --- ext/ghoul | 2 +- modules/base/rendering/renderableplanet.cpp | 7 +++++-- modules/base/rendering/renderablesphere.cpp | 4 +++- .../newhorizons/rendering/renderableplaneprojection.cpp | 4 +++- .../newhorizons/rendering/renderableplanetprojection.cpp | 4 +++- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index f38a8ada58..99a25fcc8c 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit f38a8ada5843b0350610b62120e4a9579e8f96a5 +Subproject commit 99a25fcc8c05d6448b32409462a25d9cd9432637 diff --git a/modules/base/rendering/renderableplanet.cpp b/modules/base/rendering/renderableplanet.cpp index f84d7dc599..ff5fc0228b 100644 --- a/modules/base/rendering/renderableplanet.cpp +++ b/modules/base/rendering/renderableplanet.cpp @@ -231,7 +231,9 @@ void RenderablePlanet::loadTexture() { _texture->uploadTexture(); // Textures of planets looks much smoother with AnisotropicMipMap rather than linear - _texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + // TODO: AnisotropicMipMap crashes on ATI cards ---abock + //_texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + _texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); } } if (_hasNightTexture) { @@ -242,7 +244,8 @@ void RenderablePlanet::loadTexture() { if (_nightTexture) { LDEBUG("Loaded texture from '" << _nightTexturePath << "'"); _nightTexture->uploadTexture(); - _nightTexture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + _nightTexture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); + //_nightTexture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); } } } diff --git a/modules/base/rendering/renderablesphere.cpp b/modules/base/rendering/renderablesphere.cpp index ae65124ca5..18ee8780e4 100644 --- a/modules/base/rendering/renderablesphere.cpp +++ b/modules/base/rendering/renderablesphere.cpp @@ -188,7 +188,9 @@ void RenderableSphere::loadTexture() { texture->uploadTexture(); // Textures of planets looks much smoother with AnisotropicMipMap rather than linear - texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + // TODO: AnisotropicMipMap crashes on ATI cards ---abock + //texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); if (_texture) delete _texture; diff --git a/modules/newhorizons/rendering/renderableplaneprojection.cpp b/modules/newhorizons/rendering/renderableplaneprojection.cpp index bdcf032200..d2fc37adce 100644 --- a/modules/newhorizons/rendering/renderableplaneprojection.cpp +++ b/modules/newhorizons/rendering/renderableplaneprojection.cpp @@ -183,7 +183,9 @@ void RenderablePlaneProjection::loadTexture() { ghoul::opengl::Texture* texture = ghoul::io::TextureReader::ref().loadTexture(absPath(_texturePath)); if (texture) { texture->uploadTexture(); - texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + // TODO: AnisotropicMipMap crashes on ATI cards ---abock + //texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); if (_texture) delete _texture; _texture = texture; diff --git a/modules/newhorizons/rendering/renderableplanetprojection.cpp b/modules/newhorizons/rendering/renderableplanetprojection.cpp index e17d4f81bc..09ec57d116 100644 --- a/modules/newhorizons/rendering/renderableplanetprojection.cpp +++ b/modules/newhorizons/rendering/renderableplanetprojection.cpp @@ -505,7 +505,9 @@ void RenderablePlanetProjection::loadProjectionTexture(){ _textureProj = ghoul::io::TextureReader::ref().loadTexture(absPath(_projectionTexturePath)); if (_textureProj) { _textureProj->uploadTexture(); - _textureProj->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + // TODO: AnisotropicMipMap crashes on ATI cards ---abock + //_textureProj->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + _textureProj->setFilter(ghoul::opengl::Texture::FilterMode::Linear); _textureProj->setWrapping(ghoul::opengl::Texture::WrappingMode::ClampToBorder); } } From 88917f1ede551a5f43e4e4cd0b822c069d5c2d63 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 18 Jun 2015 16:57:06 +0200 Subject: [PATCH 172/329] Remove unused build GUI option from CMakeLists --- CMakeLists.txt | 3 --- support/cmake/support_macros.cmake | 8 -------- 2 files changed, 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f6735892f2..d989aee505 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,10 +43,7 @@ set_build_output_directories() configure_openspace_version(0 1 0 "prerelease-5") option(OPENSPACE_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF) - option(OPENSPACE_DISABLE_EXTERNAL_WARNINGS "Disable warnings in external libraries" ON) -option(OPENSPACE_BUILD_GUI_APPLICATIONS "Build GUI Applications" OFF) - include(src/CMakeLists.txt) create_openspace_target() diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 09a480f4f7..ac499b1d44 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -283,14 +283,6 @@ endfunction () -function(handle_option_gui) - if (OPENSPACE_BUILD_GUI_APPLICATIONS) - add_subdirectory(gui) - endif () -endfunction () - - - function (handle_option_tests) if (OPENSPACE_HAVE_TESTS) if (NOT TARGET gtest) From 7628e30e5fdafdd8a609d1ce305c80a93e351650 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 18 Jun 2015 17:06:48 +0200 Subject: [PATCH 173/329] Enable to start OpenSpace from Launcher as a detached process --- apps/Launcher/mainwindow.cpp | 278 +---------------------------------- 1 file changed, 1 insertion(+), 277 deletions(-) diff --git a/apps/Launcher/mainwindow.cpp b/apps/Launcher/mainwindow.cpp index ac0a5accc3..ff3a0539f5 100644 --- a/apps/Launcher/mainwindow.cpp +++ b/apps/Launcher/mainwindow.cpp @@ -196,7 +196,6 @@ void MainWindow::initialize() { _scenes->addItem(i.fileName()); } _syncWidget->setSceneFiles(_sceneFiles); - } void MainWindow::shortcutButtonPressed() { @@ -208,8 +207,7 @@ void MainWindow::syncButtonPressed() { } void MainWindow::startButtonPressed() { - QProcess* p = new QProcess(this); - p->start(OpenSpaceExecutable); + QProcess::startDetached(OpenSpaceExecutable); } void MainWindow::newsNetworkError() { @@ -224,277 +222,3 @@ void MainWindow::newsReadyRead() { _informationWidget->setText(news); _newsReply->deleteLater(); } - -//MainWindow::MainWindow() -// : QWidget(nullptr) -// , _configurationWidget(nullptr) -// , _timeControlWidget(nullptr) -// , _informationWidget(nullptr) -// , _timelineWidget(nullptr) -// , _socket(nullptr) -//{ -// setWindowTitle("OpenSpace Timeline"); -// -// _configurationWidget = new ConfigurationWidget(this); -// _configurationWidget->setMinimumWidth(350); -// _timeControlWidget = new ControlWidget(this); -// _timeControlWidget->setMinimumWidth(350); -// _informationWidget = new InformationWidget(this); -// _informationWidget->setMinimumWidth(350); -// _timelineWidget = new TimelineWidget(this); -// -// QGridLayout* layout = new QGridLayout; -// layout->addWidget(_configurationWidget, 0, 0); -// layout->addWidget(_timeControlWidget, 1, 0); -// layout->addWidget(_informationWidget, 2, 0); -// layout->addWidget(_timelineWidget, 0, 1, 3, 1); -// -// layout->setColumnStretch(1, 5); -// -// -// QObject::connect( -// _configurationWidget, SIGNAL(connect(QString, QString)), -// this, SLOT(onConnect(QString, QString)) -// ); -// QObject::connect( -// _configurationWidget, SIGNAL(disconnect()), -// this, SLOT(onDisconnect()) -// ); -// -// QObject::connect( -// _timeControlWidget, SIGNAL(scriptActivity(QString)), -// this, SLOT(sendScript(QString)) -// ); -// -// setLayout(layout); -// -// _configurationWidget->socketDisconnected(); -// _timeControlWidget->socketDisconnected(); -// _informationWidget->socketDisconnected(); -// _timelineWidget->socketDisconnected(); -//} -// -//MainWindow::~MainWindow() { -// delete _socket; -//} -// -//void MainWindow::onConnect(QString host, QString port) { -// delete _socket; -// -// _socket = new QTcpSocket(this); -// QObject::connect(_socket, SIGNAL(readyRead()), SLOT(readTcpData())); -// QObject::connect(_socket, SIGNAL(connected()), SLOT(onSocketConnected())); -// QObject::connect(_socket, SIGNAL(disconnected()), SLOT(onSocketDisconnected())); -// -// _socket->connectToHost(host, port.toUInt()); -//} -// -//void MainWindow::onDisconnect() { -// delete _socket; -// _socket = nullptr; -//} -// -//void MainWindow::readTcpData() { -// static const uint16_t MessageTypeStatus = 0; -// static const uint16_t MessageTypePlayBookHongKang = 2; -// static const uint16_t MessageTypePlayBookLabel = 3; -// -// QByteArray data = _socket->readAll(); -// -// if (QString(data) == "Connected to SGCT!\r\n") -// return; -// if (QString(data) == "OK\r\n") -// return; -// -// QByteArray messageTypeData = data.left(2); -// union { -// uint16_t value; -// std::array data; -// } messageType; -// std::memcpy(messageType.data.data(), messageTypeData.data(), sizeof(uint16_t)); -// -// switch (messageType.value) { -// case MessageTypeStatus: -// break; -// case MessageTypePlayBookHongKang: -// qDebug() << "Hong Kang Playbook received"; -// break; -// case MessageTypePlayBookLabel: -// qDebug() << "Label Playbook received"; -// break; -// default: -// qDebug() << "Unknown message of type '" << messageType.value << "'"; -// } -// -// switch (messageType.value) { -// case MessageTypeStatus: -// { -// if (_hasHongKangTimeline && _hasLabelTimeline) -// handleStatusMessage(data.mid(2)); -// break; -// } -// case MessageTypePlayBookHongKang: -// case MessageTypePlayBookLabel: -// { -// const char* payloadDebug = data.mid(2).data(); -// -// size_t beginning = 0; -// uint32_t size = readFromBuffer(data.mid(2).data(), beginning); -// -// //qDebug() << "Begin reading data"; -// while (_socket->waitForReadyRead() && data.size() < int(size)) { -// //qDebug() << "."; -// data = data.append(_socket->readAll()); -// //data = data.append(_socket->read(int(size) - data.size())); -// QThread::msleep(50); -// } -// //qDebug() << "Finished reading data. Handling playbook"; -// -// handlePlaybook(data.mid(2)); -// -// //qDebug() << "Finished handling playbook"; -// -// if (messageType.value == MessageTypePlayBookHongKang) -// _hasHongKangTimeline = true; -// if (messageType.value == MessageTypePlayBookLabel) -// _hasLabelTimeline = true; -// -// if (_hasHongKangTimeline && _hasLabelTimeline) { -// fullyConnected(); -// } -// -// break; -// } -// default: -// qDebug() << QString(data); -// } -// -//} -// -//void MainWindow::handleStatusMessage(QByteArray data) { -// const char* buffer = data.data(); -// -// union { -// double value; -// std::array buffer; -// } et; -// std::memmove(et.buffer.data(), buffer, sizeof(double)); -// -// std::vector timeString(24); -// std::memmove(timeString.data(), buffer + sizeof(double), 24); -// -// union { -// double value; -// std::array buffer; -// } delta; -// std::memmove(delta.buffer.data(), buffer + sizeof(double) + 24, sizeof(double)); -// -// _timeControlWidget->update( -// QString::fromStdString(std::string(timeString.begin(), timeString.end())), -// QString::number(delta.value) -// ); -// _timelineWidget->setCurrentTime(std::string(timeString.begin(), timeString.end()), et.value); -//} -// -//std::vector instrumentsFromId(uint16_t instrumentId, std::map instrumentMap) { -// std::vector results; -// for (int i = 0; i < 16; ++i) { -// uint16_t testValue = 1 << i; -// if ((testValue & instrumentId) != 0) { -// std::string t = instrumentMap.at(testValue); -// if (t.empty()) -// qDebug() << "Empty instrument"; -// results.push_back(t); -// } -// } -// return results; -//} -// -//void MainWindow::handlePlaybook(QByteArray data) { -// char* buffer = data.data(); -// size_t currentReadLocation = 0; -// -// uint32_t totalData = readFromBuffer(buffer, currentReadLocation); -// -// uint8_t nTargets = readFromBuffer(buffer, currentReadLocation); -// qDebug() << "Targets: " << nTargets; -// std::map targetMap; -// for (uint8_t i = 0; i < nTargets; ++i) { -// uint8_t id = readFromBuffer(buffer, currentReadLocation); -// std::string value = readFromBuffer(buffer, currentReadLocation); -// qDebug() << QString::fromStdString(value); -// targetMap[id] = value; -// } -// -// uint8_t nInstruments = readFromBuffer(buffer, currentReadLocation); -// qDebug() << "Instruments: " << nInstruments; -// std::map instrumentMap; -// for (uint8_t i = 0; i < nInstruments; ++i) { -// uint16_t id = readFromBuffer(buffer, currentReadLocation); -// std::string value = readFromBuffer(buffer, currentReadLocation); -// qDebug() << QString::fromStdString(value); -// instrumentMap[id] = value; -// } -// -// uint32_t nImages = readFromBuffer(buffer, currentReadLocation); -// std::vector images; -// for (uint32_t i = 0; i < nImages; ++i) { -// Image image; -// image.beginning = readFromBuffer(buffer, currentReadLocation); -// image.ending = readFromBuffer(buffer, currentReadLocation); -// -// image.beginningString = readFromBuffer(buffer, currentReadLocation); -// image.endingString = readFromBuffer(buffer, currentReadLocation); -// -// uint8_t targetId = readFromBuffer(buffer, currentReadLocation); -// uint16_t instrumentId = readFromBuffer(buffer, currentReadLocation); -// image.target = targetMap[targetId]; -// image.instruments = instrumentsFromId(instrumentId, instrumentMap); -// if (image.instruments.empty()) -// qDebug() << "Instruments were empty"; -// images.push_back(image); -// } -// -// _timelineWidget->setData(std::move(images), std::move(targetMap), std::move(instrumentMap)); -// -//} -// -//void MainWindow::sendScript(QString script) { -// if (_socket) { -// _socket->write(("0" + script + "\r\n").toLatin1()); -// //QByteArray data = (QString("0") + script).toLocal8Bit(); -// //qDebug() << data; -// //_socket->write(data); -// //QThread::msleep(25); -// } -// //_socket->write(("0" + script + "\r\n").toLatin1()); -// //_socket->write(("0" + script + "\0").toLatin1()); -//} -// -//void MainWindow::onSocketConnected() { -// _socket->write(QString("1\r\n").toLatin1()); -// //_socket->write(QString("1").toLatin1()); -// -//} -// -//void MainWindow::onSocketDisconnected() { -// _configurationWidget->socketDisconnected(); -// _timeControlWidget->socketDisconnected(); -// _informationWidget->socketDisconnected(); -// _timelineWidget->socketDisconnected(); -// -// _informationWidget->logInformation("Disconnected."); -//} -// -//std::string MainWindow::nextTarget() const { -// return _timelineWidget->nextTarget(); -//} -// -//void MainWindow::fullyConnected() { -// _informationWidget->logInformation("Connected to " + _socket->peerName() + " on port " + QString::number(_socket->peerPort()) + "."); -// -// _configurationWidget->socketConnected(); -// _timeControlWidget->socketConnected(); -// _informationWidget->socketConnected(); -// _timelineWidget->socketConnected(); -//} From 0becba370ca74e9a42f3fff8c0560c8564b607d4 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 18 Jun 2015 17:27:45 +0200 Subject: [PATCH 174/329] Remove qDebug with Ghoul Logmanager in Launcher --- apps/Launcher/syncwidget.cpp | 71 ++++++++++++------------------------ 1 file changed, 23 insertions(+), 48 deletions(-) diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index e2810681d4..5bae7dadae 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -35,7 +35,6 @@ #include #include -#include #include #include #include @@ -53,6 +52,8 @@ #include namespace { + const std::string _loggerCat = "SyncWidget"; + const int nColumns = 3; const int DownloadApplicationVersion = 1; @@ -134,8 +135,7 @@ SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) libtorrent::error_code ec; _session->listen_on(std::make_pair(20285, 20285), ec); if (ec) { - qDebug() << "Failed to open socket: " << QString::fromStdString(ec.message()); - _session = nullptr; + LFATAL("Failed to open socket: " << ec.message()); return; } _session->start_upnp(); @@ -161,9 +161,7 @@ SyncWidget::~SyncWidget() { void SyncWidget::setSceneFiles(QMap sceneFiles) { _sceneFiles = std::move(sceneFiles); - qDebug() << _sceneFiles; QStringList keys = _sceneFiles.keys(); - qDebug() << keys; for (int i = 0; i < keys.size(); ++i) { const QString& sceneName = keys[i]; @@ -197,10 +195,9 @@ void SyncWidget::clear() { } void SyncWidget::handleDirectFiles() { - qDebug() << "Direct Files"; + LDEBUG("Direct Files"); for (const DirectFile& f : _directFiles) { - - qDebug() << f.url << " -> " << f.destination; + LDEBUG(f.url.toStdString() << " -> " << f.destination.toStdString()); openspace::DownloadManager::FileFuture* future = DlManager.downloadFile( f.url.toStdString(), @@ -210,7 +207,6 @@ void SyncWidget::handleDirectFiles() { if (future) { InfoWidget* w = new InfoWidget(f.destination); _downloadLayout->insertWidget(_downloadLayout->count() - 1, w); - //_downloadLayout->addWidget(w); _futures.push_back(future); _futureInfoWidgetMap[future] = w; @@ -219,9 +215,9 @@ void SyncWidget::handleDirectFiles() { } void SyncWidget::handleFileRequest() { - qDebug() << "File Requests"; + LDEBUG("File Requests"); for (const FileRequest& f : _fileRequests) { - qDebug() << f.identifier << " (" << f.version << ")" << " -> " << f.destination; + LDEBUG(f.identifier.toStdString() << " (" << f.version << ") -> " << f.destination.toStdString()); std::string identifier = f.identifier.toStdString(); std::string path = absPath("${SCENE}/" + f.module.toStdString() + "/" + f.destination.toStdString()); @@ -234,37 +230,22 @@ void SyncWidget::handleFileRequest() { OverwriteFiles, std::bind(&SyncWidget::handleFileFutureAddition, this, std::placeholders::_1) ); - - //std::vector futures = - // DlManager.downloadRequestFiles( - // identifier, - // path, - // version, - // OverwriteFiles - // ); - - //_futures.insert(_futures.end(), futures.begin(), futures.end()); - //for (openspace::DownloadManager::FileFuture* f : futures) { - // InfoWidget* w = new InfoWidget(QString::fromStdString(f->filePath), -1); - // _downloadLayout->addWidget(w); - - // _futureInfoWidgetMap[f] = w; - //} } } void SyncWidget::handleTorrentFiles() { - qDebug() << "Torrent Files"; + LDEBUG("Torrent Files"); for (const TorrentFile& f : _torrentFiles) { + LDEBUG(f.file.toStdString() << " -> " << f.destination.toStdString()); + ghoul::filesystem::Directory d = FileSys.currentDirectory(); std::string thisDirectory = absPath("${SCENE}/" + f.module.toStdString() + "/"); FileSys.setCurrentDirectory(thisDirectory); QString file = QString::fromStdString(absPath(f.file.toStdString())); - qDebug() << file; if (!QFileInfo(file).exists()) { - qDebug() << file << " does not exist"; + LERROR(file.toStdString() << " does not exist"); continue; } @@ -277,26 +258,22 @@ void SyncWidget::handleTorrentFiles() { //p.save_path = p.save_path = absPath(f.destination.toStdString()); - - qDebug() << QString::fromStdString(p.save_path); p.ti = new libtorrent::torrent_info(file.toStdString(), ec); p.name = f.file.toStdString(); p.storage_mode = libtorrent::storage_mode_allocate; p.auto_managed = true; if (ec) { - qDebug() << QString::fromStdString(ec.message()); + LERROR(f.file.toStdString() << ": " << ec.message()); continue; } libtorrent::torrent_handle h = _session->add_torrent(p, ec); if (ec) { - qDebug() << QString::fromStdString(ec.message()); + LERROR(f.file.toStdString() << ": " << ec.message()); continue; } - libtorrent::size_type s = h.status().total_wanted; InfoWidget* w = new InfoWidget(f.file, h.status().total_wanted); _downloadLayout->insertWidget(_downloadLayout->count() - 1, w); - //_downloadLayout->addWidget(w); _torrentInfoWidgetMap[h] = w; FileSys.setCurrentDirectory(d); @@ -307,7 +284,7 @@ void SyncWidget::syncButtonPressed() { clear(); for (const QString& scene : selectedScenes()) { - qDebug() << scene; + LDEBUG(scene.toStdString()); ghoul::Dictionary sceneDictionary; ghoul::lua::loadDictionaryFromFile( scene.toStdString(), @@ -316,23 +293,18 @@ void SyncWidget::syncButtonPressed() { ghoul::Dictionary modules; bool success = sceneDictionary.getValue("Modules", modules); - qDebug() << success; QStringList modulesList; for (int i = 1; i <= modules.size(); ++i) { std::string module = modules.value(std::to_string(i)); modulesList.append(QString::fromStdString(module)); } - qDebug() << modulesList; QDir sceneDir(scene); sceneDir.cdUp(); for (QString module : modulesList) { QString dataFile = sceneDir.absoluteFilePath(module + "/" + module + ".data"); - qDebug() << module; - qDebug() << dataFile << QFileInfo(dataFile).exists(); - if (QFileInfo(dataFile).exists()) { ghoul::Dictionary dataDictionary; ghoul::lua::loadDictionaryFromFile(dataFile.toStdString(), dataDictionary); @@ -345,16 +317,18 @@ void SyncWidget::syncButtonPressed() { if (found) { for (int i = 1; i <= directDownloadFiles.size(); ++i) { if (!directDownloadFiles.hasKeyAndValue(std::to_string(i))) { - qDebug() << QString::fromStdString(FileDownloadKey) << " is not a dictionary"; + LERROR(dataFile.toStdString() << ": " << FileDownloadKey << " is not a dictionary"); continue; } ghoul::Dictionary d = directDownloadFiles.value(std::to_string(i)); if (!directDownloadFiles.hasKeyAndValue(UrlKey)) { - qDebug() << "No '" << QString::fromStdString(UrlKey); + LERROR(dataFile.toStdString() << ": No " << UrlKey); + continue; } std::string url = d.value(UrlKey); if (!directDownloadFiles.hasKeyAndValue(DestinationKey)) { - qDebug() << "No '" << QString::fromStdString(DestinationKey); + LERROR(dataFile.toStdString() << ": No " << DestinationKey); + continue; } std::string dest = d.value(DestinationKey); @@ -495,7 +469,10 @@ QStringList SyncWidget::selectedScenes() const { result.append(_sceneFiles[t]); } } - qDebug() << result; + std::string scenes; + for (QString s : result) + scenes += s.toStdString() + "; "; + LDEBUG("Downloading scenes: " << scenes); return result; } @@ -526,7 +503,6 @@ void SyncWidget::handleTimer() { for (openspace::DownloadManager::FileFuture* f : _futuresToAdd) { InfoWidget* w = new InfoWidget(QString::fromStdString(f->filePath), -1); _downloadLayout->insertWidget(_downloadLayout->count() - 1, w); - //_downloadLayout->addWidget(w); _futureInfoWidgetMap[f] = w; _futures.push_back(f); @@ -544,7 +520,6 @@ void SyncWidget::handleTimer() { w->update(static_cast(s.total_wanted_done)); if (s.state == torrent_status::finished || s.state == torrent_status::seeding) { - //_session->remove_torrent(h); _torrentInfoWidgetMap.remove(h); delete w; } From 4d6663f824767248db024643fb8ce1fded6df034 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 18 Jun 2015 22:46:24 +0200 Subject: [PATCH 175/329] Add a time-remaining counter to Launcher --- apps/Launcher/infowidget.cpp | 19 +++++++++++++ apps/Launcher/infowidget.h | 4 +++ apps/Launcher/syncwidget.cpp | 29 ++++--------------- include/openspace/engine/downloadmanager.h | 4 ++- src/engine/downloadmanager.cpp | 33 ++++++++++++++-------- 5 files changed, 52 insertions(+), 37 deletions(-) diff --git a/apps/Launcher/infowidget.cpp b/apps/Launcher/infowidget.cpp index d15fc1d506..9ddc547912 100644 --- a/apps/Launcher/infowidget.cpp +++ b/apps/Launcher/infowidget.cpp @@ -24,6 +24,8 @@ #include "infowidget.h" +#include "syncwidget.h" + #include #include #include @@ -73,6 +75,23 @@ void InfoWidget::update(float progress) { _progress->setValue(static_cast(progress * 100)); } +void InfoWidget::update(openspace::DownloadManager::FileFuture* f) { + _bytes->setText( + QString("%1 / %2") + .arg(f->currentSize) + .arg(f->totalSize) + ); + _progress->setValue(static_cast(f->progress * 100)); + + if (f->errorMessage.empty()) { + QString t = "Time remaining %1 s"; + _messages->setText(t.arg(f->secondsRemaining)); + } + else { + _messages->setText(QString::fromStdString(f->errorMessage)); + } +} + void InfoWidget::error(QString message) { _messages->setText(message); } diff --git a/apps/Launcher/infowidget.h b/apps/Launcher/infowidget.h index 7f7f1d086d..ea968194df 100644 --- a/apps/Launcher/infowidget.h +++ b/apps/Launcher/infowidget.h @@ -27,6 +27,8 @@ #include +#include + class QLabel; class QProgressBar; @@ -38,6 +40,8 @@ public: void update(int currentBytes); void update(float progress); + void update(openspace::DownloadManager::FileFuture* f); + void error(QString message); private: diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 5bae7dadae..32d126857c 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -68,9 +68,8 @@ namespace { const std::string IdentifierKey = "Identifier"; const std::string VersionKey = "Version"; - const QString DefaultSceneName = "default.scene"; - const bool OverwriteFiles = true; + const bool CleanInfoWidgets = false; } SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) @@ -107,23 +106,6 @@ SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) _downloadLayout = new QVBoxLayout(w); _downloadLayout->addStretch(100); - - - //QGroupBox* downloadBox = new QGroupBox; - - //QWidget* - - //_downloadLayout = new QVBoxLayout(area); - //area->setWidget(_downloadLayout) - - - - ////downloadBox->setMinimumSize(450, 1000); - //downloadBox->setLayout(_downloadLayout); - - //QScrollArea* area = new QScrollArea; - //area->setWidget(downloadBox); - layout->addWidget(area); } @@ -167,8 +149,7 @@ void SyncWidget::setSceneFiles(QMap sceneFiles) { QCheckBox* checkbox = new QCheckBox(sceneName); - if (sceneName == DefaultSceneName) - checkbox->setChecked(true); + checkbox->setChecked(true); _sceneLayout->addWidget(checkbox, i / nColumns, i % nColumns); } @@ -484,14 +465,14 @@ void SyncWidget::handleTimer() { for (FileFuture* f : _futures) { InfoWidget* w = _futureInfoWidgetMap[f]; - if (f->isFinished || f->isAborted) { + if (CleanInfoWidgets && (f->isFinished || f->isAborted)) { toRemove.push_back(f); _downloadLayout->removeWidget(w); _futureInfoWidgetMap.erase(f); delete w; } else - w->update(f->progress); + w->update(f); } for (FileFuture* f : toRemove) { @@ -519,7 +500,7 @@ void SyncWidget::handleTimer() { if (w) w->update(static_cast(s.total_wanted_done)); - if (s.state == torrent_status::finished || s.state == torrent_status::seeding) { + if (CleanInfoWidgets && (s.state == torrent_status::finished || s.state == torrent_status::seeding)) { _torrentInfoWidgetMap.remove(h); delete w; } diff --git a/include/openspace/engine/downloadmanager.h b/include/openspace/engine/downloadmanager.h index 7aeb3f32ed..1cb8a9d751 100644 --- a/include/openspace/engine/downloadmanager.h +++ b/include/openspace/engine/downloadmanager.h @@ -45,12 +45,14 @@ public: FileFuture(std::string file); // Values that are written by the DownloadManager to be consumed by others + long long currentSize; long long totalSize; float progress; // [0,1] + float secondsRemaining; bool isFinished; + bool isAborted; std::string filePath; std::string errorMessage; - bool isAborted; // Values set by others to be consumed by the DownloadManager bool abortDownload; diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp index edddf7a027..0b07b72116 100644 --- a/src/engine/downloadmanager.cpp +++ b/src/engine/downloadmanager.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -45,10 +46,10 @@ namespace { struct ProgressInformation { CURL* curl; openspace::DownloadManager::FileFuture* future; + std::chrono::system_clock::time_point startTime; const openspace::DownloadManager::DownloadProgressCallback* callback; }; - size_t writeData(void* ptr, size_t size, size_t nmemb, FILE* stream) { size_t written; written = fwrite(ptr, size, nmemb, stream); @@ -75,23 +76,28 @@ namespace { return 1; } + i->future->currentSize = dlnow; i->future->totalSize = dltotal; i->future->progress = static_cast(dlnow) / static_cast(dltotal); + auto now = std::chrono::system_clock::now(); + + // Compute time spent transferring. + auto transferTime = now - i->startTime; + // Compute estimated transfer time. + auto estimatedTime = transferTime / i->future->progress; + // Compute estimated time remaining. + auto timeRemaining = estimatedTime - transferTime; + + float s = std::chrono::duration_cast(timeRemaining).count(); + + i->future->secondsRemaining = s; + if (*(i->callback)) { // The callback function is a pointer to an std::function; that is the reason // for the excessive referencing (*(i->callback))(*(i->future)); } - - - //CURL* curl = myp->curl; - //double curtime = 0; - //curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &curtime); - //fprintf(stderr, "UP: %" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T - // " DOWN: %" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T - // "\r\n", - // ulnow, ultotal, dlnow, dltotal); return 0; } @@ -100,12 +106,14 @@ namespace { namespace openspace { DownloadManager::FileFuture::FileFuture(std::string file) - : totalSize(-1) + : currentSize(-1) + , totalSize(-1) , progress(0.f) + , secondsRemaining(-1.f) , isFinished(false) + , isAborted(false) , filePath(std::move(file)) , errorMessage("") - , isAborted(false) , abortDownload(false) {} @@ -146,6 +154,7 @@ DownloadManager::FileFuture* DownloadManager::downloadFile( ProgressInformation p = { curl, future, + std::chrono::system_clock::now(), &progressCallback }; curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferinfo); From cc76be6602ca6ffc12b580d7adb058f77ad195af Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 18 Jun 2015 23:45:08 +0200 Subject: [PATCH 176/329] Update infowidgets in Launcher based on FileFutures and torrent_handles --- apps/Launcher/infowidget.cpp | 76 ++++++++++++++++++++++++---------- apps/Launcher/infowidget.h | 11 ++--- apps/Launcher/syncwidget.cpp | 12 ++---- src/engine/downloadmanager.cpp | 3 -- 4 files changed, 63 insertions(+), 39 deletions(-) diff --git a/apps/Launcher/infowidget.cpp b/apps/Launcher/infowidget.cpp index 9ddc547912..95a0e02700 100644 --- a/apps/Launcher/infowidget.cpp +++ b/apps/Launcher/infowidget.cpp @@ -31,7 +31,7 @@ #include InfoWidget::InfoWidget(QString name, int totalBytes) - : QWidget(nullptr) + : QGroupBox(nullptr) , _name(nullptr) , _bytes(nullptr) , _progress(nullptr) @@ -41,39 +41,40 @@ InfoWidget::InfoWidget(QString name, int totalBytes) setFixedHeight(100); QGridLayout* layout = new QGridLayout; + layout->setVerticalSpacing(0); + layout->setHorizontalSpacing(10); + layout->setContentsMargins(0, 0, 0, 0); _name = new QLabel(name); layout->addWidget(_name, 0, 0); _bytes = new QLabel(""); - layout->addWidget(_bytes, 0, 1); + layout->addWidget(_bytes, 1, 0); _progress = new QProgressBar; - layout->addWidget(_progress, 0, 2); + layout->addWidget(_progress, 1, 1); _messages = new QLabel(""); - layout->addWidget(_messages, 1, 0, 1, 3); + layout->addWidget(_messages, 2, 0, 1, 2); setLayout(layout); - - update(0); } -void InfoWidget::update(int currentBytes) { - _bytes->setText( - QString("%1 / %2") - .arg(currentBytes) - .arg(_totalBytes) - ); - - float progress = static_cast(currentBytes) / static_cast(_totalBytes); - _progress->setValue(static_cast(progress * 100)); -} - -void InfoWidget::update(float progress) { - _bytes->setText(""); - _progress->setValue(static_cast(progress * 100)); -} +//void InfoWidget::update(int currentBytes) { +// _bytes->setText( +// QString("%1 / %2") +// .arg(currentBytes) +// .arg(_totalBytes) +// ); +// +// float progress = static_cast(currentBytes) / static_cast(_totalBytes); +// _progress->setValue(static_cast(progress * 100)); +//} +// +//void InfoWidget::update(float progress) { +// _bytes->setText(""); +// _progress->setValue(static_cast(progress * 100)); +//} void InfoWidget::update(openspace::DownloadManager::FileFuture* f) { _bytes->setText( @@ -85,13 +86,44 @@ void InfoWidget::update(openspace::DownloadManager::FileFuture* f) { if (f->errorMessage.empty()) { QString t = "Time remaining %1 s"; - _messages->setText(t.arg(f->secondsRemaining)); + _messages->setText(t.arg(static_cast(f->secondsRemaining))); } else { _messages->setText(QString::fromStdString(f->errorMessage)); } } +void InfoWidget::update(libtorrent::torrent_status s) { + _bytes->setText( + QString("%1 / %2") + .arg(s.total_wanted_done) + .arg(s.total_wanted) + ); + float progress = static_cast(s.total_wanted_done) / s.total_wanted; + _progress->setValue(static_cast(progress * 100)); + + if (s.error.empty()) { + int bytesPerSecond = s.download_rate; + long long remainingBytes = s.total_wanted - s.total_wanted_done; + if (remainingBytes > 0) { + float seconds = static_cast(remainingBytes) / remainingBytes; + + //auto now = time(NULL); + //auto transferTime = now - s.added_time; + //auto estimatedTime = transferTime / progress; + //auto timeRemaining = estimatedTime - transferTime; + QString t = "Time remaining %1 s"; + _messages->setText(t.arg(static_cast(seconds))); + } + else + _messages->setText(""); + } + else { + _messages->setText(QString::fromStdString(s.error)); + } + +} + void InfoWidget::error(QString message) { _messages->setText(message); } diff --git a/apps/Launcher/infowidget.h b/apps/Launcher/infowidget.h index ea968194df..654408fc6d 100644 --- a/apps/Launcher/infowidget.h +++ b/apps/Launcher/infowidget.h @@ -25,22 +25,23 @@ #ifndef __INFOWIDGET_H__ #define __INFOWIDGET_H__ -#include +//#include +#include #include +#include + class QLabel; class QProgressBar; -class InfoWidget : public QWidget { +class InfoWidget : public QGroupBox { Q_OBJECT public: InfoWidget(QString name, int totalBytes = -1); - void update(int currentBytes); - void update(float progress); - void update(openspace::DownloadManager::FileFuture* f); + void update(libtorrent::torrent_status s); void error(QString message); diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 32d126857c..9f23e35368 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -104,6 +104,8 @@ SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) area->setWidget(w); _downloadLayout = new QVBoxLayout(w); + _downloadLayout->setMargin(0); + _downloadLayout->setSpacing(0); _downloadLayout->addStretch(100); layout->addWidget(area); @@ -498,7 +500,7 @@ void SyncWidget::handleTimer() { InfoWidget* w = _torrentInfoWidgetMap[h]; if (w) - w->update(static_cast(s.total_wanted_done)); + w->update(s); if (CleanInfoWidgets && (s.state == torrent_status::finished || s.state == torrent_status::seeding)) { _torrentInfoWidgetMap.remove(h); @@ -574,12 +576,4 @@ void SyncWidget::handleFileFutureAddition( while (_mutex.test_and_set()) {} _futuresToAdd.insert(_futuresToAdd.end(), futures.begin(), futures.end()); _mutex.clear(); - //_futures.insert(_futures.end(), futures.begin(), futures.end()); - //for (openspace::DownloadManager::FileFuture* f : futures) { - // InfoWidget* w = new InfoWidget(QString::fromStdString(f->filePath), -1); - // _downloadLayout->addWidget(w); - - // _futureInfoWidgetMap[f] = w; - //} - } diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp index 0b07b72116..ddbccd935d 100644 --- a/src/engine/downloadmanager.cpp +++ b/src/engine/downloadmanager.cpp @@ -44,7 +44,6 @@ namespace { const std::string RequestApplicationVersion = "application_version"; struct ProgressInformation { - CURL* curl; openspace::DownloadManager::FileFuture* future; std::chrono::system_clock::time_point startTime; const openspace::DownloadManager::DownloadProgressCallback* callback; @@ -67,7 +66,6 @@ namespace { ghoul_assert(p, "Passed progress information is nullptr"); ProgressInformation* i = static_cast(p); ghoul_assert(i, "Passed pointer is not a ProgressInformation"); - ghoul_assert(i->curl, "CURL pointer is nullptr"); ghoul_assert(i->future, "FileFuture is not initialized"); ghoul_assert(i->callback, "Callback pointer is nullptr"); @@ -152,7 +150,6 @@ DownloadManager::FileFuture* DownloadManager::downloadFile( curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData); ProgressInformation p = { - curl, future, std::chrono::system_clock::now(), &progressCallback From 77e668a1b6a1bf38d10ed9b09423161d7b2cce4a Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 19 Jun 2015 00:19:31 +0200 Subject: [PATCH 177/329] Correctly handle path tokens in download destinations --- apps/Launcher/infowidget.cpp | 16 ---------------- apps/Launcher/mainwindow.cpp | 4 +++- apps/Launcher/syncwidget.cpp | 14 +++++++++++--- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/apps/Launcher/infowidget.cpp b/apps/Launcher/infowidget.cpp index 95a0e02700..04f8ca49f6 100644 --- a/apps/Launcher/infowidget.cpp +++ b/apps/Launcher/infowidget.cpp @@ -60,22 +60,6 @@ InfoWidget::InfoWidget(QString name, int totalBytes) setLayout(layout); } -//void InfoWidget::update(int currentBytes) { -// _bytes->setText( -// QString("%1 / %2") -// .arg(currentBytes) -// .arg(_totalBytes) -// ); -// -// float progress = static_cast(currentBytes) / static_cast(_totalBytes); -// _progress->setValue(static_cast(progress * 100)); -//} -// -//void InfoWidget::update(float progress) { -// _bytes->setText(""); -// _progress->setValue(static_cast(progress * 100)); -//} - void InfoWidget::update(openspace::DownloadManager::FileFuture* f) { _bytes->setText( QString("%1 / %2") diff --git a/apps/Launcher/mainwindow.cpp b/apps/Launcher/mainwindow.cpp index ff3a0539f5..115b823dd1 100644 --- a/apps/Launcher/mainwindow.cpp +++ b/apps/Launcher/mainwindow.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -171,8 +172,9 @@ void MainWindow::initialize() { _syncWidget->setWindowModality(Qt::WindowModal); _syncWidget->hide(); - ghoul::logging::LogManager::initialize(ghoul::logging::LogManager::LogLevel::Error); + ghoul::logging::LogManager::initialize(ghoul::logging::LogManager::LogLevel::Debug); LogMgr.addLog(new ghoul::logging::ConsoleLog); + LogMgr.addLog(new ghoul::logging::HTMLLog("LauncherLog.html")); LogMgr.addLog(new QLog); std::string configurationFile = _configurationFile; diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 9f23e35368..047ac456de 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -68,8 +68,8 @@ namespace { const std::string IdentifierKey = "Identifier"; const std::string VersionKey = "Version"; - const bool OverwriteFiles = true; - const bool CleanInfoWidgets = false; + const bool OverwriteFiles = false; + const bool CleanInfoWidgets = true; } SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) @@ -202,8 +202,13 @@ void SyncWidget::handleFileRequest() { for (const FileRequest& f : _fileRequests) { LDEBUG(f.identifier.toStdString() << " (" << f.version << ") -> " << f.destination.toStdString()); + ghoul::filesystem::Directory d = FileSys.currentDirectory(); + std::string thisDirectory = absPath("${SCENE}/" + f.module.toStdString() + "/"); + FileSys.setCurrentDirectory(thisDirectory); + + std::string identifier = f.identifier.toStdString(); - std::string path = absPath("${SCENE}/" + f.module.toStdString() + "/" + f.destination.toStdString()); + std::string path = absPath(f.destination.toStdString()); int version = f.version; DlManager.downloadRequestFilesAsync( @@ -213,6 +218,8 @@ void SyncWidget::handleFileRequest() { OverwriteFiles, std::bind(&SyncWidget::handleFileFutureAddition, this, std::placeholders::_1) ); + + FileSys.setCurrentDirectory(d); } } @@ -282,6 +289,7 @@ void SyncWidget::syncButtonPressed() { std::string module = modules.value(std::to_string(i)); modulesList.append(QString::fromStdString(module)); } + modulesList.append("common"); QDir sceneDir(scene); sceneDir.cdUp(); From 45f11048148e972963c9214ca7a53ceb6a6158ca Mon Sep 17 00:00:00 2001 From: Anton Arbring Date: Sun, 21 Jun 2015 19:59:15 -0400 Subject: [PATCH 178/329] Some cleanup in planeprojection --- .../rendering/renderableplaneprojection.cpp | 36 ++++++++++++------- .../rendering/renderableplaneprojection.h | 2 ++ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/modules/newhorizons/rendering/renderableplaneprojection.cpp b/modules/newhorizons/rendering/renderableplaneprojection.cpp index bdcf032200..b3fb865b5b 100644 --- a/modules/newhorizons/rendering/renderableplaneprojection.cpp +++ b/modules/newhorizons/rendering/renderableplaneprojection.cpp @@ -44,6 +44,7 @@ namespace { const std::string KeyMoving = "Moving"; const std::string KeyTexture = "Texture"; const std::string KeyName = "Name"; + const std::string KeyTarget = "DefaultTarget"; const std::string GalacticFrame = "GALACTIC"; const double REALLY_FAR = 99999999999; } @@ -61,11 +62,14 @@ RenderablePlaneProjection::RenderablePlaneProjection(const ghoul::Dictionary& di , _vertexPositionBuffer(0) , _name("ImagePlane") , _previousTime(0) + , _moving(false) + , _hasImage(false) { dictionary.getValue(KeySpacecraft, _spacecraft); dictionary.getValue(KeyInstrument, _instrument); dictionary.getValue(KeyMoving, _moving); dictionary.getValue(KeyName, _name); + dictionary.getValue(KeyTarget, _defaultTarget); std::string texturePath = ""; bool success = dictionary.getValue(KeyTexture, _texturePath); @@ -103,7 +107,7 @@ bool RenderablePlaneProjection::initialize() { if (!_shader) return false; } - setTarget("JUPITER"); + setTarget(_defaultTarget); loadTexture(); return isReady(); } @@ -118,7 +122,10 @@ bool RenderablePlaneProjection::deinitialize() { } void RenderablePlaneProjection::render(const RenderData& data) { - + bool active = ImageSequencer2::ref().instrumentActive(_instrument); + if (!_hasImage || (_moving && !active)) + return; + glm::mat4 transform = glm::mat4(1.0); for (int i = 0; i < 3; i++){ @@ -151,20 +158,21 @@ void RenderablePlaneProjection::update(const UpdateData& data) { double time = data.time; const Image img = openspace::ImageSequencer2::ref().getLatestImageForInstrument(_instrument); + + if (img.path == "") + return; + else + _hasImage = true; openspace::SpiceManager::ref().getPositionTransformMatrix(_target.frame, GalacticFrame, time, _stateMatrix); - double timePast = 0.0; - if (img.path != "") - { - timePast = abs(img.startTime - _previousTime); - } + double timePast = abs(img.startTime - _previousTime); std::string tex = _texturePath; - if (img.path != "" && (_moving || _planeIsDirty)) + if (_moving || _planeIsDirty) updatePlane(img, time); - else if (img.path != "" && timePast > DBL_EPSILON) { + else if (timePast > DBL_EPSILON) { _previousTime = time = img.startTime; updatePlane(img, time); } @@ -201,10 +209,12 @@ void RenderablePlaneProjection::updatePlane(const Image img, double currentTime) std::vector bounds; glm::dvec3 boresight; - std::string target = "JUPITER"; //default - if (!_moving) { - target = findClosestTarget(currentTime); - } + std::string target = _defaultTarget; + // Turned on if the plane should be attached to the closest target, + // rather than the target specified in img + //if (!_moving) { + // target = findClosestTarget(currentTime); + //} if (img.path != "") target = img.target; diff --git a/modules/newhorizons/rendering/renderableplaneprojection.h b/modules/newhorizons/rendering/renderableplaneprojection.h index 3239a26958..027a48940f 100644 --- a/modules/newhorizons/rendering/renderableplaneprojection.h +++ b/modules/newhorizons/rendering/renderableplaneprojection.h @@ -87,11 +87,13 @@ private: GLuint _vertexPositionBuffer; std::string _spacecraft; std::string _instrument; + std::string _defaultTarget; double _previousTime; target _target; std::string _name; bool _moving; + bool _hasImage; }; } // namespace openspace From 5e0756fa053d94a6f7b6d3aaacd1ff94c466db38 Mon Sep 17 00:00:00 2001 From: Anton Arbring Date: Sun, 21 Jun 2015 20:01:44 -0400 Subject: [PATCH 179/329] Fixed trail start/stop time bug --- modules/base/rendering/renderabletrail.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/modules/base/rendering/renderabletrail.cpp b/modules/base/rendering/renderabletrail.cpp index 7fdb52c9e2..ed9926dba7 100644 --- a/modules/base/rendering/renderabletrail.cpp +++ b/modules/base/rendering/renderabletrail.cpp @@ -274,7 +274,7 @@ void RenderableTrail::fullYearSweep(double time) { double start = DBL_MIN; double end = DBL_MAX; if (intervalSet) { - getInterval(start, end); + intervalSet &= getInterval(start, end); } _increment = planetYear / _tropic; @@ -284,10 +284,14 @@ void RenderableTrail::fullYearSweep(double time) { psc pscPos; _vertexArray.resize(segments+2); for (int i = 0; i < segments+2; i++) { - //if (start > time) - // time = start; - //else if (end < time) - // time = end; + if (start > time && intervalSet){ + //std::cout << _target << " hasnt started at " << time << " it does at: " << start << std::endl; + time = start; + } + else if (end < time && intervalSet){ + //std::cout << _target << " has ended at " << time << " it does at: " << end << std::endl; + time = end; + } SpiceManager::ref().getTargetPosition(_target, _observer, _frame, "NONE", time, pscPos, lightTime); pscPos[3] += 3; From 083f2948e9b6eeab05afdae84be834856c9a8187 Mon Sep 17 00:00:00 2001 From: Anton Arbring Date: Sun, 21 Jun 2015 20:03:47 -0400 Subject: [PATCH 180/329] Fixed FoV crash when the input is incorrect --- modules/newhorizons/rendering/renderablefov.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/newhorizons/rendering/renderablefov.cpp b/modules/newhorizons/rendering/renderablefov.cpp index bc5f378de9..7d2a465dba 100644 --- a/modules/newhorizons/rendering/renderablefov.cpp +++ b/modules/newhorizons/rendering/renderablefov.cpp @@ -523,7 +523,7 @@ void RenderableFov::render(const RenderData& data) { _interceptTag[bounds.size()] = _interceptTag[0]; - if (!(_instrumentID == "NH_LORRI")) // image plane replaces fov square + if (!(_instrumentID == "NH_LORRI") && !(_instrumentID == "ROS_NAVCAM-A")) // image plane replaces fov square fovProjection(_interceptTag, bounds); updateData(); @@ -537,7 +537,11 @@ void RenderableFov::render(const RenderData& data) { _time, position, lt); - + pss length = position.length(); + if (length[0] < DBL_EPSILON) { + drawFOV = false; + return; + } //if aimed 80 deg away from target, dont draw white square if (glm::dot(glm::normalize(aim), glm::normalize(position.vec3())) < 0.2){ drawFOV = false; From e5d6bef5c90b32f28247b5016eac085554dca9da Mon Sep 17 00:00:00 2001 From: Anton Arbring Date: Sun, 21 Jun 2015 20:05:25 -0400 Subject: [PATCH 181/329] Changed how translations are found to avoid crash with broken lbl files --- modules/newhorizons/util/labelparser.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/newhorizons/util/labelparser.cpp b/modules/newhorizons/util/labelparser.cpp index 2331ab586c..7911d90cdb 100644 --- a/modules/newhorizons/util/labelparser.cpp +++ b/modules/newhorizons/util/labelparser.cpp @@ -101,8 +101,7 @@ std::string LabelParser::decode(std::string line){ for (auto key : _fileTranslation){ std::size_t value = line.find(key.first); if (value != std::string::npos){ - std::string toTranslate = line.substr(value); - return _fileTranslation[toTranslate]->getTranslation()[0]; //lbls always 1:1 -> single value return. + return _fileTranslation[key.first]->getTranslation()[0]; //lbls always 1:1 -> single value return. } } return ""; From 6f72010725d3694714e0c588dabee8b9aec103eb Mon Sep 17 00:00:00 2001 From: Anton Arbring Date: Sun, 21 Jun 2015 20:11:19 -0400 Subject: [PATCH 182/329] Changes to facilitate model projection, also added posibility to define model exaggeration per obj --- modules/base/rendering/modelgeometry.cpp | 25 ++++- modules/base/rendering/modelgeometry.h | 110 ++++++++++--------- modules/base/rendering/wavefrontgeometry.cpp | 21 ++-- modules/base/rendering/wavefrontgeometry.h | 25 +++-- 4 files changed, 106 insertions(+), 75 deletions(-) diff --git a/modules/base/rendering/modelgeometry.cpp b/modules/base/rendering/modelgeometry.cpp index 7630c78c30..64b3d42ba8 100644 --- a/modules/base/rendering/modelgeometry.cpp +++ b/modules/base/rendering/modelgeometry.cpp @@ -36,6 +36,8 @@ namespace { const std::string keyObjFile = "ObjFile"; const int8_t CurrentCacheVersion = 3; const std::string keyType = "Type"; + const std::string keyName = "Name"; + const std::string keySize = "Magnification"; } namespace openspace { @@ -67,12 +69,15 @@ ModelGeometry::ModelGeometry(const ghoul::Dictionary& dictionary) , _mode(GL_TRIANGLES) { setName("ModelGeometry"); - using constants::scenegraphnode::keyName; std::string name; bool success = dictionary.getValue(keyName, name); ghoul_assert(success, "Name tag was not present"); + success = dictionary.getValue(keySize, _magnification); + if (!success) + _magnification = 4; // if not set, models will be 1:1000, feel free to change @AA + success = dictionary.getValue(keyObjFile, _file); if (!success) { LERROR("WaveFrontGeometry of '" << name << "' did not provide a key '" @@ -99,7 +104,7 @@ void ModelGeometry::changeRenderMode(const GLenum mode){ _mode = mode; } -bool ModelGeometry::initialize(RenderableModel* parent) { +bool ModelGeometry::initialize(Renderable* parent) { _parent = parent; PowerScaledScalar ps = PowerScaledScalar(1.0, 0.0); // will set proper bounding soon. _parent->setBoundingSphere(ps); @@ -225,5 +230,21 @@ bool ModelGeometry::loadCachedFile(const std::string& filename) { } } +bool ModelGeometry::getVertices(std::vector* vertexList) { + vertexList->clear(); + for (auto v : _vertices) + vertexList->push_back(v); + + return !(vertexList->empty()); +} + +bool ModelGeometry::getIndices(std::vector* indexList) { + indexList->clear(); + for (auto i : _indices) + indexList->push_back(i); + + return !(indexList->empty()); +} + } // namespace modelgeometry } // namespace openspace diff --git a/modules/base/rendering/modelgeometry.h b/modules/base/rendering/modelgeometry.h index b184ee172b..6419b4cf17 100644 --- a/modules/base/rendering/modelgeometry.h +++ b/modules/base/rendering/modelgeometry.h @@ -1,26 +1,26 @@ /***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2015 * - * * - * 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. * - ****************************************************************************************/ +* * +* OpenSpace * +* * +* Copyright (c) 2014-2015 * +* * +* Permission is hereby granted, free of charge, to any person obtaining a copy of this * +* software and associated documentation files (the "Software"), to deal in the Software * +* without restriction, including without limitation the rights to use, copy, modify, * +* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * +* permit persons to whom the Software is furnished to do so, subject to the following * +* conditions: * +* * +* The above copyright notice and this permission notice shall be included in all copies * +* or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * +* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * +* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * +* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * +* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * +****************************************************************************************/ #ifndef __MODELGEOMETRY_H__ #define __MODELGEOMETRY_H__ @@ -31,43 +31,47 @@ namespace openspace { -namespace modelgeometry { + namespace modelgeometry { -class ModelGeometry : public properties::PropertyOwner { -public: - static ModelGeometry* createFromDictionary(const ghoul::Dictionary& dictionary); + class ModelGeometry : public properties::PropertyOwner { + public: + static ModelGeometry* createFromDictionary(const ghoul::Dictionary& dictionary); - ModelGeometry(const ghoul::Dictionary& dictionary); - virtual ~ModelGeometry(); - virtual bool initialize(RenderableModel* parent); - virtual void deinitialize(); - void render(); - virtual bool loadModel(const std::string& filename) = 0; - void changeRenderMode(const GLenum mode); + struct Vertex { + GLfloat location[4]; + GLfloat tex[2]; + GLfloat normal[3]; + }; -protected: - RenderableModel* _parent; - struct Vertex { - GLfloat location[4]; - GLfloat tex[2]; - GLfloat normal[3]; - }; + ModelGeometry(const ghoul::Dictionary& dictionary); + virtual ~ModelGeometry(); + virtual bool initialize(Renderable* parent); + virtual void deinitialize(); + void render(); + virtual bool loadModel(const std::string& filename) = 0; + void changeRenderMode(const GLenum mode); + bool ModelGeometry::getVertices(std::vector* vertexList); + bool ModelGeometry::getIndices(std::vector* indexList); - bool loadObj(const std::string& filename); - bool loadCachedFile(const std::string& filename); - bool saveCachedFile(const std::string& filename); - - GLuint _vaoID; - GLuint _vbo; - GLuint _ibo; - GLenum _mode; + protected: + Renderable* _parent; - std::vector _vertices; - std::vector _indices; - std::string _file; -}; + bool loadObj(const std::string& filename); + bool loadCachedFile(const std::string& filename); + bool saveCachedFile(const std::string& filename); + int _magnification; -} // namespace modelgeometry + GLuint _vaoID; + GLuint _vbo; + GLuint _ibo; + GLenum _mode; + + std::vector _vertices; + std::vector _indices; + std::string _file; + }; + + } // namespace modelgeometry } // namespace openspace #endif // __MODELGEOMETRY_H__ diff --git a/modules/base/rendering/wavefrontgeometry.cpp b/modules/base/rendering/wavefrontgeometry.cpp index ae308b01be..4717d6615a 100644 --- a/modules/base/rendering/wavefrontgeometry.cpp +++ b/modules/base/rendering/wavefrontgeometry.cpp @@ -43,7 +43,7 @@ WavefrontGeometry::WavefrontGeometry(const ghoul::Dictionary& dictionary) loadObj(_file); } -bool WavefrontGeometry::initialize(RenderableModel* parent) { +bool WavefrontGeometry::initialize(Renderable* parent) { bool success = ModelGeometry::initialize(parent); return success; } @@ -88,13 +88,18 @@ bool WavefrontGeometry::loadModel(const std::string& filename) { // The _shapeCounts array stores for each shape, how many vertices that shape has size_t currentPosition = 0; size_t p = 0; - for (int i = 0; i < shapes.size(); ++i) { - for (int j = 0; j < shapes[i].mesh.positions.size() / 3; ++j) { - _vertices[j + currentPosition].location[0] = shapes[i].mesh.positions[3 * j + 0]; - _vertices[j + currentPosition].location[1] = shapes[i].mesh.positions[3 * j + 1]; - _vertices[j + currentPosition].location[2] = shapes[i].mesh.positions[3 * j + 2]; - _vertices[j + currentPosition].location[3] = 4; // Temp size for the power scale coordinate. - // Could be defined per object as a dictionary key. + psc tmp; + for (int i = 0; i < shapes.size(); ++i) { + for (int j = 0; j < shapes[i].mesh.positions.size() / 3; ++j) { + tmp = PowerScaledCoordinate::CreatePowerScaledCoordinate(shapes[i].mesh.positions[3 * j + 0], + shapes[i].mesh.positions[3 * j + 1], + shapes[i].mesh.positions[3 * j + 2] + ); + + _vertices[j + currentPosition].location[0] = tmp[0]; + _vertices[j + currentPosition].location[1] = tmp[1]; + _vertices[j + currentPosition].location[2] = tmp[2]; + _vertices[j + currentPosition].location[3] = tmp[3] + _magnification; _vertices[j + currentPosition].normal[0] = shapes[i].mesh.normals[3 * j + 0]; _vertices[j + currentPosition].normal[1] = shapes[i].mesh.normals[3 * j + 1]; diff --git a/modules/base/rendering/wavefrontgeometry.h b/modules/base/rendering/wavefrontgeometry.h index aa61388ac6..07c01b9a73 100644 --- a/modules/base/rendering/wavefrontgeometry.h +++ b/modules/base/rendering/wavefrontgeometry.h @@ -29,22 +29,23 @@ namespace openspace { -class RenderableModel; + class RenderableModel; + class RenderableModelProjection; -namespace modelgeometry { + namespace modelgeometry { -class WavefrontGeometry : public ModelGeometry { -public: - WavefrontGeometry(const ghoul::Dictionary& dictionary); + class WavefrontGeometry : public ModelGeometry { + public: + WavefrontGeometry(const ghoul::Dictionary& dictionary); - bool initialize(RenderableModel* parent) override; - void deinitialize() override; - -private: - bool loadModel(const std::string& filename); -}; + bool initialize(Renderable* parent) override; + void deinitialize() override; -} // namespace modelgeometry + private: + bool loadModel(const std::string& filename); + }; + + } // namespace modelgeometry } // namespace openspace #endif // __WAVEFRONTOBJECT_H__ From ad55309b0e34690c40a94233193727a020e050cc Mon Sep 17 00:00:00 2001 From: Anton Arbring Date: Sun, 21 Jun 2015 20:13:44 -0400 Subject: [PATCH 183/329] Cleaning up my own mess --- modules/base/rendering/renderabletrail.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/base/rendering/renderabletrail.cpp b/modules/base/rendering/renderabletrail.cpp index ed9926dba7..87165d0b45 100644 --- a/modules/base/rendering/renderabletrail.cpp +++ b/modules/base/rendering/renderabletrail.cpp @@ -285,11 +285,9 @@ void RenderableTrail::fullYearSweep(double time) { _vertexArray.resize(segments+2); for (int i = 0; i < segments+2; i++) { if (start > time && intervalSet){ - //std::cout << _target << " hasnt started at " << time << " it does at: " << start << std::endl; time = start; } else if (end < time && intervalSet){ - //std::cout << _target << " has ended at " << time << " it does at: " << end << std::endl; time = end; } From c7a3048dda737f0ba9d22000bf23f8a280e13b48 Mon Sep 17 00:00:00 2001 From: Anton Arbring Date: Sun, 21 Jun 2015 20:23:40 -0400 Subject: [PATCH 184/329] Fix Jenkins error and minor cleanup --- modules/base/rendering/modelgeometry.cpp | 4 ++-- modules/base/rendering/modelgeometry.h | 4 ++-- modules/base/rendering/renderabletrail.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/base/rendering/modelgeometry.cpp b/modules/base/rendering/modelgeometry.cpp index 64b3d42ba8..0dd31dea6c 100644 --- a/modules/base/rendering/modelgeometry.cpp +++ b/modules/base/rendering/modelgeometry.cpp @@ -100,7 +100,7 @@ void ModelGeometry::render() { glBindVertexArray(0); } -void ModelGeometry::changeRenderMode(const GLenum mode){ +void ModelGeometry::changeRenderMode(const GLenum mode) { _mode = mode; } @@ -143,7 +143,7 @@ void ModelGeometry::deinitialize() { glDeleteBuffers(1, &_ibo); } -bool ModelGeometry::loadObj(const std::string& filename){ +bool ModelGeometry::loadObj(const std::string& filename) { std::string cachedFile = ""; FileSys.cacheManager()->getCachedFile(filename, cachedFile, true); diff --git a/modules/base/rendering/modelgeometry.h b/modules/base/rendering/modelgeometry.h index 6419b4cf17..70450a8fef 100644 --- a/modules/base/rendering/modelgeometry.h +++ b/modules/base/rendering/modelgeometry.h @@ -50,8 +50,8 @@ namespace openspace { void render(); virtual bool loadModel(const std::string& filename) = 0; void changeRenderMode(const GLenum mode); - bool ModelGeometry::getVertices(std::vector* vertexList); - bool ModelGeometry::getIndices(std::vector* indexList); + bool getVertices(std::vector* vertexList); + bool getIndices(std::vector* indexList); protected: Renderable* _parent; diff --git a/modules/base/rendering/renderabletrail.cpp b/modules/base/rendering/renderabletrail.cpp index 87165d0b45..399e678afb 100644 --- a/modules/base/rendering/renderabletrail.cpp +++ b/modules/base/rendering/renderabletrail.cpp @@ -284,10 +284,10 @@ void RenderableTrail::fullYearSweep(double time) { psc pscPos; _vertexArray.resize(segments+2); for (int i = 0; i < segments+2; i++) { - if (start > time && intervalSet){ + if (start > time && intervalSet) { time = start; } - else if (end < time && intervalSet){ + else if (end < time && intervalSet) { time = end; } From 943ab69c69f305cc558be5f1eda0bed7c1f6d1af Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 22 Jun 2015 14:43:55 +0200 Subject: [PATCH 185/329] Fix CMakeLists files for TimelineView and Launcher to fix crosstalk --- apps/Launcher/CMakeLists.txt | 2 ++ apps/Launcher/infowidget.cpp | 4 ++-- apps/TimelineView/CMakeLists.txt | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/Launcher/CMakeLists.txt b/apps/Launcher/CMakeLists.txt index ecaf7a75b6..df27f03040 100644 --- a/apps/Launcher/CMakeLists.txt +++ b/apps/Launcher/CMakeLists.txt @@ -47,7 +47,9 @@ set(HEADER_FILES find_package(Qt5Widgets) find_package(Qt5Network) +set(MOC_FILES "") qt5_wrap_cpp(MOC_FILES ${HEADER_FILES}) +set(RESOURCE_FILES "") qt5_add_resources(RESOURCE_FILES ${application_path}/files.qrc) add_executable(${APPLICATION_NAME} MACOSX_BUNDLE diff --git a/apps/Launcher/infowidget.cpp b/apps/Launcher/infowidget.cpp index 04f8ca49f6..51a792536e 100644 --- a/apps/Launcher/infowidget.cpp +++ b/apps/Launcher/infowidget.cpp @@ -89,8 +89,8 @@ void InfoWidget::update(libtorrent::torrent_status s) { if (s.error.empty()) { int bytesPerSecond = s.download_rate; long long remainingBytes = s.total_wanted - s.total_wanted_done; - if (remainingBytes > 0) { - float seconds = static_cast(remainingBytes) / remainingBytes; + if (bytesPerSecond > 0 && remainingBytes > 0) { + float seconds = static_cast(remainingBytes) / bytesPerSecond; //auto now = time(NULL); //auto transferTime = now - s.added_time; diff --git a/apps/TimelineView/CMakeLists.txt b/apps/TimelineView/CMakeLists.txt index 284192421e..c3fc56f1ba 100644 --- a/apps/TimelineView/CMakeLists.txt +++ b/apps/TimelineView/CMakeLists.txt @@ -45,6 +45,7 @@ set(HEADER_FILES find_package(Qt5Widgets) find_package(Qt5Network) +set(MOC_FILES "") qt5_wrap_cpp(MOC_FILES ${HEADER_FILES}) add_executable(${APPLICATION_NAME} MACOSX_BUNDLE ${SOURCE_FILES} ${HEADER_FILES} ${MOC_FILES}) From 072a6d3cc14b9b9fd387e6db379f5aa94986a069 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 22 Jun 2015 14:44:31 +0200 Subject: [PATCH 186/329] Fixes to prevent crashes when data is not available --- .../rendering/renderableplanetprojection.cpp | 15 +++---- modules/newhorizons/util/hongkangparser.cpp | 39 +++---------------- modules/newhorizons/util/hongkangparser.h | 2 +- modules/newhorizons/util/imagesequencer2.cpp | 37 ++++++++---------- modules/newhorizons/util/labelparser.cpp | 6 ++- modules/newhorizons/util/labelparser.h | 2 +- modules/newhorizons/util/sequenceparser.h | 2 +- src/abuffer/abufferframebuffer.cpp | 8 +--- 8 files changed, 38 insertions(+), 73 deletions(-) diff --git a/modules/newhorizons/rendering/renderableplanetprojection.cpp b/modules/newhorizons/rendering/renderableplanetprojection.cpp index 09ec57d116..d2766bcf03 100644 --- a/modules/newhorizons/rendering/renderableplanetprojection.cpp +++ b/modules/newhorizons/rendering/renderableplanetprojection.cpp @@ -231,7 +231,8 @@ bool RenderablePlanetProjection::initialize() { completeSuccess &= _geometry->initialize(this); - completeSuccess &= auxiliaryRendertarget(); + if (completeSuccess) + completeSuccess &= auxiliaryRendertarget(); return completeSuccess; } @@ -289,7 +290,7 @@ bool RenderablePlanetProjection::deinitialize(){ return true; } bool RenderablePlanetProjection::isReady() const { - return _geometry && _programObject; + return _geometry && _programObject && _texture && _textureWhiteSquare; } void RenderablePlanetProjection::imageProjectGPU(){ @@ -414,7 +415,7 @@ void RenderablePlanetProjection::attitudeParameters(double time){ } -void RenderablePlanetProjection::textureBind(){ +void RenderablePlanetProjection::textureBind() { ghoul::opengl::TextureUnit unit[2]; unit[0].activate(); _texture->bind(); @@ -424,7 +425,7 @@ void RenderablePlanetProjection::textureBind(){ _programObject->setUniform("texture2", unit[1]); } -void RenderablePlanetProjection::project(){ +void RenderablePlanetProjection::project() { for (auto img : _imageTimes){ std::thread t1(&RenderablePlanetProjection::attitudeParameters, this, img.startTime); t1.join(); @@ -484,7 +485,7 @@ void RenderablePlanetProjection::render(const RenderData& data){ } -void RenderablePlanetProjection::update(const UpdateData& data){ +void RenderablePlanetProjection::update(const UpdateData& data) { // set spice-orientation in accordance to timestamp _time = Time::ref().currentTime(); _capture = false; @@ -498,7 +499,7 @@ void RenderablePlanetProjection::update(const UpdateData& data){ _programObject->rebuildFromFile(); } -void RenderablePlanetProjection::loadProjectionTexture(){ +void RenderablePlanetProjection::loadProjectionTexture() { delete _textureProj; _textureProj = nullptr; if (_colorTexturePath.value() != "") { @@ -513,7 +514,7 @@ void RenderablePlanetProjection::loadProjectionTexture(){ } } -void RenderablePlanetProjection::loadTexture(){ +void RenderablePlanetProjection::loadTexture() { delete _texture; _texture = nullptr; if (_colorTexturePath.value() != "") { diff --git a/modules/newhorizons/util/hongkangparser.cpp b/modules/newhorizons/util/hongkangparser.cpp index e6b3a0cee3..3dcbfb4767 100644 --- a/modules/newhorizons/util/hongkangparser.cpp +++ b/modules/newhorizons/util/hongkangparser.cpp @@ -98,12 +98,12 @@ void findPlaybookSpecifiedTarget(std::string line, std::string& target){ } } -void HongKangParser::create(){ +bool HongKangParser::create(){ if (size_t position = _fileName.find_last_of(".") + 1){ if (position != std::string::npos){ std::string extension = ghoul::filesystem::File(_fileName).fileExtension(); - if (extension == "txt"){// Hong Kang. pre-parsed playbook + if (extension == "txt") {// Hong Kang. pre-parsed playbook LINFO("Using Preparsed Playbook V9H"); std::ifstream file(_fileName , std::ios::binary); if (!file.good()) LERROR("Failed to open txt file '" << _fileName << "'"); @@ -130,8 +130,8 @@ void HongKangParser::create(){ std::string cameraTarget = "VOID"; std::string scannerTarget = "VOID"; - while (!file.eof()){//only while inte do, FIX - std::getline(file, line); + while (std::getline(file, line)) {//only while inte do, FIX + //std::getline(file, line); std::string event = line.substr(0, line.find_first_of(" ")); @@ -249,36 +249,7 @@ void HongKangParser::create(){ } sendPlaybookInformation(PlaybookIdentifierName); - - //std::ofstream myfile; - //myfile.open("HongKangOutput.txt"); - - ////print all - //for (auto target : _subsetMap){ - // std::string min, max; - // SpiceManager::ref().getDateFromET(target.second._range._min, min); - // SpiceManager::ref().getDateFromET(target.second._range._max, max); - - // myfile << std::endl; - // for (auto image : target.second._subset){ - // std::string time_beg; - // std::string time_end; - // SpiceManager::ref().getDateFromET(image.startTime, time_beg); - // SpiceManager::ref().getDateFromET(image.stopTime, time_end); - - // myfile << std::fixed - // << std::setw(10) << time_beg - // << std::setw(10) << time_end - // << std::setw(10) << (int)getMetFromET(image.startTime) - // << std::setw(10) << image.target << std::setw(10); - // for (auto instrument : image.activeInstruments){ - // myfile << " " << instrument; - // } - // myfile << std::endl; - // } - //} - //myfile.close(); - // + return true; } bool HongKangParser::augmentWithSpice(Image& image, diff --git a/modules/newhorizons/util/hongkangparser.h b/modules/newhorizons/util/hongkangparser.h index d74f43db40..e787aafb6d 100644 --- a/modules/newhorizons/util/hongkangparser.h +++ b/modules/newhorizons/util/hongkangparser.h @@ -41,7 +41,7 @@ public: std::string spacecraft, ghoul::Dictionary dictionary, std::vector potentialTargets); - virtual void create(); + bool create() override; // temporary need to figure this out virtual std::map getTranslation(){ return _fileTranslation; }; diff --git a/modules/newhorizons/util/imagesequencer2.cpp b/modules/newhorizons/util/imagesequencer2.cpp index cc880b7214..62f73100a5 100644 --- a/modules/newhorizons/util/imagesequencer2.cpp +++ b/modules/newhorizons/util/imagesequencer2.cpp @@ -297,35 +297,31 @@ void ImageSequencer2::sortData(){ } void ImageSequencer2::runSequenceParser(SequenceParser* parser){ - parser->create(); + bool success = parser->create(); + if (!success) + return; // get new data - std::map in1 = parser->getTranslation(); - std::map in2 = parser->getSubsetMap(); - std::vector> in3 = parser->getIstrumentTimes(); - std::vector> in4 = parser->getTargetTimes(); - std::vector in5 = parser->getCaptureProgression(); + std::map translations = parser->getTranslation(); + std::map imageData = parser->getSubsetMap(); + std::vector> instrumentTimes = parser->getIstrumentTimes(); + std::vector> targetTimes = parser->getTargetTimes(); + std::vector captureProgression = parser->getCaptureProgression(); // check for sanity - // TODO: This cannot crash the program! ---abock - ghoul_assert(in1.size() > 0, "Sequencer failed to load Translation" ); - ghoul_assert(in2.size() > 0, "Sequencer failed to load Image data" ); - ghoul_assert(in3.size() > 0, "Sequencer failed to load Instrument Switching schedule"); - ghoul_assert(in4.size() > 0, "Sequencer failed to load Target Switching schedule" ); - ghoul_assert(in5.size() > 0, "Sequencer failed to load Capture progression" ); + if (translations.empty() || imageData.empty() || instrumentTimes.empty() || targetTimes.empty() || captureProgression.empty()) + return; - - // append data - _fileTranslation.insert ( in1.begin(), in1.end()); - _subsetMap.insert ( in2.begin(), in2.end()); - _instrumentTimes.insert ( _instrumentTimes.end(), in3.begin(), in3.end()); - _targetTimes.insert ( _targetTimes.end(), in4.begin(), in4.end()); - _captureProgression.insert(_captureProgression.end(), in5.begin(), in5.end()); + _fileTranslation.insert(translations.begin(), translations.end()); + _subsetMap.insert(imageData.begin(), imageData.end()); + _instrumentTimes.insert(_instrumentTimes.end(), instrumentTimes.begin(), instrumentTimes.end()); + _targetTimes.insert(_targetTimes.end(), targetTimes.begin(), targetTimes.end()); + _captureProgression.insert(_captureProgression.end(), captureProgression.begin(), captureProgression.end()); // sorting of data _not_ optional sortData(); // extract payload from _fileTranslation - for (auto t : _fileTranslation){ + for (auto t : _fileTranslation) { if (t.second->getDecoderType() == "CAMERA" || t.second->getDecoderType() == "SCANNER" ){ std::vector spiceIDs = t.second->getTranslation(); @@ -336,4 +332,5 @@ void ImageSequencer2::runSequenceParser(SequenceParser* parser){ } _hasData = true; } + } // namespace openspace diff --git a/modules/newhorizons/util/labelparser.cpp b/modules/newhorizons/util/labelparser.cpp index 7911d90cdb..47392af164 100644 --- a/modules/newhorizons/util/labelparser.cpp +++ b/modules/newhorizons/util/labelparser.cpp @@ -118,7 +118,7 @@ std::string LabelParser::encode(std::string line) { return ""; } -void LabelParser::create(){ +bool LabelParser::create() { auto imageComparer = [](const Image &a, const Image &b)->bool{ return a.startTime < b.startTime; }; @@ -132,7 +132,7 @@ void LabelParser::create(){ ghoul::filesystem::Directory sequenceDir(_fileName, true); if (!FileSys.directoryExists(sequenceDir)) { LERROR("Could not load Label Directory '" << sequenceDir.path() << "'"); - return; + return false; } std::vector sequencePaths = sequenceDir.read(true, false); // check inputs for (auto path : sequencePaths){ @@ -255,6 +255,7 @@ void LabelParser::create(){ ////print all for (auto target : _subsetMap){ _instrumentTimes.push_back(std::make_pair(lblName, _subsetMap[target.first]._range)); + // std::string min, max; // SpiceManager::ref().getDateFromET(target.second._range._min, min); // SpiceManager::ref().getDateFromET(target.second._range._max, max); @@ -281,6 +282,7 @@ void LabelParser::create(){ sendPlaybookInformation(PlaybookIdentifierName); + return true; } void LabelParser::createImage(Image& image, double startTime, double stopTime, std::vector instr, std::string targ, std::string pot) { diff --git a/modules/newhorizons/util/labelparser.h b/modules/newhorizons/util/labelparser.h index 6efa1c77ca..79cbe85e2c 100644 --- a/modules/newhorizons/util/labelparser.h +++ b/modules/newhorizons/util/labelparser.h @@ -38,7 +38,7 @@ public: LabelParser(); LabelParser(const std::string& fileName, ghoul::Dictionary translationDictionary); - virtual void create(); + bool create() override; // temporary need to figure this out std::map getTranslation(){ return _fileTranslation; }; diff --git a/modules/newhorizons/util/sequenceparser.h b/modules/newhorizons/util/sequenceparser.h index 4eb3a42582..b8573dd712 100644 --- a/modules/newhorizons/util/sequenceparser.h +++ b/modules/newhorizons/util/sequenceparser.h @@ -67,7 +67,7 @@ struct ImageSubset { class SequenceParser { public: - virtual void create() = 0; + virtual bool create() = 0; virtual std::map getSubsetMap() final; virtual std::vector> getIstrumentTimes() final; virtual std::vector> getTargetTimes() final; diff --git a/src/abuffer/abufferframebuffer.cpp b/src/abuffer/abufferframebuffer.cpp index d8c8ca997c..007ded525c 100644 --- a/src/abuffer/abufferframebuffer.cpp +++ b/src/abuffer/abufferframebuffer.cpp @@ -87,10 +87,4 @@ bool ABufferFramebuffer::initializeABuffer() { _resolveShader->setProgramObjectCallback(shaderCallback); } -void ABufferFramebuffer::resolve() -{ - -} - - -} // openspace \ No newline at end of file +} // openspace From 2afd193a954a4306d166ce09f0c9a6a40e1d7dec Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 22 Jun 2015 15:30:54 +0200 Subject: [PATCH 187/329] Set thread priority on Windows to not have the Launcher stall the system --- src/engine/downloadmanager.cpp | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp index ddbccd935d..715279cd5c 100644 --- a/src/engine/downloadmanager.cpp +++ b/src/engine/downloadmanager.cpp @@ -36,6 +36,10 @@ #include #endif +#ifdef WIN32 +#include +#endif + namespace { const std::string _loggerCat = "DownloadManager"; @@ -141,7 +145,7 @@ DownloadManager::FileFuture* DownloadManager::downloadFile( LDEBUG("Starting download for file: '" << url << "' into file '" << file.path() << "'"); - std::thread([url, finishedCallback, progressCallback, future, fp]() { + std::thread t = std::thread([url, finishedCallback, progressCallback, future, fp]() { CURL* curl = curl_easy_init(); if (curl) { curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); @@ -170,7 +174,17 @@ DownloadManager::FileFuture* DownloadManager::downloadFile( if (finishedCallback) finishedCallback(*future); } - }).detach(); + }); + +#ifdef WIN32 + std::thread::native_handle_type h = t.native_handle(); + SetPriorityClass(h, IDLE_PRIORITY_CLASS); + SetThreadPriority(h, THREAD_PRIORITY_LOWEST); +#else + // TODO: Implement thread priority ---abock +#endif + + t.detach(); return future; } @@ -239,7 +253,7 @@ void DownloadManager::downloadRequestFilesAsync( bool overrideFiles, AsyncDownloadFinishedCallback callback) { - std::thread([this, identifier, destination, version, overrideFiles, callback](){ + std::thread t = std::thread([this, identifier, destination, version, overrideFiles, callback](){ std::vector f = downloadRequestFiles( identifier, destination, @@ -248,8 +262,17 @@ void DownloadManager::downloadRequestFilesAsync( ); callback(f); - }).detach(); + }); +#ifdef WIN32 + std::thread::native_handle_type h = t.native_handle(); + SetPriorityClass(h, IDLE_PRIORITY_CLASS); + SetThreadPriority(h, THREAD_PRIORITY_LOWEST); +#else + // TODO: Implement thread priority ---abock +#endif + + t.detach(); } } // namespace openspace From 89923f0650003d54ff24812a41ef55d8064c5d63 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 22 Jun 2015 15:39:37 +0200 Subject: [PATCH 188/329] Allow left-aligned and right-aligned messages in Launcher --- apps/Launcher/infowidget.cpp | 31 +++++++++++++++++-------------- apps/Launcher/infowidget.h | 3 ++- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/apps/Launcher/infowidget.cpp b/apps/Launcher/infowidget.cpp index 51a792536e..45395903bc 100644 --- a/apps/Launcher/infowidget.cpp +++ b/apps/Launcher/infowidget.cpp @@ -35,7 +35,8 @@ InfoWidget::InfoWidget(QString name, int totalBytes) , _name(nullptr) , _bytes(nullptr) , _progress(nullptr) - , _messages(nullptr) + , _messagesLeft(nullptr) + , _messagesRight(nullptr) , _totalBytes(totalBytes) { setFixedHeight(100); @@ -54,8 +55,11 @@ InfoWidget::InfoWidget(QString name, int totalBytes) _progress = new QProgressBar; layout->addWidget(_progress, 1, 1); - _messages = new QLabel(""); - layout->addWidget(_messages, 2, 0, 1, 2); + _messagesLeft = new QLabel(""); + _messagesRight = new QLabel(""); + + layout->addWidget(_messagesLeft, 2, 0, 1, 2); + layout->addWidget(_messagesRight, 2, 0, 1, 2, Qt::AlignRight); setLayout(layout); } @@ -70,10 +74,10 @@ void InfoWidget::update(openspace::DownloadManager::FileFuture* f) { if (f->errorMessage.empty()) { QString t = "Time remaining %1 s"; - _messages->setText(t.arg(static_cast(f->secondsRemaining))); + _messagesLeft->setText(t.arg(static_cast(f->secondsRemaining))); } else { - _messages->setText(QString::fromStdString(f->errorMessage)); + _messagesLeft->setText(QString::fromStdString(f->errorMessage)); } } @@ -92,22 +96,21 @@ void InfoWidget::update(libtorrent::torrent_status s) { if (bytesPerSecond > 0 && remainingBytes > 0) { float seconds = static_cast(remainingBytes) / bytesPerSecond; - //auto now = time(NULL); - //auto transferTime = now - s.added_time; - //auto estimatedTime = transferTime / progress; - //auto timeRemaining = estimatedTime - transferTime; - QString t = "Time remaining %1 s"; - _messages->setText(t.arg(static_cast(seconds))); + QString left = "Time remaining %1 s"; + _messagesLeft->setText(left.arg(static_cast(seconds))); + + QString right = "Download Rate: %1 KiB/s"; + _messagesRight->setText(right.arg(bytesPerSecond / 1024)); } else - _messages->setText(""); + _messagesLeft->setText(""); } else { - _messages->setText(QString::fromStdString(s.error)); + _messagesLeft->setText(QString::fromStdString(s.error)); } } void InfoWidget::error(QString message) { - _messages->setText(message); + _messagesLeft->setText(message); } diff --git a/apps/Launcher/infowidget.h b/apps/Launcher/infowidget.h index 654408fc6d..e2d42f9e4a 100644 --- a/apps/Launcher/infowidget.h +++ b/apps/Launcher/infowidget.h @@ -51,7 +51,8 @@ private: QLabel* _name; QLabel* _bytes; QProgressBar* _progress; - QLabel* _messages; + QLabel* _messagesLeft; + QLabel* _messagesRight; int _totalBytes; }; From 1906c525e358dcdd4d8df2a2e6b7bd49f93650aa Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 22 Jun 2015 16:01:23 +0200 Subject: [PATCH 189/329] Make SpiceManager derive from Singleton --- include/openspace/util/spicemanager.h | 1234 ++++++++++++------------- src/util/spicemanager.cpp | 37 +- 2 files changed, 624 insertions(+), 647 deletions(-) diff --git a/include/openspace/util/spicemanager.h b/include/openspace/util/spicemanager.h index a0d2a1001c..4ffe0383c3 100644 --- a/include/openspace/util/spicemanager.h +++ b/include/openspace/util/spicemanager.h @@ -31,6 +31,7 @@ #include #include +#include #include @@ -42,646 +43,630 @@ namespace openspace { -class SpiceManager { +class SpiceManager : public ghoul::Singleton { + friend class ghoul::Singleton; + public: - typedef std::array TransformMatrix; - typedef unsigned int KernelIdentifier; - - static const KernelIdentifier KernelFailed = KernelIdentifier(-1); + typedef std::array TransformMatrix; + typedef unsigned int KernelIdentifier; + + static const KernelIdentifier KernelFailed = KernelIdentifier(-1); - /** - * Initializer that initializes the static member. - */ - static void initialize(); + /** + * Loads one or more SPICE kernels into a program. The provided path can either be a + * binary, text-kernel, or meta-kernel which gets loaded into the kernel pool. The + * loading is done by passing the filePath to the furnsh_c + * function. http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/furnsh_c.html + * \param filePath The path to the kernel that should be loaded + * \return The loaded kernel's unique identifier that can be used to unload the kernel + */ + KernelIdentifier loadKernel(const std::string& filePath); - /** - * Deinitializes the SpiceManager and unloads all kernels which have been loaded using - * this manager. - */ - static void deinitialize(); + /** + * Function to find and store the intervals covered by a ck file, this is done + * by using mainly the ckcov_c and ckobj_c functions. + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/ckobj_c.html , + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/ckcov_c.html + * \param filePath The path to the kernel that should be examined + * \return true if the operation was successful + */ + bool findCkCoverage(const std::string& path); - /** - * Returns the reference to the singleton SpiceManager object that must have been - * initialized by a call to the initialize method earlier. - * \return The SpiceManager singleton - */ - static SpiceManager& ref(); - - /** - * Loads one or more SPICE kernels into a program. The provided path can either be a - * binary, text-kernel, or meta-kernel which gets loaded into the kernel pool. The - * loading is done by passing the filePath to the furnsh_c - * function. http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/furnsh_c.html - * \param filePath The path to the kernel that should be loaded - * \return The loaded kernel's unique identifier that can be used to unload the kernel - */ - KernelIdentifier loadKernel(const std::string& filePath); + /** + * Function to find and store the intervals covered by a spk file, this is done + * by using mainly the spkcov_c and spkobj_c functions. + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkobj_c.html , + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkcov_c.html + * \param filePath The path to the kernel that should be examined + * \return true if the operation was successful + */ + bool findSpkCoverage(const std::string& path); + + /** + * \return true if SPK kernels have been loaded to cover target + * for time et + * \param target, the body to be examined + * \param et, the time when body is possibly covered + */ - /** - * Function to find and store the intervals covered by a ck file, this is done - * by using mainly the ckcov_c and ckobj_c functions. - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/ckobj_c.html , - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/ckcov_c.html - * \param filePath The path to the kernel that should be examined - * \return true if the operation was successful - */ - bool findCkCoverage(const std::string& path); + bool hasSpkCoverage(std::string target, double& et) const; - /** - * Function to find and store the intervals covered by a spk file, this is done - * by using mainly the spkcov_c and spkobj_c functions. - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkobj_c.html , - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkcov_c.html - * \param filePath The path to the kernel that should be examined - * \return true if the operation was successful - */ - bool findSpkCoverage(const std::string& path); - - /** - * \return true if SPK kernels have been loaded to cover target - * for time et - * \param target, the body to be examined - * \param et, the time when body is possibly covered - */ + /** + * \return true if CK kernels have been loaded to cover frame + * for time et + * \param frame, the frame to be examined + * \param et, the time when frame is possibly covered + */ + bool hasCkCoverage(std::string frame, double& et) const; + + /** + * Unloads a SPICE kernel identified by the kernelId which was returned + * by the loading call to loadKernel. The unloading is done by calling the + * unload_c function. + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/unload_c.html. + * \param kernelId The unique identifier that was returned from the call to + * loadKernel which loaded the kernel + */ + void unloadKernel(KernelIdentifier kernelId); - bool hasSpkCoverage(std::string target, double& et) const; - - /** - * \return true if CK kernels have been loaded to cover frame - * for time et - * \param frame, the frame to be examined - * \param et, the time when frame is possibly covered - */ - bool hasCkCoverage(std::string frame, double& et) const; - - /** - * Unloads a SPICE kernel identified by the kernelId which was returned - * by the loading call to loadKernel. The unloading is done by calling the - * unload_c function. - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/unload_c.html. - * \param kernelId The unique identifier that was returned from the call to - * loadKernel which loaded the kernel - */ - void unloadKernel(KernelIdentifier kernelId); - - /** - * Unloads a SPICE kernel identified by the filePath which was used in - * the loading call to loadKernel. The unloading is done by calling the - * unload_c function. - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/unload_c.html. - * \param filePath The path of the kernel that should be unloaded, it has to refer to - * a file that was loaded in using the loadKernel method - */ - void unloadKernel(const std::string& filePath); - - /** - * Determines whether values exist for some item for any body, - * identified by it's naifId, in the kernel pool by passing it to the - * bodfnd_c function. - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodfnd_c.html - * For a description on NAIF IDs, see - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. - * \param naifId NAIF ID code of body + /** + * Unloads a SPICE kernel identified by the filePath which was used in + * the loading call to loadKernel. The unloading is done by calling the + * unload_c function. + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/unload_c.html. + * \param filePath The path of the kernel that should be unloaded, it has to refer to + * a file that was loaded in using the loadKernel method + */ + void unloadKernel(const std::string& filePath); + + /** + * Determines whether values exist for some item for any body, + * identified by it's naifId, in the kernel pool by passing it to the + * bodfnd_c function. + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodfnd_c.html + * For a description on NAIF IDs, see + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. + * \param naifId NAIF ID code of body * \param item The item to find - * \return true if the function succeeded, false otherwise - */ - bool hasValue(int naifId, const std::string& item) const; + * \return true if the function succeeded, false otherwise + */ + bool hasValue(int naifId, const std::string& item) const; - /** - * Determines whether values exist for some item for any - * body in the kernel pool by passing it to the bodfnd_c - * function. - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodfnd_c.html - * \param body The name of the body that should be sampled + /** + * Determines whether values exist for some item for any + * body in the kernel pool by passing it to the bodfnd_c + * function. + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodfnd_c.html + * \param body The name of the body that should be sampled * \param item The item to find - * \return true if the function succeeded, false otherwise - */ - bool hasValue(const std::string& body, const std::string& item) const; + * \return true if the function succeeded, false otherwise + */ + bool hasValue(const std::string& body, const std::string& item) const; - /** - * Returns the NAIF ID for a specific body. For a description on NAIF IDs, see - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. The - * id will only be set if the retrieval was successful, otherwise it will - * remain unchanged. - * \param body The body name that should be retrieved - * \param id The ID of the body will be stored in this variable. The - * value will only be changed if the retrieval was successful - * \return true if the body was found, false - * otherwise - */ - bool getNaifId(const std::string& body, int& id) const; + /** + * Returns the NAIF ID for a specific body. For a description on NAIF IDs, see + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. The + * id will only be set if the retrieval was successful, otherwise it will + * remain unchanged. + * \param body The body name that should be retrieved + * \param id The ID of the body will be stored in this variable. The + * value will only be changed if the retrieval was successful + * \return true if the body was found, false + * otherwise + */ + bool getNaifId(const std::string& body, int& id) const; - /** - * Returns the NAIF ID for a specific frame using namfrm_c(), see - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/namfrm_c.html. - * The id will only be set if the retrieval was successful, - * otherwise it will remain unchanged. - * \param frame The frame name that should be retrieved - * \param id The ID of the frame will be stored in this variable. The - * value will only be changed if the retrieval was successful - * \return true if the frame was found, false - * otherwise - */ - bool getFrameId(const std::string& frame, int& id) const; + /** + * Returns the NAIF ID for a specific frame using namfrm_c(), see + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/namfrm_c.html. + * The id will only be set if the retrieval was successful, + * otherwise it will remain unchanged. + * \param frame The frame name that should be retrieved + * \param id The ID of the frame will be stored in this variable. The + * value will only be changed if the retrieval was successful + * \return true if the frame was found, false + * otherwise + */ + bool getFrameId(const std::string& frame, int& id) const; - /** - * Retrieves a single value for a certain body. This method - * succeeds iff body is the name of a valid body, value - * is a value associated with the body, and the value consists of only a single - * double value. If all conditions are true, the value is retrieved using - * the method bodvrd_c and stored in v - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodvrd_c.html. If one of - * the conditions is false an error is logged and the value v is - * unchanged. For a description on NAIF IDs, see - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. - * \param body The name of the body whose value should be retrieved or the NAIF ID of - * this body - * \param value The value of that should be retrieved, this value is case-sensitive - * \param v The destination for the retrieved value - * \return true if the body named a valid body, - * value is a valid item for the body and the retrieved - * value is only a single value. false otherwise - */ - bool getValue(const std::string& body, const std::string& value, double& v) const; + /** + * Retrieves a single value for a certain body. This method + * succeeds iff body is the name of a valid body, value + * is a value associated with the body, and the value consists of only a single + * double value. If all conditions are true, the value is retrieved using + * the method bodvrd_c and stored in v + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodvrd_c.html. If one of + * the conditions is false an error is logged and the value v is + * unchanged. For a description on NAIF IDs, see + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. + * \param body The name of the body whose value should be retrieved or the NAIF ID of + * this body + * \param value The value of that should be retrieved, this value is case-sensitive + * \param v The destination for the retrieved value + * \return true if the body named a valid body, + * value is a valid item for the body and the retrieved + * value is only a single value. false otherwise + */ + bool getValue(const std::string& body, const std::string& value, double& v) const; - /** - * Retrieves a value with three components for a certain - * body. This method succeeds iff body is the name of a - * valid body, value is a value associated with the body, and the value - * consists of three double values. If all conditions are true, the value - * is retrieved using the method bodvrd_c and stored in v - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodvrd_c.html. If one of - * the conditions is false an error is logged and the value v is - * unchanged. For a description on NAIF IDs, see - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. - * \param body The name of the body whose value should be retrieved or the NAIF ID of - * the body - * \param value The value of that should be retrieved, this value is case-sensitive - * \param v The destination for the retrieved values - * \return true if the body named a valid body, - * value is a valid item for the body and the retrieved - * value is only a single value. false otherwise - */ - bool getValue(const std::string& body, const std::string& value, glm::dvec3& v) const; + /** + * Retrieves a value with three components for a certain + * body. This method succeeds iff body is the name of a + * valid body, value is a value associated with the body, and the value + * consists of three double values. If all conditions are true, the value + * is retrieved using the method bodvrd_c and stored in v + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodvrd_c.html. If one of + * the conditions is false an error is logged and the value v is + * unchanged. For a description on NAIF IDs, see + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. + * \param body The name of the body whose value should be retrieved or the NAIF ID of + * the body + * \param value The value of that should be retrieved, this value is case-sensitive + * \param v The destination for the retrieved values + * \return true if the body named a valid body, + * value is a valid item for the body and the retrieved + * value is only a single value. false otherwise + */ + bool getValue(const std::string& body, const std::string& value, glm::dvec3& v) const; - /** - * Retrieves a value with four components for a certain - * body. This method succeeds iff body is the name of a - * valid body, value is a value associated with the body, and the value - * consists of four double values. If all conditions are true, the value - * is retrieved using the method bodvrd_c and stored in v - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodvrd_c.html. If one of - * the conditions is false an error is logged and the value v is - * unchanged. For a description on NAIF IDs, see - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. - * \param body The name of the body whose value should be retrieved or the NAIF ID of - * the body - * \param value The value of that should be retrieved, this value is case-sensitive - * \param v The destination for the retrieved values - * \return true if the body named a valid body, - * value is a valid item for the body and the retrieved - * value is only a single value. false otherwise - */ - bool getValue(const std::string& body, const std::string& value, glm::dvec4& v) const; + /** + * Retrieves a value with four components for a certain + * body. This method succeeds iff body is the name of a + * valid body, value is a value associated with the body, and the value + * consists of four double values. If all conditions are true, the value + * is retrieved using the method bodvrd_c and stored in v + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodvrd_c.html. If one of + * the conditions is false an error is logged and the value v is + * unchanged. For a description on NAIF IDs, see + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. + * \param body The name of the body whose value should be retrieved or the NAIF ID of + * the body + * \param value The value of that should be retrieved, this value is case-sensitive + * \param v The destination for the retrieved values + * \return true if the body named a valid body, + * value is a valid item for the body and the retrieved + * value is only a single value. false otherwise + */ + bool getValue(const std::string& body, const std::string& value, glm::dvec4& v) const; - /** - * Retrieves a value with an arbitrary number of components for a certain - * body. This method succeeds body is a valid body, - * value is a value associated with the body, and the value consists of a - * number of double values. The requested number is equal to the - * size of the passed vector v which means that this vector - * has to be preallocated. If all conditions are true, the value is retrieved using - * the method bodvrd_c and stored in v - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodvrd_c.html. If one of - * the conditions is false an error is logged and the value v is - * unchanged. For a description on NAIF IDs, see - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. - * \param body The body whose information should be retrieved or the NAIF ID of that - * body - * \param value The value of that should be retrieved, this value is case-sensitive - * \param v The destination for the retrieved values. The size of this vector - * determines how many values will be retrieved - * \return true if the body named a valid body, - * value is a valid item for the body and the retrieved - * value is only a single value. false otherwise - */ - bool getValue(const std::string& body, const std::string& value, - std::vector& v) const; + /** + * Retrieves a value with an arbitrary number of components for a certain + * body. This method succeeds body is a valid body, + * value is a value associated with the body, and the value consists of a + * number of double values. The requested number is equal to the + * size of the passed vector v which means that this vector + * has to be preallocated. If all conditions are true, the value is retrieved using + * the method bodvrd_c and stored in v + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodvrd_c.html. If one of + * the conditions is false an error is logged and the value v is + * unchanged. For a description on NAIF IDs, see + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. + * \param body The body whose information should be retrieved or the NAIF ID of that + * body + * \param value The value of that should be retrieved, this value is case-sensitive + * \param v The destination for the retrieved values. The size of this vector + * determines how many values will be retrieved + * \return true if the body named a valid body, + * value is a valid item for the body and the retrieved + * value is only a single value. false otherwise + */ + bool getValue(const std::string& body, const std::string& value, + std::vector& v) const; - bool spacecraftClockToET(const std::string& craftIdCode, double& craftTicks, double& et); + bool spacecraftClockToET(const std::string& craftIdCode, double& craftTicks, double& et); - /** - * Converts the timeString representing a date to a double precision + /** + * Converts the timeString representing a date to a double precision * value representing the ephemerisTime; that is the number of TDB - * seconds past the J2000 epoch. For further details, please refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/str2et_c.html. If an error - * occurs, an error is logged, the method returns false and the - * ephemerisTime remains unchanged. - * \param timeString A string representing the time to be converted - * \param ephemerisTime The destination for the converted time; the number of TDB - * seconds past the J2000 epoch, representing the passed epochString - * \return true if the epochString is a valid string and - * the conversion succeeded, false otherwise - */ - bool getETfromDate(const std::string& timeString, double& ephemerisTime) const; + * seconds past the J2000 epoch. For further details, please refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/str2et_c.html. If an error + * occurs, an error is logged, the method returns false and the + * ephemerisTime remains unchanged. + * \param timeString A string representing the time to be converted + * \param ephemerisTime The destination for the converted time; the number of TDB + * seconds past the J2000 epoch, representing the passed epochString + * \return true if the epochString is a valid string and + * the conversion succeeded, false otherwise + */ + bool getETfromDate(const std::string& timeString, double& ephemerisTime) const; - /** - * Converts the passed ephemerisTime into a human-readable - * date string with a specific format. For details on the - * formatting, refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/timout_c.html. In case of - * an error, date will not be modified, an error will be logged and the - * method returns false. - * \param ephemerisTime The ephemeris time, that is the number of TDB seconds past the - * J2000 epoch - * \param date The destination for the converted date. This will only be changed if - * the conversion succeeded - * \param format The format string describing the output format for the - * date - * \return true if the conversion succeeded, false otherwise - */ - bool getDateFromET(double ephemerisTime, std::string& date, - const std::string& format = "YYYY MON DDTHR:MN:SC.### ::RND") const; + /** + * Converts the passed ephemerisTime into a human-readable + * date string with a specific format. For details on the + * formatting, refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/timout_c.html. In case of + * an error, date will not be modified, an error will be logged and the + * method returns false. + * \param ephemerisTime The ephemeris time, that is the number of TDB seconds past the + * J2000 epoch + * \param date The destination for the converted date. This will only be changed if + * the conversion succeeded + * \param format The format string describing the output format for the + * date + * \return true if the conversion succeeded, false otherwise + */ + bool getDateFromET(double ephemerisTime, std::string& date, + const std::string& format = "YYYY MON DDTHR:MN:SC.### ::RND") const; - /** - * Returns the position of a target body relative to an - * observer in a specific referenceFrame, optionally - * corrected for light time (planetary aberration) and stellar aberration - * (aberrationCorrection). For further details, refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkpos_c.html. For more - * information on NAIF IDs, refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html - * \param target The target body name or the target body's NAIF ID - * \param observer The observing body name or the observing body's NAIF ID - * \param referenceFrame The reference frame of the output position vector - * \param aberrationCorrection The aberration correction flag out of the list of - * values (NONE, LT, LT+S, CN, - * CN+S for the reception case or XLT, XLT+S, - * XCN, or XCN+S for the transmission case. - * \param ephemerisTime The time at which the position is to be queried - * \param position The output containing the position of the target; if the method - * fails, the target position is unchanged - * \param lightTime If the aberrationCorrection is different from - * NONE, this variable will contain the one-way light time between the - * observer and the target. If the method fails, the lightTime is unchanged - * \return true if the function was successful, false - * otherwise - */ - bool getTargetPosition(const std::string& target, - const std::string& observer, - const std::string& referenceFrame, - const std::string& aberrationCorrection, - double ephemerisTime, - glm::dvec3& position, - double& lightTime) const; + /** + * Returns the position of a target body relative to an + * observer in a specific referenceFrame, optionally + * corrected for light time (planetary aberration) and stellar aberration + * (aberrationCorrection). For further details, refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkpos_c.html. For more + * information on NAIF IDs, refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html + * \param target The target body name or the target body's NAIF ID + * \param observer The observing body name or the observing body's NAIF ID + * \param referenceFrame The reference frame of the output position vector + * \param aberrationCorrection The aberration correction flag out of the list of + * values (NONE, LT, LT+S, CN, + * CN+S for the reception case or XLT, XLT+S, + * XCN, or XCN+S for the transmission case. + * \param ephemerisTime The time at which the position is to be queried + * \param position The output containing the position of the target; if the method + * fails, the target position is unchanged + * \param lightTime If the aberrationCorrection is different from + * NONE, this variable will contain the one-way light time between the + * observer and the target. If the method fails, the lightTime is unchanged + * \return true if the function was successful, false + * otherwise + */ + bool getTargetPosition(const std::string& target, + const std::string& observer, + const std::string& referenceFrame, + const std::string& aberrationCorrection, + double ephemerisTime, + glm::dvec3& position, + double& lightTime) const; - bool getTargetPosition(const std::string& target, - const std::string& observer, - const std::string& referenceFrame, - const std::string& aberrationCorrection, - double ephemerisTime, - psc& position, - double& lightTime) const; + bool getTargetPosition(const std::string& target, + const std::string& observer, + const std::string& referenceFrame, + const std::string& aberrationCorrection, + double ephemerisTime, + psc& position, + double& lightTime) const; - /** - * If a position is requested for an uncovered time in the SPK kernels, - * this function will insert a position in modelPosition. - * If the coverage has not yet started, the first position will be retrieved, - * If the coverage has ended, the last position will be retrieved - * If time is in a coverage gap, the position will be interpolated - * \param time, for which an estimated position is desirable - * \param target, the body which is missing SPK data for this time - * \param origin, the observer, the position will be retrieved in relation to this body - * \param modelPosition, the position of the body, passed by reference - * \return true if an estimated position is found - */ - bool getEstimatedPosition(const double time, const std::string target, const std::string origin, psc& modelPosition) const; + /** + * If a position is requested for an uncovered time in the SPK kernels, + * this function will insert a position in modelPosition. + * If the coverage has not yet started, the first position will be retrieved, + * If the coverage has ended, the last position will be retrieved + * If time is in a coverage gap, the position will be interpolated + * \param time, for which an estimated position is desirable + * \param target, the body which is missing SPK data for this time + * \param origin, the observer, the position will be retrieved in relation to this body + * \param modelPosition, the position of the body, passed by reference + * \return true if an estimated position is found + */ + bool getEstimatedPosition(const double time, const std::string target, const std::string origin, psc& modelPosition) const; - /** - * This helper method converts a 3 dimensional vector from one reference frame to another. - * \param v The vector to be converted - * \param from The frame to be converted from - * \param to The frame to be converted to - * \param ephemerisTime Time at which to get rotational matrix that transforms vector - * \return true if the conversion succeeded, false otherwise - */ - bool frameConversion(glm::dvec3& v, const std::string& from, const std::string& to, double ephemerisTime) const; + /** + * This helper method converts a 3 dimensional vector from one reference frame to another. + * \param v The vector to be converted + * \param from The frame to be converted from + * \param to The frame to be converted to + * \param ephemerisTime Time at which to get rotational matrix that transforms vector + * \return true if the conversion succeeded, false otherwise + */ + bool frameConversion(glm::dvec3& v, const std::string& from, const std::string& to, double ephemerisTime) const; - /** - * Finds the projection of one vector onto another vector. - * All vectors are 3-dimensional. - * \param v1 The vector to be projected. - * \param v2 The vector onto which v1 is to be projected. - * \return The projection of v1 onto v2. - */ - glm::dvec3 orthogonalProjection(glm::dvec3& v1, glm::dvec3& v2); + /** + * Finds the projection of one vector onto another vector. + * All vectors are 3-dimensional. + * \param v1 The vector to be projected. + * \param v2 The vector onto which v1 is to be projected. + * \return The projection of v1 onto v2. + */ + glm::dvec3 orthogonalProjection(glm::dvec3& v1, glm::dvec3& v2); - /** - * Given an observer and a direction vector defining a ray, compute - * the surface intercept of the ray on a target body at a specified - * epoch, optionally corrected for light time and stellar - * aberration. - * \param target Name of target body. - * \param observer Name of observing body. - * \param fovFrame Reference frame of ray's direction vector. - * \param bodyFixedFrame Body-fixed, body-centered target body frame. - * \param method Computation method. - * \param aberrationCorrection Aberration correction. - * \param ephemerisTime Epoch in ephemeris seconds past J2000 TDB. - * \param targetEpoch Intercept epoch. - * \param directionVector Ray's direction vector. - * \param surfaceIntercept Surface intercept point on the target body. - * \param surfaceVector Vector from observer to intercept point. - * \param isVisible Flag indicating whether intercept was found. - * \return true if not error occurred, false otherwise - * For further details, refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/sincpt_c.html - */ - bool getSurfaceIntercept(const std::string& target, - const std::string& observer, - const std::string& fovFrame, - const std::string& bodyFixedFrame, - const std::string& method, - const std::string& aberrationCorrection, - double ephemerisTime, - double& targetEpoch, - glm::dvec3& directionVector, - glm::dvec3& surfaceIntercept, - glm::dvec3& surfaceVector, + /** + * Given an observer and a direction vector defining a ray, compute + * the surface intercept of the ray on a target body at a specified + * epoch, optionally corrected for light time and stellar + * aberration. + * \param target Name of target body. + * \param observer Name of observing body. + * \param fovFrame Reference frame of ray's direction vector. + * \param bodyFixedFrame Body-fixed, body-centered target body frame. + * \param method Computation method. + * \param aberrationCorrection Aberration correction. + * \param ephemerisTime Epoch in ephemeris seconds past J2000 TDB. + * \param targetEpoch Intercept epoch. + * \param directionVector Ray's direction vector. + * \param surfaceIntercept Surface intercept point on the target body. + * \param surfaceVector Vector from observer to intercept point. + * \param isVisible Flag indicating whether intercept was found. + * \return true if not error occurred, false otherwise + * For further details, refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/sincpt_c.html + */ + bool getSurfaceIntercept(const std::string& target, + const std::string& observer, + const std::string& fovFrame, + const std::string& bodyFixedFrame, + const std::string& method, + const std::string& aberrationCorrection, + double ephemerisTime, + double& targetEpoch, + glm::dvec3& directionVector, + glm::dvec3& surfaceIntercept, + glm::dvec3& surfaceVector, bool& isVisible ) const; - /** - * Determine if a specified ephemeris object is within the - * field-of-view (FOV) of a specified instrument at a given time. - * \param instrument Name or ID code string of the instrument. - * \param target Name or ID code string of the target. - * \param observer Name or ID code string of the observer. - * \param aberrationCorrection Aberration correction method. - * \param method Type of shape model used for the target. - * \param referenceFrame Body-fixed, body-centered frame for target body. - * \param targetEpoch Time of the observation (seconds past J2000). - * \param isVisible true if the target is visible - * \return The success of the function - * For further detail, refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/fovtrg_c.html - */ - bool targetWithinFieldOfView(const std::string& instrument, - const std::string& target, - const std::string& observer, - const std::string& method, - const std::string& referenceFrame, - const std::string& aberrationCorrection, - double& targetEpoch, + /** + * Determine if a specified ephemeris object is within the + * field-of-view (FOV) of a specified instrument at a given time. + * \param instrument Name or ID code string of the instrument. + * \param target Name or ID code string of the target. + * \param observer Name or ID code string of the observer. + * \param aberrationCorrection Aberration correction method. + * \param method Type of shape model used for the target. + * \param referenceFrame Body-fixed, body-centered frame for target body. + * \param targetEpoch Time of the observation (seconds past J2000). + * \param isVisible true if the target is visible + * \return The success of the function + * For further detail, refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/fovtrg_c.html + */ + bool targetWithinFieldOfView(const std::string& instrument, + const std::string& target, + const std::string& observer, + const std::string& method, + const std::string& referenceFrame, + const std::string& aberrationCorrection, + double& targetEpoch, bool& isVisible ) const; - /** - * This method performs the same computation as the function its overloading - * with the exception that in doing so it assumes the inertial bodyfixed frame - * is that of 'IAU' type, allowing the client to omitt the - * referenceFrame for planetary objects. - */ - bool targetWithinFieldOfView(const std::string& instrument, - const std::string& target, - const std::string& observer, - const std::string& method, - const std::string& aberrationCorrection, - double& targetEpoch, + /** + * This method performs the same computation as the function its overloading + * with the exception that in doing so it assumes the inertial bodyfixed frame + * is that of 'IAU' type, allowing the client to omitt the + * referenceFrame for planetary objects. + */ + bool targetWithinFieldOfView(const std::string& instrument, + const std::string& target, + const std::string& observer, + const std::string& method, + const std::string& aberrationCorrection, + double& targetEpoch, bool& isVisible ) const; - /** - * Returns the state vector (position and velocity) of a - * target body relative to an observer in a specific - * referenceFrame, optionally corrected for light time (planetary - * aberration) and stellar aberration (aberrationCorrection). For further - * details, refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkezr_c.html. For more - * information on NAIF IDs, refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html - * \param target The target body name or the target body's NAIF ID - * \param observer The observing body name or the observing body's NAIF ID - * \param referenceFrame The reference frame of the output position vector - * \param aberrationCorrection The aberration correction flag out of the list of - * values (NONE, LT, LT+S, CN, - * CN+S for the reception case or XLT, XLT+S, - * XCN, or XCN+S for the transmission case. - * \param ephemerisTime The time at which the position is to be queried - * \param position The output containing the position of the target; if the method - * fails, the position is unchanged - * \param velocity The output containing the velocity of the target; if the method - * fails, the velocity is unchanged - * \param lightTime If the aberrationCorrection is different from - * NONE, this variable will contain the one-way light time between the - * observer and the target.If the method fails, the lightTime is unchanged - * \return true if the function was successful, false - * otherwise - */ - bool getTargetState(const std::string& target, - const std::string& observer, - const std::string& referenceFrame, - const std::string& aberrationCorrection, - double ephemerisTime, - glm::dvec3& position, - glm::dvec3& velocity, - double& lightTime) const; + /** + * Returns the state vector (position and velocity) of a + * target body relative to an observer in a specific + * referenceFrame, optionally corrected for light time (planetary + * aberration) and stellar aberration (aberrationCorrection). For further + * details, refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkezr_c.html. For more + * information on NAIF IDs, refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html + * \param target The target body name or the target body's NAIF ID + * \param observer The observing body name or the observing body's NAIF ID + * \param referenceFrame The reference frame of the output position vector + * \param aberrationCorrection The aberration correction flag out of the list of + * values (NONE, LT, LT+S, CN, + * CN+S for the reception case or XLT, XLT+S, + * XCN, or XCN+S for the transmission case. + * \param ephemerisTime The time at which the position is to be queried + * \param position The output containing the position of the target; if the method + * fails, the position is unchanged + * \param velocity The output containing the velocity of the target; if the method + * fails, the velocity is unchanged + * \param lightTime If the aberrationCorrection is different from + * NONE, this variable will contain the one-way light time between the + * observer and the target.If the method fails, the lightTime is unchanged + * \return true if the function was successful, false + * otherwise + */ + bool getTargetState(const std::string& target, + const std::string& observer, + const std::string& referenceFrame, + const std::string& aberrationCorrection, + double ephemerisTime, + glm::dvec3& position, + glm::dvec3& velocity, + double& lightTime) const; - bool getTargetState(const std::string& target, - const std::string& observer, - const std::string& referenceFrame, - const std::string& aberrationCorrection, - double ephemerisTime, - PowerScaledCoordinate& position, - PowerScaledCoordinate& velocity, - double& lightTime) const; + bool getTargetState(const std::string& target, + const std::string& observer, + const std::string& referenceFrame, + const std::string& aberrationCorrection, + double ephemerisTime, + PowerScaledCoordinate& position, + PowerScaledCoordinate& velocity, + double& lightTime) const; - /** - * Returns the state transformation matrix used to convert from one frame to another - * at a specified ephemerisTime. For further details, please refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/sxform_c.html. - * \param sourceFrame The name of the source reference frame - * \param destinationFrame The name of the destination reference frame - * \param ephemerisTime The time at which the transformation matrix is to be queried - * \param transformationMatrix The output containing the TransformMatrix containing - * the transformation matrix that defines the transformation from the - * sourceFrame to the destinationFrame. If the method fails - * the transformationMatrix is unchanged - * \return true if the function was successful, false - * otherwise - */ - bool getStateTransformMatrix(const std::string& sourceFrame, - const std::string& destinationFrame, - double ephemerisTime, - TransformMatrix& transformationMatrix) const; + /** + * Returns the state transformation matrix used to convert from one frame to another + * at a specified ephemerisTime. For further details, please refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/sxform_c.html. + * \param sourceFrame The name of the source reference frame + * \param destinationFrame The name of the destination reference frame + * \param ephemerisTime The time at which the transformation matrix is to be queried + * \param transformationMatrix The output containing the TransformMatrix containing + * the transformation matrix that defines the transformation from the + * sourceFrame to the destinationFrame. If the method fails + * the transformationMatrix is unchanged + * \return true if the function was successful, false + * otherwise + */ + bool getStateTransformMatrix(const std::string& sourceFrame, + const std::string& destinationFrame, + double ephemerisTime, + TransformMatrix& transformationMatrix) const; - /** - * Returns the matrix that transforms position vectors from one reference frame to - * another at a specified ephemerisTime. For further details, please refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/pxform_c.html. - * \param sourceFrame The name of the source reference frame - * \param destinationFrame The name of the destination reference frame - * \param ephemerisTime The time at which the transformation matrix is to be queried + /** + * Returns the matrix that transforms position vectors from one reference frame to + * another at a specified ephemerisTime. For further details, please refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/pxform_c.html. + * \param sourceFrame The name of the source reference frame + * \param destinationFrame The name of the destination reference frame + * \param ephemerisTime The time at which the transformation matrix is to be queried * \param transformationMatrix The output containing the transformation matrix that - * defines the transformation from the sourceFrame to the - * destinationFrame. If the method fails the - * transformationMatrix is unchanged - * \return true if the function was successful, false - * otherwise - */ - bool getPositionTransformMatrix(const std::string& sourceFrame, - const std::string& destinationFrame, - double ephemerisTime, - glm::dmat3& transformationMatrix) const; + * defines the transformation from the sourceFrame to the + * destinationFrame. If the method fails the + * transformationMatrix is unchanged + * \return true if the function was successful, false + * otherwise + */ + bool getPositionTransformMatrix(const std::string& sourceFrame, + const std::string& destinationFrame, + double ephemerisTime, + glm::dmat3& transformationMatrix) const; - /** - * The following overloaded function performs similar to its default - the exception being - * that it computes transformationMatrix with respect to local time offset - * between an observer and its target. This allows for the accountance of light travel of - * photons, e.g to account for instrument pointing offsets due to said phenomenon. - * \param sourceFrame The name of the source reference frame - * \param destinationFrame The name of the destination reference frame - * \param ephemerisTimeFrom Recorded/observed observation time - * \param ephemerisTimeTo Emission local target-time - * \param transformationMatrix The output containing the transformation matrix that - */ + /** + * The following overloaded function performs similar to its default - the exception being + * that it computes transformationMatrix with respect to local time offset + * between an observer and its target. This allows for the accountance of light travel of + * photons, e.g to account for instrument pointing offsets due to said phenomenon. + * \param sourceFrame The name of the source reference frame + * \param destinationFrame The name of the destination reference frame + * \param ephemerisTimeFrom Recorded/observed observation time + * \param ephemerisTimeTo Emission local target-time + * \param transformationMatrix The output containing the transformation matrix that + */ - bool getPositionTransformMatrix(const std::string& sourceFrame, - const std::string& destinationFrame, - double ephemerisTimeFrom, - double ephemerisTimeTo, - glm::dmat3& transformationMatrix) const; + bool getPositionTransformMatrix(const std::string& sourceFrame, + const std::string& destinationFrame, + double ephemerisTimeFrom, + double ephemerisTimeTo, + glm::dmat3& transformationMatrix) const; - /** - * If a transform matrix is requested for an uncovered time in the CK kernels, - * this function will insert a transform matrix in positionMatrix. - * If the coverage has not yet started, the first transform matrix will be retrieved, - * If the coverage has ended, the last transform matrix will be retrieved - * If time is in a coverage gap, the transform matrix will be interpolated - * \param time, for which an estimated transform matrix is desirable - * \param fromFrame, the transform matrix will be retrieved in relation to this frame - * \param toFrame, the frame missing CK data for this time - * \param positionMatrix, the estimated transform matrix of the frame, passed by reference - * \return true if an estimated transform matrix is found - */ - bool getEstimatedTransformMatrix(const double time, const std::string fromFrame, const std::string toFrame, glm::dmat3& positionMatrix) const; + /** + * If a transform matrix is requested for an uncovered time in the CK kernels, + * this function will insert a transform matrix in positionMatrix. + * If the coverage has not yet started, the first transform matrix will be retrieved, + * If the coverage has ended, the last transform matrix will be retrieved + * If time is in a coverage gap, the transform matrix will be interpolated + * \param time, for which an estimated transform matrix is desirable + * \param fromFrame, the transform matrix will be retrieved in relation to this frame + * \param toFrame, the frame missing CK data for this time + * \param positionMatrix, the estimated transform matrix of the frame, passed by reference + * \return true if an estimated transform matrix is found + */ + bool getEstimatedTransformMatrix(const double time, const std::string fromFrame, const std::string toFrame, glm::dmat3& positionMatrix) const; - /** - * Applies the transformationMatrix retrieved from - * getStateTransformMatrix to the position and velocity. The - * position and velocity parameters are used as input and - * output. - * \param position The position that should be transformed. The transformed position - * will be stored back in this parameter - * \param velocity The velocity that should be transformed. The transformed velocity - * will be stored back in this parameter - * \param transformationMatrix The 6x6 transformation matrix retrieved from - * getStateTransformMatrix that is used to transform the position and - * velocity vectors - */ - void applyTransformationMatrix(glm::dvec3& position, - glm::dvec3& velocity, - const TransformMatrix& transformationMatrix); - - /** - * This routine returns the field-of-view (FOV) parameters for a specified - * instrument. For further details, please refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/getfov_c.html. - * \param instrument The name of the instrument for which the FOV is to be retrieved - * \param fovShape The output containing the rough shape of the returned FOV. If the - * method fails, this value remains unchanged - * \param frameName The output containing the name of the frame in which the FOV - * bounds are computed. If the method fails, this value remains unchanged - * \param boresightVector The output containing the boresight, that is the vector for - * the center direction of the FOV. If the method fails, this value remains unchanged - * \param bounds The output containing the values defining the bounds of the FOV as - * explained by http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/getfov_c.html. - * If the method fails, this value remains unchanged - * \return true if the function was successful, false - * otherwise - */ - bool getFieldOfView(const std::string& instrument, - std::string& fovShape, - std::string& frameName, - glm::dvec3& boresightVector, - std::vector& bounds) const; + /** + * Applies the transformationMatrix retrieved from + * getStateTransformMatrix to the position and velocity. The + * position and velocity parameters are used as input and + * output. + * \param position The position that should be transformed. The transformed position + * will be stored back in this parameter + * \param velocity The velocity that should be transformed. The transformed velocity + * will be stored back in this parameter + * \param transformationMatrix The 6x6 transformation matrix retrieved from + * getStateTransformMatrix that is used to transform the position and + * velocity vectors + */ + void applyTransformationMatrix(glm::dvec3& position, + glm::dvec3& velocity, + const TransformMatrix& transformationMatrix); + + /** + * This routine returns the field-of-view (FOV) parameters for a specified + * instrument. For further details, please refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/getfov_c.html. + * \param instrument The name of the instrument for which the FOV is to be retrieved + * \param fovShape The output containing the rough shape of the returned FOV. If the + * method fails, this value remains unchanged + * \param frameName The output containing the name of the frame in which the FOV + * bounds are computed. If the method fails, this value remains unchanged + * \param boresightVector The output containing the boresight, that is the vector for + * the center direction of the FOV. If the method fails, this value remains unchanged + * \param bounds The output containing the values defining the bounds of the FOV as + * explained by http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/getfov_c.html. + * If the method fails, this value remains unchanged + * \return true if the function was successful, false + * otherwise + */ + bool getFieldOfView(const std::string& instrument, + std::string& fovShape, + std::string& frameName, + glm::dvec3& boresightVector, + std::vector& bounds) const; - /** - * This routine returns the field-of-view (FOV) parameters for a specified - * instrument. For further details, please refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/getfov_c.html. - * \param instrument The NAIF id of the instrument for which the FOV is to be - * retrieved. For more information on NAIF IDs, refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html - * \param fovShape The output containing the rough shape of the returned FOV. If the - * method fails, this value remains unchanged - * \param frameName The output containing the name of the frame in which the FOV - * bounds are computed. If the method fails, this value remains unchanged - * \param boresightVector The output containing the boresight, that is the vector for - * the center direction of the FOV. If the method fails, this value remains unchanged - * \param bounds The output containing the values defining the bounds of the FOV as - * explained by http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/getfov_c.html. - * If the method fails, this value remains unchanged - * \return true if the function was successful, false - * otherwise - */ - bool getFieldOfView(int instrument, std::string& fovShape, std::string& frameName, - glm::dvec3& boresightVector, std::vector& bounds) const; - - /** - This routine computes a set of points on the umbral or penumbral terminator of - a specified target body, where SPICE models the target shape as an ellipsoid. - \param numberOfPoints - number of points along terminator returned by this method - \param terminatorType - is a string indicating the type of terminator to compute: - umbral or penumbral. The umbral terminator is the boundary of the portion of the - ellipsoid surface in total shadow. The penumbral terminator is the boundary of - the portion of the surface that is completely illuminated. Note that in astronomy - references, the unqualified word "terminator" refers to the umbral terminator. - Here, the unqualified word refers to either type of terminator. - \param lightSource - name of body acting as light source - \param observer - name of bodserving body - \param target - name of target body - \param frame - name of the reference frame relative to which the output terminator - points are expressed. - \param aberrationCorrection - correction for light time and/or stellar aberration - \param ephemerisTime - the epoch of participation of the observer - \param targetEpoch - is the "target epoch.", time it takes for - \param observerPosition - is the vector from the target body at targetEpoch - \param terminatorPoints - an array of points on the umbral or penumbral terminator - of the ellipsoid, as specified by the input argument `numberOfPoints' - For further, more specific details please refer to - http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/edterm_c.html - */ - bool getTerminatorEllipse(const int numberOfPoints, - const std::string terminatorType, - const std::string lightSource, - const std::string observer, - const std::string target, - const std::string frame, - const std::string aberrationCorrection, - double ephemerisTime, - double& targetEpoch, - glm::dvec3& observerPosition, - std::vector& terminatorPoints); + /** + * This routine returns the field-of-view (FOV) parameters for a specified + * instrument. For further details, please refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/getfov_c.html. + * \param instrument The NAIF id of the instrument for which the FOV is to be + * retrieved. For more information on NAIF IDs, refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html + * \param fovShape The output containing the rough shape of the returned FOV. If the + * method fails, this value remains unchanged + * \param frameName The output containing the name of the frame in which the FOV + * bounds are computed. If the method fails, this value remains unchanged + * \param boresightVector The output containing the boresight, that is the vector for + * the center direction of the FOV. If the method fails, this value remains unchanged + * \param bounds The output containing the values defining the bounds of the FOV as + * explained by http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/getfov_c.html. + * If the method fails, this value remains unchanged + * \return true if the function was successful, false + * otherwise + */ + bool getFieldOfView(int instrument, std::string& fovShape, std::string& frameName, + glm::dvec3& boresightVector, std::vector& bounds) const; + + /** + * This routine computes a set of points on the umbral or penumbral terminator of + * a specified target body, where SPICE models the target shape as an ellipsoid. + * \param numberOfPoints - number of points along terminator returned by this method + * \param terminatorType - is a string indicating the type of terminator to compute: + * umbral or penumbral. The umbral terminator is the boundary of the portion of the + * ellipsoid surface in total shadow. The penumbral terminator is the boundary of + * the portion of the surface that is completely illuminated. Note that in astronomy + * references, the unqualified word "terminator" refers to the umbral terminator. + * Here, the unqualified word refers to either type of terminator. + * \param lightSource - name of body acting as light source + * \param observer - name of bodserving body + * \param target - name of target body + * \param frame - name of the reference frame relative to which the output terminator + * points are expressed. + * \param aberrationCorrection - correction for light time and/or stellar aberration + * \param ephemerisTime - the epoch of participation of the observer + * \param targetEpoch - is the "target epoch.", time it takes for + * \param observerPosition - is the vector from the target body at targetEpoch + * \param terminatorPoints - an array of points on the umbral or penumbral terminator + * of the ellipsoid, as specified by the input argument `numberOfPoints' + * For further, more specific details please refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/edterm_c.html + */ + bool getTerminatorEllipse(const int numberOfPoints, + const std::string terminatorType, + const std::string lightSource, + const std::string observer, + const std::string target, + const std::string frame, + const std::string aberrationCorrection, + double ephemerisTime, + double& targetEpoch, + glm::dvec3& observerPosition, + std::vector& terminatorPoints); - /** - * This function adds a frame to a body - * \param body - the name of the body - * \param frame - the name of the frame - * \return false if the arguments are empty - */ - bool addFrame(const std::string body, const std::string frame); + /** + * This function adds a frame to a body + * \param body - the name of the body + * \param frame - the name of the frame + * \return false if the arguments are empty + */ + bool addFrame(const std::string body, const std::string frame); - /** - * This function returns the frame of a body if defined, otherwise it returns - * IAU_ + body (most frames are known by the International Astronomical Union) - * \param body - the name of the body - * \return the frame of the body - */ - std::string frameFromBody(const std::string body) const; + /** + * This function returns the frame of a body if defined, otherwise it returns + * IAU_ + body (most frames are known by the International Astronomical Union) + * \param body - the name of the body + * \return the frame of the body + */ + std::string frameFromBody(const std::string body) const; /** * This method checks if one of the previous SPICE methods has failed. If it has, the @@ -693,47 +678,48 @@ public: */ static bool checkForError(std::string errorMessage); - /** - * This method uses the SPICE kernels to get the radii of bodies defined as a - * triaxial ellipsoid. The benefit of this is to be able to create more accurate - * planet shapes, which is desirable when projecting images with SPICE intersection - * methods - * \param planetName - the name of the body, should be recognizable by SPICE - * \param a - equatorial radius 1 - * \param b - equatorial radius 2 - * \param c - polar radius - * \return true if SPICE reports no errors - */ - bool getPlanetEllipsoid(std::string planetName, float &a, float &b, float &c); + /** + * This method uses the SPICE kernels to get the radii of bodies defined as a + * triaxial ellipsoid. The benefit of this is to be able to create more accurate + * planet shapes, which is desirable when projecting images with SPICE intersection + * methods + * \param planetName - the name of the body, should be recognizable by SPICE + * \param a - equatorial radius 1 + * \param b - equatorial radius 2 + * \param c - polar radius + * \return true if SPICE reports no errors + */ + bool getPlanetEllipsoid(std::string planetName, float &a, float &b, float &c); -private: - struct KernelInformation { - std::string path; /// The path from which the kernel was loaded - KernelIdentifier id; /// A unique identifier for each kernel +protected: + struct KernelInformation { + std::string path; /// The path from which the kernel was loaded + KernelIdentifier id; /// A unique identifier for each kernel int refCount; /// How many parts loaded this kernel and are interested in it - }; + }; - SpiceManager() = default; - SpiceManager(const SpiceManager& c) = delete; - SpiceManager& operator=(const SpiceManager& r) = delete; - SpiceManager(SpiceManager&& r) = delete; + SpiceManager(); + SpiceManager(const SpiceManager& c) = delete; + SpiceManager& operator=(const SpiceManager& r) = delete; + SpiceManager(SpiceManager&& r) = delete; + ~SpiceManager(); /// A list of all loaded kernels - std::vector _loadedKernels; - // Map: id, vector of pairs. Pair: Start time, end time; - std::map > > _ckIntervals; - std::map > > _spkIntervals; - std::map > _ckCoverageTimes; - std::map > _spkCoverageTimes; - // Vector of pairs: Body, Frame - std::vector< std::pair > _frameByBody; - - const static bool _showErrors = false; + std::vector _loadedKernels; + // Map: id, vector of pairs. Pair: Start time, end time; + std::map > > _ckIntervals; + std::map > > _spkIntervals; + std::map > _ckCoverageTimes; + std::map > _spkCoverageTimes; + // Vector of pairs: Body, Frame + std::vector< std::pair > _frameByBody; + + const static bool _showErrors = false; /// The last assigned kernel-id, used to determine the next free kernel id - KernelIdentifier _lastAssignedKernel; + KernelIdentifier _lastAssignedKernel; - static SpiceManager* _manager; + static SpiceManager* _manager; }; } // namespace openspace diff --git a/src/util/spicemanager.cpp b/src/util/spicemanager.cpp index 896b249610..fea22903c9 100644 --- a/src/util/spicemanager.cpp +++ b/src/util/spicemanager.cpp @@ -42,33 +42,24 @@ namespace openspace { SpiceManager* SpiceManager::_manager = nullptr; -void SpiceManager::initialize() { - assert(_manager == nullptr); - _manager = new SpiceManager; - _manager->_lastAssignedKernel = 0; - - // Set the SPICE library to not exit the program if an error occurs - erract_c("SET", 0, const_cast("REPORT")); - // But we do not want SPICE to print the errors, we will fetch them ourselves - errprt_c("SET", 0, const_cast("NONE")); +SpiceManager::SpiceManager() + : _lastAssignedKernel(0) +{ + // Set the SPICE library to not exit the program if an error occurs + erract_c("SET", 0, const_cast("REPORT")); + // But we do not want SPICE to print the errors, we will fetch them ourselves + errprt_c("SET", 0, const_cast("NONE")); } -void SpiceManager::deinitialize() { - for (const KernelInformation& i : _manager->_loadedKernels) - unload_c(i.path.c_str()); +SpiceManager::~SpiceManager() { + for (const KernelInformation& i : _manager->_loadedKernels) + unload_c(i.path.c_str()); - delete _manager; - _manager = nullptr; - - // Set values back to default - erract_c("SET", 0, const_cast("DEFAULT")); - errprt_c("SET", 0, const_cast("DEFAULT")); + // Set values back to default + erract_c("SET", 0, const_cast("DEFAULT")); + errprt_c("SET", 0, const_cast("DEFAULT")); } -SpiceManager& SpiceManager::ref() { - assert(_manager != nullptr); - return *_manager; -} SpiceManager::KernelIdentifier SpiceManager::loadKernel(const std::string& filePath) { if (filePath.empty()) { @@ -1068,4 +1059,4 @@ bool SpiceManager::getPlanetEllipsoid(std::string planetName, float &a, float &b return !hasError; } -} \ No newline at end of file +} From 6b301d96eb5d22f39f64bbf5a0bd07d267bc6027 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 22 Jun 2015 16:52:12 +0200 Subject: [PATCH 190/329] Updated data folder reference --- data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data b/data index 1e33b0ba5d..2acb59c5fc 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 1e33b0ba5dca3dfa358a98aa64fe6e386eecc425 +Subproject commit 2acb59c5fc3ac88a473aee3337ef41ec216131d6 From 4ae8211508997741a8feba3fe6468f3374f01c86 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 22 Jun 2015 18:14:33 +0200 Subject: [PATCH 191/329] Fix method renaming error in volume module --- modules/volume/rendering/renderablevolumegl.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/volume/rendering/renderablevolumegl.cpp b/modules/volume/rendering/renderablevolumegl.cpp index 7c45905eb5..de1deeb89f 100644 --- a/modules/volume/rendering/renderablevolumegl.cpp +++ b/modules/volume/rendering/renderablevolumegl.cpp @@ -177,13 +177,13 @@ bool RenderableVolumeGL::initialize() { if(_filename != "") { _volume = loadVolume(_filename, _hintsDictionary); _volume->uploadTexture(); - OsEng.renderEngine()->abuffer()->addVolume(_volumeName, _volume); + OsEng.renderEngine()->aBuffer()->addVolume(_volumeName, _volume); } if(_transferFunctionPath != "") { _transferFunction = loadTransferFunction(_transferFunctionPath); _transferFunction->uploadTexture(); - OsEng.renderEngine()->abuffer()->addTransferFunction(_transferFunctionName, _transferFunction); + OsEng.renderEngine()->aBuffer()->addTransferFunction(_transferFunctionName, _transferFunction); auto textureCallback = [this](const ghoul::filesystem::File& file) { _updateTransferfunction = true; @@ -192,7 +192,7 @@ bool RenderableVolumeGL::initialize() { } // add the sampler and get the ID - _id = OsEng.renderEngine()->abuffer()->addSamplerfile(_samplerFilename); + _id = OsEng.renderEngine()->aBuffer()->addSamplerfile(_samplerFilename); OsEng.configurationManager()->getValue("RaycastProgram", _boxProgram); From 4fbf08b6b8ad87f1106f3fc0b034dfd6221f613c Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 22 Jun 2015 18:29:51 +0200 Subject: [PATCH 192/329] Add the FindCurl module from CMake into the support directory --- support/cmake/FindCURL.cmake | 68 ++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 support/cmake/FindCURL.cmake diff --git a/support/cmake/FindCURL.cmake b/support/cmake/FindCURL.cmake new file mode 100644 index 0000000000..209fd877de --- /dev/null +++ b/support/cmake/FindCURL.cmake @@ -0,0 +1,68 @@ +#.rst: +# FindCURL +# -------- +# +# Find curl +# +# Find the native CURL headers and libraries. +# +# :: +# +# CURL_INCLUDE_DIRS - where to find curl/curl.h, etc. +# CURL_LIBRARIES - List of libraries when using curl. +# CURL_FOUND - True if curl found. +# CURL_VERSION_STRING - the version of curl found (since CMake 2.8.8) + +#============================================================================= +# Copyright 2006-2009 Kitware, Inc. +# Copyright 2012 Rolf Eike Beer +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +# Look for the header file. +find_path(CURL_INCLUDE_DIR NAMES curl/curl.h) +mark_as_advanced(CURL_INCLUDE_DIR) + +# Look for the library (sorted from most current/relevant entry to least). +find_library(CURL_LIBRARY NAMES + curl + # Windows MSVC prebuilts: + curllib + libcurl_imp + curllib_static + # Windows older "Win32 - MSVC" prebuilts (libcurl.lib, e.g. libcurl-7.15.5-win32-msvc.zip): + libcurl +) +mark_as_advanced(CURL_LIBRARY) + +if(CURL_INCLUDE_DIR) + foreach(_curl_version_header curlver.h curl.h) + if(EXISTS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}") + file(STRINGS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}" curl_version_str REGEX "^#define[\t ]+LIBCURL_VERSION[\t ]+\".*\"") + + string(REGEX REPLACE "^#define[\t ]+LIBCURL_VERSION[\t ]+\"([^\"]*)\".*" "\\1" CURL_VERSION_STRING "${curl_version_str}") + unset(curl_version_str) + break() + endif() + endforeach() +endif() + +# handle the QUIETLY and REQUIRED arguments and set CURL_FOUND to TRUE if +# all listed variables are TRUE +include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(CURL + REQUIRED_VARS CURL_LIBRARY CURL_INCLUDE_DIR + VERSION_VAR CURL_VERSION_STRING) + +if(CURL_FOUND) + set(CURL_LIBRARIES ${CURL_LIBRARY}) + set(CURL_INCLUDE_DIRS ${CURL_INCLUDE_DIR}) +endif() From 249081c55698aebf76f4b1e9eaef3c6e5e070265 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 22 Jun 2015 18:34:38 +0200 Subject: [PATCH 193/329] Correct CURL cmake specification --- support/cmake/FindCURL.cmake | 68 ------------------------------ support/cmake/support_macros.cmake | 2 +- 2 files changed, 1 insertion(+), 69 deletions(-) delete mode 100644 support/cmake/FindCURL.cmake diff --git a/support/cmake/FindCURL.cmake b/support/cmake/FindCURL.cmake deleted file mode 100644 index 209fd877de..0000000000 --- a/support/cmake/FindCURL.cmake +++ /dev/null @@ -1,68 +0,0 @@ -#.rst: -# FindCURL -# -------- -# -# Find curl -# -# Find the native CURL headers and libraries. -# -# :: -# -# CURL_INCLUDE_DIRS - where to find curl/curl.h, etc. -# CURL_LIBRARIES - List of libraries when using curl. -# CURL_FOUND - True if curl found. -# CURL_VERSION_STRING - the version of curl found (since CMake 2.8.8) - -#============================================================================= -# Copyright 2006-2009 Kitware, Inc. -# Copyright 2012 Rolf Eike Beer -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distribute this file outside of CMake, substitute the full -# License text for the above reference.) - -# Look for the header file. -find_path(CURL_INCLUDE_DIR NAMES curl/curl.h) -mark_as_advanced(CURL_INCLUDE_DIR) - -# Look for the library (sorted from most current/relevant entry to least). -find_library(CURL_LIBRARY NAMES - curl - # Windows MSVC prebuilts: - curllib - libcurl_imp - curllib_static - # Windows older "Win32 - MSVC" prebuilts (libcurl.lib, e.g. libcurl-7.15.5-win32-msvc.zip): - libcurl -) -mark_as_advanced(CURL_LIBRARY) - -if(CURL_INCLUDE_DIR) - foreach(_curl_version_header curlver.h curl.h) - if(EXISTS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}") - file(STRINGS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}" curl_version_str REGEX "^#define[\t ]+LIBCURL_VERSION[\t ]+\".*\"") - - string(REGEX REPLACE "^#define[\t ]+LIBCURL_VERSION[\t ]+\"([^\"]*)\".*" "\\1" CURL_VERSION_STRING "${curl_version_str}") - unset(curl_version_str) - break() - endif() - endforeach() -endif() - -# handle the QUIETLY and REQUIRED arguments and set CURL_FOUND to TRUE if -# all listed variables are TRUE -include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(CURL - REQUIRED_VARS CURL_LIBRARY CURL_INCLUDE_DIR - VERSION_VAR CURL_VERSION_STRING) - -if(CURL_FOUND) - set(CURL_LIBRARIES ${CURL_LIBRARY}) - set(CURL_INCLUDE_DIRS ${CURL_INCLUDE_DIR}) -endif() diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index ac499b1d44..9a0865b5c0 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -171,7 +171,7 @@ function (add_external_dependencies) target_link_libraries(libOpenSpace ${CURL_ROOT_DIR}/lib/libcurl_imp.lib) target_compile_definitions(libOpenSpace PUBLIC "OPENSPACE_CURL_ENABLED" "CURL_STATICLIB") else () - find_package(curl) + find_package(CURL) if (CURL_FOUND) target_include_directories(libOpenSpace SYSTEM PUBLIC ${CURL_INCLUDE_DIRS}) target_link_libraries(libOpenSpace ${CURL_LIBRARIES}) From febb9f87ee93ad902eb4f906355617bd82125480 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 22 Jun 2015 18:45:12 +0200 Subject: [PATCH 194/329] Set Launcher as default application Don't copy curl.dll on non-windows system --- support/cmake/support_macros.cmake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 9a0865b5c0..1f90d5e24f 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -191,6 +191,7 @@ function (handle_applications) set(DEFAULT_APPLICATIONS "OpenSpace" + "Launcher" ) mark_as_advanced(DEFAULT_APPLICATIONS) @@ -237,8 +238,9 @@ function (handle_applications) endif () - copy_files(${APPLICATION_NAME} "${CURL_ROOT_DIR}/lib/libcurl.dll") - + if (WIN32) + copy_files(${APPLICATION_NAME} "${CURL_ROOT_DIR}/lib/libcurl.dll") + endif () endif () list(APPEND applications ${APPLICATION_NAME}) From 9ed5ee4469d8404e52e001e6f0390d3436d16618 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 22 Jun 2015 21:59:12 +0200 Subject: [PATCH 195/329] Always build libtorrent as a static library --- apps/Launcher/ext/libtorrent/CMakeLists.txt | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/apps/Launcher/ext/libtorrent/CMakeLists.txt b/apps/Launcher/ext/libtorrent/CMakeLists.txt index d9050522fc..b320846e13 100644 --- a/apps/Launcher/ext/libtorrent/CMakeLists.txt +++ b/apps/Launcher/ext/libtorrent/CMakeLists.txt @@ -120,7 +120,6 @@ set(ed25519_sources set(includes include ed25519/src) -option(LIBTORRENT_shared "build libtorrent as a shared library" ON) option(LIBTORRENT_pool-allocators "Uses a pool allocator for disk and piece buffers" ON) option(LIBTORRENT_dht "enable support for Mainline DHT" OFF) option(LIBTORRENT_unicode "enable unicode support" ON) @@ -153,12 +152,7 @@ if (LIBTORRENT_dht) endforeach(s) endif () -if (LIBTORRENT_shared) - add_definitions(-DTORRENT_BUILDING_SHARED) - add_library(libtorrent SHARED ${sources2}) -else () - add_library(libtorrent STATIC ${sources2}) -endif () +add_library(libtorrent STATIC ${sources2}) target_include_directories(libtorrent PUBLIC ${includes}) target_compile_definitions(libtorrent PUBLIC From 978b535a2f236cc1c16ce50ef7017acfaf8bd9c5 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 22 Jun 2015 22:57:15 +0200 Subject: [PATCH 196/329] Remove setName function from OpenSpaceModule and move naming into constructor --- include/openspace/util/openspacemodule.h | 5 ++--- modules/base/basemodule.cpp | 6 +++--- modules/fieldlines/fieldlinesmodule.cpp | 6 +++--- modules/kameleon/kameleonmodule.cpp | 6 +++--- modules/newhorizons/newhorizonsmodule.cpp | 6 +++--- modules/onscreengui/onscreenguimodule.cpp | 6 +++--- modules/volume/volumemodule.cpp | 6 +++--- src/util/openspacemodule.cpp | 11 ++++++----- 8 files changed, 26 insertions(+), 26 deletions(-) diff --git a/include/openspace/util/openspacemodule.h b/include/openspace/util/openspacemodule.h index 7f2d17c7d7..30e43de50b 100644 --- a/include/openspace/util/openspacemodule.h +++ b/include/openspace/util/openspacemodule.h @@ -31,7 +31,7 @@ namespace openspace { class OpenSpaceModule { public: - OpenSpaceModule() = default; + OpenSpaceModule(std::string name); virtual ~OpenSpaceModule() = default; virtual bool initialize(); @@ -40,10 +40,9 @@ public: std::string name() const; protected: - void setName(std::string name); std::string modulePath() const; - std::string _name; + const std::string _name; }; } // namespace openspace diff --git a/modules/base/basemodule.cpp b/modules/base/basemodule.cpp index e03cc14326..42ca428188 100644 --- a/modules/base/basemodule.cpp +++ b/modules/base/basemodule.cpp @@ -51,9 +51,9 @@ namespace openspace { -BaseModule::BaseModule() { - setName("Base"); -} +BaseModule::BaseModule() + : OpenSpaceModule("Base") +{} bool BaseModule::initialize() { bool success = OpenSpaceModule::initialize(); diff --git a/modules/fieldlines/fieldlinesmodule.cpp b/modules/fieldlines/fieldlinesmodule.cpp index 1c72f6d264..323a106745 100644 --- a/modules/fieldlines/fieldlinesmodule.cpp +++ b/modules/fieldlines/fieldlinesmodule.cpp @@ -33,9 +33,9 @@ namespace openspace { -FieldlinesModule::FieldlinesModule() { - setName("Fieldlines"); -} +FieldlinesModule::FieldlinesModule() + : OpenSpaceModule("Fieldlines") +{} bool FieldlinesModule::initialize() { bool success = OpenSpaceModule::initialize(); diff --git a/modules/kameleon/kameleonmodule.cpp b/modules/kameleon/kameleonmodule.cpp index bde173db2b..0da01945a0 100644 --- a/modules/kameleon/kameleonmodule.cpp +++ b/modules/kameleon/kameleonmodule.cpp @@ -26,9 +26,9 @@ namespace openspace { -KameleonModule::KameleonModule() { - setName("Kameleon"); -} +KameleonModule::KameleonModule() + : OpenSpaceModule("Kameleon") +{} bool KameleonModule::initialize() { bool success = OpenSpaceModule::initialize(); diff --git a/modules/newhorizons/newhorizonsmodule.cpp b/modules/newhorizons/newhorizonsmodule.cpp index 8a1570fcf2..59ea77c0b3 100644 --- a/modules/newhorizons/newhorizonsmodule.cpp +++ b/modules/newhorizons/newhorizonsmodule.cpp @@ -46,9 +46,9 @@ namespace openspace { -NewHorizonsModule::NewHorizonsModule() { - setName("NewHorizons"); -} +NewHorizonsModule::NewHorizonsModule() + : OpenSpaceModule("NewHorizons") +{} bool NewHorizonsModule::initialize() { bool success = OpenSpaceModule::initialize(); diff --git a/modules/onscreengui/onscreenguimodule.cpp b/modules/onscreengui/onscreenguimodule.cpp index ac024bc300..a41f09a988 100644 --- a/modules/onscreengui/onscreenguimodule.cpp +++ b/modules/onscreengui/onscreenguimodule.cpp @@ -26,9 +26,9 @@ namespace openspace { -OnScreenGUIModule::OnScreenGUIModule() { - setName("OnScreenGUI"); -} +OnScreenGUIModule::OnScreenGUIModule() + : OpenSpaceModule("OnScreenGUI") +{} bool OnScreenGUIModule::initialize() { bool success = OpenSpaceModule::initialize(); diff --git a/modules/volume/volumemodule.cpp b/modules/volume/volumemodule.cpp index 08eba0f856..3120ad753d 100644 --- a/modules/volume/volumemodule.cpp +++ b/modules/volume/volumemodule.cpp @@ -33,9 +33,9 @@ namespace openspace { -VolumeModule::VolumeModule() { - setName("Volume"); -} +VolumeModule::VolumeModule() + : OpenSpaceModule("Volume") +{} bool VolumeModule::initialize() { bool success = OpenSpaceModule::initialize(); diff --git a/src/util/openspacemodule.cpp b/src/util/openspacemodule.cpp index dc944f3fe3..5a11d03f66 100644 --- a/src/util/openspacemodule.cpp +++ b/src/util/openspacemodule.cpp @@ -37,8 +37,13 @@ namespace { //ghoul::filesystem::FileSystem::TokenClosingBraces namespace openspace { +OpenSpaceModule::OpenSpaceModule(std::string name) + : _name(std::move(name)) +{ + ghoul_assert(!_name.empty(), "Empty module name is not allowed"); +} + bool OpenSpaceModule::initialize() { - ghoul_assert(!(name().empty()), "Module name must be set before initialize call"); std::string moduleNameUpper = name(); std::transform(moduleNameUpper.begin(), moduleNameUpper.end(), moduleNameUpper.begin(), toupper); std::string moduleToken = @@ -61,10 +66,6 @@ std::string OpenSpaceModule::name() const { return _name; } -void OpenSpaceModule::setName(std::string name) { - _name = std::move(name); -} - std::string OpenSpaceModule::modulePath() const { std::string moduleName = name(); std::transform(moduleName.begin(), moduleName.end(), moduleName.begin(), tolower); From 5227ce2213c5e4d2c94c7339100b293714a336c3 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 22 Jun 2015 23:09:19 +0200 Subject: [PATCH 197/329] Renamed OpenSpaceModule's initialize method to create Created new initialize method that gets called in OpenSpaceEngine initialize --- include/openspace/engine/moduleengine.h | 3 ++ include/openspace/util/openspacemodule.h | 3 ++ modules/base/basemodule.cpp | 4 +-- modules/base/basemodule.h | 2 +- modules/fieldlines/fieldlinesmodule.cpp | 4 +-- modules/fieldlines/fieldlinesmodule.h | 2 +- modules/kameleon/kameleonmodule.cpp | 4 +-- modules/kameleon/kameleonmodule.h | 2 +- modules/newhorizons/newhorizonsmodule.cpp | 4 +-- modules/newhorizons/newhorizonsmodule.h | 2 +- modules/onscreengui/onscreenguimodule.cpp | 4 +-- modules/onscreengui/onscreenguimodule.h | 2 +- modules/volume/volumemodule.cpp | 4 +-- modules/volume/volumemodule.h | 2 +- src/engine/moduleengine.cpp | 35 ++++++++++++++++++++--- src/engine/openspaceengine.cpp | 12 +++----- src/util/openspacemodule.cpp | 12 ++++++-- 17 files changed, 69 insertions(+), 32 deletions(-) diff --git a/include/openspace/engine/moduleengine.h b/include/openspace/engine/moduleengine.h index 229af21f84..2ad6954846 100644 --- a/include/openspace/engine/moduleengine.h +++ b/include/openspace/engine/moduleengine.h @@ -33,6 +33,9 @@ class OpenSpaceModule; class ModuleEngine { public: + bool create(); + bool destroy(); + bool initialize(); bool deinitialize(); diff --git a/include/openspace/util/openspacemodule.h b/include/openspace/util/openspacemodule.h index 30e43de50b..3a4ade1eaa 100644 --- a/include/openspace/util/openspacemodule.h +++ b/include/openspace/util/openspacemodule.h @@ -34,6 +34,9 @@ public: OpenSpaceModule(std::string name); virtual ~OpenSpaceModule() = default; + virtual bool create(); + virtual bool destroy(); + virtual bool initialize(); virtual bool deinitialize(); diff --git a/modules/base/basemodule.cpp b/modules/base/basemodule.cpp index 42ca428188..8280208f98 100644 --- a/modules/base/basemodule.cpp +++ b/modules/base/basemodule.cpp @@ -55,8 +55,8 @@ BaseModule::BaseModule() : OpenSpaceModule("Base") {} -bool BaseModule::initialize() { - bool success = OpenSpaceModule::initialize(); +bool BaseModule::create() { + bool success = OpenSpaceModule::create(); if (!success) return false; diff --git a/modules/base/basemodule.h b/modules/base/basemodule.h index 37dc678bb2..597e6cf2af 100644 --- a/modules/base/basemodule.h +++ b/modules/base/basemodule.h @@ -32,7 +32,7 @@ namespace openspace { class BaseModule : public OpenSpaceModule { public: BaseModule(); - bool initialize() override; + bool create() override; }; } // namespace openspace diff --git a/modules/fieldlines/fieldlinesmodule.cpp b/modules/fieldlines/fieldlinesmodule.cpp index 323a106745..4fcba99081 100644 --- a/modules/fieldlines/fieldlinesmodule.cpp +++ b/modules/fieldlines/fieldlinesmodule.cpp @@ -37,8 +37,8 @@ FieldlinesModule::FieldlinesModule() : OpenSpaceModule("Fieldlines") {} -bool FieldlinesModule::initialize() { - bool success = OpenSpaceModule::initialize(); +bool FieldlinesModule::create() { + bool success = OpenSpaceModule::create(); if (!success) return false; diff --git a/modules/fieldlines/fieldlinesmodule.h b/modules/fieldlines/fieldlinesmodule.h index 2acd5d2164..8edc1d6705 100644 --- a/modules/fieldlines/fieldlinesmodule.h +++ b/modules/fieldlines/fieldlinesmodule.h @@ -32,7 +32,7 @@ namespace openspace { class FieldlinesModule : public OpenSpaceModule { public: FieldlinesModule(); - bool initialize() override; + bool create() override; }; } // namespace openspace diff --git a/modules/kameleon/kameleonmodule.cpp b/modules/kameleon/kameleonmodule.cpp index 0da01945a0..1000b27184 100644 --- a/modules/kameleon/kameleonmodule.cpp +++ b/modules/kameleon/kameleonmodule.cpp @@ -30,8 +30,8 @@ KameleonModule::KameleonModule() : OpenSpaceModule("Kameleon") {} -bool KameleonModule::initialize() { - bool success = OpenSpaceModule::initialize(); +bool KameleonModule::create() { + bool success = OpenSpaceModule::create(); if (!success) return false; diff --git a/modules/kameleon/kameleonmodule.h b/modules/kameleon/kameleonmodule.h index 19e2b51712..b99b5d3f77 100644 --- a/modules/kameleon/kameleonmodule.h +++ b/modules/kameleon/kameleonmodule.h @@ -32,7 +32,7 @@ namespace openspace { class KameleonModule : public OpenSpaceModule { public: KameleonModule(); - bool initialize() override; + bool create() override; }; } // namespace openspace diff --git a/modules/newhorizons/newhorizonsmodule.cpp b/modules/newhorizons/newhorizonsmodule.cpp index 59ea77c0b3..2698839ea2 100644 --- a/modules/newhorizons/newhorizonsmodule.cpp +++ b/modules/newhorizons/newhorizonsmodule.cpp @@ -50,8 +50,8 @@ NewHorizonsModule::NewHorizonsModule() : OpenSpaceModule("NewHorizons") {} -bool NewHorizonsModule::initialize() { - bool success = OpenSpaceModule::initialize(); +bool NewHorizonsModule::create() { + bool success = OpenSpaceModule::create(); if (!success) return false; diff --git a/modules/newhorizons/newhorizonsmodule.h b/modules/newhorizons/newhorizonsmodule.h index ebbc4ef3d6..6ca4b49cda 100644 --- a/modules/newhorizons/newhorizonsmodule.h +++ b/modules/newhorizons/newhorizonsmodule.h @@ -32,7 +32,7 @@ namespace openspace { class NewHorizonsModule : public OpenSpaceModule { public: NewHorizonsModule(); - bool initialize() override; + bool create() override; }; } // namespace openspace diff --git a/modules/onscreengui/onscreenguimodule.cpp b/modules/onscreengui/onscreenguimodule.cpp index a41f09a988..8518eef832 100644 --- a/modules/onscreengui/onscreenguimodule.cpp +++ b/modules/onscreengui/onscreenguimodule.cpp @@ -30,8 +30,8 @@ OnScreenGUIModule::OnScreenGUIModule() : OpenSpaceModule("OnScreenGUI") {} -bool OnScreenGUIModule::initialize() { - bool success = OpenSpaceModule::initialize(); +bool OnScreenGUIModule::create() { + bool success = OpenSpaceModule::create(); if (!success) return false; return true; diff --git a/modules/onscreengui/onscreenguimodule.h b/modules/onscreengui/onscreenguimodule.h index 0479d78b99..affd7bb9d5 100644 --- a/modules/onscreengui/onscreenguimodule.h +++ b/modules/onscreengui/onscreenguimodule.h @@ -32,7 +32,7 @@ namespace openspace { class OnScreenGUIModule : public OpenSpaceModule { public: OnScreenGUIModule(); - bool initialize() override; + bool create() override; }; } // namespace openspace diff --git a/modules/volume/volumemodule.cpp b/modules/volume/volumemodule.cpp index 3120ad753d..274e408f6f 100644 --- a/modules/volume/volumemodule.cpp +++ b/modules/volume/volumemodule.cpp @@ -37,8 +37,8 @@ VolumeModule::VolumeModule() : OpenSpaceModule("Volume") {} -bool VolumeModule::initialize() { - bool success = OpenSpaceModule::initialize(); +bool VolumeModule::create() { + bool success = OpenSpaceModule::create(); if (!success) return false; diff --git a/modules/volume/volumemodule.h b/modules/volume/volumemodule.h index 73094ba1d6..1e57079063 100644 --- a/modules/volume/volumemodule.h +++ b/modules/volume/volumemodule.h @@ -32,7 +32,7 @@ namespace openspace { class VolumeModule : public OpenSpaceModule { public: VolumeModule(); - bool initialize() override; + bool create() override; }; } // namespace openspace diff --git a/src/engine/moduleengine.cpp b/src/engine/moduleengine.cpp index 86f5163810..d6888df8f9 100644 --- a/src/engine/moduleengine.cpp +++ b/src/engine/moduleengine.cpp @@ -36,11 +36,39 @@ namespace { namespace openspace { -bool ModuleEngine::initialize() { - LDEBUG("Initializing modules"); +bool ModuleEngine::create() { + LDEBUG("Creating modules"); registerModules(AllModules); + for (OpenSpaceModule* m : _modules) { + bool success = m->create(); + if (!success) { + LERROR("Could not initialize module '" << m->name() << "'"); + return false; + } + } + LDEBUG("Finished creating modules"); + return true; +} + +bool ModuleEngine::destroy() { + LDEBUG("Destroying modules"); + for (OpenSpaceModule* m : _modules) { + bool success = m->destroy(); + if (!success) { + LERROR("Could not deinitialize module '" << m->name() << "'"); + return false; + } + delete m; + } + LDEBUG("Finished destroying modules"); + return true; +} + +bool ModuleEngine::initialize() { + LDEBUG("Initializing modules"); + for (OpenSpaceModule* m : _modules) { bool success = m->initialize(); if (!success) { @@ -54,13 +82,13 @@ bool ModuleEngine::initialize() { bool ModuleEngine::deinitialize() { LDEBUG("Deinitializing modules"); + for (OpenSpaceModule* m : _modules) { bool success = m->deinitialize(); if (!success) { LERROR("Could not deinitialize module '" << m->name() << "'"); return false; } - delete m; } LDEBUG("Finished Deinitializing modules"); return true; @@ -78,5 +106,4 @@ const std::vector ModuleEngine::modules() const { return _modules; } - } // namespace openspace diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 2e1421fe2e..3d919d213c 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -230,7 +230,7 @@ bool OpenSpaceEngine::create( } // Register modules - _engine->_moduleEngine->initialize(); + _engine->_moduleEngine->create(); // Create the cachemanager FileSys.createCacheManager(absPath("${" + ConfigurationManager::KeyCache + "}"), CacheVersion); @@ -264,6 +264,7 @@ bool OpenSpaceEngine::create( void OpenSpaceEngine::destroy() { _engine->_moduleEngine->deinitialize(); + _engine->_moduleEngine->destroy(); _engine->_console->deinitialize(); _engine->_scriptEngine->deinitialize(); @@ -351,11 +352,6 @@ bool OpenSpaceEngine::initialize() { // initialize the RenderEngine _renderEngine->initialize(); - // if (_configurationManager->hasKeyAndValue(KeyRenderingMethod)) - // _renderEngine->initialize(_configurationManager->value(KeyRenderingMethod)); - // else - // _renderEngine->initialize(DefaultRenderingMethod); - sceneGraph->initialize(); std::string sceneDescriptionPath; @@ -365,8 +361,6 @@ bool OpenSpaceEngine::initialize() { sceneGraph->scheduleLoadSceneFile(sceneDescriptionPath); _interactionHandler->setKeyboardController(new interaction::KeyboardControllerFixed); - //_interactionHandler.setKeyboardController(new interaction::KeyboardControllerLua); - //_interactionHandler.setMouseController(new interaction::TrackballMouseController); _interactionHandler->setMouseController(new interaction::OrbitalMouseController); // Run start up scripts @@ -378,6 +372,8 @@ bool OpenSpaceEngine::initialize() { LINFO("Initializing GUI"); _gui->initialize(); + // Initialize modules + _moduleEngine->initialize(); LINFO("Finished initializing"); return true; diff --git a/src/util/openspacemodule.cpp b/src/util/openspacemodule.cpp index 5a11d03f66..35a3dee439 100644 --- a/src/util/openspacemodule.cpp +++ b/src/util/openspacemodule.cpp @@ -43,7 +43,7 @@ OpenSpaceModule::OpenSpaceModule(std::string name) ghoul_assert(!_name.empty(), "Empty module name is not allowed"); } -bool OpenSpaceModule::initialize() { +bool OpenSpaceModule::create() { std::string moduleNameUpper = name(); std::transform(moduleNameUpper.begin(), moduleNameUpper.end(), moduleNameUpper.begin(), toupper); std::string moduleToken = @@ -58,7 +58,7 @@ bool OpenSpaceModule::initialize() { return true; } -bool OpenSpaceModule::deinitialize() { +bool OpenSpaceModule::destroy() { return true; } @@ -80,5 +80,13 @@ std::string OpenSpaceModule::modulePath() const { return ""; } +bool OpenSpaceModule::initialize() { + return true; +} + +bool OpenSpaceModule::deinitialize() { + return true; +} + } // namespace openspace From d60846a3ec4caaf7e8207cb36c120554a8507b0b Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 23 Jun 2015 00:36:31 +0200 Subject: [PATCH 198/329] Made destination optional in data specifications --- apps/Launcher/syncwidget.cpp | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 047ac456de..7f6540f696 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -312,16 +312,15 @@ void SyncWidget::syncButtonPressed() { continue; } ghoul::Dictionary d = directDownloadFiles.value(std::to_string(i)); - if (!directDownloadFiles.hasKeyAndValue(UrlKey)) { + if (!d.hasKeyAndValue(UrlKey)) { LERROR(dataFile.toStdString() << ": No " << UrlKey); continue; } std::string url = d.value(UrlKey); - if (!directDownloadFiles.hasKeyAndValue(DestinationKey)) { - LERROR(dataFile.toStdString() << ": No " << DestinationKey); - continue; - } - std::string dest = d.value(DestinationKey); + + std::string dest = ""; + if (d.hasKeyAndValue(DestinationKey)) + dest = d.value(DestinationKey); _directFiles.append({ module, @@ -335,10 +334,22 @@ void SyncWidget::syncButtonPressed() { if (found) { for (int i = 1; i <= fileRequests.size(); ++i) { ghoul::Dictionary d = fileRequests.value(std::to_string(i)); - std::string url = d.value(IdentifierKey); - std::string dest = d.value(DestinationKey); - int version = static_cast(d.value(VersionKey)); + if (!d.hasKeyAndValue(IdentifierKey)) { + LERROR(dataFile.toStdString() << ": No " << IdentifierKey); + continue; + } + std::string url = d.value(IdentifierKey); + + std::string dest = ""; + if (d.hasKeyAndValue(DestinationKey)) + dest = d.value(DestinationKey); + + if (!d.hasKeyAndValue(VersionKey)) { + LERROR(dataFile.toStdString() << ": No " << VersionKey); + continue; + } + int version = static_cast(d.value(VersionKey)); _fileRequests.append({ module, @@ -353,6 +364,11 @@ void SyncWidget::syncButtonPressed() { if (found) { for (int i = 1; i <= torrentFiles.size(); ++i) { ghoul::Dictionary d = torrentFiles.value(std::to_string(i)); + + if (!d.hasKeyAndValue(FileKey)) { + LERROR(dataFile.toStdString() << ": No " << FileKey); + continue; + } std::string file = d.value(FileKey); std::string dest; From b2b7563018321fbde06789e7b148bf759d700dd3 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Tue, 23 Jun 2015 09:33:09 +0200 Subject: [PATCH 199/329] added functionality to send and decode scripts --- .../openspace/network/osparallelconnection.h | 12 +++- src/network/osparallelconnection.cpp | 56 +++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/include/openspace/network/osparallelconnection.h b/include/openspace/network/osparallelconnection.h index d17c5bd80d..edcff7ae2a 100644 --- a/include/openspace/network/osparallelconnection.h +++ b/include/openspace/network/osparallelconnection.h @@ -30,6 +30,8 @@ #include #include #include + +//glm includes #include //std includes @@ -38,6 +40,7 @@ #include #include #include +#include #ifdef __WIN32__ #ifndef WIN32_LEAN_AND_MEAN @@ -133,10 +136,13 @@ namespace openspace{ void setPassword(const std::string &password); + void sendScript(const std::string script); + enum MessageTypes{ Authentication=0, Initialization, Data, + Script, HostInfo, InitializationRequest, HostshipRequest @@ -189,6 +195,8 @@ namespace openspace{ void decodeInitializationMessage(); void decodeDataMessage(); + + void decodeScript(); void decodeHostInfoMessage(); @@ -196,8 +204,8 @@ namespace openspace{ void broadcast(); - int receiveData(_SOCKET & socket, std::vector &buffer, int length, int flags); - + int receiveData(_SOCKET & socket, std::vector &buffer, int length, int flags); + uint32_t _passCode; std::string _port; std::string _address; diff --git a/src/network/osparallelconnection.cpp b/src/network/osparallelconnection.cpp index 87cb9530cf..1332253da9 100644 --- a/src/network/osparallelconnection.cpp +++ b/src/network/osparallelconnection.cpp @@ -248,6 +248,8 @@ namespace openspace { case MessageTypes::Data: decodeDataMessage(); break; + case MessageTypes::Script: + break; case MessageTypes::HostInfo: decodeHostInfoMessage(); break; @@ -296,6 +298,33 @@ namespace openspace { OsEng.interactionHandler()->addKeyframe(kf); } + void OSParallelConnection::decodeScript(){ + int result; + uint16_t msglen; + std::vector buffer; + buffer.resize(sizeof(msglen)); + result = receiveData(_clientSocket, buffer, sizeof(msglen), 0); + + if (result <= 0){ + //error + return; + } + + msglen = (*(reinterpret_cast(buffer.data()))); + + buffer.clear(); + buffer.resize(msglen); + + result = receiveData(_clientSocket, buffer, msglen, 0); + if (result <= 0){ + //error + return; + } + + std::string script(buffer.data()); + OsEng.scriptEngine()->queueScript(script); + } + void OSParallelConnection::decodeHostInfoMessage(){ std::vector hostflag; hostflag.resize(1); @@ -457,6 +486,33 @@ namespace openspace { void OSParallelConnection::setPassword(const std::string& pwd){ _passCode = hash(pwd); } + + void OSParallelConnection::sendScript(const std::string script){ + + uint16_t msglen = static_cast(script.length()); + std::vector buffer; + buffer.reserve(headerSize + sizeof(msglen) + msglen); + + //header + buffer.insert(buffer.end(), 'O'); + buffer.insert(buffer.end(), 'S'); + buffer.insert(buffer.end(), 0); + buffer.insert(buffer.end(), 0); + + //type of message + int type = OSParallelConnection::MessageTypes::Script; + buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(type)); + + //size of message + buffer.insert(buffer.end(), reinterpret_cast(&msglen), reinterpret_cast(&msglen) + sizeof(msglen)); + + //actual message + buffer.insert(buffer.end(), script.begin(), script.end()); + + //send message + send(_clientSocket, buffer.data(), buffer.size(), 0); + } + void OSParallelConnection::disconnect(){ //must be run before trying to join communication threads, else the threads are stuck trying to receive data From fabbd3c1e80be45d7cc842fea0fb072c7bdad167 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Tue, 23 Jun 2015 09:34:34 +0200 Subject: [PATCH 200/329] added a vector containing all executed scripts for this session and method to get it. added a call to sending of scripts via parallel connection as soon as they are executed. --- include/openspace/scripting/scriptengine.h | 6 ++++++ src/scripting/scriptengine.cpp | 16 ++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/include/openspace/scripting/scriptengine.h b/include/openspace/scripting/scriptengine.h index e8aa0e0c52..3e90dcdd92 100644 --- a/include/openspace/scripting/scriptengine.h +++ b/include/openspace/scripting/scriptengine.h @@ -78,6 +78,8 @@ public: void preSynchronization(); void queueScript(const std::string &script); + + std::vector executedScripts(); std::vector allLuaFunctions() const; @@ -98,6 +100,10 @@ private: std::vector _queuedScripts; std::vector _receivedScripts; std::string _currentSyncedScript; + + //parallel variables @TODO make a more permanent solution to this - JK + std::vector _executedScripts; + std::mutex _executedScriptsMutex; }; } // namespace scripting diff --git a/src/scripting/scriptengine.cpp b/src/scripting/scriptengine.cpp index 83cc1bc49e..690820fef3 100644 --- a/src/scripting/scriptengine.cpp +++ b/src/scripting/scriptengine.cpp @@ -27,7 +27,8 @@ #include #include #include - +#include +#include #include #include #include @@ -154,6 +155,8 @@ bool ScriptEngine::runScript(const std::string& script) { return false; } + OsEng.parallelConnection()->sendScript(script); + return true; } @@ -497,6 +500,10 @@ void ScriptEngine::deserialize(SyncBuffer* syncBuffer){ _mutex.lock(); _receivedScripts.push_back(_currentSyncedScript); _mutex.unlock(); + + _executedScriptsMutex.lock(); + _executedScripts.push_back(_currentSyncedScript); + _executedScriptsMutex.unlock(); } } @@ -527,13 +534,18 @@ void ScriptEngine::preSynchronization(){ void ScriptEngine::queueScript(const std::string &script){ if (script.empty()) return; - + _mutex.lock(); _queuedScripts.insert(_queuedScripts.begin(), script); _mutex.unlock(); } + +std::vector ScriptEngine::executedScripts(){ + std::lock_guard lockGuard(_executedScriptsMutex); + return _executedScripts; +} } // namespace scripting From b6bd20530490d98dbf05eb037c529d3a4b174cce Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Tue, 23 Jun 2015 11:33:20 +0200 Subject: [PATCH 201/329] Added init request handling for non-hosts osparallelconnection is no longer a property owner --- .../openspace/network/osparallelconnection.h | 5 +---- src/network/osparallelconnection.cpp | 20 ++++++++++++++++++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/include/openspace/network/osparallelconnection.h b/include/openspace/network/osparallelconnection.h index edcff7ae2a..cb03302abd 100644 --- a/include/openspace/network/osparallelconnection.h +++ b/include/openspace/network/osparallelconnection.h @@ -26,8 +26,6 @@ #define __OSPARALLELCONNECTION_H__ //openspace includes -#include -#include #include #include @@ -95,11 +93,10 @@ namespace openspace{ //timestamp size = sizeof(_timeStamp); memcpy(&_timeStamp, buffer.data() + offset, size); - offset += size; }; }; - class OSParallelConnection : public properties::PropertyOwner { + class OSParallelConnection{ public: OSParallelConnection(); diff --git a/src/network/osparallelconnection.cpp b/src/network/osparallelconnection.cpp index 1332253da9..c9607cb648 100644 --- a/src/network/osparallelconnection.cpp +++ b/src/network/osparallelconnection.cpp @@ -204,7 +204,6 @@ namespace openspace { } void OSParallelConnection::authenticate(){ - int pos = 4; uint16_t namelen = static_cast(_name.length()); int size = headerSize + sizeof(uint32_t) + sizeof(uint16_t) + static_cast(namelen); std::vector buffer; @@ -355,6 +354,25 @@ namespace openspace { else{ //we were not host so nothing to do } + + //request init packages from the host + int size = headerSize + sizeof(uint32_t); + std::vector buffer; + buffer.reserve(size); + + //version + buffer.insert(buffer.end(), 'O'); + buffer.insert(buffer.end(), 'S'); + buffer.insert(buffer.end(), 0); + buffer.insert(buffer.end(), 0); + + //msg type, 0 = auth + int type = MessageTypes::InitializationRequest; + buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(int)); + + //send message + send(_clientSocket, buffer.data(), buffer.size(), 0); + } } else{ From 466d9028f9d6e3c432722957967e18ff8d811c8b Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Tue, 23 Jun 2015 11:34:43 +0200 Subject: [PATCH 202/329] changed ghoul version --- ext/ghoul | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/ghoul b/ext/ghoul index eb66778e0a..99a25fcc8c 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit eb66778e0aaf1809fff1a1476872594a24ac0ac3 +Subproject commit 99a25fcc8c05d6448b32409462a25d9cd9432637 From 9a74bd60b7d8f24ecb28f0883cf86b0ee0e73d69 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 24 Jun 2015 11:23:36 +0200 Subject: [PATCH 203/329] renamed osparallelconnection to parallel connection. Updated all files and CMakeLists to use new name. started rebase work on parallel connection --- include/openspace/engine/openspaceengine.h | 6 +- .../interaction/interactionhandler.h | 6 +- ...allelconnection.h => parallelconnection.h} | 19 +- src/CMakeLists.txt | 6 +- src/engine/openspaceengine.cpp | 8 +- src/interaction/interactionhandler.cpp | 2 +- ...lconnection.cpp => parallelconnection.cpp} | 173 ++++++++++-------- ...ion_lua.inl => parallelconnection_lua.inl} | 0 src/scripting/scriptengine.cpp | 2 +- 9 files changed, 127 insertions(+), 95 deletions(-) rename include/openspace/network/{osparallelconnection.h => parallelconnection.h} (95%) rename src/network/{osparallelconnection.cpp => parallelconnection.cpp} (78%) rename src/network/{osparallelconnection_lua.inl => parallelconnection_lua.inl} (100%) diff --git a/include/openspace/engine/openspaceengine.h b/include/openspace/engine/openspaceengine.h index c21d6d59a2..98ba179966 100644 --- a/include/openspace/engine/openspaceengine.h +++ b/include/openspace/engine/openspaceengine.h @@ -58,7 +58,7 @@ namespace scripting { } namespace network{ - class OSParallelConnection; + class ParallelConnection; } class OpenSpaceEngine { @@ -81,7 +81,7 @@ public: NetworkEngine* networkEngine(); LuaConsole* console(); ModuleEngine* moduleEngine(); - network::OSParallelConnection* parallelConnection(); + network::ParallelConnection* parallelConnection(); gui::GUI* gui(); @@ -129,7 +129,7 @@ private: LuaConsole* _console; ModuleEngine* _moduleEngine; gui::GUI* _gui; - network::OSParallelConnection* _parallelConnection; + network::ParallelConnection* _parallelConnection; bool _isMaster; SyncBuffer* _syncBuffer; diff --git a/include/openspace/interaction/interactionhandler.h b/include/openspace/interaction/interactionhandler.h index 80c8651929..ad1b828788 100644 --- a/include/openspace/interaction/interactionhandler.h +++ b/include/openspace/interaction/interactionhandler.h @@ -76,7 +76,7 @@ #include #include -#include +#include #include @@ -140,7 +140,7 @@ public: void setInvertRotation(bool invert); bool invertRotation() const; - void addKeyframe(const network::Keyframe &kf); + void addKeyframe(const network::StreamDataKeyframe &kf); /** * Returns the Lua library that contains all Lua functions available to affect the @@ -178,7 +178,7 @@ private: std::vector _controllers; //remote controller - std::vector _keyframes; + std::vector _keyframes; double _currentKeyframeTime; std::mutex _keyframeMutex; }; diff --git a/include/openspace/network/osparallelconnection.h b/include/openspace/network/parallelconnection.h similarity index 95% rename from include/openspace/network/osparallelconnection.h rename to include/openspace/network/parallelconnection.h index cb03302abd..f4639400cd 100644 --- a/include/openspace/network/osparallelconnection.h +++ b/include/openspace/network/parallelconnection.h @@ -22,8 +22,8 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#ifndef __OSPARALLELCONNECTION_H__ -#define __OSPARALLELCONNECTION_H__ +#ifndef __PARALLELCONNECTION_H__ +#define __PARALLELCONNECTION_H__ //openspace includes #include @@ -60,7 +60,7 @@ namespace openspace{ namespace network{ - struct Keyframe{ + struct StreamDataKeyframe{ glm::quat _viewRotationQuat; psc _position; double _timeStamp; @@ -96,12 +96,12 @@ namespace openspace{ }; }; - class OSParallelConnection{ + class ParallelConnection{ public: - OSParallelConnection(); + ParallelConnection(); - ~OSParallelConnection(); + ~ParallelConnection(); void clientConnect(); @@ -138,7 +138,7 @@ namespace openspace{ enum MessageTypes{ Authentication=0, Initialization, - Data, + StreamData, Script, HostInfo, InitializationRequest, @@ -174,6 +174,8 @@ namespace openspace{ return hashVal; }; + + void writeHeader(std::vector &buffer); void closeSocket(); @@ -200,9 +202,12 @@ namespace openspace{ void decodeInitializationRequestMessage(); void broadcast(); + + int headerSize(); int receiveData(_SOCKET & socket, std::vector &buffer, int length, int flags); + int _headerSize; uint32_t _passCode; std::string _port; std::string _address; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9ec41ef3cc..9eb0475b66 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -49,8 +49,8 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/interaction/externalcontrol/pythonexternalcontrol.cpp ${OPENSPACE_BASE_DIR}/src/interaction/externalcontrol/randomexternalcontrol.cpp ${OPENSPACE_BASE_DIR}/src/network/networkengine.cpp - ${OPENSPACE_BASE_DIR}/src/network/osparallelconnection.cpp - ${OPENSPACE_BASE_DIR}/src/network/osparallelconnection_lua.inl + ${OPENSPACE_BASE_DIR}/src/network/parallelconnection.cpp + ${OPENSPACE_BASE_DIR}/src/network/parallelconnection_lua.inl ${OPENSPACE_BASE_DIR}/src/properties/matrixproperty.cpp ${OPENSPACE_BASE_DIR}/src/properties/optionproperty.cpp ${OPENSPACE_BASE_DIR}/src/properties/property.cpp @@ -111,7 +111,7 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/interaction/externalcontrol/pythonexternalcontrol.h ${OPENSPACE_BASE_DIR}/include/openspace/interaction/externalcontrol/randomexternalcontrol.h ${OPENSPACE_BASE_DIR}/include/openspace/network/networkengine.h - ${OPENSPACE_BASE_DIR}/include/openspace/network/osparallelconnection.h + ${OPENSPACE_BASE_DIR}/include/openspace/network/parallelconnection.h ${OPENSPACE_BASE_DIR}/include/openspace/properties/matrixproperty.h ${OPENSPACE_BASE_DIR}/include/openspace/properties/numericalproperty.h ${OPENSPACE_BASE_DIR}/include/openspace/properties/numericalproperty.inl diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index bae0532874..00568fec65 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -47,7 +47,7 @@ #include #include #include -#include +#include #include #include @@ -111,7 +111,7 @@ OpenSpaceEngine::OpenSpaceEngine(std::string programName) , _gui(new gui::GUI) , _isMaster(false) , _syncBuffer(nullptr) - , _parallelConnection(new network::OSParallelConnection) + , _parallelConnection(new network::ParallelConnection) { FactoryManager::initialize(); SpiceManager::initialize(); @@ -305,7 +305,7 @@ bool OpenSpaceEngine::initialize() { _scriptEngine->addLibrary(interaction::InteractionHandler::luaLibrary()); _scriptEngine->addLibrary(LuaConsole::luaLibrary()); _scriptEngine->addLibrary(gui::GUI::luaLibrary()); - _scriptEngine->addLibrary(network::OSParallelConnection::luaLibrary()); + _scriptEngine->addLibrary(network::ParallelConnection::luaLibrary()); // TODO: Maybe move all scenegraph and renderengine stuff to initializeGL scriptEngine()->initialize(); @@ -760,7 +760,7 @@ ModuleEngine* OpenSpaceEngine::moduleEngine() { return _moduleEngine; } -network::OSParallelConnection* OpenSpaceEngine::parallelConnection() { +network::ParallelConnection* OpenSpaceEngine::parallelConnection() { ghoul_assert(_parallelConnection != nullptr, "ParallelConnection is nullptr"); return _parallelConnection; } diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index 672e345cc5..3b1e2cbcb3 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -960,7 +960,7 @@ bool InteractionHandler::invertRotation() const { return _invertRotation; } -void InteractionHandler::addKeyframe(const network::Keyframe &kf){ +void InteractionHandler::addKeyframe(const network::StreamDataKeyframe &kf){ _keyframeMutex.lock(); //save a maximum of 10 samples (1 seconds of buffer) diff --git a/src/network/osparallelconnection.cpp b/src/network/parallelconnection.cpp similarity index 78% rename from src/network/osparallelconnection.cpp rename to src/network/parallelconnection.cpp index c9607cb648..f0bb6e8d4a 100644 --- a/src/network/osparallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -22,9 +22,6 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -//@TODO CHANGE THIS! -const int headerSize = 8; - #ifdef __WIN32__ #ifndef _ERRNO #define _ERRNO WSAGetLastError() @@ -57,21 +54,25 @@ const int headerSize = 8; #endif #endif -#include +//openspace includes +#include #include #include #include +#include +#include -#include "osparallelconnection_lua.inl" +//lua functions +#include "ParallelConnection_lua.inl" namespace{ - const std::string _loggerCat = "Parallel"; + const std::string _loggerCat = "ParallelConnection"; } namespace openspace { namespace network{ - OSParallelConnection::OSParallelConnection(): + ParallelConnection::ParallelConnection(): _passCode(0), _port("20501"), _address("127.0.0.1"), @@ -80,18 +81,19 @@ namespace openspace { _connectionThread(nullptr), _broadcastThread(nullptr), _isRunning(false), - _isHost(false) + _isHost(false), + _headerSize(headerSize()) { } - OSParallelConnection::~OSParallelConnection(){ + ParallelConnection::~ParallelConnection(){ disconnect(); } - void OSParallelConnection::clientConnect(){ + void ParallelConnection::clientConnect(){ if (!initNetworkAPI()){ - //error, handle this + LERROR("Failed to initialize network API for Parallel Connection"); } struct addrinfo *addresult = NULL, *ptr = NULL, hints; @@ -114,22 +116,18 @@ namespace openspace { #if defined(__WIN32__) WSACleanup(); #endif - std::cerr << "Failed to parse hints for connection!" << std::endl; + LERROR("Failed to parse hints for Parallel Connection"); } - // Attempt to connect to the first address returned by - // the call to getaddrinfo - ptr = addresult; + LINFO("Attempting to connect to address "<< _address << " on port " << _port); - std::cout << "Client started on port " << _port << std::endl; - - //start accept connections thread + //start connection thread _isRunning.store(true); - _connectionThread = new (std::nothrow) std::thread(&OSParallelConnection::connection, this, addresult); + _connectionThread = new (std::nothrow) std::thread(&ParallelConnection::connection, this, addresult); } - void OSParallelConnection::connection(addrinfo *info){ + void ParallelConnection::connection(addrinfo *info){ _clientSocket = socket(info->ai_family, info->ai_socktype, info->ai_protocol); @@ -138,7 +136,7 @@ namespace openspace { #if defined(__WIN32__) WSACleanup(); #endif - std::cerr << "Failed to InitializationRequest client socket!" << std::endl; + LERROR("Failed to create client socket, shutting down connection thread"); return; } @@ -162,6 +160,7 @@ namespace openspace { sizeof(timeout)); //set receive timeout + timeout = 0; //infinite result = setsockopt( _clientSocket, SOL_SOCKET, @@ -171,41 +170,46 @@ namespace openspace { result = setsockopt(_clientSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&flag, sizeof(int)); if (result == SOCKET_ERROR) - std::cout << "Failed to set reuse address with error:" << _ERRNO << std::endl; + LERROR("Failed to set socket option 'reuse address'. Error code: " << _ERRNO); result = setsockopt(_clientSocket, SOL_SOCKET, SO_KEEPALIVE, (char*)&flag, sizeof(int)); if (result == SOCKET_ERROR) - std::cout << "Failed to set keep alive with error: " << _ERRNO << std::endl; - - //try to connect to server + LERROR("Failed to set socket option 'keep alive'. Error code: " << _ERRNO); + + //while the connection thread is still running while (_isRunning.load()){ + //try to connect result = connect(_clientSocket, info->ai_addr, (int)info->ai_addrlen); + + //if the connection was successfull if (result != SOCKET_ERROR) { //send authentication authenticate(); //start listening for communication - communicate(); + //communicate(); } - //one sec sleep + //try to connect once per second std::this_thread::sleep_for(std::chrono::seconds(1)); } + //make sure to join the broadcast thread if started //dont delete it, that will be done in disconnect() function - if(_broadcastThread != nullptr && _isHost.load()){ - _isHost.store(false); - _broadcastThread->join(); - } +// if(_broadcastThread != nullptr && _isHost.load()){ +// _isHost.store(false); +// _broadcastThread->join(); +// } + //cleanup freeaddrinfo(info); } - void OSParallelConnection::authenticate(){ + void ParallelConnection::authenticate(){ uint16_t namelen = static_cast(_name.length()); - int size = headerSize + sizeof(uint32_t) + sizeof(uint16_t) + static_cast(namelen); + int size = headerSize() + sizeof(uint32_t) + sizeof(uint16_t) + static_cast(namelen); std::vector buffer; buffer.reserve(size); @@ -236,7 +240,7 @@ namespace openspace { } } - void OSParallelConnection::delegateDecoding(int type){ + void ParallelConnection::delegateDecoding(int type){ switch (type){ case MessageTypes::Authentication: decodeAuthenticationMessage(); @@ -244,7 +248,7 @@ namespace openspace { case MessageTypes::Initialization: decodeInitializationMessage(); break; - case MessageTypes::Data: + case MessageTypes::StreamData: decodeDataMessage(); break; case MessageTypes::Script: @@ -261,15 +265,15 @@ namespace openspace { } } - void OSParallelConnection::decodeAuthenticationMessage(){ + void ParallelConnection::decodeAuthenticationMessage(){ printf("Auth OK!\n"); //more stuff here later } - void OSParallelConnection::decodeInitializationMessage(){ + void ParallelConnection::decodeInitializationMessage(){ printf("Init message received!\n"); } - void OSParallelConnection::decodeDataMessage(){ + void ParallelConnection::decodeDataMessage(){ int result; uint16_t msglen; std::vector buffer; @@ -292,12 +296,12 @@ namespace openspace { return; } - network::Keyframe kf; + network::StreamDataKeyframe kf; kf.deserialize(buffer); OsEng.interactionHandler()->addKeyframe(kf); } - void OSParallelConnection::decodeScript(){ + void ParallelConnection::decodeScript(){ int result; uint16_t msglen; std::vector buffer; @@ -324,7 +328,7 @@ namespace openspace { OsEng.scriptEngine()->queueScript(script); } - void OSParallelConnection::decodeHostInfoMessage(){ + void ParallelConnection::decodeHostInfoMessage(){ std::vector hostflag; hostflag.resize(1); int result = receiveData(_clientSocket, hostflag, 1, 0); @@ -338,7 +342,7 @@ namespace openspace { else{ //start broadcasting _isHost.store(true); - _broadcastThread = new (std::nothrow) std::thread(&OSParallelConnection::broadcast, this); + _broadcastThread = new (std::nothrow) std::thread(&ParallelConnection::broadcast, this); } } else{ @@ -356,7 +360,7 @@ namespace openspace { } //request init packages from the host - int size = headerSize + sizeof(uint32_t); + int size = headerSize() + sizeof(uint32_t); std::vector buffer; buffer.reserve(size); @@ -381,18 +385,18 @@ namespace openspace { } } - void OSParallelConnection::decodeInitializationRequestMessage(){ + void ParallelConnection::decodeInitializationRequestMessage(){ printf("InitRequest message received!\n"); } - void OSParallelConnection::communicate(){ + void ParallelConnection::communicate(){ std::vector buffer; buffer.resize(8); int result; while (_isRunning.load()){ - result = receiveData(_clientSocket, buffer, headerSize, 0); + result = receiveData(_clientSocket, buffer, headerSize(), 0); if (result > 0){ if (buffer[0] == 'O' && //Open @@ -420,7 +424,7 @@ namespace openspace { } - int OSParallelConnection::receiveData(_SOCKET & socket, std::vector &buffer, int length, int flags){ + int ParallelConnection::receiveData(_SOCKET & socket, std::vector &buffer, int length, int flags){ int result = 0; int received = 0; while (result < length){ @@ -440,53 +444,53 @@ namespace openspace { return result; } - void OSParallelConnection::setPort(const std::string &port){ + void ParallelConnection::setPort(const std::string &port){ _port = port; } - std::string OSParallelConnection::port(){ + std::string ParallelConnection::port(){ return _port; } - void OSParallelConnection::setAddress(const std::string &address){ + void ParallelConnection::setAddress(const std::string &address){ _address = address; } - std::string OSParallelConnection::address(){ + std::string ParallelConnection::address(){ return _address; } - void OSParallelConnection::setName(const std::string& name){ + void ParallelConnection::setName(const std::string& name){ _name = name; } - std::string OSParallelConnection::name(){ + std::string ParallelConnection::name(){ return _name; } - void OSParallelConnection::setSocket(_SOCKET socket){ + void ParallelConnection::setSocket(_SOCKET socket){ _clientSocket = socket; } - _SOCKET OSParallelConnection::clientSocket(){ + _SOCKET ParallelConnection::clientSocket(){ return _clientSocket; } - void OSParallelConnection::setHost(bool host){ + void ParallelConnection::setHost(bool host){ _isHost.store(host); } - bool OSParallelConnection::isHost(){ + bool ParallelConnection::isHost(){ return _isHost.load(); } - bool OSParallelConnection::isRunning(){ + bool ParallelConnection::isRunning(){ return _isRunning.load(); } - void OSParallelConnection::requestHostship(){ + void ParallelConnection::requestHostship(){ std::vector buffer; - buffer.reserve(headerSize + sizeof(int)); + buffer.reserve(headerSize() + sizeof(int)); //header buffer.insert(buffer.end(), 'O'); buffer.insert(buffer.end(), 'S'); @@ -494,22 +498,22 @@ namespace openspace { buffer.insert(buffer.end(), 0); //type of message - int type = OSParallelConnection::MessageTypes::HostshipRequest; + int type = ParallelConnection::MessageTypes::HostshipRequest; buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(type)); //send message send(_clientSocket, buffer.data(), buffer.size(), 0); } - void OSParallelConnection::setPassword(const std::string& pwd){ + void ParallelConnection::setPassword(const std::string& pwd){ _passCode = hash(pwd); } - void OSParallelConnection::sendScript(const std::string script){ + void ParallelConnection::sendScript(const std::string script){ uint16_t msglen = static_cast(script.length()); std::vector buffer; - buffer.reserve(headerSize + sizeof(msglen) + msglen); + buffer.reserve(headerSize() + sizeof(msglen) + msglen); //header buffer.insert(buffer.end(), 'O'); @@ -518,7 +522,7 @@ namespace openspace { buffer.insert(buffer.end(), 0); //type of message - int type = OSParallelConnection::MessageTypes::Script; + int type = ParallelConnection::MessageTypes::Script; buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(type)); //size of message @@ -532,7 +536,8 @@ namespace openspace { } - void OSParallelConnection::disconnect(){ + void ParallelConnection::disconnect(){ + //must be run before trying to join communication threads, else the threads are stuck trying to receive data closeSocket(); @@ -557,7 +562,7 @@ namespace openspace { #endif } - void OSParallelConnection::closeSocket(){ + void ParallelConnection::closeSocket(){ if (_clientSocket != INVALID_SOCKET) { /* @@ -583,7 +588,7 @@ namespace openspace { } } - bool OSParallelConnection::initNetworkAPI(){ + bool ParallelConnection::initNetworkAPI(){ #if defined(__WIN32__) WSADATA wsaData; WORD version; @@ -609,11 +614,11 @@ namespace openspace { return true; } - void OSParallelConnection::broadcast(){ + void ParallelConnection::broadcast(){ while (_isHost.load()){ - network::Keyframe kf; + network::StreamDataKeyframe kf; kf._position = OsEng.interactionHandler()->camera()->position(); kf._viewRotationQuat = glm::quat_cast(OsEng.interactionHandler()->camera()->viewRotationMatrix()); @@ -626,7 +631,7 @@ namespace openspace { uint16_t msglen = static_cast(kfBuffer.size()); std::vector buffer; - buffer.reserve(headerSize + sizeof(msglen) + msglen); + buffer.reserve(headerSize() + sizeof(msglen) + msglen); //header buffer.insert(buffer.end(), 'O'); @@ -635,7 +640,7 @@ namespace openspace { buffer.insert(buffer.end(), 0); //type of message - int type = OSParallelConnection::MessageTypes::Data; + int type = ParallelConnection::MessageTypes::StreamData; buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(type)); //size of message @@ -651,8 +656,30 @@ namespace openspace { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } + + void ParallelConnection::writeHeader(std::vector &buffer){ + //make sure the buffer is large enough to hold at least the header + if(buffer.size() < headerSize()){ + buffer.reserve(headerSize()); + } + + //get the current running version of openspace + uint8_t versionMajor = static_cast(OPENSPACE_VERSION_MAJOR); + uint8_t versionMinor = static_cast(OPENSPACE_VERSION_MINOR); + + //insert header into buffer + buffer.insert(buffer.begin(), 'O'); + buffer.insert(buffer.begin(), 'S'); + buffer.insert(buffer.begin(), static_cast(versionMajor)); + buffer.insert(buffer.begin(), static_cast(versionMinor)); + } + + int ParallelConnection::headerSize(){ + //minor and major version (as uint8_t) + two bytes for the chars 'O' and 'S' + return 2 * sizeof(uint8_t) + 2; + } - scripting::ScriptEngine::LuaLibrary OSParallelConnection::luaLibrary() { + scripting::ScriptEngine::LuaLibrary ParallelConnection::luaLibrary() { return { "parallel", { diff --git a/src/network/osparallelconnection_lua.inl b/src/network/parallelconnection_lua.inl similarity index 100% rename from src/network/osparallelconnection_lua.inl rename to src/network/parallelconnection_lua.inl diff --git a/src/scripting/scriptengine.cpp b/src/scripting/scriptengine.cpp index 690820fef3..fa6708bc37 100644 --- a/src/scripting/scriptengine.cpp +++ b/src/scripting/scriptengine.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include From 13eb012a0457237a662be1280fc9c41b53db85cb Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 24 Jun 2015 17:51:32 +0200 Subject: [PATCH 204/329] added a current run time variable for the openspace engine + get/set method. run time is set in preSync from SGCT runtime at the moment --- apps/OpenSpace/main.cpp | 1 + include/openspace/engine/openspaceengine.h | 3 +++ src/engine/openspaceengine.cpp | 9 +++++++++ 3 files changed, 13 insertions(+) diff --git a/apps/OpenSpace/main.cpp b/apps/OpenSpace/main.cpp index 0edbe2edc9..92a18417ff 100644 --- a/apps/OpenSpace/main.cpp +++ b/apps/OpenSpace/main.cpp @@ -201,6 +201,7 @@ void mainInitFunc() { } void mainPreSyncFunc() { + OsEng.setRunTime(sgct::Engine::getTime()); OsEng.preSynchronization(); } diff --git a/include/openspace/engine/openspaceengine.h b/include/openspace/engine/openspaceengine.h index 98ba179966..a2160a7194 100644 --- a/include/openspace/engine/openspaceengine.h +++ b/include/openspace/engine/openspaceengine.h @@ -71,6 +71,8 @@ public: bool initialize(); bool isMaster(); void setMaster(bool master); + double runTime(); + void setRunTime(double t); static bool findConfiguration(std::string& filename); // Guaranteed to return a valid pointer @@ -131,6 +133,7 @@ private: gui::GUI* _gui; network::ParallelConnection* _parallelConnection; bool _isMaster; + double _runTime; SyncBuffer* _syncBuffer; }; diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 00568fec65..b7cc428445 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -110,6 +110,7 @@ OpenSpaceEngine::OpenSpaceEngine(std::string programName) , _moduleEngine(new ModuleEngine) , _gui(new gui::GUI) , _isMaster(false) + , _runTime(0.0) , _syncBuffer(nullptr) , _parallelConnection(new network::ParallelConnection) { @@ -586,7 +587,15 @@ bool OpenSpaceEngine::isMaster(){ void OpenSpaceEngine::setMaster(bool master){ _isMaster = master; } + +double OpenSpaceEngine::runTime(){ + return _runTime; +} +void OpenSpaceEngine::setRunTime(double d){ + _runTime = d; +} + void OpenSpaceEngine::preSynchronization() { FileSys.triggerFilesystemEvents(); if (_isMaster) { From ff49a7cdd25e9b9772ed525daaf1da2b953edaa6 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 24 Jun 2015 17:52:21 +0200 Subject: [PATCH 205/329] code cleanup, commenting, refactoring, and renaming. fixed a bug where threads would not close down correctly on Unix systems --- .../openspace/network/parallelconnection.h | 26 +- src/network/parallelconnection.cpp | 333 +++++++++--------- 2 files changed, 185 insertions(+), 174 deletions(-) diff --git a/include/openspace/network/parallelconnection.h b/include/openspace/network/parallelconnection.h index f4639400cd..558b78208b 100644 --- a/include/openspace/network/parallelconnection.h +++ b/include/openspace/network/parallelconnection.h @@ -119,14 +119,8 @@ namespace openspace{ std::string name(); - void setSocket(_SOCKET socket); - _SOCKET clientSocket(); - void setHost(bool host); - - bool isHost(); - bool isRunning(); void requestHostship(); @@ -175,27 +169,25 @@ namespace openspace{ return hashVal; }; - void writeHeader(std::vector &buffer); + void writeHeader(std::vector &buffer, uint32_t messageType); void closeSocket(); bool initNetworkAPI(); - void connection(addrinfo *info); + void tryConnect(addrinfo *info); - void authenticate(); + void sendAuthentication(); - void communicate(); + void listenCommunication(); - void delegateDecoding(int type); - - void decodeAuthenticationMessage(); + void delegateDecoding(uint32_t type); void decodeInitializationMessage(); - void decodeDataMessage(); + void decodeStreamDataMessage(); - void decodeScript(); + void decodeScriptMessage(); void decodeHostInfoMessage(); @@ -207,7 +199,6 @@ namespace openspace{ int receiveData(_SOCKET & socket, std::vector &buffer, int length, int flags); - int _headerSize; uint32_t _passCode; std::string _port; std::string _address; @@ -215,8 +206,9 @@ namespace openspace{ _SOCKET _clientSocket; std::thread *_connectionThread; std::thread *_broadcastThread; - std::atomic _isRunning; std::atomic _isHost; + std::atomic _isConnected; + std::atomic _isListening; }; } // namespace network diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index f0bb6e8d4a..4232cc79b8 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -80,11 +80,11 @@ namespace openspace { _clientSocket(INVALID_SOCKET), _connectionThread(nullptr), _broadcastThread(nullptr), - _isRunning(false), _isHost(false), - _headerSize(headerSize()) + _isConnected(false), + _isListening(false) { - + } ParallelConnection::~ParallelConnection(){ @@ -92,6 +92,11 @@ namespace openspace { } void ParallelConnection::clientConnect(){ + //we're already connected, do nothing + if(_isConnected.load()){ + return; + } + if (!initNetworkAPI()){ LERROR("Failed to initialize network API for Parallel Connection"); } @@ -107,10 +112,8 @@ namespace openspace { hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE; - int result; - // Resolve the local address and port to be used by the server - result = getaddrinfo(_address.c_str(), _port.c_str(), &hints, &addresult); + int result = getaddrinfo(_address.c_str(), _port.c_str(), &hints, &addresult); if (result != 0) { #if defined(__WIN32__) @@ -121,13 +124,15 @@ namespace openspace { LINFO("Attempting to connect to address "<< _address << " on port " << _port); - //start connection thread - _isRunning.store(true); - _connectionThread = new (std::nothrow) std::thread(&ParallelConnection::connection, this, addresult); + //we're not connected + _isConnected.store(false); + + //start connection thread + _connectionThread = new (std::nothrow) std::thread(&ParallelConnection::tryConnect, this, addresult); } - void ParallelConnection::connection(addrinfo *info){ + void ParallelConnection::tryConnect(addrinfo *info){ _clientSocket = socket(info->ai_family, info->ai_socktype, info->ai_protocol); @@ -144,14 +149,14 @@ namespace openspace { int result; //set no delay - result = setsockopt(_clientSocket, /* socket affected */ - IPPROTO_TCP, /* set option at TCP level */ - TCP_NODELAY, /* name of option */ - (char *)&flag, /* the cast is historical cruft */ - sizeof(int)); /* length of option value */ + result = setsockopt(_clientSocket, /* socket affected */ + IPPROTO_TCP, /* set option at TCP level */ + TCP_NODELAY, /* name of option */ + (char *)&flag, /* the cast is historical cruft */ + sizeof(int)); /* length of option value */ //set send timeout - int timeout = 0; //infinite + int timeout = 0; result = setsockopt( _clientSocket, SOL_SOCKET, @@ -160,14 +165,13 @@ namespace openspace { sizeof(timeout)); //set receive timeout - timeout = 0; //infinite result = setsockopt( _clientSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)); - + result = setsockopt(_clientSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&flag, sizeof(int)); if (result == SOCKET_ERROR) LERROR("Failed to set socket option 'reuse address'. Error code: " << _ERRNO); @@ -175,83 +179,84 @@ namespace openspace { result = setsockopt(_clientSocket, SOL_SOCKET, SO_KEEPALIVE, (char*)&flag, sizeof(int)); if (result == SOCKET_ERROR) LERROR("Failed to set socket option 'keep alive'. Error code: " << _ERRNO); - + + //while the connection thread is still running - while (_isRunning.load()){ - + while (!_isConnected.load()){ + //try to connect - result = connect(_clientSocket, info->ai_addr, (int)info->ai_addrlen); + result = connect(_clientSocket, info->ai_addr, (int)info->ai_addrlen); //if the connection was successfull - if (result != SOCKET_ERROR) - { - //send authentication - authenticate(); - - //start listening for communication - //communicate(); - } - - //try to connect once per second - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - - //make sure to join the broadcast thread if started - //dont delete it, that will be done in disconnect() function -// if(_broadcastThread != nullptr && _isHost.load()){ -// _isHost.store(false); -// _broadcastThread->join(); -// } + if (result != SOCKET_ERROR) + { + //we're connected + _isConnected.store(true); + + //and ready to start receiving messages + _isListening.store(true); + + //send authentication + sendAuthentication(); + + //start listening for communication + listenCommunication(); + } + + //try to connect once per second + std::this_thread::sleep_for(std::chrono::seconds(1)); + } //cleanup freeaddrinfo(info); } - void ParallelConnection::authenticate(){ + void ParallelConnection::sendAuthentication(){ + //length of this nodes name uint16_t namelen = static_cast(_name.length()); - int size = headerSize() + sizeof(uint32_t) + sizeof(uint16_t) + static_cast(namelen); - std::vector buffer; + + //total size of the buffer, header + size of passcodde + namelength + size of namelength + int size = headerSize() + sizeof(uint32_t) + sizeof(namelen) + static_cast(namelen); + + //create and reserve buffer + std::vector buffer; buffer.reserve(size); - //version - buffer.insert(buffer.end(), 'O'); - buffer.insert(buffer.end(), 'S'); - buffer.insert(buffer.end(), 0); - buffer.insert(buffer.end(), 0); + //write header to buffer + writeHeader(buffer, MessageTypes::Authentication); - //msg type, 0 = auth - int type = MessageTypes::Authentication; - buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(int)); - - //passcode + //write passcode to buffer buffer.insert(buffer.end(), reinterpret_cast(&_passCode), reinterpret_cast(&_passCode) + sizeof(uint32_t)); - //name length + //write the length of the nodes name to buffer buffer.insert(buffer.end(), reinterpret_cast(&namelen), reinterpret_cast(&namelen) + sizeof(uint16_t)); - //name + //write this nodes name to buffer buffer.insert(buffer.end(), _name.begin(), _name.end()); - + + //send buffer int result = send(_clientSocket, buffer.data(), size, 0); + //if send failed if (result == SOCKET_ERROR){ //failed to send auth msg. - std::cerr << "Failed to send authentication message!" << std::endl; + LERROR("Failed to send authentication message."); } } - void ParallelConnection::delegateDecoding(int type){ + void ParallelConnection::delegateDecoding(uint32_t type){ switch (type){ case MessageTypes::Authentication: - decodeAuthenticationMessage(); + //do nothing for now break; case MessageTypes::Initialization: decodeInitializationMessage(); break; case MessageTypes::StreamData: - decodeDataMessage(); + decodeStreamDataMessage(); break; case MessageTypes::Script: + decodeScriptMessage(); break; case MessageTypes::HostInfo: decodeHostInfoMessage(); @@ -265,47 +270,58 @@ namespace openspace { } } - void ParallelConnection::decodeAuthenticationMessage(){ - printf("Auth OK!\n"); //more stuff here later - } - void ParallelConnection::decodeInitializationMessage(){ printf("Init message received!\n"); } - void ParallelConnection::decodeDataMessage(){ + void ParallelConnection::decodeStreamDataMessage(){ int result; uint16_t msglen; - std::vector buffer; + + //create a buffer to hold the size of streamdata message + std::vector buffer; buffer.resize(sizeof(msglen)); + + //read size of streamdata message result = receiveData(_clientSocket, buffer, sizeof(msglen), 0); if (result <= 0){ //error return; } - + + //the size in bytes of the streamdata message msglen = (*(reinterpret_cast(buffer.data()))); + //resize the buffer to be able to read the streamdata buffer.clear(); buffer.resize(msglen); + //read the data into buffer result = receiveData(_clientSocket, buffer, msglen, 0); + if (result <= 0){ //error return; } + //construct a keyframe ffrom the streamdata network::StreamDataKeyframe kf; kf.deserialize(buffer); + + //and add the keyframe to the interaction handler OsEng.interactionHandler()->addKeyframe(kf); } - void ParallelConnection::decodeScript(){ + void ParallelConnection::decodeScriptMessage(){ int result; uint16_t msglen; + + //create buffer to decode size of script std::vector buffer; buffer.resize(sizeof(msglen)); + + //read size of received script result = receiveData(_clientSocket, buffer, sizeof(msglen), 0); if (result <= 0){ @@ -313,74 +329,89 @@ namespace openspace { return; } + //size of recived script msglen = (*(reinterpret_cast(buffer.data()))); + //clear and resize buffer to decode actual script buffer.clear(); buffer.resize(msglen); + //decode script result = receiveData(_clientSocket, buffer, msglen, 0); + if (result <= 0){ //error return; } + //construct a script (string) from the data contained in the buffer std::string script(buffer.data()); + + //tell the script engine to execute the script when appropriate OsEng.scriptEngine()->queueScript(script); } void ParallelConnection::decodeHostInfoMessage(){ + //create buffer std::vector hostflag; + //resize to hold a flag saying if we're host or not hostflag.resize(1); + + //read data into buffer int result = receiveData(_clientSocket, hostflag, 1, 0); + //enough data was read if (result > 0){ + + //we've been assigned as host if (hostflag.at(0) == 1){ + //we're already host, do nothing (dummy check) if (_isHost.load()){ return; } else{ + //start broadcasting _isHost.store(true); _broadcastThread = new (std::nothrow) std::thread(&ParallelConnection::broadcast, this); } } - else{ + else{ //we've been assigned as client + //we were broadcasting but should stop now if (_isHost.load()){ + + //stop broadcast loop _isHost.store(false); - if (_broadcastThread != nullptr){ + + //and delete broadcasting thread + if (_broadcastThread != nullptr){ _broadcastThread->join(); delete _broadcastThread; _broadcastThread = nullptr; } + } else{ - //we were not host so nothing to do + //we were not broadcasting so nothing to do } - //request init packages from the host - int size = headerSize() + sizeof(uint32_t); + //request init package from the host + int size = headerSize(); std::vector buffer; buffer.reserve(size); - //version - buffer.insert(buffer.end(), 'O'); - buffer.insert(buffer.end(), 'S'); - buffer.insert(buffer.end(), 0); - buffer.insert(buffer.end(), 0); - - //msg type, 0 = auth - int type = MessageTypes::InitializationRequest; - buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(int)); - + //write header + writeHeader(buffer, MessageTypes::InitializationRequest); + //send message send(_clientSocket, buffer.data(), buffer.size(), 0); } } else{ - std::cerr << "Error " << _ERRNO << " detected in connection!" << std::endl; + LERROR("Error " << _ERRNO << " detected in connection."); disconnect(); } } @@ -389,34 +420,44 @@ namespace openspace { printf("InitRequest message received!\n"); } - void ParallelConnection::communicate(){ + void ParallelConnection::listenCommunication(){ + //create basic buffer for receiving first part of messages std::vector buffer; - buffer.resize(8); + //size of the header + buffer.resize(headerSize()); + int result; - - while (_isRunning.load()){ + //while we're still connected and listening + while (_isListening.load()){ + //receive the first parts of a message result = receiveData(_clientSocket, buffer, headerSize(), 0); + //if enough data was received if (result > 0){ - if (buffer[0] == 'O' && //Open - buffer[1] == 'S' && //Space - buffer[2] == 0 && //version - buffer[3] == 0 //version + + //make sure that header matches this version of OpenSpace + if (buffer[0] == 'O' && //Open + buffer[1] == 'S' && //Space + buffer[2] == OPENSPACE_VERSION_MAJOR && // major version + buffer[3] == OPENSPACE_VERSION_MINOR // minor version ) { //parse type - int type = (*(reinterpret_cast(&buffer[4]))); + uint32_t type = (*(reinterpret_cast(&buffer[4]))); + + //and delegate decoding depending on type delegateDecoding(type); } } else{ if (result == 0){ //connection rejected - _isRunning.store(false); + _isConnected.store(false); + _isListening.store(false); } else{ - std::cerr << "Error " << _ERRNO << " detected in connection!" << std::endl; + LERROR("Error " << _ERRNO << " detected in connection!"); } break; } @@ -468,38 +509,16 @@ namespace openspace { return _name; } - void ParallelConnection::setSocket(_SOCKET socket){ - _clientSocket = socket; - } - _SOCKET ParallelConnection::clientSocket(){ return _clientSocket; } - void ParallelConnection::setHost(bool host){ - _isHost.store(host); - } - - bool ParallelConnection::isHost(){ - return _isHost.load(); - } - - bool ParallelConnection::isRunning(){ - return _isRunning.load(); - } - void ParallelConnection::requestHostship(){ std::vector buffer; - buffer.reserve(headerSize() + sizeof(int)); - //header - buffer.insert(buffer.end(), 'O'); - buffer.insert(buffer.end(), 'S'); - buffer.insert(buffer.end(), 0); - buffer.insert(buffer.end(), 0); - - //type of message - int type = ParallelConnection::MessageTypes::HostshipRequest; - buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(type)); + buffer.reserve(headerSize()); + + //write header + writeHeader(buffer, MessageTypes::HostshipRequest); //send message send(_clientSocket, buffer.data(), buffer.size(), 0); @@ -515,16 +534,9 @@ namespace openspace { std::vector buffer; buffer.reserve(headerSize() + sizeof(msglen) + msglen); - //header - buffer.insert(buffer.end(), 'O'); - buffer.insert(buffer.end(), 'S'); - buffer.insert(buffer.end(), 0); - buffer.insert(buffer.end(), 0); - - //type of message - int type = ParallelConnection::MessageTypes::Script; - buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(type)); - + //write header + writeHeader(buffer, MessageTypes::Script); + //size of message buffer.insert(buffer.end(), reinterpret_cast(&msglen), reinterpret_cast(&msglen) + sizeof(msglen)); @@ -541,22 +553,27 @@ namespace openspace { //must be run before trying to join communication threads, else the threads are stuck trying to receive data closeSocket(); - _isRunning.store(false); + //tell broadcast thread to stop broadcasting _isHost.store(false); + + //tell connection thread to stop trying to connect and stop listening for communication + _isConnected.store(true); + _isListening.store(false); + + //join connection thread and delete it if (_connectionThread != nullptr){ _connectionThread->join(); delete _connectionThread; _connectionThread = nullptr; } + //join broadcast thread and delete it if (_broadcastThread != nullptr){ _broadcastThread->join(); delete _broadcastThread; _broadcastThread = nullptr; } - - #if defined(__WIN32__) WSACleanup(); #endif @@ -581,7 +598,8 @@ namespace openspace { shutdown(_clientSocket, SD_BOTH); closesocket(_clientSocket); #else - shutdown(_clientSocket, SHUT_RDWR); + shutdown(_clientSocket, SHUT_RDWR); + close(_clientSocket); #endif _clientSocket = INVALID_SOCKET; @@ -603,7 +621,7 @@ namespace openspace { HIBYTE(wsaData.wVersion) != 2) { /* incorrect WinSock version */ - std::cerr << "Failed to init winsock API!" << std::endl; + LERROR("Failed to init winsock API."); WSACleanup(); return false; } @@ -616,32 +634,32 @@ namespace openspace { void ParallelConnection::broadcast(){ + //while we're still the host while (_isHost.load()){ + //create a keyframe with current position and orientation of camera network::StreamDataKeyframe kf; kf._position = OsEng.interactionHandler()->camera()->position(); kf._viewRotationQuat = glm::quat_cast(OsEng.interactionHandler()->camera()->viewRotationMatrix()); - //@TODO, implement method in openspace engine for this - kf._timeStamp = sgct::Engine::getTime(); + //timestamp as current runtime of OpenSpace instance + kf._timeStamp = OsEng.runTime(); - + //create a buffer for the keyframe std::vector kfBuffer; + + //fill the keyframe buffer kf.serialize(kfBuffer); + //get the size of the keyframebuffer uint16_t msglen = static_cast(kfBuffer.size()); + + //create the full buffer std::vector buffer; buffer.reserve(headerSize() + sizeof(msglen) + msglen); - //header - buffer.insert(buffer.end(), 'O'); - buffer.insert(buffer.end(), 'S'); - buffer.insert(buffer.end(), 0); - buffer.insert(buffer.end(), 0); - - //type of message - int type = ParallelConnection::MessageTypes::StreamData; - buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(type)); + //write header + writeHeader(buffer, MessageTypes::StreamData); //size of message buffer.insert(buffer.end(), reinterpret_cast(&msglen), reinterpret_cast(&msglen) + sizeof(msglen)); @@ -652,12 +670,12 @@ namespace openspace { //send message send(_clientSocket, buffer.data(), buffer.size(), 0); - //100 ms sleep + //100 ms sleep - send keyframes 10 times per second std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } - void ParallelConnection::writeHeader(std::vector &buffer){ + void ParallelConnection::writeHeader(std::vector &buffer, uint32_t messageType){ //make sure the buffer is large enough to hold at least the header if(buffer.size() < headerSize()){ buffer.reserve(headerSize()); @@ -668,15 +686,16 @@ namespace openspace { uint8_t versionMinor = static_cast(OPENSPACE_VERSION_MINOR); //insert header into buffer - buffer.insert(buffer.begin(), 'O'); - buffer.insert(buffer.begin(), 'S'); - buffer.insert(buffer.begin(), static_cast(versionMajor)); - buffer.insert(buffer.begin(), static_cast(versionMinor)); + buffer.insert(buffer.end(), 'O'); + buffer.insert(buffer.end(), 'S'); + buffer.insert(buffer.end(), versionMajor); + buffer.insert(buffer.end(), versionMinor); + buffer.insert(buffer.end(), reinterpret_cast(&messageType), reinterpret_cast(&messageType) + sizeof(messageType)); } int ParallelConnection::headerSize(){ - //minor and major version (as uint8_t) + two bytes for the chars 'O' and 'S' - return 2 * sizeof(uint8_t) + 2; + //minor and major version (as uint8_t -> 1 byte) + two bytes for the chars 'O' and 'S' + 4 bytes for type of message + return 2 * sizeof(uint8_t) + 2 + sizeof(uint32_t); } scripting::ScriptEngine::LuaLibrary ParallelConnection::luaLibrary() { From 77ecdcec7447789e88aaeb97d0122280febc7b83 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 24 Jun 2015 18:00:55 +0200 Subject: [PATCH 206/329] removed call to sendScript from script engine. Not all scripts should be synchronised --- src/scripting/scriptengine.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/scripting/scriptengine.cpp b/src/scripting/scriptengine.cpp index fa6708bc37..466c75cbf0 100644 --- a/src/scripting/scriptengine.cpp +++ b/src/scripting/scriptengine.cpp @@ -155,8 +155,6 @@ bool ScriptEngine::runScript(const std::string& script) { return false; } - OsEng.parallelConnection()->sendScript(script); - return true; } From 7fa460e9dc44c9d8c1e11add3547d7b772157f0a Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 24 Jun 2015 19:41:59 +0200 Subject: [PATCH 207/329] changed definition of Lua function slightly to incorporate a boolean defining if the script should be shared or not. added a constructor with the sharing variable as default to zero so every script doesn't have to be changed. added functionality in runScript function to check if a script should be shared and if so send it. --- include/openspace/scripting/scriptengine.h | 16 +++-- src/scripting/scriptengine.cpp | 78 +++++++++++++++++++--- 2 files changed, 78 insertions(+), 16 deletions(-) diff --git a/include/openspace/scripting/scriptengine.h b/include/openspace/scripting/scriptengine.h index 3e90dcdd92..94f52796d5 100644 --- a/include/openspace/scripting/scriptengine.h +++ b/include/openspace/scripting/scriptengine.h @@ -46,6 +46,17 @@ public: lua_CFunction function; std::string argumentText; std::string helpText; + bool parallelShared; + + Function(std::string n, lua_CFunction f, std::string a, std::string h , bool ps = false): + name(n), + function(f), + argumentText(a), + helpText(h), + parallelShared(ps) + { + + } }; std::string name; std::vector functions; @@ -78,8 +89,6 @@ public: void preSynchronization(); void queueScript(const std::string &script); - - std::vector executedScripts(); std::vector allLuaFunctions() const; @@ -101,9 +110,6 @@ private: std::vector _receivedScripts; std::string _currentSyncedScript; - //parallel variables @TODO make a more permanent solution to this - JK - std::vector _executedScripts; - std::mutex _executedScriptsMutex; }; } // namespace scripting diff --git a/src/scripting/scriptengine.cpp b/src/scripting/scriptengine.cpp index 466c75cbf0..ef7d0dac81 100644 --- a/src/scripting/scriptengine.cpp +++ b/src/scripting/scriptengine.cpp @@ -141,7 +141,7 @@ bool ScriptEngine::runScript(const std::string& script) { LWARNING("Script was empty"); return false; } - + int status = luaL_loadstring(_state, script.c_str()); if (status != LUA_OK) { LERROR("Error loading script: '" << lua_tostring(_state, -1) << "'"); @@ -155,6 +155,72 @@ bool ScriptEngine::runScript(const std::string& script) { return false; } + //if we're currently hosting the parallel session find out if script should be synchronized. + if(OsEng.parallelConnection()->isHost()){ + + //"deconstruct the script to find library and function name + //assuming a script looks like: "openspace.library.function()" + //or openspace.funcion() + std::string sub; + std::string lib; + std::string func; + //find first "." + std::size_t pos = script.find("."); + if(pos != std::string::npos){ + //strip "openspace." + sub = script.substr(pos + 1, script.size()); + pos = sub.find("."); + //one more "." was found, we have a library name + if(pos != std::string::npos){ + //assing library name + lib = sub.substr(0, pos); + //strip "library." + sub = sub.substr(pos + 1, sub.size()); + + pos = sub.find("("); + if(pos != std::string::npos && pos > 0){ + //strip the () and we're left with function name + func = sub.substr(0, pos); + } + } + else{ + //no more "." was found, we have the case of "openspace.funcion()" + pos = sub.find("("); + if(pos != std::string::npos && pos > 0){ + //strip the () and we're left with function name + func = sub.substr(0, pos); + } + } + } + + LuaLibrary *library = nullptr; + std::set::const_iterator libit; + for(libit = _registeredLibraries.cbegin(); + libit != _registeredLibraries.cend(); + ++libit){ + if(libit->name.compare(lib) == 0){ + break; + } + } + + std::vector::const_iterator funcit; + //library was found + if(libit != _registeredLibraries.cend()){ + for( funcit = libit->functions.cbegin(); + funcit != libit->functions.cend(); + ++funcit){ + //function was found! + if(funcit->name.compare(func) == 0){ + //is the function of a type that should be shared via parallel connection? + //and are we currently hosting the session? + if(funcit->parallelShared ){ + OsEng.parallelConnection()->sendScript(script); + } + } + } + } + } + return true; } @@ -498,10 +564,6 @@ void ScriptEngine::deserialize(SyncBuffer* syncBuffer){ _mutex.lock(); _receivedScripts.push_back(_currentSyncedScript); _mutex.unlock(); - - _executedScriptsMutex.lock(); - _executedScripts.push_back(_currentSyncedScript); - _executedScriptsMutex.unlock(); } } @@ -539,12 +601,6 @@ void ScriptEngine::queueScript(const std::string &script){ _mutex.unlock(); } - -std::vector ScriptEngine::executedScripts(){ - std::lock_guard lockGuard(_executedScriptsMutex); - return _executedScripts; -} - } // namespace scripting } // namespace openspace From c32841e0ac01b30141e2e9f02781d78130a2821d Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 24 Jun 2015 19:44:17 +0200 Subject: [PATCH 208/329] added a vector of executed scripts to parallel connection. vector is filled every time a script is sent or received --- include/openspace/network/parallelconnection.h | 4 +++- src/network/parallelconnection.cpp | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/include/openspace/network/parallelconnection.h b/include/openspace/network/parallelconnection.h index 558b78208b..0a73370d53 100644 --- a/include/openspace/network/parallelconnection.h +++ b/include/openspace/network/parallelconnection.h @@ -121,7 +121,7 @@ namespace openspace{ _SOCKET clientSocket(); - bool isRunning(); + bool isHost(); void requestHostship(); @@ -209,6 +209,8 @@ namespace openspace{ std::atomic _isHost; std::atomic _isConnected; std::atomic _isListening; + std::vector _sentScripts; + std::mutex _sentScriptsMutex; }; } // namespace network diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index 4232cc79b8..672bbb9091 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -349,6 +349,11 @@ namespace openspace { //tell the script engine to execute the script when appropriate OsEng.scriptEngine()->queueScript(script); + + //add script to all executed scripts + _sentScriptsMutex.lock(); + _sentScripts.push_back(script); + _sentScriptsMutex.unlock(); } void ParallelConnection::decodeHostInfoMessage(){ @@ -513,6 +518,10 @@ namespace openspace { return _clientSocket; } + bool ParallelConnection::isHost(){ + return _isHost.load(); + } + void ParallelConnection::requestHostship(){ std::vector buffer; buffer.reserve(headerSize()); @@ -529,6 +538,9 @@ namespace openspace { } void ParallelConnection::sendScript(const std::string script){ + _sentScriptsMutex.lock(); + _sentScripts.push_back(script); + _sentScriptsMutex.unlock(); uint16_t msglen = static_cast(script.length()); std::vector buffer; From 80df2a0abd16b0d3f9935672dc66b77064975384 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Thu, 25 Jun 2015 11:19:48 +0200 Subject: [PATCH 209/329] added decoding of requester ID in init request message --- src/network/parallelconnection.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index 672bbb9091..8eaf5eaa6a 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -422,7 +422,11 @@ namespace openspace { } void ParallelConnection::decodeInitializationRequestMessage(){ - printf("InitRequest message received!\n"); + std::vector buffer; + buffer.resize(sizeof(uint32_t)); + receiveData(_clientSocket, buffer, sizeof(uint32_t), 0); + uint32_t requesterID = *reinterpret_cast(buffer.data()); + printf("InitRequest message received from client %d!\n", requesterID); } void ParallelConnection::listenCommunication(){ From 2cd827a4fd5b87e9e41178c9369edd38d3e786a2 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Thu, 25 Jun 2015 11:28:18 +0200 Subject: [PATCH 210/329] added method to clear keyframes and call to that method when hostship is switched --- include/openspace/interaction/interactionhandler.h | 1 + src/interaction/interactionhandler.cpp | 5 +++++ src/network/parallelconnection.cpp | 3 +++ 3 files changed, 9 insertions(+) diff --git a/include/openspace/interaction/interactionhandler.h b/include/openspace/interaction/interactionhandler.h index ad1b828788..19ed741437 100644 --- a/include/openspace/interaction/interactionhandler.h +++ b/include/openspace/interaction/interactionhandler.h @@ -141,6 +141,7 @@ public: bool invertRotation() const; void addKeyframe(const network::StreamDataKeyframe &kf); + void clearKeyframes(); /** * Returns the Lua library that contains all Lua functions available to affect the diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index 3b1e2cbcb3..1dfaabe7ae 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -972,6 +972,11 @@ void InteractionHandler::addKeyframe(const network::StreamDataKeyframe &kf){ _keyframeMutex.unlock(); } +void InteractionHandler::clearKeyframes(){ + _keyframeMutex.lock(); + _keyframes.clear(); + _keyframeMutex.unlock(); +} } // namespace interaction //>>>>>>> feature/interactionhandler } // namespace openspace diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index 8eaf5eaa6a..ac05edfeb3 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -402,6 +402,9 @@ namespace openspace { //we were not broadcasting so nothing to do } + //clear buffered any keyframes + OsEng.interactionHandler()->clearKeyframes(); + //request init package from the host int size = headerSize(); std::vector buffer; From 46757113fdf3ed8ef962f514b22a05d79096a2da Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Thu, 25 Jun 2015 12:28:17 +0200 Subject: [PATCH 211/329] removed debug printf --- src/interaction/interactionhandler.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index 1dfaabe7ae..2db6f8e320 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -365,10 +365,7 @@ void InteractionHandler::update(double deltaTime) { double t0 = _keyframes[1]._timeStamp; double t1 = _keyframes[2]._timeStamp; double fact = (_currentKeyframeTime - t0) / (t1 - t0); - if (fact > 1.0){ - printf("%f\n", fact); - //fact = fmin(1.0, fact); - } + //glm::dvec4 v = positionInterpCR.interpolate(fact, _keyframes[0]._position.dvec4(), _keyframes[1]._position.dvec4(), _keyframes[2]._position.dvec4(), _keyframes[3]._position.dvec4()); glm::dvec4 v = positionInterpLin.interpolate(fact, _keyframes[1]._position.dvec4(), _keyframes[2]._position.dvec4()); psc pos(v.x, v.y, v.z, v.w); From 22af992078c365253042dd883dd4a1c25bd75c7b Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Thu, 25 Jun 2015 12:31:11 +0200 Subject: [PATCH 212/329] removed a comment, changed name of script storing variable to something that makes more sense --- src/network/parallelconnection.cpp | 12 ++++++------ src/scripting/scriptengine.cpp | 3 +-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index ac05edfeb3..f87f6c897e 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -351,9 +351,9 @@ namespace openspace { OsEng.scriptEngine()->queueScript(script); //add script to all executed scripts - _sentScriptsMutex.lock(); - _sentScripts.push_back(script); - _sentScriptsMutex.unlock(); + _executedScriptsMutex.lock(); + _executedScripts.push_back(script); + _executedScriptsMutex.unlock(); } void ParallelConnection::decodeHostInfoMessage(){ @@ -545,9 +545,9 @@ namespace openspace { } void ParallelConnection::sendScript(const std::string script){ - _sentScriptsMutex.lock(); - _sentScripts.push_back(script); - _sentScriptsMutex.unlock(); + _executedScriptsMutex.lock(); + _executedScripts.push_back(script); + _executedScriptsMutex.unlock(); uint16_t msglen = static_cast(script.length()); std::vector buffer; diff --git a/src/scripting/scriptengine.cpp b/src/scripting/scriptengine.cpp index ef7d0dac81..e528145b77 100644 --- a/src/scripting/scriptengine.cpp +++ b/src/scripting/scriptengine.cpp @@ -155,7 +155,7 @@ bool ScriptEngine::runScript(const std::string& script) { return false; } - //if we're currently hosting the parallel session find out if script should be synchronized. + //if we're currently hosting the parallel session, find out if script should be synchronized. if(OsEng.parallelConnection()->isHost()){ //"deconstruct the script to find library and function name @@ -212,7 +212,6 @@ bool ScriptEngine::runScript(const std::string& script) { //function was found! if(funcit->name.compare(func) == 0){ //is the function of a type that should be shared via parallel connection? - //and are we currently hosting the session? if(funcit->parallelShared ){ OsEng.parallelConnection()->sendScript(script); } From 4e72e248c3908af0676a45e288a5d1763995fc8f Mon Sep 17 00:00:00 2001 From: Anton Arbring Date: Thu, 25 Jun 2015 20:07:21 -0400 Subject: [PATCH 213/329] Fixing bug in model magnification --- modules/base/rendering/modelgeometry.cpp | 2 +- modules/base/rendering/modelgeometry.h | 2 +- modules/base/rendering/wavefrontgeometry.cpp | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/modules/base/rendering/modelgeometry.cpp b/modules/base/rendering/modelgeometry.cpp index 0dd31dea6c..e29e3adaf8 100644 --- a/modules/base/rendering/modelgeometry.cpp +++ b/modules/base/rendering/modelgeometry.cpp @@ -76,7 +76,7 @@ ModelGeometry::ModelGeometry(const ghoul::Dictionary& dictionary) success = dictionary.getValue(keySize, _magnification); if (!success) - _magnification = 4; // if not set, models will be 1:1000, feel free to change @AA + _magnification = 0; // if not set, models will be 1:1 (earlier 1:1000) @AA success = dictionary.getValue(keyObjFile, _file); if (!success) { diff --git a/modules/base/rendering/modelgeometry.h b/modules/base/rendering/modelgeometry.h index 70450a8fef..c49b7b36f5 100644 --- a/modules/base/rendering/modelgeometry.h +++ b/modules/base/rendering/modelgeometry.h @@ -59,7 +59,7 @@ namespace openspace { bool loadObj(const std::string& filename); bool loadCachedFile(const std::string& filename); bool saveCachedFile(const std::string& filename); - int _magnification; + float _magnification; GLuint _vaoID; GLuint _vbo; diff --git a/modules/base/rendering/wavefrontgeometry.cpp b/modules/base/rendering/wavefrontgeometry.cpp index 4717d6615a..999e43bbbd 100644 --- a/modules/base/rendering/wavefrontgeometry.cpp +++ b/modules/base/rendering/wavefrontgeometry.cpp @@ -30,8 +30,6 @@ namespace { const std::string _loggerCat = "WavefrontGeometry"; - const std::string keyObjFile = "ObjFile"; -// const int8_t CurrentCacheVersion = 3; } namespace openspace { From 901ed6053eaf2d65639db2b5941c5bcef153b712 Mon Sep 17 00:00:00 2001 From: Anton Arbring Date: Thu, 25 Jun 2015 20:08:35 -0400 Subject: [PATCH 214/329] Adding support for imageplanes for multiple instruments --- modules/newhorizons/util/imagesequencer2.cpp | 14 +++++++++----- modules/newhorizons/util/imagesequencer2.h | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/modules/newhorizons/util/imagesequencer2.cpp b/modules/newhorizons/util/imagesequencer2.cpp index aea8dd4238..812b9ce9cd 100644 --- a/modules/newhorizons/util/imagesequencer2.cpp +++ b/modules/newhorizons/util/imagesequencer2.cpp @@ -46,8 +46,7 @@ namespace openspace { ImageSequencer2* ImageSequencer2::_instance = nullptr; ImageSequencer2::ImageSequencer2() - : _latestImage() - , _hasData(false) + : _hasData(false) {} ImageSequencer2& ImageSequencer2::ref() { @@ -161,8 +160,13 @@ double ImageSequencer2::getNextCaptureTime(){ return nextCaptureTime; } const Image ImageSequencer2::getLatestImageForInstrument(const std::string _instrumentID){ - - return _latestImage; + auto it = _latestImages.find(_instrumentID); + if (it != _latestImages.end()) + return _latestImages[_instrumentID]; + else { + Image dummyImage = { 0, 0, "", std::vector(), "", false }; + return dummyImage; + } } std::map ImageSequencer2::getActiveInstruments(){ @@ -270,7 +274,7 @@ bool ImageSequencer2::getImagePaths(std::vector& captures, std::reverse(captureTimes.begin(), captureTimes.end()); captures = captureTimes; if (!captures.empty()) - _latestImage = captures.back(); + _latestImages[captures.back().activeInstruments.front()] = captures.back(); return true; } diff --git a/modules/newhorizons/util/imagesequencer2.h b/modules/newhorizons/util/imagesequencer2.h index 0a30437740..722a37e86b 100644 --- a/modules/newhorizons/util/imagesequencer2.h +++ b/modules/newhorizons/util/imagesequencer2.h @@ -196,7 +196,7 @@ private: // default capture image std::string _defaultCaptureImage; - Image _latestImage; + std::map _latestImages; // if no data, no run bool _hasData; }; From 927cfc27664b08600157b54fafea7d55f68ba07a Mon Sep 17 00:00:00 2001 From: Anton Arbring Date: Thu, 25 Jun 2015 20:12:37 -0400 Subject: [PATCH 215/329] Adding the possibility to project to arbitrary bodies model files Class RenderableModelProjection, using the geometry position and texture coordinate data instead of reverse engineering the geometry in shader. Adding the shaders used to project and the modified model shader. Adding the new Renderable class to newHorizons module and CMakelist --- modules/newhorizons/CMakeLists.txt | 6 + modules/newhorizons/newhorizonsmodule.cpp | 2 + .../rendering/renderablemodelprojection.cpp | 531 ++++++++++++++++++ .../rendering/renderablemodelprojection.h | 147 +++++ .../newhorizons/shaders/modelShader_fs.glsl | 96 ++++ .../newhorizons/shaders/modelShader_vs.glsl | 60 ++ .../shaders/projectionPass_fs.glsl | 67 +++ .../shaders/projectionPass_vs.glsl | 60 ++ 8 files changed, 969 insertions(+) create mode 100644 modules/newhorizons/rendering/renderablemodelprojection.cpp create mode 100644 modules/newhorizons/rendering/renderablemodelprojection.h create mode 100644 modules/newhorizons/shaders/modelShader_fs.glsl create mode 100644 modules/newhorizons/shaders/modelShader_vs.glsl create mode 100644 modules/newhorizons/shaders/projectionPass_fs.glsl create mode 100644 modules/newhorizons/shaders/projectionPass_vs.glsl diff --git a/modules/newhorizons/CMakeLists.txt b/modules/newhorizons/CMakeLists.txt index fa6a7a06b4..6b9cd1d8b7 100644 --- a/modules/newhorizons/CMakeLists.txt +++ b/modules/newhorizons/CMakeLists.txt @@ -32,6 +32,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplanetprojection.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableshadowcylinder.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometryprojection.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablemodelprojection.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/writeToTexture.h ${CMAKE_CURRENT_SOURCE_DIR}/util/decoder.h ${CMAKE_CURRENT_SOURCE_DIR}/util/hongkangparser.h @@ -53,6 +54,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplanetprojection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableshadowcylinder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometryprojection.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablemodelprojection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/decoder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/hongkangparser.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer.cpp @@ -72,6 +74,10 @@ set(SHADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/shaders/fov_vs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/projectiveTexture_fs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/projectiveTexture_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/projectionPass_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/projectionPass_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/modelShader_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/modelShader_vs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/terminatorshadow_fs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/terminatorshadow_vs.glsl ) diff --git a/modules/newhorizons/newhorizonsmodule.cpp b/modules/newhorizons/newhorizonsmodule.cpp index 8a1570fcf2..72a59c096e 100644 --- a/modules/newhorizons/newhorizonsmodule.cpp +++ b/modules/newhorizons/newhorizonsmodule.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -69,6 +70,7 @@ bool NewHorizonsModule::initialize() { fRenderable->registerClass("RenderableFov"); fRenderable->registerClass("RenderablePlaneProjection"); fRenderable->registerClass("RenderablePlanetProjection"); + fRenderable->registerClass("RenderableModelProjection"); auto fPlanetGeometryProjection = FactoryManager::ref().factory(); fPlanetGeometryProjection->registerClass("SimpleSphereProjection"); diff --git a/modules/newhorizons/rendering/renderablemodelprojection.cpp b/modules/newhorizons/rendering/renderablemodelprojection.cpp new file mode 100644 index 0000000000..af0244403c --- /dev/null +++ b/modules/newhorizons/rendering/renderablemodelprojection.cpp @@ -0,0 +1,531 @@ +/***************************************************************************************** +* * +* OpenSpace * +* * +* Copyright (c) 2014-2015 * +* * +* 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. * +****************************************************************************************/ + +// open space includes +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include "imgui.h" + +#define _USE_MATH_DEFINES +#include +#include + +namespace { + const std::string _loggerCat = "RenderableModelProjection"; + const std::string keySource = "Rotation.Source"; + const std::string keyDestination = "Rotation.Destination"; + const std::string keyBody = "Body"; + const std::string keyGeometry = "Geometry"; + + const std::string keyTextureColor = "Textures.Color"; + const std::string keyTextureProject = "Textures.Project"; + const std::string keyTextureDefault = "Textures.Default"; + + const std::string keySequenceDir = "Projection.Sequence"; + const std::string keySequenceType = "Projection.SequenceType"; + const std::string keyProjObserver = "Projection.Observer"; + const std::string keyProjTarget = "Projection.Target"; + const std::string keyProjAberration = "Projection.Aberration"; + + const std::string keyInstrument = "Instrument.Name"; + const std::string keyInstrumentFovy = "Instrument.Fovy"; + const std::string keyInstrumentAspect = "Instrument.Aspect"; + const std::string keyInstrumentNear = "Instrument.Near"; + const std::string keyInstrumentFar = "Instrument.Far"; + + const std::string keyTranslation = "DataInputTranslation"; + const std::string sequenceTypeImage = "image-sequence"; + +} + +namespace openspace { + + RenderableModelProjection::RenderableModelProjection(const ghoul::Dictionary& dictionary) + : Renderable(dictionary) + , _colorTexturePath("colorTexture", "Color Texture") + , _projectionTexturePath("projectionTexture", "RGB Texture") + , _rotationX("rotationX", "RotationX", 0, 0, 360) + , _rotationY("rotationY", "RotationY", 0, 0, 360) + , _rotationZ("rotationZ", "RotationZ", 0, 0, 360) + , _programObject(nullptr) + , _fboProgramObject(nullptr) + , _texture(nullptr) + , _geometry(nullptr) + , _textureOriginal(nullptr) + , _textureProj(nullptr) + , _textureWhiteSquare(nullptr) + , _alpha(1.f) + , _performShading("performShading", "Perform Shading", true) + , _performProjection("performProjection", "Perform Projections", true) + , _frameCount(0) + , _programIsDirty(false) + { + std::string name; + bool success = dictionary.getValue(constants::scenegraphnode::keyName, name); + ghoul_assert(success, "Name was not passed to RenderableModelProjection"); + + ghoul::Dictionary geometryDictionary; + success = dictionary.getValue(keyGeometry, geometryDictionary); + if (success) { + geometryDictionary.setValue(constants::scenegraphnode::keyName, name); + _geometry = modelgeometry::ModelGeometry::createFromDictionary(geometryDictionary); + } + + std::string texturePath = ""; + success = dictionary.getValue(keyTextureColor, texturePath); + if (success) + _colorTexturePath = absPath(texturePath); + + success = dictionary.getValue(keyTextureProject, texturePath); + if (success) + _projectionTexturePath = absPath(texturePath); + + success = dictionary.getValue(keyTextureDefault, texturePath); + if (success) + _defaultProjImage = absPath(texturePath); + + addPropertySubOwner(_geometry); + + addProperty(_colorTexturePath); + addProperty(_projectionTexturePath); + _colorTexturePath.onChange(std::bind(&RenderableModelProjection::loadTexture, this)); + _projectionTexturePath.onChange(std::bind(&RenderableModelProjection::loadProjectionTexture, this)); + + dictionary.getValue(keySource, _source); + dictionary.getValue(keyDestination, _destination); + dictionary.getValue(keyBody, _target); + if (_target != "") + setBody(_target); + + bool completeSuccess = true; + completeSuccess &= dictionary.getValue(keyInstrument, _instrumentID); + completeSuccess &= dictionary.getValue(keyProjObserver, _projectorID); + completeSuccess &= dictionary.getValue(keyProjTarget, _projecteeID); + completeSuccess &= dictionary.getValue(keyInstrumentFovy, _fovy); + completeSuccess &= dictionary.getValue(keyInstrumentAspect, _aspectRatio); + completeSuccess &= dictionary.getValue(keyInstrumentNear, _nearPlane); + completeSuccess &= dictionary.getValue(keyInstrumentFar, _farPlane); + ghoul_assert(completeSuccess, "All neccessary attributes not found in modfile"); + + completeSuccess = dictionary.getValue(keyProjAberration, _aberration); + if (!completeSuccess) + _aberration = "NONE"; + + openspace::SpiceManager::ref().addFrame(_target, _source); + setBoundingSphere(pss(1.f, 9.f)); + + addProperty(_performShading); + addProperty(_performProjection); + addProperty(_rotationX); + addProperty(_rotationY); + addProperty(_rotationZ); + + SequenceParser* parser; + + bool foundSequence = dictionary.getValue(keySequenceDir, _sequenceSource); + if (foundSequence) { + _sequenceSource = absPath(_sequenceSource); + + foundSequence = dictionary.getValue(keySequenceType, _sequenceType); + //Important: client must define translation-list in mod file IFF playbook + if (dictionary.hasKey(keyTranslation)) { + ghoul::Dictionary translationDictionary; + //get translation dictionary + dictionary.getValue(keyTranslation, translationDictionary); + if (_sequenceType == sequenceTypeImage) { + parser = new LabelParser(_sequenceSource, translationDictionary); + openspace::ImageSequencer2::ref().runSequenceParser(parser); + + } + } + else { + LWARNING("No translation provided, please make sure all spice calls match playbook!"); + } + } + + } + + bool RenderableModelProjection::isReady() const { + bool ready = true; + ready &= (_programObject != nullptr); + ready &= (_texture != nullptr); + return ready; + } + + bool RenderableModelProjection::initialize() { + bool completeSuccess = true; + + if (_programObject == nullptr) { + _programObject = ghoul::opengl::ProgramObject::Build("ModelShader", + "${MODULES}/newhorizons/shaders/modelShader_vs.glsl", + "${MODULES}/newhorizons/shaders/modelShader_fs.glsl"); + if (!_programObject) + return false; + } + _programObject->setProgramObjectCallback([&](ghoul::opengl::ProgramObject*) { this->_programIsDirty = true; } ); + + if (_fboProgramObject == nullptr) { + _fboProgramObject = ghoul::opengl::ProgramObject::Build("ProjectionPass", + "${MODULES}/newhorizons/shaders/projectionPass_vs.glsl", + "${MODULES}/newhorizons/shaders/projectionPass_fs.glsl"); + if (!_fboProgramObject) + return false; + } + _fboProgramObject->setProgramObjectCallback([&](ghoul::opengl::ProgramObject*) { this->_programIsDirty = true; } ); + + loadTexture(); + loadProjectionTexture(); + + completeSuccess &= (_texture != nullptr); + completeSuccess &= (_textureOriginal != nullptr); + completeSuccess &= (_textureProj != nullptr); + completeSuccess &= (_textureWhiteSquare != nullptr); + + completeSuccess &= _geometry->initialize(this); + completeSuccess &= !_source.empty(); + completeSuccess &= !_destination.empty(); + + + bool gotverts = _geometry->getVertices(&_geometryVertecies) && _geometry->getIndices(&_geometryIndeces); + if (!gotverts) + LWARNING("Lack of vertex data from geometry for image projection"); + + completeSuccess &= auxiliaryRendertarget(); + + return completeSuccess; + } + + bool RenderableModelProjection::auxiliaryRendertarget() { + bool completeSuccess = true; + // set FBO to texture to project to + glGenFramebuffers(1, &_fboID); + glBindFramebuffer(GL_FRAMEBUFFER, _fboID); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *_texture, 0); + // check FBO status + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) + completeSuccess &= false; + // switch back to window-system-provided framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + int vertexSize = sizeof(modelgeometry::ModelGeometry::Vertex); + + glGenVertexArrays(1, &_vaoID); + glGenBuffers(1, &_vbo); + glGenBuffers(1, &_ibo); + + glBindVertexArray(_vaoID); + glBindBuffer(GL_ARRAY_BUFFER, _vbo); + glBufferData(GL_ARRAY_BUFFER, _geometryVertecies.size() * vertexSize, &_geometryVertecies[0], GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, vertexSize, + reinterpret_cast(offsetof(modelgeometry::ModelGeometry::Vertex, location))); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, vertexSize, + reinterpret_cast(offsetof(modelgeometry::ModelGeometry::Vertex, tex))); + glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, vertexSize, + reinterpret_cast(offsetof(modelgeometry::ModelGeometry::Vertex, normal))); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ibo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, _geometryIndeces.size() * sizeof(int), &_geometryIndeces[0], GL_STATIC_DRAW); + + glBindVertexArray(0); + + + return completeSuccess; + } + + bool RenderableModelProjection::deinitialize() { + if (_geometry) { + _geometry->deinitialize(); + delete _geometry; + } + + if (_texture) + delete _texture; + if (_textureProj) + delete _textureProj; + if (_textureOriginal) + delete _textureOriginal; + if (_textureWhiteSquare) + delete _textureWhiteSquare; + + _geometry = nullptr; + _texture = nullptr; + _textureProj = nullptr; + _textureOriginal = nullptr; + _textureWhiteSquare = nullptr; + + glDeleteBuffers(1, &_vbo); + + return true; + } + + void RenderableModelProjection::render(const RenderData& data) { + if (!_programObject) return; + if (!_textureProj) return; + _programObject->activate(); + _frameCount++; + + _camScaling = data.camera.scaling(); + _up = data.camera.lookUpVector(); + + if (_capture && _performProjection) + project(); + + attitudeParameters(_time); + _imageTimes.clear(); + + double time = openspace::Time::ref().currentTime(); + bool targetPositionCoverage = openspace::SpiceManager::ref().hasSpkCoverage(_target, time); + if (!targetPositionCoverage) { + int frame = _frameCount % 180; + + float fadingFactor = static_cast(sin((frame * M_PI) / 180)); + _alpha = 0.5f + fadingFactor * 0.5f; + } + else + _alpha = 1.0f; + + _programObject->setUniform("ProjectorMatrix", _projectorMatrix); + _programObject->setUniform("boresight", _boresight); + _programObject->setUniform("_performShading", _performShading); + _programObject->setUniform("sun_pos", _sunPosition.vec3()); + _viewProjection = data.camera.viewProjectionMatrix(); + _programObject->setUniform("ViewProjection", _viewProjection); + _programObject->setUniform("ModelTransform", _transform); + setPscUniforms(_programObject, &data.camera, data.position); + + textureBind(); + _geometry->render(); + + // disable shader + _programObject->deactivate(); + } + + void RenderableModelProjection::update(const UpdateData& data) { + if (_programIsDirty) { + _programObject->rebuildFromFile(); + _fboProgramObject->rebuildFromFile(); + _programIsDirty = false; + } + + _time = data.time; + + if (openspace::ImageSequencer2::ref().isReady() && _performProjection) { + openspace::ImageSequencer2::ref().updateSequencer(_time); + _capture = openspace::ImageSequencer2::ref().getImagePaths(_imageTimes, _projecteeID, _instrumentID); + } + + // set spice-orientation in accordance to timestamp + if (!_source.empty()) + openspace::SpiceManager::ref().getPositionTransformMatrix(_source, _destination, _time, _stateMatrix); + + double lt; + openspace::SpiceManager::ref().getTargetPosition("SUN", _target, "GALACTIC", "NONE", _time, _sunPosition, lt); + } + + void RenderableModelProjection::imageProjectGPU() { + + // keep handle to the current bound FBO + GLint defaultFBO; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); + + GLint m_viewport[4]; + glGetIntegerv(GL_VIEWPORT, m_viewport); + glBindFramebuffer(GL_FRAMEBUFFER, _fboID); + // set blend eq + glEnable(GL_BLEND); + glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ZERO); + + glViewport(0, 0, static_cast(_texture->width()), static_cast(_texture->height())); + _fboProgramObject->activate(); + + ghoul::opengl::TextureUnit unitFboProject; + unitFboProject.activate(); + _textureProj->bind(); + _fboProgramObject->setUniform("projectTexture", unitFboProject); + + ghoul::opengl::TextureUnit unitFboCurrent; + unitFboCurrent.activate(); + _texture->bind(); + _fboProgramObject->setUniform("currentTexture", unitFboCurrent); + _fboProgramObject->setUniform("ProjectorMatrix", _projectorMatrix); + _fboProgramObject->setUniform("ModelTransform", _transform); + _fboProgramObject->setUniform("_scaling", _camScaling); + _fboProgramObject->setUniform("boresight", _boresight); + + glBindVertexArray(_vaoID); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ibo); + glDrawElements(GL_TRIANGLES, static_cast(_geometryIndeces.size()), GL_UNSIGNED_INT, 0); + glBindVertexArray(0); + + _fboProgramObject->deactivate(); + glDisable(GL_BLEND); + //bind back to default + glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); + glViewport(m_viewport[0], m_viewport[1], + m_viewport[2], m_viewport[3]); + + } + + void RenderableModelProjection::attitudeParameters(double time) { + openspace::SpiceManager::ref().getPositionTransformMatrix(_source, _destination, time, _stateMatrix); + openspace::SpiceManager::ref().getPositionTransformMatrix(_instrumentID, _destination, time, _instrumentMatrix); + + _transform = glm::mat4(1); + + glm::mat4 rotPropX = glm::rotate(_transform, static_cast(_rotationX), glm::vec3(1, 0, 0)); + glm::mat4 rotPropY = glm::rotate(_transform, static_cast(_rotationY), glm::vec3(0, 1, 0)); + glm::mat4 rotPropZ = glm::rotate(_transform, static_cast(_rotationZ), glm::vec3(0, 0, 1)); + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + _transform[i][j] = static_cast(_stateMatrix[i][j]); + } + } + _transform = _transform * rotPropX * rotPropY * rotPropZ; + + std::string shape, instrument; + std::vector bounds; + glm::dvec3 boresight; + bool found = openspace::SpiceManager::ref().getFieldOfView(_instrumentID, shape, instrument, boresight, bounds); + if (!found) + return; + + double lightTime; + psc position; //observer target + found = SpiceManager::ref().getTargetPosition(_projectorID, _projecteeID, _destination, _aberration, time, position, lightTime); + + position[3] += (3 + _camScaling[1]); + glm::vec3 cpos = position.vec3(); + + _projectorMatrix = computeProjectorMatrix(cpos, boresight, _up); + } + + glm::mat4 RenderableModelProjection::computeProjectorMatrix(const glm::vec3 loc, glm::dvec3 aim, const glm::vec3 up) { + //rotate boresight into correct alignment + _boresight = _instrumentMatrix*aim; + glm::vec3 uptmp(_instrumentMatrix*glm::dvec3(up)); + + // create view matrix + glm::vec3 e3 = glm::normalize(_boresight); + glm::vec3 e1 = glm::normalize(glm::cross(uptmp, e3)); + glm::vec3 e2 = glm::normalize(glm::cross(e3, e1)); + glm::mat4 projViewMatrix = glm::mat4(e1.x, e2.x, e3.x, 0.f, + e1.y, e2.y, e3.y, 0.f, + e1.z, e2.z, e3.z, 0.f, + -glm::dot(e1, loc), -glm::dot(e2, loc), -glm::dot(e3, loc), 1.f); + + // create perspective projection matrix + glm::mat4 projProjectionMatrix = glm::perspective(_fovy, _aspectRatio, _nearPlane, _farPlane); + // bias matrix + glm::mat4 projNormalizationMatrix = glm::mat4(0.5f, 0, 0, 0, + 0, 0.5f, 0, 0, + 0, 0, 0.5f, 0, + 0.5f, 0.5f, 0.5f, 1); + return projNormalizationMatrix*projProjectionMatrix*projViewMatrix; + } + + + void RenderableModelProjection::textureBind() { + ghoul::opengl::TextureUnit unit[2]; + unit[0].activate(); + _texture->bind(); + _programObject->setUniform("currentTexture", unit[0]); + unit[1].activate(); + _textureWhiteSquare->bind(); + _programObject->setUniform("projectedTexture", unit[1]); + } + + void RenderableModelProjection::project() { + for (auto img : _imageTimes) { + std::thread t1(&RenderableModelProjection::attitudeParameters, this, img.startTime); + t1.join(); + _projectionTexturePath = img.path; + imageProjectGPU(); //fbopass + } + _capture = false; + } + + void RenderableModelProjection::loadTexture() { + delete _texture; + _texture = nullptr; + if (_colorTexturePath.value() != "") { + _texture = ghoul::io::TextureReader::ref().loadTexture(absPath(_colorTexturePath)); + if (_texture) { + LDEBUG("Loaded texture from '" << absPath(_colorTexturePath) << "'"); + _texture->uploadTexture(); + _texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); + } + } + delete _textureOriginal; + _textureOriginal = nullptr; + if (_colorTexturePath.value() != "") { + _textureOriginal = ghoul::io::TextureReader::ref().loadTexture(absPath(_colorTexturePath)); + if (_textureOriginal) { + LDEBUG("Loaded texture from '" << absPath(_colorTexturePath) << "'"); + _textureOriginal->uploadTexture(); + _textureOriginal->setFilter(ghoul::opengl::Texture::FilterMode::Linear); + } + } + delete _textureWhiteSquare; + _textureWhiteSquare = nullptr; + if (_defaultProjImage != "") { + _textureWhiteSquare = ghoul::io::TextureReader::ref().loadTexture(absPath(_defaultProjImage)); + if (_textureWhiteSquare) { + _textureWhiteSquare->uploadTexture(); + _textureWhiteSquare->setFilter(ghoul::opengl::Texture::FilterMode::Linear); + } + } + } + + void RenderableModelProjection::loadProjectionTexture() { + delete _textureProj; + _textureProj = nullptr; + if (_projectionTexturePath.value() != "") { + _textureProj = ghoul::io::TextureReader::ref().loadTexture(absPath(_projectionTexturePath)); + if (_textureProj) { + _textureProj->uploadTexture(); + _textureProj->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + _textureProj->setWrapping(ghoul::opengl::Texture::WrappingMode::ClampToBorder); + } + } + + } + +} // namespace openspace \ No newline at end of file diff --git a/modules/newhorizons/rendering/renderablemodelprojection.h b/modules/newhorizons/rendering/renderablemodelprojection.h new file mode 100644 index 0000000000..51c4514722 --- /dev/null +++ b/modules/newhorizons/rendering/renderablemodelprojection.h @@ -0,0 +1,147 @@ +/***************************************************************************************** +* * +* OpenSpace * +* * +* Copyright (c) 2014-2015 * +* * +* Permission is hereby granted, free of charge, to any person obtaining a copy of this * +* software and associated documentation files (the "Software"), to deal in the Software * +* without restriction, including without limitation the rights to use, copy, modify, * +* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * +* permit persons to whom the Software is furnished to do so, subject to the following * +* conditions: * +* * +* The above copyright notice and this permission notice shall be included in all copies * +* or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * +* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * +* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * +* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * +* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * +****************************************************************************************/ + +#ifndef __RENDERABLEMODELPROJECTION_H__ +#define __RENDERABLEMODELPROJECTION_H__ + +#include + +#include +#include + +#include +#include +#include + +#include +#include + +namespace openspace { + + namespace modelgeometry { + class ModelGeometry; + } + + class RenderableModelProjection : public Renderable { + public: + RenderableModelProjection(const ghoul::Dictionary& dictionary); + + bool initialize() override; + bool deinitialize() override; + + bool isReady() const override; + + void render(const RenderData& data) override; + void update(const UpdateData& data) override; + + + protected: + void loadTexture(); + void loadProjectionTexture(); + + private: + bool auxiliaryRendertarget(); + glm::mat4 computeProjectorMatrix(const glm::vec3 loc, glm::dvec3 aim, const glm::vec3 up); + void attitudeParameters(double time); + void imageProjectGPU(); + + void textureBind(); + void project(); + + properties::StringProperty _colorTexturePath; + properties::BoolProperty _performProjection; + + properties::IntProperty _rotationX; + properties::IntProperty _rotationY; + properties::IntProperty _rotationZ; + + ghoul::opengl::ProgramObject* _programObject; + ghoul::opengl::ProgramObject* _fboProgramObject; + + ghoul::opengl::Texture* _texture; + ghoul::opengl::Texture* _textureOriginal; + ghoul::opengl::Texture* _textureProj; + ghoul::opengl::Texture* _textureWhiteSquare; + + modelgeometry::ModelGeometry* _geometry; + + float _alpha; + glm::dmat3 _stateMatrix; + glm::dmat3 _instrumentMatrix; + + properties::StringProperty _projectionTexturePath; + std::string _defaultProjImage; + std::string _source; + std::string _destination; + std::string _target; + + // sequence loading + std::string _sequenceSource; + std::string _sequenceType; + + // projection mod info + std::string _instrumentID; + std::string _projectorID; + std::string _projecteeID; + std::string _aberration; + std::vector _potentialTargets; + float _fovy; + float _aspectRatio; + float _nearPlane; + float _farPlane; + + // uniforms + glm::vec2 _camScaling; + glm::vec3 _up; + glm::mat4 _transform; + glm::mat4 _viewProjection; + glm::mat4 _projectorMatrix; + glm::vec3 _boresight; + + // FBO stuff + GLuint _fboID; + GLuint _quad; + GLuint _vertexPositionBuffer; + + GLuint _vbo; + GLuint _ibo; + GLuint _vaoID; + std::vector _geometryVertecies; + std::vector _geometryIndeces; + + std::vector _imageTimes; + int _frameCount; + double _time; + + bool _capture; + + psc _sunPosition; + + properties::BoolProperty _performShading; + bool _programIsDirty; + }; + +} // namespace openspace + +#endif // __RENDERABLEMODELPROJECTION_H__ \ No newline at end of file diff --git a/modules/newhorizons/shaders/modelShader_fs.glsl b/modules/newhorizons/shaders/modelShader_fs.glsl new file mode 100644 index 0000000000..12a513c725 --- /dev/null +++ b/modules/newhorizons/shaders/modelShader_fs.glsl @@ -0,0 +1,96 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * 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. * + ****************************************************************************************/ + +#version __CONTEXT__ + +uniform vec4 campos; +uniform vec4 objpos; +uniform vec3 camdir; + +uniform float time; +uniform sampler2D currentTexture; +uniform sampler2D projectedTexture; +uniform bool _performShading; + +in vec2 vs_st; +in vec4 vs_normal; +in vec4 vs_position; + +in vec4 ProjTexCoord; +uniform vec3 boresight; +uniform vec3 sun_pos; + +#include "ABuffer/abufferStruct.hglsl" +#include "ABuffer/abufferAddToBuffer.hglsl" +#include "PowerScaling/powerScaling_fs.hglsl" + +//#include "PowerScaling/powerScaling_vs.hglsl" +void main() +{ + vec4 position = vs_position; + float depth = pscDepth(position); + vec4 diffuse = texture(currentTexture, vs_st); + + // directional lighting + vec3 origin = vec3(0.0); + vec4 spec = vec4(0.0); + + vec3 n = normalize(vs_normal.xyz); + vec3 e = normalize(camdir); + vec3 l_pos = sun_pos; + vec3 l_dir = normalize(l_pos-objpos.xyz); + float intensity = 1; + + if (_performShading) { + float terminatorBright = 0.4; + intensity = min(max(5*dot(n,l_dir), terminatorBright), 1); + } + + float shine = 0.0001; + vec4 specular = vec4(0.1); + vec4 ambient = vec4(0.f,0.f,0.f,1); + //Specular + if (intensity > 0.f) { + vec3 h = normalize(l_dir + e); + float intSpec = max(dot(h,n),0.0); + spec = specular * pow(intSpec, shine); + } + + vec4 projTexColor = textureProj(projectedTexture, ProjTexCoord); + vec4 shaded = max(intensity * diffuse, ambient); + if (ProjTexCoord[0] > 0.0 || ProjTexCoord[1] > 0.0 || + ProjTexCoord[0] < ProjTexCoord[2] || + ProjTexCoord[1] < ProjTexCoord[2]) { + diffuse = shaded; + } else if (dot(n, boresight) < 0 && projTexColor.w != 0) {// frontfacing + diffuse = projTexColor; + } else { + diffuse = shaded; + } + + ABufferStruct_t frag = createGeometryFragment(diffuse, position, depth); + addToBuffer(frag); + +} + diff --git a/modules/newhorizons/shaders/modelShader_vs.glsl b/modules/newhorizons/shaders/modelShader_vs.glsl new file mode 100644 index 0000000000..26b63bd914 --- /dev/null +++ b/modules/newhorizons/shaders/modelShader_vs.glsl @@ -0,0 +1,60 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * 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. * + ****************************************************************************************/ + +#version __CONTEXT__ + +uniform mat4 ViewProjection; +uniform mat4 ModelTransform; +uniform mat4 ProjectorMatrix; + +layout(location = 0) in vec4 in_position; +layout(location = 1) in vec2 in_st; +layout(location = 2) in vec3 in_normal; + +uniform vec3 boresight; + +out vec2 vs_st; +out vec4 vs_normal; +out vec4 vs_position; +out float s; + + +out vec4 ProjTexCoord; +#include "PowerScaling/powerScaling_vs.hglsl" +void main(){ + + vs_st = in_st; + vs_position = in_position; + vec4 tmp = in_position; + //tmp[3] += _magnification for runtime alteration of model size @AA + + vs_normal = normalize(ModelTransform * vec4(in_normal,0)); + vec4 position = pscTransform(tmp, ModelTransform); + vs_position = tmp; + + vec4 raw_pos = psc_to_meter(in_position, scaling); + ProjTexCoord = ProjectorMatrix * ModelTransform * raw_pos; + position = ViewProjection * position; + gl_Position = z_normalization(position); +} diff --git a/modules/newhorizons/shaders/projectionPass_fs.glsl b/modules/newhorizons/shaders/projectionPass_fs.glsl new file mode 100644 index 0000000000..bd2d19f935 --- /dev/null +++ b/modules/newhorizons/shaders/projectionPass_fs.glsl @@ -0,0 +1,67 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * 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. * + ****************************************************************************************/ + +#version __CONTEXT__ +uniform sampler2D projectTexture; +uniform sampler2D currentTexture; + +uniform mat4 ProjectorMatrix; +uniform mat4 ModelTransform; +uniform vec2 _scaling; +uniform vec3 boresight; + + +in vec4 vs_position; +in vec4 ProjTexCoord; +in vec2 vs_uv; +in vec4 vs_normal; + +out vec4 color; + +#include "PowerScaling/powerScaling_vs.hglsl" + +bool inRange(float x, float a, float b) { + return (x >= a && x <= b); +} + +void main() { + vec2 uv = vec2(0.5,0.5)*vs_uv+vec2(0.5,0.5); + + vec3 n = normalize(vs_normal.xyz); + vec4 projected = ProjTexCoord; + + //normalize + projected.x /= projected.w; + projected.y /= projected.w; + //invert gl coordinates + projected.x = 1 - projected.x; + projected.y = 1 - projected.y; + + if((inRange(projected.x, 0, 1) && inRange(projected.y, 0, 1)) && (dot(n, boresight) < 0)) { + color = texture(projectTexture, projected.xy); + } else { + color = texture(currentTexture, uv); + } + +} \ No newline at end of file diff --git a/modules/newhorizons/shaders/projectionPass_vs.glsl b/modules/newhorizons/shaders/projectionPass_vs.glsl new file mode 100644 index 0000000000..77c197a10c --- /dev/null +++ b/modules/newhorizons/shaders/projectionPass_vs.glsl @@ -0,0 +1,60 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * 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. * + ****************************************************************************************/ + +#version __CONTEXT__ +uniform mat4 ProjectorMatrix; +uniform mat4 ModelTransform; +uniform vec2 _scaling; + +layout(location = 0) in vec4 in_position; +layout(location = 1) in vec2 in_st; +layout(location = 2) in vec3 in_normal; + +uniform vec3 boresight; + +out vec4 vs_position; +out vec4 ProjTexCoord; +out vec2 vs_uv; +out vec4 vs_normal; + +#include "PowerScaling/powerScaling_vs.hglsl" + +void main() { + vs_position = in_position; + + vec4 tmp = in_position; + vec4 position = pscTransform(tmp, ModelTransform); + vs_position = tmp; + + vec4 raw_pos = psc_to_meter(in_position, _scaling); + ProjTexCoord = ProjectorMatrix * ModelTransform * raw_pos; + + vs_normal = normalize(ModelTransform * vec4(in_normal,0)); + + //match clipping plane + vec2 texco = (in_st * 2) - 1; + vs_uv = texco; + gl_Position = vec4(texco, 0.0, 1.0); + +} From e0ea622989ceb56aa6138e7fd764af8a98034f35 Mon Sep 17 00:00:00 2001 From: Michal Marcinkowski Date: Thu, 25 Jun 2015 20:19:08 -0400 Subject: [PATCH 216/329] Adding hybrid read method for Pluto Cleanups + logical changes to parsing and sequencing --- data | 2 +- .../newhorizons/rendering/renderablefov.cpp | 34 +++- modules/newhorizons/rendering/renderablefov.h | 1 + .../rendering/renderableplanetprojection.cpp | 50 ++++-- modules/newhorizons/util/hongkangparser.cpp | 122 +++++++-------- modules/newhorizons/util/hongkangparser.h | 8 +- modules/newhorizons/util/imagesequencer2.cpp | 146 +++++++++++------- modules/newhorizons/util/imagesequencer2.h | 7 +- modules/newhorizons/util/labelparser.cpp | 67 ++++---- modules/newhorizons/util/labelparser.h | 3 +- modules/newhorizons/util/sequenceparser.h | 2 +- scripts/bind_keys.lua | 4 + scripts/default_startup.lua | 6 +- src/rendering/renderengine.cpp | 15 +- 14 files changed, 269 insertions(+), 198 deletions(-) diff --git a/data b/data index f3928948f2..a65a7f832b 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit f3928948f25ac520240a4c7a6ac280b278ddf3b7 +Subproject commit a65a7f832bad63e845ad5b00e2e51bb3ebcaae56 diff --git a/modules/newhorizons/rendering/renderablefov.cpp b/modules/newhorizons/rendering/renderablefov.cpp index bc5f378de9..d29a6a6d7f 100644 --- a/modules/newhorizons/rendering/renderablefov.cpp +++ b/modules/newhorizons/rendering/renderablefov.cpp @@ -138,6 +138,8 @@ void RenderableFov::allocateData() { } RenderableFov::~RenderableFov() { + delete[] _iarray1[0]; + delete[] _iarray1[1]; deinitialize(); } @@ -224,6 +226,19 @@ glm::dvec3 RenderableFov::interpolate(glm::dvec3 p0, glm::dvec3 p1, float t) { return glm::dvec3(p0.x*t2 + p1.x*t, p0.y*t2 + p1.y*t, p0.z*t2 + p1.z*t); } +glm::dvec3 RenderableFov::pscSlerp(glm::dvec3 p0, glm::dvec3 p1, float t){ + assert(t >= 0 && t <= 1); + float t2 = (1.f - t); + float omega = acosf(glm::dot(p0, p1)); + if (omega > 0.f){ + float s1 = sin(t*omega) / sin(omega); + float s2 = sin(t2*omega) / sin(omega); + return glm::dvec3(p0.x*s2 + p1.x*s1, p0.y*s2 + p1.y*s1, p0.z*s2 + p1.z*s1); + + } + return p0;//tmp +} + // This method is the current bottleneck. psc RenderableFov::checkForIntercept(glm::dvec3 ray) { double targetEt; @@ -231,6 +246,7 @@ psc RenderableFov::checkForIntercept(glm::dvec3 ray) { openspace::SpiceManager::ref().getSurfaceIntercept(_fovTarget, _spacecraft, _instrumentID, _frame, _method, _aberrationCorrection, _time, targetEt, ray, ipoint, ivec, intercepted); + ivec *= 0.9999; _interceptVector = PowerScaledCoordinate::CreatePowerScaledCoordinate(ivec[0], ivec[1], ivec[2]); _interceptVector[3] += 3; @@ -297,6 +313,7 @@ void RenderableFov::fovProjection(bool H[], std::vector bounds) { glm::dvec3 current; glm::dvec3 next; glm::vec4 tmp(1); + glm::vec4 test_col(0, 0, 1, 1); if (bounds.size() > 1){ for (int i = 0; i < bounds.size(); i++){ int k = (i + 1 > bounds.size() - 1) ? 0 : i + 1; @@ -475,8 +492,8 @@ void RenderableFov::render(const RenderData& data) { // compute surface intercept openspace::SpiceManager::ref().getSurfaceIntercept(_fovTarget, _spacecraft, _instrumentID, - _frame, _method, _aberrationCorrection, - _time, targetEpoch, bounds[r], ipoint, ivec, _interceptTag[r]); + _frame, _method, _aberrationCorrection, + _time, targetEpoch, bounds[r], ipoint, ivec, _interceptTag[r]); // if not found, use the orthogonal projected point if (!_interceptTag[r]) _projectionBounds[r] = orthogonalProjection(bounds[r]); @@ -554,16 +571,17 @@ void RenderableFov::render(const RenderData& data) { glDrawArrays(_mode, 0, _vtotal[0]); glBindVertexArray(0); - glLineWidth(_lineWidth); - glBindVertexArray(_vaoID[0]); - glDrawArrays(GL_LINES, 0, _vtotal[0]); - glBindVertexArray(0); - if (drawFOV){ - glLineWidth(1.f); + glLineWidth(2.f); glBindVertexArray(_vaoID[1]); glDrawArrays(GL_LINE_LOOP, 0, _vtotal[1]); glBindVertexArray(0); + + glPointSize(5.f); + glBindVertexArray(_vaoID[1]); + glDrawArrays(GL_POINTS, 0, _vtotal[1]); + glBindVertexArray(0); + glPointSize(1.f); } glLineWidth(1.f); } diff --git a/modules/newhorizons/rendering/renderablefov.h b/modules/newhorizons/rendering/renderablefov.h index 79516b8083..682aae7f36 100644 --- a/modules/newhorizons/rendering/renderablefov.h +++ b/modules/newhorizons/rendering/renderablefov.h @@ -69,6 +69,7 @@ public: psc sphericalInterpolate(glm::dvec3 p0, glm::dvec3 p1, float t); glm::dvec3 interpolate(glm::dvec3 p0, glm::dvec3 p1, float t); + glm::dvec3 pscSlerp(glm::dvec3 p0, glm::dvec3 p1, float t); glm::dvec3 bisection(glm::dvec3 p1, glm::dvec3 p2, double tolerance); void computeColors(); diff --git a/modules/newhorizons/rendering/renderableplanetprojection.cpp b/modules/newhorizons/rendering/renderableplanetprojection.cpp index e17d4f81bc..3cbcf6c3c1 100644 --- a/modules/newhorizons/rendering/renderableplanetprojection.cpp +++ b/modules/newhorizons/rendering/renderableplanetprojection.cpp @@ -69,13 +69,12 @@ namespace { const std::string keyFrame = "Frame"; const std::string keyGeometry = "Geometry"; const std::string keyShading = "PerformShading"; - const std::string keyBody = "Body"; - const std::string _mainFrame = "GALACTIC"; - const std::string sequenceTypeImage = "image-sequence"; const std::string sequenceTypePlaybook = "playbook"; + const std::string sequenceTypeHybrid = "hybrid"; + } namespace openspace { @@ -165,6 +164,7 @@ RenderablePlanetProjection::RenderablePlanetProjection(const ghoul::Dictionary& addProperty(_performProjection); addProperty(_clearAllProjections); + addProperty(_colorTexturePath); _colorTexturePath.onChange(std::bind(&RenderablePlanetProjection::loadTexture, this)); addProperty(_projectionTexturePath); @@ -186,15 +186,34 @@ RenderablePlanetProjection::RenderablePlanetProjection(const ghoul::Dictionary& if (_sequenceType == sequenceTypePlaybook){ parser = new HongKangParser(_sequenceSource, - "NEW HORIZONS", + _projectorID, translationDictionary, _potentialTargets); - openspace::ImageSequencer2::ref().runSequenceParser(parser); + openspace::ImageSequencer2::ref().runSequenceParser(parser); } else if (_sequenceType == sequenceTypeImage){ parser = new LabelParser(_sequenceSource, translationDictionary); openspace::ImageSequencer2::ref().runSequenceParser(parser); + } + else if (_sequenceType == sequenceTypeHybrid){ + //first read labels + parser = new LabelParser(_sequenceSource, translationDictionary); + openspace::ImageSequencer2::ref().runSequenceParser(parser); + std::string _eventFile; + bool foundEventFile = dictionary.getValue("Projection.EventFile", _eventFile); + if (foundEventFile){ + //then read playbook + _eventFile = absPath(_eventFile); + parser = new HongKangParser(_eventFile, + _projectorID, + translationDictionary, + _potentialTargets); + openspace::ImageSequencer2::ref().runSequenceParser(parser); + } + else{ + LWARNING("No eventfile has been provided, please check modfiles"); + } } } else{ @@ -238,6 +257,7 @@ bool RenderablePlanetProjection::initialize() { bool RenderablePlanetProjection::auxiliaryRendertarget(){ bool completeSuccess = true; + if (!_texture) return false; // setup FBO glGenFramebuffers(1, &_fboID); glBindFramebuffer(GL_FRAMEBUFFER, _fboID); @@ -379,7 +399,7 @@ glm::mat4 RenderablePlanetProjection::computeProjectorMatrix(const glm::vec3 loc void RenderablePlanetProjection::attitudeParameters(double time){ // precomputations for shader - openspace::SpiceManager::ref().getPositionTransformMatrix(_frame, _mainFrame, time, _stateMatrix); + openspace::SpiceManager::ref().getPositionTransformMatrix(_frame, _mainFrame, _time, _stateMatrix); openspace::SpiceManager::ref().getPositionTransformMatrix(_instrumentID, _mainFrame, time, _instrumentMatrix); _transform = glm::mat4(1); @@ -425,11 +445,12 @@ void RenderablePlanetProjection::textureBind(){ } void RenderablePlanetProjection::project(){ - for (auto img : _imageTimes){ - std::thread t1(&RenderablePlanetProjection::attitudeParameters, this, img.startTime); - t1.join(); - _projectionTexturePath = img.path; // path to current images - imageProjectGPU(); //fbopass + for (auto const &img : _imageTimes){ + //if (img.activeInstruments[0] == "NH_LORRI"){ + RenderablePlanetProjection::attitudeParameters(img.startTime); + _projectionTexturePath = img.path; // path to current images + imageProjectGPU(); //fbopass + //} } _capture = false; } @@ -444,7 +465,6 @@ void RenderablePlanetProjection::clearAllProjections(){ } -#define GPU_PROJ void RenderablePlanetProjection::render(const RenderData& data){ if (!_programObject) return; if (!_textureProj) return; @@ -454,11 +474,9 @@ void RenderablePlanetProjection::render(const RenderData& data){ _camScaling = data.camera.scaling(); _up = data.camera.lookUpVector(); -#ifdef GPU_PROJ if (_capture && _performProjection) project(); -#endif - attitudeParameters(_time); + attitudeParameters(_time); _imageTimes.clear(); psc sun_pos; @@ -481,11 +499,9 @@ void RenderablePlanetProjection::render(const RenderData& data){ _geometry->render(); // disable shader _programObject->deactivate(); - } void RenderablePlanetProjection::update(const UpdateData& data){ - // set spice-orientation in accordance to timestamp _time = Time::ref().currentTime(); _capture = false; diff --git a/modules/newhorizons/util/hongkangparser.cpp b/modules/newhorizons/util/hongkangparser.cpp index e6b3a0cee3..24bbc41048 100644 --- a/modules/newhorizons/util/hongkangparser.cpp +++ b/modules/newhorizons/util/hongkangparser.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -44,11 +43,12 @@ namespace { } namespace openspace { - HongKangParser::HongKangParser(const std::string& fileName, - std::string spacecraft, - ghoul::Dictionary translationDictionary, - std::vector potentialTargets) : - _defaultCaptureImage(absPath("${OPENSPACE_DATA}/scene/common/textures/placeholder.png")) +HongKangParser::HongKangParser(const std::string& fileName, + std::string spacecraft, + ghoul::Dictionary translationDictionary, + std::vector potentialTargets) : + + _defaultCaptureImage(absPath("${OPENSPACE_DATA}/scene/common/textures/placeholder.png")) { _fileName = fileName; _spacecraft = spacecraft; @@ -81,9 +81,9 @@ namespace openspace { } } -void findPlaybookSpecifiedTarget(std::string line, std::string& target){ +void HongKangParser::findPlaybookSpecifiedTarget(std::string line, std::string& target){ //remembto add this lua later... - std::vector ptarg = { "PLUTO", "CHARON", "NIX", "HYDRA", "P5", "P4" }; + std::vector ptarg = _potentialTargets; for (auto p : ptarg){ // loop over all targets and determine from 4th col which target this instrument points to std::transform(line.begin(), line.end(), line.begin(), toupper); @@ -98,7 +98,40 @@ void findPlaybookSpecifiedTarget(std::string line, std::string& target){ } } -void HongKangParser::create(){ +void HongKangParser::writeUTCEventFile(const Image image){ + std::string time_beg; + std::string time_end; + SpiceManager::ref().getDateFromET(image.startTime, time_beg); + SpiceManager::ref().getDateFromET(image.stopTime, time_end, "HR:MN:SC.### ::RND"); + + _eventsAsUTCFile << std::fixed + << std::setw(10) << time_beg << "->" + << std::setw(10) << time_end + << std::setw(10) << (int)getMetFromET(image.startTime) << "->" + << std::setw(10) << (int)getMetFromET(image.stopTime) + << std::setw(10) << image.target << std::setw(10); + for (auto instrument : image.activeInstruments){ + _eventsAsUTCFile << " " << instrument; + } +} + +bool HongKangParser::create(){ + //check input for errors. + int tmp; + bool hasObserver = SpiceManager::ref().getNaifId(_spacecraft, tmp); + if (!hasObserver){ + LERROR("SPICE navigation system has no pooled observer: '" << _spacecraft << "' in kernel" << + "Please check that all necessary kernels are loaded"<< + "along with correct modfile definition."); + return hasObserver; + } + if (_potentialTargets.size() == 0){ + LERROR("In order to find targeting from event file user has to provide list of potential targets " + << "please check modfile"); + } + + _eventsAsUTCFile.open("utcEvents.txt"); + if (size_t position = _fileName.find_last_of(".") + 1){ if (position != std::string::npos){ std::string extension = ghoul::filesystem::File(_fileName).fileExtension(); @@ -106,7 +139,10 @@ void HongKangParser::create(){ if (extension == "txt"){// Hong Kang. pre-parsed playbook LINFO("Using Preparsed Playbook V9H"); std::ifstream file(_fileName , std::ios::binary); - if (!file.good()) LERROR("Failed to open txt file '" << _fileName << "'"); + if (!file.good()){ + LERROR("Failed to open event file '" << _fileName << "'"); + return false; + } std::string line = ""; double shutter = 0.01; @@ -127,10 +163,10 @@ void HongKangParser::create(){ double scan_start = -1; double scan_stop = -1; - std::string cameraTarget = "VOID"; + std::string cameraTarget = "VOID"; std::string scannerTarget = "VOID"; - while (!file.eof()){//only while inte do, FIX + while (!file.eof()){ std::getline(file, line); std::string event = line.substr(0, line.find_first_of(" ")); @@ -160,6 +196,7 @@ void HongKangParser::create(){ //fill image createImage(image, time, time + shutter, cameraSpiceID, cameraTarget, _defaultCaptureImage); + writeUTCEventFile(image); //IFF spaccraft has decided to switch target, store in target map (used for: 'next observation focus') if (previousTarget != image.target){ previousTarget = image.target; @@ -197,9 +234,9 @@ void HongKangParser::create(){ scanRange._max = scan_stop; _instrumentTimes.push_back(std::make_pair(it->first, scanRange)); - //store individual image createImage(image, scan_start, scan_stop, scannerSpiceID, scannerTarget, _defaultCaptureImage); + writeUTCEventFile(image); _subsetMap[image.target]._subset.push_back(image); _subsetMap[image.target]._range.setRange(scan_start); @@ -207,13 +244,6 @@ void HongKangParser::create(){ } //go back to stored position in file file.seekg(len, std::ios_base::beg); - - /*//scanner works like state-machine -only store start time now - scan_start = time; - previousScanner = it->first; - //store scanning instrument - store image once stopTime is found! - findPlaybookSpecifiedTarget(line, scannerTarget); - scannerSpiceID = it->second->getTranslation();*/ } } else{ // we have reached the end of a scan or consecutive capture sequence! @@ -226,59 +256,14 @@ void HongKangParser::create(){ capture_start = -1; } - /*if (line.find("END_NOM") != std::string::npos){ - assert(scan_start != -1, "SCAN end occured before SCAN call!"); - //end of scan, store end time of this scan + store the scan image - scan_stop = time; - scanRange._min = scan_start; - scanRange._max = scan_stop; - _instrumentTimes.push_back(std::make_pair(previousScanner, scanRange)); - - //store individual image - createImage(image, scan_start, scan_stop, scannerSpiceID, scannerTarget, _defaultCaptureImage); - - _subsetMap[image.target]._subset.push_back(image); - _subsetMap[image.target]._range.setRange(scan_start); - - scan_start = -1; - }*/ } + _eventsAsUTCFile << std::endl; } } } } - sendPlaybookInformation(PlaybookIdentifierName); - - //std::ofstream myfile; - //myfile.open("HongKangOutput.txt"); - - ////print all - //for (auto target : _subsetMap){ - // std::string min, max; - // SpiceManager::ref().getDateFromET(target.second._range._min, min); - // SpiceManager::ref().getDateFromET(target.second._range._max, max); - - // myfile << std::endl; - // for (auto image : target.second._subset){ - // std::string time_beg; - // std::string time_end; - // SpiceManager::ref().getDateFromET(image.startTime, time_beg); - // SpiceManager::ref().getDateFromET(image.stopTime, time_end); - - // myfile << std::fixed - // << std::setw(10) << time_beg - // << std::setw(10) << time_end - // << std::setw(10) << (int)getMetFromET(image.startTime) - // << std::setw(10) << image.target << std::setw(10); - // for (auto instrument : image.activeInstruments){ - // myfile << " " << instrument; - // } - // myfile << std::endl; - // } - //} - //myfile.close(); - // + return true; } bool HongKangParser::augmentWithSpice(Image& image, @@ -288,6 +273,7 @@ bool HongKangParser::augmentWithSpice(Image& image, image.target = "VOID"; // we have (?) to cast to int, unfortunately // Why? --abock + // because: old comment --m int exposureTime = image.stopTime - image.startTime; if (exposureTime == 0) exposureTime = 1; @@ -339,6 +325,8 @@ double HongKangParser::getETfromMet(double met){ openspace::SpiceManager::ref().getETfromDate("2015-07-14T11:50:00.00", referenceET); double et = referenceET; + //_metRef += 3; // MET reference time is off by 3 sec? + diff = std::abs(met - _metRef); if (met > _metRef){ et = referenceET + diff; diff --git a/modules/newhorizons/util/hongkangparser.h b/modules/newhorizons/util/hongkangparser.h index d74f43db40..d9dee88de3 100644 --- a/modules/newhorizons/util/hongkangparser.h +++ b/modules/newhorizons/util/hongkangparser.h @@ -31,6 +31,7 @@ #include #include #include +#include namespace openspace { @@ -41,11 +42,13 @@ public: std::string spacecraft, ghoul::Dictionary dictionary, std::vector potentialTargets); - virtual void create(); + virtual bool create(); - // temporary need to figure this out + void findPlaybookSpecifiedTarget(std::string line, std::string& target); virtual std::map getTranslation(){ return _fileTranslation; }; + void writeUTCEventFile(const Image image); + private: double getMetFromET(double et); double getETfromMet(std::string timestr); @@ -70,6 +73,7 @@ private: std::string _spacecraft; std::map _fileTranslation; std::vector _potentialTargets; + std::ofstream _eventsAsUTCFile; }; } diff --git a/modules/newhorizons/util/imagesequencer2.cpp b/modules/newhorizons/util/imagesequencer2.cpp index aea8dd4238..7e78907451 100644 --- a/modules/newhorizons/util/imagesequencer2.cpp +++ b/modules/newhorizons/util/imagesequencer2.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -170,13 +171,13 @@ std::map ImageSequencer2::getActiveInstruments(){ for (auto i : _switchingMap) _switchingMap[i.first] = false; // go over the filetranslation map - for (auto key : _fileTranslation){ + for (const auto &key : _fileTranslation){ // for each spice-instrument - for (auto instrumentID : key.second->getTranslation()){ + for (const auto &instrumentID : key.second->getTranslation()){ // check if the spice-instrument is active if (instrumentActive(instrumentID)){ // go over switching map - for (auto instrument : _switchingMap){ + for (const auto &instrument : _switchingMap){ // if instrument is present in switching map if (instrumentID == instrument.first){ // set as active @@ -223,20 +224,14 @@ float ImageSequencer2::instrumentActiveTime(const std::string& instrumentID) con return -1.f; } -bool ImageSequencer2::getImagePaths(std::vector& captures, - std::string projectee, - std::string instrumentID){ - - if (!instrumentActive(instrumentID) && !Time::ref().timeJumped()) return false; - // dev. note: this is only due to LORRI being the only instrument implemented so far. - return getImagePaths(captures, projectee); -} - bool ImageSequencer2::getImagePaths(std::vector& captures, - std::string projectee){ + std::string projectee, + std::string instrumentRequest){ // check if this instance is either in range or // a valid candidate to recieve data + if (!instrumentActive(instrumentRequest) && !Time::ref().timeJumped()) return false; + //if (!Time::ref().timeJumped() && projectee == getCurrentTarget().second) if (_subsetMap[projectee]._range.inRange(_currentTime) || @@ -244,8 +239,7 @@ bool ImageSequencer2::getImagePaths(std::vector& captures, auto compareTime = [](const Image &a, const Image &b)->bool{ return a.startTime < b.startTime; - }; - + }; // for readability we store the iterators auto begin = _subsetMap[projectee]._subset.begin(); auto end = _subsetMap[projectee]._subset.end(); @@ -258,15 +252,16 @@ bool ImageSequencer2::getImagePaths(std::vector& captures, findCurrent.startTime = _currentTime; // find the two iterators that correspond to the latest time jump - auto curr = std::lower_bound(begin, end, findCurrent, compareTime); + auto curr = std::lower_bound(begin, end, findCurrent , compareTime); auto prev = std::lower_bound(begin, end, findPrevious, compareTime); - + if (curr != begin && curr != end && prev != begin && prev != end && prev < curr){ if (curr->startTime >= prev->startTime){ - std::transform(prev, curr, std::back_inserter(captureTimes), - [](const Image& i) { - return i; - }); + std::copy_if(prev, curr, back_inserter(captureTimes), + [instrumentRequest](const Image& i) { + return i.activeInstruments[0] == instrumentRequest; + }); + std::reverse(captureTimes.begin(), captureTimes.end()); captures = captureTimes; if (!captures.empty()) @@ -297,42 +292,89 @@ void ImageSequencer2::sortData(){ } void ImageSequencer2::runSequenceParser(SequenceParser* parser){ - parser->create(); - // get new data - std::map in1 = parser->getTranslation(); - std::map in2 = parser->getSubsetMap(); - std::vector> in3 = parser->getIstrumentTimes(); - std::vector> in4 = parser->getTargetTimes(); - std::vector in5 = parser->getCaptureProgression(); - - // check for sanity - ghoul_assert(in1.size() > 0, "Sequencer failed to load Translation" ); - ghoul_assert(in2.size() > 0, "Sequencer failed to load Image data" ); - ghoul_assert(in3.size() > 0, "Sequencer failed to load Instrument Switching schedule"); - ghoul_assert(in4.size() > 0, "Sequencer failed to load Target Switching schedule" ); - ghoul_assert(in5.size() > 0, "Sequencer failed to load Capture progression" ); + bool parserComplete = parser->create(); + if (parserComplete){ + // get new data + std::map in1 = parser->getTranslation(); + std::map in2 = parser->getSubsetMap(); + std::vector> in3 = parser->getIstrumentTimes(); + std::vector> in4 = parser->getTargetTimes(); + std::vector in5 = parser->getCaptureProgression(); + // check for sanity + ghoul_assert(in1.size() > 0, "Sequencer failed to load Translation"); + ghoul_assert(in2.size() > 0, "Sequencer failed to load Image data"); + ghoul_assert(in3.size() > 0, "Sequencer failed to load Instrument Switching schedule"); + ghoul_assert(in4.size() > 0, "Sequencer failed to load Target Switching schedule"); + ghoul_assert(in5.size() > 0, "Sequencer failed to load Capture progression"); - // append data - _fileTranslation.insert ( in1.begin(), in1.end()); - _subsetMap.insert ( in2.begin(), in2.end()); - _instrumentTimes.insert ( _instrumentTimes.end(), in3.begin(), in3.end()); - _targetTimes.insert ( _targetTimes.end(), in4.begin(), in4.end()); - _captureProgression.insert(_captureProgression.end(), in5.begin(), in5.end()); + // append data + _fileTranslation.insert(in1.begin(), in1.end()); + for (auto it : in2){ + if (_subsetMap.find(it.first) == _subsetMap.end()) { + // if key not exist yet - add sequence data for key (target) + _subsetMap.insert(it); + } else { + std::string key = it.first; + std::vector &source = it.second._subset; // prediction + std::vector &destination = _subsetMap[key]._subset; // imagery - // sorting of data _not_ optional - sortData(); - - // extract payload from _fileTranslation - for (auto t : _fileTranslation){ - if (t.second->getDecoderType() == "CAMERA" || - t.second->getDecoderType() == "SCANNER" ){ - std::vector spiceIDs = t.second->getTranslation(); - for (auto id : spiceIDs){ - _switchingMap[id] = false; + // simple search function + double min = 10; + auto findMin = [&](std::vector &vector)->double{ + for (int i = 1; i < vector.size(); i++){ + double e = abs(vector[i].startTime - vector[i - 1].startTime); + if (e < min){ + min = e; + } + } + return min; + }; + + // find the smallest separation of images in time + double epsilon; + epsilon = findMin(source); + epsilon = findMin(destination); + // set epsilon as 1% smaller than min + epsilon -= min*0.01; + + // IFF images have same time as mission planned capture, erase that event from + // 'predicted event file' (mission-playbook) + std::vector tmp; + for (int i = 0; i < source.size(); i++){ + for (int j = 0; j < destination.size(); j++){ + double diff = abs(source[i].startTime - destination[j].startTime); + if (diff < epsilon){ + source.erase(source.begin() + i); + } + } + } + // pad image data with predictions (ie - where no actual images, add placeholder) + _subsetMap[key]._subset.insert(_subsetMap[key]._subset.end(), source.begin(), source.end()); } } + + _instrumentTimes.insert(_instrumentTimes.end(), in3.begin(), in3.end()); + _targetTimes.insert(_targetTimes.end(), in4.begin(), in4.end()); + _captureProgression.insert(_captureProgression.end(), in5.begin(), in5.end()); + + // sorting of data _not_ optional + sortData(); + + // extract payload from _fileTranslation + for (auto t : _fileTranslation){ + if (t.second->getDecoderType() == "CAMERA" || + t.second->getDecoderType() == "SCANNER"){ + std::vector spiceIDs = t.second->getTranslation(); + for (auto id : spiceIDs){ + _switchingMap[id] = false; + } + } + } + _hasData = true; + } + else{ + ghoul_assert(parserComplete, "one or more sequence loads failed, please check mod files. "); } - _hasData = true; } } // namespace openspace diff --git a/modules/newhorizons/util/imagesequencer2.h b/modules/newhorizons/util/imagesequencer2.h index 0a30437740..87f8116449 100644 --- a/modules/newhorizons/util/imagesequencer2.h +++ b/modules/newhorizons/util/imagesequencer2.h @@ -122,12 +122,9 @@ public: * makes the request. If an instance is not registered in the class then the singleton * returns false and no projections will occur. */ - bool getImagePaths(std::vector& captures, - std::string projectee, - std::string instrumentID); - bool getImagePaths(std::vector& captures, - std::string projectee); + std::string projectee, + std::string instrumentRequest); /* * returns true if instrumentID is within a capture range. diff --git a/modules/newhorizons/util/labelparser.cpp b/modules/newhorizons/util/labelparser.cpp index 2331ab586c..de83976476 100644 --- a/modules/newhorizons/util/labelparser.cpp +++ b/modules/newhorizons/util/labelparser.cpp @@ -45,7 +45,7 @@ namespace { namespace openspace { LabelParser::LabelParser(const std::string& fileName, - ghoul::Dictionary translationDictionary) + ghoul::Dictionary translationDictionary) : _badDecoding(false) { _fileName = fileName; //get the different instrument types @@ -102,7 +102,16 @@ std::string LabelParser::decode(std::string line){ std::size_t value = line.find(key.first); if (value != std::string::npos){ std::string toTranslate = line.substr(value); - return _fileTranslation[toTranslate]->getTranslation()[0]; //lbls always 1:1 -> single value return. + + //if (_fileTranslation.find(toTranslate) == _fileTranslation.end()) { + // // not found + // _badDecoding = true; + // LERROR("Could not fins '" << toTranslate << "' in translation map." << + // "\nPlease check label files"); + // return ""; + //} + return _fileTranslation[toTranslate]->getTranslation()[0]; //lbls always 1:1 -> single value return + } } return ""; @@ -112,14 +121,13 @@ std::string LabelParser::encode(std::string line) { for (auto key : _fileTranslation) { std::size_t value = line.find(key.first); if (value != std::string::npos) { - //std::cout << line.substr(value) << std::endl; return line.substr(value); } } return ""; } -void LabelParser::create(){ +bool LabelParser::create(){ auto imageComparer = [](const Image &a, const Image &b)->bool{ return a.startTime < b.startTime; }; @@ -133,20 +141,21 @@ void LabelParser::create(){ ghoul::filesystem::Directory sequenceDir(_fileName, true); if (!FileSys.directoryExists(sequenceDir)) { LERROR("Could not load Label Directory '" << sequenceDir.path() << "'"); - return; + return false; } std::vector sequencePaths = sequenceDir.read(true, false); // check inputs for (auto path : sequencePaths){ - //std::cout << path << std::endl; if (size_t position = path.find_last_of(".") + 1){ if (position != std::string::npos){ ghoul::filesystem::File currentFile(path); std::string extension = currentFile.fileExtension(); - if (extension == "lbl" || extension == "LBL"){ // discovered header file std::ifstream file(currentFile.path()); - if (!file.good()) LERROR("Failed to open label file '" << currentFile.path() << "'"); + if (!file.good()){ + LERROR("Failed to open label file '" << currentFile.path() << "'"); + return false; + } int count = 0; @@ -164,6 +173,8 @@ void LabelParser::create(){ line.erase(std::remove(line.begin(), line.end(), ' '), line.end()); line.erase(std::remove(line.begin(), line.end(), '\r'), line.end()); + _detectorType = "CAMERA"; //default value + /* Add more */ if (read == "TARGET_NAME"){ _target = decode(line); @@ -180,8 +191,13 @@ void LabelParser::create(){ } if (read == "DETECTOR_TYPE"){ _detectorType = decode(line); - count++; + count++; } + // if (_badDecoding){ + // LERROR("Please examine file: '" << currentFile.path() << "'"); + // return false; + // } + if (read == "START_TIME"){ std::string start = line.substr(line.find("=") + 2); start.erase(std::remove(start.begin(), start.end(), ' '), start.end()); @@ -197,7 +213,7 @@ void LabelParser::create(){ count++; } else{ - LERROR("Label file " + _fileName + " deviates from generic standard!"); + LERROR("Label file " + currentFile.path() + " deviates from generic standard!"); LINFO("Please make sure input data adheres to format https://pds.jpl.nasa.gov/documents/qs/labels.html"); } } @@ -248,40 +264,11 @@ void LabelParser::create(){ } } - //sendPlaybookInformation(); - - //std::ofstream myfile; - //myfile.open("LabelFileOutput.txt"); - - ////print all for (auto target : _subsetMap){ _instrumentTimes.push_back(std::make_pair(lblName, _subsetMap[target.first]._range)); - // std::string min, max; - // SpiceManager::ref().getDateFromET(target.second._range._min, min); - // SpiceManager::ref().getDateFromET(target.second._range._max, max); - - // myfile << std::endl; - // for (auto image : target.second._subset){ - // std::string time_beg; - // std::string time_end; - // SpiceManager::ref().getDateFromET(image.startTime, time_beg); - // SpiceManager::ref().getDateFromET(image.stopTime, time_end); - - // myfile << std::fixed - // << " " << time_beg - // << "-->" << time_end - // << " [ " << image.startTime - // << " ] " << image.target << std::setw(10); - // for (auto instrument : image.activeInstruments){ - // myfile << " " << instrument; - // } - // myfile << std::endl; - // } } - //myfile.close(); - sendPlaybookInformation(PlaybookIdentifierName); - + return true; } void LabelParser::createImage(Image& image, double startTime, double stopTime, std::vector instr, std::string targ, std::string pot) { diff --git a/modules/newhorizons/util/labelparser.h b/modules/newhorizons/util/labelparser.h index 6efa1c77ca..b887a4d92a 100644 --- a/modules/newhorizons/util/labelparser.h +++ b/modules/newhorizons/util/labelparser.h @@ -38,7 +38,7 @@ public: LabelParser(); LabelParser(const std::string& fileName, ghoul::Dictionary translationDictionary); - virtual void create(); + virtual bool create(); // temporary need to figure this out std::map getTranslation(){ return _fileTranslation; }; @@ -71,6 +71,7 @@ private: std::string _sequenceID; double _startTime; double _stopTime; + bool _badDecoding; }; } #endif //__LABELPARSER_H__ \ No newline at end of file diff --git a/modules/newhorizons/util/sequenceparser.h b/modules/newhorizons/util/sequenceparser.h index 4eb3a42582..b8573dd712 100644 --- a/modules/newhorizons/util/sequenceparser.h +++ b/modules/newhorizons/util/sequenceparser.h @@ -67,7 +67,7 @@ struct ImageSubset { class SequenceParser { public: - virtual void create() = 0; + virtual bool create() = 0; virtual std::map getSubsetMap() final; virtual std::vector> getIstrumentTimes() final; virtual std::vector> getTargetTimes() final; diff --git a/scripts/bind_keys.lua b/scripts/bind_keys.lua index 63a4b18286..cfa16194ee 100644 --- a/scripts/bind_keys.lua +++ b/scripts/bind_keys.lua @@ -32,6 +32,8 @@ openspace.bindKey("F10", "openspace.time.setTime('2015-07-14T10:00:00.00'); open openspace.bindKey("F11", "openspace.time.setTime('2015-07-14T11:17:00.00'); openspace.setPropertyValue('PlutoProjection.renderable.clearAllProjections', true); openspace.setPropertyValue('Charon.renderable.clearAllProjections', true);") openspace.bindKey("F12", "openspace.time.setTime('2015-07-14T12:45:00.00'); openspace.setPropertyValue('PlutoProjection.renderable.clearAllProjections', true); openspace.setPropertyValue('Charon.renderable.clearAllProjections', true);") +openspace.bindKey("r", "local b = openspace.getPropertyValue('PlutoProjection.renderable.fk'); openspace.setPropertyValue('PlutoProjection.renderable.fk', not b)") + openspace.bindKey("a", "openspace.setOrigin('NewHorizons')") openspace.bindKey("s", "openspace.setOrigin('PlutoProjection')") openspace.bindKey("d", "openspace.setOrigin('Charon')") @@ -82,6 +84,8 @@ openspace.bindKey("m", "local b = openspace.getPropertyValue('NH_RALPH_MVIC_NIR. openspace.bindKey("m", "local b = openspace.getPropertyValue('NH_ALICE_AIRGLOW.renderable.solidDraw'); openspace.setPropertyValue('NH_ALICE_AIRGLOW.renderable.solidDraw', not b)") openspace.bindKey("m", "local b = openspace.getPropertyValue('NH_ALICE_SOC.renderable.solidDraw'); openspace.setPropertyValue('NH_ALICE_SOC.renderable.solidDraw', not b)") +openspace.bindKey("t", "local b = openspace.getPropertyValue('PlutoShadow.renderable.enabled'); openspace.setPropertyValue('PlutoShadow.renderable.enabled', not b)") +openspace.bindKey("t", "local b = openspace.getPropertyValue('CharonShadow.renderable.enabled'); openspace.setPropertyValue('CharonShadow.renderable.enabled', not b)") openspace.bindKey("p", "local b = openspace.getPropertyValue('JupiterProjection.renderable.performProjection'); openspace.setPropertyValue('JupiterProjection.renderable.performProjection', not b)") openspace.bindKey("p", "local b = openspace.getPropertyValue('Io.renderable.performProjection'); openspace.setPropertyValue('Io.renderable.performProjection', not b)") openspace.bindKey("p", "local b = openspace.getPropertyValue('Ganymede.renderable.performProjection'); openspace.setPropertyValue('Ganymede.renderable.performProjection', not b)") diff --git a/scripts/default_startup.lua b/scripts/default_startup.lua index 75eaa06d73..d835789899 100644 --- a/scripts/default_startup.lua +++ b/scripts/default_startup.lua @@ -5,12 +5,12 @@ openspace.setInvertRoll(true); --openspace.setInteractionSensitivity(10) -- This is the default value for the sensitivity (the higher, the more sensitive) -openspace.time.setTime("2007 FEB 27 16:30:00") -- This is the start time for a Jupiter run of New Horizons +--openspace.time.setTime("2007 FEB 27 16:30:00") -- This is the start time for a Jupiter run of New Horizons --openspace.time.setTime("2007 FEB 28 11:45:00") -- europa rise --TESTING ALICE --openspace.time.setTime("2015-07-13T00:00:00.00") ---openspace.time.setTime("2015-07-14T08:00:00.00") +openspace.time.setTime("2015-07-14T10:00:00.00") openspace.time.setDeltaTime(0) -- How many seconds pass per second of realtime, changeable in the GUI @@ -18,7 +18,7 @@ dofile(openspace.absPath('${SCRIPTS}/bind_keys.lua')) -- Load the default keyb -- openspace.time.setDeltaTime(50); ---openspace.time.setTime("2015-07-14T12:00:00.00") -- PLUTO +--openspace.time.setTime("2015-07-14T12:30:00.00") -- PLUTO -- NH takes series of images from visible to dark side (across terminator) -- Sequence lasts ~10 mins, (recommended dt = 10) diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index f4677942f7..366ff36eed 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -445,11 +445,25 @@ void RenderEngine::render(const glm::mat4 &projectionMatrix, const glm::mat4 &vi int line = 0; + double currentTime = Time::ref().currentTime(); + //PrintText(line++, "Date: %s", Time::ref().currentTimeUTC().c_str()); PrintColorTextArg(line++, "Date: %s", 10, glm::vec4(1), Time::ref().currentTimeUTC().c_str()); glm::vec4 targetColor(0.00, 0.75, 1.00, 1); double dt = Time::ref().deltaTime(); PrintColorTextArg(line++, "Simulation increment (s): %.0f", 10, glm::vec4(1), dt); + + psc nhPos; + double lt; + SpiceManager::ref().getTargetPosition("PLUTO", "NEW HORIZONS", "GALACTIC", "NONE", currentTime, nhPos, lt); + //nhPos[3] += 3; + float a, b, c; + SpiceManager::ref().getPlanetEllipsoid("PLUTO", a, b, c); + float radius = (a + b) / 2.f; + + float distToSurf = glm::length(nhPos.vec3()) - radius; + PrintText(line++, "Distance to Pluto: % .1f (KM)", distToSurf); + PrintText(line++, "Avg. Frametime: %.5f", sgct::Engine::instance()->getAvgDt()); //PrintText(line++, "Drawtime: %.5f", sgct::Engine::instance()->getDrawTime()); @@ -461,7 +475,6 @@ void RenderEngine::render(const glm::mat4 &projectionMatrix, const glm::mat4 &vi //PrintText(i++, "Scaling: (% .5f, % .5f)", scaling[0], scaling[1]); #ifdef OPENSPACE_MODULE_NEWHORIZONS_ENABLED - double currentTime = Time::ref().currentTime(); if (openspace::ImageSequencer2::ref().isReady()) { double remaining = openspace::ImageSequencer2::ref().getNextCaptureTime() - currentTime; double t = 1.0 - remaining / openspace::ImageSequencer2::ref().getIntervalLength(); From 8fbd027a1ecb0e05e75581494ccfd92dccc37955 Mon Sep 17 00:00:00 2001 From: Anton Arbring Date: Thu, 25 Jun 2015 20:28:36 -0400 Subject: [PATCH 217/329] Adding convenient scripts for showing 67P and changing coordinate system to 67P --- scripts/bind_keys.lua | 18 ++++++++++++++++- scripts/default_settings.lua | 1 + scripts/default_startup.lua | 3 +++ src/rendering/renderengine.cpp | 35 ++++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 1 deletion(-) diff --git a/scripts/bind_keys.lua b/scripts/bind_keys.lua index 63a4b18286..c72e06c737 100644 --- a/scripts/bind_keys.lua +++ b/scripts/bind_keys.lua @@ -22,7 +22,23 @@ openspace.bindKey("5", "openspace.time.setDeltaTime(60)") openspace.bindKey("6", "openspace.time.setDeltaTime(120)") openspace.bindKey("7", "openspace.time.setDeltaTime(360)") openspace.bindKey("8", "openspace.time.setDeltaTime(540)") -openspace.bindKey("9", "openspace.time.setDeltaTime(720)") +openspace.bindKey("9", "openspace.time.setDeltaTime(720)") + +--[[openspace.bindKey("2", "openspace.time.setDeltaTime(30)") +openspace.bindKey("3", "openspace.time.setDeltaTime(180)") -- 3m +openspace.bindKey("4", "openspace.time.setDeltaTime(900)") -- 15m +openspace.bindKey("5", "openspace.time.setDeltaTime(3600)") -- 1h +openspace.bindKey("6", "openspace.time.setDeltaTime(14400)") -- 3h +openspace.bindKey("7", "openspace.time.setDeltaTime(43200)") -- 12h +openspace.bindKey("8", "openspace.time.setDeltaTime(86400)") -- 1d +openspace.bindKey("9", "openspace.time.setDeltaTime(172800)") -- 2d + +openspace.bindKey("v", "openspace.time.setTime('2014 AUG 22 03:45:00'); openspace.time.setDeltaTime(1);") +openspace.bindKey("b", "openspace.time.setTime('2014 SEP 02 11:30:30'); openspace.time.setDeltaTime(1);") +openspace.bindKey("n", "openspace.time.setTime('2014 SEP 14 17:55:00'); openspace.time.setDeltaTime(1);") + +openspace.bindKey("i", "local b = openspace.getPropertyValue('ImagePlaneRosetta.renderable.enabled'); openspace.setPropertyValue('ImagePlaneRosetta.renderable.enabled', not b)") +--]] openspace.bindKey("F8", "openspace.time.setTime('2015-07-14T09:00:00.00'); openspace.setPropertyValue('PlutoProjection.renderable.clearAllProjections', true); openspace.setPropertyValue('Charon.renderable.clearAllProjections', true);") diff --git a/scripts/default_settings.lua b/scripts/default_settings.lua index 0f3f2ffcb1..1e76452e7d 100644 --- a/scripts/default_settings.lua +++ b/scripts/default_settings.lua @@ -14,5 +14,6 @@ openspace.setPropertyValue("MilkyWay.renderable.transparency", 0.75) openspace.setPropertyValue("MilkyWay.renderable.segments", 50) openspace.changeCoordinateSystem("Jupiter") +--openspace.changeCoordinateSystem("67P") openspace.printInfo("Done setting default values") diff --git a/scripts/default_startup.lua b/scripts/default_startup.lua index 75eaa06d73..6b7cbadcf2 100644 --- a/scripts/default_startup.lua +++ b/scripts/default_startup.lua @@ -6,6 +6,9 @@ openspace.setInvertRoll(true); --openspace.setInteractionSensitivity(10) -- This is the default value for the sensitivity (the higher, the more sensitive) openspace.time.setTime("2007 FEB 27 16:30:00") -- This is the start time for a Jupiter run of New Horizons + +--openspace.time.setTime("2011 AUG 06 00:00:00") -- Dawn takes pictures of Vesta +--openspace.time.setTime("2011 JUL 28 12:00:00") -- Rosetta starts dancing around 67p --openspace.time.setTime("2007 FEB 28 11:45:00") -- europa rise --TESTING ALICE --openspace.time.setTime("2015-07-13T00:00:00.00") diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 75e1f7ed9a..0a761ee744 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -1236,6 +1236,41 @@ void RenderEngine::changeViewPoint(std::string origin) { // return; //} + //if (origin == "67P") { + // SceneGraphNode* rosettaNode = scene()->sceneGraphNode("Rosetta"); + // SceneGraphNode* cgNode = scene()->sceneGraphNode("67P"); + // //jupiterBarycenterNode->setParent(solarSystemBarycenterNode); + // //plutoBarycenterNode->setParent(solarSystemBarycenterNode); + // solarSystemBarycenterNode->setParent(cgNode); + // rosettaNode->setParent(cgNode); + // + // ghoul::Dictionary solarDictionary = + // { + // { std::string("Type"), std::string("Spice") }, + // { std::string("Body"), std::string("SUN") }, + // { std::string("Reference"), std::string("GALACTIC") }, + // { std::string("Observer"), std::string("CHURYUMOV-GERASIMENKO") }, + // { std::string("Kernels"), ghoul::Dictionary() } + // }; + // solarSystemBarycenterNode->setEphemeris(new SpiceEphemeris(solarDictionary)); + // + // ghoul::Dictionary rosettaDictionary = + // { + // { std::string("Type"), std::string("Spice") }, + // { std::string("Body"), std::string("ROSETTA") }, + // { std::string("Reference"), std::string("GALACTIC") }, + // { std::string("Observer"), std::string("CHURYUMOV-GERASIMENKO") }, + // { std::string("Kernels"), ghoul::Dictionary() } + // }; + // + // cgNode->setParent(scene()->sceneGraphNode("SolarSystem")); + // rosettaNode->setEphemeris(new SpiceEphemeris(rosettaDictionary)); + // cgNode->setEphemeris(new StaticEphemeris); + // + // return; + // + //} + ghoul_assert(false, "This function is being misused"); } From 4fb40ceeb906c5a5181c730ee1ba93b38e359536 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Fri, 26 Jun 2015 09:23:30 +0200 Subject: [PATCH 218/329] also changed name of scripts variable in header file.... --- include/openspace/network/parallelconnection.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/openspace/network/parallelconnection.h b/include/openspace/network/parallelconnection.h index 0a73370d53..d5c8f0f05a 100644 --- a/include/openspace/network/parallelconnection.h +++ b/include/openspace/network/parallelconnection.h @@ -209,8 +209,8 @@ namespace openspace{ std::atomic _isHost; std::atomic _isConnected; std::atomic _isListening; - std::vector _sentScripts; - std::mutex _sentScriptsMutex; + std::vector _executedScripts; + std::mutex _executedScriptsMutex; }; } // namespace network From 2529d4c4d6338c533bf9ab6245cd94722285a582 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Fri, 26 Jun 2015 09:37:51 +0200 Subject: [PATCH 219/329] added definitions of which scripts should be shared over parallel connection --- src/interaction/interactionhandler.cpp | 6 ++++-- src/rendering/renderengine.cpp | 12 ++++++++---- src/scene/scene.cpp | 3 ++- src/util/time.cpp | 12 ++++++++---- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index 2db6f8e320..1b6ecc297f 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -856,7 +856,8 @@ scripting::ScriptEngine::LuaLibrary InteractionHandler::luaLibrary() { "setOrigin", &luascriptfunctions::setOrigin, "string", - "Set the camera origin node by name" + "Set the camera origin node by name", + true }, { "clearKeys", @@ -880,7 +881,8 @@ scripting::ScriptEngine::LuaLibrary InteractionHandler::luaLibrary() { "distance", &luascriptfunctions::distance, "number", - "Change distance to origin" + "Change distance to origin", + true }, { "setInteractionSensitivity", diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 75e1f7ed9a..8d546ee02d 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -759,7 +759,8 @@ scripting::ScriptEngine::LuaLibrary RenderEngine::luaLibrary() { "visualizeABuffer", &luascriptfunctions::visualizeABuffer, "bool", - "Toggles the visualization of the ABuffer" + "Toggles the visualization of the ABuffer", + true }, { "showRenderInformation", @@ -784,20 +785,23 @@ scripting::ScriptEngine::LuaLibrary RenderEngine::luaLibrary() { "changeCoordinateSystem", &luascriptfunctions::changeCoordinateSystem, "string", - "Changes the origin of the coordinate system to the passed node" + "Changes the origin of the coordinate system to the passed node", + true }, { "fadeIn", &luascriptfunctions::fadeIn, "number", - "" + "", + true }, //also temporary @JK { "fadeOut", &luascriptfunctions::fadeOut, "number", - "" + "", + true }, }, }; diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 8e6b0c58bc..10a09084e5 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -571,7 +571,8 @@ scripting::ScriptEngine::LuaLibrary Scene::luaLibrary() { "string, *", "Sets a property identified by the URI in " "the first argument. The second argument can be any type, but it has to " - " agree with the type that the property expects" + " agree with the type that the property expects", + true }, { "getPropertyValue", diff --git a/src/util/time.cpp b/src/util/time.cpp index 8bbf95b81c..20aa2ff71d 100644 --- a/src/util/time.cpp +++ b/src/util/time.cpp @@ -195,7 +195,8 @@ scripting::ScriptEngine::LuaLibrary Time::luaLibrary() { &luascriptfunctions::time_setDeltaTime, "number", "Sets the amount of simulation time that happens " - "in one second of real time" + "in one second of real time", + true }, { "deltaTime", @@ -208,14 +209,16 @@ scripting::ScriptEngine::LuaLibrary Time::luaLibrary() { "setPause", &luascriptfunctions::time_setPause, "bool", - "Pauses the simulation time or restores the delta time" + "Pauses the simulation time or restores the delta time", + true }, { "togglePause", &luascriptfunctions::time_togglePause, "", "Toggles the pause function, i.e. temporarily setting the delta time to 0" - " and restoring it afterwards" + " and restoring it afterwards", + true }, { "setTime", @@ -224,7 +227,8 @@ scripting::ScriptEngine::LuaLibrary Time::luaLibrary() { "Sets the current simulation time to the " "specified value. If the parameter is a number, the value is the number " "of seconds past the J2000 epoch. If it is a string, it has to be a " - "valid ISO 8601 date string (YYYY-MM-DDTHH:MN:SS)" + "valid ISO 8601 date string (YYYY-MM-DDTHH:MN:SS)", + true }, { "currentTime", From 42dbf6a6a145c03ff7948d63f42f051544b06c97 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Fri, 26 Jun 2015 10:42:35 +0200 Subject: [PATCH 220/329] moved script parsing for library/function names into a separate function --- include/openspace/scripting/scriptengine.h | 2 + src/scripting/scriptengine.cpp | 137 +++++++++++---------- 2 files changed, 76 insertions(+), 63 deletions(-) diff --git a/include/openspace/scripting/scriptengine.h b/include/openspace/scripting/scriptengine.h index 94f52796d5..f9c3b3bac0 100644 --- a/include/openspace/scripting/scriptengine.h +++ b/include/openspace/scripting/scriptengine.h @@ -100,6 +100,8 @@ private: void addBaseLibrary(); void remapPrintFunction(); + + bool findLibraryAndFunction(std::string &library, std::string &function, const std::string &script); lua_State* _state; std::set _registeredLibraries; diff --git a/src/scripting/scriptengine.cpp b/src/scripting/scriptengine.cpp index e528145b77..f69c5140e4 100644 --- a/src/scripting/scriptengine.cpp +++ b/src/scripting/scriptengine.cpp @@ -156,73 +156,84 @@ bool ScriptEngine::runScript(const std::string& script) { } //if we're currently hosting the parallel session, find out if script should be synchronized. - if(OsEng.parallelConnection()->isHost()){ - - //"deconstruct the script to find library and function name - //assuming a script looks like: "openspace.library.function()" - //or openspace.funcion() - std::string sub; - std::string lib; - std::string func; - //find first "." - std::size_t pos = script.find("."); - if(pos != std::string::npos){ - //strip "openspace." - sub = script.substr(pos + 1, script.size()); - pos = sub.find("."); - //one more "." was found, we have a library name - if(pos != std::string::npos){ - //assing library name - lib = sub.substr(0, pos); - //strip "library." - sub = sub.substr(pos + 1, sub.size()); - - pos = sub.find("("); - if(pos != std::string::npos && pos > 0){ - //strip the () and we're left with function name - func = sub.substr(0, pos); - } - } - else{ - //no more "." was found, we have the case of "openspace.funcion()" - pos = sub.find("("); - if(pos != std::string::npos && pos > 0){ - //strip the () and we're left with function name - func = sub.substr(0, pos); - } - } - } - - LuaLibrary *library = nullptr; - std::set::const_iterator libit; - for(libit = _registeredLibraries.cbegin(); - libit != _registeredLibraries.cend(); - ++libit){ - if(libit->name.compare(lib) == 0){ - break; - } - } - - std::vector::const_iterator funcit; - //library was found - if(libit != _registeredLibraries.cend()){ - for( funcit = libit->functions.cbegin(); - funcit != libit->functions.cend(); - ++funcit){ - //function was found! - if(funcit->name.compare(func) == 0){ - //is the function of a type that should be shared via parallel connection? - if(funcit->parallelShared ){ - OsEng.parallelConnection()->sendScript(script); - } - } - } - } - } + if (OsEng.parallelConnection()->isHost()){ + + std::string lib, func; + if (findLibraryAndFunction(lib, func, script)){ + + LuaLibrary *library = nullptr; + std::set::const_iterator libit; + for (libit = _registeredLibraries.cbegin(); + libit != _registeredLibraries.cend(); + ++libit){ + if (libit->name.compare(lib) == 0){ + break; + } + } + + std::vector::const_iterator funcit; + //library was found + if (libit != _registeredLibraries.cend()){ + for (funcit = libit->functions.cbegin(); + funcit != libit->functions.cend(); + ++funcit){ + //function was found! + if (funcit->name.compare(func) == 0){ + //is the function of a type that should be shared via parallel connection? + if (funcit->parallelShared){ + OsEng.parallelConnection()->sendScript(script); + } + } + } + } + } + } return true; } +bool ScriptEngine::findLibraryAndFunction(std::string &library, std::string &function, const std::string &script){ + + //"deconstruct the script to find library and function name + //assuming a script looks like: "openspace.library.function()" + //or openspace.funcion() + std::string sub; + library.clear(); + function.clear(); + //find first "." + std::size_t pos = script.find("."); + + if (pos != std::string::npos){ + //strip "openspace." + sub = script.substr(pos + 1, script.size()); + pos = sub.find("."); + //one more "." was found, if the "." comes before first "(" we have a library name + if (pos != std::string::npos && pos < sub.find("(")){ + //assing library name + library = sub.substr(0, pos); + //strip "library." + sub = sub.substr(pos + 1, sub.size()); + + pos = sub.find("("); + if (pos != std::string::npos && pos > 0){ + //strip the () and we're left with function name + function = sub.substr(0, pos); + } + } + else{ + //no more "." was found, we have the case of "openspace.funcion()" + pos = sub.find("("); + if (pos != std::string::npos && pos > 0){ + //strip the () and we're left with function name + function = sub.substr(0, pos); + } + } + } + + //if we found a function all is good + return !function.empty(); +} + bool ScriptEngine::runScriptFile(const std::string& filename) { if (filename.empty()) { LWARNING("Filename was empty"); From 06ca17326e45f944e8a6cef84be23640b12803b0 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Fri, 26 Jun 2015 11:15:14 +0200 Subject: [PATCH 221/329] fixed a bug where received sscripts sometimes got an extra character when converted from vector to string --- src/network/parallelconnection.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index f87f6c897e..f3a5ba4223 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -345,8 +345,9 @@ namespace openspace { } //construct a script (string) from the data contained in the buffer - std::string script(buffer.data()); - + std::string script; + script.assign(buffer.begin(), buffer.end()); + //tell the script engine to execute the script when appropriate OsEng.scriptEngine()->queueScript(script); From 8cc1498b577e04668d67e970e95f34334e5d28a0 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Fri, 26 Jun 2015 13:54:00 +0200 Subject: [PATCH 222/329] made functions for script sharing public for now, temporary fix --- include/openspace/scripting/scriptengine.h | 14 ++- src/scripting/scriptengine.cpp | 136 +++++++++++++-------- 2 files changed, 95 insertions(+), 55 deletions(-) diff --git a/include/openspace/scripting/scriptengine.h b/include/openspace/scripting/scriptengine.h index f9c3b3bac0..5e804ac092 100644 --- a/include/openspace/scripting/scriptengine.h +++ b/include/openspace/scripting/scriptengine.h @@ -28,6 +28,7 @@ #include #include +#include /** * \defgroup LuaScripts Lua Scripts @@ -89,9 +90,16 @@ public: void preSynchronization(); void queueScript(const std::string &script); + + std::vector cachedScripts(); std::vector allLuaFunctions() const; + //parallel functions + bool parseLibraryAndFunctionNames(std::string &library, std::string &function, const std::string &script); + bool shouldScriptBeSent(const std::string &library, const std::string &function); + void cacheScript(const std::string &library, const std::string &function, const std::string &script); + private: bool registerLuaLibrary(lua_State* state, const LuaLibrary& library); void addLibraryFunctions(lua_State* state, const LuaLibrary& library, bool replace); @@ -100,8 +108,6 @@ private: void addBaseLibrary(); void remapPrintFunction(); - - bool findLibraryAndFunction(std::string &library, std::string &function, const std::string &script); lua_State* _state; std::set _registeredLibraries; @@ -112,6 +118,10 @@ private: std::vector _receivedScripts; std::string _currentSyncedScript; + //parallel variables + std::map> _cachedScripts; + std::mutex _cachedScriptsMutex; + }; } // namespace scripting diff --git a/src/scripting/scriptengine.cpp b/src/scripting/scriptengine.cpp index f69c5140e4..f55b7d2449 100644 --- a/src/scripting/scriptengine.cpp +++ b/src/scripting/scriptengine.cpp @@ -159,40 +159,95 @@ bool ScriptEngine::runScript(const std::string& script) { if (OsEng.parallelConnection()->isHost()){ std::string lib, func; - if (findLibraryAndFunction(lib, func, script)){ - - LuaLibrary *library = nullptr; - std::set::const_iterator libit; - for (libit = _registeredLibraries.cbegin(); - libit != _registeredLibraries.cend(); - ++libit){ - if (libit->name.compare(lib) == 0){ - break; - } - } - - std::vector::const_iterator funcit; - //library was found - if (libit != _registeredLibraries.cend()){ - for (funcit = libit->functions.cbegin(); - funcit != libit->functions.cend(); - ++funcit){ - //function was found! - if (funcit->name.compare(func) == 0){ - //is the function of a type that should be shared via parallel connection? - if (funcit->parallelShared){ - OsEng.parallelConnection()->sendScript(script); - } - } - } - } + if (parseLibraryAndFunctionNames(lib, func, script) && shouldScriptBeSent(lib, func)){ + OsEng.parallelConnection()->sendScript(script); + cacheScript(lib, func, script); } } return true; } + + +bool ScriptEngine::runScriptFile(const std::string& filename) { + if (filename.empty()) { + LWARNING("Filename was empty"); + return false; + } + if (!FileSys.fileExists(filename)) { + LERROR("Script with name '" << filename << "' did not exist"); + return false; + } + + int status = luaL_loadfile(_state, filename.c_str()); + if (status != LUA_OK) { + LERROR("Error loading script: '" << lua_tostring(_state, -1) << "'"); + return false; + } + + LDEBUG("Executing script '" << filename << "'"); + if (lua_pcall(_state, 0, LUA_MULTRET, 0)) { + LERROR("Error executing script: " << lua_tostring(_state, -1)); + return false; + } -bool ScriptEngine::findLibraryAndFunction(std::string &library, std::string &function, const std::string &script){ + return true; +} + +bool ScriptEngine::shouldScriptBeSent(const std::string &library, const std::string &function){ + + std::set::const_iterator libit; + for (libit = _registeredLibraries.cbegin(); + libit != _registeredLibraries.cend(); + ++libit){ + if (libit->name.compare(library) == 0){ + break; + } + } + + std::vector::const_iterator funcit; + //library was found + if (libit != _registeredLibraries.cend()){ + for (funcit = libit->functions.cbegin(); + funcit != libit->functions.cend(); + ++funcit){ + //function was found! + if (funcit->name.compare(function) == 0){ + //is the function of a type that should be shared via parallel connection? + return funcit->parallelShared; + } + } + } + + return false; +} + +void ScriptEngine::cacheScript(const std::string &library, const std::string &function, const std::string &script){ + _cachedScriptsMutex.lock(); + _cachedScripts[library][function] = script; + _cachedScriptsMutex.unlock(); +} + +std::vector ScriptEngine::cachedScripts(){ + _cachedScriptsMutex.lock(); + + std::vector retVal; + std::map>::const_iterator outerIt; + std::map::const_iterator innerIt; + for(outerIt = _cachedScripts.cbegin(); + outerIt != _cachedScripts.cend(); + ++outerIt){ + for(innerIt = outerIt->second.cbegin(); + innerIt != outerIt->second.cend(); + ++innerIt){ + + } + } + + _cachedScriptsMutex.unlock(); +} + +bool ScriptEngine::parseLibraryAndFunctionNames(std::string &library, std::string &function, const std::string &script){ //"deconstruct the script to find library and function name //assuming a script looks like: "openspace.library.function()" @@ -234,31 +289,6 @@ bool ScriptEngine::findLibraryAndFunction(std::string &library, std::string &fun return !function.empty(); } -bool ScriptEngine::runScriptFile(const std::string& filename) { - if (filename.empty()) { - LWARNING("Filename was empty"); - return false; - } - if (!FileSys.fileExists(filename)) { - LERROR("Script with name '" << filename << "' did not exist"); - return false; - } - - int status = luaL_loadfile(_state, filename.c_str()); - if (status != LUA_OK) { - LERROR("Error loading script: '" << lua_tostring(_state, -1) << "'"); - return false; - } - - LDEBUG("Executing script '" << filename << "'"); - if (lua_pcall(_state, 0, LUA_MULTRET, 0)) { - LERROR("Error executing script: " << lua_tostring(_state, -1)); - return false; - } - - return true; -} - bool ScriptEngine::hasLibrary(const std::string& name) { auto it = std::find_if( From c1cf5f458198e2be94e19211d08eca72cb027e97 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Fri, 26 Jun 2015 13:55:02 +0200 Subject: [PATCH 223/329] added temporary functionality to be able to ensure that startup scripts are also synchronised over parallel connection if they should be. --- src/engine/openspaceengine.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index b7cc428445..d54e722b3b 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -473,6 +473,23 @@ void OpenSpaceEngine::runScripts(const ghoul::Dictionary& scripts) { scripts.getValue(key, scriptPath); std::string&& absoluteScriptPath = absPath(scriptPath); _engine->scriptEngine()->runScriptFile(absoluteScriptPath); + + //@JK + //temporary solution to ensure that startup scripts may be syncrhonized over parallel connection + std::ifstream scriptFile; + scriptFile.open(absoluteScriptPath.c_str()); + std::string line; + + while(getline(scriptFile,line)){ + //valid line and not a comment + if(line.size() > 0 && line.at(0) != '-'){ + std::string lib, func; + if(_engine->scriptEngine()->parseLibraryAndFunctionNames(lib, func, line) && + _engine->scriptEngine()->shouldScriptBeSent(lib, func)){ + _engine->scriptEngine()->cacheScript(lib, func, line); + } + } + } } } From 6057019d20537a0c17fe58f9862bfb5e49364db9 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Fri, 26 Jun 2015 14:06:20 +0200 Subject: [PATCH 224/329] added proper return value to cachedScripts function --- src/scripting/scriptengine.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/scripting/scriptengine.cpp b/src/scripting/scriptengine.cpp index f55b7d2449..f5b52c2f5a 100644 --- a/src/scripting/scriptengine.cpp +++ b/src/scripting/scriptengine.cpp @@ -240,11 +240,13 @@ std::vector ScriptEngine::cachedScripts(){ for(innerIt = outerIt->second.cbegin(); innerIt != outerIt->second.cend(); ++innerIt){ - + retVal.push_back(innerIt->second); } } _cachedScriptsMutex.unlock(); + + return retVal; } bool ScriptEngine::parseLibraryAndFunctionNames(std::string &library, std::string &function, const std::string &script){ From 2bde14b7286fc107699daea8fdc1ff2f506c3f8d Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 26 Jun 2015 19:20:56 +0200 Subject: [PATCH 225/329] Fix crash that happens when SpiceManager is deinitialized --- include/openspace/util/spicemanager.h | 2 -- src/util/spicemanager.cpp | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/include/openspace/util/spicemanager.h b/include/openspace/util/spicemanager.h index 4ffe0383c3..1a8df376ac 100644 --- a/include/openspace/util/spicemanager.h +++ b/include/openspace/util/spicemanager.h @@ -718,8 +718,6 @@ protected: /// The last assigned kernel-id, used to determine the next free kernel id KernelIdentifier _lastAssignedKernel; - - static SpiceManager* _manager; }; } // namespace openspace diff --git a/src/util/spicemanager.cpp b/src/util/spicemanager.cpp index fea22903c9..5a806ea244 100644 --- a/src/util/spicemanager.cpp +++ b/src/util/spicemanager.cpp @@ -40,8 +40,6 @@ namespace { namespace openspace { -SpiceManager* SpiceManager::_manager = nullptr; - SpiceManager::SpiceManager() : _lastAssignedKernel(0) { @@ -52,7 +50,7 @@ SpiceManager::SpiceManager() } SpiceManager::~SpiceManager() { - for (const KernelInformation& i : _manager->_loadedKernels) + for (const KernelInformation& i : _loadedKernels) unload_c(i.path.c_str()); // Set values back to default From 7bf24ec74eca6d4ac6c50ca13a1e709f4dcaad19 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 26 Jun 2015 23:31:51 +0200 Subject: [PATCH 226/329] Remove the rendering method override --- openspace.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openspace.cfg b/openspace.cfg index c41cecff48..6c9f304463 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -55,5 +55,5 @@ return { File = "${BASE_PATH}/Properties.txt" }, DownloadRequestURL = "http://openspace.itn.liu.se/request.cgi", - RenderingMethod = "ABufferFrameBuffer" + -- RenderingMethod = "ABufferFrameBuffer" } \ No newline at end of file From 0d0c963bcec428e1f3bf2fad79178a084206e592 Mon Sep 17 00:00:00 2001 From: Anton Arbring Date: Sat, 27 Jun 2015 15:00:55 -0400 Subject: [PATCH 227/329] Fixed path bug --- src/rendering/renderengine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 92ea150b82..4b448c77fc 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -914,9 +914,9 @@ void RenderEngine::changeViewPoint(std::string origin) { if (origin == "Pluto") { if (newHorizonsPathNodeP) { Renderable* R = newHorizonsPathNodeP->renderable(); + newHorizonsPathNodeP->setParent(plutoBarycenterNode); nhPath = static_cast(R); nhPath->calculatePath("PLUTO BARYCENTER"); - newHorizonsPathNodeP->setParent(plutoBarycenterNode); } plutoBarycenterNode->setParent(scene()->sceneGraphNode("SolarSystem")); @@ -1079,9 +1079,9 @@ void RenderEngine::changeViewPoint(std::string origin) { if (origin == "Jupiter") { if (newHorizonsPathNodeJ) { Renderable* R = newHorizonsPathNodeJ->renderable(); + newHorizonsPathNodeJ->setParent(jupiterBarycenterNode); nhPath = static_cast(R); nhPath->calculatePath("JUPITER BARYCENTER"); - newHorizonsPathNodeJ->setParent(jupiterBarycenterNode); } jupiterBarycenterNode->setParent(scene()->sceneGraphNode("SolarSystem")); From 4abb7f210badccc931cbb5e104b1a86197f2cb31 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 29 Jun 2015 16:18:17 +0200 Subject: [PATCH 228/329] Disable blackout on startup --- src/rendering/renderengine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 4b448c77fc..bc40dd4c7c 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -99,7 +99,7 @@ RenderEngine::RenderEngine() , _takeScreenshot(false) , _doPerformanceMeasurements(false) , _performanceMemory(nullptr) - , _globalBlackOutFactor(0.f) + , _globalBlackOutFactor(1.f) , _fadeDuration(2.f) , _currentFadeTime(0.f) , _fadeDirection(0) From 0b3552ea09d60078df981998b4f40b9265bd3a39 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Tue, 30 Jun 2015 09:23:25 +0200 Subject: [PATCH 229/329] added initialization message encoding + sending and receiving + decoding --- src/network/parallelconnection.cpp | 97 +++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 3 deletions(-) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index f3a5ba4223..d0f266d241 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -122,8 +122,6 @@ namespace openspace { LERROR("Failed to parse hints for Parallel Connection"); } - LINFO("Attempting to connect to address "<< _address << " on port " << _port); - //we're not connected _isConnected.store(false); @@ -184,6 +182,8 @@ namespace openspace { //while the connection thread is still running while (!_isConnected.load()){ + LINFO("Attempting to connect to server "<< _address << " on port " << _port); + //try to connect result = connect(_clientSocket, info->ai_addr, (int)info->ai_addrlen); @@ -271,7 +271,71 @@ namespace openspace { } void ParallelConnection::decodeInitializationMessage(){ - printf("Init message received!\n"); + + int result; + uint16_t numScripts; + + //create a buffer to hold the number of scripts in the initialization message + std::vector buffer; + buffer.resize(sizeof(numScripts)); + + //read number of scripts in the initialization message + result = receiveData(_clientSocket, buffer, sizeof(numScripts), 0); + + if (result <= 0){ + //error + return; + } + + //assign number of scripts + numScripts = *reinterpret_cast(buffer.data()); + + //declare placeholder for all received scripts + std::vector initScripts; + initScripts.reserve(numScripts); + + //length of each script and resize receiveing buffer + uint16_t scriptlen; + buffer.clear(); + buffer.resize(sizeof(scriptlen)); + + //buffer for holding received scripts + std::vector scriptbuffer; + + for(int n = 0; n < numScripts; ++n){ + + //read size in chars of next script + result = receiveData(_clientSocket, buffer, sizeof(scriptlen), 0); + + if (result <= 0){ + //error + return; + } + + //assign size of next script + scriptlen = *reinterpret_cast(buffer.data()); + + //resize buffer + scriptbuffer.clear(); + scriptbuffer.resize(scriptlen); + + //read next script + result = receiveData(_clientSocket, scriptbuffer, scriptlen, 0); + + if (result <= 0){ + //error + return; + } + + //create a string from received chars in buffer + std::string script; + script.assign(scriptbuffer.begin(), scriptbuffer.end()); + + //and add that script to all received init scripts + initScripts.push_back(script); + + } + } void ParallelConnection::decodeStreamDataMessage(){ @@ -431,6 +495,33 @@ namespace openspace { receiveData(_clientSocket, buffer, sizeof(uint32_t), 0); uint32_t requesterID = *reinterpret_cast(buffer.data()); printf("InitRequest message received from client %d!\n", requesterID); + + //construct init msg + std::vector scripts = OsEng.scriptEngine()->cachedScripts(); + uint16_t numScrips = scripts.size(); + + //write header + buffer.clear(); + writeHeader(buffer, MessageTypes::Initialization); + + //write number of scripts + buffer.insert(buffer.end(), reinterpret_cast(&numScrips), reinterpret_cast(&numScrips) + sizeof(numScrips)); + + uint16_t msglen; + std::vector::const_iterator it; + + //write all scripts + for(it = scripts.cbegin(); + it != scripts.cend(); + ++it){ + //write size of script in chars + msglen = (*it).size(); + buffer.insert(buffer.end(), reinterpret_cast(msglen), reinterpret_cast(msglen) + sizeof(msglen)); + buffer.insert(buffer.end(), (*it).begin(), (*it).end()); + } + + //send initialization message + send(_clientSocket, buffer.data(), buffer.size(), 0); } void ParallelConnection::listenCommunication(){ From d834603c66a457dbd0dc7f1ab7decb47e200b313 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Tue, 30 Jun 2015 18:30:29 +0200 Subject: [PATCH 230/329] Clearing function bindings to avoid messages being logged from SGCT after OpenSpace engine has been destroyed. --- apps/OpenSpace/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/OpenSpace/main.cpp b/apps/OpenSpace/main.cpp index 92a18417ff..64cf7f8b56 100644 --- a/apps/OpenSpace/main.cpp +++ b/apps/OpenSpace/main.cpp @@ -173,6 +173,10 @@ int main(int argc, char** argv) { LDEBUG("Starting rendering loop"); _sgctEngine->render(); + //clear function bindings to avoid crash after destroying the OpenSpace Engine + sgct::MessageHandler::instance()->setLogToCallback(false); + sgct::MessageHandler::instance()->setLogCallback(nullptr); + LDEBUG("Destroying OpenSpaceEngine"); openspace::OpenSpaceEngine::destroy(); From d6cb9394748a07963cf2a4118f5d94b682045deb Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 1 Jul 2015 09:55:13 +0200 Subject: [PATCH 231/329] created a send que for messages and a thread to handle sending --- .../openspace/network/parallelconnection.h | 9 ++- src/network/parallelconnection.cpp | 55 +++++++++++++++---- 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/include/openspace/network/parallelconnection.h b/include/openspace/network/parallelconnection.h index d5c8f0f05a..8aeb93d01b 100644 --- a/include/openspace/network/parallelconnection.h +++ b/include/openspace/network/parallelconnection.h @@ -129,6 +129,8 @@ namespace openspace{ void sendScript(const std::string script); + void queMessage(std::vector message); + enum MessageTypes{ Authentication=0, Initialization, @@ -199,6 +201,8 @@ namespace openspace{ int receiveData(_SOCKET & socket, std::vector &buffer, int length, int flags); + void sendLoop(); + uint32_t _passCode; std::string _port; std::string _address; @@ -206,11 +210,12 @@ namespace openspace{ _SOCKET _clientSocket; std::thread *_connectionThread; std::thread *_broadcastThread; + std::thread *_sendThread; std::atomic _isHost; std::atomic _isConnected; std::atomic _isListening; - std::vector _executedScripts; - std::mutex _executedScriptsMutex; + std::vector> _sendBuffer; + std::mutex _sendBufferMutex; }; } // namespace network diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index d0f266d241..b7407af03f 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -80,6 +80,7 @@ namespace openspace { _clientSocket(INVALID_SOCKET), _connectionThread(nullptr), _broadcastThread(nullptr), + _sendThread(nullptr), _isHost(false), _isConnected(false), _isListening(false) @@ -201,6 +202,7 @@ namespace openspace { //start listening for communication listenCommunication(); + } //try to connect once per second @@ -331,8 +333,8 @@ namespace openspace { std::string script; script.assign(scriptbuffer.begin(), scriptbuffer.end()); - //and add that script to all received init scripts - initScripts.push_back(script); + //que script with the script engine + OsEng.scriptEngine()->queueScript(script); } @@ -414,11 +416,24 @@ namespace openspace { //tell the script engine to execute the script when appropriate OsEng.scriptEngine()->queueScript(script); - - //add script to all executed scripts - _executedScriptsMutex.lock(); - _executedScripts.push_back(script); - _executedScriptsMutex.unlock(); + } + + void ParallelConnection::queMessage(std::vector message){ + _sendBufferMutex.lock(); + _sendBuffer.push_back(message); + _sendBufferMutex.unlock(); + } + + void ParallelConnection::sendLoop(){ + while(_isHost.load()){ + _sendBufferMutex.lock(); + if(_sendBuffer.size() > 0){ + //send first queued message + send(_clientSocket, _sendBuffer.front().data(), _sendBuffer.front().size(), 0); + _sendBuffer.erase(_sendBuffer.begin()); + } + _sendBufferMutex.unlock(); + } } void ParallelConnection::decodeHostInfoMessage(){ @@ -445,6 +460,9 @@ namespace openspace { //start broadcasting _isHost.store(true); _broadcastThread = new (std::nothrow) std::thread(&ParallelConnection::broadcast, this); + + //and sending messages + _sendThread = new (std::nothrow) std::thread(&ParallelConnection::sendLoop, this); } } else{ //we've been assigned as client @@ -462,6 +480,18 @@ namespace openspace { _broadcastThread = nullptr; } + //delete send thread + if(_sendThread != nullptr){ + _sendThread->join(); + delete _sendThread; + _sendThread = nullptr; + + //and clear all queued messages + _sendBufferMutex.lock(); + _sendBuffer.clear(); + _sendBufferMutex.unlock(); + } + } else{ //we were not broadcasting so nothing to do @@ -637,10 +667,6 @@ namespace openspace { } void ParallelConnection::sendScript(const std::string script){ - _executedScriptsMutex.lock(); - _executedScripts.push_back(script); - _executedScriptsMutex.unlock(); - uint16_t msglen = static_cast(script.length()); std::vector buffer; buffer.reserve(headerSize() + sizeof(msglen) + msglen); @@ -685,6 +711,13 @@ namespace openspace { _broadcastThread = nullptr; } + //join send thread and delete it + if (_sendThread != nullptr){ + _sendThread->join(); + delete _sendThread; + _sendThread = nullptr; + } + #if defined(__WIN32__) WSACleanup(); #endif From 6697eeb7b701596d062f291a097acc7599d9ca8b Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 1 Jul 2015 10:18:45 +0200 Subject: [PATCH 232/329] changes to how messages are sent --- src/network/parallelconnection.cpp | 55 ++++++++++++++++++------------ 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index b7407af03f..0a357ee8ac 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -93,7 +93,7 @@ namespace openspace { } void ParallelConnection::clientConnect(){ - //we're already connected, do nothing + //we're already connected, do nothing (dummy check) if(_isConnected.load()){ return; } @@ -237,13 +237,7 @@ namespace openspace { buffer.insert(buffer.end(), _name.begin(), _name.end()); //send buffer - int result = send(_clientSocket, buffer.data(), size, 0); - - //if send failed - if (result == SOCKET_ERROR){ - //failed to send auth msg. - LERROR("Failed to send authentication message."); - } + queMessage(buffer); } void ParallelConnection::delegateDecoding(uint32_t type){ @@ -425,12 +419,24 @@ namespace openspace { } void ParallelConnection::sendLoop(){ + int result; while(_isHost.load()){ _sendBufferMutex.lock(); if(_sendBuffer.size() > 0){ - //send first queued message - send(_clientSocket, _sendBuffer.front().data(), _sendBuffer.front().size(), 0); - _sendBuffer.erase(_sendBuffer.begin()); + + //send first queued message + result = send(_clientSocket, _sendBuffer.front().data(), _sendBuffer.front().size(), 0); + + //remove the message that was just sent + _sendBuffer.erase(_sendBuffer.begin()); + + //make sure everything went well + if (result == SOCKET_ERROR){ + //failed to send message + LERROR("Failed to send message.\nError: " << _ERRNO << " detected in connection."); + disconnect(); + } + } _sendBufferMutex.unlock(); } @@ -457,8 +463,10 @@ namespace openspace { } else{ - //start broadcasting + //we're the host _isHost.store(true); + + //start broadcasting _broadcastThread = new (std::nothrow) std::thread(&ParallelConnection::broadcast, this); //and sending messages @@ -509,8 +517,7 @@ namespace openspace { writeHeader(buffer, MessageTypes::InitializationRequest); //send message - send(_clientSocket, buffer.data(), buffer.size(), 0); - + queMessage(buffer); } } else{ @@ -551,7 +558,7 @@ namespace openspace { } //send initialization message - send(_clientSocket, buffer.data(), buffer.size(), 0); + queMessage(buffer); } void ParallelConnection::listenCommunication(){ @@ -589,6 +596,7 @@ namespace openspace { //connection rejected _isConnected.store(false); _isListening.store(false); + _isHost.store(false); } else{ LERROR("Error " << _ERRNO << " detected in connection!"); @@ -659,7 +667,7 @@ namespace openspace { writeHeader(buffer, MessageTypes::HostshipRequest); //send message - send(_clientSocket, buffer.data(), buffer.size(), 0); + queMessage(buffer); } void ParallelConnection::setPassword(const std::string& pwd){ @@ -681,7 +689,7 @@ namespace openspace { buffer.insert(buffer.end(), script.begin(), script.end()); //send message - send(_clientSocket, buffer.data(), buffer.size(), 0); + queMessage(buffer); } @@ -693,9 +701,11 @@ namespace openspace { //tell broadcast thread to stop broadcasting _isHost.store(false); - //tell connection thread to stop trying to connect and stop listening for communication - _isConnected.store(true); + //tell connection thread to stop listening _isListening.store(false); + + //tell connection thread that we are connected (to be able to join and delete thread) + _isConnected.store(true); //join connection thread and delete it if (_connectionThread != nullptr){ @@ -703,7 +713,10 @@ namespace openspace { delete _connectionThread; _connectionThread = nullptr; } - + + //make sure to set us as NOT connected again, connection thread is now deleted + _isConnected.store(false); + //join broadcast thread and delete it if (_broadcastThread != nullptr){ _broadcastThread->join(); @@ -812,7 +825,7 @@ namespace openspace { buffer.insert(buffer.end(), kfBuffer.begin(), kfBuffer.end()); //send message - send(_clientSocket, buffer.data(), buffer.size(), 0); + queMessage(buffer); //100 ms sleep - send keyframes 10 times per second std::this_thread::sleep_for(std::chrono::milliseconds(100)); From aea4a1ac0c2ca6f373472f2535d6fe9072ff4f98 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 1 Jul 2015 12:17:20 +0200 Subject: [PATCH 233/329] added a receive thread. fixed a bug where threads tried to join/delete themselves --- .../openspace/network/parallelconnection.h | 1 + src/network/parallelconnection.cpp | 189 +++++++++--------- 2 files changed, 98 insertions(+), 92 deletions(-) diff --git a/include/openspace/network/parallelconnection.h b/include/openspace/network/parallelconnection.h index 8aeb93d01b..3e23ead18a 100644 --- a/include/openspace/network/parallelconnection.h +++ b/include/openspace/network/parallelconnection.h @@ -211,6 +211,7 @@ namespace openspace{ std::thread *_connectionThread; std::thread *_broadcastThread; std::thread *_sendThread; + std::thread *_receiveThread; std::atomic _isHost; std::atomic _isConnected; std::atomic _isListening; diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index 0a357ee8ac..c9f9b31682 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -81,6 +81,7 @@ namespace openspace { _connectionThread(nullptr), _broadcastThread(nullptr), _sendThread(nullptr), + _receiveThread(nullptr), _isHost(false), _isConnected(false), _isListening(false) @@ -193,16 +194,19 @@ namespace openspace { { //we're connected _isConnected.store(true); - - //and ready to start receiving messages - _isListening.store(true); - - //send authentication - sendAuthentication(); - - //start listening for communication - listenCommunication(); - + + //and ready to start receiving messages + _isListening.store(true); + + //start listening for communication + _receiveThread = new (std::nothrow) std::thread(&ParallelConnection::listenCommunication, this); + + //start sending messages + _sendThread = new (std::nothrow) std::thread(&ParallelConnection::sendLoop, this); + + //and send authentication + sendAuthentication(); + } //try to connect once per second @@ -420,7 +424,7 @@ namespace openspace { void ParallelConnection::sendLoop(){ int result; - while(_isHost.load()){ + while(_isConnected.load()){ _sendBufferMutex.lock(); if(_sendBuffer.size() > 0){ @@ -433,8 +437,11 @@ namespace openspace { //make sure everything went well if (result == SOCKET_ERROR){ //failed to send message - LERROR("Failed to send message.\nError: " << _ERRNO << " detected in connection."); - disconnect(); + LERROR("Failed to send message.\nError: " << _ERRNO << " detected in connection, disconnecting."); + //@TODO a better solution for this - description: + //A thread cannot delete/join itself, in this case the listener thread would need to remove itself + //solution (for now) is to call the disconnect script via scriptengine. This is done from a separate thread so it works. + OsEng.scriptEngine()->queueScript("openspace.parallel.disconnect();"); } } @@ -468,9 +475,6 @@ namespace openspace { //start broadcasting _broadcastThread = new (std::nothrow) std::thread(&ParallelConnection::broadcast, this); - - //and sending messages - _sendThread = new (std::nothrow) std::thread(&ParallelConnection::sendLoop, this); } } else{ //we've been assigned as client @@ -486,20 +490,7 @@ namespace openspace { _broadcastThread->join(); delete _broadcastThread; _broadcastThread = nullptr; - } - - //delete send thread - if(_sendThread != nullptr){ - _sendThread->join(); - delete _sendThread; - _sendThread = nullptr; - - //and clear all queued messages - _sendBufferMutex.lock(); - _sendBuffer.clear(); - _sendBufferMutex.unlock(); - } - + } } else{ //we were not broadcasting so nothing to do @@ -521,8 +512,11 @@ namespace openspace { } } else{ - LERROR("Error " << _ERRNO << " detected in connection."); - disconnect(); + LERROR("Error " << _ERRNO << " detected in connection, disconnecting."); + //@TODO a better solution for this - description: + //A thread cannot delete/join itself, in this case the listener thread would need to remove itself + //solution (for now) is to call the disconnect script via scriptengine. This is done from a separate thread so it works. + OsEng.scriptEngine()->queueScript("openspace.parallel.disconnect();"); } } @@ -594,13 +588,17 @@ namespace openspace { else{ if (result == 0){ //connection rejected - _isConnected.store(false); - _isListening.store(false); - _isHost.store(false); + LERROR("Parallel connection rejected, disconnecting..."); } else{ - LERROR("Error " << _ERRNO << " detected in connection!"); + LERROR("Error " << _ERRNO << " detected in connection, disconnecting!"); } + + //@TODO (JK) a better solution for this - description: + //A thread cannot delete/join itself, in this case the listener thread would need to remove itself + //solution (for now) is to call the disconnect script via scriptengine. This is done from a separate thread so it works. + OsEng.scriptEngine()->queueScript("openspace.parallel.disconnect();"); + break; } } @@ -694,73 +692,80 @@ namespace openspace { void ParallelConnection::disconnect(){ - - //must be run before trying to join communication threads, else the threads are stuck trying to receive data - closeSocket(); - - //tell broadcast thread to stop broadcasting - _isHost.store(false); - - //tell connection thread to stop listening - _isListening.store(false); + if (_clientSocket != INVALID_SOCKET){ + //must be run before trying to join communication threads, else the threads are stuck trying to receive data + closeSocket(); - //tell connection thread that we are connected (to be able to join and delete thread) - _isConnected.store(true); - - //join connection thread and delete it - if (_connectionThread != nullptr){ - _connectionThread->join(); - delete _connectionThread; - _connectionThread = nullptr; - } + //tell broadcast thread to stop broadcasting + _isHost.store(false); - //make sure to set us as NOT connected again, connection thread is now deleted - _isConnected.store(false); + //tell connection thread to stop listening + _isListening.store(false); + + //tell connection thread that we are connected (to be able to join and delete thread) + _isConnected.store(true); + + //join connection thread and delete it + if (_connectionThread != nullptr){ + _connectionThread->join(); + delete _connectionThread; + _connectionThread = nullptr; + } + + //make sure to set us as NOT connected again, connection thread is now deleted + _isConnected.store(false); + + + //join receive thread and delete it + if (_receiveThread != nullptr){ + _receiveThread->join(); + delete _receiveThread; + _receiveThread = nullptr; + } + + //join broadcast thread and delete it + if (_broadcastThread != nullptr){ + _broadcastThread->join(); + delete _broadcastThread; + _broadcastThread = nullptr; + } + + //join send thread and delete it + if (_sendThread != nullptr){ + _sendThread->join(); + delete _sendThread; + _sendThread = nullptr; + } - //join broadcast thread and delete it - if (_broadcastThread != nullptr){ - _broadcastThread->join(); - delete _broadcastThread; - _broadcastThread = nullptr; - } - - //join send thread and delete it - if (_sendThread != nullptr){ - _sendThread->join(); - delete _sendThread; - _sendThread = nullptr; - } - #if defined(__WIN32__) - WSACleanup(); + WSACleanup(); #endif + } } void ParallelConnection::closeSocket(){ - if (_clientSocket != INVALID_SOCKET) - { - /* - Windows shutdown options - * SD_RECIEVE - * SD_SEND - * SD_BOTH - Linux & Mac shutdown options - * SHUT_RD (Disables further receive operations) - * SHUT_WR (Disables further send operations) - * SHUT_RDWR (Disables further send and receive operations) - */ + /* + Windows shutdown options + * SD_RECIEVE + * SD_SEND + * SD_BOTH - #ifdef __WIN32__ - shutdown(_clientSocket, SD_BOTH); - closesocket(_clientSocket); - #else - shutdown(_clientSocket, SHUT_RDWR); - close(_clientSocket); - #endif + Linux & Mac shutdown options + * SHUT_RD (Disables further receive operations) + * SHUT_WR (Disables further send operations) + * SHUT_RDWR (Disables further send and receive operations) + */ - _clientSocket = INVALID_SOCKET; - } +#ifdef __WIN32__ + shutdown(_clientSocket, SD_BOTH); + closesocket(_clientSocket); +#else + shutdown(_clientSocket, SHUT_RDWR); + close(_clientSocket); +#endif + + _clientSocket = INVALID_SOCKET; } bool ParallelConnection::initNetworkAPI(){ From 762e70f14be90f5df076d514d8388b5cefe0886b Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 1 Jul 2015 13:33:33 +0200 Subject: [PATCH 234/329] fixes to init message construction --- src/network/parallelconnection.cpp | 48 +++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index c9f9b31682..e17603735f 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -86,7 +86,7 @@ namespace openspace { _isConnected(false), _isListening(false) { - + } ParallelConnection::~ParallelConnection(){ @@ -94,6 +94,7 @@ namespace openspace { } void ParallelConnection::clientConnect(){ + //we're already connected, do nothing (dummy check) if(_isConnected.load()){ return; @@ -267,7 +268,7 @@ namespace openspace { default: //unknown message type break; - } + } } void ParallelConnection::decodeInitializationMessage(){ @@ -531,28 +532,47 @@ namespace openspace { std::vector scripts = OsEng.scriptEngine()->cachedScripts(); uint16_t numScrips = scripts.size(); - //write header - buffer.clear(); - writeHeader(buffer, MessageTypes::Initialization); - - //write number of scripts - buffer.insert(buffer.end(), reinterpret_cast(&numScrips), reinterpret_cast(&numScrips) + sizeof(numScrips)); - - uint16_t msglen; + uint16_t scriptlen; + uint32_t totlen = 0; std::vector::const_iterator it; + std::vector scriptMsg; //write all scripts for(it = scripts.cbegin(); it != scripts.cend(); ++it){ //write size of script in chars - msglen = (*it).size(); - buffer.insert(buffer.end(), reinterpret_cast(msglen), reinterpret_cast(msglen) + sizeof(msglen)); - buffer.insert(buffer.end(), (*it).begin(), (*it).end()); + scriptlen = (*it).size(); + scriptMsg.insert(scriptMsg.end(), reinterpret_cast(&scriptlen), reinterpret_cast(&scriptlen) + sizeof(scriptlen)); + + //write actual scripts + scriptMsg.insert(scriptMsg.end(), (*it).begin(), (*it).end()); + + //add script length to total data length + totlen += static_cast(scriptlen) + sizeof(scriptlen); } + //clear buffer + buffer.clear(); + + //write header + writeHeader(buffer, MessageTypes::Initialization); + + //write requester ID + buffer.insert(buffer.end(), reinterpret_cast(&requesterID), reinterpret_cast(&requesterID) + sizeof(requesterID)); + + + //write size of data chunk + buffer.insert(buffer.end(), reinterpret_cast(&totlen), reinterpret_cast(&totlen) + sizeof(totlen)); + + //write number of scripts + buffer.insert(buffer.end(), reinterpret_cast(&numScrips), reinterpret_cast(&numScrips) + sizeof(numScrips)); + + //write all scripts and their lengths + buffer.insert(buffer.end(), scriptMsg.begin(), scriptMsg.end()); + //send initialization message - queMessage(buffer); + queMessage(buffer); } void ParallelConnection::listenCommunication(){ From 7159dd8246bcb6ce4fd02cb852ea796beb990abb Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 1 Jul 2015 14:16:18 +0200 Subject: [PATCH 235/329] further fixes to thread stability --- .../openspace/network/parallelconnection.h | 3 ++ src/engine/openspaceengine.cpp | 1 + src/network/parallelconnection.cpp | 33 ++++++++++--------- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/include/openspace/network/parallelconnection.h b/include/openspace/network/parallelconnection.h index 3e23ead18a..ab748a199a 100644 --- a/include/openspace/network/parallelconnection.h +++ b/include/openspace/network/parallelconnection.h @@ -131,6 +131,8 @@ namespace openspace{ void queMessage(std::vector message); + void update(double dt); + enum MessageTypes{ Authentication=0, Initialization, @@ -215,6 +217,7 @@ namespace openspace{ std::atomic _isHost; std::atomic _isConnected; std::atomic _isListening; + std::atomic _performDisconnect; std::vector> _sendBuffer; std::mutex _sendBufferMutex; }; diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index d54e722b3b..670ba2647b 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -623,6 +623,7 @@ void OpenSpaceEngine::preSynchronization() { Time::ref().preSynchronization(); _interactionHandler->update(dt); + _parallelConnection->update(dt); //_interactionHandler.lockControls(); _scriptEngine->preSynchronization(); diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index e17603735f..c77aa38db8 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -84,7 +84,8 @@ namespace openspace { _receiveThread(nullptr), _isHost(false), _isConnected(false), - _isListening(false) + _isListening(false), + _performDisconnect(false) { } @@ -439,10 +440,7 @@ namespace openspace { if (result == SOCKET_ERROR){ //failed to send message LERROR("Failed to send message.\nError: " << _ERRNO << " detected in connection, disconnecting."); - //@TODO a better solution for this - description: - //A thread cannot delete/join itself, in this case the listener thread would need to remove itself - //solution (for now) is to call the disconnect script via scriptengine. This is done from a separate thread so it works. - OsEng.scriptEngine()->queueScript("openspace.parallel.disconnect();"); + _performDisconnect.store(true); } } @@ -514,10 +512,7 @@ namespace openspace { } else{ LERROR("Error " << _ERRNO << " detected in connection, disconnecting."); - //@TODO a better solution for this - description: - //A thread cannot delete/join itself, in this case the listener thread would need to remove itself - //solution (for now) is to call the disconnect script via scriptengine. This is done from a separate thread so it works. - OsEng.scriptEngine()->queueScript("openspace.parallel.disconnect();"); + _performDisconnect.store(true); } } @@ -614,16 +609,18 @@ namespace openspace { LERROR("Error " << _ERRNO << " detected in connection, disconnecting!"); } - //@TODO (JK) a better solution for this - description: - //A thread cannot delete/join itself, in this case the listener thread would need to remove itself - //solution (for now) is to call the disconnect script via scriptengine. This is done from a separate thread so it works. - OsEng.scriptEngine()->queueScript("openspace.parallel.disconnect();"); - + _performDisconnect.store(true); break; } } } + + void ParallelConnection::update(double dt){ + if(_performDisconnect.load()){ + disconnect(); + } + } int ParallelConnection::receiveData(_SOCKET & socket, std::vector &buffer, int length, int flags){ int result = 0; @@ -713,6 +710,10 @@ namespace openspace { void ParallelConnection::disconnect(){ if (_clientSocket != INVALID_SOCKET){ + + //we're disconnecting + _performDisconnect.store(false); + //must be run before trying to join communication threads, else the threads are stuck trying to receive data closeSocket(); @@ -758,7 +759,9 @@ namespace openspace { } #if defined(__WIN32__) - WSACleanup(); + //this line causes issues with SGCT since winsock dll file is unloaded upon call + //@TODO should this be here? +// WSACleanup(); #endif } } From a89bce72ccc8623699b7abe31930c4daa30bbc02 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 1 Jul 2015 14:17:23 +0200 Subject: [PATCH 236/329] Merge branch 'feature/remote' of openspace.itn.liu.se:/openspace into feature/remote fixed decoding of init messages to include data size --- src/network/parallelconnection.cpp | 42 +++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index e17603735f..008d1fe5ea 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -275,22 +275,39 @@ namespace openspace { int result; uint16_t numScripts; + uint32_t datalen; + uint32_t id; //create a buffer to hold the number of scripts in the initialization message std::vector buffer; - buffer.resize(sizeof(numScripts)); - - //read number of scripts in the initialization message - result = receiveData(_clientSocket, buffer, sizeof(numScripts), 0); - - if (result <= 0){ - //error - return; - } - - //assign number of scripts - numScripts = *reinterpret_cast(buffer.data()); + buffer.resize(sizeof(id)); + //read recepient ID (mine) + result = receiveData(_clientSocket, buffer, sizeof(id), 0); + if (result <= 0){ + //error + return; + } + id = *(reinterpret_cast(buffer.data())); + + //read data length + result = receiveData(_clientSocket, buffer, sizeof(datalen), 0); + if (result <= 0){ + //error + return; + } + datalen = *(reinterpret_cast(buffer.data())); + + buffer.clear(); + buffer.resize(sizeof(numScripts)); + //read number of scripts + result = receiveData(_clientSocket, buffer, sizeof(numScripts), 0); + if (result <= 0){ + //error + return; + } + numScripts = *(reinterpret_cast(buffer.data())); + //declare placeholder for all received scripts std::vector initScripts; initScripts.reserve(numScripts); @@ -334,7 +351,6 @@ namespace openspace { //que script with the script engine OsEng.scriptEngine()->queueScript(script); - } } From c9f3402cae6b31c7b2489a534ffc74ef1fc333fa Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 1 Jul 2015 14:23:46 +0200 Subject: [PATCH 237/329] moved assignment of variable --- src/network/parallelconnection.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index 1396a555d1..983ea9997f 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -725,11 +725,11 @@ namespace openspace { void ParallelConnection::disconnect(){ + //we're disconnecting + _performDisconnect.store(false); + if (_clientSocket != INVALID_SOCKET){ - //we're disconnecting - _performDisconnect.store(false); - //must be run before trying to join communication threads, else the threads are stuck trying to receive data closeSocket(); From 532e2b1004a02d7ff7fb1d20a55b5908b0aee0cf Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 1 Jul 2015 14:31:45 +0200 Subject: [PATCH 238/329] even more fixes to thread safety, fixed an issue on Mac where logging kept going after connection was lost --- src/network/parallelconnection.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index 983ea9997f..ca7fa9c994 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -456,7 +456,12 @@ namespace openspace { if (result == SOCKET_ERROR){ //failed to send message LERROR("Failed to send message.\nError: " << _ERRNO << " detected in connection, disconnecting."); + + //stop all threads and signal that a disconnect should be performed _performDisconnect.store(true); + _isConnected.store(false); + _isHost.store(false); + _isListening.store(false); } } @@ -528,7 +533,11 @@ namespace openspace { } else{ LERROR("Error " << _ERRNO << " detected in connection, disconnecting."); + //stop all threads and signal that a disconnect should be performed _performDisconnect.store(true); + _isConnected.store(false); + _isHost.store(false); + _isListening.store(false); } } @@ -625,7 +634,11 @@ namespace openspace { LERROR("Error " << _ERRNO << " detected in connection, disconnecting!"); } + //stop all threads and signal that a disconnect should be performed _performDisconnect.store(true); + _isConnected.store(false); + _isHost.store(false); + _isListening.store(false); break; } } From 86fc74770be359da2c57a7d5d3724e00f758b5d9 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 1 Jul 2015 16:17:51 +0200 Subject: [PATCH 239/329] Fixing issues that occurred with newer nVidia drivers and ABuffer rendering --- ext/ghoul | 2 +- src/abuffer/abuffersinglelinked.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ext/ghoul b/ext/ghoul index 3a095f1ce6..a5111530a3 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 3a095f1ce633e52b94877bde67e77775262e3dbd +Subproject commit a5111530a3e211a45db3f2f8de1ae6c34caeecbf diff --git a/src/abuffer/abuffersinglelinked.cpp b/src/abuffer/abuffersinglelinked.cpp index 834f54ad07..35305bbced 100644 --- a/src/abuffer/abuffersinglelinked.cpp +++ b/src/abuffer/abuffersinglelinked.cpp @@ -89,6 +89,9 @@ bool ABufferSingleLinked::reinitializeInternal() { glBindTexture(GL_TEXTURE_2D, _anchorPointerTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_R32UI, _width, _height, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _anchorPointerTextureInitializer); glBufferData(GL_PIXEL_UNPACK_BUFFER, _totalPixels * sizeof(GLuint), NULL, GL_STATIC_DRAW); From 84ba9b150397bf4be9d1df3ea1175971beca8cf3 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 1 Jul 2015 17:55:06 +0200 Subject: [PATCH 240/329] Set correct default scene file --- apps/Launcher/mainwindow.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/Launcher/mainwindow.cpp b/apps/Launcher/mainwindow.cpp index 115b823dd1..c186e6c36c 100644 --- a/apps/Launcher/mainwindow.cpp +++ b/apps/Launcher/mainwindow.cpp @@ -197,6 +197,9 @@ void MainWindow::initialize() { _sceneFiles.insert(i.fileName(), i.absoluteFilePath()); _scenes->addItem(i.fileName()); } + + _scenes->setCurrentText("default.scene"); + _syncWidget->setSceneFiles(_sceneFiles); } From c205c720c696813e5c5044962faf3c24fb386950 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 1 Jul 2015 17:55:26 +0200 Subject: [PATCH 241/329] Removal of the old imagesequencer --- modules/newhorizons/CMakeLists.txt | 10 +- modules/newhorizons/util/imagesequencer.cpp | 545 -------------------- modules/newhorizons/util/imagesequencer.h | 140 ----- 3 files changed, 4 insertions(+), 691 deletions(-) delete mode 100644 modules/newhorizons/util/imagesequencer.cpp delete mode 100644 modules/newhorizons/util/imagesequencer.h diff --git a/modules/newhorizons/CMakeLists.txt b/modules/newhorizons/CMakeLists.txt index 6b9cd1d8b7..5ee0503ec1 100644 --- a/modules/newhorizons/CMakeLists.txt +++ b/modules/newhorizons/CMakeLists.txt @@ -32,11 +32,10 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplanetprojection.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableshadowcylinder.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometryprojection.h - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablemodelprojection.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablemodelprojection.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/writeToTexture.h ${CMAKE_CURRENT_SOURCE_DIR}/util/decoder.h ${CMAKE_CURRENT_SOURCE_DIR}/util/hongkangparser.h - ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer.h ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer2.h ${CMAKE_CURRENT_SOURCE_DIR}/util/instrumentdecoder.h ${CMAKE_CURRENT_SOURCE_DIR}/util/labelparser.h @@ -54,10 +53,9 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplanetprojection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableshadowcylinder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometryprojection.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablemodelprojection.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablemodelprojection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/decoder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/hongkangparser.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer2.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/instrumentdecoder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/labelparser.cpp @@ -74,9 +72,9 @@ set(SHADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/shaders/fov_vs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/projectiveTexture_fs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/projectiveTexture_vs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/projectionPass_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/projectionPass_fs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/projectionPass_vs.glsl - ${CMAKE_CURRENT_SOURCE_DIR}/shaders/modelShader_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/modelShader_fs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/modelShader_vs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/terminatorshadow_fs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/terminatorshadow_vs.glsl diff --git a/modules/newhorizons/util/imagesequencer.cpp b/modules/newhorizons/util/imagesequencer.cpp deleted file mode 100644 index dbcc2dc767..0000000000 --- a/modules/newhorizons/util/imagesequencer.cpp +++ /dev/null @@ -1,545 +0,0 @@ -/***************************************************************************************** -* * -* OpenSpace * -* * -* Copyright (c) 2014 * -* * -* 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. * -****************************************************************************************/ - -// open space includes -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace { -const std::string _loggerCat = "ImageSequencer"; -} - -namespace openspace { - -ImageSequencer* ImageSequencer::_sequencer = nullptr; - -struct ImageParams{ - double startTime; - std::string path; - std::string activeInstrument; - std::string target; - bool projected; -}; - - - -std::vector> _timeStamps; -void createImage(std::vector& vec, double t1, std::string instrument, std::string target, std::string path = "dummypath"); - -auto imageComparer = [](const ImageParams &a, const ImageParams &b)->bool{ - return a.startTime < b.startTime; -}; - - -std::vector::iterator binary_find(std::vector::iterator begin, - std::vector::iterator end, - const ImageParams &val, - bool(*imageComparer)(const ImageParams &a, const ImageParams &b)){ - // Finds the lower bound in at most log(last - first) + 1 comparisons - std::vector::iterator it = std::lower_bound(begin, end, val, imageComparer); - if (it != begin){ - return it; - } - return end; -} - -ImageSequencer::ImageSequencer() - : _nextCapture(-1.0) - , _currentTime(-1.0) - , _sequenceIDs(0) - , _defaultCaptureImage(absPath("${OPENSPACE_DATA}/scene/common/textures/placeholder_blank.png")) - , _targetsAdded(false) -{} - - -ImageSequencer& ImageSequencer::ref() { - assert(_sequencer != nullptr); - return *_sequencer; -} -void ImageSequencer::initialize() { - assert(_sequencer == nullptr); - _sequencer = new ImageSequencer; -} - -void ImageSequencer::deinitialize() { - delete _sequencer; - _sequencer = nullptr; -} - -void ImageSequencer::setSequenceId(int& id){ - id = _sequenceIDs; - _sequenceIDs++; -} - -void ImageSequencer::addSequenceObserver(int sequenceID, std::string name, std::vector payload){ - if (sequenceID >= 0){ - _observers.insert(std::make_pair(sequenceID, name)); - _instruments.insert(std::make_pair(name, payload)); - } -} - -void ImageSequencer::registerTargets(std::vector& potential){ - for (auto p : potential){ - if (_projectableTargets.find(p) == _projectableTargets.end()){ - _projectableTargets[p] = _currentTime; - } - } -} - -void ImageSequencer::update(double time){ - _currentTime = time; - static bool time_initialized; - - if (!time_initialized){ - for (auto &it : _projectableTargets) { - it.second = _currentTime; - assert(it.second > 0.0); - } - time_initialized = true; - } -} - -void createImage(std::vector& vec, double t1, std::string instrument, std::string target, std::string path) { - // insert - ImageParams image; - image.startTime = t1; - image.path = path; - image.activeInstrument = instrument; - image.target = target; - image.projected = false; - - vec.push_back(image); -} - -double ImageSequencer::getNextCaptureTime(){ - return _nextCapture; -} -double ImageSequencer::nextCaptureTime(double time, int sequenceID){ - if (time < _nextCapture) return _nextCapture; - auto it = binary_find(_timeStamps[sequenceID].begin(), _timeStamps[sequenceID].end(), { time, "", "", "", false }, imageComparer); - if (it == _timeStamps[sequenceID].end()) return _nextCapture; - - return it->startTime; -} - -std::string ImageSequencer::findActiveInstrument(double time, int sequenceID){ - auto it = binary_find(_timeStamps[sequenceID].begin(), _timeStamps[sequenceID].end(), { time, "", "", "",false }, imageComparer); - if ((it == _timeStamps[sequenceID].end())){ - _activeInstrument = "Not found, incufficient playbook-data"; - }else{ - _activeInstrument = std::prev(it)->activeInstrument; - } - - return _activeInstrument; -} - -void ImageSequencer::augumentSequenceWithTargets(int sequenceID){ - if (!_targetsAdded){ - // if there is an registered observer for this sequence - if (_observers.count(sequenceID) > 0) { - // find observer - std::string observer = _observers.at(sequenceID); - // find its instruments - std::map >::iterator it2 = _instruments.find(observer); - if (it2 != _instruments.end()){ - std::string _targetFOV; - bool _withinFOV; - // for each image taken - for (std::vector::iterator image = _timeStamps[sequenceID].begin(); image != _timeStamps[sequenceID].end(); ++image) { - // traverse potential targets... - for (auto t : _projectableTargets){ - // ... and potential instruments - for (auto i : it2->second){ - //register precisely which target is being projected to upon image-capture - bool success = openspace::SpiceManager::ref().targetWithinFieldOfView( - i, // Instrumnet - t.first, // projectables - observer, // new horizons - "ELLIPSOID", - "NONE", - image->startTime, - _withinFOV); - //if (!_withinFOV) image->target = "VOID"; - if (success && _withinFOV){ - image->target = t.first; - //once we find it abort search, break the loop. - break; - } - } - } - } - }else{ - LERROR("Spacecraft payload not provided, cannot write playbook"); - } - } - else{ - LERROR("Did not find observing spacecraft for sequence, cannot write playbook"); - } - _targetsAdded = true; - } -} - -bool ImageSequencer::getImagePath(std::vector>& _imageTimes, int sequenceID, std::string projectee, bool withinFOV){ - /*if (withinFOV && !Time::ref().timeJumped()){ - getSingleImage(_imageTimes, sequenceID, projectee); - }else{*/ - getMultipleImages(_imageTimes, sequenceID, projectee); - //} - return true; -} - -bool ImageSequencer::getMultipleImages(std::vector>& _imageTimes, int sequenceID, std::string projectee){ - double previousTime; - std::map::iterator it = _projectableTargets.find(projectee); - if (it != _projectableTargets.end()){ - previousTime = it->second; - it->second = _currentTime; - } - auto it1 = binary_find(_timeStamps[sequenceID].begin(), _timeStamps[sequenceID].end(), { previousTime, "", "", "", false }, imageComparer); - auto it2 = binary_find(_timeStamps[sequenceID].begin(), _timeStamps[sequenceID].end(), { _currentTime, "", "", "", false }, imageComparer); - - if (it1 != _timeStamps[sequenceID].end() && it2 != _timeStamps[sequenceID].end() && it1 != it2){ - std::transform(it1, it2, std::back_inserter(_imageTimes), - [](const ImageParams& i) { - return std::make_pair(i.startTime, i.path); - }); - } - std::reverse(_imageTimes.begin(), _imageTimes.end()); - - double upcoming = nextCaptureTime(_currentTime, sequenceID); - if (_nextCapture != upcoming){ - _nextCapture = upcoming; - _intervalLength = upcoming - _currentTime; - } - - return true; -} - -bool ImageSequencer::getSingleImage(std::vector>& _imageTimes, int sequenceID, std::string projectee){ - - auto bfind = [](std::vector::iterator begin, - std::vector::iterator end, - const ImageParams &val, - bool(*imageComparer)(const ImageParams &a, const ImageParams &b))->std::vector::iterator{ - // Finds the lower bound in at most log(last - first) + 1 comparisons - std::vector::iterator it = std::lower_bound(begin, end, val, imageComparer); - if (it != begin){ - return std::prev(it); - } - return end; - }; - - auto it = bfind(_timeStamps[sequenceID].begin(), _timeStamps[sequenceID].end(), { _currentTime, "", "", "", false }, imageComparer); - - if (it != _timeStamps[sequenceID].end() && !it->projected){ - it->projected = true; - _imageTimes.push_back(std::make_pair(it->startTime, it->path)); - } - - double upcoming = nextCaptureTime(_currentTime, sequenceID); - if (_nextCapture != upcoming){ - _nextCapture = upcoming; - _intervalLength = upcoming - _currentTime; - } - - return true; -} -/* -bool ImageSequencer::getImagePath(std::vector>& _imageTimes, int sequenceID, std::string projectee, bool closedInterval){ - double t = _currentTime; - - double ptime; - std::map::iterator it = _previous.find(projectee); - if (it != _previous.end()){ - ptime = it->second; - it->second = _currentTime; - } - - while (t > ptime){ - auto binary_find = [](std::vector::iterator begin, - std::vector::iterator end, - const ImageParams &val, - bool(*imageComparer)(const ImageParams &a, const ImageParams &b))->std::vector::iterator{ - // Finds the lower bound in at most log(last - first) + 1 comparisons - std::vector::iterator it = std::lower_bound(begin, end, val, imageComparer); - if (it != begin){ - return std::prev(it); - } - return end; - }; - - auto it = binary_find(_timeStamps[sequenceID].begin(), _timeStamps[sequenceID].end(), { t, 0, "", "", false }, imageComparer); - - if (it == _timeStamps[sequenceID].end() || it->startTime < ptime) break; - - if (!it->projected || it != _timeStamps[sequenceID].end()){ - _imageTimes.push_back(std::make_pair(it->startTime, it->path)); - } - t = it->startTime - 1; - //it->projected = true; - } - std::reverse(_imageTimes.begin(), _imageTimes.end()); - - double upcoming = nextCaptureTime(_currentTime, sequenceID); - if (_nextCapture != upcoming){ - _nextCapture = upcoming; - _intervalLength = upcoming - _currentTime; - } - - return true; -}*/ -/* -bool ImageSequencer::getImagePath(double& currentTime, std::string& path, bool closedInterval){ - auto binary_find = [](std::vector::iterator begin, - std::vector::iterator end, - const ImageParams &val, - bool(*imageComparer)(const ImageParams &a, const ImageParams &b))->std::vector::iterator{ - // Finds the lower bound in at most log(last - first) + 1 comparisons - std::vector::iterator it = std::lower_bound(begin, end, val, imageComparer); - if (it != begin){ - return std::prev(it); - } - return end; - }; - - auto it = binary_find(_timeStamps.begin(), _timeStamps.end(), { currentTime, 0, "", "", false }, imageComparer); - //check [start, stop] - if (closedInterval && (it == _timeStamps.end() || it->stopTime < currentTime || it->projected)){ - return false; - }else if (!closedInterval && (it == _timeStamps.end() || it->projected)){ - return false; - } - - double upcoming = nextCaptureTime(currentTime); - if (_nextCapture != upcoming){ - _nextCapture = upcoming; - _intervalLength = upcoming - currentTime; - } - - it->projected = true; - path = it->path; - currentTime = it->startTime; - - return true; -} - -bool ImageSequencer::sequenceReset(){ - for (auto image : _timeStamps){ - image.projected = false; - } - return true; -} -*/ - -// ----------------- LOAD-DATA RELATED STUFF -------------------------------------- - -bool replace(std::string& str, const std::string& from, const std::string& to) { - size_t start_pos = str.find(from); - if (start_pos == std::string::npos) - return false; - str.replace(start_pos, from.length(), to); - return true; -} - -double ImageSequencer::getMissionElapsedTime(std::string timestr){ - std::string::size_type sz; // alias of size_t - double met = std::stod(timestr, &sz); - double diff; - double et; - //met ref time. - openspace::SpiceManager::ref().getETfromDate("2015-07-14T11:50:00.00", et); - - diff = std::abs(met - _metRef); - if (met > _metRef){ - et += diff; - } - else if (met < _metRef){ - et -= diff; - } - return et; -} - -bool ImageSequencer::parsePlaybookFile(const std::string& fileName, int& sequenceID, std::string year) { - setSequenceId(sequenceID); - std::vector tmp; - - if (size_t position = fileName.find_last_of(".") + 1){ - if (position != std::string::npos){ - std::string extension = ghoul::filesystem::File(fileName).fileExtension(); -#ifdef BACKUP_PLAYBOOK // Comma separated playbook in case we dont recieve updates from kang. - if (extension == "csv"){ // comma separated playbook - std::cout << "USING COMMA SEPARATED TIMELINE V9F" << std::endl; - - std::string cachedFile = ""; - FileSys.cacheManager()->getCachedFile(fileName, cachedFile, true); - - bool hasCachedFile = FileSys.fileExists(cachedFile); - if (hasCachedFile) { - std::ifstream file(cachedFile); - if (!file.good()) - LERROR("Error loading cached playbook '" << cachedFile << "'"); - else { - do { - std::string line; - std::getline(file, line); - - std::stringstream s(line); - - double start, end; - std::string path; - - s >> start; - s >> end; - - std::getline(s, path); - createImage(start, end, _defaultCaptureImage); - } while (!file.eof()); - } - } else { - std::ifstream file(fileName); - if (!file.good()) LERROR("Failed to open csv file '" << fileName << "'"); - - std::string timestr = ""; - double shutter = 0.01; - double et; - do{ - std::getline(file, timestr); - auto pos = timestr.find("LORRI image started"); - if (pos != std::string::npos){ - timestr = timestr.substr(timestr.find_first_of(",") + 1); - timestr = timestr.substr(0, timestr.find_first_of(",")); - - replace(timestr, " ", "::"); - timestr = year + " " + timestr; - - openspace::SpiceManager::ref().getETfromDate(timestr, et); - createImage(et, et + shutter, _defaultCaptureImage); - } - } while (!file.eof()); - - std::sort(_timeStamps.begin(), _timeStamps.end(), imageComparer); - - std::ofstream cachedFileStream(cachedFile); - cachedFileStream << std::setprecision(64); - if (cachedFileStream.good()) { - for (const ImageParams& i : _timeStamps) - cachedFileStream << i.startTime << "\t" << i.stopTime << "\t" << i.path << std::endl; - } - - } - } -#endif - if (extension == "txt"){// Hong Kang. pre-parsed playbook - LINFO("Using Preparsed Playbook V9H"); - std::ifstream file(fileName); - if (!file.good()) LERROR("Failed to open txt file '" << fileName << "'"); - - std::string timestr = ""; - double et; - - //@TODO: change similar to renderableFOV - std::string instruments[3] = { "LORRI", "RALPH_LEISA", "MVIC" }; - std::string id[3] = { "NH_LORRI", "NH_RALPH_LEISA", "MVIC" }; - int isize = sizeof(instruments) / sizeof(instruments[0]); - - do{ - std::getline(file, timestr); - for (int i = 0; i < isize; i++){ - auto pos = timestr.find(instruments[i]); - if (pos != std::string::npos){ - timestr = timestr.substr(24, 9); - et = getMissionElapsedTime(timestr); - createImage(tmp, et, id[i], "", _defaultCaptureImage); - std::sort(tmp.begin(), tmp.end(), imageComparer); - } - } - } while (!file.eof()); - } - - } - } - _timeStamps.push_back(tmp); - return true; -} - -bool ImageSequencer::loadSequence(const std::string& dir, int& sequenceID) { - setSequenceId(sequenceID); - std::vector tmp; - - ghoul::filesystem::Directory sequenceDir(dir, true); - std::vector sequencePaths = sequenceDir.read(true, false); // check inputs - for (auto path : sequencePaths){ - if (size_t position = path.find_last_of(".") + 1){ - if (position != std::string::npos){ - ghoul::filesystem::File currentFile(path); - std::string extension = currentFile.fileExtension(); - - if (extension == "lbl"){ // discovered header file - std::ifstream file(currentFile.path()); - - if (!file.good()) LERROR("Failed to open label file '" << currentFile.path() << "'"); - - // open up label files - std::string line = ""; - std::string specsOfInterest = "START_TIME"; // can be extended - double timestamp= 0.0; - bool found = false; - do { - std::getline(file, line); - auto pos = line.find(specsOfInterest); - if (pos != std::string::npos){ - std::string time = line.substr(line.find("=") + 2); - time.erase(std::remove(time.begin(), time.end(), ' '), time.end()); - openspace::SpiceManager::ref().getETfromDate(time, timestamp); - } - if (timestamp != 0.0){ - found = true; - std::string ext = "jpg"; - path.replace(path.begin() + position, path.end(), ext); - bool fileExists = FileSys.fileExists(path); - if (fileExists) { - createImage(tmp, timestamp, "NH_LORRI", "", path); /// fix active instrument! - std::sort(tmp.begin(), tmp.end(), imageComparer); - } - } - } while (!file.eof() && found == false); - } - } - } - } - _timeStamps.push_back(tmp); - return !_timeStamps.empty(); -} - -} // namespace openspace diff --git a/modules/newhorizons/util/imagesequencer.h b/modules/newhorizons/util/imagesequencer.h deleted file mode 100644 index 8dad22159c..0000000000 --- a/modules/newhorizons/util/imagesequencer.h +++ /dev/null @@ -1,140 +0,0 @@ -/***************************************************************************************** -* * -* OpenSpace * -* * -* Copyright (c) 2014 * -* * -* Permission is hereby granted, free of charge, to any person obtaining a copy of this * -* software and associated documentation files (the "Software"), to deal in the Software * -* without restriction, including without limitation the rights to use, copy, modify, * -* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * -* permit persons to whom the Software is furnished to do so, subject to the following * -* conditions: * -* * -* The above copyright notice and this permission notice shall be included in all copies * -* or substantial portions of the Software. * -* * -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * -* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * -* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * -* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * -* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * -* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * -****************************************************************************************/ - -#ifndef __IMAGESEQUENCER_H__ -#define __IMAGESEQUENCER_H__ - -// open space includes -#include -#include -#include -#include -#include - -namespace openspace { - -class ImageSequencer { -public: - ImageSequencer(); - /** - * Singelton instantiation - */ - static ImageSequencer& ref(); - static void initialize(); - static void deinitialize(); - - /** - * Updates current time and initializes the previous time member - */ - void update(double initTime); - - /** - * When the a projectable class loads its sequence it also has to request an ID - * which is set by reference and returned to the projectable. This ID is later - * used to access whatever data ImageSequencer loads. - */ - void setSequenceId(int& id); - - //bool sequenceReset(); - - /** - * Based on sequenceID and unique projectee name, the ImageSequencer determines which subset of data is to be filled to _imageTimes - * which can be used in a projecable class for projections. - */ - bool getImagePath(std::vector>& _imageTimes, int sequenceID, std::string projectee, bool withinFOV); - - /* - * Returns the time until next capture in seconds. - * - */ - double getNextCaptureTime(); - - /* - * Returns the time until next capture in seconds. - */ - double getIntervalLength(){ return _intervalLength; }; - - /* - * Returns next active instrument - */ - std::string& getActiveInstrument(){ return _activeInstrument; }; - - /* - * Performs search to find next consective instrument thats active - */ - std::string findActiveInstrument(double time, int sequenceID); - - /* - * Performs search to find next consecutive projection image. - */ - double nextCaptureTime(double _time, int sequenceID); - - /* - * Load (from *.fit converted) jpg image sequence based on corresponding *.lbl header files - */ - bool loadSequence(const std::string& dir, int& sequenceID); - /* - * Load sequence file of either *.csv type (excel) or preparsed *.txt type - */ - bool parsePlaybook(const std::string& dir, const std::string& type, std::string year = "2015"); - bool parsePlaybookFile(const std::string& fileName, int& sequenceID, std::string year = "2015"); - /* - * These three methods augment the playbook - */ - void augumentSequenceWithTargets(int sequenceID); - void addSequenceObserver(int sequenceID, std::string name, std::vector payload); - void registerTargets(std::vector& potential); - - - static ImageSequencer* _sequencer; - -protected: - - bool getMultipleImages(std::vector>& _imageTimes, int sequenceID, std::string projectee); - bool getSingleImage(std::vector>& _imageTimes, int sequenceID, std::string projectee); - -private: - - double getMissionElapsedTime(std::string timestr); - - std::map _projectableTargets; - std::map _observers; - std::map > _instruments; - - double _nextCapture; - double _intervalLength; - double _metRef = 299180517; - double _currentTime; - int _sequenceIDs; - - std::string _defaultCaptureImage; - std::string _activeInstrument; - - bool _targetsAdded; - -}; - -} // namespace openspace - -#endif // __IMAGESEQUENCER_H__ \ No newline at end of file From 8f88ead11327bde654cdd3a3fd2e1fff8723f303 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 1 Jul 2015 17:55:41 +0200 Subject: [PATCH 242/329] Remove crashing error from imagesequencer2 --- modules/newhorizons/util/imagesequencer2.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/newhorizons/util/imagesequencer2.cpp b/modules/newhorizons/util/imagesequencer2.cpp index 385efeaaec..31435afb82 100644 --- a/modules/newhorizons/util/imagesequencer2.cpp +++ b/modules/newhorizons/util/imagesequencer2.cpp @@ -374,9 +374,8 @@ void ImageSequencer2::runSequenceParser(SequenceParser* parser){ } _hasData = true; } - else{ - ghoul_assert(parserComplete, "one or more sequence loads failed, please check mod files. "); - } + else + LERROR("One of more sequence loads failed; please check mod files"); } } // namespace openspace From 40c42ff7720aba0cf0f748329d0d8ad1a6c0cd87 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 1 Jul 2015 17:56:51 +0200 Subject: [PATCH 243/329] Fix the scaling of renderablemodel by making the magnification a floatproperty --- data | 2 +- modules/base/rendering/modelgeometry.cpp | 12 +++- modules/base/rendering/modelgeometry.h | 70 ++++++++++--------- modules/base/rendering/renderablemodel.cpp | 2 + modules/base/rendering/wavefrontgeometry.cpp | 2 +- modules/base/rendering/wavefrontgeometry.h | 24 +++---- modules/base/shaders/model_vs.glsl | 12 ++-- .../newhorizons/shaders/modelShader_vs.glsl | 17 +++-- 8 files changed, 80 insertions(+), 61 deletions(-) diff --git a/data b/data index 3860dfdac4..fa00aaa0c2 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 3860dfdac47e63729ec6addd9e09c00d6382ad02 +Subproject commit fa00aaa0c244e8e5709877a344c3007d2244e9ec diff --git a/modules/base/rendering/modelgeometry.cpp b/modules/base/rendering/modelgeometry.cpp index e29e3adaf8..155b34e76a 100644 --- a/modules/base/rendering/modelgeometry.cpp +++ b/modules/base/rendering/modelgeometry.cpp @@ -66,6 +66,7 @@ ModelGeometry* ModelGeometry::createFromDictionary(const ghoul::Dictionary& dict ModelGeometry::ModelGeometry(const ghoul::Dictionary& dictionary) : _parent(nullptr) + , _magnification("magnification", "Magnification", 0.f, 0.f, 10.f) , _mode(GL_TRIANGLES) { setName("ModelGeometry"); @@ -74,9 +75,8 @@ ModelGeometry::ModelGeometry(const ghoul::Dictionary& dictionary) bool success = dictionary.getValue(keyName, name); ghoul_assert(success, "Name tag was not present"); - success = dictionary.getValue(keySize, _magnification); - if (!success) - _magnification = 0; // if not set, models will be 1:1 (earlier 1:1000) @AA + if (dictionary.hasKeyAndValue(keySize)) + _magnification = static_cast(dictionary.value(keySize)); success = dictionary.getValue(keyObjFile, _file); if (!success) { @@ -88,6 +88,8 @@ ModelGeometry::ModelGeometry(const ghoul::Dictionary& dictionary) if (!FileSys.fileExists(_file, true)) LERROR("Could not load OBJ file '" << _file << "': File not found"); + + addProperty(_magnification); } ModelGeometry::~ModelGeometry() { @@ -246,5 +248,9 @@ bool ModelGeometry::getIndices(std::vector* indexList) { return !(indexList->empty()); } +void ModelGeometry::setUniforms(ghoul::opengl::ProgramObject& program) { + program.setUniform("_magnification", _magnification); +} + } // namespace modelgeometry } // namespace openspace diff --git a/modules/base/rendering/modelgeometry.h b/modules/base/rendering/modelgeometry.h index c49b7b36f5..6293d5f43f 100644 --- a/modules/base/rendering/modelgeometry.h +++ b/modules/base/rendering/modelgeometry.h @@ -26,52 +26,56 @@ #define __MODELGEOMETRY_H__ #include + +#include #include #include namespace openspace { - namespace modelgeometry { +namespace modelgeometry { - class ModelGeometry : public properties::PropertyOwner { - public: - static ModelGeometry* createFromDictionary(const ghoul::Dictionary& dictionary); +class ModelGeometry : public properties::PropertyOwner { +public: + static ModelGeometry* createFromDictionary(const ghoul::Dictionary& dictionary); - struct Vertex { - GLfloat location[4]; - GLfloat tex[2]; - GLfloat normal[3]; - }; + struct Vertex { + GLfloat location[4]; + GLfloat tex[2]; + GLfloat normal[3]; + }; - ModelGeometry(const ghoul::Dictionary& dictionary); - virtual ~ModelGeometry(); - virtual bool initialize(Renderable* parent); - virtual void deinitialize(); - void render(); - virtual bool loadModel(const std::string& filename) = 0; - void changeRenderMode(const GLenum mode); - bool getVertices(std::vector* vertexList); - bool getIndices(std::vector* indexList); + ModelGeometry(const ghoul::Dictionary& dictionary); + virtual ~ModelGeometry(); + virtual bool initialize(Renderable* parent); + virtual void deinitialize(); + void render(); + virtual bool loadModel(const std::string& filename) = 0; + void changeRenderMode(const GLenum mode); + bool getVertices(std::vector* vertexList); + bool getIndices(std::vector* indexList); - protected: - Renderable* _parent; + virtual void setUniforms(ghoul::opengl::ProgramObject& program); - bool loadObj(const std::string& filename); - bool loadCachedFile(const std::string& filename); - bool saveCachedFile(const std::string& filename); - float _magnification; +protected: + Renderable* _parent; - GLuint _vaoID; - GLuint _vbo; - GLuint _ibo; - GLenum _mode; + bool loadObj(const std::string& filename); + bool loadCachedFile(const std::string& filename); + bool saveCachedFile(const std::string& filename); + properties::FloatProperty _magnification; - std::vector _vertices; - std::vector _indices; - std::string _file; - }; + GLuint _vaoID; + GLuint _vbo; + GLuint _ibo; + GLenum _mode; - } // namespace modelgeometry + std::vector _vertices; + std::vector _indices; + std::string _file; +}; + +} // namespace modelgeometry } // namespace openspace #endif // __MODELGEOMETRY_H__ diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index 8151fb54af..5c5c00aa6f 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -196,6 +196,8 @@ void RenderableModel::render(const RenderData& data) { _programObject->setUniform("_performShading", _performShading); + _geometry->setUniforms(*_programObject); + if (_performFade && _fading > 0.f){ _fading = _fading - 0.01f; } diff --git a/modules/base/rendering/wavefrontgeometry.cpp b/modules/base/rendering/wavefrontgeometry.cpp index 999e43bbbd..092a1bae10 100644 --- a/modules/base/rendering/wavefrontgeometry.cpp +++ b/modules/base/rendering/wavefrontgeometry.cpp @@ -97,7 +97,7 @@ bool WavefrontGeometry::loadModel(const std::string& filename) { _vertices[j + currentPosition].location[0] = tmp[0]; _vertices[j + currentPosition].location[1] = tmp[1]; _vertices[j + currentPosition].location[2] = tmp[2]; - _vertices[j + currentPosition].location[3] = tmp[3] + _magnification; + _vertices[j + currentPosition].location[3] = tmp[3]; _vertices[j + currentPosition].normal[0] = shapes[i].mesh.normals[3 * j + 0]; _vertices[j + currentPosition].normal[1] = shapes[i].mesh.normals[3 * j + 1]; diff --git a/modules/base/rendering/wavefrontgeometry.h b/modules/base/rendering/wavefrontgeometry.h index 07c01b9a73..d068c27a34 100644 --- a/modules/base/rendering/wavefrontgeometry.h +++ b/modules/base/rendering/wavefrontgeometry.h @@ -29,23 +29,23 @@ namespace openspace { - class RenderableModel; - class RenderableModelProjection; +class RenderableModel; +class RenderableModelProjection; - namespace modelgeometry { +namespace modelgeometry { - class WavefrontGeometry : public ModelGeometry { - public: - WavefrontGeometry(const ghoul::Dictionary& dictionary); + class WavefrontGeometry : public ModelGeometry { + public: + WavefrontGeometry(const ghoul::Dictionary& dictionary); - bool initialize(Renderable* parent) override; - void deinitialize() override; + bool initialize(Renderable* parent) override; + void deinitialize() override; - private: - bool loadModel(const std::string& filename); - }; + private: + bool loadModel(const std::string& filename); + }; - } // namespace modelgeometry +} // namespace modelgeometry } // namespace openspace #endif // __WAVEFRONTOBJECT_H__ diff --git a/modules/base/shaders/model_vs.glsl b/modules/base/shaders/model_vs.glsl index 13f3025471..338160f233 100644 --- a/modules/base/shaders/model_vs.glsl +++ b/modules/base/shaders/model_vs.glsl @@ -27,6 +27,8 @@ uniform mat4 ViewProjection; uniform mat4 ModelTransform; +uniform float _magnification; + layout(location = 0) in vec4 in_position; //in vec3 in_position; layout(location = 1) in vec2 in_st; @@ -39,13 +41,15 @@ out float s; #include "PowerScaling/powerScaling_vs.hglsl" -void main() -{ +void main() { + vec4 pos = in_position; + pos.w += _magnification; + // set variables vs_st = in_st; //vs_stp = in_position.xyz; - vs_position = in_position; - vec4 tmp = in_position; + vs_position = pos; + vec4 tmp = pos; // this is wrong for the normal. The normal transform is the transposed inverse of the model transform vs_normal = normalize(ModelTransform * vec4(in_normal,0)); diff --git a/modules/newhorizons/shaders/modelShader_vs.glsl b/modules/newhorizons/shaders/modelShader_vs.glsl index 26b63bd914..6ae3ac64ea 100644 --- a/modules/newhorizons/shaders/modelShader_vs.glsl +++ b/modules/newhorizons/shaders/modelShader_vs.glsl @@ -34,26 +34,29 @@ layout(location = 2) in vec3 in_normal; uniform vec3 boresight; +uniform float _magnification; + out vec2 vs_st; out vec4 vs_normal; out vec4 vs_position; out float s; - - out vec4 ProjTexCoord; + + #include "PowerScaling/powerScaling_vs.hglsl" -void main(){ +void main() { + vec4 pos = in_position; + pos.w += _magnification; vs_st = in_st; - vs_position = in_position; - vec4 tmp = in_position; - //tmp[3] += _magnification for runtime alteration of model size @AA + vs_position = pos; + vec4 tmp = pos; vs_normal = normalize(ModelTransform * vec4(in_normal,0)); vec4 position = pscTransform(tmp, ModelTransform); vs_position = tmp; - vec4 raw_pos = psc_to_meter(in_position, scaling); + vec4 raw_pos = psc_to_meter(pos, scaling); ProjTexCoord = ProjectorMatrix * ModelTransform * raw_pos; position = ViewProjection * position; gl_Position = z_normalization(position); From cc5e6074ad85fceff14657f0cc1024c594f5b49e Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 1 Jul 2015 22:03:52 +0200 Subject: [PATCH 244/329] Rename imagesequencer2 to imagesequencer --- ext/ghoul | 2 +- modules/newhorizons/CMakeLists.txt | 4 ++-- modules/newhorizons/newhorizonsmodule.cpp | 2 +- modules/newhorizons/rendering/renderablecrawlingline.cpp | 2 +- modules/newhorizons/rendering/renderablefov.cpp | 2 +- modules/newhorizons/rendering/renderablemodelprojection.h | 2 +- modules/newhorizons/rendering/renderableplaneprojection.h | 2 +- modules/newhorizons/rendering/renderableplanetprojection.h | 2 +- modules/newhorizons/util/hongkangparser.cpp | 2 +- modules/newhorizons/util/hongkangparser.h | 2 +- .../util/{imagesequencer2.cpp => imagesequencer.cpp} | 2 +- .../newhorizons/util/{imagesequencer2.h => imagesequencer.h} | 0 modules/newhorizons/util/labelparser.h | 2 +- src/rendering/renderengine.cpp | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) rename modules/newhorizons/util/{imagesequencer2.cpp => imagesequencer.cpp} (99%) rename modules/newhorizons/util/{imagesequencer2.h => imagesequencer.h} (100%) diff --git a/ext/ghoul b/ext/ghoul index a5111530a3..5a6d99432b 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit a5111530a3e211a45db3f2f8de1ae6c34caeecbf +Subproject commit 5a6d99432b394ea98e9c5d50c8a99c990c504f8c diff --git a/modules/newhorizons/CMakeLists.txt b/modules/newhorizons/CMakeLists.txt index 5ee0503ec1..31177d614b 100644 --- a/modules/newhorizons/CMakeLists.txt +++ b/modules/newhorizons/CMakeLists.txt @@ -36,7 +36,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/writeToTexture.h ${CMAKE_CURRENT_SOURCE_DIR}/util/decoder.h ${CMAKE_CURRENT_SOURCE_DIR}/util/hongkangparser.h - ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer2.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer.h ${CMAKE_CURRENT_SOURCE_DIR}/util/instrumentdecoder.h ${CMAKE_CURRENT_SOURCE_DIR}/util/labelparser.h ${CMAKE_CURRENT_SOURCE_DIR}/util/scannerdecoder.h @@ -56,7 +56,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablemodelprojection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/decoder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/hongkangparser.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer2.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/instrumentdecoder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/labelparser.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/scannerdecoder.cpp diff --git a/modules/newhorizons/newhorizonsmodule.cpp b/modules/newhorizons/newhorizonsmodule.cpp index a822f91991..caf960247f 100644 --- a/modules/newhorizons/newhorizonsmodule.cpp +++ b/modules/newhorizons/newhorizonsmodule.cpp @@ -42,7 +42,7 @@ #include #include -#include +#include namespace openspace { diff --git a/modules/newhorizons/rendering/renderablecrawlingline.cpp b/modules/newhorizons/rendering/renderablecrawlingline.cpp index 8169ce1b33..cfe9d7d6a2 100644 --- a/modules/newhorizons/rendering/renderablecrawlingline.cpp +++ b/modules/newhorizons/rendering/renderablecrawlingline.cpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include //#include namespace { diff --git a/modules/newhorizons/rendering/renderablefov.cpp b/modules/newhorizons/rendering/renderablefov.cpp index bdb160449d..fca2aba4f2 100644 --- a/modules/newhorizons/rendering/renderablefov.cpp +++ b/modules/newhorizons/rendering/renderablefov.cpp @@ -29,7 +29,7 @@ #include #include -#include // testing +#include #include diff --git a/modules/newhorizons/rendering/renderablemodelprojection.h b/modules/newhorizons/rendering/renderablemodelprojection.h index 51c4514722..4508094e30 100644 --- a/modules/newhorizons/rendering/renderablemodelprojection.h +++ b/modules/newhorizons/rendering/renderablemodelprojection.h @@ -27,7 +27,7 @@ #include -#include +#include #include #include diff --git a/modules/newhorizons/rendering/renderableplaneprojection.h b/modules/newhorizons/rendering/renderableplaneprojection.h index 027a48940f..5fc819ea1b 100644 --- a/modules/newhorizons/rendering/renderableplaneprojection.h +++ b/modules/newhorizons/rendering/renderableplaneprojection.h @@ -28,7 +28,7 @@ #include #include -#include +#include #include #include diff --git a/modules/newhorizons/rendering/renderableplanetprojection.h b/modules/newhorizons/rendering/renderableplanetprojection.h index de9a15d541..09e1eb7177 100644 --- a/modules/newhorizons/rendering/renderableplanetprojection.h +++ b/modules/newhorizons/rendering/renderableplanetprojection.h @@ -29,7 +29,7 @@ // open space includes #include -#include +#include #include #include diff --git a/modules/newhorizons/util/hongkangparser.cpp b/modules/newhorizons/util/hongkangparser.cpp index abe05e953f..7f898f82c3 100644 --- a/modules/newhorizons/util/hongkangparser.cpp +++ b/modules/newhorizons/util/hongkangparser.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include #include diff --git a/modules/newhorizons/util/hongkangparser.h b/modules/newhorizons/util/hongkangparser.h index 59cf7faf17..9d827a03de 100644 --- a/modules/newhorizons/util/hongkangparser.h +++ b/modules/newhorizons/util/hongkangparser.h @@ -25,7 +25,7 @@ #ifndef __HONGKANGPARSER_H__ #define __HONGKANGPARSER_H__ -#include +#include #include #include diff --git a/modules/newhorizons/util/imagesequencer2.cpp b/modules/newhorizons/util/imagesequencer.cpp similarity index 99% rename from modules/newhorizons/util/imagesequencer2.cpp rename to modules/newhorizons/util/imagesequencer.cpp index 31435afb82..2bb8191511 100644 --- a/modules/newhorizons/util/imagesequencer2.cpp +++ b/modules/newhorizons/util/imagesequencer.cpp @@ -23,7 +23,7 @@ ****************************************************************************************/ // open space includes -#include +#include #include #include #include diff --git a/modules/newhorizons/util/imagesequencer2.h b/modules/newhorizons/util/imagesequencer.h similarity index 100% rename from modules/newhorizons/util/imagesequencer2.h rename to modules/newhorizons/util/imagesequencer.h diff --git a/modules/newhorizons/util/labelparser.h b/modules/newhorizons/util/labelparser.h index 85cb67d846..d4f7ada971 100644 --- a/modules/newhorizons/util/labelparser.h +++ b/modules/newhorizons/util/labelparser.h @@ -25,7 +25,7 @@ #ifndef __LABELPARSER_H__ #define __LABELPARSER_H__ -#include +#include #include #include diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 1bc3af02ac..10fe760fc8 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -25,7 +25,7 @@ #include #ifdef OPENSPACE_MODULE_NEWHORIZONS_ENABLED -#include +#include #endif #include From bae990babbe0fc55410f83a8b39d829155455450 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 1 Jul 2015 23:40:38 +0200 Subject: [PATCH 245/329] Some work on making the TimelineGui work with the new hybrid parsers --- apps/TimelineView/mainwindow.cpp | 22 +- apps/TimelineView/mainwindow.h | 3 +- include/openspace/network/networkengine.h | 4 +- .../rendering/renderablemodelprojection.cpp | 824 +++++++++--------- .../rendering/renderableplanetprojection.cpp | 20 +- modules/newhorizons/util/hongkangparser.cpp | 8 +- modules/newhorizons/util/hongkangparser.h | 3 +- modules/newhorizons/util/labelparser.cpp | 7 +- modules/newhorizons/util/labelparser.h | 4 +- src/network/networkengine.cpp | 52 +- 10 files changed, 489 insertions(+), 458 deletions(-) diff --git a/apps/TimelineView/mainwindow.cpp b/apps/TimelineView/mainwindow.cpp index 87c030c662..9a59501fd7 100644 --- a/apps/TimelineView/mainwindow.cpp +++ b/apps/TimelineView/mainwindow.cpp @@ -130,6 +130,7 @@ void MainWindow::readTcpData() { static const uint16_t MessageTypeStatus = 0; static const uint16_t MessageTypePlayBookHongKang = 2; static const uint16_t MessageTypePlayBookLabel = 3; + static const uint16_t MessageTypeInitialMessageFinished = 4; QByteArray data = _socket->readAll(); @@ -161,7 +162,7 @@ void MainWindow::readTcpData() { switch (messageType.value) { case MessageTypeStatus: { - if (_hasHongKangTimeline && _hasLabelTimeline) + if (_isConnected) handleStatusMessage(data.mid(2)); break; } @@ -186,17 +187,22 @@ void MainWindow::readTcpData() { //qDebug() << "Finished handling playbook"; - if (messageType.value == MessageTypePlayBookHongKang) - _hasHongKangTimeline = true; - if (messageType.value == MessageTypePlayBookLabel) - _hasLabelTimeline = true; + //if (messageType.value == MessageTypePlayBookHongKang) + // _hasHongKangTimeline = true; + //if (messageType.value == MessageTypePlayBookLabel) + // _hasLabelTimeline = true; - if (_hasHongKangTimeline && _hasLabelTimeline) { - fullyConnected(); - } + //if (_hasHongKangTimeline && _hasLabelTimeline) { + // fullyConnected(); + //} break; } + case MessageTypeInitialMessageFinished: + _isConnected = true; + fullyConnected(); + break; + default: qDebug() << QString(data); } diff --git a/apps/TimelineView/mainwindow.h b/apps/TimelineView/mainwindow.h index c998082e39..a8d9b9efbf 100644 --- a/apps/TimelineView/mainwindow.h +++ b/apps/TimelineView/mainwindow.h @@ -67,8 +67,7 @@ private: QTcpSocket* _socket; - bool _hasHongKangTimeline = false; - bool _hasLabelTimeline = false; + bool _isConnected = false; }; #endif // __MAINWINDOW_H__ diff --git a/include/openspace/network/networkengine.h b/include/openspace/network/networkengine.h index 4df8b2158d..1ca77d9210 100644 --- a/include/openspace/network/networkengine.h +++ b/include/openspace/network/networkengine.h @@ -53,8 +53,9 @@ public: // Background MessageIdentifier identifier(std::string name); + private: - std::map _identifiers; + std::map _identifiers; MessageIdentifier _lastAssignedIdentifier; struct Message { @@ -69,6 +70,7 @@ private: MessageIdentifier _statusMessageIdentifier; MessageIdentifier _identifierMappingIdentifier; + MessageIdentifier _initialMessageFinishedIdentifier; }; } // namespace openspace diff --git a/modules/newhorizons/rendering/renderablemodelprojection.cpp b/modules/newhorizons/rendering/renderablemodelprojection.cpp index af0244403c..573cff0d7e 100644 --- a/modules/newhorizons/rendering/renderablemodelprojection.cpp +++ b/modules/newhorizons/rendering/renderablemodelprojection.cpp @@ -71,461 +71,461 @@ namespace { namespace openspace { - RenderableModelProjection::RenderableModelProjection(const ghoul::Dictionary& dictionary) - : Renderable(dictionary) - , _colorTexturePath("colorTexture", "Color Texture") - , _projectionTexturePath("projectionTexture", "RGB Texture") - , _rotationX("rotationX", "RotationX", 0, 0, 360) - , _rotationY("rotationY", "RotationY", 0, 0, 360) - , _rotationZ("rotationZ", "RotationZ", 0, 0, 360) - , _programObject(nullptr) - , _fboProgramObject(nullptr) - , _texture(nullptr) - , _geometry(nullptr) - , _textureOriginal(nullptr) - , _textureProj(nullptr) - , _textureWhiteSquare(nullptr) - , _alpha(1.f) - , _performShading("performShading", "Perform Shading", true) - , _performProjection("performProjection", "Perform Projections", true) - , _frameCount(0) - , _programIsDirty(false) - { - std::string name; - bool success = dictionary.getValue(constants::scenegraphnode::keyName, name); - ghoul_assert(success, "Name was not passed to RenderableModelProjection"); +RenderableModelProjection::RenderableModelProjection(const ghoul::Dictionary& dictionary) + : Renderable(dictionary) + , _colorTexturePath("colorTexture", "Color Texture") + , _projectionTexturePath("projectionTexture", "RGB Texture") + , _rotationX("rotationX", "RotationX", 0, 0, 360) + , _rotationY("rotationY", "RotationY", 0, 0, 360) + , _rotationZ("rotationZ", "RotationZ", 0, 0, 360) + , _programObject(nullptr) + , _fboProgramObject(nullptr) + , _texture(nullptr) + , _geometry(nullptr) + , _textureOriginal(nullptr) + , _textureProj(nullptr) + , _textureWhiteSquare(nullptr) + , _alpha(1.f) + , _performShading("performShading", "Perform Shading", true) + , _performProjection("performProjection", "Perform Projections", true) + , _frameCount(0) + , _programIsDirty(false) +{ + std::string name; + bool success = dictionary.getValue(constants::scenegraphnode::keyName, name); + ghoul_assert(success, "Name was not passed to RenderableModelProjection"); - ghoul::Dictionary geometryDictionary; - success = dictionary.getValue(keyGeometry, geometryDictionary); - if (success) { - geometryDictionary.setValue(constants::scenegraphnode::keyName, name); - _geometry = modelgeometry::ModelGeometry::createFromDictionary(geometryDictionary); - } + ghoul::Dictionary geometryDictionary; + success = dictionary.getValue(keyGeometry, geometryDictionary); + if (success) { + geometryDictionary.setValue(constants::scenegraphnode::keyName, name); + _geometry = modelgeometry::ModelGeometry::createFromDictionary(geometryDictionary); + } - std::string texturePath = ""; - success = dictionary.getValue(keyTextureColor, texturePath); - if (success) - _colorTexturePath = absPath(texturePath); + std::string texturePath = ""; + success = dictionary.getValue(keyTextureColor, texturePath); + if (success) + _colorTexturePath = absPath(texturePath); - success = dictionary.getValue(keyTextureProject, texturePath); - if (success) - _projectionTexturePath = absPath(texturePath); + success = dictionary.getValue(keyTextureProject, texturePath); + if (success) + _projectionTexturePath = absPath(texturePath); - success = dictionary.getValue(keyTextureDefault, texturePath); - if (success) - _defaultProjImage = absPath(texturePath); + success = dictionary.getValue(keyTextureDefault, texturePath); + if (success) + _defaultProjImage = absPath(texturePath); - addPropertySubOwner(_geometry); + addPropertySubOwner(_geometry); - addProperty(_colorTexturePath); - addProperty(_projectionTexturePath); - _colorTexturePath.onChange(std::bind(&RenderableModelProjection::loadTexture, this)); - _projectionTexturePath.onChange(std::bind(&RenderableModelProjection::loadProjectionTexture, this)); + addProperty(_colorTexturePath); + addProperty(_projectionTexturePath); + _colorTexturePath.onChange(std::bind(&RenderableModelProjection::loadTexture, this)); + _projectionTexturePath.onChange(std::bind(&RenderableModelProjection::loadProjectionTexture, this)); - dictionary.getValue(keySource, _source); - dictionary.getValue(keyDestination, _destination); - dictionary.getValue(keyBody, _target); - if (_target != "") - setBody(_target); + dictionary.getValue(keySource, _source); + dictionary.getValue(keyDestination, _destination); + dictionary.getValue(keyBody, _target); + if (_target != "") + setBody(_target); - bool completeSuccess = true; - completeSuccess &= dictionary.getValue(keyInstrument, _instrumentID); - completeSuccess &= dictionary.getValue(keyProjObserver, _projectorID); - completeSuccess &= dictionary.getValue(keyProjTarget, _projecteeID); - completeSuccess &= dictionary.getValue(keyInstrumentFovy, _fovy); - completeSuccess &= dictionary.getValue(keyInstrumentAspect, _aspectRatio); - completeSuccess &= dictionary.getValue(keyInstrumentNear, _nearPlane); - completeSuccess &= dictionary.getValue(keyInstrumentFar, _farPlane); - ghoul_assert(completeSuccess, "All neccessary attributes not found in modfile"); + bool completeSuccess = true; + completeSuccess &= dictionary.getValue(keyInstrument, _instrumentID); + completeSuccess &= dictionary.getValue(keyProjObserver, _projectorID); + completeSuccess &= dictionary.getValue(keyProjTarget, _projecteeID); + completeSuccess &= dictionary.getValue(keyInstrumentFovy, _fovy); + completeSuccess &= dictionary.getValue(keyInstrumentAspect, _aspectRatio); + completeSuccess &= dictionary.getValue(keyInstrumentNear, _nearPlane); + completeSuccess &= dictionary.getValue(keyInstrumentFar, _farPlane); + ghoul_assert(completeSuccess, "All neccessary attributes not found in modfile"); - completeSuccess = dictionary.getValue(keyProjAberration, _aberration); - if (!completeSuccess) - _aberration = "NONE"; + completeSuccess = dictionary.getValue(keyProjAberration, _aberration); + if (!completeSuccess) + _aberration = "NONE"; - openspace::SpiceManager::ref().addFrame(_target, _source); - setBoundingSphere(pss(1.f, 9.f)); + openspace::SpiceManager::ref().addFrame(_target, _source); + setBoundingSphere(pss(1.f, 9.f)); - addProperty(_performShading); - addProperty(_performProjection); - addProperty(_rotationX); - addProperty(_rotationY); - addProperty(_rotationZ); + addProperty(_performShading); + addProperty(_performProjection); + addProperty(_rotationX); + addProperty(_rotationY); + addProperty(_rotationZ); - SequenceParser* parser; + SequenceParser* parser; - bool foundSequence = dictionary.getValue(keySequenceDir, _sequenceSource); - if (foundSequence) { - _sequenceSource = absPath(_sequenceSource); + bool foundSequence = dictionary.getValue(keySequenceDir, _sequenceSource); + if (foundSequence) { + _sequenceSource = absPath(_sequenceSource); - foundSequence = dictionary.getValue(keySequenceType, _sequenceType); - //Important: client must define translation-list in mod file IFF playbook - if (dictionary.hasKey(keyTranslation)) { - ghoul::Dictionary translationDictionary; - //get translation dictionary - dictionary.getValue(keyTranslation, translationDictionary); - if (_sequenceType == sequenceTypeImage) { - parser = new LabelParser(_sequenceSource, translationDictionary); - openspace::ImageSequencer2::ref().runSequenceParser(parser); + foundSequence = dictionary.getValue(keySequenceType, _sequenceType); + //Important: client must define translation-list in mod file IFF playbook + if (dictionary.hasKey(keyTranslation)) { + ghoul::Dictionary translationDictionary; + //get translation dictionary + dictionary.getValue(keyTranslation, translationDictionary); + if (_sequenceType == sequenceTypeImage) { + parser = new LabelParser(name, _sequenceSource, translationDictionary); + openspace::ImageSequencer2::ref().runSequenceParser(parser); - } - } - else { - LWARNING("No translation provided, please make sure all spice calls match playbook!"); } } - + else { + LWARNING("No translation provided, please make sure all spice calls match playbook!"); + } } - bool RenderableModelProjection::isReady() const { - bool ready = true; - ready &= (_programObject != nullptr); - ready &= (_texture != nullptr); - return ready; - } +} - bool RenderableModelProjection::initialize() { - bool completeSuccess = true; +bool RenderableModelProjection::isReady() const { + bool ready = true; + ready &= (_programObject != nullptr); + ready &= (_texture != nullptr); + return ready; +} + +bool RenderableModelProjection::initialize() { + bool completeSuccess = true; - if (_programObject == nullptr) { - _programObject = ghoul::opengl::ProgramObject::Build("ModelShader", - "${MODULES}/newhorizons/shaders/modelShader_vs.glsl", - "${MODULES}/newhorizons/shaders/modelShader_fs.glsl"); - if (!_programObject) - return false; - } - _programObject->setProgramObjectCallback([&](ghoul::opengl::ProgramObject*) { this->_programIsDirty = true; } ); + if (_programObject == nullptr) { + _programObject = ghoul::opengl::ProgramObject::Build("ModelShader", + "${MODULES}/newhorizons/shaders/modelShader_vs.glsl", + "${MODULES}/newhorizons/shaders/modelShader_fs.glsl"); + if (!_programObject) + return false; + } + _programObject->setProgramObjectCallback([&](ghoul::opengl::ProgramObject*) { this->_programIsDirty = true; } ); - if (_fboProgramObject == nullptr) { - _fboProgramObject = ghoul::opengl::ProgramObject::Build("ProjectionPass", - "${MODULES}/newhorizons/shaders/projectionPass_vs.glsl", - "${MODULES}/newhorizons/shaders/projectionPass_fs.glsl"); - if (!_fboProgramObject) - return false; - } - _fboProgramObject->setProgramObjectCallback([&](ghoul::opengl::ProgramObject*) { this->_programIsDirty = true; } ); + if (_fboProgramObject == nullptr) { + _fboProgramObject = ghoul::opengl::ProgramObject::Build("ProjectionPass", + "${MODULES}/newhorizons/shaders/projectionPass_vs.glsl", + "${MODULES}/newhorizons/shaders/projectionPass_fs.glsl"); + if (!_fboProgramObject) + return false; + } + _fboProgramObject->setProgramObjectCallback([&](ghoul::opengl::ProgramObject*) { this->_programIsDirty = true; } ); - loadTexture(); - loadProjectionTexture(); + loadTexture(); + loadProjectionTexture(); - completeSuccess &= (_texture != nullptr); - completeSuccess &= (_textureOriginal != nullptr); - completeSuccess &= (_textureProj != nullptr); - completeSuccess &= (_textureWhiteSquare != nullptr); + completeSuccess &= (_texture != nullptr); + completeSuccess &= (_textureOriginal != nullptr); + completeSuccess &= (_textureProj != nullptr); + completeSuccess &= (_textureWhiteSquare != nullptr); - completeSuccess &= _geometry->initialize(this); - completeSuccess &= !_source.empty(); - completeSuccess &= !_destination.empty(); + completeSuccess &= _geometry->initialize(this); + completeSuccess &= !_source.empty(); + completeSuccess &= !_destination.empty(); - bool gotverts = _geometry->getVertices(&_geometryVertecies) && _geometry->getIndices(&_geometryIndeces); - if (!gotverts) - LWARNING("Lack of vertex data from geometry for image projection"); + bool gotverts = _geometry->getVertices(&_geometryVertecies) && _geometry->getIndices(&_geometryIndeces); + if (!gotverts) + LWARNING("Lack of vertex data from geometry for image projection"); - completeSuccess &= auxiliaryRendertarget(); + completeSuccess &= auxiliaryRendertarget(); - return completeSuccess; - } + return completeSuccess; +} - bool RenderableModelProjection::auxiliaryRendertarget() { - bool completeSuccess = true; - // set FBO to texture to project to - glGenFramebuffers(1, &_fboID); - glBindFramebuffer(GL_FRAMEBUFFER, _fboID); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *_texture, 0); - // check FBO status - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) - completeSuccess &= false; - // switch back to window-system-provided framebuffer - glBindFramebuffer(GL_FRAMEBUFFER, 0); +bool RenderableModelProjection::auxiliaryRendertarget() { + bool completeSuccess = true; + // set FBO to texture to project to + glGenFramebuffers(1, &_fboID); + glBindFramebuffer(GL_FRAMEBUFFER, _fboID); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *_texture, 0); + // check FBO status + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) + completeSuccess &= false; + // switch back to window-system-provided framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, 0); - int vertexSize = sizeof(modelgeometry::ModelGeometry::Vertex); + int vertexSize = sizeof(modelgeometry::ModelGeometry::Vertex); - glGenVertexArrays(1, &_vaoID); - glGenBuffers(1, &_vbo); - glGenBuffers(1, &_ibo); + glGenVertexArrays(1, &_vaoID); + glGenBuffers(1, &_vbo); + glGenBuffers(1, &_ibo); - glBindVertexArray(_vaoID); - glBindBuffer(GL_ARRAY_BUFFER, _vbo); - glBufferData(GL_ARRAY_BUFFER, _geometryVertecies.size() * vertexSize, &_geometryVertecies[0], GL_STATIC_DRAW); + glBindVertexArray(_vaoID); + glBindBuffer(GL_ARRAY_BUFFER, _vbo); + glBufferData(GL_ARRAY_BUFFER, _geometryVertecies.size() * vertexSize, &_geometryVertecies[0], GL_STATIC_DRAW); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glEnableVertexAttribArray(2); - glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, vertexSize, - reinterpret_cast(offsetof(modelgeometry::ModelGeometry::Vertex, location))); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, vertexSize, - reinterpret_cast(offsetof(modelgeometry::ModelGeometry::Vertex, tex))); - glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, vertexSize, - reinterpret_cast(offsetof(modelgeometry::ModelGeometry::Vertex, normal))); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, vertexSize, + reinterpret_cast(offsetof(modelgeometry::ModelGeometry::Vertex, location))); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, vertexSize, + reinterpret_cast(offsetof(modelgeometry::ModelGeometry::Vertex, tex))); + glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, vertexSize, + reinterpret_cast(offsetof(modelgeometry::ModelGeometry::Vertex, normal))); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ibo); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, _geometryIndeces.size() * sizeof(int), &_geometryIndeces[0], GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ibo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, _geometryIndeces.size() * sizeof(int), &_geometryIndeces[0], GL_STATIC_DRAW); - glBindVertexArray(0); + glBindVertexArray(0); - return completeSuccess; + return completeSuccess; +} + +bool RenderableModelProjection::deinitialize() { + if (_geometry) { + _geometry->deinitialize(); + delete _geometry; } - bool RenderableModelProjection::deinitialize() { - if (_geometry) { - _geometry->deinitialize(); - delete _geometry; - } - - if (_texture) - delete _texture; - if (_textureProj) - delete _textureProj; - if (_textureOriginal) - delete _textureOriginal; - if (_textureWhiteSquare) - delete _textureWhiteSquare; - - _geometry = nullptr; - _texture = nullptr; - _textureProj = nullptr; - _textureOriginal = nullptr; - _textureWhiteSquare = nullptr; - - glDeleteBuffers(1, &_vbo); - - return true; - } - - void RenderableModelProjection::render(const RenderData& data) { - if (!_programObject) return; - if (!_textureProj) return; - _programObject->activate(); - _frameCount++; - - _camScaling = data.camera.scaling(); - _up = data.camera.lookUpVector(); - - if (_capture && _performProjection) - project(); - - attitudeParameters(_time); - _imageTimes.clear(); - - double time = openspace::Time::ref().currentTime(); - bool targetPositionCoverage = openspace::SpiceManager::ref().hasSpkCoverage(_target, time); - if (!targetPositionCoverage) { - int frame = _frameCount % 180; - - float fadingFactor = static_cast(sin((frame * M_PI) / 180)); - _alpha = 0.5f + fadingFactor * 0.5f; - } - else - _alpha = 1.0f; - - _programObject->setUniform("ProjectorMatrix", _projectorMatrix); - _programObject->setUniform("boresight", _boresight); - _programObject->setUniform("_performShading", _performShading); - _programObject->setUniform("sun_pos", _sunPosition.vec3()); - _viewProjection = data.camera.viewProjectionMatrix(); - _programObject->setUniform("ViewProjection", _viewProjection); - _programObject->setUniform("ModelTransform", _transform); - setPscUniforms(_programObject, &data.camera, data.position); - - textureBind(); - _geometry->render(); - - // disable shader - _programObject->deactivate(); - } - - void RenderableModelProjection::update(const UpdateData& data) { - if (_programIsDirty) { - _programObject->rebuildFromFile(); - _fboProgramObject->rebuildFromFile(); - _programIsDirty = false; - } - - _time = data.time; - - if (openspace::ImageSequencer2::ref().isReady() && _performProjection) { - openspace::ImageSequencer2::ref().updateSequencer(_time); - _capture = openspace::ImageSequencer2::ref().getImagePaths(_imageTimes, _projecteeID, _instrumentID); - } - - // set spice-orientation in accordance to timestamp - if (!_source.empty()) - openspace::SpiceManager::ref().getPositionTransformMatrix(_source, _destination, _time, _stateMatrix); - - double lt; - openspace::SpiceManager::ref().getTargetPosition("SUN", _target, "GALACTIC", "NONE", _time, _sunPosition, lt); - } - - void RenderableModelProjection::imageProjectGPU() { - - // keep handle to the current bound FBO - GLint defaultFBO; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); - - GLint m_viewport[4]; - glGetIntegerv(GL_VIEWPORT, m_viewport); - glBindFramebuffer(GL_FRAMEBUFFER, _fboID); - // set blend eq - glEnable(GL_BLEND); - glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ZERO); - - glViewport(0, 0, static_cast(_texture->width()), static_cast(_texture->height())); - _fboProgramObject->activate(); - - ghoul::opengl::TextureUnit unitFboProject; - unitFboProject.activate(); - _textureProj->bind(); - _fboProgramObject->setUniform("projectTexture", unitFboProject); - - ghoul::opengl::TextureUnit unitFboCurrent; - unitFboCurrent.activate(); - _texture->bind(); - _fboProgramObject->setUniform("currentTexture", unitFboCurrent); - _fboProgramObject->setUniform("ProjectorMatrix", _projectorMatrix); - _fboProgramObject->setUniform("ModelTransform", _transform); - _fboProgramObject->setUniform("_scaling", _camScaling); - _fboProgramObject->setUniform("boresight", _boresight); - - glBindVertexArray(_vaoID); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ibo); - glDrawElements(GL_TRIANGLES, static_cast(_geometryIndeces.size()), GL_UNSIGNED_INT, 0); - glBindVertexArray(0); - - _fboProgramObject->deactivate(); - glDisable(GL_BLEND); - //bind back to default - glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); - glViewport(m_viewport[0], m_viewport[1], - m_viewport[2], m_viewport[3]); - - } - - void RenderableModelProjection::attitudeParameters(double time) { - openspace::SpiceManager::ref().getPositionTransformMatrix(_source, _destination, time, _stateMatrix); - openspace::SpiceManager::ref().getPositionTransformMatrix(_instrumentID, _destination, time, _instrumentMatrix); - - _transform = glm::mat4(1); - - glm::mat4 rotPropX = glm::rotate(_transform, static_cast(_rotationX), glm::vec3(1, 0, 0)); - glm::mat4 rotPropY = glm::rotate(_transform, static_cast(_rotationY), glm::vec3(0, 1, 0)); - glm::mat4 rotPropZ = glm::rotate(_transform, static_cast(_rotationZ), glm::vec3(0, 0, 1)); - - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - _transform[i][j] = static_cast(_stateMatrix[i][j]); - } - } - _transform = _transform * rotPropX * rotPropY * rotPropZ; - - std::string shape, instrument; - std::vector bounds; - glm::dvec3 boresight; - bool found = openspace::SpiceManager::ref().getFieldOfView(_instrumentID, shape, instrument, boresight, bounds); - if (!found) - return; - - double lightTime; - psc position; //observer target - found = SpiceManager::ref().getTargetPosition(_projectorID, _projecteeID, _destination, _aberration, time, position, lightTime); - - position[3] += (3 + _camScaling[1]); - glm::vec3 cpos = position.vec3(); - - _projectorMatrix = computeProjectorMatrix(cpos, boresight, _up); - } - - glm::mat4 RenderableModelProjection::computeProjectorMatrix(const glm::vec3 loc, glm::dvec3 aim, const glm::vec3 up) { - //rotate boresight into correct alignment - _boresight = _instrumentMatrix*aim; - glm::vec3 uptmp(_instrumentMatrix*glm::dvec3(up)); - - // create view matrix - glm::vec3 e3 = glm::normalize(_boresight); - glm::vec3 e1 = glm::normalize(glm::cross(uptmp, e3)); - glm::vec3 e2 = glm::normalize(glm::cross(e3, e1)); - glm::mat4 projViewMatrix = glm::mat4(e1.x, e2.x, e3.x, 0.f, - e1.y, e2.y, e3.y, 0.f, - e1.z, e2.z, e3.z, 0.f, - -glm::dot(e1, loc), -glm::dot(e2, loc), -glm::dot(e3, loc), 1.f); - - // create perspective projection matrix - glm::mat4 projProjectionMatrix = glm::perspective(_fovy, _aspectRatio, _nearPlane, _farPlane); - // bias matrix - glm::mat4 projNormalizationMatrix = glm::mat4(0.5f, 0, 0, 0, - 0, 0.5f, 0, 0, - 0, 0, 0.5f, 0, - 0.5f, 0.5f, 0.5f, 1); - return projNormalizationMatrix*projProjectionMatrix*projViewMatrix; - } - - - void RenderableModelProjection::textureBind() { - ghoul::opengl::TextureUnit unit[2]; - unit[0].activate(); - _texture->bind(); - _programObject->setUniform("currentTexture", unit[0]); - unit[1].activate(); - _textureWhiteSquare->bind(); - _programObject->setUniform("projectedTexture", unit[1]); - } - - void RenderableModelProjection::project() { - for (auto img : _imageTimes) { - std::thread t1(&RenderableModelProjection::attitudeParameters, this, img.startTime); - t1.join(); - _projectionTexturePath = img.path; - imageProjectGPU(); //fbopass - } - _capture = false; - } - - void RenderableModelProjection::loadTexture() { + if (_texture) delete _texture; - _texture = nullptr; - if (_colorTexturePath.value() != "") { - _texture = ghoul::io::TextureReader::ref().loadTexture(absPath(_colorTexturePath)); - if (_texture) { - LDEBUG("Loaded texture from '" << absPath(_colorTexturePath) << "'"); - _texture->uploadTexture(); - _texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); - } - } - delete _textureOriginal; - _textureOriginal = nullptr; - if (_colorTexturePath.value() != "") { - _textureOriginal = ghoul::io::TextureReader::ref().loadTexture(absPath(_colorTexturePath)); - if (_textureOriginal) { - LDEBUG("Loaded texture from '" << absPath(_colorTexturePath) << "'"); - _textureOriginal->uploadTexture(); - _textureOriginal->setFilter(ghoul::opengl::Texture::FilterMode::Linear); - } - } - delete _textureWhiteSquare; - _textureWhiteSquare = nullptr; - if (_defaultProjImage != "") { - _textureWhiteSquare = ghoul::io::TextureReader::ref().loadTexture(absPath(_defaultProjImage)); - if (_textureWhiteSquare) { - _textureWhiteSquare->uploadTexture(); - _textureWhiteSquare->setFilter(ghoul::opengl::Texture::FilterMode::Linear); - } - } - } - - void RenderableModelProjection::loadProjectionTexture() { + if (_textureProj) delete _textureProj; - _textureProj = nullptr; - if (_projectionTexturePath.value() != "") { - _textureProj = ghoul::io::TextureReader::ref().loadTexture(absPath(_projectionTexturePath)); - if (_textureProj) { - _textureProj->uploadTexture(); - _textureProj->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); - _textureProj->setWrapping(ghoul::opengl::Texture::WrappingMode::ClampToBorder); - } - } + if (_textureOriginal) + delete _textureOriginal; + if (_textureWhiteSquare) + delete _textureWhiteSquare; + _geometry = nullptr; + _texture = nullptr; + _textureProj = nullptr; + _textureOriginal = nullptr; + _textureWhiteSquare = nullptr; + + glDeleteBuffers(1, &_vbo); + + return true; +} + +void RenderableModelProjection::render(const RenderData& data) { + if (!_programObject) return; + if (!_textureProj) return; + _programObject->activate(); + _frameCount++; + + _camScaling = data.camera.scaling(); + _up = data.camera.lookUpVector(); + + if (_capture && _performProjection) + project(); + + attitudeParameters(_time); + _imageTimes.clear(); + + double time = openspace::Time::ref().currentTime(); + bool targetPositionCoverage = openspace::SpiceManager::ref().hasSpkCoverage(_target, time); + if (!targetPositionCoverage) { + int frame = _frameCount % 180; + + float fadingFactor = static_cast(sin((frame * M_PI) / 180)); + _alpha = 0.5f + fadingFactor * 0.5f; } + else + _alpha = 1.0f; + + _programObject->setUniform("ProjectorMatrix", _projectorMatrix); + _programObject->setUniform("boresight", _boresight); + _programObject->setUniform("_performShading", _performShading); + _programObject->setUniform("sun_pos", _sunPosition.vec3()); + _viewProjection = data.camera.viewProjectionMatrix(); + _programObject->setUniform("ViewProjection", _viewProjection); + _programObject->setUniform("ModelTransform", _transform); + setPscUniforms(_programObject, &data.camera, data.position); + + textureBind(); + _geometry->render(); + + // disable shader + _programObject->deactivate(); +} + +void RenderableModelProjection::update(const UpdateData& data) { + if (_programIsDirty) { + _programObject->rebuildFromFile(); + _fboProgramObject->rebuildFromFile(); + _programIsDirty = false; + } + + _time = data.time; + + if (openspace::ImageSequencer2::ref().isReady() && _performProjection) { + openspace::ImageSequencer2::ref().updateSequencer(_time); + _capture = openspace::ImageSequencer2::ref().getImagePaths(_imageTimes, _projecteeID, _instrumentID); + } + + // set spice-orientation in accordance to timestamp + if (!_source.empty()) + openspace::SpiceManager::ref().getPositionTransformMatrix(_source, _destination, _time, _stateMatrix); + + double lt; + openspace::SpiceManager::ref().getTargetPosition("SUN", _target, "GALACTIC", "NONE", _time, _sunPosition, lt); +} + +void RenderableModelProjection::imageProjectGPU() { + + // keep handle to the current bound FBO + GLint defaultFBO; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); + + GLint m_viewport[4]; + glGetIntegerv(GL_VIEWPORT, m_viewport); + glBindFramebuffer(GL_FRAMEBUFFER, _fboID); + // set blend eq + glEnable(GL_BLEND); + glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ZERO); + + glViewport(0, 0, static_cast(_texture->width()), static_cast(_texture->height())); + _fboProgramObject->activate(); + + ghoul::opengl::TextureUnit unitFboProject; + unitFboProject.activate(); + _textureProj->bind(); + _fboProgramObject->setUniform("projectTexture", unitFboProject); + + ghoul::opengl::TextureUnit unitFboCurrent; + unitFboCurrent.activate(); + _texture->bind(); + _fboProgramObject->setUniform("currentTexture", unitFboCurrent); + _fboProgramObject->setUniform("ProjectorMatrix", _projectorMatrix); + _fboProgramObject->setUniform("ModelTransform", _transform); + _fboProgramObject->setUniform("_scaling", _camScaling); + _fboProgramObject->setUniform("boresight", _boresight); + + glBindVertexArray(_vaoID); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ibo); + glDrawElements(GL_TRIANGLES, static_cast(_geometryIndeces.size()), GL_UNSIGNED_INT, 0); + glBindVertexArray(0); + + _fboProgramObject->deactivate(); + glDisable(GL_BLEND); + //bind back to default + glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); + glViewport(m_viewport[0], m_viewport[1], + m_viewport[2], m_viewport[3]); + +} + +void RenderableModelProjection::attitudeParameters(double time) { + openspace::SpiceManager::ref().getPositionTransformMatrix(_source, _destination, time, _stateMatrix); + openspace::SpiceManager::ref().getPositionTransformMatrix(_instrumentID, _destination, time, _instrumentMatrix); + + _transform = glm::mat4(1); + + glm::mat4 rotPropX = glm::rotate(_transform, static_cast(_rotationX), glm::vec3(1, 0, 0)); + glm::mat4 rotPropY = glm::rotate(_transform, static_cast(_rotationY), glm::vec3(0, 1, 0)); + glm::mat4 rotPropZ = glm::rotate(_transform, static_cast(_rotationZ), glm::vec3(0, 0, 1)); + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + _transform[i][j] = static_cast(_stateMatrix[i][j]); + } + } + _transform = _transform * rotPropX * rotPropY * rotPropZ; + + std::string shape, instrument; + std::vector bounds; + glm::dvec3 boresight; + bool found = openspace::SpiceManager::ref().getFieldOfView(_instrumentID, shape, instrument, boresight, bounds); + if (!found) + return; + + double lightTime; + psc position; //observer target + found = SpiceManager::ref().getTargetPosition(_projectorID, _projecteeID, _destination, _aberration, time, position, lightTime); + + position[3] += (3 + _camScaling[1]); + glm::vec3 cpos = position.vec3(); + + _projectorMatrix = computeProjectorMatrix(cpos, boresight, _up); +} + +glm::mat4 RenderableModelProjection::computeProjectorMatrix(const glm::vec3 loc, glm::dvec3 aim, const glm::vec3 up) { + //rotate boresight into correct alignment + _boresight = _instrumentMatrix*aim; + glm::vec3 uptmp(_instrumentMatrix*glm::dvec3(up)); + + // create view matrix + glm::vec3 e3 = glm::normalize(_boresight); + glm::vec3 e1 = glm::normalize(glm::cross(uptmp, e3)); + glm::vec3 e2 = glm::normalize(glm::cross(e3, e1)); + glm::mat4 projViewMatrix = glm::mat4(e1.x, e2.x, e3.x, 0.f, + e1.y, e2.y, e3.y, 0.f, + e1.z, e2.z, e3.z, 0.f, + -glm::dot(e1, loc), -glm::dot(e2, loc), -glm::dot(e3, loc), 1.f); + + // create perspective projection matrix + glm::mat4 projProjectionMatrix = glm::perspective(_fovy, _aspectRatio, _nearPlane, _farPlane); + // bias matrix + glm::mat4 projNormalizationMatrix = glm::mat4(0.5f, 0, 0, 0, + 0, 0.5f, 0, 0, + 0, 0, 0.5f, 0, + 0.5f, 0.5f, 0.5f, 1); + return projNormalizationMatrix*projProjectionMatrix*projViewMatrix; +} + + +void RenderableModelProjection::textureBind() { + ghoul::opengl::TextureUnit unit[2]; + unit[0].activate(); + _texture->bind(); + _programObject->setUniform("currentTexture", unit[0]); + unit[1].activate(); + _textureWhiteSquare->bind(); + _programObject->setUniform("projectedTexture", unit[1]); +} + +void RenderableModelProjection::project() { + for (auto img : _imageTimes) { + std::thread t1(&RenderableModelProjection::attitudeParameters, this, img.startTime); + t1.join(); + _projectionTexturePath = img.path; + imageProjectGPU(); //fbopass + } + _capture = false; +} + +void RenderableModelProjection::loadTexture() { + delete _texture; + _texture = nullptr; + if (_colorTexturePath.value() != "") { + _texture = ghoul::io::TextureReader::ref().loadTexture(absPath(_colorTexturePath)); + if (_texture) { + LDEBUG("Loaded texture from '" << absPath(_colorTexturePath) << "'"); + _texture->uploadTexture(); + _texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); + } + } + delete _textureOriginal; + _textureOriginal = nullptr; + if (_colorTexturePath.value() != "") { + _textureOriginal = ghoul::io::TextureReader::ref().loadTexture(absPath(_colorTexturePath)); + if (_textureOriginal) { + LDEBUG("Loaded texture from '" << absPath(_colorTexturePath) << "'"); + _textureOriginal->uploadTexture(); + _textureOriginal->setFilter(ghoul::opengl::Texture::FilterMode::Linear); + } + } + delete _textureWhiteSquare; + _textureWhiteSquare = nullptr; + if (_defaultProjImage != "") { + _textureWhiteSquare = ghoul::io::TextureReader::ref().loadTexture(absPath(_defaultProjImage)); + if (_textureWhiteSquare) { + _textureWhiteSquare->uploadTexture(); + _textureWhiteSquare->setFilter(ghoul::opengl::Texture::FilterMode::Linear); + } + } +} + +void RenderableModelProjection::loadProjectionTexture() { + delete _textureProj; + _textureProj = nullptr; + if (_projectionTexturePath.value() != "") { + _textureProj = ghoul::io::TextureReader::ref().loadTexture(absPath(_projectionTexturePath)); + if (_textureProj) { + _textureProj->uploadTexture(); + _textureProj->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + _textureProj->setWrapping(ghoul::opengl::Texture::WrappingMode::ClampToBorder); + } + } + +} } // namespace openspace \ No newline at end of file diff --git a/modules/newhorizons/rendering/renderableplanetprojection.cpp b/modules/newhorizons/rendering/renderableplanetprojection.cpp index 8f1cf3badf..a734d25496 100644 --- a/modules/newhorizons/rendering/renderableplanetprojection.cpp +++ b/modules/newhorizons/rendering/renderableplanetprojection.cpp @@ -184,20 +184,25 @@ RenderablePlanetProjection::RenderablePlanetProjection(const ghoul::Dictionary& //get translation dictionary dictionary.getValue(keyTranslation, translationDictionary); - if (_sequenceType == sequenceTypePlaybook){ - parser = new HongKangParser(_sequenceSource, + if (_sequenceType == sequenceTypePlaybook) { + parser = new HongKangParser(name, + _sequenceSource, _projectorID, translationDictionary, _potentialTargets); openspace::ImageSequencer2::ref().runSequenceParser(parser); } - else if (_sequenceType == sequenceTypeImage){ - parser = new LabelParser(_sequenceSource, translationDictionary); + else if (_sequenceType == sequenceTypeImage) { + parser = new LabelParser(name, + _sequenceSource, + translationDictionary); openspace::ImageSequencer2::ref().runSequenceParser(parser); } - else if (_sequenceType == sequenceTypeHybrid){ + else if (_sequenceType == sequenceTypeHybrid) { //first read labels - parser = new LabelParser(_sequenceSource, translationDictionary); + parser = new LabelParser(name, + _sequenceSource, + translationDictionary); openspace::ImageSequencer2::ref().runSequenceParser(parser); std::string _eventFile; @@ -205,7 +210,8 @@ RenderablePlanetProjection::RenderablePlanetProjection(const ghoul::Dictionary& if (foundEventFile){ //then read playbook _eventFile = absPath(_eventFile); - parser = new HongKangParser(_eventFile, + parser = new HongKangParser(name, + _eventFile, _projectorID, translationDictionary, _potentialTargets); diff --git a/modules/newhorizons/util/hongkangparser.cpp b/modules/newhorizons/util/hongkangparser.cpp index 7f898f82c3..38687886a3 100644 --- a/modules/newhorizons/util/hongkangparser.cpp +++ b/modules/newhorizons/util/hongkangparser.cpp @@ -43,12 +43,12 @@ namespace { } namespace openspace { -HongKangParser::HongKangParser(const std::string& fileName, +HongKangParser::HongKangParser(std::string name, const std::string& fileName, std::string spacecraft, ghoul::Dictionary translationDictionary, - std::vector potentialTargets) : - - _defaultCaptureImage(absPath("${OPENSPACE_DATA}/scene/common/textures/placeholder.png")) + std::vector potentialTargets) + : _name(std::move(name)) + , _defaultCaptureImage(absPath("${OPENSPACE_DATA}/scene/common/textures/placeholder.png")) { _fileName = fileName; _spacecraft = spacecraft; diff --git a/modules/newhorizons/util/hongkangparser.h b/modules/newhorizons/util/hongkangparser.h index 9d827a03de..a91b6d14ef 100644 --- a/modules/newhorizons/util/hongkangparser.h +++ b/modules/newhorizons/util/hongkangparser.h @@ -38,7 +38,7 @@ namespace openspace { class HongKangParser : public SequenceParser { public: HongKangParser(); - HongKangParser(const std::string& fileName, + HongKangParser(std::string name, const std::string& fileName, std::string spacecraft, ghoul::Dictionary dictionary, std::vector potentialTargets); @@ -69,6 +69,7 @@ private: std::string _defaultCaptureImage; double _metRef = 299180517; + std::string _name; std::string _fileName; std::string _spacecraft; std::map _fileTranslation; diff --git a/modules/newhorizons/util/labelparser.cpp b/modules/newhorizons/util/labelparser.cpp index 7ca299ac99..565fc53329 100644 --- a/modules/newhorizons/util/labelparser.cpp +++ b/modules/newhorizons/util/labelparser.cpp @@ -44,8 +44,11 @@ namespace { } namespace openspace { -LabelParser::LabelParser(const std::string& fileName, - ghoul::Dictionary translationDictionary) : _badDecoding(false) + +LabelParser::LabelParser(std::string name, const std::string& fileName, + ghoul::Dictionary translationDictionary) + : _name(std::move(name)) + , _badDecoding(false) { _fileName = fileName; //get the different instrument types diff --git a/modules/newhorizons/util/labelparser.h b/modules/newhorizons/util/labelparser.h index d4f7ada971..abfd58ba64 100644 --- a/modules/newhorizons/util/labelparser.h +++ b/modules/newhorizons/util/labelparser.h @@ -36,7 +36,8 @@ namespace openspace { class LabelParser : public SequenceParser{ public: LabelParser(); - LabelParser(const std::string& fileName, + LabelParser(std::string name, + const std::string& fileName, ghoul::Dictionary translationDictionary); bool create() override; @@ -60,6 +61,7 @@ private: std::vector payload, std::vector potentialTargets); + std::string _name; std::string _fileName; std::string _spacecraft; std::map _fileTranslation; diff --git a/src/network/networkengine.cpp b/src/network/networkengine.cpp index cc32a43418..dbf4359c28 100644 --- a/src/network/networkengine.cpp +++ b/src/network/networkengine.cpp @@ -40,6 +40,7 @@ namespace { const std::string StatusMessageIdentifierName = "StatusMessage"; const std::string MappingIdentifierIdentifierName = "IdentifierMapping"; + const std::string InitialMessageFinishedIdentifierName = "InitialMessageFinished"; const char MessageTypeLuaScript = '0'; const char MessageTypeExternalControlConnected = '1'; @@ -57,6 +58,7 @@ NetworkEngine::NetworkEngine() ); _statusMessageIdentifier = identifier(StatusMessageIdentifierName); _identifierMappingIdentifier = identifier(MappingIdentifierIdentifierName); + _initialMessageFinishedIdentifier = identifier(InitialMessageFinishedIdentifierName); } bool NetworkEngine::handleMessage(const std::string& message) { @@ -117,18 +119,18 @@ void NetworkEngine::publishStatusMessage() { void NetworkEngine::publishIdentifierMappingMessage() { size_t bufferSize = 0; - for (const std::pair& i : _identifiers) { + for (const std::pair& i : _identifiers) { bufferSize += sizeof(MessageIdentifier); - bufferSize += i.second.size() + 1; // +1 for \0 terminating character + bufferSize += i.first.size() + 1; // +1 for \0 terminating character } std::vector buffer(bufferSize); size_t currentWritingPosition = 0; - for (const std::pair& i : _identifiers) { - std::memcpy(buffer.data() + currentWritingPosition, &(i.first), sizeof(MessageIdentifier)); + for (const std::pair& i : _identifiers) { + std::memcpy(buffer.data() + currentWritingPosition, &(i.second), sizeof(MessageIdentifier)); currentWritingPosition += sizeof(MessageIdentifier); - std::memcpy(buffer.data() + currentWritingPosition, i.second.data(), i.second.size()); - currentWritingPosition += i.second.size(); + std::memcpy(buffer.data() + currentWritingPosition, i.first.data(), i.first.size()); + currentWritingPosition += i.first.size(); buffer[currentWritingPosition] = '\0'; currentWritingPosition += 1; } @@ -138,21 +140,17 @@ void NetworkEngine::publishIdentifierMappingMessage() { NetworkEngine::MessageIdentifier NetworkEngine::identifier(std::string name) { -#ifdef DEBUG - // Check if name has been assigned already - for (const std::pair& p : _identifiers) { - if (p.second == name) { - LERROR("Name '" << name << "' for identifier has been registered before"); - return -1; - } + auto i = _identifiers.find(name); + if (i != _identifiers.end()) + return i->second; + else { + _lastAssignedIdentifier++; + + MessageIdentifier result = _lastAssignedIdentifier; + + _identifiers[std::move(name)] = result; + return result; } -#endif - _lastAssignedIdentifier++; - - MessageIdentifier result = _lastAssignedIdentifier; - - _identifiers[result] = std::move(name); - return result; } void NetworkEngine::publishMessage(MessageIdentifier identifier, std::vector message) { @@ -205,6 +203,20 @@ void NetworkEngine::sendInitialInformation() { std::this_thread::sleep_for(std::chrono::milliseconds(SleepTime)); } + std::this_thread::sleep_for(std::chrono::milliseconds(SleepTime)); + + // Send finished message + union { + MessageIdentifier value; + std::array data; + } identifier; + identifier.value = _initialMessageFinishedIdentifier; + + sgct::Engine::instance()->sendMessageToExternalControl( + identifier.data.data(), + 2 + ); + _shouldPublishStatusMessage = true; } From 19c7022220a83c227b0fce989053d393ee5f0727 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Thu, 2 Jul 2015 12:25:44 +0200 Subject: [PATCH 246/329] added current time sync --- src/network/parallelconnection.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index ca7fa9c994..a257ab9693 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -557,6 +557,12 @@ namespace openspace { std::vector::const_iterator it; std::vector scriptMsg; + + //add as a first script the current time to ensure all nodes are on the same page + std::string timenow = Time::ref().currentTimeUTC(); + std::string timescript = "openspace.time.setTime(\"" + timenow + "\");"; + scripts.push_back(timescript); + //write all scripts for(it = scripts.cbegin(); it != scripts.cend(); From cb9bd514f6b17fe806a46a40a859a64d9b582f23 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Thu, 2 Jul 2015 14:01:54 +0200 Subject: [PATCH 247/329] added message type saying if initialisation is done and lua function + script added current time and current delta time to initialisation scripts --- .../openspace/network/parallelconnection.h | 5 ++- src/network/parallelconnection.cpp | 40 +++++++++++++++++-- src/network/parallelconnection_lua.inl | 8 ++++ 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/include/openspace/network/parallelconnection.h b/include/openspace/network/parallelconnection.h index ab748a199a..06f21a342a 100644 --- a/include/openspace/network/parallelconnection.h +++ b/include/openspace/network/parallelconnection.h @@ -133,6 +133,8 @@ namespace openspace{ void update(double dt); + void initDone(); + enum MessageTypes{ Authentication=0, Initialization, @@ -140,7 +142,8 @@ namespace openspace{ Script, HostInfo, InitializationRequest, - HostshipRequest + HostshipRequest, + InitializationCompleted }; /** diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index a257ab9693..d93c015dc3 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -194,6 +194,9 @@ namespace openspace { //if the connection was successfull if (result != SOCKET_ERROR) { + + LINFO("Connection established with server at ip: "<< _address); + //we're connected _isConnected.store(true); @@ -550,7 +553,7 @@ namespace openspace { //construct init msg std::vector scripts = OsEng.scriptEngine()->cachedScripts(); - uint16_t numScrips = scripts.size(); + uint16_t scriptlen; uint32_t totlen = 0; @@ -558,11 +561,22 @@ namespace openspace { std::vector scriptMsg; - //add as a first script the current time to ensure all nodes are on the same page - std::string timenow = Time::ref().currentTimeUTC(); - std::string timescript = "openspace.time.setTime(\"" + timenow + "\");"; + //add a script of the current time to ensure all nodes are on the same page + std::string timescript = "openspace.time.setTime(\"" + std::to_string(Time::ref().currentTime()); + "\");"; scripts.push_back(timescript); + //add a script of the current delta time to ensure all nodes are on the same page + + std::string dtscript = "openspace.time.setTime(\"" + std::to_string(Time::ref().deltaTime()) + "\");"; + scripts.push_back(dtscript); + + //add a terminating script letting the server know the client is fully initialized + std::string donescript = "openspace.parallel.initialized();"; + scripts.push_back(donescript); + + //total number of scripts + uint16_t numScrips = static_cast(scripts.size()); + //write all scripts for(it = scripts.cbegin(); it != scripts.cend(); @@ -917,6 +931,18 @@ namespace openspace { //minor and major version (as uint8_t -> 1 byte) + two bytes for the chars 'O' and 'S' + 4 bytes for type of message return 2 * sizeof(uint8_t) + 2 + sizeof(uint32_t); } + + void ParallelConnection::initDone(){ + //create buffer and reserve size + std::vector buffer; + buffer.reserve(headerSize()); + + //write header + writeHeader(buffer, MessageTypes::InitializationCompleted); + + //queue script + queMessage(buffer); + } scripting::ScriptEngine::LuaLibrary ParallelConnection::luaLibrary() { return { @@ -964,6 +990,12 @@ namespace openspace { "", "Request to be the host for this session" }, + { + "initialized", + &luascriptfunctions::initialized, + "", + "Request to be the host for this session" + }, } }; } diff --git a/src/network/parallelconnection_lua.inl b/src/network/parallelconnection_lua.inl index 3237675c1a..1e66631776 100644 --- a/src/network/parallelconnection_lua.inl +++ b/src/network/parallelconnection_lua.inl @@ -148,6 +148,14 @@ int requestHostship(lua_State* L) { return 0; } +int initialized(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 0) + return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); + OsEng.parallelConnection()->initDone(); + return 0; +} + } // namespace luascriptfunctions } // namespace openspace From c11c0eb81fd77ccb5b151ec6032c6a1d2d95ad46 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Thu, 2 Jul 2015 14:48:17 +0200 Subject: [PATCH 248/329] changed time scripts to not include "" in them. moved thread deletion to prevent deadlock --- src/network/parallelconnection.cpp | 35 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index d93c015dc3..291ce9572a 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -562,12 +562,12 @@ namespace openspace { std::vector scriptMsg; //add a script of the current time to ensure all nodes are on the same page - std::string timescript = "openspace.time.setTime(\"" + std::to_string(Time::ref().currentTime()); + "\");"; + std::string timescript = "openspace.time.setTime(" + std::to_string(Time::ref().currentTime()) + ");"; scripts.push_back(timescript); //add a script of the current delta time to ensure all nodes are on the same page - std::string dtscript = "openspace.time.setTime(\"" + std::to_string(Time::ref().deltaTime()) + "\");"; + std::string dtscript = "openspace.time.setDeltaTime(" + std::to_string(Time::ref().deltaTime()) + ");"; scripts.push_back(dtscript); //add a terminating script letting the server know the client is fully initialized @@ -770,22 +770,8 @@ namespace openspace { _isHost.store(false); //tell connection thread to stop listening - _isListening.store(false); - - //tell connection thread that we are connected (to be able to join and delete thread) - _isConnected.store(true); - - //join connection thread and delete it - if (_connectionThread != nullptr){ - _connectionThread->join(); - delete _connectionThread; - _connectionThread = nullptr; - } - - //make sure to set us as NOT connected again, connection thread is now deleted - _isConnected.store(false); - - + _isListening.store(false); + //join receive thread and delete it if (_receiveThread != nullptr){ _receiveThread->join(); @@ -806,6 +792,19 @@ namespace openspace { delete _sendThread; _sendThread = nullptr; } + + //tell connection thread that we are connected (to be able to join and delete thread) + _isConnected.store(true); + + //join connection thread and delete it + if (_connectionThread != nullptr){ + _connectionThread->join(); + delete _connectionThread; + _connectionThread = nullptr; + } + + //make sure to set us as NOT connected again, connection thread is now deleted + _isConnected.store(false); #if defined(__WIN32__) //this line causes issues with SGCT since winsock dll file is unloaded upon call From 6f3b534969d008fe3d388d28507b099ad07bf32f Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Thu, 2 Jul 2015 15:07:28 +0200 Subject: [PATCH 249/329] minor bugfixes --- src/network/parallelconnection.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index 291ce9572a..a658715647 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -92,6 +92,7 @@ namespace openspace { ParallelConnection::~ParallelConnection(){ disconnect(); + WSACleanup(); } void ParallelConnection::clientConnect(){ @@ -121,9 +122,10 @@ namespace openspace { if (result != 0) { #if defined(__WIN32__) - WSACleanup(); + //WSACleanup(); #endif LERROR("Failed to parse hints for Parallel Connection"); + return; } //we're not connected @@ -141,7 +143,7 @@ namespace openspace { if (_clientSocket == INVALID_SOCKET){ freeaddrinfo(info); #if defined(__WIN32__) - WSACleanup(); + //WSACleanup(); #endif LERROR("Failed to create client socket, shutting down connection thread"); return; @@ -855,7 +857,7 @@ namespace openspace { { /* incorrect WinSock version */ LERROR("Failed to init winsock API."); - WSACleanup(); + //WSACleanup(); return false; } #else From 0e4dbbb1cc22da19c3d3061aadc3af661b76ec66 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 2 Jul 2015 15:53:25 +0200 Subject: [PATCH 250/329] Some work on the Launcher --- apps/Launcher/infowidget.cpp | 6 ++++++ apps/Launcher/infowidget.h | 1 + apps/Launcher/syncwidget.cpp | 6 +++--- data | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/Launcher/infowidget.cpp b/apps/Launcher/infowidget.cpp index 45395903bc..11ea196638 100644 --- a/apps/Launcher/infowidget.cpp +++ b/apps/Launcher/infowidget.cpp @@ -36,6 +36,7 @@ InfoWidget::InfoWidget(QString name, int totalBytes) , _bytes(nullptr) , _progress(nullptr) , _messagesLeft(nullptr) + , _messagesCenter(nullptr) , _messagesRight(nullptr) , _totalBytes(totalBytes) { @@ -56,9 +57,11 @@ InfoWidget::InfoWidget(QString name, int totalBytes) layout->addWidget(_progress, 1, 1); _messagesLeft = new QLabel(""); + _messagesCenter = new QLabel(""); _messagesRight = new QLabel(""); layout->addWidget(_messagesLeft, 2, 0, 1, 2); + layout->addWidget(_messagesCenter, 2, 0, 1, 2, Qt::AlignCenter); layout->addWidget(_messagesRight, 2, 0, 1, 2, Qt::AlignRight); setLayout(layout); @@ -99,6 +102,9 @@ void InfoWidget::update(libtorrent::torrent_status s) { QString left = "Time remaining %1 s"; _messagesLeft->setText(left.arg(static_cast(seconds))); + QString center = "Peers: %1 (%2) | Seeds: %3 (%4)"; + _messagesCenter->setText(center.arg(s.num_peers).arg(s.list_peers).arg(s.num_seeds).arg(s.list_seeds)); + QString right = "Download Rate: %1 KiB/s"; _messagesRight->setText(right.arg(bytesPerSecond / 1024)); } diff --git a/apps/Launcher/infowidget.h b/apps/Launcher/infowidget.h index e2d42f9e4a..2cf9d1f5da 100644 --- a/apps/Launcher/infowidget.h +++ b/apps/Launcher/infowidget.h @@ -52,6 +52,7 @@ private: QLabel* _bytes; QProgressBar* _progress; QLabel* _messagesLeft; + QLabel* _messagesCenter; QLabel* _messagesRight; int _totalBytes; diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 7f6540f696..6ad5409e4f 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -117,7 +117,7 @@ SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) openspace::DownloadManager::initialize("http://openspace.itn.liu.se/data/request", DownloadApplicationVersion); libtorrent::error_code ec; - _session->listen_on(std::make_pair(20285, 20285), ec); + _session->listen_on(std::make_pair(20280, 20290), ec); if (ec) { LFATAL("Failed to open socket: " << ec.message()); return; @@ -125,9 +125,9 @@ SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) _session->start_upnp(); _session->start_dht(); - _session->add_dht_router({ "dht.transmissionbt.com", 6881}); - _session->add_dht_router({ "router.bittorrent.com", 6881}); _session->add_dht_router({ "router.utorrent.com", 6881 }); + _session->add_dht_router({ "dht.transmissionbt.com", 6881 }); + _session->add_dht_router({ "router.bittorrent.com", 6881 }); _session->add_dht_router({ "router.bitcomet.com", 6881 }); QTimer* timer = new QTimer(this); diff --git a/data b/data index fa00aaa0c2..e234fc3176 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit fa00aaa0c244e8e5709877a344c3007d2244e9ec +Subproject commit e234fc317660bf8b593a23ca505892a44a17d70e From 2bd2270434a48c3ec9de5c0e7402a53fae143c5a Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Thu, 2 Jul 2015 17:10:43 +0200 Subject: [PATCH 251/329] fixed compile issue on mac --- src/network/parallelconnection.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index a658715647..404dda51c6 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -92,7 +92,9 @@ namespace openspace { ParallelConnection::~ParallelConnection(){ disconnect(); - WSACleanup(); +#if defined(__WIN32__) + WSACleanup(); +#endif } void ParallelConnection::clientConnect(){ From 19af89575217ff180307d01382598d6f6a253338 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Thu, 2 Jul 2015 17:28:04 +0200 Subject: [PATCH 252/329] changed it so parallel connection scripts are only run ont he master mode. --- src/network/parallelconnection_lua.inl | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/network/parallelconnection_lua.inl b/src/network/parallelconnection_lua.inl index 1e66631776..0e135d9239 100644 --- a/src/network/parallelconnection_lua.inl +++ b/src/network/parallelconnection_lua.inl @@ -32,6 +32,9 @@ namespace luascriptfunctions { * Set the port for parallel connection */ int setPort(lua_State* L) { + if(!OsEng.isMaster()){ + return; + } const bool isFunction = (lua_isfunction(L, -1) != 0); if (isFunction) { // If the top of the stack is a function, it is ourself @@ -56,6 +59,9 @@ int setPort(lua_State* L) { } int setAddress(lua_State* L) { + if(!OsEng.isMaster()){ + return; + } const bool isFunction = (lua_isfunction(L, -1) != 0); if (isFunction) { // If the top of the stack is a function, it is ourself @@ -79,6 +85,9 @@ int setAddress(lua_State* L) { } int setPassword(lua_State* L) { + if(!OsEng.isMaster()){ + return; + } const bool isFunction = (lua_isfunction(L, -1) != 0); if (isFunction) { // If the top of the stack is a function, it is ourself @@ -102,6 +111,9 @@ int setPassword(lua_State* L) { } int setDisplayName(lua_State* L) { + if(!OsEng.isMaster()){ + return; + } const bool isFunction = (lua_isfunction(L, -1) != 0); if (isFunction) { // If the top of the stack is a function, it is ourself @@ -125,6 +137,9 @@ int setDisplayName(lua_State* L) { } int connect(lua_State* L) { + if(!OsEng.isMaster()){ + return; + } int nArguments = lua_gettop(L); if (nArguments != 0) return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); @@ -133,6 +148,9 @@ int connect(lua_State* L) { } int disconnect(lua_State* L) { + if(!OsEng.isMaster()){ + return; + } int nArguments = lua_gettop(L); if (nArguments != 0) return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); @@ -141,6 +159,9 @@ int disconnect(lua_State* L) { } int requestHostship(lua_State* L) { + if(!OsEng.isMaster()){ + return; + } int nArguments = lua_gettop(L); if (nArguments != 0) return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); @@ -149,6 +170,9 @@ int requestHostship(lua_State* L) { } int initialized(lua_State* L) { + if(!OsEng.isMaster()){ + return; + } int nArguments = lua_gettop(L); if (nArguments != 0) return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); From 304a328cdee0e3791c8791852db80bfd4eec7745 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 3 Jul 2015 00:00:48 +0200 Subject: [PATCH 253/329] Let TimelineView handle multiple instances of Labelparser and HongkangParsers --- apps/TimelineView/mainwindow.cpp | 64 +++++++++++++++++++++++++++----- apps/TimelineView/mainwindow.h | 3 +- src/network/networkengine.cpp | 20 +++++++--- 3 files changed, 71 insertions(+), 16 deletions(-) diff --git a/apps/TimelineView/mainwindow.cpp b/apps/TimelineView/mainwindow.cpp index 9a59501fd7..6fdc04fc4e 100644 --- a/apps/TimelineView/mainwindow.cpp +++ b/apps/TimelineView/mainwindow.cpp @@ -37,6 +37,10 @@ #include #include +namespace { + QByteArray continuousData; +} + template T readFromBuffer(char* buffer, size_t& currentReadLocation) { union { @@ -128,16 +132,25 @@ void MainWindow::onDisconnect() { void MainWindow::readTcpData() { static const uint16_t MessageTypeStatus = 0; - static const uint16_t MessageTypePlayBookHongKang = 2; + static const uint16_t MessageTypeMappingIdentifier = 1; + static const uint16_t MessageTypeInitialMessageFinished = 2; static const uint16_t MessageTypePlayBookLabel = 3; - static const uint16_t MessageTypeInitialMessageFinished = 4; + static const uint16_t MessageTypePlayBookHongKang = 4; - QByteArray data = _socket->readAll(); + QByteArray data = continuousData.append(_socket->readAll()); + int d = data.size(); - if (QString(data) == "Connected to SGCT!\r\n") + if (data.size() != 42) + qDebug() << QString(data); + + if (QString(data) == "Connected to SGCT!\r\n") { + continuousData.clear(); return; - if (QString(data) == "OK\r\n") + } + if (QString(data) == "OK\r\n") { + continuousData.clear(); return; + } QByteArray messageTypeData = data.left(2); union { @@ -149,6 +162,14 @@ void MainWindow::readTcpData() { switch (messageType.value) { case MessageTypeStatus: break; + case MessageTypeMappingIdentifier: + qDebug() << "Mapping Identifier received"; + printMapping(data.mid(2)); + continuousData.clear(); + break; + case MessageTypeInitialMessageFinished: + qDebug() << "InitialMessageFinished received"; + break; case MessageTypePlayBookHongKang: qDebug() << "Hong Kang Playbook received"; break; @@ -164,6 +185,7 @@ void MainWindow::readTcpData() { { if (_isConnected) handleStatusMessage(data.mid(2)); + continuousData.clear(); break; } case MessageTypePlayBookHongKang: @@ -177,13 +199,16 @@ void MainWindow::readTcpData() { //qDebug() << "Begin reading data"; while (_socket->waitForReadyRead() && data.size() < int(size)) { //qDebug() << "."; + //_socket->read + //data = data.append(_socket->re) data = data.append(_socket->readAll()); //data = data.append(_socket->read(int(size) - data.size())); - QThread::msleep(50); + //QThread::msleep(50); } //qDebug() << "Finished reading data. Handling playbook"; - handlePlaybook(data.mid(2)); + continuousData = handlePlaybook(data.mid(2)); + //qDebug() << "Finished handling playbook"; @@ -201,6 +226,7 @@ void MainWindow::readTcpData() { case MessageTypeInitialMessageFinished: _isConnected = true; fullyConnected(); + continuousData.clear(); break; default: @@ -248,7 +274,7 @@ std::vector instrumentsFromId(uint16_t instrumentId, std::mapsetData(std::move(images), std::move(targetMap), std::move(instrumentMap)); + auto dataSize = data.size(); + auto readSize = currentReadLocation; + auto extraBytes = dataSize - readSize; + if (extraBytes > 0) + return data.mid(currentReadLocation); + else + return QByteArray(); } void MainWindow::sendScript(QString script) { @@ -336,3 +368,17 @@ void MainWindow::fullyConnected() { _informationWidget->socketConnected(); _timelineWidget->socketConnected(); } + +void MainWindow::printMapping(QByteArray data) { + char* buffer = data.data(); + size_t currentReadPosition = 0; + + uint16_t size = readFromBuffer(buffer, currentReadPosition); + for (uint16_t i = 0; i < size; ++i) { + uint16_t identifier = readFromBuffer(buffer, currentReadPosition); + std::string mapping = readFromBuffer(buffer, currentReadPosition); + + qDebug() << identifier << ": " << QString::fromStdString(mapping); + + } +} diff --git a/apps/TimelineView/mainwindow.h b/apps/TimelineView/mainwindow.h index a8d9b9efbf..126dc314cf 100644 --- a/apps/TimelineView/mainwindow.h +++ b/apps/TimelineView/mainwindow.h @@ -55,7 +55,8 @@ private slots: void readTcpData(); void handleStatusMessage(QByteArray data); - void handlePlaybook(QByteArray data); + QByteArray handlePlaybook(QByteArray data); + void printMapping(QByteArray data); void fullyConnected(); diff --git a/src/network/networkengine.cpp b/src/network/networkengine.cpp index dbf4359c28..747a78083a 100644 --- a/src/network/networkengine.cpp +++ b/src/network/networkengine.cpp @@ -74,6 +74,8 @@ bool NetworkEngine::handleMessage(const std::string& message) { } case MessageTypeExternalControlConnected: { + publishIdentifierMappingMessage(); + std::this_thread::sleep_for(std::chrono::milliseconds(250)); sendInitialInformation(); return true; } @@ -118,7 +120,7 @@ void NetworkEngine::publishStatusMessage() { } void NetworkEngine::publishIdentifierMappingMessage() { - size_t bufferSize = 0; + size_t bufferSize = sizeof(uint16_t); for (const std::pair& i : _identifiers) { bufferSize += sizeof(MessageIdentifier); bufferSize += i.first.size() + 1; // +1 for \0 terminating character @@ -126,13 +128,17 @@ void NetworkEngine::publishIdentifierMappingMessage() { std::vector buffer(bufferSize); size_t currentWritingPosition = 0; + uint16_t size = _identifiers.size(); + std::memcpy(buffer.data(), &size, sizeof(uint16_t)); + currentWritingPosition += sizeof(uint16_t); for (const std::pair& i : _identifiers) { std::memcpy(buffer.data() + currentWritingPosition, &(i.second), sizeof(MessageIdentifier)); currentWritingPosition += sizeof(MessageIdentifier); - std::memcpy(buffer.data() + currentWritingPosition, i.first.data(), i.first.size()); + uint8_t stringSize = i.first.size(); + std::memcpy(buffer.data() + currentWritingPosition, &stringSize, sizeof(uint8_t)); + currentWritingPosition += sizeof(uint8_t); + std::memcpy(buffer.data() + currentWritingPosition, i.first.data(), stringSize); currentWritingPosition += i.first.size(); - buffer[currentWritingPosition] = '\0'; - currentWritingPosition += 1; } publishMessage(_identifierMappingIdentifier, std::move(buffer)); @@ -178,13 +184,14 @@ void NetworkEngine::sendMessages() { m.body.data(), static_cast(m.body.size()) ); + //LINFO("Sent message: (s=" << m.body.size() << "): " << std::string(m.body.begin(), m.body.end())); } _messagesToSend.clear(); } void NetworkEngine::sendInitialInformation() { - static const int SleepTime = 100; + static const int SleepTime = 250; _shouldPublishStatusMessage = false; for (const Message& m : _initialConnectionMessages) { union { @@ -199,11 +206,12 @@ void NetworkEngine::sendInitialInformation() { payload.data(), static_cast(payload.size()) ); + LINFO("Sent initial message: (s=" << m.body.size() << ") [i=" << identifier.value << "]"); std::this_thread::sleep_for(std::chrono::milliseconds(SleepTime)); } - std::this_thread::sleep_for(std::chrono::milliseconds(SleepTime)); + std::this_thread::sleep_for(std::chrono::milliseconds(4 * SleepTime)); // Send finished message union { From 2397a28d49c52865d139f843fa7ebbb75426dd1f Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 3 Jul 2015 00:40:49 +0200 Subject: [PATCH 254/329] Set Launcher's information widget to readonly --- apps/Launcher/mainwindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/Launcher/mainwindow.cpp b/apps/Launcher/mainwindow.cpp index c186e6c36c..53f08c04f3 100644 --- a/apps/Launcher/mainwindow.cpp +++ b/apps/Launcher/mainwindow.cpp @@ -90,6 +90,7 @@ MainWindow::MainWindow() _informationWidget = new QTextEdit(this); _informationWidget->setReadOnly(true); + _informationWidget->setEnabled(false); layout->addWidget(_informationWidget, 1, 0, 2, 1); QWidget* container = new QWidget; From 3d17e6bcf286c4667ab512db11819dc72f0a0033 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Fri, 3 Jul 2015 14:42:20 +0200 Subject: [PATCH 255/329] moved setting of master. (before it was set after init so some scripts could fail from startup.lua if they required one to be master) --- apps/OpenSpace/main.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/OpenSpace/main.cpp b/apps/OpenSpace/main.cpp index d79c93c777..0d74c96399 100644 --- a/apps/OpenSpace/main.cpp +++ b/apps/OpenSpace/main.cpp @@ -152,6 +152,7 @@ int main(int argc, char** argv) { } sgct::Engine::RunMode rm = versionMapping[glVersion]; const bool initSuccess = _sgctEngine->init(rm); + if (!initSuccess) { LFATAL("Initializing failed"); // could not open a window, deallocates and exits @@ -160,8 +161,7 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } - //is this node the master? (must be set after call to _sgctEngine->init()) - OsEng.setMaster(_sgctEngine->isMaster()); + // Main loop LDEBUG("Starting rendering loop"); @@ -183,6 +183,9 @@ int main(int argc, char** argv) { } void mainInitFunc() { + //is this node the master? (must be set after call to _sgctEngine->init()) + OsEng.setMaster(_sgctEngine->isMaster()); + bool success = OsEng.initialize(); if (success) success = OsEng.initializeGL(); From 8c2b5bb1999900d19ba059d2ff398ff1cdaab940 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Fri, 3 Jul 2015 14:43:48 +0200 Subject: [PATCH 256/329] Tons of stability improvements to threading of parallel connection. changed name of variables and functions to reflect more what they do. removed obsolete functions. parallel connection is no longer in contact with main thread unless to be deleted --- .../openspace/network/parallelconnection.h | 32 +- src/engine/openspaceengine.cpp | 1 - src/network/parallelconnection.cpp | 303 +++++++++--------- src/network/parallelconnection_lua.inl | 64 ++-- 4 files changed, 198 insertions(+), 202 deletions(-) diff --git a/include/openspace/network/parallelconnection.h b/include/openspace/network/parallelconnection.h index 06f21a342a..dc0bc88c1e 100644 --- a/include/openspace/network/parallelconnection.h +++ b/include/openspace/network/parallelconnection.h @@ -105,22 +105,12 @@ namespace openspace{ void clientConnect(); - void disconnect(); - void setPort(const std::string &port); - std::string port(); - void setAddress(const std::string &address); - std::string address(); - void setName(const std::string& name); - std::string name(); - - _SOCKET clientSocket(); - bool isHost(); void requestHostship(); @@ -129,12 +119,10 @@ namespace openspace{ void sendScript(const std::string script); - void queMessage(std::vector message); - - void update(double dt); - void initDone(); + void signalDisconnect(); + enum MessageTypes{ Authentication=0, Initialization, @@ -176,13 +164,17 @@ namespace openspace{ return hashVal; }; + void queMessage(std::vector message); + + void disconnect(); + void writeHeader(std::vector &buffer, uint32_t messageType); void closeSocket(); bool initNetworkAPI(); - void tryConnect(addrinfo *info); + void establishConnection(addrinfo *info); void sendAuthentication(); @@ -208,6 +200,10 @@ namespace openspace{ void sendLoop(); + bool parseHints(addrinfo &info); + + void threadManagement(); + uint32_t _passCode; std::string _port; std::string _address; @@ -216,11 +212,13 @@ namespace openspace{ std::thread *_connectionThread; std::thread *_broadcastThread; std::thread *_sendThread; - std::thread *_receiveThread; + std::thread *_listenThread; + std::thread *_handlerThread; std::atomic _isHost; std::atomic _isConnected; - std::atomic _isListening; std::atomic _performDisconnect; + std::atomic _isRunning; + std::atomic _tryConnect; std::vector> _sendBuffer; std::mutex _sendBufferMutex; }; diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 3051f573e1..8c101ce271 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -638,7 +638,6 @@ void OpenSpaceEngine::preSynchronization() { Time::ref().preSynchronization(); _interactionHandler->update(dt); - _parallelConnection->update(dt); //_interactionHandler.lockControls(); _scriptEngine->preSynchronization(); diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index 404dda51c6..49d33185ef 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -81,31 +81,132 @@ namespace openspace { _connectionThread(nullptr), _broadcastThread(nullptr), _sendThread(nullptr), - _receiveThread(nullptr), + _listenThread(nullptr), + _handlerThread(nullptr), + _isRunning(true), _isHost(false), _isConnected(false), - _isListening(false), + _tryConnect(false), _performDisconnect(false) { - + //create handler thread + _handlerThread = new (std::nothrow) std::thread(&ParallelConnection::threadManagement, this); } ParallelConnection::~ParallelConnection(){ - disconnect(); -#if defined(__WIN32__) - WSACleanup(); + + //signal that a disconnect should occur + signalDisconnect(); + + //signal that execution has stopped + _isRunning.store(false); + + //join handler + _handlerThread->join(); + + //and delete handler + delete _handlerThread; + } + + void ParallelConnection::threadManagement(){ + //while program is running + while(_isRunning.load()){ + //if we need to disconnect and clean up + if(_performDisconnect.load()){ + disconnect(); + } + } + } + + void ParallelConnection::signalDisconnect(){ + _performDisconnect.store(true); + } + + void ParallelConnection::closeSocket(){ + + /* + Windows shutdown options + * SD_RECIEVE + * SD_SEND + * SD_BOTH + + Linux & Mac shutdown options + * SHUT_RD (Disables further receive operations) + * SHUT_WR (Disables further send operations) + * SHUT_RDWR (Disables further send and receive operations) + */ + +#ifdef __WIN32__ + shutdown(_clientSocket, SD_BOTH); + closesocket(_clientSocket); +#else + shutdown(_clientSocket, SHUT_RDWR); + close(_clientSocket); #endif + + _clientSocket = INVALID_SOCKET; + } + + void ParallelConnection::disconnect(){ + //we're disconnecting + + if (_clientSocket != INVALID_SOCKET){ + + //must be run before trying to join communication threads, else the threads are stuck trying to receive data + closeSocket(); + + //tell connection thread to stop trying to connect + _tryConnect.store(false); + + //tell send thread to stop sending and listen thread to stop listenin + _isConnected.store(false); + + //tell broadcast thread to stop broadcasting (we're no longer host) + _isHost.store(false); + + //join connection thread and delete it + if(_connectionThread != nullptr){ + _connectionThread->join(); + delete _connectionThread; + _connectionThread = nullptr; + } + + //join send thread and delete it + if (_sendThread != nullptr){ + _sendThread->join(); + delete _sendThread; + _sendThread = nullptr; + } + + //join listen thread and delete it + if( _listenThread != nullptr){ + _listenThread->join(); + delete _listenThread; + _listenThread = nullptr; + } + + //join broadcast thread and delete it + if(_broadcastThread != nullptr){ + _broadcastThread->join(); + delete _broadcastThread; + _broadcastThread = nullptr; + } + + // disconnect and cleanup completed + _performDisconnect.store(false); + } } void ParallelConnection::clientConnect(){ - //we're already connected, do nothing (dummy check) - if(_isConnected.load()){ + //we're already connected (or already trying to connect), do nothing (dummy check) + if(_isConnected.load() || _tryConnect.load()){ return; } if (!initNetworkAPI()){ LERROR("Failed to initialize network API for Parallel Connection"); + return; } struct addrinfo *addresult = NULL, *ptr = NULL, hints; @@ -123,32 +224,31 @@ namespace openspace { int result = getaddrinfo(_address.c_str(), _port.c_str(), &hints, &addresult); if (result != 0) { - #if defined(__WIN32__) - //WSACleanup(); - #endif LERROR("Failed to parse hints for Parallel Connection"); return; } - + //we're not connected - _isConnected.store(false); + _isConnected.store(false); + + //we want to try and establish a connection + _tryConnect.store(true); //start connection thread - _connectionThread = new (std::nothrow) std::thread(&ParallelConnection::tryConnect, this, addresult); + _connectionThread = new (std::nothrow) std::thread(&ParallelConnection::establishConnection, this, addresult); } - void ParallelConnection::tryConnect(addrinfo *info){ + void ParallelConnection::establishConnection(addrinfo *info){ _clientSocket = socket(info->ai_family, info->ai_socktype, info->ai_protocol); if (_clientSocket == INVALID_SOCKET){ freeaddrinfo(info); -#if defined(__WIN32__) - //WSACleanup(); -#endif - LERROR("Failed to create client socket, shutting down connection thread"); - return; + LERROR("Failed to create client socket, disconnecting."); + + //signal a disconnect + signalDisconnect(); } int flag = 1; @@ -188,7 +288,7 @@ namespace openspace { //while the connection thread is still running - while (!_isConnected.load()){ + while (_tryConnect.load()){ LINFO("Attempting to connect to server "<< _address << " on port " << _port); @@ -203,23 +303,36 @@ namespace openspace { //we're connected _isConnected.store(true); - - //and ready to start receiving messages - _isListening.store(true); + + //we no longer need to try to establish connection + _tryConnect.store(false); //start listening for communication - _receiveThread = new (std::nothrow) std::thread(&ParallelConnection::listenCommunication, this); + _listenThread = new (std::nothrow) std::thread(&ParallelConnection::listenCommunication, this); //start sending messages _sendThread = new (std::nothrow) std::thread(&ParallelConnection::sendLoop, this); - //and send authentication + //send authentication sendAuthentication(); } - //try to connect once per second +#ifdef __WIN32__ + //on windows: try to connect once per second std::this_thread::sleep_for(std::chrono::seconds(1)); +#else + if(!_isConnected.load()){ + //on unix disconnect and display error message if we're not connected + LERROR("Failed to establish a connection with server "<< _address << " on port " << _port<<", terminating connection."); + + //signal disconnect + signalDisconnect(); + + //stop loop + break; + } +#endif } //cleanup @@ -465,10 +578,7 @@ namespace openspace { LERROR("Failed to send message.\nError: " << _ERRNO << " detected in connection, disconnecting."); //stop all threads and signal that a disconnect should be performed - _performDisconnect.store(true); - _isConnected.store(false); - _isHost.store(false); - _isListening.store(false); + signalDisconnect(); } } @@ -539,12 +649,8 @@ namespace openspace { } } else{ - LERROR("Error " << _ERRNO << " detected in connection, disconnecting."); - //stop all threads and signal that a disconnect should be performed - _performDisconnect.store(true); - _isConnected.store(false); - _isHost.store(false); - _isListening.store(false); + LERROR("Error " << _ERRNO << " detected in connection, disconnecting."); + signalDisconnect(); } } @@ -627,8 +733,9 @@ namespace openspace { buffer.resize(headerSize()); int result; - //while we're still connected and listening - while (_isListening.load()){ + + //while we're still connected + while (_isConnected.load()){ //receive the first parts of a message result = receiveData(_clientSocket, buffer, headerSize(), 0); @@ -658,22 +765,13 @@ namespace openspace { LERROR("Error " << _ERRNO << " detected in connection, disconnecting!"); } - //stop all threads and signal that a disconnect should be performed - _performDisconnect.store(true); - _isConnected.store(false); - _isHost.store(false); - _isListening.store(false); + //signal that a disconnect should be performed + signalDisconnect(); break; } } } - - void ParallelConnection::update(double dt){ - if(_performDisconnect.load()){ - disconnect(); - } - } int ParallelConnection::receiveData(_SOCKET & socket, std::vector &buffer, int length, int flags){ int result = 0; @@ -698,31 +796,15 @@ namespace openspace { void ParallelConnection::setPort(const std::string &port){ _port = port; } - - std::string ParallelConnection::port(){ - return _port; - } - + void ParallelConnection::setAddress(const std::string &address){ _address = address; } - std::string ParallelConnection::address(){ - return _address; - } - void ParallelConnection::setName(const std::string& name){ _name = name; } - std::string ParallelConnection::name(){ - return _name; - } - - _SOCKET ParallelConnection::clientSocket(){ - return _clientSocket; - } - bool ParallelConnection::isHost(){ return _isHost.load(); } @@ -760,89 +842,6 @@ namespace openspace { queMessage(buffer); } - - void ParallelConnection::disconnect(){ - //we're disconnecting - _performDisconnect.store(false); - - if (_clientSocket != INVALID_SOCKET){ - - //must be run before trying to join communication threads, else the threads are stuck trying to receive data - closeSocket(); - - //tell broadcast thread to stop broadcasting - _isHost.store(false); - - //tell connection thread to stop listening - _isListening.store(false); - - //join receive thread and delete it - if (_receiveThread != nullptr){ - _receiveThread->join(); - delete _receiveThread; - _receiveThread = nullptr; - } - - //join broadcast thread and delete it - if (_broadcastThread != nullptr){ - _broadcastThread->join(); - delete _broadcastThread; - _broadcastThread = nullptr; - } - - //join send thread and delete it - if (_sendThread != nullptr){ - _sendThread->join(); - delete _sendThread; - _sendThread = nullptr; - } - - //tell connection thread that we are connected (to be able to join and delete thread) - _isConnected.store(true); - - //join connection thread and delete it - if (_connectionThread != nullptr){ - _connectionThread->join(); - delete _connectionThread; - _connectionThread = nullptr; - } - - //make sure to set us as NOT connected again, connection thread is now deleted - _isConnected.store(false); - -#if defined(__WIN32__) - //this line causes issues with SGCT since winsock dll file is unloaded upon call - //@TODO should this be here? -// WSACleanup(); -#endif - } - } - - void ParallelConnection::closeSocket(){ - - /* - Windows shutdown options - * SD_RECIEVE - * SD_SEND - * SD_BOTH - - Linux & Mac shutdown options - * SHUT_RD (Disables further receive operations) - * SHUT_WR (Disables further send operations) - * SHUT_RDWR (Disables further send and receive operations) - */ - -#ifdef __WIN32__ - shutdown(_clientSocket, SD_BOTH); - closesocket(_clientSocket); -#else - shutdown(_clientSocket, SHUT_RDWR); - close(_clientSocket); -#endif - - _clientSocket = INVALID_SOCKET; - } - bool ParallelConnection::initNetworkAPI(){ #if defined(__WIN32__) WSADATA wsaData; @@ -871,8 +870,8 @@ namespace openspace { void ParallelConnection::broadcast(){ - //while we're still the host - while (_isHost.load()){ + //while we're still connected and we're the host + while (_isConnected.load() && _isHost.load()){ //create a keyframe with current position and orientation of camera network::StreamDataKeyframe kf; diff --git a/src/network/parallelconnection_lua.inl b/src/network/parallelconnection_lua.inl index 0e135d9239..edcc4c1868 100644 --- a/src/network/parallelconnection_lua.inl +++ b/src/network/parallelconnection_lua.inl @@ -32,9 +32,7 @@ namespace luascriptfunctions { * Set the port for parallel connection */ int setPort(lua_State* L) { - if(!OsEng.isMaster()){ - return; - } + const bool isFunction = (lua_isfunction(L, -1) != 0); if (isFunction) { // If the top of the stack is a function, it is ourself @@ -46,7 +44,9 @@ int setPort(lua_State* L) { if (isNumber) { int value = lua_tonumber(L, -1); std::string port = std::to_string(value); - OsEng.parallelConnection()->setPort(port); + if(OsEng.isMaster()){ + OsEng.parallelConnection()->setPort(port); + } return 0; } else { @@ -59,9 +59,7 @@ int setPort(lua_State* L) { } int setAddress(lua_State* L) { - if(!OsEng.isMaster()){ - return; - } + const bool isFunction = (lua_isfunction(L, -1) != 0); if (isFunction) { // If the top of the stack is a function, it is ourself @@ -72,7 +70,9 @@ int setAddress(lua_State* L) { const int type = lua_type(L, -1); if (type == LUA_TSTRING) { std::string address = luaL_checkstring(L, -1); - OsEng.parallelConnection()->setAddress(address); + if(OsEng.isMaster()){ + OsEng.parallelConnection()->setAddress(address); + } return 0; } else { @@ -85,9 +85,7 @@ int setAddress(lua_State* L) { } int setPassword(lua_State* L) { - if(!OsEng.isMaster()){ - return; - } + const bool isFunction = (lua_isfunction(L, -1) != 0); if (isFunction) { // If the top of the stack is a function, it is ourself @@ -98,7 +96,9 @@ int setPassword(lua_State* L) { const int type = lua_type(L, -1); if (type == LUA_TSTRING) { std::string pwd = luaL_checkstring(L, -1); - OsEng.parallelConnection()->setPassword(pwd); + if(OsEng.isMaster()){ + OsEng.parallelConnection()->setPassword(pwd); + } return 0; } else { @@ -111,9 +111,7 @@ int setPassword(lua_State* L) { } int setDisplayName(lua_State* L) { - if(!OsEng.isMaster()){ - return; - } + const bool isFunction = (lua_isfunction(L, -1) != 0); if (isFunction) { // If the top of the stack is a function, it is ourself @@ -124,7 +122,9 @@ int setDisplayName(lua_State* L) { const int type = lua_type(L, -1); if (type == LUA_TSTRING) { std::string name = luaL_checkstring(L, -1); - OsEng.parallelConnection()->setName(name); + if(OsEng.isMaster()){ + OsEng.parallelConnection()->setName(name); + } return 0; } else { @@ -137,46 +137,46 @@ int setDisplayName(lua_State* L) { } int connect(lua_State* L) { - if(!OsEng.isMaster()){ - return; - } + int nArguments = lua_gettop(L); if (nArguments != 0) return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); - OsEng.parallelConnection()->clientConnect(); + if(OsEng.isMaster()){ + OsEng.parallelConnection()->clientConnect(); + } return 0; } int disconnect(lua_State* L) { - if(!OsEng.isMaster()){ - return; - } + int nArguments = lua_gettop(L); if (nArguments != 0) return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); - OsEng.parallelConnection()->disconnect(); + if(OsEng.isMaster()){ + OsEng.parallelConnection()->signalDisconnect(); + } return 0; } int requestHostship(lua_State* L) { - if(!OsEng.isMaster()){ - return; - } + int nArguments = lua_gettop(L); if (nArguments != 0) return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); - OsEng.parallelConnection()->requestHostship(); + if(OsEng.isMaster()){ + OsEng.parallelConnection()->requestHostship(); + } return 0; } int initialized(lua_State* L) { - if(!OsEng.isMaster()){ - return; - } + int nArguments = lua_gettop(L); if (nArguments != 0) return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); - OsEng.parallelConnection()->initDone(); + if(OsEng.isMaster()){ + OsEng.parallelConnection()->initDone(); + } return 0; } From 41dd524d387d5d59d45ee214e476a99d8277301d Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Fri, 3 Jul 2015 15:30:34 +0200 Subject: [PATCH 257/329] changes to how mutex protection of camera works. moved mutex locking of camera variables into camera class and changed where locks occur inte interaction handler to avoid deadlocks --- src/interaction/interactionhandler.cpp | 81 +++++++++++++++++--------- src/util/camera.cpp | 17 +++++- 2 files changed, 68 insertions(+), 30 deletions(-) diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index 1b6ecc297f..38dc20a29d 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -350,41 +350,63 @@ void InteractionHandler::update(double deltaTime) { _deltaTime = deltaTime; _mouseController->update(deltaTime); + bool hasKeys = false; + psc pos; + glm::quat q; + _keyframeMutex.lock(); if (_keyframes.size() > 4){ //wait until enough samples are buffered + hasKeys = true; ghoul::Interpolator positionInterpCR; ghoul::Interpolator positionInterpLin; ghoul::Interpolator quatInterpLin; + + openspace::network::StreamDataKeyframe p0, p1, p2, p3; + + p0 = _keyframes[0]; + p1 = _keyframes[1]; + p2 = _keyframes[2]; + p3 = _keyframes[3]; //interval check - if (_currentKeyframeTime < _keyframes[1]._timeStamp){ - _currentKeyframeTime = _keyframes[1]._timeStamp; + if (_currentKeyframeTime < p1._timeStamp){ + _currentKeyframeTime = p1._timeStamp; } - double t0 = _keyframes[1]._timeStamp; - double t1 = _keyframes[2]._timeStamp; + double t0 = p1._timeStamp; + double t1 = p2._timeStamp; double fact = (_currentKeyframeTime - t0) / (t1 - t0); + + //glm::dvec4 v = positionInterpCR.interpolate(fact, _keyframes[0]._position.dvec4(), _keyframes[1]._position.dvec4(), _keyframes[2]._position.dvec4(), _keyframes[3]._position.dvec4()); - glm::dvec4 v = positionInterpLin.interpolate(fact, _keyframes[1]._position.dvec4(), _keyframes[2]._position.dvec4()); - psc pos(v.x, v.y, v.z, v.w); - - glm::quat q = quatInterpLin.interpolate(fact, _keyframes[1]._viewRotationQuat, _keyframes[2]._viewRotationQuat); - + glm::dvec4 v = positionInterpLin.interpolate(fact, p1._position.dvec4(), p2._position.dvec4()); + + pos = psc(v.x, v.y, v.z, v.w); + q = quatInterpLin.interpolate(fact, p1._viewRotationQuat, p2._viewRotationQuat); + + //we're done with this sample interval + if (_currentKeyframeTime >= p2._timeStamp){ + _keyframes.erase(_keyframes.begin()); + _currentKeyframeTime = p1._timeStamp; + } + + _currentKeyframeTime += deltaTime; + + } + + _keyframeMutex.unlock(); + + if(hasKeys){ _camera->setPosition(pos); _camera->setViewRotationMatrix(glm::mat4_cast(q)); - - _currentKeyframeTime += deltaTime; - - //we're done with this sample interval - if (_currentKeyframeTime >= _keyframes[2]._timeStamp){ - _keyframes.erase(_keyframes.begin()); - _currentKeyframeTime = _keyframes[1]._timeStamp; - } } - _keyframeMutex.unlock(); + + + + } void InteractionHandler::setFocusNode(SceneGraphNode* node) { @@ -498,11 +520,12 @@ void InteractionHandler::orbit(const float &dx, const float &dy, const float &dz target = relative; } - _camera->setFocusPosition(origin); + unlockControls(); + + _camera->setFocusPosition(origin); _camera->setPosition(target); _camera->rotate(glm::quat_cast(transform)); - unlockControls(); } //void InteractionHandler::distance(const float &d){ @@ -543,16 +566,19 @@ void InteractionHandler::orbitDelta(const glm::quat& rotation) //relative_origin_coordinate = relative_origin_coordinate.vec4() * glm::inverse(rotation); relative_origin_coordinate = glm::inverse(rotation) * relative_origin_coordinate.vec4(); relative = relative_origin_coordinate + origin; - + glm::mat4 la = glm::lookAt(_camera->position().vec3(), origin.vec3(), glm::rotate(rotation, _camera->lookUpVector())); + + unlockControls(); + _camera->setPosition(relative); //camera_->rotate(rotation); //camera_->setRotation(glm::mat4_cast(rotation)); - glm::mat4 la = glm::lookAt(_camera->position().vec3(), origin.vec3(), glm::rotate(rotation, _camera->lookUpVector())); + _camera->setRotation(la); //camera_->setLookUpVector(); - unlockControls(); + } //<<<<<<< HEAD @@ -571,9 +597,7 @@ void InteractionHandler::orbitDelta(const glm::quat& rotation) //======= void InteractionHandler::rotateDelta(const glm::quat& rotation) { - lockControls(); _camera->rotate(rotation); - unlockControls(); } void InteractionHandler::distanceDelta(const PowerScaledScalar& distance, size_t iterations) @@ -586,7 +610,9 @@ void InteractionHandler::distanceDelta(const PowerScaledScalar& distance, size_t psc relative = _camera->position(); const psc origin = (_focusNode) ? _focusNode->worldPosition() : psc(); - psc relative_origin_coordinate = relative - origin; + unlockControls(); + + psc relative_origin_coordinate = relative - origin; const glm::vec3 dir(relative_origin_coordinate.direction()); glm::vec3 newdir = dir * distance[0]; @@ -604,11 +630,8 @@ void InteractionHandler::distanceDelta(const PowerScaledScalar& distance, size_t // update only if on the same side of the origin if (glm::angle(newdir, dir) < 90.0f) { _camera->setPosition(relative); - unlockControls(); - } else { - unlockControls(); PowerScaledScalar d2 = distance; d2[0] *= 0.75f; d2[1] *= 0.85f; diff --git a/src/util/camera.cpp b/src/util/camera.cpp index e99d7eaf29..3e4ef52b1d 100644 --- a/src/util/camera.cpp +++ b/src/util/camera.cpp @@ -32,6 +32,7 @@ #include #include + namespace openspace { @@ -64,12 +65,12 @@ Camera::~Camera() void Camera::setPosition(psc pos) { + std::lock_guard _lock(_mutex); _localPosition = std::move(pos); } const psc& Camera::position() const { - //return _localPosition; return _syncedPosition; } @@ -78,6 +79,7 @@ const psc& Camera::unsynchedPosition() const{ } void Camera::setModelMatrix(glm::mat4 modelMatrix){ + std::lock_guard _lock(_mutex); _modelMatrix = std::move(modelMatrix); } @@ -86,6 +88,7 @@ const glm::mat4& Camera::modelMatrix() const{ } void Camera::setViewMatrix(glm::mat4 viewMatrix){ + std::lock_guard _lock(_mutex); _viewMatrix = std::move(viewMatrix); } @@ -94,6 +97,7 @@ const glm::mat4& Camera::viewMatrix() const{ } void Camera::setProjectionMatrix(glm::mat4 projectionMatrix){ + std::lock_guard _lock(_mutex); _projectionMatrix = std::move(projectionMatrix); } @@ -103,6 +107,7 @@ const glm::mat4& Camera::projectionMatrix() const{ void Camera::setViewProjectionMatrix(glm::mat4 viewProjectionMatrix) { + std::lock_guard _lock(_mutex); _viewProjectionMatrix = std::move(viewProjectionMatrix); } @@ -113,6 +118,7 @@ const glm::mat4& Camera::viewProjectionMatrix() const void Camera::setCameraDirection(glm::vec3 cameraDirection) { + std::lock_guard _lock(_mutex); _cameraDirection = std::move(cameraDirection); } @@ -122,6 +128,7 @@ glm::vec3 Camera::cameraDirection() const } void Camera::setViewRotationMatrix(glm::mat4 m) { + std::lock_guard _lock(_mutex); _localViewRotationMatrix = m; } @@ -133,6 +140,7 @@ const glm::mat4& Camera::viewRotationMatrix() const void Camera::compileViewRotationMatrix() { + std::lock_guard _lock(_mutex); // convert from quaternion to rotation matrix using glm //_viewRotationMatrix = glm::mat4_cast(_viewRotation); @@ -145,6 +153,7 @@ void Camera::compileViewRotationMatrix() void Camera::rotate(const glm::quat& rotation) { + std::lock_guard _lock(_mutex); glm::mat4 tmp = glm::mat4_cast(rotation); _localViewRotationMatrix = _localViewRotationMatrix * tmp; //_viewRotation = rotation * _viewRotation; @@ -153,12 +162,14 @@ void Camera::rotate(const glm::quat& rotation) void Camera::setRotation(glm::quat rotation) { + std::lock_guard _lock(_mutex); //_viewRotation = glm::normalize(std::move(rotation)); _localViewRotationMatrix = glm::mat4_cast(rotation); } void Camera::setRotation(glm::mat4 rotation) { + std::lock_guard _lock(_mutex); _localViewRotationMatrix = std::move(rotation); } @@ -168,6 +179,7 @@ void Camera::setRotation(glm::mat4 rotation) //} void Camera::setFocusPosition(psc pos){ + std::lock_guard _lock(_mutex); _focusPosition = pos; } @@ -193,12 +205,14 @@ const float& Camera::sinMaxFov() const void Camera::setMaxFov(float fov) { + std::lock_guard _lock(_mutex); _maxFov = fov; _sinMaxFov = sin(_maxFov); } void Camera::setScaling(glm::vec2 scaling) { + std::lock_guard _lock(_mutex); _localScaling = std::move(scaling); } @@ -210,6 +224,7 @@ const glm::vec2& Camera::scaling() const void Camera::setLookUpVector(glm::vec3 lookUp) { + std::lock_guard _lock(_mutex); _lookUp = std::move(lookUp); } From 0cd9e6b6878f2f0d440733c678ea0d3429762589 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Fri, 3 Jul 2015 16:17:48 +0200 Subject: [PATCH 258/329] script execution is no longer bound by mutexes --- src/scripting/scriptengine.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/scripting/scriptengine.cpp b/src/scripting/scriptengine.cpp index f5b52c2f5a..bffbf962f5 100644 --- a/src/scripting/scriptengine.cpp +++ b/src/scripting/scriptengine.cpp @@ -610,12 +610,19 @@ void ScriptEngine::deserialize(SyncBuffer* syncBuffer){ } void ScriptEngine::postSynchronizationPreDraw(){ + + std::vector scripts; + _mutex.lock(); - while(!_receivedScripts.empty()){ - runScript(_receivedScripts.back()); - _receivedScripts.pop_back(); - } + scripts.assign(_receivedScripts.begin(), _receivedScripts.end()); + _receivedScripts.clear(); _mutex.unlock(); + + while (!scripts.empty()){ + runScript(scripts.back()); + scripts.pop_back(); + } + } void ScriptEngine::preSynchronization(){ From 4e1d4d6820489e67b80d6b50a89465f08de73337 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 3 Jul 2015 18:55:39 +0200 Subject: [PATCH 259/329] Fix libtorrent CMakeLists to always enable DHT mode Close torrents when SyncWidget is closed --- apps/Launcher/ext/libtorrent/CMakeLists.txt | 2 +- apps/Launcher/syncwidget.cpp | 12 ++++++++++++ apps/Launcher/syncwidget.h | 2 ++ data | 2 +- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/apps/Launcher/ext/libtorrent/CMakeLists.txt b/apps/Launcher/ext/libtorrent/CMakeLists.txt index b320846e13..18b052bc1b 100644 --- a/apps/Launcher/ext/libtorrent/CMakeLists.txt +++ b/apps/Launcher/ext/libtorrent/CMakeLists.txt @@ -121,7 +121,7 @@ set(ed25519_sources set(includes include ed25519/src) option(LIBTORRENT_pool-allocators "Uses a pool allocator for disk and piece buffers" ON) -option(LIBTORRENT_dht "enable support for Mainline DHT" OFF) +option(LIBTORRENT_dht "enable support for Mainline DHT" ON) option(LIBTORRENT_unicode "enable unicode support" ON) set(CMAKE_CONFIGURATION_TYPES Debug Release RelWithDebInfo) diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 6ad5409e4f..7b0181bbfa 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -138,11 +138,22 @@ SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) } SyncWidget::~SyncWidget() { + libtorrent::entry dht = _session->dht_state(); + std::string s = dht.to_string(); + openspace::DownloadManager::deinitialize(); ghoul::deinitialize(); delete _session; } +void SyncWidget::closeEvent(QCloseEvent* event) { + std::vector handles = _session->get_torrents(); + for (libtorrent::torrent_handle h : handles) { + h.flush_cache(); + _session->remove_torrent(h); + } +} + void SyncWidget::setSceneFiles(QMap sceneFiles) { _sceneFiles = std::move(sceneFiles); QStringList keys = _sceneFiles.keys(); @@ -601,3 +612,4 @@ void SyncWidget::handleFileFutureAddition( _futuresToAdd.insert(_futuresToAdd.end(), futures.begin(), futures.end()); _mutex.clear(); } + diff --git a/apps/Launcher/syncwidget.h b/apps/Launcher/syncwidget.h index e1e560b021..c83a55064a 100644 --- a/apps/Launcher/syncwidget.h +++ b/apps/Launcher/syncwidget.h @@ -58,6 +58,8 @@ private slots: void syncButtonPressed(); void handleTimer(); + void closeEvent(QCloseEvent* event); + private: struct DirectFile { QString module; diff --git a/data b/data index e234fc3176..8665ad57ba 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit e234fc317660bf8b593a23ca505892a44a17d70e +Subproject commit 8665ad57baba608771bfb0233f4502b941f516a1 From af814475581b03477029f9e0ec2e4316fcdd3339 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 3 Jul 2015 19:26:04 +0200 Subject: [PATCH 260/329] Store libtorrent dht settings between executions of Launcher --- apps/Launcher/syncwidget.cpp | 45 +++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 7b0181bbfa..d43e80dfc6 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -26,7 +26,6 @@ #include "infowidget.h" - #include #include #include @@ -51,9 +50,13 @@ #include #include +#include + namespace { const std::string _loggerCat = "SyncWidget"; + const std::string _configurationFile = "libtorrent.config"; + const int nColumns = 3; const int DownloadApplicationVersion = 1; @@ -116,6 +119,7 @@ SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) ghoul::initialize(); openspace::DownloadManager::initialize("http://openspace.itn.liu.se/data/request", DownloadApplicationVersion); + libtorrent::error_code ec; _session->listen_on(std::make_pair(20280, 20290), ec); if (ec) { @@ -123,13 +127,32 @@ SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) return; } _session->start_upnp(); - _session->start_dht(); + + std::ifstream file(_configurationFile); + if (!file.fail()) { + union { + uint32_t value; + std::array data; + } size; + + file.read(size.data.data(), sizeof(uint32_t)); + std::vector buffer(size.value); + file.read(buffer.data(), size.value); + file.close(); + + libtorrent::entry e = libtorrent::bdecode(buffer.begin(), buffer.end()); + _session->start_dht(e); + } + else + _session->start_dht(); _session->add_dht_router({ "router.utorrent.com", 6881 }); _session->add_dht_router({ "dht.transmissionbt.com", 6881 }); _session->add_dht_router({ "router.bittorrent.com", 6881 }); _session->add_dht_router({ "router.bitcomet.com", 6881 }); + + QTimer* timer = new QTimer(this); QObject::connect(timer, SIGNAL(timeout()), this, SLOT(handleTimer())); timer->start(100); @@ -139,7 +162,19 @@ SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) SyncWidget::~SyncWidget() { libtorrent::entry dht = _session->dht_state(); - std::string s = dht.to_string(); + + std::vector buffer; + libtorrent::bencode(std::back_inserter(buffer), dht); + + std::ofstream f(_configurationFile); + + union { + uint32_t value; + std::array data; + } size; + size.value = buffer.size(); + f.write(size.data.data(), sizeof(uint32_t)); + f.write(buffer.data(), buffer.size()); openspace::DownloadManager::deinitialize(); ghoul::deinitialize(); @@ -294,6 +329,10 @@ void SyncWidget::syncButtonPressed() { ghoul::Dictionary modules; bool success = sceneDictionary.getValue("Modules", modules); + if (!success) { + LERROR("Could not find 'Modules'"); + return; + } QStringList modulesList; for (int i = 1; i <= modules.size(); ++i) { From 16ccd7550f68c872b8bce046e879a6100c11cbfa Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 3 Jul 2015 19:54:14 +0200 Subject: [PATCH 261/329] Set the libtorrent settings correctly --- apps/Launcher/syncwidget.cpp | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index d43e80dfc6..d2abd0d983 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -26,6 +26,8 @@ #include "infowidget.h" +#include + #include #include #include @@ -122,6 +124,21 @@ SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) libtorrent::error_code ec; _session->listen_on(std::make_pair(20280, 20290), ec); + + libtorrent::session_settings settings = _session->settings(); + settings.user_agent = + "OpenSpace/" + + std::to_string(OPENSPACE_VERSION_MAJOR) + "." + + std::to_string(OPENSPACE_VERSION_MINOR) + "." + + std::to_string(OPENSPACE_VERSION_PATCH); + settings.allow_multiple_connections_per_ip = true; + settings.ignore_limits_on_local_network = true; + settings.connection_speed = 20; + settings.active_downloads = -1; + settings.active_seeds = -1; + settings.active_limit = 30; + settings.dht_announce_interval = 60; + if (ec) { LFATAL("Failed to open socket: " << ec.message()); return; @@ -308,9 +325,11 @@ void SyncWidget::handleTorrentFiles() { continue; } - InfoWidget* w = new InfoWidget(f.file, h.status().total_wanted); - _downloadLayout->insertWidget(_downloadLayout->count() - 1, w); - _torrentInfoWidgetMap[h] = w; + if (_torrentInfoWidgetMap.find(h) == _torrentInfoWidgetMap.end()) { + InfoWidget* w = new InfoWidget(f.file, h.status().total_wanted); + _downloadLayout->insertWidget(_downloadLayout->count() - 1, w); + _torrentInfoWidgetMap[h] = w; + } FileSys.setCurrentDirectory(d); } From 1510aca4488bf5d36ffb9e1852e5368815f1d5ad Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 4 Jul 2015 13:22:03 +0200 Subject: [PATCH 262/329] Add stylesheets and general improvements --- apps/Launcher/ext/libtorrent/CMakeLists.txt | 6 +- apps/Launcher/infowidget.cpp | 30 +- apps/Launcher/main.cpp | 411 ++++++++++++++++---- apps/Launcher/mainwindow.cpp | 15 +- apps/Launcher/syncwidget.cpp | 17 +- support/cmake/handle_external_library.cmake | 3 +- 6 files changed, 379 insertions(+), 103 deletions(-) diff --git a/apps/Launcher/ext/libtorrent/CMakeLists.txt b/apps/Launcher/ext/libtorrent/CMakeLists.txt index 18b052bc1b..1cbdeb7355 100644 --- a/apps/Launcher/ext/libtorrent/CMakeLists.txt +++ b/apps/Launcher/ext/libtorrent/CMakeLists.txt @@ -1,7 +1,7 @@ -cmake_minimum_required(VERSION 2.6) +cmake_minimum_required(VERSION 3.0) project(libtorrent) -set (SOVERSION "8") -set (VERSION "1.0.5") +set(SOVERSION "8") +set(VERSION "1.0.5") set(sources web_connection_base diff --git a/apps/Launcher/infowidget.cpp b/apps/Launcher/infowidget.cpp index 11ea196638..39cea3db00 100644 --- a/apps/Launcher/infowidget.cpp +++ b/apps/Launcher/infowidget.cpp @@ -43,26 +43,36 @@ InfoWidget::InfoWidget(QString name, int totalBytes) setFixedHeight(100); QGridLayout* layout = new QGridLayout; - layout->setVerticalSpacing(0); + //layout->setVerticalSpacing(0); layout->setHorizontalSpacing(10); layout->setContentsMargins(0, 0, 0, 0); _name = new QLabel(name); - layout->addWidget(_name, 0, 0); + _name->setObjectName("Name"); + //_name->setMaximumWidth(300); + _name->setFixedWidth(450); + layout->addWidget(_name, 0, 0, 1, 2); + layout->setRowStretch(1, 10); _bytes = new QLabel(""); - layout->addWidget(_bytes, 1, 0); + _bytes->setObjectName("Bytes"); + layout->addWidget(_bytes, 2, 0); _progress = new QProgressBar; - layout->addWidget(_progress, 1, 1); + _progress->setTextVisible(false); + _progress->setFixedWidth(285); + layout->addWidget(_progress, 2, 1); _messagesLeft = new QLabel(""); + _messagesLeft->setObjectName("MessageLeft"); _messagesCenter = new QLabel(""); + _messagesCenter->setObjectName("MessageCenter"); _messagesRight = new QLabel(""); - - layout->addWidget(_messagesLeft, 2, 0, 1, 2); - layout->addWidget(_messagesCenter, 2, 0, 1, 2, Qt::AlignCenter); - layout->addWidget(_messagesRight, 2, 0, 1, 2, Qt::AlignRight); + _messagesRight->setObjectName("MessageRight"); + + layout->addWidget(_messagesLeft, 3, 0, 1, 2); + layout->addWidget(_messagesCenter, 3, 0, 1, 2, Qt::AlignCenter); + layout->addWidget(_messagesRight, 3, 0, 1, 2, Qt::AlignRight); setLayout(layout); } @@ -99,13 +109,13 @@ void InfoWidget::update(libtorrent::torrent_status s) { if (bytesPerSecond > 0 && remainingBytes > 0) { float seconds = static_cast(remainingBytes) / bytesPerSecond; - QString left = "Time remaining %1 s"; + QString left = "Remaining: %1 s"; _messagesLeft->setText(left.arg(static_cast(seconds))); QString center = "Peers: %1 (%2) | Seeds: %3 (%4)"; _messagesCenter->setText(center.arg(s.num_peers).arg(s.list_peers).arg(s.num_seeds).arg(s.list_seeds)); - QString right = "Download Rate: %1 KiB/s"; + QString right = "%1 KiB/s"; _messagesRight->setText(right.arg(bytesPerSecond / 1024)); } else diff --git a/apps/Launcher/main.cpp b/apps/Launcher/main.cpp index b4c17f6d04..28f8634105 100644 --- a/apps/Launcher/main.cpp +++ b/apps/Launcher/main.cpp @@ -24,131 +24,376 @@ #include +#include + #include "mainwindow.h" -//static const QString style = R"style( -//QWidget { -// background-color: rgb(80, 80, 80); -// font-family: Helvetica; +#include + +// Copyright 2013 Emanuel Claesson +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//*/ +// +///* +// COLOR_DARK = #191919 +// COLOR_MEDIUM = #353535 +// COLOR_MEDLIGHT = #5A5A5A +// COLOR_LIGHT = #DDDDDD +// COLOR_ACCENT = #3D7848 +//*/ +// +//* { +// background: #191919; +// color: #DDDDDD; +// border: 1px solid #5A5A5A; +//} +// +//QWidget::item:selected { +// background: #3D7848; +//} +// +//QCheckBox, QRadioButton { +// border: none; +//} +// +//QRadioButton::indicator, QCheckBox::indicator { +// width: 13px; +// height: 13px; +//} +// +//QRadioButton::indicator::unchecked, QCheckBox::indicator::unchecked { +// border: 1px solid #5A5A5A; +// background: none; +//} +// +//QRadioButton::indicator:unchecked:hover, QCheckBox::indicator:unchecked:hover { +// border: 1px solid #DDDDDD; +//} +// +//QRadioButton::indicator::checked, QCheckBox::indicator::checked { +// border: 1px solid #5A5A5A; +// background: #5A5A5A; +//} +// +//QRadioButton::indicator:checked:hover, QCheckBox::indicator:checked:hover { +// border: 1px solid #DDDDDD; +// background: #DDDDDD; //} // //QGroupBox { -// background-color: qlineargradient( -// x1: 0, y1: 0, x2: 0, y2: 1, -// stop: 0 #858585, -// stop: 1 #959595); -// border: 2px solid gray; -// border-radius: 5px; -// margin-top: 4ex; -// font-size: bold 12px; +// margin-top: 6px; //} // //QGroupBox::title { -// background-color: #E0E0E0; -// border: 2px solid gray; -// border-radius: 5px; -// subcontrol-origin: margin; -// subcontrol-position: top center; -// padding: 0 10px; +// top: -7px; +// left: 7px; //} // -//QLineEdit { -// color: lightgray; +//QScrollBar { +// border: 1px solid #5A5A5A; +// background: #191919; +//} +// +//QScrollBar:horizontal { +// height: 15px; +// margin: 0px 0px 0px 32px; +//} +// +//QScrollBar:vertical { +// width: 15px; +// margin: 32px 0px 0px 0px; +//} +// +//QScrollBar::handle { +// background: #353535; +// border: 1px solid #5A5A5A; +//} +// +//QScrollBar::handle:horizontal { +// border-width: 0px 1px 0px 1px; +//} +// +//QScrollBar::handle:vertical { +// border-width: 1px 0px 1px 0px; +//} +// +//QScrollBar::handle:horizontal { +// min-width: 20px; +//} +// +//QScrollBar::handle:vertical { +// min-height: 20px; +//} +// +//QScrollBar::add-line, QScrollBar::sub-line { +// background:#353535; +// border: 1px solid #5A5A5A; +// subcontrol-origin: margin; +//} +// +//QScrollBar::add-line { +// position: absolute; +//} +// +//QScrollBar::add-line:horizontal { +// width: 15px; +// subcontrol-position: left; +// left: 15px; +//} +// +//QScrollBar::add-line:vertical { +// height: 15px; +// subcontrol-position: top; +// top: 15px; +//} +// +//QScrollBar::sub-line:horizontal { +// width: 15px; +// subcontrol-position: top left; +//} +// +//QScrollBar::sub-line:vertical { +// height: 15px; +// subcontrol-position: top; +//} +// +//QScrollBar:left-arrow, QScrollBar::right-arrow, QScrollBar::up-arrow, QScrollBar::down-arrow { +// border: 1px solid #5A5A5A; +// width: 3px; +// height: 3px; +//} +// +//QScrollBar::add-page, QScrollBar::sub-page { +// background: none; +//} +// +//QAbstractButton:hover { +// background: #353535; +//} +// +//QAbstractButton:pressed { +// background: #5A5A5A; +//} +// +//QAbstractItemView { +// show-decoration-selected: 1; +// selection-background-color: #3D7848; +// selection-color: #DDDDDD; +// alternate-background-color: #353535; +//} +// +//QHeaderView { +// border: 1px solid #5A5A5A; +//} +// +//QHeaderView::section { +// background: #191919; +// border: 1px solid #5A5A5A; +// padding: 4px; +//} +// +//QHeaderView::section:selected, QHeaderView::section::checked { +// background: #353535; +//} +// +//QTableView { +// gridline-color: #5A5A5A; +//} +// +//QTabBar { +// margin-left: 2px; +//} +// +//QTabBar::tab { +// border-radius: 0px; +// padding: 4px; +// margin: 4px; +//} +// +//QTabBar::tab:selected { +// background: #353535; +//} +// +//QComboBox::down-arrow { +// border: 1px solid #5A5A5A; +// background: #353535; +//} +// +//QComboBox::drop-down { +// border: 1px solid #5A5A5A; +// background: #353535; +//} +// +//QComboBox::down-arrow { +// width: 3px; +// height: 3px; +// border: 1px solid #5A5A5A; +//} +// +//QAbstractSpinBox { +// padding-right: 15px; +//} +// +//QAbstractSpinBox::up-button, QAbstractSpinBox::down-button { +// border: 1px solid #5A5A5A; +// background: #353535; +// subcontrol-origin: border; +//} +// +//QAbstractSpinBox::up-arrow, QAbstractSpinBox::down-arrow { +// width: 3px; +// height: 3px; +// border: 1px solid #5A5A5A; +//} +// +//QSlider { +// border: none; //} // //QSlider::groove:horizontal { -// border: 1px solid #999999; -// height: 8px; /* the groove expands to the size of the slider by default. by giving it a height, it has a fixed size */ -// background: qlineargradient( -// x1:0, y1:0, x2:1, y2:0, -// stop:0 #c4c4c4, -// stop:0.5 #555555, -// stop:1 #c4c4c4 -// ); -// margin: 2px 0; +// height: 5px; +// margin: 4px 0px 4px 0px; +//} +// +//QSlider::groove:vertical { +// width: 5px; +// margin: 0px 4px 0px 4px; +//} +// +//QSlider::handle { +// border: 1px solid #5A5A5A; +// background: #353535; //} // //QSlider::handle:horizontal { -// background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #b4b4b4, stop:1 #8f8f8f); -// border: 1px solid #5c5c5c; -// width: 18px; -// margin: -2px 0; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ -// border-radius: 3px; +// width: 15px; +// margin: -4px 0px -4px 0px; //} // -//QPushButton { -// background-color: lightgray; -// border-style: outset; -// border-width: 0.5px; -// border-radius: 5px; -// border-color: black; -// font: bold 12px; -// min-width: 10em; +//QSlider::handle:vertical { +// height: 15px; +// margin: 0px -4px 0px -4px; //} // -//QPushButton#connection { -// background-color: lightgreen; +//QSlider::add-page:vertical, QSlider::sub-page:horizontal { +// background: #3D7848; //} // -//QPushButton#connection:pressed { -// background-color: green; +//QSlider::sub-page:vertical, QSlider::add-page:horizontal { +// background: #353535; //} // -// -//QPushButton#pause, QPushButton#play { -// padding: 5px; +//QLabel { +// border: none; //} // -//QPushButton#pause:pressed, QPushButton#play:pressed, QPushButton:pressed { -// background-color: darkgray; -// border-style: inset; +//QProgressBar { +// text-align: center; //} // -//QCombobox { -// border: 1px solid gray; -// border-radius: 3px; -// padding: 1px 18px 1px 3px; -// min-width: 6em; +//QProgressBar::chunk { +// width: 1px; +// background-color: #3D7848; //} // -//QComboBox:editable { -// background: lightgrey; +//QMenu::separator { +// background: #353535; //} -// -//QComboBox QAbstractItemView { -// border: 2px solid darkgray; -// border-radius: 5px; -// background-color: #a8a8a8; -// selection-background-color: #a8a8a8; +//)style"; + +//static const QString style = R"style( +//QWidget { +// font-family: Helvetica; //} -// -//QLabel#label { -// font-size: 13px; -// background-color: transparent; -// font-variant: small-caps; +//QWidget#MainWindow, QTextEdit, QWidget#SyncWidget, QWidget#DownloadArea { +// background-color: rgb(40, 40, 40); //} -// -//QLabel#value { -// font-family: monospace; -// font-weight: bold; -// font-size: 14px; -// background-color: transparent; +//QTextEdit, QLabel, QComboBox, QCheckBox { +// color: white; +// font-size: 12px; //} -// -//QWidget#background { -// background-color: transparent; -//} -// //QTextEdit { -// font-family: monospace; +// border-width: 2px 2px 0px 0px; +// border-style: solid; +// background-color: rgb(60, 60, 60); +//} +//QPushButton { +// background-color: +// qlineargradient( +// x1: 0, y1: 0, x2: 0, y2: 1, +// stop: 0 white, +// stop: 1 #505050 +// ); +// border-style: solid; +// border-color: black; +// border-width: 1px; +// font-size: 11px; +// min-height: 17.5px; +//} +//QComboBox { +// background-color: rgb(60, 60, 60); +// min-height: 20px; +//} +//QComboBox:focus, QComboBox:focus QAbstractItemView { +// color: white; +// background-color: rgb(60, 60, 60); +// selection-background-color: rgb(75, 75, 75); +//} +//QCheckBox { +// border: none; +//} +//QCheckBox::indicator { +// width: 13px; +// height: 13px; +//} +//QCheckBox::indicator::unchecked { +// border: 1px solid #5A5A5A; +// background: transparent; +//} +//QCheckBox::indicator:unchecked:hover { +// border: 1px solid #DDDDDD; +//} +//QCheckBox::indicator::checked { +// border: 1px solid #666666; +// background: #666666; +//} +//QCheckBox::indicator:checked:hover { +// border: 1px solid #DDDDDD; +// background: #555555; +//} +//QGroupBox, QScrollArea { +// border: 0px; //} //)style"; int main(int argc, char** argv) { QApplication app(argc, argv); -// app.setStyleSheet(style); MainWindow window; + + auto setStyle = [](const ghoul::filesystem::File& f) { + QFile file(QString::fromStdString(f.path())); + file.open(QIODevice::ReadOnly); + QTextStream in(&file); + QString style = in.readAll(); + qApp->setStyleSheet(style); + }; + + ghoul::filesystem::File f("stylesheet.css", false, setStyle); + + setStyle(f); + window.show(); return app.exec(); diff --git a/apps/Launcher/mainwindow.cpp b/apps/Launcher/mainwindow.cpp index 53f08c04f3..3ce0e50dbc 100644 --- a/apps/Launcher/mainwindow.cpp +++ b/apps/Launcher/mainwindow.cpp @@ -79,19 +79,28 @@ MainWindow::MainWindow() , _shortcutWidget(nullptr) , _syncWidget(nullptr) { + setObjectName("MainWindow"); setFixedSize(WindowSize); + //setContentsMargins(0, 0, 0, 0); QGridLayout* layout = new QGridLayout; + layout->setContentsMargins(0, 0, 0, 0); QLabel* image = new QLabel; + //image->setContentsMargins(0, 0, 0, 0); + image->setObjectName("Image"); QPixmap p = QPixmap(":/images/header.png"); image->setPixmap(p.scaledToWidth(WindowSize.width())); layout->addWidget(image, 0, 0, 1, 2); + _informationWidget = new QTextEdit(this); _informationWidget->setReadOnly(true); _informationWidget->setEnabled(false); layout->addWidget(_informationWidget, 1, 0, 2, 1); + layout->setRowStretch(1, 10); + layout->setColumnStretch(0, 4); + layout->setColumnStretch(1, 5); QWidget* container = new QWidget; { @@ -104,11 +113,13 @@ MainWindow::MainWindow() this, SLOT(shortcutButtonPressed()) ); layout->addWidget(shortcutButton, 0, 1); + + layout->setRowStretch(1, 10); QLabel* sceneSelectionLabel = new QLabel("Scenes:"); - layout->addWidget(sceneSelectionLabel, 1, 0); + layout->addWidget(sceneSelectionLabel, 2, 0); _scenes = new QComboBox; - layout->addWidget(_scenes); + layout->addWidget(_scenes, 2, 1); container->setLayout(layout); } diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index d2abd0d983..12d200ad74 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -74,7 +74,7 @@ namespace { const std::string VersionKey = "Version"; const bool OverwriteFiles = false; - const bool CleanInfoWidgets = true; + const bool CleanInfoWidgets = false; } SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) @@ -82,9 +82,11 @@ SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) , _sceneLayout(nullptr) , _session(new libtorrent::session) { + setObjectName("SyncWidget"); setFixedSize(500, 500); QBoxLayout* layout = new QVBoxLayout; + layout->setContentsMargins(0, 0, 0, 0); { QGroupBox* sceneBox = new QGroupBox; _sceneLayout = new QGridLayout; @@ -92,7 +94,8 @@ SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) layout->addWidget(sceneBox); } { - QPushButton* syncButton = new QPushButton("Synchronize Scenes"); + QPushButton* syncButton = new QPushButton("Synchronize Data"); + syncButton->setObjectName("SyncButton"); QObject::connect( syncButton, SIGNAL(clicked(bool)), this, SLOT(syncButtonPressed()) @@ -106,6 +109,7 @@ SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) area->setWidgetResizable(true); QWidget* w = new QWidget; + w->setObjectName("DownloadArea"); area->setWidget(w); _downloadLayout = new QVBoxLayout(w); @@ -213,7 +217,6 @@ void SyncWidget::setSceneFiles(QMap sceneFiles) { const QString& sceneName = keys[i]; QCheckBox* checkbox = new QCheckBox(sceneName); - checkbox->setChecked(true); _sceneLayout->addWidget(checkbox, i / nColumns, i % nColumns); @@ -326,7 +329,13 @@ void SyncWidget::handleTorrentFiles() { } if (_torrentInfoWidgetMap.find(h) == _torrentInfoWidgetMap.end()) { - InfoWidget* w = new InfoWidget(f.file, h.status().total_wanted); + QString fileString = f.file; + QString t = QString(".torrent"); + fileString.replace(fileString.indexOf(t), t.size(), ""); + + fileString = f.module + "/" + fileString; + + InfoWidget* w = new InfoWidget(fileString, h.status().total_wanted); _downloadLayout->insertWidget(_downloadLayout->count() - 1, w); _torrentInfoWidgetMap[h] = w; } diff --git a/support/cmake/handle_external_library.cmake b/support/cmake/handle_external_library.cmake index 27cd7af07f..e93bfd2ffd 100644 --- a/support/cmake/handle_external_library.cmake +++ b/support/cmake/handle_external_library.cmake @@ -33,9 +33,10 @@ function (include_external_library target_name library_name path) target_link_libraries(${target_name} ${library_name}) target_include_directories(${target_name} PUBLIC SYSTEM ${INCLUDE_DIR}) set_property(TARGET ${library_name} PROPERTY FOLDER "External") + target_compile_options(${library_name} PUBLIC "/MP") if (OPENSPACE_DISABLE_EXTERNAL_WARNINGS) if (MSVC) - target_compile_options(${library_name} PUBLIC "/W0" "/MP") + target_compile_options(${library_name} PUBLIC "/W0") else () target_compile_options(${library_name} PUBLIC "-w") endif () From 003a438602e4671ac509c68885b7004bb691946e Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 4 Jul 2015 15:41:43 +0200 Subject: [PATCH 263/329] Removed while loops from path_vs that caused driver issues with big w values Removed error that caused the postDraw method not to be called on slave nodes --- modules/base/rendering/renderablepath.cpp | 3 +- modules/base/shaders/path_fs.glsl | 3 +- modules/base/shaders/path_vs.glsl | 152 ++++++++++++---------- src/engine/openspaceengine.cpp | 2 +- 4 files changed, 88 insertions(+), 72 deletions(-) diff --git a/modules/base/rendering/renderablepath.cpp b/modules/base/rendering/renderablepath.cpp index 9ccdf8622b..300b34ecbf 100644 --- a/modules/base/rendering/renderablepath.cpp +++ b/modules/base/rendering/renderablepath.cpp @@ -186,6 +186,8 @@ void RenderablePath::render(const RenderData& data) { glDrawArrays(GL_POINTS, 0, static_cast(_vertexArray.size())); glBindVertexArray(0); + glDisable(GL_PROGRAM_POINT_SIZE); + _programObject->deactivate(); } @@ -197,7 +199,6 @@ void RenderablePath::update(const UpdateData& data) { calculatePath(_observer); sendToGPU(); _needsSweep = false; - return; } if (_programObject->isDirty()) diff --git a/modules/base/shaders/path_fs.glsl b/modules/base/shaders/path_fs.glsl index 60535c0aba..4db21096fd 100644 --- a/modules/base/shaders/path_fs.glsl +++ b/modules/base/shaders/path_fs.glsl @@ -23,6 +23,7 @@ ****************************************************************************************/ #version __CONTEXT__ + layout(location = 0) in vec4 vs_point_position; layout(location = 1) flat in int isHour; layout(location = 2) in vec4 vs_point_color; @@ -43,5 +44,3 @@ void main() { ABufferStruct_t frag = createGeometryFragment(diffuse, position, depth); addToBuffer(frag); } - - diff --git a/modules/base/shaders/path_vs.glsl b/modules/base/shaders/path_vs.glsl index 06f37f3876..3b1a47ef63 100644 --- a/modules/base/shaders/path_vs.glsl +++ b/modules/base/shaders/path_vs.glsl @@ -31,22 +31,26 @@ uniform vec4 lastPosition; //this function does not consider cases where w component is negative float psc_distance(vec4 v1, vec4 v2) { - // reduce position numbers - /*while(v1.w > 1 && v2.w > 1) { - v1.w -= 1; - v2.w -= 1; - } */ - // get position in vec3 - while(v1.w > 1) { - v1.xyz *= 10; - v1.w -= 1; - } - while(v2.w > 1 ) { - v2.xyz *= 10; - v2.w -= 1; - } - // using native distance function - return distance(v1.xyz, v2.xyz); + // reduce position numbers + /*while(v1.w > 1 && v2.w > 1) { + v1.w -= 1; + v2.w -= 1; + } */ + // get position in vec3 + if (v1.w > 1) { + float f = floor(v1.w); + v1.xyz *= pow(10, f); + v1.w -= f; + } + + if (v2.w > 1) { + float f = floor(v2.w); + v2.xyz *= pow(10, f); + v2.w -= f; + } + + // using native distance function + return distance(v1.xyz, v2.xyz); } layout(location = 0) in vec4 in_point_position; @@ -60,59 +64,71 @@ layout(location = 2) out vec4 vs_point_color; #include "PowerScaling/powerScaling_vs.hglsl" -void main() { +void main() { + vec4 gray = vec4(0.6f, 0.6f, 0.6f, 0.8f); + float cameraTooFar = 1 * pow(k, 10); + float bigPoint = 5.f; + float smallPoint = 2.f; + + vec4 tmp = in_point_position; + vec4 position = pscTransform(tmp, ModelTransform); + vs_point_position = tmp; + position = ViewProjection * position; + gl_Position = z_normalization(position); - vec4 gray = vec4(0.6f, 0.6f, 0.6f, 0.8f); - float cameraTooFar = 1 * pow(k, 10); - float bigPoint = 5.f; - float smallPoint = 2.f; - - vec4 tmp = in_point_position; - vec4 position = pscTransform(tmp, ModelTransform); - vs_point_position = tmp; - position = ViewProjection * position; - gl_Position = z_normalization(position); - - - int id = gl_VertexID; - float hour = mod(id, 4); - - vs_point_color.xyz = color; - vs_point_color[3] = 1.f; - - vec4 v1 = campos; - vec4 v2 = vs_point_position; - float cameraDistance = psc_distance(v1,v2); + + int id = gl_VertexID; + float hour = mod(id, 4); + + vs_point_color.xyz = color; + vs_point_color[3] = 1.f; - vec4 temp = in_point_position; - vec4 templast = lastPosition; - while(temp.w > 1) { - temp.xyz *= 10; - temp.w -= 1; - } - while(templast.w > 1) { - templast.xyz *= 10; - templast.w -= 1; - } - float observerDistance = length(temp.xyz); - float lastDistance = length(templast.xyz); - - if(hour > 0.1f) { - isHour = 0; - vs_point_color = gray; - gl_PointSize = bigPoint; - } - else { - isHour = 1; - gl_PointSize = bigPoint; - } - if (observerDistance > (lastDistance/20)) { - gl_PointSize = smallPoint; - //vs_point_color = gray; - } - /*if (cameraDistance > cameraTooFar ) { - vs_point_color[3] = 0.0f; - - }*/ + vec4 v1 = campos; + vec4 v2 = vs_point_position; + float cameraDistance = psc_distance(v1,v2); + + vec4 temp = in_point_position; + vec4 templast = lastPosition; + + if (temp.w > 1) { + float f = floor(temp.w); + temp.w -= f; + temp.xyz *= pow(10, f); + } + + if (templast.w > 1) { + float f = floor(templast.w); + templast.w -= f; + templast.xyz *= pow(10, f); + } + + // while(temp.w > 1) { + // temp.xyz *= 10; + // temp.w -= 1; + // } + // while(templast.w > 1) { + // templast.xyz *= 10; + // templast.w -= 1; + // } + float observerDistance = length(temp.xyz); + float lastDistance = length(templast.xyz); + + if(hour > 0.1f) { + isHour = 0; + vs_point_color = gray; + gl_PointSize = bigPoint; + } + else { + isHour = 1; + gl_PointSize = bigPoint; + } + if (observerDistance > (lastDistance/20)) { + gl_PointSize = smallPoint; + //vs_point_color = gray; + } + /*if (cameraDistance > cameraTooFar ) { + vs_point_color[3] = 0.0f; + + }*/ } \ No newline at end of file diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 8c101ce271..5f17fbc39e 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -684,7 +684,7 @@ void OpenSpaceEngine::render(const glm::mat4 &projectionMatrix, const glm::mat4 } void OpenSpaceEngine::postDraw() { - if (_isMaster) + //if (_isMaster) //_interactionHandler.unlockControls(); _renderEngine->postDraw(); From b8db1676478cdfab7a86eec8edd1207a0ef379ec Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 4 Jul 2015 20:26:38 +0200 Subject: [PATCH 264/329] Include stylesheet in main file --- apps/Launcher/infowidget.cpp | 2 - apps/Launcher/main.cpp | 494 ++++++++++------------------------- apps/Launcher/syncwidget.cpp | 2 +- 3 files changed, 136 insertions(+), 362 deletions(-) diff --git a/apps/Launcher/infowidget.cpp b/apps/Launcher/infowidget.cpp index 39cea3db00..a10d9430e6 100644 --- a/apps/Launcher/infowidget.cpp +++ b/apps/Launcher/infowidget.cpp @@ -43,9 +43,7 @@ InfoWidget::InfoWidget(QString name, int totalBytes) setFixedHeight(100); QGridLayout* layout = new QGridLayout; - //layout->setVerticalSpacing(0); layout->setHorizontalSpacing(10); - layout->setContentsMargins(0, 0, 0, 0); _name = new QLabel(name); _name->setObjectName("Name"); diff --git a/apps/Launcher/main.cpp b/apps/Launcher/main.cpp index 28f8634105..63a90a620e 100644 --- a/apps/Launcher/main.cpp +++ b/apps/Launcher/main.cpp @@ -30,370 +30,146 @@ #include -// Copyright 2013 Emanuel Claesson -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -//*/ -// -///* -// COLOR_DARK = #191919 -// COLOR_MEDIUM = #353535 -// COLOR_MEDLIGHT = #5A5A5A -// COLOR_LIGHT = #DDDDDD -// COLOR_ACCENT = #3D7848 -//*/ -// -//* { -// background: #191919; -// color: #DDDDDD; -// border: 1px solid #5A5A5A; -//} -// -//QWidget::item:selected { -// background: #3D7848; -//} -// -//QCheckBox, QRadioButton { -// border: none; -//} -// -//QRadioButton::indicator, QCheckBox::indicator { -// width: 13px; -// height: 13px; -//} -// -//QRadioButton::indicator::unchecked, QCheckBox::indicator::unchecked { -// border: 1px solid #5A5A5A; -// background: none; -//} -// -//QRadioButton::indicator:unchecked:hover, QCheckBox::indicator:unchecked:hover { -// border: 1px solid #DDDDDD; -//} -// -//QRadioButton::indicator::checked, QCheckBox::indicator::checked { -// border: 1px solid #5A5A5A; -// background: #5A5A5A; -//} -// -//QRadioButton::indicator:checked:hover, QCheckBox::indicator:checked:hover { -// border: 1px solid #DDDDDD; -// background: #DDDDDD; -//} -// -//QGroupBox { -// margin-top: 6px; -//} -// -//QGroupBox::title { -// top: -7px; -// left: 7px; -//} -// -//QScrollBar { -// border: 1px solid #5A5A5A; -// background: #191919; -//} -// -//QScrollBar:horizontal { -// height: 15px; -// margin: 0px 0px 0px 32px; -//} -// -//QScrollBar:vertical { -// width: 15px; -// margin: 32px 0px 0px 0px; -//} -// -//QScrollBar::handle { -// background: #353535; -// border: 1px solid #5A5A5A; -//} -// -//QScrollBar::handle:horizontal { -// border-width: 0px 1px 0px 1px; -//} -// -//QScrollBar::handle:vertical { -// border-width: 1px 0px 1px 0px; -//} -// -//QScrollBar::handle:horizontal { -// min-width: 20px; -//} -// -//QScrollBar::handle:vertical { -// min-height: 20px; -//} -// -//QScrollBar::add-line, QScrollBar::sub-line { -// background:#353535; -// border: 1px solid #5A5A5A; -// subcontrol-origin: margin; -//} -// -//QScrollBar::add-line { -// position: absolute; -//} -// -//QScrollBar::add-line:horizontal { -// width: 15px; -// subcontrol-position: left; -// left: 15px; -//} -// -//QScrollBar::add-line:vertical { -// height: 15px; -// subcontrol-position: top; -// top: 15px; -//} -// -//QScrollBar::sub-line:horizontal { -// width: 15px; -// subcontrol-position: top left; -//} -// -//QScrollBar::sub-line:vertical { -// height: 15px; -// subcontrol-position: top; -//} -// -//QScrollBar:left-arrow, QScrollBar::right-arrow, QScrollBar::up-arrow, QScrollBar::down-arrow { -// border: 1px solid #5A5A5A; -// width: 3px; -// height: 3px; -//} -// -//QScrollBar::add-page, QScrollBar::sub-page { -// background: none; -//} -// -//QAbstractButton:hover { -// background: #353535; -//} -// -//QAbstractButton:pressed { -// background: #5A5A5A; -//} -// -//QAbstractItemView { -// show-decoration-selected: 1; -// selection-background-color: #3D7848; -// selection-color: #DDDDDD; -// alternate-background-color: #353535; -//} -// -//QHeaderView { -// border: 1px solid #5A5A5A; -//} -// -//QHeaderView::section { -// background: #191919; -// border: 1px solid #5A5A5A; -// padding: 4px; -//} -// -//QHeaderView::section:selected, QHeaderView::section::checked { -// background: #353535; -//} -// -//QTableView { -// gridline-color: #5A5A5A; -//} -// -//QTabBar { -// margin-left: 2px; -//} -// -//QTabBar::tab { -// border-radius: 0px; -// padding: 4px; -// margin: 4px; -//} -// -//QTabBar::tab:selected { -// background: #353535; -//} -// -//QComboBox::down-arrow { -// border: 1px solid #5A5A5A; -// background: #353535; -//} -// -//QComboBox::drop-down { -// border: 1px solid #5A5A5A; -// background: #353535; -//} -// -//QComboBox::down-arrow { -// width: 3px; -// height: 3px; -// border: 1px solid #5A5A5A; -//} -// -//QAbstractSpinBox { -// padding-right: 15px; -//} -// -//QAbstractSpinBox::up-button, QAbstractSpinBox::down-button { -// border: 1px solid #5A5A5A; -// background: #353535; -// subcontrol-origin: border; -//} -// -//QAbstractSpinBox::up-arrow, QAbstractSpinBox::down-arrow { -// width: 3px; -// height: 3px; -// border: 1px solid #5A5A5A; -//} -// -//QSlider { -// border: none; -//} -// -//QSlider::groove:horizontal { -// height: 5px; -// margin: 4px 0px 4px 0px; -//} -// -//QSlider::groove:vertical { -// width: 5px; -// margin: 0px 4px 0px 4px; -//} -// -//QSlider::handle { -// border: 1px solid #5A5A5A; -// background: #353535; -//} -// -//QSlider::handle:horizontal { -// width: 15px; -// margin: -4px 0px -4px 0px; -//} -// -//QSlider::handle:vertical { -// height: 15px; -// margin: 0px -4px 0px -4px; -//} -// -//QSlider::add-page:vertical, QSlider::sub-page:horizontal { -// background: #3D7848; -//} -// -//QSlider::sub-page:vertical, QSlider::add-page:horizontal { -// background: #353535; -//} -// -//QLabel { -// border: none; -//} -// -//QProgressBar { -// text-align: center; -//} -// -//QProgressBar::chunk { -// width: 1px; -// background-color: #3D7848; -//} -// -//QMenu::separator { -// background: #353535; -//} -//)style"; - -//static const QString style = R"style( -//QWidget { -// font-family: Helvetica; -//} -//QWidget#MainWindow, QTextEdit, QWidget#SyncWidget, QWidget#DownloadArea { -// background-color: rgb(40, 40, 40); -//} -//QTextEdit, QLabel, QComboBox, QCheckBox { -// color: white; -// font-size: 12px; -//} -//QTextEdit { -// border-width: 2px 2px 0px 0px; -// border-style: solid; -// background-color: rgb(60, 60, 60); -//} -//QPushButton { -// background-color: -// qlineargradient( -// x1: 0, y1: 0, x2: 0, y2: 1, -// stop: 0 white, -// stop: 1 #505050 -// ); -// border-style: solid; -// border-color: black; -// border-width: 1px; -// font-size: 11px; -// min-height: 17.5px; -//} -//QComboBox { -// background-color: rgb(60, 60, 60); -// min-height: 20px; -//} -//QComboBox:focus, QComboBox:focus QAbstractItemView { -// color: white; -// background-color: rgb(60, 60, 60); -// selection-background-color: rgb(75, 75, 75); -//} -//QCheckBox { -// border: none; -//} -//QCheckBox::indicator { -// width: 13px; -// height: 13px; -//} -//QCheckBox::indicator::unchecked { -// border: 1px solid #5A5A5A; -// background: transparent; -//} -//QCheckBox::indicator:unchecked:hover { -// border: 1px solid #DDDDDD; -//} -//QCheckBox::indicator::checked { -// border: 1px solid #666666; -// background: #666666; -//} -//QCheckBox::indicator:checked:hover { -// border: 1px solid #DDDDDD; -// background: #555555; -//} -//QGroupBox, QScrollArea { -// border: 0px; -//} -//)style"; +static const QString style = R"style( +QWidget { + /*font-family: "Helvetica";*/ +} +QWidget#MainWindow, QTextEdit, QWidget#SyncWidget, QWidget#DownloadArea { + background-color: rgb(40, 40, 40); +} +QTextEdit, QLabel, QComboBox, QCheckBox { + color: #EDEDED; + font-size: 12px; +} +QLabel { + font-size: 13px; +} +QLabel#Image { + margin: -10px 0px -5px 0px; +} +QTextEdit { + border-width: 2px 2px 0px 0px; + border-style: solid; + background-color: rgb(60, 60, 60); +} +QPushButton { + color:#202020; + background-color: + qlineargradient( + x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 white, + stop: 1 #555555 + ); + border: 1px solid black; + font-size: 11px; + min-height: 20px; +} +QComboBox { + background-color: rgb(60, 60, 60); + min-height: 20px; +} +QComboBox:focus, QComboBox:focus QAbstractItemView { + color: white; + background-color: rgb(60, 60, 60); + selection-background-color: rgb(75, 75, 75); +} +QCheckBox { + border: none; +} +QCheckBox::indicator { + width: 12px; + height: 12px; +} +QCheckBox::indicator::unchecked { + border: 1px solid #5A5A5A; + background: transparent; +} +QCheckBox::indicator:unchecked:hover { + border: 1px solid #DDDDDD; +} +QCheckBox::indicator::checked { + border: 1px solid #AAAAAA; + background: #666666; +} +QCheckBox::indicator:checked:hover { + border: 1px solid #DDDDDD; + background: #555555; +} +QGroupBox, QScrollArea { + border: 0px; +} +InfoWidget { + border-width: 1px; + border-style: solid; + border-color: #BBBBBB; + margin: 2px 1px 2px 1px; + padding: 7.5px; +} +InfoWidget QLabel#Name { + font-size: 17px; +} +InfoWidget QLabel#Bytes { + font-size: 13px; + font-family: "Lucida Console"; +} +InfoWidget QLabel#MessageLeft, QLabel#MessageCenter, QLabel#MessageRight { + font-size: 11.5px; + margin-top: -2px; +} +InfoWidget QProgressBar { + border: 2px solid #BBBBBB; + border-radius: 5px; + background: white; + height: 15px; +} +InfoWidget QProgressBar::chunk { + background: qlineargradient( + x1: 0, y1: 0.5, x2: 1, y2: 0.5, + stop: 0 #444444, + stop: 1 #600000 + ); +} +QScrollBar { + border: 1px solid #000000; + background: #282828; + width: 15px; + margin: 16px 0px 16px 0px; +} +QScrollBar::handle { + background: #B0B0B0; + border: 1px solid #000000; + border-width: 1px 0px 1px 0px; + min-height: 20px; +} +QScrollBar::add-line, QScrollBar::sub-line { + background:#B0B0B0; + border: 1px solid #5A5A5A; + subcontrol-origin: margin; +} +QScrollBar::add-line { + top: 15px; + height: 15px; +} +QScrollBar::sub-line { + height: 15px; + subcontrol-position: top; +} +QScrollBar::up-arrow, QScrollBar::down-arrow { + border: 1px solid #5A5A5A; + width: 3px; + height: 3px; + background-color: #353535; +} +QScrollBar::add-page, QScrollBar::sub-page { + background: none; +} +)style"; int main(int argc, char** argv) { QApplication app(argc, argv); + app.setStyleSheet(style); MainWindow window; - - auto setStyle = [](const ghoul::filesystem::File& f) { - QFile file(QString::fromStdString(f.path())); - file.open(QIODevice::ReadOnly); - QTextStream in(&file); - QString style = in.readAll(); - qApp->setStyleSheet(style); - }; - - ghoul::filesystem::File f("stylesheet.css", false, setStyle); - - setStyle(f); - window.show(); return app.exec(); diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 12d200ad74..1477efca09 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -74,7 +74,7 @@ namespace { const std::string VersionKey = "Version"; const bool OverwriteFiles = false; - const bool CleanInfoWidgets = false; + const bool CleanInfoWidgets = true; } SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) From 8fc343b57f9db144d630c352220046d66de5f95c Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 4 Jul 2015 20:34:32 +0200 Subject: [PATCH 265/329] Non-Windows compile fix --- support/cmake/handle_external_library.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/support/cmake/handle_external_library.cmake b/support/cmake/handle_external_library.cmake index e93bfd2ffd..40b5c237b7 100644 --- a/support/cmake/handle_external_library.cmake +++ b/support/cmake/handle_external_library.cmake @@ -33,7 +33,9 @@ function (include_external_library target_name library_name path) target_link_libraries(${target_name} ${library_name}) target_include_directories(${target_name} PUBLIC SYSTEM ${INCLUDE_DIR}) set_property(TARGET ${library_name} PROPERTY FOLDER "External") - target_compile_options(${library_name} PUBLIC "/MP") + if (MSVC) + target_compile_options(${library_name} PUBLIC "/MP") + endif () if (OPENSPACE_DISABLE_EXTERNAL_WARNINGS) if (MSVC) target_compile_options(${library_name} PUBLIC "/W0") From c375f18dac5dae3d559063c666fda5cc966076ab Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 4 Jul 2015 21:21:27 +0200 Subject: [PATCH 266/329] Add commandline argument to OpenSpaceEngine to override scene file Make scene file selectable in Launcher, using the configurationfile value as default --- apps/Launcher/mainwindow.cpp | 52 +++++++++++++++++++++++++--------- apps/Launcher/mainwindow.h | 5 +++- src/engine/openspaceengine.cpp | 21 ++++++++++---- 3 files changed, 59 insertions(+), 19 deletions(-) diff --git a/apps/Launcher/mainwindow.cpp b/apps/Launcher/mainwindow.cpp index 3ce0e50dbc..9d97a0ffe3 100644 --- a/apps/Launcher/mainwindow.cpp +++ b/apps/Launcher/mainwindow.cpp @@ -115,11 +115,16 @@ MainWindow::MainWindow() layout->addWidget(shortcutButton, 0, 1); layout->setRowStretch(1, 10); + + QLabel* configurationSelectionLabel = new QLabel("Configuration:"); + layout->addWidget(configurationSelectionLabel, 2, 0); + _configurations = new QComboBox; + layout->addWidget(_configurations, 2, 1); QLabel* sceneSelectionLabel = new QLabel("Scenes:"); - layout->addWidget(sceneSelectionLabel, 2, 0); + layout->addWidget(sceneSelectionLabel, 3, 0); _scenes = new QComboBox; - layout->addWidget(_scenes, 2, 1); + layout->addWidget(_scenes, 3, 1); container->setLayout(layout); } @@ -192,27 +197,40 @@ void MainWindow::initialize() { std::string configurationFile = _configurationFile; bool found = openspace::OpenSpaceEngine::findConfiguration(configurationFile); if (!found) { + LERRORC("MainWindow", "Could not find configuration file"); } _configuration = new openspace::ConfigurationManager; _configuration->loadFromFile(configurationFile); - - QString modulesDirectory = QString::fromStdString( - absPath("${SCENE}") - ); + // Load all available scenes + QString modulesDirectory = QString::fromStdString(absPath("${SCENE}")); QDir d(modulesDirectory); d.setFilter(QDir::Files); - QFileInfoList list = d.entryInfoList(); + _scenes->addItem("Use Default"); for (const QFileInfo& i : list) { - _sceneFiles.insert(i.fileName(), i.absoluteFilePath()); - _scenes->addItem(i.fileName()); + QString file = i.fileName(); + file = file.replace(".scene", ""); + _sceneFiles.insert(file, i.absoluteFilePath()); + _scenes->addItem(file); } - - _scenes->setCurrentText("default.scene"); - + _scenes->setCurrentText("Use Default"); _syncWidget->setSceneFiles(_sceneFiles); + + // Load all available configuration files + QString configurationDirectory = QString::fromStdString(absPath("${SGCT}")); + d = QDir(configurationDirectory); + d.setFilter(QDir::Files); + list = d.entryInfoList(); + _configurations->addItem("Use Default"); + for (const QFileInfo& i : list) { + QString file = i.fileName(); + file = file.replace(".xml", ""); + _configurationFiles.insert(file, i.absoluteFilePath()); + _configurations->addItem(file); + } + _configurations->setCurrentText("Use Default"); } void MainWindow::shortcutButtonPressed() { @@ -224,7 +242,15 @@ void MainWindow::syncButtonPressed() { } void MainWindow::startButtonPressed() { - QProcess::startDetached(OpenSpaceExecutable); + QString exec = OpenSpaceExecutable; + if (_sceneFiles.contains(_scenes->currentText())) + exec += " -scene \"" + _sceneFiles[_scenes->currentText()] + "\""; + + if (_configurationFiles.contains(_configurations->currentText())) + exec += " -sgct \"" + _configurationFiles[_configurations->currentText()] + "\""; + + LINFOC("MainWindow", "Executing: " << exec.toStdString()); + QProcess::startDetached(exec); } void MainWindow::newsNetworkError() { diff --git a/apps/Launcher/mainwindow.h b/apps/Launcher/mainwindow.h index f131a8aa25..62859548b0 100644 --- a/apps/Launcher/mainwindow.h +++ b/apps/Launcher/mainwindow.h @@ -63,9 +63,12 @@ private: QTextEdit* _informationWidget; + QComboBox* _configurations; + QMap _configurationFiles; + QComboBox* _scenes; QMap _sceneFiles; - + ShortcutWidget* _shortcutWidget; SyncWidget* _syncWidget; diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 3d919d213c..e51fae286d 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -92,6 +92,7 @@ namespace { struct { std::string configurationName; std::string sgctConfigurationName; + std::string sceneName; } commandlineArgumentPlaceholders; } @@ -354,11 +355,14 @@ bool OpenSpaceEngine::initialize() { _renderEngine->initialize(); sceneGraph->initialize(); - std::string sceneDescriptionPath; - success = configurationManager()->getValue( - ConfigurationManager::KeyConfigScene, sceneDescriptionPath); - if (success) - sceneGraph->scheduleLoadSceneFile(sceneDescriptionPath); + std::string sceneDescriptionPath = ""; + if (commandlineArgumentPlaceholders.sceneName.empty()) { + success = configurationManager()->getValue( + ConfigurationManager::KeyConfigScene, sceneDescriptionPath); + } + else + sceneDescriptionPath = commandlineArgumentPlaceholders.sceneName; + sceneGraph->scheduleLoadSceneFile(sceneDescriptionPath); _interactionHandler->setKeyboardController(new interaction::KeyboardControllerFixed); _interactionHandler->setMouseController(new interaction::OrbitalMouseController); @@ -409,6 +413,13 @@ bool OpenSpaceEngine::gatherCommandlineArguments() { "the OpenSpace configuration file"); _commandlineParser->addCommand(sgctConfigFileCommand); + commandlineArgumentPlaceholders.sceneName = ""; + CommandlineCommand* sceneFileCommand = new SingleCommand( + &commandlineArgumentPlaceholders.sceneName, "-scene", "", + "Provides the path to the scene file, overriding the value set in the OpenSpace" + " configuration file"); + _commandlineParser->addCommand(sceneFileCommand); + return true; } From ccab7ec32ca5a3c208ae45b6b0be5677b4a37c07 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 4 Jul 2015 21:48:21 +0200 Subject: [PATCH 267/329] Correctly Set the CMAKE_PREFIX_PATH for Qt on Mac --- support/cmake/handle_external_library.cmake | 2 +- support/cmake/support_macros.cmake | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/support/cmake/handle_external_library.cmake b/support/cmake/handle_external_library.cmake index 40b5c237b7..da53da1ea4 100644 --- a/support/cmake/handle_external_library.cmake +++ b/support/cmake/handle_external_library.cmake @@ -44,4 +44,4 @@ function (include_external_library target_name library_name path) endif () endif () endif () -endfunction () \ No newline at end of file +endfunction () diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 1f90d5e24f..9ed1a86d9b 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -178,6 +178,13 @@ function (add_external_dependencies) target_compile_definitions(libOpenSpace PUBLIC "OPENSPACE_CURL_ENABLED") endif () endif() + + # Qt + # Unfortunately, we have to set this value manually; sigh + # In the future, if the Qt version is updated, just add to this variable ---abock + if (APPLE) + set(CMAKE_PREFIX_PATH "~/Qt/5.5/clang_64/lib/cmake" PARENT_SCOPE) + endif () endfunction () From df3688f04a77c42c3e7d4f05af98382cb3c89c1c Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 4 Jul 2015 22:03:54 +0200 Subject: [PATCH 268/329] Make multithreaded download a toggle --- src/engine/downloadmanager.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp index 715279cd5c..499839e4b3 100644 --- a/src/engine/downloadmanager.cpp +++ b/src/engine/downloadmanager.cpp @@ -40,13 +40,15 @@ #include #endif +#define USE_MULTITHREADED_DOWNLOAD + namespace { const std::string _loggerCat = "DownloadManager"; const std::string RequestIdentifier = "identifier"; const std::string RequestFileVersion = "file_version"; const std::string RequestApplicationVersion = "application_version"; - + struct ProgressInformation { openspace::DownloadManager::FileFuture* future; std::chrono::system_clock::time_point startTime; @@ -145,7 +147,10 @@ DownloadManager::FileFuture* DownloadManager::downloadFile( LDEBUG("Starting download for file: '" << url << "' into file '" << file.path() << "'"); + +#ifdef USE_MULTITHREADED_DOWNLOAD std::thread t = std::thread([url, finishedCallback, progressCallback, future, fp]() { +#endif CURL* curl = curl_easy_init(); if (curl) { curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); @@ -174,6 +179,7 @@ DownloadManager::FileFuture* DownloadManager::downloadFile( if (finishedCallback) finishedCallback(*future); } +#ifdef USE_MULTITHREADED_DOWNLOAD }); #ifdef WIN32 @@ -185,6 +191,7 @@ DownloadManager::FileFuture* DownloadManager::downloadFile( #endif t.detach(); +#endif // USE_MULTITHREADED_DOWNLOAD return future; } @@ -253,7 +260,9 @@ void DownloadManager::downloadRequestFilesAsync( bool overrideFiles, AsyncDownloadFinishedCallback callback) { +#ifdef USE_MULTITHREADED_DOWNLOAD std::thread t = std::thread([this, identifier, destination, version, overrideFiles, callback](){ +#endif std::vector f = downloadRequestFiles( identifier, destination, @@ -262,6 +271,7 @@ void DownloadManager::downloadRequestFilesAsync( ); callback(f); +#ifdef USE_MULTITHREADED_DOWNLOAD }); #ifdef WIN32 @@ -273,6 +283,7 @@ void DownloadManager::downloadRequestFilesAsync( #endif t.detach(); +#endif // USE_MULTITHREADED_DOWNLOAD } } // namespace openspace From d694b14ec2ca592515d3774e9943f7ccde030e10 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 4 Jul 2015 22:14:00 +0200 Subject: [PATCH 269/329] Updated to new Ghoul version --- ext/ghoul | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/ghoul b/ext/ghoul index 5a6d99432b..f87ad95260 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 5a6d99432b394ea98e9c5d50c8a99c990c504f8c +Subproject commit f87ad95260996a56105fb4543e734e86d187413a From e66e81568309861d87b9d855c1aba72f9f8b25df Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 4 Jul 2015 23:06:23 +0200 Subject: [PATCH 270/329] Add a button to the SyncWidget to allow it to be closed --- apps/Launcher/syncwidget.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 1477efca09..69a127156c 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -119,6 +119,12 @@ SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) layout->addWidget(area); } + QPushButton* close = new QPushButton("Close"); + layout->addWidget(close, Qt::AlignRight); + QObject::connect( + close, SIGNAL(clicked(bool)), + this, SLOT(close()) + ); setLayout(layout); From 93e4a9b526b9e46a25920968e72a5624a8ba9565 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Sun, 5 Jul 2015 16:09:57 +0200 Subject: [PATCH 271/329] redesigning the communication protocol and message types. also added time keyframes which are sent on a per-frame basis --- .../interaction/interactionhandler.h | 4 +- include/openspace/network/messagestructures.h | 164 ++++ .../openspace/network/parallelconnection.h | 51 +- include/openspace/util/time.h | 8 +- src/CMakeLists.txt | 1 + src/engine/openspaceengine.cpp | 6 +- src/engine/openspaceengine.cpp.orig | 814 ++++++++++++++++++ src/interaction/interactionhandler.cpp | 4 +- src/network/parallelconnection.cpp | 136 ++- src/util/time.cpp | 12 +- 10 files changed, 1124 insertions(+), 76 deletions(-) create mode 100644 include/openspace/network/messagestructures.h create mode 100644 src/engine/openspaceengine.cpp.orig diff --git a/include/openspace/interaction/interactionhandler.h b/include/openspace/interaction/interactionhandler.h index 19ed741437..8a59919ca3 100644 --- a/include/openspace/interaction/interactionhandler.h +++ b/include/openspace/interaction/interactionhandler.h @@ -140,7 +140,7 @@ public: void setInvertRotation(bool invert); bool invertRotation() const; - void addKeyframe(const network::StreamDataKeyframe &kf); + void addKeyframe(const network::datamessagestructures::PositionKeyframe &kf); void clearKeyframes(); /** @@ -179,7 +179,7 @@ private: std::vector _controllers; //remote controller - std::vector _keyframes; + std::vector _keyframes; double _currentKeyframeTime; std::mutex _keyframeMutex; }; diff --git a/include/openspace/network/messagestructures.h b/include/openspace/network/messagestructures.h new file mode 100644 index 0000000000..ee08a643d0 --- /dev/null +++ b/include/openspace/network/messagestructures.h @@ -0,0 +1,164 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __MESSAGESTRUCTURES_H__ +#define __MESSAGESTRUCTURES_H__ + +//std includes +#include +#include + +//glm includes +#include + +//openspace includes +#include + +namespace openspace{ + + namespace network{ + + namespace datamessagestructures{ + enum type{ + PositionData = 0, + TimeData, + ScriptData + }; + + struct PositionKeyframe{ + glm::quat _viewRotationQuat; + psc _position; + double _timeStamp; + + void serialize(std::vector &buffer){ + //add position + buffer.insert(buffer.end(), reinterpret_cast(&_position), reinterpret_cast(&_position) + sizeof(_position)); + + //add orientation + buffer.insert(buffer.end(), reinterpret_cast(&_viewRotationQuat), reinterpret_cast(&_viewRotationQuat) + sizeof(_viewRotationQuat)); + + //add timestamp + buffer.insert(buffer.end(), reinterpret_cast(&_timeStamp), reinterpret_cast(&_timeStamp) + sizeof(_timeStamp)); + }; + + void deserialize(const std::vector &buffer){ + int offset = 0; + int size = 0; + + //position + size = sizeof(_position); + memcpy(&_position, buffer.data() + offset, size); + offset += size; + + //orientation + size = sizeof(_viewRotationQuat); + memcpy(&_viewRotationQuat, buffer.data() + offset, size); + offset += size; + + //timestamp + size = sizeof(_timeStamp); + memcpy(&_timeStamp, buffer.data() + offset, size); + }; + }; + + struct TimeKeyframe{ + + double _time; + double _dt; + bool _paused; + bool _requiresTimeJump; + + void serialize(std::vector &buffer){ + //add current time + buffer.insert(buffer.end(), reinterpret_cast(&_time), reinterpret_cast(&_time) + sizeof(_time)); + + //add delta time + buffer.insert(buffer.end(), reinterpret_cast(&_dt), reinterpret_cast(&_dt) + sizeof(_dt)); + + //add wether time is paused or not + buffer.insert(buffer.end(), reinterpret_cast(&_paused), reinterpret_cast(&_paused) + sizeof(_paused)); + + //add wether a time jump is necessary (recompute paths etc) + buffer.insert(buffer.end(), reinterpret_cast(&_requiresTimeJump), reinterpret_cast(&_requiresTimeJump) + sizeof(_requiresTimeJump)); + }; + + void deserialize(const std::vector &buffer){ + int offset = 0; + int size = 0; + + //current time + size = sizeof(_time); + memcpy(&_time, buffer.data() + offset, size); + offset += size; + + //delta time + size = sizeof(_dt); + memcpy(&_dt, buffer.data() + offset, size); + offset += size; + + //is time paused? + size = sizeof(_paused); + memcpy(&_paused, buffer.data() + offset, size); + + //is a time jump required? + size = sizeof(_requiresTimeJump); + memcpy(&_requiresTimeJump, buffer.data() + offset, size); + }; + }; + + struct ScriptMessage{ + + uint16_t _scriptlen; + std::string _script; + + void serialize(std::vector &buffer){ + //add script length + buffer.insert(buffer.end(), reinterpret_cast(&_scriptlen), reinterpret_cast(&_scriptlen) + sizeof(_scriptlen)); + + //add script + buffer.insert(buffer.end(), _script.begin(), _script.end()); + + }; + + void deserialize(const std::vector &buffer){ + int offset = 0; + int size = 0; + + //size of script + size = sizeof(uint16_t); + memcpy(&_scriptlen, buffer.data() + offset, size); + offset += size; + + //actual script + _script.assign(buffer.begin() + offset, buffer.end()); + }; + }; + + } //namespace messagestructures + + } // namespace network + +} // namespace openspace + +#endif // __MESSAGESTRUCTURES_H__ \ No newline at end of file diff --git a/include/openspace/network/parallelconnection.h b/include/openspace/network/parallelconnection.h index dc0bc88c1e..bb8de83310 100644 --- a/include/openspace/network/parallelconnection.h +++ b/include/openspace/network/parallelconnection.h @@ -28,6 +28,7 @@ //openspace includes #include #include +#include //glm includes #include @@ -60,42 +61,6 @@ namespace openspace{ namespace network{ - struct StreamDataKeyframe{ - glm::quat _viewRotationQuat; - psc _position; - double _timeStamp; - - void serialize(std::vector &buffer){ - //add position - buffer.insert(buffer.end(), reinterpret_cast(&_position), reinterpret_cast(&_position) + sizeof(_position)); - - //add orientation - buffer.insert(buffer.end(), reinterpret_cast(&_viewRotationQuat), reinterpret_cast(&_viewRotationQuat) + sizeof(_viewRotationQuat)); - - //add timestamp - buffer.insert(buffer.end(), reinterpret_cast(&_timeStamp), reinterpret_cast(&_timeStamp) + sizeof(_timeStamp)); - }; - - void deserialize(const std::vector &buffer){ - int offset = 0; - int size = 0; - - //position - size = sizeof(_position); - memcpy(&_position, buffer.data() + offset, size); - offset += size; - - //orientation - size = sizeof(_viewRotationQuat); - memcpy(&_viewRotationQuat, buffer.data() + offset, size); - offset += size; - - //timestamp - size = sizeof(_timeStamp); - memcpy(&_timeStamp, buffer.data() + offset, size); - }; - }; - class ParallelConnection{ public: @@ -123,11 +88,13 @@ namespace openspace{ void signalDisconnect(); + void update(double dt); + enum MessageTypes{ Authentication=0, Initialization, - StreamData, - Script, + Data, + Script, //obsolete now HostInfo, InitializationRequest, HostshipRequest, @@ -182,15 +149,15 @@ namespace openspace{ void delegateDecoding(uint32_t type); - void decodeInitializationMessage(); + void initializationMessageReceived(); - void decodeStreamDataMessage(); + void dataMessageReceived(); void decodeScriptMessage(); - void decodeHostInfoMessage(); + void hostInfoMessageReceived(); - void decodeInitializationRequestMessage(); + void initializationRequestMessageReceived(); void broadcast(); diff --git a/include/openspace/util/time.h b/include/openspace/util/time.h index 39573ea40e..cba2eced3f 100644 --- a/include/openspace/util/time.h +++ b/include/openspace/util/time.h @@ -86,16 +86,18 @@ public: * Sets the current time to the specified value in seconds past the J2000 epoch. This * value can be negative to represent dates before the epoch. * \param value The number of seconds after the J2000 epoch + * \param requireJump Wether or not the time change is big enough to require a time-jump, defaults to false */ - void setTime(double value); + void setTime(double value, bool requireJump = false); /** * Sets the current time to the specified value given as a Spice compliant string as * described in the Spice documentation * (http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/str2et_c.html) * \param time The time to be set as a date string + * \param requireJump Wether or not the time change is big enough to require a time-jump, defaults to false */ - void setTime(std::string time); + void setTime(std::string time, bool requireJump = false); /** * Returns the current time as the number of seconds past the J2000 epoch. If the @@ -167,6 +169,8 @@ public: bool timeJumped() const; void setTimeJumped(bool jumped); + + bool paused() const; /** * Returns the Lua library that contains all Lua functions available to change the diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4200427481..1f09313c1f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -114,6 +114,7 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/interaction/externalcontrol/randomexternalcontrol.h ${OPENSPACE_BASE_DIR}/include/openspace/network/networkengine.h ${OPENSPACE_BASE_DIR}/include/openspace/network/parallelconnection.h + ${OPENSPACE_BASE_DIR}/include/openspace/network/messagestructures.h ${OPENSPACE_BASE_DIR}/include/openspace/properties/matrixproperty.h ${OPENSPACE_BASE_DIR}/include/openspace/properties/numericalproperty.h ${OPENSPACE_BASE_DIR}/include/openspace/properties/numericalproperty.inl diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 5f17fbc39e..0e1bd1e13c 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -638,10 +638,12 @@ void OpenSpaceEngine::preSynchronization() { Time::ref().preSynchronization(); _interactionHandler->update(dt); - //_interactionHandler.lockControls(); _scriptEngine->preSynchronization(); - _renderEngine->preSynchronization(); + + _renderEngine->preSynchronization(); + + _parallelConnection->update(dt); } } diff --git a/src/engine/openspaceengine.cpp.orig b/src/engine/openspaceengine.cpp.orig new file mode 100644 index 0000000000..e2f7ebd71a --- /dev/null +++ b/src/engine/openspaceengine.cpp.orig @@ -0,0 +1,814 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include + +#define SGCT_WINDOWS_INCLUDE +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +<<<<<<< HEAD +#include +======= +#include +>>>>>>> develop + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// std +#include +#include + +#ifdef OPENSPACE_MODULE_ONSCREENGUI_ENABLED +#include +#endif + +#ifdef _MSC_VER +#ifdef OPENSPACE_ENABLE_VLD +#include +#endif +#endif + +using namespace openspace::scripting; +using namespace ghoul::filesystem; +using namespace ghoul::logging; +using namespace ghoul::cmdparser; + +namespace { + const std::string _loggerCat = "OpenSpaceEngine"; + const std::string _configurationFile = "openspace.cfg"; + const std::string _sgctDefaultConfigFile = "${SGCT}/single.xml"; + const std::string _defaultCacheLocation = "${BASE_PATH}/cache"; + + const std::string _sgctConfigArgumentCommand = "-config"; + + const int CacheVersion = 1; + const int DownloadVersion = 1; + + struct { + std::string configurationName; + std::string sgctConfigurationName; + } commandlineArgumentPlaceholders; +} + +namespace openspace { + +OpenSpaceEngine* OpenSpaceEngine::_engine = nullptr; + +OpenSpaceEngine::OpenSpaceEngine(std::string programName) + : _configurationManager(new ConfigurationManager) + , _interactionHandler(new interaction::InteractionHandler) + , _renderEngine(new RenderEngine) + , _scriptEngine(new scripting::ScriptEngine) + , _networkEngine(new NetworkEngine) + , _commandlineParser(new ghoul::cmdparser::CommandlineParser(programName, true)) + , _console(new LuaConsole) + , _moduleEngine(new ModuleEngine) + , _gui(new gui::GUI) + , _isMaster(false) + , _runTime(0.0) + , _syncBuffer(nullptr) + , _parallelConnection(new network::ParallelConnection) +{ + FactoryManager::initialize(); + SpiceManager::initialize(); + Time::initialize(); + ghoul::systemcapabilities::SystemCapabilities::initialize(); +} + +OpenSpaceEngine::~OpenSpaceEngine() { + _gui->deinitializeGL(); + + delete _parallelConnection; + delete _configurationManager; + delete _interactionHandler; + delete _renderEngine; + delete _scriptEngine; + delete _networkEngine; + delete _commandlineParser; + delete _console; + delete _moduleEngine; + delete _gui; + + if(_syncBuffer) + delete _syncBuffer; + _syncBuffer = nullptr; +} + +OpenSpaceEngine& OpenSpaceEngine::ref() { + ghoul_assert(_engine, "OpenSpaceEngine not created"); + return *_engine; +} + +bool OpenSpaceEngine::create( + int argc, char** argv, + std::vector& sgctArguments) +{ + ghoul::initialize(); + + ghoul_assert(!_engine, "OpenSpaceEngine was already created"); + + // Initialize the LogManager and add the console log as this will be used every time + // and we need a fall back if something goes wrong between here and when we add the + // logs from the configuration file. If the user requested as specific loglevel in the + // configuration file, we will deinitialize this LogManager and reinitialize it later + // with the correct LogLevel + LogManager::initialize(LogManager::LogLevel::Debug, true); + LogMgr.addLog(new ConsoleLog); + + LDEBUG("Initialize FileSystem"); + +#ifdef __APPLE__ + ghoul::filesystem::File app(argv[0]); + std::string dirName = app.directoryName(); + LINFO("Setting starting directory to '" << dirName << "'"); + FileSys.setCurrentDirectory(dirName); +#endif + + // Sanity check of values + if (argc < 1 || argv == nullptr) { + LFATAL("No arguments were passed to this function"); + return false; + } + + // Create other objects + LDEBUG("Creating OpenSpaceEngine"); + _engine = new OpenSpaceEngine(std::string(argv[0])); + + // Query modules for commandline arguments + const bool gatherSuccess = _engine->gatherCommandlineArguments(); + if (!gatherSuccess) + return false; + + // Parse commandline arguments + _engine->_commandlineParser->setCommandLine(argc, argv, &sgctArguments); + const bool executeSuccess = _engine->_commandlineParser->execute(); + if (!executeSuccess) + return false; + + // Find configuration + std::string configurationFilePath = commandlineArgumentPlaceholders.configurationName; + if (configurationFilePath.empty()) { + LDEBUG("Finding configuration"); + const bool findConfigurationSuccess = + OpenSpaceEngine::findConfiguration(configurationFilePath); + if (!findConfigurationSuccess) { + LFATAL("Could not find OpenSpace configuration file!"); + return false; + } + } + configurationFilePath = absPath(configurationFilePath); + LINFO("Configuration Path: '" << configurationFilePath << "'"); + + // Loading configuration from disk + LDEBUG("Loading configuration from disk"); + const bool configLoadSuccess = _engine->configurationManager()->loadFromFile( + configurationFilePath); + if (!configLoadSuccess) { + LFATAL("Loading of configuration file '" << configurationFilePath << "' failed"); + return false; + } + + // Initialize the requested logs from the configuration file + _engine->configureLogging(); + + LINFOC("OpenSpace Version", + OPENSPACE_VERSION_MAJOR << "." << + OPENSPACE_VERSION_MINOR << "." << + OPENSPACE_VERSION_PATCH << " (" << OPENSPACE_VERSION_STRING << ")"); + + // Create directories that doesn't exist + auto tokens = FileSys.tokens(); + for (const std::string& token : tokens) { + if (!FileSys.directoryExists(token)) { + std::string p = absPath(token); + LDEBUG("Directory '" << p << "' does not exist, creating."); + if (!FileSys.createDirectory(p, true)) + LERROR("Directory '" << p << "' could not be created"); + } + } + + // Register modules + _engine->_moduleEngine->create(); + + // Create the cachemanager + FileSys.createCacheManager(absPath("${" + ConfigurationManager::KeyCache + "}"), CacheVersion); + _engine->_console->initialize(); + + // Register the provided shader directories + ghoul::opengl::ShaderObject::addIncludePath("${SHADERS}"); + + _engine->_syncBuffer = new SyncBuffer(1024); + + // Determining SGCT configuration file + LDEBUG("Determining SGCT configuration file"); + std::string sgctConfigurationPath = _sgctDefaultConfigFile; + _engine->configurationManager()->getValue( + ConfigurationManager::KeyConfigSgct, sgctConfigurationPath); + + if (!commandlineArgumentPlaceholders.sgctConfigurationName.empty()) { + LDEBUG("Overwriting SGCT configuration file with commandline argument: " << + commandlineArgumentPlaceholders.sgctConfigurationName); + sgctConfigurationPath = commandlineArgumentPlaceholders.sgctConfigurationName; + } + + // Prepend the outgoing sgctArguments with the program name + // as well as the configuration file that sgct is supposed to use + sgctArguments.insert(sgctArguments.begin(), argv[0]); + sgctArguments.insert(sgctArguments.begin() + 1, _sgctConfigArgumentCommand); + sgctArguments.insert(sgctArguments.begin() + 2, absPath(sgctConfigurationPath)); + + return true; +} + +void OpenSpaceEngine::destroy() { + _engine->_moduleEngine->deinitialize(); + _engine->_moduleEngine->destroy(); + _engine->_console->deinitialize(); + + _engine->_scriptEngine->deinitialize(); + delete _engine; + ghoul::systemcapabilities::SystemCapabilities::deinitialize(); + FactoryManager::deinitialize(); + Time::deinitialize(); + SpiceManager::deinitialize(); + + FileSystem::deinitialize(); + LogManager::deinitialize(); + + ghoul::deinitialize(); +} + +bool OpenSpaceEngine::initialize() { + // clear the screen so the user don't have to see old buffer contents from the + // graphics card + clearAllWindows(); + + // Detect and log OpenCL and OpenGL versions and available devices + SysCap.addComponent(new ghoul::systemcapabilities::GeneralCapabilitiesComponent); + SysCap.addComponent(new ghoul::systemcapabilities::OpenGLCapabilitiesComponent); + SysCap.detectCapabilities(); + + using Verbosity = ghoul::systemcapabilities::SystemCapabilitiesComponent::Verbosity; + Verbosity verbosity = Verbosity::Default; + if (configurationManager()->hasKeyAndValue(ConfigurationManager::KeyCapabilitiesVerbosity)) { + std::map verbosityMap = { + { "Minimal", Verbosity::Minimal }, + { "Default", Verbosity::Default }, + { "Full", Verbosity::Full } + }; + + std::string v = configurationManager()->value(ConfigurationManager::KeyCapabilitiesVerbosity); + if (verbosityMap.find(v) != verbosityMap.end()) + verbosity = verbosityMap[v]; + } + SysCap.logCapabilities(verbosity); + + std::string requestURL = ""; + bool success = configurationManager()->getValue(ConfigurationManager::KeyDownloadRequestURL, requestURL); + if (success) + DownloadManager::initialize(requestURL, DownloadVersion); + + // Load SPICE time kernel + success = loadSpiceKernels(); + if (!success) + return false; + + // Register Lua script functions + LDEBUG("Registering Lua libraries"); + _scriptEngine->addLibrary(RenderEngine::luaLibrary()); + _scriptEngine->addLibrary(Scene::luaLibrary()); + _scriptEngine->addLibrary(Time::luaLibrary()); + _scriptEngine->addLibrary(interaction::InteractionHandler::luaLibrary()); + _scriptEngine->addLibrary(LuaConsole::luaLibrary()); + _scriptEngine->addLibrary(gui::GUI::luaLibrary()); + _scriptEngine->addLibrary(network::ParallelConnection::luaLibrary()); + + // TODO: Maybe move all scenegraph and renderengine stuff to initializeGL + scriptEngine()->initialize(); + + // If a LuaDocumentationFile was specified, generate it now + const bool hasType = configurationManager()->hasKey(ConfigurationManager::KeyLuaDocumentationType); + const bool hasFile = configurationManager()->hasKey(ConfigurationManager::KeyLuaDocumentationFile); + if (hasType && hasFile) { + std::string luaDocumentationType; + configurationManager()->getValue(ConfigurationManager::KeyLuaDocumentationType, luaDocumentationType); + std::string luaDocumentationFile; + configurationManager()->getValue(ConfigurationManager::KeyLuaDocumentationFile, luaDocumentationFile); + + luaDocumentationFile = absPath(luaDocumentationFile); + _scriptEngine->writeDocumentation(luaDocumentationFile, luaDocumentationType); + } + + bool disableMasterRendering = false; + configurationManager()->getValue( + ConfigurationManager::KeyDisableMasterRendering, disableMasterRendering); + _renderEngine->setDisableRenderingOnMaster(disableMasterRendering); + + + // Load scenegraph + Scene* sceneGraph = new Scene; + _renderEngine->setSceneGraph(sceneGraph); + + // initialize the RenderEngine + _renderEngine->initialize(); + sceneGraph->initialize(); + + std::string sceneDescriptionPath; + success = configurationManager()->getValue( + ConfigurationManager::KeyConfigScene, sceneDescriptionPath); + if (success) + sceneGraph->scheduleLoadSceneFile(sceneDescriptionPath); + + _interactionHandler->setKeyboardController(new interaction::KeyboardControllerFixed); + _interactionHandler->setMouseController(new interaction::OrbitalMouseController); + + // Run start up scripts + runStartupScripts(); + + // Load a light and a monospaced font + loadFonts(); + + LINFO("Initializing GUI"); + _gui->initialize(); + + // Initialize modules + _moduleEngine->initialize(); + + LINFO("Finished initializing"); + return true; +} + +bool OpenSpaceEngine::isInitialized() { + return _engine != nullptr; +} + +void OpenSpaceEngine::clearAllWindows() { + size_t n = sgct::Engine::instance()->getNumberOfWindows(); + for (size_t i = 0; i < n; ++i) { + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GLFWwindow* win = sgct::Engine::instance()->getWindowPtr(i)->getWindowHandle(); + glfwSwapBuffers(win); + } +} + +bool OpenSpaceEngine::gatherCommandlineArguments() { + // TODO: Get commandline arguments from all modules + + commandlineArgumentPlaceholders.configurationName = ""; + CommandlineCommand* configurationFileCommand = new SingleCommand( + &commandlineArgumentPlaceholders.configurationName, "-config", "-c", + "Provides the path to the OpenSpace configuration file"); + _commandlineParser->addCommand(configurationFileCommand); + + commandlineArgumentPlaceholders.sgctConfigurationName = ""; + CommandlineCommand* sgctConfigFileCommand = new SingleCommand( + &commandlineArgumentPlaceholders.sgctConfigurationName, "-sgct", "-s", + "Provides the path to the SGCT configuration file, overriding the value set in" + "the OpenSpace configuration file"); + _commandlineParser->addCommand(sgctConfigFileCommand); + + return true; +} + +bool OpenSpaceEngine::findConfiguration(std::string& filename) { + using ghoul::filesystem::Directory; + + Directory directory = FileSys.currentDirectory(); + std::string configurationName = _configurationFile; + + while (true) { + std::string&& fullPath = FileSys.pathByAppendingComponent(directory, + configurationName); + bool exists = FileSys.fileExists(fullPath); + if (exists) { + filename = fullPath; + return true; + } + + Directory nextDirectory = directory.parentDirectory(true); + + if (directory.path() == nextDirectory.path()) + // We have reached the root of the file system and did not find the file + return false; + directory = nextDirectory; + } +} + +bool OpenSpaceEngine::loadSpiceKernels() { + // Load time kernel + std::string timeKernel; + bool success = configurationManager()->getValue(ConfigurationManager::KeySpiceTimeKernel, timeKernel); + // Move this to configurationmanager::completenesscheck ---abock + if (!success) { + LERROR("Configuration file does not contain a '" << ConfigurationManager::KeySpiceTimeKernel << "'"); + return false; + } + SpiceManager::KernelIdentifier id = + SpiceManager::ref().loadKernel(timeKernel); + if (id == SpiceManager::KernelFailed) { + LERROR("Error loading time kernel '" << timeKernel << "'"); + return false; + } + + // Load SPICE leap second kernel + std::string leapSecondKernel; + success = configurationManager()->getValue(ConfigurationManager::KeySpiceLeapsecondKernel, leapSecondKernel); + if (!success) { + // Move this to configurationmanager::completenesscheck ---abock + LERROR("Configuration file does not have a '" << ConfigurationManager::KeySpiceLeapsecondKernel << "'"); + return false; + } + id = SpiceManager::ref().loadKernel(std::move(leapSecondKernel)); + if (id == SpiceManager::KernelFailed) { + LERROR("Error loading leap second kernel '" << leapSecondKernel << "'"); + return false; + } + return true; +} + +void OpenSpaceEngine::runScripts(const ghoul::Dictionary& scripts) { + for (size_t i = 1; i <= scripts.size(); ++i) { + std::stringstream stream; + stream << i; + const std::string& key = stream.str(); + const bool hasKey = scripts.hasKeyAndValue(key); + if (!hasKey) { + LERROR("The startup scripts have to be declared in a simple array format." + " Startup scripts did not contain the key '" << key << "'"); + break; + } + + std::string scriptPath; + scripts.getValue(key, scriptPath); + std::string&& absoluteScriptPath = absPath(scriptPath); + _engine->scriptEngine()->runScriptFile(absoluteScriptPath); + + //@JK + //temporary solution to ensure that startup scripts may be syncrhonized over parallel connection + std::ifstream scriptFile; + scriptFile.open(absoluteScriptPath.c_str()); + std::string line; + + while(getline(scriptFile,line)){ + //valid line and not a comment + if(line.size() > 0 && line.at(0) != '-'){ + std::string lib, func; + if(_engine->scriptEngine()->parseLibraryAndFunctionNames(lib, func, line) && + _engine->scriptEngine()->shouldScriptBeSent(lib, func)){ + _engine->scriptEngine()->cacheScript(lib, func, line); + } + } + } + } +} + + +void OpenSpaceEngine::runStartupScripts() { + ghoul::Dictionary scripts; + configurationManager()->getValue( + ConfigurationManager::KeyStartupScript, scripts); + runScripts(scripts); +} + +void OpenSpaceEngine::runSettingsScripts() { + ghoul::Dictionary scripts; + configurationManager()->getValue( + ConfigurationManager::KeySettingsScript, scripts); + runScripts(scripts); +} + +void OpenSpaceEngine::loadFonts() { + sgct_text::FontManager::FontPath local = sgct_text::FontManager::FontPath::FontPath_Local; + + ghoul::Dictionary fonts; + configurationManager()->getValue(ConfigurationManager::KeyFonts, fonts); + + for (const std::string& key : fonts.keys()) { + std::string font; + fonts.getValue(key, font); + font = absPath(font); + if(!FileSys.fileExists(font)) { + LERROR("Could not find font '" << font << "'"); + continue; + } + + LINFO("Registering font '" << font << "' with key '" << key << "'"); + sgct_text::FontManager::instance()->addFont(key, font, local); + } +} + +void OpenSpaceEngine::configureLogging() { + if (configurationManager()->hasKeyAndValue(ConfigurationManager::KeyLogLevel)) { + std::string logLevel; + configurationManager()->getValue(ConfigurationManager::KeyLogLevel, logLevel); + + bool immediateFlush = false; + configurationManager()->getValue(ConfigurationManager::KeyLogImmediateFlush, immediateFlush); + + LogManager::LogLevel level = LogManager::levelFromString(logLevel); + LogManager::deinitialize(); + LogManager::initialize(level, immediateFlush); + LogMgr.addLog(new ConsoleLog); + } + + if (configurationManager()->hasKeyAndValue(ConfigurationManager::KeyLogs)) { + ghoul::Dictionary logs; + configurationManager()->getValue(ConfigurationManager::KeyLogs, logs); + + for (size_t i = 1; i <= logs.size(); ++i) { + ghoul::Dictionary logInfo; + logs.getValue(std::to_string(i), logInfo); + + Log* log = LogFactory::createLog(logInfo); + + if (log) + LogMgr.addLog(log); + } + } +} + +ConfigurationManager* OpenSpaceEngine::configurationManager() { + ghoul_assert(_configurationManager != nullptr, "ConfigurationManager is nullptr"); + return _configurationManager; +} + +interaction::InteractionHandler* OpenSpaceEngine::interactionHandler() { + ghoul_assert(_interactionHandler != nullptr, "InteractionHandler is nullptr"); + return _interactionHandler; +} + +RenderEngine* OpenSpaceEngine::renderEngine() { + ghoul_assert(_renderEngine != nullptr, "RenderEngine is nullptr"); + return _renderEngine; +} + +ScriptEngine* OpenSpaceEngine::scriptEngine() { + ghoul_assert(_scriptEngine != nullptr, "ScriptEngine is nullptr"); + return _scriptEngine; +} + +LuaConsole* OpenSpaceEngine::console() { + ghoul_assert(_console != nullptr, "LuaConsole is nullptr"); + return _console; +} + +gui::GUI* OpenSpaceEngine::gui() { + ghoul_assert(_gui != nullptr, "GUI is nullptr"); + return _gui; +} + +bool OpenSpaceEngine::initializeGL() { + LINFO("Initializing Rendering Engine"); + bool success = _renderEngine->initializeGL(); + LINFO("Initializing OnScreen GUI GL"); + _gui->initializeGL(); + LINFO("Finished initializing OpenGL"); + return success; +} + +bool OpenSpaceEngine::isMaster(){ + return _isMaster; +} + +void OpenSpaceEngine::setMaster(bool master){ + _isMaster = master; +} + +double OpenSpaceEngine::runTime(){ + return _runTime; +} + +void OpenSpaceEngine::setRunTime(double d){ + _runTime = d; +} + +void OpenSpaceEngine::preSynchronization() { + FileSys.triggerFilesystemEvents(); + if (_isMaster) { + //const double dt = sgct::Engine::instance()->getDt(); + const double dt = sgct::Engine::instance()->getAvgDt(); + + Time::ref().advanceTime(dt); + Time::ref().preSynchronization(); + + _interactionHandler->update(dt); + _parallelConnection->update(dt); + //_interactionHandler.lockControls(); + + _scriptEngine->preSynchronization(); + _renderEngine->preSynchronization(); + } +} + +void OpenSpaceEngine::postSynchronizationPreDraw() { + Time::ref().postSynchronizationPreDraw(); + + _scriptEngine->postSynchronizationPreDraw(); + _renderEngine->postSynchronizationPreDraw(); + + + if (_isMaster && _gui->isEnabled()) { + double posX, posY; + sgct::Engine::instance()->getMousePos(0, &posX, &posY); + + int x,y; + sgct::Engine::instance()->getWindowPtr(0)->getFinalFBODimensions(x, y); + + int button0 = sgct::Engine::instance()->getMouseButton(0, 0); + int button1 = sgct::Engine::instance()->getMouseButton(0, 1); + bool buttons[2] = { button0 != 0, button1 != 0 }; + + double dt = std::max(sgct::Engine::instance()->getDt(), 1.0/60.0); + _gui->startFrame(static_cast(dt), glm::vec2(glm::ivec2(x,y)), glm::vec2(posX, posY), buttons); + } +} + +void OpenSpaceEngine::render(const glm::mat4 &projectionMatrix, const glm::mat4 &viewMatrix) { + _renderEngine->render(projectionMatrix, viewMatrix); + + if (_isMaster) { + // If currently writing a command, render it to screen + sgct::SGCTWindow* w = sgct::Engine::instance()->getActiveWindowPtr(); + if (_isMaster && !w->isUsingFisheyeRendering() && _console->isVisible()) { + _console->render(); + } + + if (_gui->isEnabled()) + _gui->endFrame(); + } +} + +void OpenSpaceEngine::postDraw() { + if (_isMaster) + //_interactionHandler.unlockControls(); + + _renderEngine->postDraw(); +} + +void OpenSpaceEngine::keyboardCallback(int key, int action) { + if (_isMaster) { + if (_gui->isEnabled()) { + bool isConsumed = _gui->keyCallback(key, action); + if (isConsumed) + return; + } + + if (static_cast(key) == _console->commandInputButton() && (action == SGCT_PRESS || action == SGCT_REPEAT)) + _console->toggleVisibility(); + + if (!_console->isVisible()) { + _interactionHandler->keyboardCallback(key, action); + } + else { + _console->keyboardCallback(key, action); + } + } +} + +void OpenSpaceEngine::charCallback(unsigned int codepoint) { + if (_isMaster) { + if (_gui->isEnabled()) { + bool isConsumed = _gui->charCallback(codepoint); + if (isConsumed) + return; + } + + if (_console->isVisible()) { + _console->charCallback(codepoint); + } + } +} + +void OpenSpaceEngine::mouseButtonCallback(int key, int action) { + if (_isMaster) { + if (_gui->isEnabled()) { + bool isConsumed = _gui->mouseButtonCallback(key, action); + if (isConsumed && action != SGCT_RELEASE) + return; + } + + _interactionHandler->mouseButtonCallback(key, action); + } +} + +void OpenSpaceEngine::mousePositionCallback(double x, double y) { + if (_isMaster) { + _interactionHandler->mousePositionCallback(x, y); + } +} + +void OpenSpaceEngine::mouseScrollWheelCallback(double pos) { + if (_isMaster) { + if (_gui->isEnabled()) { + bool isConsumed = _gui->mouseWheelCallback(pos); + if (isConsumed) + return; + } + + _interactionHandler->mouseScrollWheelCallback(pos); + } +} + +void OpenSpaceEngine::encode() { + if (_syncBuffer) { + Time::ref().serialize(_syncBuffer); + _scriptEngine->serialize(_syncBuffer); + _renderEngine->serialize(_syncBuffer); + + _syncBuffer->write(); + } + _networkEngine->publishStatusMessage(); + _networkEngine->sendMessages(); +} + +void OpenSpaceEngine::decode() { + if (_syncBuffer) { + _syncBuffer->read(); + + Time::ref().deserialize(_syncBuffer); + _scriptEngine->deserialize(_syncBuffer); + _renderEngine->deserialize(_syncBuffer); + } +} + +void OpenSpaceEngine::externalControlCallback(const char* receivedChars, + int size, int clientId) +{ + if (size == 0) + return; + + _networkEngine->handleMessage(std::string(receivedChars, size)); +} + +void OpenSpaceEngine::enableBarrier() { + sgct::SGCTWindow::setBarrier(true); +} + +void OpenSpaceEngine::disableBarrier() { + sgct::SGCTWindow::setBarrier(false); +} + +NetworkEngine* OpenSpaceEngine::networkEngine() { + return _networkEngine; +} + +ModuleEngine* OpenSpaceEngine::moduleEngine() { + return _moduleEngine; +} + +network::ParallelConnection* OpenSpaceEngine::parallelConnection() { + ghoul_assert(_parallelConnection != nullptr, "ParallelConnection is nullptr"); + return _parallelConnection; +} + +} // namespace openspace diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index 38dc20a29d..eb30ae75ca 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -362,7 +362,7 @@ void InteractionHandler::update(double deltaTime) { ghoul::Interpolator positionInterpLin; ghoul::Interpolator quatInterpLin; - openspace::network::StreamDataKeyframe p0, p1, p2, p3; + openspace::network::datamessagestructures::PositionKeyframe p0, p1, p2, p3; p0 = _keyframes[0]; p1 = _keyframes[1]; @@ -982,7 +982,7 @@ bool InteractionHandler::invertRotation() const { return _invertRotation; } -void InteractionHandler::addKeyframe(const network::StreamDataKeyframe &kf){ + void InteractionHandler::addKeyframe(const network::datamessagestructures::PositionKeyframe &kf){ _keyframeMutex.lock(); //save a maximum of 10 samples (1 seconds of buffer) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index 49d33185ef..af92a9f240 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -372,19 +372,20 @@ namespace openspace { //do nothing for now break; case MessageTypes::Initialization: - decodeInitializationMessage(); + initializationMessageReceived(); break; - case MessageTypes::StreamData: - decodeStreamDataMessage(); + case MessageTypes::Data: + dataMessageReceived(); break; case MessageTypes::Script: - decodeScriptMessage(); + //disabled for now +// decodeScriptMessage(); break; case MessageTypes::HostInfo: - decodeHostInfoMessage(); + hostInfoMessageReceived(); break; case MessageTypes::InitializationRequest: - decodeInitializationRequestMessage(); + initializationRequestMessageReceived(); break; default: //unknown message type @@ -392,7 +393,7 @@ namespace openspace { } } - void ParallelConnection::decodeInitializationMessage(){ + void ParallelConnection::initializationMessageReceived(){ int result; uint16_t numScripts; @@ -476,22 +477,36 @@ namespace openspace { } - void ParallelConnection::decodeStreamDataMessage(){ + void ParallelConnection::dataMessageReceived(){ int result; uint16_t msglen; + uint16_t type; //create a buffer to hold the size of streamdata message std::vector buffer; - buffer.resize(sizeof(msglen)); + buffer.resize(sizeof(type)); - //read size of streamdata message - result = receiveData(_clientSocket, buffer, sizeof(msglen), 0); + //read type of data message + result = receiveData(_clientSocket, buffer, sizeof(type), 0); if (result <= 0){ //error + LERROR("Failed to read type of data message received."); return; } + //the type of data message received + type =(*(reinterpret_cast(buffer.data()))); + + //read size of streamdata message + result = receiveData(_clientSocket, buffer, sizeof(msglen), 0); + + if (result <= 0){ + //error + LERROR("Failed to read size of data message received."); + return; + } + //the size in bytes of the streamdata message msglen = (*(reinterpret_cast(buffer.data()))); @@ -504,15 +519,49 @@ namespace openspace { if (result <= 0){ //error + LERROR("Failed to read data message."); return; } - //construct a keyframe ffrom the streamdata - network::StreamDataKeyframe kf; - kf.deserialize(buffer); - - //and add the keyframe to the interaction handler - OsEng.interactionHandler()->addKeyframe(kf); + //which type of data message was received? + switch(type){ + case network::datamessagestructures::PositionData:{ + //position data message + //create and read a position keyframe from the data buffer + network::datamessagestructures::PositionKeyframe kf; + kf.deserialize(buffer); + + //add the keyframe to the interaction handler + OsEng.interactionHandler()->addKeyframe(kf); + break; + } + case network::datamessagestructures::TimeData:{ + //time data message + //create and read a time keyframe from the data buffer + network::datamessagestructures::TimeKeyframe tf; + tf.deserialize(buffer); + + //set the time parameters based on recevied keyframe + Time::ref().setDeltaTime(tf._dt); + Time::ref().setPause(tf._paused); + Time::ref().setTime(tf._time, tf._requiresTimeJump); + break; + } + case network::datamessagestructures::ScriptData:{ + //script data message + //create and read a script message from data buffer + network::datamessagestructures::ScriptMessage sm; + sm.deserialize(buffer); + + //Que script to be executed by script engine + OsEng.scriptEngine()->queueScript(sm._script); + break; + } + default:{ + LERROR("Unidentified data message with identifier " << type << " received in parallel connection."); + break; + } + } } void ParallelConnection::decodeScriptMessage(){ @@ -586,7 +635,7 @@ namespace openspace { } } - void ParallelConnection::decodeHostInfoMessage(){ + void ParallelConnection::hostInfoMessageReceived(){ //create buffer std::vector hostflag; //resize to hold a flag saying if we're host or not @@ -654,7 +703,7 @@ namespace openspace { } } - void ParallelConnection::decodeInitializationRequestMessage(){ + void ParallelConnection::initializationRequestMessageReceived(){ std::vector buffer; buffer.resize(sizeof(uint32_t)); receiveData(_clientSocket, buffer, sizeof(uint32_t), 0); @@ -868,13 +917,51 @@ namespace openspace { return true; } + void ParallelConnection::update(double dt){ + + //get current time parameters and create a keyframe + network::datamessagestructures::TimeKeyframe tf; + tf._dt = Time::ref().deltaTime(); + tf._paused = Time::ref().paused(); + tf._requiresTimeJump = Time::ref().timeJumped(); + tf._time = Time::ref().currentTime(); + + //create a buffer and serialize message + std::vector tbuffer; + tf.serialize(tbuffer); + + //get the size of the keyframebuffer + uint16_t msglen = static_cast(tbuffer.size()); + + uint16_t type = static_cast(network::datamessagestructures::PositionData); + + //create the full buffer + std::vector buffer; + buffer.reserve(headerSize() + sizeof(type) + sizeof(msglen) + msglen); + + //write header + writeHeader(buffer, MessageTypes::Data); + + //type of message + buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(type)); + + //size of message + buffer.insert(buffer.end(), reinterpret_cast(&msglen), reinterpret_cast(&msglen) + sizeof(msglen)); + + //actual message + buffer.insert(buffer.end(), tbuffer.begin(), tbuffer.end()); + + //send message + queMessage(buffer); + } + void ParallelConnection::broadcast(){ //while we're still connected and we're the host while (_isConnected.load() && _isHost.load()){ //create a keyframe with current position and orientation of camera - network::StreamDataKeyframe kf; + network::datamessagestructures::PositionKeyframe kf; kf._position = OsEng.interactionHandler()->camera()->position(); kf._viewRotationQuat = glm::quat_cast(OsEng.interactionHandler()->camera()->viewRotationMatrix()); @@ -890,12 +977,17 @@ namespace openspace { //get the size of the keyframebuffer uint16_t msglen = static_cast(kfBuffer.size()); + uint16_t type = static_cast(network::datamessagestructures::PositionData); + //create the full buffer std::vector buffer; - buffer.reserve(headerSize() + sizeof(msglen) + msglen); + buffer.reserve(headerSize() + sizeof(type) + sizeof(msglen) + msglen); //write header - writeHeader(buffer, MessageTypes::StreamData); + writeHeader(buffer, MessageTypes::Data); + + //type of message + buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(type)); //size of message buffer.insert(buffer.end(), reinterpret_cast(&msglen), reinterpret_cast(&msglen) + sizeof(msglen)); diff --git a/src/util/time.cpp b/src/util/time.cpp index 20aa2ff71d..89c1c796cc 100644 --- a/src/util/time.cpp +++ b/src/util/time.cpp @@ -80,9 +80,9 @@ bool Time::isInitialized() { return (_instance != nullptr); } -void Time::setTime(double value) { +void Time::setTime(double value, bool requireJump) { _time = std::move(value); - _timeJumped = true; + _timeJumped = requireJump; } double Time::currentTime() const { @@ -116,9 +116,9 @@ bool Time::togglePause() { return _timePaused; } -void Time::setTime(std::string time) { +void Time::setTime(std::string time, bool requireJump) { SpiceManager::ref().getETfromDate(std::move(time), _time); - _timeJumped = true; + _timeJumped = requireJump; } std::string Time::currentTimeUTC() const { @@ -185,6 +185,10 @@ bool Time::timeJumped() const { void Time::setTimeJumped(bool jumped){ _timeJumped = jumped; } + +bool Time::paused() const{ + return _timePaused; +} scripting::ScriptEngine::LuaLibrary Time::luaLibrary() { scripting::ScriptEngine::LuaLibrary timeLibrary = { From 75a64e770fb17478d3669034c34b9af47ee02920 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 5 Jul 2015 16:23:48 +0200 Subject: [PATCH 272/329] Enable Properties to be set by strings --- .../openspace/properties/numericalproperty.h | 3 + .../properties/numericalproperty.inl | 97 ++++++++++- include/openspace/properties/property.h | 4 + .../openspace/properties/propertydelegate.h | 6 + .../openspace/properties/propertydelegate.inl | 14 ++ .../openspace/properties/selectionproperty.h | 8 + .../openspace/properties/templateproperty.h | 4 + .../openspace/properties/templateproperty.inl | 77 +++++++-- src/properties/matrixproperty.cpp | 137 +++++++++++++--- src/properties/property.cpp | 13 +- src/properties/scalarproperty.cpp | 153 +++++++++++++----- src/properties/selectionproperty.cpp | 25 +++ src/properties/stringproperty.cpp | 8 + src/properties/vectorproperty.cpp | 137 ++++++++++++---- 14 files changed, 581 insertions(+), 105 deletions(-) diff --git a/include/openspace/properties/numericalproperty.h b/include/openspace/properties/numericalproperty.h index 36edef62aa..52bfceb679 100644 --- a/include/openspace/properties/numericalproperty.h +++ b/include/openspace/properties/numericalproperty.h @@ -44,6 +44,9 @@ public: bool setLua(lua_State* state) override; int typeLua() const override; + bool getString(std::string& value) const override; + bool setString(std::string value) override; + T minValue() const; T maxValue() const; diff --git a/include/openspace/properties/numericalproperty.inl b/include/openspace/properties/numericalproperty.inl index e1bd5b77e1..d64a402b87 100644 --- a/include/openspace/properties/numericalproperty.inl +++ b/include/openspace/properties/numericalproperty.inl @@ -29,22 +29,29 @@ namespace properties { #define REGISTER_NUMERICALPROPERTY_HEADER(CLASS_NAME, TYPE) \ typedef NumericalProperty CLASS_NAME; \ + \ template <> \ std::string PropertyDelegate>::className(); \ + \ template <> \ std::string PropertyDelegate>::className(); \ + \ template <> \ template <> \ TYPE PropertyDelegate>::defaultValue(); \ + \ template <> \ template <> \ TYPE PropertyDelegate>::defaultMinimumValue(); \ + \ template <> \ template <> \ TYPE PropertyDelegate>::defaultMaximumValue(); \ + \ template <> \ template <> \ TYPE PropertyDelegate>::defaultSteppingValue(); \ + \ template <> \ template <> \ TYPE PropertyDelegate>::fromLuaValue(lua_State* state, \ @@ -64,61 +71,92 @@ namespace properties { template <> \ int PropertyDelegate>::typeLua(); \ template <> \ - int PropertyDelegate>::typeLua(); + int PropertyDelegate>::typeLua(); \ + \ + template <> \ + template <> \ + TYPE PropertyDelegate>::fromString(std::string value, \ + bool& success); \ + \ + template <> \ + template <> \ + TYPE PropertyDelegate>::fromString(std::string value, \ + bool& success); \ + \ + template <> \ + template <> \ + bool PropertyDelegate>::toString(std::string& outValue, \ + TYPE inValue); \ + \ + template <> \ + template <> \ + bool PropertyDelegate>::toString(std::string& outValue, \ + TYPE inValue); + #define REGISTER_NUMERICALPROPERTY_SOURCE(CLASS_NAME, TYPE, DEFAULT_VALUE, \ DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, \ DEFAULT_STEPPING, FROM_LUA_LAMBDA_EXPRESSION, \ - TO_LUA_LAMBDA_EXPRESSION, LUA_TYPE) \ + TO_LUA_LAMBDA_EXPRESSION, \ + FROM_STRING_LAMBDA_EXPRESSION, \ + TO_STRING_LAMBDA_EXPRESSION, LUA_TYPE) \ template <> \ std::string PropertyDelegate>::className() \ { \ return #CLASS_NAME; \ } \ + \ template <> \ std::string PropertyDelegate>::className() \ { \ return PropertyDelegate>::className(); \ } \ + \ template <> \ template <> \ TYPE PropertyDelegate>::defaultValue() \ { \ return DEFAULT_VALUE; \ } \ + \ template <> \ template <> \ TYPE PropertyDelegate>::defaultMinimumValue() \ { \ return DEFAULT_MIN_VALUE; \ } \ + \ template <> \ template <> \ TYPE PropertyDelegate>::defaultMaximumValue() \ { \ return DEFAULT_MAX_VALUE; \ } \ + \ template <> \ template <> \ TYPE PropertyDelegate>::defaultSteppingValue() \ { \ return DEFAULT_STEPPING; \ } \ + \ template <> \ template <> \ - TYPE PropertyDelegate>::fromLuaValue(lua_State * state, \ + TYPE PropertyDelegate>::fromLuaValue(lua_State* state, \ bool& success) \ { \ return FROM_LUA_LAMBDA_EXPRESSION(state, success); \ } \ + \ template <> \ template <> \ TYPE PropertyDelegate>::fromLuaValue( \ - lua_State * state, bool& success) \ + lua_State* state, bool& success) \ { \ return PropertyDelegate>::fromLuaValue(state, \ success); \ } \ + \ template <> \ template <> \ bool PropertyDelegate>::toLuaValue(lua_State * state, \ @@ -126,6 +164,7 @@ namespace properties { { \ return TO_LUA_LAMBDA_EXPRESSION(state, value); \ } \ + \ template <> \ template <> \ bool PropertyDelegate>::toLuaValue(lua_State * state, \ @@ -133,15 +172,50 @@ namespace properties { { \ return PropertyDelegate>::toLuaValue(state, value); \ } \ + \ template <> \ int PropertyDelegate>::typeLua() \ { \ return LUA_TYPE; \ } \ + \ template <> \ int PropertyDelegate>::typeLua() \ { \ return PropertyDelegate>::typeLua(); \ + } \ + \ + template <> \ + template <> \ + TYPE PropertyDelegate>::fromString(std::string value, \ + bool& success) \ + { \ + return FROM_STRING_LAMBDA_EXPRESSION(value, success); \ + } \ + \ + template <> \ + template <> \ + TYPE PropertyDelegate>::fromString(std::string value, \ + bool& success) \ + { \ + return PropertyDelegate>::fromString(value, \ + success); \ + } \ + \ + template <> \ + template <> \ + bool PropertyDelegate>::toString(std::string& outValue, \ + TYPE inValue) \ + { \ + return TO_STRING_LAMBDA_EXPRESSION(outValue, inValue); \ + } \ + \ + template <> \ + template <> \ + bool PropertyDelegate>::toString(std::string& outValue, \ + TYPE inValue) \ + { \ + return PropertyDelegate>::toString(outValue, inValue); \ } @@ -227,6 +301,21 @@ int NumericalProperty::typeLua() const { return PropertyDelegate>::typeLua(); } +template +bool NumericalProperty::getString(std::string& value) const { + bool success = PropertyDelegate>::template toString(value, _value); + return success; +} + +template +bool NumericalProperty::setString(std::string value) { + bool success = false; + T thisValue = PropertyDelegate>::template fromString(value, success); + if (success) + set(boost::any(thisValue)); + return success; +} + template T NumericalProperty::minValue() const { return _minimumValue; diff --git a/include/openspace/properties/property.h b/include/openspace/properties/property.h index 471ed0579a..93ce1f5d32 100644 --- a/include/openspace/properties/property.h +++ b/include/openspace/properties/property.h @@ -152,6 +152,10 @@ public: */ virtual int typeLua() const; + virtual bool getString(std::string& value) const; + + virtual bool setString(std::string value); + /** * This method registers a callback function that will be called every * time if either Property:set or Property::setLua was called with a value that is diff --git a/include/openspace/properties/propertydelegate.h b/include/openspace/properties/propertydelegate.h index f09ca43686..b9bf9f218f 100644 --- a/include/openspace/properties/propertydelegate.h +++ b/include/openspace/properties/propertydelegate.h @@ -149,6 +149,12 @@ public: * PropertyDelegate::toLuaValue and PropertyDelegate::fromLuaValue methods. */ static int typeLua(); + + template + static U fromString(std::string value, bool& success); + + template + static bool toString(std::string& outValue, U inValue); }; } // namespace properties diff --git a/include/openspace/properties/propertydelegate.inl b/include/openspace/properties/propertydelegate.inl index 60cedb8a9a..21fa253e1d 100644 --- a/include/openspace/properties/propertydelegate.inl +++ b/include/openspace/properties/propertydelegate.inl @@ -81,5 +81,19 @@ int PropertyDelegate::typeLua() { "Unimplemented PropertyDelegate::luaType specialization"); } +template +template +bool PropertyDelegate::toString(std::string& outValue, U inValue) { + static_assert(sizeof(T) == 0, + "Unimplemented PropertyDelegate::toString specialization"); +} + +template +template +U PropertyDelegate::fromString(std::string value, bool& success) { + static_assert(sizeof(T) == 0, + "Unimplemented PropertyDelegate::fromString specialization"); +} + } // namespace properties } // namespace openspace diff --git a/include/openspace/properties/selectionproperty.h b/include/openspace/properties/selectionproperty.h index b7a2277f25..1e793354cc 100644 --- a/include/openspace/properties/selectionproperty.h +++ b/include/openspace/properties/selectionproperty.h @@ -67,6 +67,14 @@ bool PropertyDelegate>>::toLuaValue(lua_State* template <> int PropertyDelegate>>::typeLua(); +template <> +template <> +std::vector PropertyDelegate>>::fromString(std::string value, bool& success); + +template <> +template <> +bool PropertyDelegate>>::toString(std::string& outValue, std::vector inValue); + } // namespace properties } // namespace openspace diff --git a/include/openspace/properties/templateproperty.h b/include/openspace/properties/templateproperty.h index 000754e4d3..464e11d1c9 100644 --- a/include/openspace/properties/templateproperty.h +++ b/include/openspace/properties/templateproperty.h @@ -127,6 +127,10 @@ public: /// \see Property::typeLua int typeLua() const override; + bool getString(std::string& value) const override; + + bool setString(std::string value) override; + /** * Returns the description for this TemplateProperty as a Lua script that returns a * table on execution diff --git a/include/openspace/properties/templateproperty.inl b/include/openspace/properties/templateproperty.inl index 2b68100a91..a897cae89d 100644 --- a/include/openspace/properties/templateproperty.inl +++ b/include/openspace/properties/templateproperty.inl @@ -37,21 +37,37 @@ namespace properties { // TYPE = The template parameter T for which the TemplateProperty is specialized #define REGISTER_TEMPLATEPROPERTY_HEADER(CLASS_NAME, TYPE) \ typedef TemplateProperty CLASS_NAME; \ + \ template <> \ std::string PropertyDelegate>::className(); \ + \ template <> \ template <> \ TYPE PropertyDelegate>::defaultValue(); \ + \ template <> \ template <> \ TYPE PropertyDelegate>::fromLuaValue(lua_State* state, \ bool& success); \ + \ template <> \ template <> \ bool PropertyDelegate>::toLuaValue(lua_State* state, \ TYPE value); \ + \ template <> \ - int PropertyDelegate>::typeLua(); + int PropertyDelegate>::typeLua(); \ + \ + template <> \ + template <> \ + TYPE PropertyDelegate>::fromString(std::string value, \ + bool& success); \ + \ + template <> \ + template <> \ + bool PropertyDelegate>::toString(std::string& outValue, \ + TYPE inValue); + // CLASS_NAME = The string that the Property::className() should return as well as the // C++ class name for which a typedef will be created @@ -70,18 +86,22 @@ namespace properties { // Lambda expressions #define REGISTER_TEMPLATEPROPERTY_SOURCE(CLASS_NAME, TYPE, DEFAULT_VALUE, \ FROM_LUA_LAMBDA_EXPRESSION, \ - TO_LUA_LAMBDA_EXPRESSION, LUA_TYPE) \ + TO_LUA_LAMBDA_EXPRESSION, \ + FROM_STRING_LAMBDA_EXPRESSION, \ + TO_STRING_LAMBDA_EXPRESSION, LUA_TYPE) \ template <> \ std::string PropertyDelegate>::className() \ { \ return #CLASS_NAME; \ } \ + \ template <> \ template <> \ TYPE PropertyDelegate>::defaultValue() \ { \ return DEFAULT_VALUE; \ } \ + \ template <> \ template <> \ TYPE PropertyDelegate>::fromLuaValue(lua_State * state, \ @@ -89,6 +109,7 @@ namespace properties { { \ return FROM_LUA_LAMBDA_EXPRESSION(state, success); \ } \ + \ template <> \ template <> \ bool PropertyDelegate>::toLuaValue(lua_State * state, \ @@ -96,11 +117,30 @@ namespace properties { { \ return TO_LUA_LAMBDA_EXPRESSION(state, value); \ } \ + \ template <> \ int PropertyDelegate>::typeLua() \ { \ return LUA_TYPE; \ - } + } \ + \ + template <> \ + template <> \ + TYPE PropertyDelegate>::fromString(std::string value, \ + bool& success) \ + { \ + return FROM_STRING_LAMBDA_EXPRESSION(value, success); \ + } \ + \ + template <> \ + template <> \ + bool PropertyDelegate>::toString(std::string& outValue, \ + TYPE inValue) \ + { \ + return TO_STRING_LAMBDA_EXPRESSION(outValue, inValue); \ + } \ + + // Delegating constructors are necessary; automatic template deduction cannot // deduce template argument for 'U' if 'default' methods are used as default values in @@ -195,8 +235,13 @@ const std::type_info& TemplateProperty::type() const { } template -bool TemplateProperty::setLua(lua_State* state) -{ +bool TemplateProperty::getLua(lua_State* state) const { + bool success = PropertyDelegate>::template toLuaValue(state, _value); + return success; +} + +template +bool TemplateProperty::setLua(lua_State* state) { bool success = false; T thisValue = PropertyDelegate>::template fromLuaValue(state, success); if (success) @@ -204,17 +249,25 @@ bool TemplateProperty::setLua(lua_State* state) return success; } -template -bool TemplateProperty::getLua(lua_State* state) const -{ - bool success = PropertyDelegate>::template toLuaValue(state, _value); - return success; -} - template int TemplateProperty::typeLua() const { return PropertyDelegate>::typeLua(); } +template +bool TemplateProperty::getString(std::string& value) const { + bool success = PropertyDelegate>::template toString(value, _value); + return success; +} + +template +bool TemplateProperty::setString(std::string value) { + bool success = false; + T thisValue = PropertyDelegate>::template fromString(value, success); + if (success) + set(boost::any(thisValue)); + return success; +} + } // namespace properties } // namespace openspace diff --git a/src/properties/matrixproperty.cpp b/src/properties/matrixproperty.cpp index 11f67f0c7d..37eeb75787 100644 --- a/src/properties/matrixproperty.cpp +++ b/src/properties/matrixproperty.cpp @@ -24,7 +24,11 @@ #include +#include + #include +#include +#include using std::numeric_limits; @@ -32,7 +36,7 @@ namespace openspace { namespace properties { #define DEFAULT_FROM_LUA_LAMBDA(__TYPE__) \ - [](lua_State * state, bool& success) -> __TYPE__ { \ + [](lua_State* state, bool& success) -> __TYPE__ { \ __TYPE__ result; \ int number = 1; \ for (__TYPE__::size_type i = 0; i < __TYPE__::row_size(); ++i) { \ @@ -54,7 +58,7 @@ namespace properties { } #define DEFAULT_TO_LUA_LAMBDA(__TYPE__) \ - [](lua_State * state, __TYPE__ value) -> bool { \ + [](lua_State* state, __TYPE__ value) -> bool { \ lua_newtable(state); \ int number = 1; \ for (__TYPE__::size_type i = 0; i < __TYPE__::row_size(); ++i) { \ @@ -67,6 +71,45 @@ namespace properties { return true; \ } +#define DEFAULT_FROM_STRING_LAMBDA(__TYPE__) \ + [](std::string value, bool& success) -> __TYPE__ { \ + __TYPE__ result; \ + std::vector tokens = ghoul::tokenizeString(value, ','); \ + if (tokens.size() != (__TYPE__::row_size() * __TYPE__::col_size())) { \ + success = false; \ + return result; \ + } \ + int number = 0; \ + for (__TYPE__::size_type i = 0; i < __TYPE__::row_size(); ++i) { \ + for (__TYPE__::size_type j = 0; j < __TYPE__::col_size(); ++j) { \ + std::stringstream s(tokens[number]); \ + __TYPE__::value_type v; \ + s >> v; \ + if (s.fail()) { \ + success = false; \ + return result; \ + } \ + else { \ + result[i][j] = v; \ + ++number; \ + } \ + } \ + } \ + success = true; \ + return result; \ + } + +#define DEFAULT_TO_STRING_LAMBDA(__TYPE__) \ + [](std::string& outValue, __TYPE__ inValue) -> bool { \ + outValue = ""; \ + for (__TYPE__::size_type i = 0; i < __TYPE__::row_size(); ++i) { \ + for (__TYPE__::size_type j = 0; j < __TYPE__::col_size(); ++j) { \ + outValue += std::to_string(inValue[i][j]) + ","; \ + } \ + } \ + return true; \ + } + REGISTER_NUMERICALPROPERTY_SOURCE(Mat2Property, glm::mat2x2, glm::mat2x2(0), glm::mat2x2( numeric_limits::lowest(), @@ -85,7 +128,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(Mat2Property, glm::mat2x2, glm::mat2x2(0), 0.01f, 0.01f ), DEFAULT_FROM_LUA_LAMBDA(glm::mat2x2), - DEFAULT_TO_LUA_LAMBDA(glm::mat2x2), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::mat2x2), + DEFAULT_FROM_STRING_LAMBDA(glm::mat2x2), + DEFAULT_TO_STRING_LAMBDA(glm::mat2x2), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(Mat2x3Property, glm::mat2x3, glm::mat2x3(0), glm::mat2x3( @@ -109,7 +155,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(Mat2x3Property, glm::mat2x3, glm::mat2x3(0), 0.01f, 0.01f, 0.01f ), DEFAULT_FROM_LUA_LAMBDA(glm::mat2x3), - DEFAULT_TO_LUA_LAMBDA(glm::mat2x3), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::mat2x3), + DEFAULT_FROM_STRING_LAMBDA(glm::mat2x3), + DEFAULT_TO_STRING_LAMBDA(glm::mat2x3), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(Mat2x4Property, glm::mat2x4, glm::mat2x4(0), glm::mat2x4( @@ -137,7 +186,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(Mat2x4Property, glm::mat2x4, glm::mat2x4(0), 0.01f, 0.01f, 0.01f, 0.01f ), DEFAULT_FROM_LUA_LAMBDA(glm::mat2x4), - DEFAULT_TO_LUA_LAMBDA(glm::mat2x4), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::mat2x4), + DEFAULT_FROM_STRING_LAMBDA(glm::mat2x4), + DEFAULT_TO_STRING_LAMBDA(glm::mat2x4), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(Mat3x2Property, glm::mat3x2, glm::mat3x2(0), glm::mat3x2( @@ -161,7 +213,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(Mat3x2Property, glm::mat3x2, glm::mat3x2(0), 0.01f, 0.01f, 0.01f ), DEFAULT_FROM_LUA_LAMBDA(glm::mat3x2), - DEFAULT_TO_LUA_LAMBDA(glm::mat3x2), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::mat3x2), + DEFAULT_FROM_STRING_LAMBDA(glm::mat3x2), + DEFAULT_TO_STRING_LAMBDA(glm::mat3x2), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(Mat3Property, glm::mat3x3, glm::mat3x3(0), glm::mat3x3( @@ -192,7 +247,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(Mat3Property, glm::mat3x3, glm::mat3x3(0), 0.01f, 0.01f, 0.01f ), DEFAULT_FROM_LUA_LAMBDA(glm::mat3x3), - DEFAULT_TO_LUA_LAMBDA(glm::mat3x3), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::mat3x3), + DEFAULT_FROM_STRING_LAMBDA(glm::mat3x3), + DEFAULT_TO_STRING_LAMBDA(glm::mat3x3), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(Mat3x4Property, glm::mat3x4, glm::mat3x4(0), glm::mat3x4( @@ -229,7 +287,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(Mat3x4Property, glm::mat3x4, glm::mat3x4(0), 0.01f, 0.01f, 0.01f, 0.01f ), DEFAULT_FROM_LUA_LAMBDA(glm::mat3x4), - DEFAULT_TO_LUA_LAMBDA(glm::mat3x4), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::mat3x4), + DEFAULT_FROM_STRING_LAMBDA(glm::mat3x4), + DEFAULT_TO_STRING_LAMBDA(glm::mat3x4), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(Mat4x2Property, glm::mat4x2, glm::mat4x2(0), glm::mat4x2( @@ -257,7 +318,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(Mat4x2Property, glm::mat4x2, glm::mat4x2(0), 0.01f, 0.01f, 0.01f, 0.01f ), DEFAULT_FROM_LUA_LAMBDA(glm::mat4x2), - DEFAULT_TO_LUA_LAMBDA(glm::mat4x2), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::mat4x2), + DEFAULT_FROM_STRING_LAMBDA(glm::mat4x2), + DEFAULT_TO_STRING_LAMBDA(glm::mat4x2), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(Mat4x3Property, glm::mat4x3, glm::mat4x3(0), glm::mat4x3( @@ -294,7 +358,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(Mat4x3Property, glm::mat4x3, glm::mat4x3(0), 0.01f, 0.01f, 0.01f, 0.01f ), DEFAULT_FROM_LUA_LAMBDA(glm::mat4x3), - DEFAULT_TO_LUA_LAMBDA(glm::mat4x3), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::mat4x3), + DEFAULT_FROM_STRING_LAMBDA(glm::mat4x3), + DEFAULT_TO_STRING_LAMBDA(glm::mat4x3), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(Mat4Property, glm::mat4x4, glm::mat4x4(0), glm::mat4x4( @@ -340,7 +407,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(Mat4Property, glm::mat4x4, glm::mat4x4(0), 0.01f, 0.01f, 0.01f, 0.01f ), DEFAULT_FROM_LUA_LAMBDA(glm::mat4x4), - DEFAULT_TO_LUA_LAMBDA(glm::mat4x4), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::mat4x4), + DEFAULT_FROM_STRING_LAMBDA(glm::mat4x4), + DEFAULT_TO_STRING_LAMBDA(glm::mat4x4), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(DMat2Property, glm::dmat2x2, glm::dmat2x2(0), glm::dmat2x2( @@ -360,7 +430,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(DMat2Property, glm::dmat2x2, glm::dmat2x2(0), 0.01, 0.01 ), DEFAULT_FROM_LUA_LAMBDA(glm::dmat2x2), - DEFAULT_TO_LUA_LAMBDA(glm::dmat2x2), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::dmat2x2), + DEFAULT_FROM_STRING_LAMBDA(glm::dmat2x2), + DEFAULT_TO_STRING_LAMBDA(glm::dmat2x2), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(DMat2x3Property, glm::dmat2x3, glm::dmat2x3(0), glm::dmat2x3( @@ -384,7 +457,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(DMat2x3Property, glm::dmat2x3, glm::dmat2x3(0) 0.01, 0.01, 0.01 ), DEFAULT_FROM_LUA_LAMBDA(glm::dmat2x3), - DEFAULT_TO_LUA_LAMBDA(glm::dmat2x3), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::dmat2x3), + DEFAULT_FROM_STRING_LAMBDA(glm::dmat2x3), + DEFAULT_TO_STRING_LAMBDA(glm::dmat2x3), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(DMat2x4Property, glm::dmat2x4, glm::dmat2x4(0), glm::dmat2x4( @@ -412,7 +488,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(DMat2x4Property, glm::dmat2x4, glm::dmat2x4(0) 0.01, 0.01, 0.01, 0.01 ), DEFAULT_FROM_LUA_LAMBDA(glm::dmat2x4), - DEFAULT_TO_LUA_LAMBDA(glm::dmat2x4), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::dmat2x4), + DEFAULT_FROM_STRING_LAMBDA(glm::dmat2x4), + DEFAULT_TO_STRING_LAMBDA(glm::dmat2x4), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(DMat3x2Property, glm::dmat3x2, glm::dmat3x2(0), glm::dmat3x2( @@ -436,7 +515,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(DMat3x2Property, glm::dmat3x2, glm::dmat3x2(0) 0.01, 0.01, 0.01 ), DEFAULT_FROM_LUA_LAMBDA(glm::dmat3x2), - DEFAULT_TO_LUA_LAMBDA(glm::dmat3x2), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::dmat3x2), + DEFAULT_FROM_STRING_LAMBDA(glm::dmat3x2), + DEFAULT_TO_STRING_LAMBDA(glm::dmat3x2), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(DMat3Property, glm::dmat3x3, glm::dmat3x3(0), glm::dmat3x3( @@ -467,7 +549,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(DMat3Property, glm::dmat3x3, glm::dmat3x3(0), 0.01, 0.01, 0.01 ), DEFAULT_FROM_LUA_LAMBDA(glm::dmat3x3), - DEFAULT_TO_LUA_LAMBDA(glm::dmat3x3), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::dmat3x3), + DEFAULT_FROM_STRING_LAMBDA(glm::dmat3x3), + DEFAULT_TO_STRING_LAMBDA(glm::dmat3x3), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(DMat3x4Property, glm::dmat3x4, glm::dmat3x4(0), glm::dmat3x4( @@ -504,7 +589,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(DMat3x4Property, glm::dmat3x4, glm::dmat3x4(0) 0.01, 0.01, 0.01, 0.01 ), DEFAULT_FROM_LUA_LAMBDA(glm::dmat3x4), - DEFAULT_TO_LUA_LAMBDA(glm::dmat3x4), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::dmat3x4), + DEFAULT_FROM_STRING_LAMBDA(glm::dmat3x4), + DEFAULT_TO_STRING_LAMBDA(glm::dmat3x4), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(DMat4x2Property, glm::dmat4x2, glm::dmat4x2(0), glm::dmat4x2( @@ -532,7 +620,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(DMat4x2Property, glm::dmat4x2, glm::dmat4x2(0) 0.01, 0.01, 0.01, 0.01 ), DEFAULT_FROM_LUA_LAMBDA(glm::dmat4x2), - DEFAULT_TO_LUA_LAMBDA(glm::dmat4x2), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::dmat4x2), + DEFAULT_FROM_STRING_LAMBDA(glm::dmat4x2), + DEFAULT_TO_STRING_LAMBDA(glm::dmat4x2), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(DMat4x3Property, glm::dmat4x3, glm::dmat4x3(0), glm::dmat4x3( @@ -569,7 +660,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(DMat4x3Property, glm::dmat4x3, glm::dmat4x3(0) 0.01, 0.01, 0.01, 0.01 ), DEFAULT_FROM_LUA_LAMBDA(glm::dmat4x3), - DEFAULT_TO_LUA_LAMBDA(glm::dmat4x3), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::dmat4x3), + DEFAULT_FROM_STRING_LAMBDA(glm::dmat4x3), + DEFAULT_TO_STRING_LAMBDA(glm::dmat4x3), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(DMat4Property, glm::dmat4x4, glm::dmat4x4(0), glm::dmat4x4( @@ -615,7 +709,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(DMat4Property, glm::dmat4x4, glm::dmat4x4(0), 0.01, 0.01, 0.01, 0.01 ), DEFAULT_FROM_LUA_LAMBDA(glm::dmat4x4), - DEFAULT_TO_LUA_LAMBDA(glm::dmat4x4), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::dmat4x4), + DEFAULT_FROM_STRING_LAMBDA(glm::dmat4x4), + DEFAULT_TO_STRING_LAMBDA(glm::dmat4x4), + LUA_TTABLE); } // namespace properties } // namespace openspace diff --git a/src/properties/property.cpp b/src/properties/property.cpp index fda0a6cc17..2db40b0ae9 100644 --- a/src/properties/property.cpp +++ b/src/properties/property.cpp @@ -86,11 +86,10 @@ boost::any Property::get() const { } bool Property::getLua(lua_State* state) const { - return true; + return false; } -void Property::set(boost::any value) { -} +void Property::set(boost::any value) {} bool Property::setLua(lua_State* state) { return false; @@ -104,6 +103,14 @@ int Property::typeLua() const { return LUA_TNIL; } +bool Property::getString(std::string& value) const { + return false; +} + +bool Property::setString(std::string value) { + return false; +} + std::string Property::guiName() const { std::string result; _metaData.getValue(_metaDataKeyGuiName, result); diff --git a/src/properties/scalarproperty.cpp b/src/properties/scalarproperty.cpp index e46b266308..fe817b7f78 100644 --- a/src/properties/scalarproperty.cpp +++ b/src/properties/scalarproperty.cpp @@ -27,27 +27,44 @@ #include #include +#include using std::numeric_limits; namespace openspace { namespace properties { -#define DEFAULT_FROM_LUA_LAMBDA(TYPE, DEFAULT_VALUE) \ - [](lua_State* state, bool& success) -> TYPE { \ - success = (lua_isnumber(state, -1) == 1); \ - if (success) \ - return static_cast(lua_tonumber(state, -1)); \ - else \ - return DEFAULT_VALUE; \ +#define DEFAULT_FROM_LUA_LAMBDA(TYPE, DEFAULT_VALUE) \ + [](lua_State* state, bool& success) -> TYPE { \ + success = (lua_isnumber(state, -1) == 1); \ + if (success) \ + return static_cast(lua_tonumber(state, -1)); \ + else \ + return DEFAULT_VALUE; \ } -#define DEFAULT_TO_LUA_LAMBDA(TYPE) \ - [](lua_State* state, TYPE value) -> bool { \ - lua_pushnumber(state, static_cast(value)); \ - return true; \ +#define DEFAULT_TO_LUA_LAMBDA(TYPE) \ + [](lua_State* state, TYPE value) -> bool { \ + lua_pushnumber(state, static_cast(value)); \ + return true; \ } +#define DEFAULT_FROM_STRING_LAMBDA(TYPE, DEFAULT_VALUE) \ + [](std::string value, bool& success) -> TYPE { \ + std::stringstream s(value); \ + TYPE v; \ + s >> v; \ + success = !s.fail(); \ + if (success) \ + return v; \ + } + +#define DEFAULT_TO_STRING_LAMBDA(TYPE) \ + [](std::string& outValue, TYPE inValue) -> bool { \ + outValue = std::to_string(inValue); \ + return true; \ + } + // char16_t and char32_t are not supported on Visual Studio 2013 and are defined to // be equal to unsigned short and unsigned int which causes a compile error @@ -63,6 +80,8 @@ REGISTER_TEMPLATEPROPERTY_SOURCE(BoolProperty, bool, false, lua_pushboolean(state, value); return true; }, + DEFAULT_FROM_STRING_LAMBDA(bool, false), + DEFAULT_TO_STRING_LAMBDA(bool), LUA_TBOOLEAN ); @@ -70,7 +89,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(CharProperty, char, char(0), numeric_limits::lowest(), numeric_limits::max(), char(1), DEFAULT_FROM_LUA_LAMBDA(char, char(0)), - DEFAULT_TO_LUA_LAMBDA(char), LUA_TNUMBER); + DEFAULT_TO_LUA_LAMBDA(char), + DEFAULT_FROM_STRING_LAMBDA(char, char(0)), + DEFAULT_TO_STRING_LAMBDA(char), + LUA_TNUMBER); // REGISTER_NUMERICALPROPERTY_SOURCE(Char16Property, char16_t, char16_t(0), // numeric_limits::lowest(), numeric_limits::max(), char16_t(1)); @@ -78,92 +100,145 @@ REGISTER_NUMERICALPROPERTY_SOURCE(CharProperty, char, char(0), // REGISTER_NUMERICALPROPERTY_SOURCE(Char32Property, char32_t, char32_t(0), // numeric_limits::lowest(), numeric_limits::max(), char32_t(1)); -REGISTER_NUMERICALPROPERTY_SOURCE(WCharProperty, wchar_t, wchar_t(0), - numeric_limits::lowest(), - numeric_limits::max(), wchar_t(1), - DEFAULT_FROM_LUA_LAMBDA(wchar_t, wchar_t(0)), - DEFAULT_TO_LUA_LAMBDA(wchar_t), LUA_TNUMBER); +//REGISTER_NUMERICALPROPERTY_SOURCE(WCharProperty, wchar_t, wchar_t(0), +// numeric_limits::lowest(), +// numeric_limits::max(), wchar_t(1), +// DEFAULT_FROM_LUA_LAMBDA(wchar_t, wchar_t(0)), +// DEFAULT_TO_LUA_LAMBDA(wchar_t), +// DEFAULT_FROM_STRING_LAMBDA(wchar_t, wchar_t(0)), +// DEFAULT_TO_STRING_LAMBDA(wchar_t), +// LUA_TNUMBER); REGISTER_NUMERICALPROPERTY_SOURCE(SignedCharProperty, signed char, (signed char)(0), numeric_limits::lowest(), numeric_limits::max(), (signed char)0, DEFAULT_FROM_LUA_LAMBDA(signed char, (signed char)(0)), - DEFAULT_TO_LUA_LAMBDA(signed char), LUA_TNUMBER); + DEFAULT_TO_LUA_LAMBDA(signed char), + DEFAULT_FROM_STRING_LAMBDA(signed char, (signed char)(0)), + DEFAULT_TO_STRING_LAMBDA(signed char), + LUA_TNUMBER); REGISTER_NUMERICALPROPERTY_SOURCE(UCharProperty, unsigned char, (unsigned char)0, numeric_limits::lowest(), numeric_limits::max(), (unsigned char)1, DEFAULT_FROM_LUA_LAMBDA(unsigned char, (unsigned char)(0)), - DEFAULT_TO_LUA_LAMBDA(unsigned char), LUA_TNUMBER); + DEFAULT_TO_LUA_LAMBDA(unsigned char), + DEFAULT_FROM_STRING_LAMBDA(unsigned char, + (unsigned char)(0)), + DEFAULT_TO_STRING_LAMBDA(unsigned char), + LUA_TNUMBER); REGISTER_NUMERICALPROPERTY_SOURCE(ShortProperty, short, short(0), numeric_limits::lowest(), numeric_limits::max(), short(1), DEFAULT_FROM_LUA_LAMBDA(short, short(0)), - DEFAULT_TO_LUA_LAMBDA(short), LUA_TNUMBER); + DEFAULT_TO_LUA_LAMBDA(short), + DEFAULT_FROM_STRING_LAMBDA(short, short(0)), + DEFAULT_TO_STRING_LAMBDA(short), + LUA_TNUMBER); -REGISTER_NUMERICALPROPERTY_SOURCE( - UShortProperty, unsigned short, (unsigned short)(0), - numeric_limits::lowest(), numeric_limits::max(), - (unsigned short)1, DEFAULT_FROM_LUA_LAMBDA(unsigned short, (unsigned short)(0)), - DEFAULT_TO_LUA_LAMBDA(unsigned short), LUA_TNUMBER); +REGISTER_NUMERICALPROPERTY_SOURCE(UShortProperty, unsigned short, (unsigned short)(0), + numeric_limits::lowest(), + numeric_limits::max(), + (unsigned short)1, + DEFAULT_FROM_LUA_LAMBDA(unsigned short, + (unsigned short)(0)), + DEFAULT_TO_LUA_LAMBDA(unsigned short), + DEFAULT_FROM_STRING_LAMBDA(unsigned short, + (unsigned short)(0)), + DEFAULT_TO_STRING_LAMBDA(unsigned short), + LUA_TNUMBER); REGISTER_NUMERICALPROPERTY_SOURCE(IntProperty, int, int(0), numeric_limits::lowest(), numeric_limits::max(), int(1), DEFAULT_FROM_LUA_LAMBDA(int, int(0)), - DEFAULT_TO_LUA_LAMBDA(int), LUA_TNUMBER); + DEFAULT_TO_LUA_LAMBDA(int), + DEFAULT_FROM_STRING_LAMBDA(int, int(0)), + DEFAULT_TO_STRING_LAMBDA(int), + LUA_TNUMBER); REGISTER_NUMERICALPROPERTY_SOURCE(UIntProperty, unsigned int, (unsigned int)0, numeric_limits::lowest(), numeric_limits::max(), (unsigned int)1, DEFAULT_FROM_LUA_LAMBDA(unsigned int, (unsigned int)(0)), - DEFAULT_TO_LUA_LAMBDA(unsigned int), LUA_TNUMBER); + DEFAULT_TO_LUA_LAMBDA(unsigned int), + DEFAULT_FROM_STRING_LAMBDA(unsigned int, + (unsigned int)(0)), + DEFAULT_TO_STRING_LAMBDA(unsigned int), + LUA_TNUMBER); REGISTER_NUMERICALPROPERTY_SOURCE(LongProperty, long, long(0), numeric_limits::lowest(), numeric_limits::max(), long(1), DEFAULT_FROM_LUA_LAMBDA(long, long(0)), - DEFAULT_TO_LUA_LAMBDA(long), LUA_TNUMBER); + DEFAULT_TO_LUA_LAMBDA(long), + DEFAULT_FROM_STRING_LAMBDA(long, long(0)), + DEFAULT_TO_STRING_LAMBDA(long), + LUA_TNUMBER); REGISTER_NUMERICALPROPERTY_SOURCE(ULongProperty, unsigned long, (unsigned long)0, numeric_limits::lowest(), numeric_limits::max(), (unsigned long)1, DEFAULT_FROM_LUA_LAMBDA(unsigned long, (unsigned long)(0)), - DEFAULT_TO_LUA_LAMBDA(unsigned long), LUA_TNUMBER); + DEFAULT_TO_LUA_LAMBDA(unsigned long), + DEFAULT_FROM_STRING_LAMBDA(unsigned long, + (unsigned long)(0)), + DEFAULT_TO_STRING_LAMBDA(unsigned long), + LUA_TNUMBER); REGISTER_NUMERICALPROPERTY_SOURCE(LongLongProperty, long long, (long long)0, numeric_limits::lowest(), numeric_limits::max(), (long long)1, DEFAULT_FROM_LUA_LAMBDA(long long, (long long)(0)), - DEFAULT_TO_LUA_LAMBDA(long long), LUA_TNUMBER); + DEFAULT_TO_LUA_LAMBDA(long long), + DEFAULT_FROM_STRING_LAMBDA(long long, + (long long)(0)), + DEFAULT_TO_STRING_LAMBDA(long long), + LUA_TNUMBER); -REGISTER_NUMERICALPROPERTY_SOURCE( - ULongLongProperty, unsigned long long, (unsigned long long)1, - numeric_limits::lowest(), - numeric_limits::max(), (unsigned long long)1, - DEFAULT_FROM_LUA_LAMBDA(unsigned long long, (unsigned long long)(0)), - DEFAULT_TO_LUA_LAMBDA(unsigned long long), LUA_TNUMBER); +REGISTER_NUMERICALPROPERTY_SOURCE(ULongLongProperty, unsigned long long, + (unsigned long long)1, + numeric_limits::lowest(), + numeric_limits::max(), + (unsigned long long)1, + DEFAULT_FROM_LUA_LAMBDA(unsigned long long, + (unsigned long long)(0)), + DEFAULT_TO_LUA_LAMBDA(unsigned long long), + DEFAULT_FROM_STRING_LAMBDA(unsigned long long, + (unsigned long long)(0)), + DEFAULT_TO_STRING_LAMBDA(unsigned long long), + LUA_TNUMBER); REGISTER_NUMERICALPROPERTY_SOURCE(FloatProperty, float, 0.f, numeric_limits::lowest(), numeric_limits::max(), 0.01f, DEFAULT_FROM_LUA_LAMBDA(float, float(0)), - DEFAULT_TO_LUA_LAMBDA(float), LUA_TNUMBER); + DEFAULT_TO_LUA_LAMBDA(float), + DEFAULT_FROM_STRING_LAMBDA(float, float(0)), + DEFAULT_TO_STRING_LAMBDA(float), + LUA_TNUMBER); REGISTER_NUMERICALPROPERTY_SOURCE(DoubleProperty, double, 0.0, numeric_limits::lowest(), numeric_limits::max(), 0.01, DEFAULT_FROM_LUA_LAMBDA(double, double(0)), - DEFAULT_TO_LUA_LAMBDA(double), LUA_TNUMBER); + DEFAULT_TO_LUA_LAMBDA(double), + DEFAULT_FROM_STRING_LAMBDA(double, double(0)), + DEFAULT_TO_STRING_LAMBDA(double), + LUA_TNUMBER); REGISTER_NUMERICALPROPERTY_SOURCE(LongDoubleProperty, long double, (long double)0, numeric_limits::lowest(), numeric_limits::max(), (long double)0.01f, DEFAULT_FROM_LUA_LAMBDA(long double, (long double)(0)), - DEFAULT_TO_LUA_LAMBDA(long double), LUA_TNUMBER); + DEFAULT_TO_LUA_LAMBDA(long double), + DEFAULT_FROM_STRING_LAMBDA(long double, + (long double)(0)), + DEFAULT_TO_STRING_LAMBDA(long double), + LUA_TNUMBER); } // namespace properties } // namespace openspace diff --git a/src/properties/selectionproperty.cpp b/src/properties/selectionproperty.cpp index 56bcf2abd0..cb4b5ce461 100644 --- a/src/properties/selectionproperty.cpp +++ b/src/properties/selectionproperty.cpp @@ -26,6 +26,8 @@ namespace { const std::string _loggerCat = "SelectionProperty"; + + const std::string Delimiter = ","; } namespace openspace { @@ -109,6 +111,29 @@ int PropertyDelegate>>::typeLua() { return LUA_TTABLE; } +template <> +template <> +std::vector PropertyDelegate>>::fromString(std::string value, bool& success) { + std::vector result; + size_t pos = 0; + while ((pos = value.find(Delimiter)) != std::string::npos) { + std::string token = value.substr(0, pos); + result.push_back(std::stoi(token)); + value.erase(0, pos + Delimiter.length()); + } + return result; +} + +template <> +template <> +bool PropertyDelegate>>::toString(std::string& outValue, std::vector inValue) { + outValue = "["; + for (int i : inValue) + outValue += std::to_string(i) + Delimiter; + outValue += "]"; + return true; +} + std::string SelectionProperty::generateAdditionalDescription() const { std::string result; result += OptionsKey + " = {"; diff --git a/src/properties/stringproperty.cpp b/src/properties/stringproperty.cpp index 1192acb36f..0eeffd7a3b 100644 --- a/src/properties/stringproperty.cpp +++ b/src/properties/stringproperty.cpp @@ -41,6 +41,14 @@ REGISTER_TEMPLATEPROPERTY_SOURCE(StringProperty, std::string, "", lua_pushstring(state, value.c_str()); return true; }, +[](std::string value, bool& success) -> std::string { + success = true; + return value; +}, +[](std::string& outValue, std::string inValue) -> bool { + outValue = inValue; + return true; +}, LUA_TSTRING ); diff --git a/src/properties/vectorproperty.cpp b/src/properties/vectorproperty.cpp index 9fcc7cdf57..98b9c40a29 100644 --- a/src/properties/vectorproperty.cpp +++ b/src/properties/vectorproperty.cpp @@ -26,6 +26,7 @@ #include #include +#include #include @@ -68,6 +69,37 @@ namespace properties { return true; \ } +#define DEFAULT_FROM_STRING_LAMBDA(__TYPE__) \ + [](std::string value, bool& success) -> __TYPE__ { \ + __TYPE__ result; \ + std::vector tokens = ghoul::tokenizeString(value, ','); \ + if (tokens.size() != result.length()) { \ + success = false; \ + return result; \ + } \ + for (__TYPE__::size_type i = 0; i < result.length(); ++i) { \ + std::stringstream s(tokens[i]); \ + __TYPE__::value_type v; \ + s >> v; \ + if (s.fail()) { \ + success = false; \ + return result; \ + } \ + else \ + result[i] = v; \ + } \ + success = true; \ + return result; \ + } + +#define DEFAULT_TO_STRING_LAMBDA(__TYPE__) \ + [](std::string& outValue, __TYPE__ inValue) -> bool { \ + outValue = ""; \ + for (__TYPE__::size_type i = 0; i < inValue.length(); ++i) \ + outValue += std::to_string(inValue[i]) + ","; \ + return true; \ + } + // Forcing value from int to bool is acceptable here (line 48) #ifdef WIN32 @@ -78,40 +110,64 @@ namespace properties { REGISTER_TEMPLATEPROPERTY_SOURCE(BVec2Property, glm::bvec2, glm::bvec2(false), DEFAULT_FROM_LUA_LAMBDA(glm::bvec2, lua_toboolean, lua_isboolean), - DEFAULT_TO_LUA_LAMBDA(glm::bvec2), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::bvec2), + DEFAULT_FROM_STRING_LAMBDA(glm::bvec2), + DEFAULT_TO_STRING_LAMBDA(glm::bvec2), + LUA_TTABLE); REGISTER_TEMPLATEPROPERTY_SOURCE(BVec3Property, glm::bvec3, glm::bvec3(false), DEFAULT_FROM_LUA_LAMBDA(glm::bvec3, lua_toboolean, lua_isboolean), - DEFAULT_TO_LUA_LAMBDA(glm::bvec3), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::bvec3), + DEFAULT_FROM_STRING_LAMBDA(glm::bvec3), + DEFAULT_TO_STRING_LAMBDA(glm::bvec3), + LUA_TTABLE); REGISTER_TEMPLATEPROPERTY_SOURCE(BVec4Property, glm::bvec4, glm::bvec4(false), DEFAULT_FROM_LUA_LAMBDA(glm::bvec4, lua_toboolean, lua_isboolean), - DEFAULT_TO_LUA_LAMBDA(glm::bvec4), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::bvec4), + DEFAULT_FROM_STRING_LAMBDA(glm::bvec4), + DEFAULT_TO_STRING_LAMBDA(glm::bvec4), + LUA_TTABLE); #ifdef WIN32 #pragma warning(default : 4800) #endif -REGISTER_NUMERICALPROPERTY_SOURCE( - Vec2Property, glm::vec2, glm::vec2(0), glm::vec2(numeric_limits::lowest()), - glm::vec2(numeric_limits::max()), glm::vec2(0.01f), - DEFAULT_FROM_LUA_LAMBDA(glm::vec2, lua_tonumber, lua_isnumber), - DEFAULT_TO_LUA_LAMBDA(glm::vec2), LUA_TTABLE); +REGISTER_NUMERICALPROPERTY_SOURCE(Vec2Property, glm::vec2, glm::vec2(0), + glm::vec2(numeric_limits::lowest()), + glm::vec2(numeric_limits::max()), + glm::vec2(0.01f), + DEFAULT_FROM_LUA_LAMBDA(glm::vec2, lua_tonumber, + lua_isnumber), + DEFAULT_TO_LUA_LAMBDA(glm::vec2), + DEFAULT_FROM_STRING_LAMBDA(glm::vec2), + DEFAULT_TO_STRING_LAMBDA(glm::vec2), + LUA_TTABLE); -REGISTER_NUMERICALPROPERTY_SOURCE( - Vec3Property, glm::vec3, glm::vec3(0), glm::vec3(numeric_limits::lowest()), - glm::vec3(numeric_limits::max()), glm::vec3(0.01f), - DEFAULT_FROM_LUA_LAMBDA(glm::vec3, lua_tonumber, lua_isnumber), - DEFAULT_TO_LUA_LAMBDA(glm::vec3), LUA_TTABLE); +REGISTER_NUMERICALPROPERTY_SOURCE(Vec3Property, glm::vec3, glm::vec3(0), + glm::vec3(numeric_limits::lowest()), + glm::vec3(numeric_limits::max()), + glm::vec3(0.01f), + DEFAULT_FROM_LUA_LAMBDA(glm::vec3, lua_tonumber, + lua_isnumber), + DEFAULT_TO_LUA_LAMBDA(glm::vec3), + DEFAULT_FROM_STRING_LAMBDA(glm::vec3), + DEFAULT_TO_STRING_LAMBDA(glm::vec3), + LUA_TTABLE); -REGISTER_NUMERICALPROPERTY_SOURCE( - Vec4Property, glm::vec4, glm::vec4(0), glm::vec4(numeric_limits::lowest()), - glm::vec4(numeric_limits::max()), glm::vec4(0.01f), - DEFAULT_FROM_LUA_LAMBDA(glm::vec4, lua_tonumber, lua_isnumber), - DEFAULT_TO_LUA_LAMBDA(glm::vec4), LUA_TTABLE); +REGISTER_NUMERICALPROPERTY_SOURCE(Vec4Property, glm::vec4, glm::vec4(0), + glm::vec4(numeric_limits::lowest()), + glm::vec4(numeric_limits::max()), + glm::vec4(0.01f), + DEFAULT_FROM_LUA_LAMBDA(glm::vec4, lua_tonumber, + lua_isnumber), + DEFAULT_TO_LUA_LAMBDA(glm::vec4), + DEFAULT_FROM_STRING_LAMBDA(glm::vec4), + DEFAULT_TO_STRING_LAMBDA(glm::vec4), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(DVec2Property, glm::dvec2, glm::dvec2(0), glm::dvec2(numeric_limits::lowest()), @@ -119,7 +175,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(DVec2Property, glm::dvec2, glm::dvec2(0), glm::dvec2(0.01), DEFAULT_FROM_LUA_LAMBDA(glm::dvec2, lua_tonumber, lua_isnumber), - DEFAULT_TO_LUA_LAMBDA(glm::dvec2), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::dvec2), + DEFAULT_FROM_STRING_LAMBDA(glm::dvec2), + DEFAULT_TO_STRING_LAMBDA(glm::dvec2), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(DVec3Property, glm::dvec3, glm::dvec3(0), glm::dvec3(numeric_limits::lowest()), @@ -127,7 +186,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(DVec3Property, glm::dvec3, glm::dvec3(0), glm::dvec3(0.01), DEFAULT_FROM_LUA_LAMBDA(glm::dvec3, lua_tonumber, lua_isnumber), - DEFAULT_TO_LUA_LAMBDA(glm::dvec3), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::dvec3), + DEFAULT_FROM_STRING_LAMBDA(glm::dvec3), + DEFAULT_TO_STRING_LAMBDA(glm::dvec3), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(DVec4Property, glm::dvec4, glm::dvec4(0), glm::dvec4(numeric_limits::lowest()), @@ -135,28 +197,40 @@ REGISTER_NUMERICALPROPERTY_SOURCE(DVec4Property, glm::dvec4, glm::dvec4(0), glm::dvec4(0.01), DEFAULT_FROM_LUA_LAMBDA(glm::dvec4, lua_tonumber, lua_isnumber), - DEFAULT_TO_LUA_LAMBDA(glm::dvec4), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::dvec4), + DEFAULT_FROM_STRING_LAMBDA(glm::dvec4), + DEFAULT_TO_STRING_LAMBDA(glm::dvec4), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(IVec2Property, glm::ivec2, glm::ivec2(0), glm::ivec2(numeric_limits::lowest()), glm::ivec2(numeric_limits::max()), glm::ivec2(1), DEFAULT_FROM_LUA_LAMBDA(glm::ivec2, lua_tonumber, lua_isnumber), - DEFAULT_TO_LUA_LAMBDA(glm::ivec2), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::ivec2), + DEFAULT_FROM_STRING_LAMBDA(glm::ivec2), + DEFAULT_TO_STRING_LAMBDA(glm::ivec2), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(IVec3Property, glm::ivec3, glm::ivec3(0), glm::ivec3(numeric_limits::lowest()), glm::ivec3(numeric_limits::max()), glm::ivec3(1), DEFAULT_FROM_LUA_LAMBDA(glm::ivec3, lua_tonumber, lua_isnumber), - DEFAULT_TO_LUA_LAMBDA(glm::ivec3), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::ivec3), + DEFAULT_FROM_STRING_LAMBDA(glm::ivec3), + DEFAULT_TO_STRING_LAMBDA(glm::ivec3), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(IVec4Property, glm::ivec4, glm::ivec4(0), glm::ivec4(numeric_limits::lowest()), glm::ivec4(numeric_limits::max()), glm::ivec4(1), DEFAULT_FROM_LUA_LAMBDA(glm::ivec4, lua_tonumber, lua_isnumber), - DEFAULT_TO_LUA_LAMBDA(glm::ivec4), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::ivec4), + DEFAULT_FROM_STRING_LAMBDA(glm::ivec4), + DEFAULT_TO_STRING_LAMBDA(glm::ivec4), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(UVec2Property, glm::uvec2, glm::uvec2(0), glm::uvec2(numeric_limits::lowest()), @@ -164,7 +238,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(UVec2Property, glm::uvec2, glm::uvec2(0), glm::uvec2(1), DEFAULT_FROM_LUA_LAMBDA(glm::uvec2, lua_tonumber, lua_isnumber), - DEFAULT_TO_LUA_LAMBDA(glm::uvec2), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::uvec2), + DEFAULT_FROM_STRING_LAMBDA(glm::uvec2), + DEFAULT_TO_STRING_LAMBDA(glm::uvec2), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(UVec3Property, glm::uvec3, glm::uvec3(0), glm::uvec3(numeric_limits::lowest()), @@ -172,7 +249,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(UVec3Property, glm::uvec3, glm::uvec3(0), glm::uvec3(1), DEFAULT_FROM_LUA_LAMBDA(glm::uvec3, lua_tonumber, lua_isnumber), - DEFAULT_TO_LUA_LAMBDA(glm::uvec3), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::uvec3), + DEFAULT_FROM_STRING_LAMBDA(glm::uvec3), + DEFAULT_TO_STRING_LAMBDA(glm::uvec3), + LUA_TTABLE); REGISTER_NUMERICALPROPERTY_SOURCE(UVec4Property, glm::uvec4, glm::uvec4(0), glm::uvec4(numeric_limits::lowest()), @@ -180,7 +260,10 @@ REGISTER_NUMERICALPROPERTY_SOURCE(UVec4Property, glm::uvec4, glm::uvec4(0), glm::uvec4(1), DEFAULT_FROM_LUA_LAMBDA(glm::uvec4, lua_tonumber, lua_isnumber), - DEFAULT_TO_LUA_LAMBDA(glm::uvec4), LUA_TTABLE); + DEFAULT_TO_LUA_LAMBDA(glm::uvec4), + DEFAULT_FROM_STRING_LAMBDA(glm::uvec4), + DEFAULT_TO_STRING_LAMBDA(glm::uvec4), + LUA_TTABLE); } // namespace properties } // namespace openspace From 03994b2758e0f156a5e37f75d54baac89e2aa3ee Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 5 Jul 2015 16:37:03 +0200 Subject: [PATCH 273/329] Renamed getLua, setLua, getString, and setString methods to add "Value" to the end of the method name Added documentation for getString/setString methods --- .../openspace/properties/numericalproperty.h | 8 +-- .../properties/numericalproperty.inl | 8 +-- include/openspace/properties/property.h | 56 +++++++++++++------ .../openspace/properties/templateproperty.h | 8 +-- .../openspace/properties/templateproperty.inl | 8 +-- .../openspace/properties/triggerproperty.h | 2 +- src/properties/property.cpp | 8 +-- src/properties/triggerproperty.cpp | 2 +- src/scene/scene_lua.inl | 4 +- 9 files changed, 62 insertions(+), 42 deletions(-) diff --git a/include/openspace/properties/numericalproperty.h b/include/openspace/properties/numericalproperty.h index 52bfceb679..80fce6e9f4 100644 --- a/include/openspace/properties/numericalproperty.h +++ b/include/openspace/properties/numericalproperty.h @@ -40,12 +40,12 @@ public: NumericalProperty(std::string identifier, std::string guiName, T value, T minimumValue, T maximumValue, T steppingValue); - bool getLua(lua_State* state) const override; - bool setLua(lua_State* state) override; + bool getLuaValue(lua_State* state) const override; + bool setLuaValue(lua_State* state) override; int typeLua() const override; - bool getString(std::string& value) const override; - bool setString(std::string value) override; + bool getStringValue(std::string& value) const override; + bool setStringValue(std::string value) override; T minValue() const; T maxValue() const; diff --git a/include/openspace/properties/numericalproperty.inl b/include/openspace/properties/numericalproperty.inl index d64a402b87..cf4a0ec5f9 100644 --- a/include/openspace/properties/numericalproperty.inl +++ b/include/openspace/properties/numericalproperty.inl @@ -280,7 +280,7 @@ std::string NumericalProperty::className() const { } template -bool NumericalProperty::setLua(lua_State* state) +bool NumericalProperty::setLuaValue(lua_State* state) { bool success = false; T value = PropertyDelegate>::template fromLuaValue(state, success); @@ -290,7 +290,7 @@ bool NumericalProperty::setLua(lua_State* state) } template -bool NumericalProperty::getLua(lua_State* state) const +bool NumericalProperty::getLuaValue(lua_State* state) const { bool success = PropertyDelegate>::template toLuaValue(state, TemplateProperty::_value); return success; @@ -302,13 +302,13 @@ int NumericalProperty::typeLua() const { } template -bool NumericalProperty::getString(std::string& value) const { +bool NumericalProperty::getStringValue(std::string& value) const { bool success = PropertyDelegate>::template toString(value, _value); return success; } template -bool NumericalProperty::setString(std::string value) { +bool NumericalProperty::setStringValue(std::string value) { bool success = false; T thisValue = PropertyDelegate>::template fromString(value, success); if (success) diff --git a/include/openspace/properties/property.h b/include/openspace/properties/property.h index 93ce1f5d32..9770fa426e 100644 --- a/include/openspace/properties/property.h +++ b/include/openspace/properties/property.h @@ -45,8 +45,9 @@ class PropertyOwner; * Property. Per PropertyOwner, the identifier needs to be unique and can be * used as a URI. This class is an abstract base class and each subclass (most notable * TemplateProperty) needs to implement the methods Property::className, Property::get, - * Property::set, Property::type(), Property::getLua, Property::setLua, and - * Property::typeLua to make full use of the infrastructure. + * Property::set, Property::type(), Property::getLuaValue, Property::setLuaValue, + * Property::getStringValue, Property::setStringValue and Property::typeLua to make full + * use of the infrastructure. * The most common types can be implemented by creating a specialized instantiation of * TemplateProperty, which provides default implementations for these methods. * @@ -118,51 +119,69 @@ public: * This method encodes the encapsulated value of this Property at the top of the Lua * stack. The specific details of this serialization is up to the property developer * as long as the rest of the stack is unchanged. The implementation has to be - * synchronized with the Property::setLua method. The default implementation is a + * synchronized with the Property::setLuaValue method. The default implementation is a * no-op. * \param state The Lua state to which the value will be encoded * \return true if the encoding succeeded, false otherwise */ - virtual bool getLua(lua_State* state) const; + virtual bool getLuaValue(lua_State* state) const; /** * This method sets the value encapsulated by this Property by deserializing the value * on top of the passed Lua stack. The specific details of the deserialization are up * to the Property developer, but they must only depend on the top element of the * stack and must leave all other elements unchanged. The implementation has to be - * synchronized with the Property::getLua method. The default implementation is a + * synchronized with the Property::getLuaValue method. The default implementation is a * no-op. * \param state The Lua state from which the value will be decoded * \return true if the decoding and setting of the value succeeded, * false otherwise */ - virtual bool setLua(lua_State* state); + virtual bool setLuaValue(lua_State* state); /** * Returns the Lua type that will be put onto the stack in the Property::getLua method - * and which will be consumed by the Property::setLua method. The returned value + * and which will be consumed by the Property::setLuaValue method. The returned value * can belong to the set of Lua types: LUA_TNONE, LUA_TNIL, * LUA_TBOOLEAN, LUA_TLIGHTUSERDATA, * LUA_TNUMBER, LUA_TSTRING, LUA_TTABLE, * LUA_TFUNCTION, LUA_TUSERDATA, or * LUA_TTHREAD. The default implementation will return * LUA_TNONE. - * \return The Lua type that will be consumed or produced by the Property::getLua and - * Property::setLua methods. + * \return The Lua type that will be consumed or produced by the Property::getLuaValue + * and Property::setLuaValue methods. */ virtual int typeLua() const; - virtual bool getString(std::string& value) const; + /** + * This method encodes the encapsulated value of this Property as a + * std::string. The specific details of this serialization is up to the + * property developer. The implementation has to be synchronized with the + Property::setStringValue method. The default implementation is a no-op. + * \param value The value to which the Property will be encoded + * \return true if the encoding succeeded, false otherwise + */ + virtual bool getStringValue(std::string& value) const; - virtual bool setString(std::string value); + /** + * This method sets the value encapsulated by this Property by deserializing the + * passed std::string. The specific details of the deserialization are up + * to the Property developer. The implementation has to be synchronized with the + * Property::getLuaValue method. The default implementation is a no-op. + * \param value The value from which the Property will be decoded + * \return true if the decoding and setting of the value succeeded, + * false otherwise + */ + virtual bool setStringValue(std::string value); /** * This method registers a callback function that will be called every - * time if either Property:set or Property::setLua was called with a value that is - * different from the previously stored value. The callback can be removed my passing - * an empty std::function object. + * time if either Property:set or Property::setLuaValue was called with a value that + * is different from the previously stored value. The callback can be removed by + * passing an empty std::function object. * \param callback The callback function that is called when the encapsulated type has - * been successfully changed by either the Property::set or Property::setLua methods. + * been successfully changed by either the Property::set or Property::setLuaValue + * methods. */ virtual void onChange(std::function callback); @@ -247,9 +266,10 @@ public: /** * This method determines if this Property should be read-only in external * applications. This setting is only a hint and does not need to be followed by GUI - * applications and does not have any effect on the Property::set or Property::setLua - * methods. The value is stored in the metaData Dictionary with the key: - * isReadOnly. The default value is false. + * applications and does not have any effect on the Property::set, + * Property::setLuaValue, or Property::setStringValue methods. The value is stored in + * the metaData Dictionary with the key: isReadOnly. The default value is + * false. * \param state true if the Property should be read only, * false otherwise */ diff --git a/include/openspace/properties/templateproperty.h b/include/openspace/properties/templateproperty.h index 464e11d1c9..08dfedcc43 100644 --- a/include/openspace/properties/templateproperty.h +++ b/include/openspace/properties/templateproperty.h @@ -111,7 +111,7 @@ public: * \param state The Lua state onto which the encoded object will be pushed * \return true if the encoding succeeded; false otherwise */ - bool getLua(lua_State* state) const override; + bool getLuaValue(lua_State* state) const override; /** * Sets the value of this TemplateProprty by decoding the object at the top of the Lua @@ -122,14 +122,14 @@ public: * \param state The Lua state from which the value will be decoded * \return true if the decoding succeeded; false otherwise */ - bool setLua(lua_State* state) override; + bool setLuaValue(lua_State* state) override; /// \see Property::typeLua int typeLua() const override; - bool getString(std::string& value) const override; + bool getStringValue(std::string& value) const override; - bool setString(std::string value) override; + bool setStringValue(std::string value) override; /** * Returns the description for this TemplateProperty as a Lua script that returns a diff --git a/include/openspace/properties/templateproperty.inl b/include/openspace/properties/templateproperty.inl index a897cae89d..396c05613f 100644 --- a/include/openspace/properties/templateproperty.inl +++ b/include/openspace/properties/templateproperty.inl @@ -235,13 +235,13 @@ const std::type_info& TemplateProperty::type() const { } template -bool TemplateProperty::getLua(lua_State* state) const { +bool TemplateProperty::getLuaValue(lua_State* state) const { bool success = PropertyDelegate>::template toLuaValue(state, _value); return success; } template -bool TemplateProperty::setLua(lua_State* state) { +bool TemplateProperty::setLuaValue(lua_State* state) { bool success = false; T thisValue = PropertyDelegate>::template fromLuaValue(state, success); if (success) @@ -255,13 +255,13 @@ int TemplateProperty::typeLua() const { } template -bool TemplateProperty::getString(std::string& value) const { +bool TemplateProperty::getStringValue(std::string& value) const { bool success = PropertyDelegate>::template toString(value, _value); return success; } template -bool TemplateProperty::setString(std::string value) { +bool TemplateProperty::setStringValue(std::string value) { bool success = false; T thisValue = PropertyDelegate>::template fromString(value, success); if (success) diff --git a/include/openspace/properties/triggerproperty.h b/include/openspace/properties/triggerproperty.h index c4a0cc8f50..33c48f0377 100644 --- a/include/openspace/properties/triggerproperty.h +++ b/include/openspace/properties/triggerproperty.h @@ -56,7 +56,7 @@ public: * \param state The unused Lua state * \return Returns always true */ - bool setLua(lua_State* state); + bool setLuaValue(lua_State* state); /** * Silently ignores any value that is passed into this function and will trigger the diff --git a/src/properties/property.cpp b/src/properties/property.cpp index 2db40b0ae9..621191ef93 100644 --- a/src/properties/property.cpp +++ b/src/properties/property.cpp @@ -85,13 +85,13 @@ boost::any Property::get() const { return boost::any(); } -bool Property::getLua(lua_State* state) const { +bool Property::getLuaValue(lua_State* state) const { return false; } void Property::set(boost::any value) {} -bool Property::setLua(lua_State* state) { +bool Property::setLuaValue(lua_State* state) { return false; } @@ -103,11 +103,11 @@ int Property::typeLua() const { return LUA_TNIL; } -bool Property::getString(std::string& value) const { +bool Property::getStringValue(std::string& value) const { return false; } -bool Property::setString(std::string value) { +bool Property::setStringValue(std::string value) { return false; } diff --git a/src/properties/triggerproperty.cpp b/src/properties/triggerproperty.cpp index 41b114711a..f9450afd65 100644 --- a/src/properties/triggerproperty.cpp +++ b/src/properties/triggerproperty.cpp @@ -35,7 +35,7 @@ std::string TriggerProperty::className() const { return "TriggerProperty"; } -bool TriggerProperty::setLua(lua_State* state) { +bool TriggerProperty::setLuaValue(lua_State* state) { notifyListener(); return true; } diff --git a/src/scene/scene_lua.inl b/src/scene/scene_lua.inl index c08347e1d5..6c43277031 100644 --- a/src/scene/scene_lua.inl +++ b/src/scene/scene_lua.inl @@ -58,7 +58,7 @@ int property_setValue(lua_State* L) { return 0; } else - prop->setLua(L); + prop->setLuaValue(L); return 0; } @@ -84,7 +84,7 @@ int property_getValue(lua_State* L) { return 0; } else - prop->getLua(L); + prop->getLuaValue(L); return 1; } From b53efe4f0e90bb15946fee5ccbbc186b2b5ac09d Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Sun, 5 Jul 2015 16:41:38 +0200 Subject: [PATCH 274/329] changed how time keyframes are used --- .../openspace/network/parallelconnection.h | 6 +- include/openspace/util/time.h | 8 +- src/engine/openspaceengine.cpp | 2 +- src/network/parallelconnection.cpp | 130 ++++++++++++------ 4 files changed, 100 insertions(+), 46 deletions(-) diff --git a/include/openspace/network/parallelconnection.h b/include/openspace/network/parallelconnection.h index bb8de83310..e20ba4d9c4 100644 --- a/include/openspace/network/parallelconnection.h +++ b/include/openspace/network/parallelconnection.h @@ -88,7 +88,7 @@ namespace openspace{ void signalDisconnect(); - void update(double dt); + void preSynchronization(); enum MessageTypes{ Authentication=0, @@ -188,6 +188,10 @@ namespace openspace{ std::atomic _tryConnect; std::vector> _sendBuffer; std::mutex _sendBufferMutex; + + network::datamessagestructures::TimeKeyframe _latestTimeKeyframe; + std::mutex _timeKeyframeMutex; + std::atomic _latestTimeKeyframeValid; }; } // namespace network diff --git a/include/openspace/util/time.h b/include/openspace/util/time.h index cba2eced3f..16906e4494 100644 --- a/include/openspace/util/time.h +++ b/include/openspace/util/time.h @@ -86,18 +86,18 @@ public: * Sets the current time to the specified value in seconds past the J2000 epoch. This * value can be negative to represent dates before the epoch. * \param value The number of seconds after the J2000 epoch - * \param requireJump Wether or not the time change is big enough to require a time-jump, defaults to false + * \param requireJump Wether or not the time change is big enough to require a time-jump, defaults to true as most calls to set time will require recomputation of planetary paths etc. */ - void setTime(double value, bool requireJump = false); + void setTime(double value, bool requireJump = true); /** * Sets the current time to the specified value given as a Spice compliant string as * described in the Spice documentation * (http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/str2et_c.html) * \param time The time to be set as a date string - * \param requireJump Wether or not the time change is big enough to require a time-jump, defaults to false + * \param requireJump Wether or not the time change is big enough to require a time-jump, defaults to true as most calls to set time will require recomputation of planetary paths etc. */ - void setTime(std::string time, bool requireJump = false); + void setTime(std::string time, bool requireJump = true); /** * Returns the current time as the number of seconds past the J2000 epoch. If the diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 0e1bd1e13c..96d361787e 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -643,7 +643,7 @@ void OpenSpaceEngine::preSynchronization() { _renderEngine->preSynchronization(); - _parallelConnection->update(dt); + _parallelConnection->preSynchronization(); } } diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index af92a9f240..78edcd7062 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -87,7 +87,9 @@ namespace openspace { _isHost(false), _isConnected(false), _tryConnect(false), - _performDisconnect(false) + _performDisconnect(false), + _latestTimeKeyframeValid(false) + { //create handler thread _handlerThread = new (std::nothrow) std::thread(&ParallelConnection::threadManagement, this); @@ -541,10 +543,29 @@ namespace openspace { network::datamessagestructures::TimeKeyframe tf; tf.deserialize(buffer); - //set the time parameters based on recevied keyframe - Time::ref().setDeltaTime(tf._dt); - Time::ref().setPause(tf._paused); - Time::ref().setTime(tf._time, tf._requiresTimeJump); + //lock mutex and assign latest time keyframe parameters + _timeKeyframeMutex.lock(); + + _latestTimeKeyframe._dt = tf._dt; + _latestTimeKeyframe._time = tf._time; + _latestTimeKeyframe._paused = tf._paused; + + //ensure that we never miss a timejump + //if last keyframe required a jump and that keyframe has not been used yet + if(_latestTimeKeyframe._requiresTimeJump && _latestTimeKeyframeValid){ + //do nothing to the boolean. Old value must be executed + }else{ + //either the latest keyframe didnt require a jump, or we have already spent that keyframe. + //in either case we can go ahead and write the bool value of newest frame + _latestTimeKeyframe._requiresTimeJump = tf._requiresTimeJump; + } + + //unlock mutex + _timeKeyframeMutex.unlock(); + + //the keyframe is now valid for use + _latestTimeKeyframeValid.store(true); + break; } case network::datamessagestructures::ScriptData:{ @@ -917,42 +938,70 @@ namespace openspace { return true; } - void ParallelConnection::update(double dt){ + void ParallelConnection::preSynchronization(){ - //get current time parameters and create a keyframe - network::datamessagestructures::TimeKeyframe tf; - tf._dt = Time::ref().deltaTime(); - tf._paused = Time::ref().paused(); - tf._requiresTimeJump = Time::ref().timeJumped(); - tf._time = Time::ref().currentTime(); - - //create a buffer and serialize message - std::vector tbuffer; - tf.serialize(tbuffer); - - //get the size of the keyframebuffer - uint16_t msglen = static_cast(tbuffer.size()); - - uint16_t type = static_cast(network::datamessagestructures::PositionData); - - //create the full buffer - std::vector buffer; - buffer.reserve(headerSize() + sizeof(type) + sizeof(msglen) + msglen); - - //write header - writeHeader(buffer, MessageTypes::Data); - - //type of message - buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(type)); - - //size of message - buffer.insert(buffer.end(), reinterpret_cast(&msglen), reinterpret_cast(&msglen) + sizeof(msglen)); - - //actual message - buffer.insert(buffer.end(), tbuffer.begin(), tbuffer.end()); - - //send message - queMessage(buffer); + //if we're the host + if(_isHost){ + //get current time parameters and create a keyframe + network::datamessagestructures::TimeKeyframe tf; + tf._dt = Time::ref().deltaTime(); + tf._paused = Time::ref().paused(); + tf._requiresTimeJump = Time::ref().timeJumped(); + tf._time = Time::ref().currentTime(); + + //create a buffer and serialize message + std::vector tbuffer; + tf.serialize(tbuffer); + + //get the size of the keyframebuffer + uint16_t msglen = static_cast(tbuffer.size()); + + //the type of data message + uint16_t type = static_cast(network::datamessagestructures::TimeData); + + //create the full buffer + std::vector buffer; + buffer.reserve(headerSize() + sizeof(type) + sizeof(msglen) + msglen); + + //write header + writeHeader(buffer, MessageTypes::Data); + + //type of message + buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(type)); + + //size of message + buffer.insert(buffer.end(), reinterpret_cast(&msglen), reinterpret_cast(&msglen) + sizeof(msglen)); + + //actual message + buffer.insert(buffer.end(), tbuffer.begin(), tbuffer.end()); + + //send message + queMessage(buffer); + } + else{ + //if we're not the host and we have a valid keyframe (one that hasnt been used before) + if(_latestTimeKeyframeValid.load()){ + + //lock mutex and retrieve parameters from latest keyframe + _timeKeyframeMutex.lock(); + + double dt = _latestTimeKeyframe._dt; + double time = _latestTimeKeyframe._time; + bool jump = _latestTimeKeyframe._requiresTimeJump; + bool paused = _latestTimeKeyframe._paused; + + _timeKeyframeMutex.unlock(); + + //this keyframe is now spent + _latestTimeKeyframeValid.store(false); + + //assign latest params + Time::ref().setDeltaTime(dt); + Time::ref().setTime(time, jump); + Time::ref().setPause(paused); + + } + } } void ParallelConnection::broadcast(){ @@ -977,6 +1026,7 @@ namespace openspace { //get the size of the keyframebuffer uint16_t msglen = static_cast(kfBuffer.size()); + //the type of message uint16_t type = static_cast(network::datamessagestructures::PositionData); //create the full buffer From 35cdfd4abeac826c630292f53fd634b227180f29 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 5 Jul 2015 16:51:20 +0200 Subject: [PATCH 275/329] GCC compilation fix for Jenkins --- include/openspace/properties/numericalproperty.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openspace/properties/numericalproperty.inl b/include/openspace/properties/numericalproperty.inl index cf4a0ec5f9..7e73993feb 100644 --- a/include/openspace/properties/numericalproperty.inl +++ b/include/openspace/properties/numericalproperty.inl @@ -303,7 +303,7 @@ int NumericalProperty::typeLua() const { template bool NumericalProperty::getStringValue(std::string& value) const { - bool success = PropertyDelegate>::template toString(value, _value); + bool success = PropertyDelegate>::template toString(value, TemplateProperty::_value); return success; } From a25b64c8b9d6fcc12fbdd490096d1e1abb3d9e05 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 5 Jul 2015 17:01:47 +0200 Subject: [PATCH 276/329] One more GCC fix --- include/openspace/properties/numericalproperty.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openspace/properties/numericalproperty.inl b/include/openspace/properties/numericalproperty.inl index 7e73993feb..a5770e03b3 100644 --- a/include/openspace/properties/numericalproperty.inl +++ b/include/openspace/properties/numericalproperty.inl @@ -312,7 +312,7 @@ bool NumericalProperty::setStringValue(std::string value) { bool success = false; T thisValue = PropertyDelegate>::template fromString(value, success); if (success) - set(boost::any(thisValue)); + TemplateProperty::set(boost::any(thisValue)); return success; } From d76e18d9ee914698eaabf1c0573098e6d1cda8cb Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Sun, 5 Jul 2015 17:21:23 +0200 Subject: [PATCH 277/329] more work on scripting synchronization and state saving --- .../openspace/network/parallelconnection.h | 6 +++ src/network/parallelconnection.cpp | 52 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/include/openspace/network/parallelconnection.h b/include/openspace/network/parallelconnection.h index e20ba4d9c4..84d7c79d63 100644 --- a/include/openspace/network/parallelconnection.h +++ b/include/openspace/network/parallelconnection.h @@ -40,6 +40,7 @@ #include #include #include +#include #ifdef __WIN32__ #ifndef WIN32_LEAN_AND_MEAN @@ -90,6 +91,8 @@ namespace openspace{ void preSynchronization(); + void scriptMessage(const std::string propIdentifier, const std::string propValue); + enum MessageTypes{ Authentication=0, Initialization, @@ -171,6 +174,8 @@ namespace openspace{ void threadManagement(); + std::string scriptFromPropertyAndValue(const std::string property, const std::string value); + uint32_t _passCode; std::string _port; std::string _address; @@ -192,6 +197,7 @@ namespace openspace{ network::datamessagestructures::TimeKeyframe _latestTimeKeyframe; std::mutex _timeKeyframeMutex; std::atomic _latestTimeKeyframeValid; + std::map _currentState; }; } // namespace network diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index 78edcd7062..2b79b7eead 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -1004,6 +1004,58 @@ namespace openspace { } } + void ParallelConnection::scriptMessage(const std::string propIdentifier, const std::string propValue){ + + //save script as current state + _currentState[propIdentifier] = propValue; + + //construct script + std::string script = scriptFromPropertyAndValue(propIdentifier, propValue); + + //create a script message + network::datamessagestructures::ScriptMessage sm; + sm._script = script; + sm._scriptlen = static_cast(script.length()); + + //create a buffer for the script + std::vector sbuffer; + + //fill the script buffer + sm.serialize(sbuffer); + + //get the size of the keyframebuffer + uint16_t msglen = static_cast(sbuffer.size()); + + //the type of message + uint16_t type = static_cast(network::datamessagestructures::ScriptData); + + //create the full buffer + std::vector buffer; + buffer.reserve(headerSize() + sizeof(type) + sizeof(msglen) + msglen); + + //write header + writeHeader(buffer, MessageTypes::Data); + + //type of message + buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(type)); + + //size of message + buffer.insert(buffer.end(), reinterpret_cast(&msglen), reinterpret_cast(&msglen) + sizeof(msglen)); + + //actual message + buffer.insert(buffer.end(), sbuffer.begin(), sbuffer.end()); + + //send message + queMessage(buffer); + + } + + std::string ParallelConnection::scriptFromPropertyAndValue(const std::string property, const std::string value){ + //consruct script + std::string script = "openspace.setPropertyValue(\"" + property + ",\"" + value + "\")"; + return script; + } + void ParallelConnection::broadcast(){ //while we're still connected and we're the host From 34412b7a8b10b3e22f01181f7cd1f17d1453d1d2 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Mon, 6 Jul 2015 10:00:07 +0200 Subject: [PATCH 278/329] more work on communication protocol --- src/network/parallelconnection.cpp | 103 +++++++++++++++-------------- src/scene/scene_lua.inl | 7 +- 2 files changed, 61 insertions(+), 49 deletions(-) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index 2b79b7eead..2695c868c0 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -373,22 +373,22 @@ namespace openspace { case MessageTypes::Authentication: //do nothing for now break; - case MessageTypes::Initialization: - initializationMessageReceived(); - break; +// case MessageTypes::Initialization: +// initializationMessageReceived(); +// break; case MessageTypes::Data: dataMessageReceived(); - break; - case MessageTypes::Script: - //disabled for now -// decodeScriptMessage(); - break; +// break; +// case MessageTypes::Script: +// //disabled for now +//// decodeScriptMessage(); +// break; case MessageTypes::HostInfo: hostInfoMessageReceived(); break; - case MessageTypes::InitializationRequest: - initializationRequestMessageReceived(); - break; +// case MessageTypes::InitializationRequest: +// initializationRequestMessageReceived(); +// break; default: //unknown message type break; @@ -825,6 +825,9 @@ namespace openspace { //and delegate decoding depending on type delegateDecoding(type); } + else{ + LERROR("Error: Client OpenSpace version " << OPENSPACE_VERSION_MAJOR << ", " << OPENSPACE_VERSION_MINOR << " does not match server version " << buffer[2] <<", " << buffer[3] << std::endl << "Message not decoded."); + } } else{ if (result == 0){ @@ -1009,44 +1012,48 @@ namespace openspace { //save script as current state _currentState[propIdentifier] = propValue; - //construct script - std::string script = scriptFromPropertyAndValue(propIdentifier, propValue); + //if we're connected, also send the script - //create a script message - network::datamessagestructures::ScriptMessage sm; - sm._script = script; - sm._scriptlen = static_cast(script.length()); - - //create a buffer for the script - std::vector sbuffer; - - //fill the script buffer - sm.serialize(sbuffer); - - //get the size of the keyframebuffer - uint16_t msglen = static_cast(sbuffer.size()); - - //the type of message - uint16_t type = static_cast(network::datamessagestructures::ScriptData); - - //create the full buffer - std::vector buffer; - buffer.reserve(headerSize() + sizeof(type) + sizeof(msglen) + msglen); - - //write header - writeHeader(buffer, MessageTypes::Data); - - //type of message - buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(type)); - - //size of message - buffer.insert(buffer.end(), reinterpret_cast(&msglen), reinterpret_cast(&msglen) + sizeof(msglen)); - - //actual message - buffer.insert(buffer.end(), sbuffer.begin(), sbuffer.end()); - - //send message - queMessage(buffer); + if(_isConnected.load()){ + //construct script + std::string script = scriptFromPropertyAndValue(propIdentifier, propValue); + + //create a script message + network::datamessagestructures::ScriptMessage sm; + sm._script = script; + sm._scriptlen = static_cast(script.length()); + + //create a buffer for the script + std::vector sbuffer; + + //fill the script buffer + sm.serialize(sbuffer); + + //get the size of the keyframebuffer + uint16_t msglen = static_cast(sbuffer.size()); + + //the type of message + uint16_t type = static_cast(network::datamessagestructures::ScriptData); + + //create the full buffer + std::vector buffer; + buffer.reserve(headerSize() + sizeof(type) + sizeof(msglen) + msglen); + + //write header + writeHeader(buffer, MessageTypes::Data); + + //type of message + buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(type)); + + //size of message + buffer.insert(buffer.end(), reinterpret_cast(&msglen), reinterpret_cast(&msglen) + sizeof(msglen)); + + //actual message + buffer.insert(buffer.end(), sbuffer.begin(), sbuffer.end()); + + //send message + queMessage(buffer); + } } diff --git a/src/scene/scene_lua.inl b/src/scene/scene_lua.inl index 6c43277031..79fbe882bf 100644 --- a/src/scene/scene_lua.inl +++ b/src/scene/scene_lua.inl @@ -57,8 +57,13 @@ int property_setValue(lua_State* L) { "'. Requested type: '" << luaTypeToString(prop->typeLua()) << "'"); return 0; } - else + else{ prop->setLuaValue(L); + //ensure properties are synced over parallel connection + std::string value; + prop->getStringValue(value); + OsEng.parallelConnection()->scriptMessage(prop->fullyQualifiedIdentifier(), value); + } return 0; } From d475d89b93c21740f118e7a29334e21be7c8c7c9 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Mon, 6 Jul 2015 11:26:49 +0200 Subject: [PATCH 279/329] fixed a bug in deserialising of time message --- include/openspace/network/messagestructures.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/openspace/network/messagestructures.h b/include/openspace/network/messagestructures.h index ee08a643d0..dee4b9f6e6 100644 --- a/include/openspace/network/messagestructures.h +++ b/include/openspace/network/messagestructures.h @@ -120,6 +120,7 @@ namespace openspace{ //is time paused? size = sizeof(_paused); memcpy(&_paused, buffer.data() + offset, size); + offset += sizeof(_paused); //is a time jump required? size = sizeof(_requiresTimeJump); From e8809ac84ce93b324dcd5821423f8a7f5bf7d965 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Mon, 6 Jul 2015 11:28:15 +0200 Subject: [PATCH 280/329] changes to syncing of scripts for properties --- src/network/parallelconnection.cpp | 6 +++--- src/properties/matrixproperty.cpp | 1 + src/properties/scalarproperty.cpp | 5 ++++- src/properties/vectorproperty.cpp | 1 + 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index 2695c868c0..cddcb2729c 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -1012,9 +1012,9 @@ namespace openspace { //save script as current state _currentState[propIdentifier] = propValue; - //if we're connected, also send the script + //if we're connected and we're the host, also send the script - if(_isConnected.load()){ + if(_isConnected.load() && _isHost.load()){ //construct script std::string script = scriptFromPropertyAndValue(propIdentifier, propValue); @@ -1059,7 +1059,7 @@ namespace openspace { std::string ParallelConnection::scriptFromPropertyAndValue(const std::string property, const std::string value){ //consruct script - std::string script = "openspace.setPropertyValue(\"" + property + ",\"" + value + "\")"; + std::string script = "openspace.setPropertyValue(\"" + property + "\"," + value + ");"; return script; } diff --git a/src/properties/matrixproperty.cpp b/src/properties/matrixproperty.cpp index 37eeb75787..d256d9f629 100644 --- a/src/properties/matrixproperty.cpp +++ b/src/properties/matrixproperty.cpp @@ -75,6 +75,7 @@ namespace properties { [](std::string value, bool& success) -> __TYPE__ { \ __TYPE__ result; \ std::vector tokens = ghoul::tokenizeString(value, ','); \ + tokens.pop_back(); \ if (tokens.size() != (__TYPE__::row_size() * __TYPE__::col_size())) { \ success = false; \ return result; \ diff --git a/src/properties/scalarproperty.cpp b/src/properties/scalarproperty.cpp index fe817b7f78..3953f6d70a 100644 --- a/src/properties/scalarproperty.cpp +++ b/src/properties/scalarproperty.cpp @@ -81,7 +81,10 @@ REGISTER_TEMPLATEPROPERTY_SOURCE(BoolProperty, bool, false, return true; }, DEFAULT_FROM_STRING_LAMBDA(bool, false), - DEFAULT_TO_STRING_LAMBDA(bool), + [](std::string& outValue, bool inValue) -> bool { + outValue = inValue ? "true" : "false"; + return true; + }, LUA_TBOOLEAN ); diff --git a/src/properties/vectorproperty.cpp b/src/properties/vectorproperty.cpp index 98b9c40a29..4c50103424 100644 --- a/src/properties/vectorproperty.cpp +++ b/src/properties/vectorproperty.cpp @@ -73,6 +73,7 @@ namespace properties { [](std::string value, bool& success) -> __TYPE__ { \ __TYPE__ result; \ std::vector tokens = ghoul::tokenizeString(value, ','); \ + tokens.pop_back(); \ if (tokens.size() != result.length()) { \ success = false; \ return result; \ From 16437856b4b8e4363a77dc8d0a29213fd408d61e Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Mon, 6 Jul 2015 12:50:04 +0200 Subject: [PATCH 281/329] fixes to matrix/vector property toString methods --- src/properties/matrixproperty.cpp | 2 +- src/properties/vectorproperty.cpp | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/properties/matrixproperty.cpp b/src/properties/matrixproperty.cpp index d256d9f629..dc8a86b44a 100644 --- a/src/properties/matrixproperty.cpp +++ b/src/properties/matrixproperty.cpp @@ -75,7 +75,6 @@ namespace properties { [](std::string value, bool& success) -> __TYPE__ { \ __TYPE__ result; \ std::vector tokens = ghoul::tokenizeString(value, ','); \ - tokens.pop_back(); \ if (tokens.size() != (__TYPE__::row_size() * __TYPE__::col_size())) { \ success = false; \ return result; \ @@ -107,6 +106,7 @@ namespace properties { for (__TYPE__::size_type j = 0; j < __TYPE__::col_size(); ++j) { \ outValue += std::to_string(inValue[i][j]) + ","; \ } \ + outValue.pop_back(); \ } \ return true; \ } diff --git a/src/properties/vectorproperty.cpp b/src/properties/vectorproperty.cpp index 4c50103424..8d3264c016 100644 --- a/src/properties/vectorproperty.cpp +++ b/src/properties/vectorproperty.cpp @@ -73,7 +73,6 @@ namespace properties { [](std::string value, bool& success) -> __TYPE__ { \ __TYPE__ result; \ std::vector tokens = ghoul::tokenizeString(value, ','); \ - tokens.pop_back(); \ if (tokens.size() != result.length()) { \ success = false; \ return result; \ @@ -95,9 +94,11 @@ namespace properties { #define DEFAULT_TO_STRING_LAMBDA(__TYPE__) \ [](std::string& outValue, __TYPE__ inValue) -> bool { \ - outValue = ""; \ + outValue = "{"; \ for (__TYPE__::size_type i = 0; i < inValue.length(); ++i) \ outValue += std::to_string(inValue[i]) + ","; \ + outValue.pop_back(); \ + outValue += "}"; \ return true; \ } From 446aa544b899905165de8302bcc0c25f8d59a279 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Mon, 6 Jul 2015 13:10:59 +0200 Subject: [PATCH 282/329] Making origin a property --- include/openspace/interaction/interactionhandler.h | 7 ++++++- src/interaction/interactionhandler.cpp | 12 ++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/include/openspace/interaction/interactionhandler.h b/include/openspace/interaction/interactionhandler.h index 8a59919ca3..c0c0ecc12e 100644 --- a/include/openspace/interaction/interactionhandler.h +++ b/include/openspace/interaction/interactionhandler.h @@ -77,6 +77,8 @@ #include #include #include +#include +#include #include @@ -87,7 +89,7 @@ class SceneGraphNode; namespace interaction { -class InteractionHandler { + class InteractionHandler : public properties::PropertyOwner{ public: InteractionHandler(); @@ -178,6 +180,9 @@ private: MouseController* _mouseController; std::vector _controllers; + + properties::StringProperty _origin; + //remote controller std::vector _keyframes; double _currentKeyframeTime; diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index eb30ae75ca..8b8745d55b 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -255,7 +255,19 @@ InteractionHandler::InteractionHandler() , _keyboardController(nullptr) , _mouseController(nullptr) , _currentKeyframeTime(-1.0) + , _origin("origin", "Origin", "") { + + addProperty(_origin); + _origin.onChange([this](){ + SceneGraphNode* node = sceneGraphNode(_origin.value()); + if (!node) { + LWARNING("Could not find a node in scenegraph called '" << _origin.value() <<"'"); + return; + } + setFocusNode(node); + }); + } InteractionHandler::~InteractionHandler() { From f60dfac1cf70ff7c15892e5f8fdfb73d76c71b80 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 6 Jul 2015 13:44:23 +0200 Subject: [PATCH 283/329] Update OpenSpace version to 0.1.1 prerelease-6 Do not print the SGCT . waiting message rename libtorrent.config to Launcher.config --- CMakeLists.txt | 2 +- apps/Launcher/syncwidget.cpp | 2 +- apps/OpenSpace/main.cpp | 4 ++++ ext/ghoul | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d989aee505..42cc95085a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,7 +40,7 @@ include(${OPENSPACE_EXT_DIR}/ghoul/ext/CopySharedLibraries.cmake) test_compiler_compatibility() cleanup_project() set_build_output_directories() -configure_openspace_version(0 1 0 "prerelease-5") +configure_openspace_version(0 1 1 "prerelease-6") option(OPENSPACE_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF) option(OPENSPACE_DISABLE_EXTERNAL_WARNINGS "Disable warnings in external libraries" ON) diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 69a127156c..b4c2152364 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -57,7 +57,7 @@ namespace { const std::string _loggerCat = "SyncWidget"; - const std::string _configurationFile = "libtorrent.config"; + const std::string _configurationFile = "Launcher.config"; const int nColumns = 3; diff --git a/apps/OpenSpace/main.cpp b/apps/OpenSpace/main.cpp index 2854a29e94..c798f1ae31 100644 --- a/apps/OpenSpace/main.cpp +++ b/apps/OpenSpace/main.cpp @@ -260,6 +260,10 @@ void mainDecodeFun() { void mainLogCallback(const char* msg){ std::string message = msg; + if (message == ".") + // We don't want the empty '.' message that SGCT sends while it is waiting for + // connections from other network nodes + return; // Remove the trailing \n that is passed along LINFOC("SGCT", message.substr(0, std::max(message.size() - 1, 0))); } diff --git a/ext/ghoul b/ext/ghoul index f87ad95260..310b4ece36 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit f87ad95260996a56105fb4543e734e86d187413a +Subproject commit 310b4ece3653b13e37f0d49cc31386545cf45737 From 51bab14dcc28dd55bddc94dfadf4da28ccbe8c3f Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Mon, 6 Jul 2015 14:06:54 +0200 Subject: [PATCH 284/329] adding initialization messages functionality --- .../openspace/network/parallelconnection.h | 3 +- src/network/parallelconnection.cpp | 375 ++++++++++++------ 2 files changed, 248 insertions(+), 130 deletions(-) diff --git a/include/openspace/network/parallelconnection.h b/include/openspace/network/parallelconnection.h index 84d7c79d63..61483a418d 100644 --- a/include/openspace/network/parallelconnection.h +++ b/include/openspace/network/parallelconnection.h @@ -134,7 +134,7 @@ namespace openspace{ return hashVal; }; - void queMessage(std::vector message); + void queueMessage(std::vector message); void disconnect(); @@ -198,6 +198,7 @@ namespace openspace{ std::mutex _timeKeyframeMutex; std::atomic _latestTimeKeyframeValid; std::map _currentState; + std::mutex _currentStateMutex; }; } // namespace network diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index cddcb2729c..23b5e0cbfa 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -365,7 +365,7 @@ namespace openspace { buffer.insert(buffer.end(), _name.begin(), _name.end()); //send buffer - queMessage(buffer); + queueMessage(buffer); } void ParallelConnection::delegateDecoding(uint32_t type){ @@ -373,9 +373,9 @@ namespace openspace { case MessageTypes::Authentication: //do nothing for now break; -// case MessageTypes::Initialization: -// initializationMessageReceived(); -// break; + case MessageTypes::Initialization: + initializationMessageReceived(); + break; case MessageTypes::Data: dataMessageReceived(); // break; @@ -386,9 +386,9 @@ namespace openspace { case MessageTypes::HostInfo: hostInfoMessageReceived(); break; -// case MessageTypes::InitializationRequest: -// initializationRequestMessageReceived(); -// break; + case MessageTypes::InitializationRequest: + initializationRequestMessageReceived(); + break; default: //unknown message type break; @@ -398,85 +398,140 @@ namespace openspace { void ParallelConnection::initializationMessageReceived(){ int result; - uint16_t numScripts; - uint32_t datalen; - uint32_t id; - //create a buffer to hold the number of scripts in the initialization message + //ID and size of data chunk are removed by the server + uint16_t numscripts; + std::vector buffer; - buffer.resize(sizeof(id)); + buffer.resize(sizeof(numscripts)); - //read recepient ID (mine) - result = receiveData(_clientSocket, buffer, sizeof(id), 0); - if (result <= 0){ - //error - return; - } - id = *(reinterpret_cast(buffer.data())); - - //read data length - result = receiveData(_clientSocket, buffer, sizeof(datalen), 0); - if (result <= 0){ - //error - return; - } - datalen = *(reinterpret_cast(buffer.data())); - - buffer.clear(); - buffer.resize(sizeof(numScripts)); - //read number of scripts - result = receiveData(_clientSocket, buffer, sizeof(numScripts), 0); - if (result <= 0){ - //error - return; - } - numScripts = *(reinterpret_cast(buffer.data())); - - //declare placeholder for all received scripts - std::vector initScripts; - initScripts.reserve(numScripts); + //read number of scripts + result = receiveData(_clientSocket, buffer, sizeof(numscripts), 0); + if(result < 0){ + //error + } - //length of each script and resize receiveing buffer + numscripts = *(reinterpret_cast(buffer.data())); + + //length of current script uint16_t scriptlen; + buffer.clear(); - buffer.resize(sizeof(scriptlen)); + buffer.resize(scriptlen); - //buffer for holding received scripts - std::vector scriptbuffer; + //holder for current script + std::string script; - for(int n = 0; n < numScripts; ++n){ - - //read size in chars of next script - result = receiveData(_clientSocket, buffer, sizeof(scriptlen), 0); - - if (result <= 0){ + for(int n = 0; n < numscripts; ++n){ + //read length of script + result = receiveData(_clientSocket, buffer, sizeof(numscripts), 0); + if(result < 0){ //error - return; } - - //assign size of next script - scriptlen = *reinterpret_cast(buffer.data()); + scriptlen = *(reinterpret_cast(buffer.data())); //resize buffer - scriptbuffer.clear(); - scriptbuffer.resize(scriptlen); + buffer.clear(); + buffer.resize(scriptlen); - //read next script - result = receiveData(_clientSocket, scriptbuffer, scriptlen, 0); + //read script + result = receiveData(_clientSocket, buffer, scriptlen, 0); + + //assign current script + script.clear(); + script.assign(buffer.begin(), buffer.end()); - if (result <= 0){ - //error - return; - } - - //create a string from received chars in buffer - std::string script; - script.assign(scriptbuffer.begin(), scriptbuffer.end()); - - //que script with the script engine + //queue received script OsEng.scriptEngine()->queueScript(script); } + //we've gone through all scripts, initialization is done + buffer.clear(); + writeHeader(buffer, MessageTypes::InitializationCompleted); + + //let the server know + queueMessage(buffer); + +// int result; +// uint16_t numScripts; +// uint32_t datalen; +// uint32_t id; +// +// //create a buffer to hold the number of scripts in the initialization message +// std::vector buffer; +// buffer.resize(sizeof(id)); +// +// //read recepient ID (mine) +// result = receiveData(_clientSocket, buffer, sizeof(id), 0); +// if (result <= 0){ +// //error +// return; +// } +// id = *(reinterpret_cast(buffer.data())); +// +// //read data length +// result = receiveData(_clientSocket, buffer, sizeof(datalen), 0); +// if (result <= 0){ +// //error +// return; +// } +// datalen = *(reinterpret_cast(buffer.data())); +// +// buffer.clear(); +// buffer.resize(sizeof(numScripts)); +// //read number of scripts +// result = receiveData(_clientSocket, buffer, sizeof(numScripts), 0); +// if (result <= 0){ +// //error +// return; +// } +// numScripts = *(reinterpret_cast(buffer.data())); +// +// //declare placeholder for all received scripts +// std::vector initScripts; +// initScripts.reserve(numScripts); +// +// //length of each script and resize receiveing buffer +// uint16_t scriptlen; +// buffer.clear(); +// buffer.resize(sizeof(scriptlen)); +// +// //buffer for holding received scripts +// std::vector scriptbuffer; +// +// for(int n = 0; n < numScripts; ++n){ +// +// //read size in chars of next script +// result = receiveData(_clientSocket, buffer, sizeof(scriptlen), 0); +// +// if (result <= 0){ +// //error +// return; +// } +// +// //assign size of next script +// scriptlen = *reinterpret_cast(buffer.data()); +// +// //resize buffer +// scriptbuffer.clear(); +// scriptbuffer.resize(scriptlen); +// +// //read next script +// result = receiveData(_clientSocket, scriptbuffer, scriptlen, 0); +// +// if (result <= 0){ +// //error +// return; +// } +// +// //create a string from received chars in buffer +// std::string script; +// script.assign(scriptbuffer.begin(), scriptbuffer.end()); +// +// //que script with the script engine +// OsEng.scriptEngine()->queueScript(script); +// } + } void ParallelConnection::dataMessageReceived(){ @@ -624,7 +679,7 @@ namespace openspace { OsEng.scriptEngine()->queueScript(script); } - void ParallelConnection::queMessage(std::vector message){ + void ParallelConnection::queueMessage(std::vector message){ _sendBufferMutex.lock(); _sendBuffer.push_back(message); _sendBufferMutex.unlock(); @@ -715,7 +770,7 @@ namespace openspace { writeHeader(buffer, MessageTypes::InitializationRequest); //send message - queMessage(buffer); + queueMessage(buffer); } } else{ @@ -725,74 +780,133 @@ namespace openspace { } void ParallelConnection::initializationRequestMessageReceived(){ + + //get current state as scripts + std::vector scripts; + std::map::iterator state_it; + { + //mutex protect + std::lock_guard lock(_currentStateMutex); + + for(state_it = _currentState.begin(); + state_it != _currentState.end(); + ++state_it){ + scripts.push_back(scriptFromPropertyAndValue(state_it->first, state_it->second)); + } + } + + //get requester ID std::vector buffer; buffer.resize(sizeof(uint32_t)); receiveData(_clientSocket, buffer, sizeof(uint32_t), 0); uint32_t requesterID = *reinterpret_cast(buffer.data()); - printf("InitRequest message received from client %d!\n", requesterID); - //construct init msg - std::vector scripts = OsEng.scriptEngine()->cachedScripts(); + //total number of scripts sent + uint16_t numscripts = 0; + //temporary buffers + std::vector scriptbuffer; + std::vector tmpbuffer; - uint16_t scriptlen; - uint32_t totlen = 0; - std::vector::const_iterator it; - - std::vector scriptMsg; - - //add a script of the current time to ensure all nodes are on the same page - std::string timescript = "openspace.time.setTime(" + std::to_string(Time::ref().currentTime()) + ");"; - scripts.push_back(timescript); - - //add a script of the current delta time to ensure all nodes are on the same page - - std::string dtscript = "openspace.time.setDeltaTime(" + std::to_string(Time::ref().deltaTime()) + ");"; - scripts.push_back(dtscript); - - //add a terminating script letting the server know the client is fully initialized - std::string donescript = "openspace.parallel.initialized();"; - scripts.push_back(donescript); - - //total number of scripts - uint16_t numScrips = static_cast(scripts.size()); - - //write all scripts - for(it = scripts.cbegin(); - it != scripts.cend(); - ++it){ - //write size of script in chars - scriptlen = (*it).size(); - scriptMsg.insert(scriptMsg.end(), reinterpret_cast(&scriptlen), reinterpret_cast(&scriptlen) + sizeof(scriptlen)); + //serialize and encode all scripts into scriptbuffer + std::vector::iterator script_it; + network::datamessagestructures::ScriptMessage sm; + for(script_it = scripts.begin(); + script_it != scripts.end(); + ++script_it){ + sm._script = *script_it; + sm._scriptlen = script_it->length(); + + //serialize current script + tmpbuffer.clear(); + sm.serialize(tmpbuffer); - //write actual scripts - scriptMsg.insert(scriptMsg.end(), (*it).begin(), (*it).end()); + //and insert into full buffer + scriptbuffer.insert(scriptbuffer.end(), tmpbuffer.begin(), tmpbuffer.end()); - //add script length to total data length - totlen += static_cast(scriptlen) + sizeof(scriptlen); + //increment number of scripts + numscripts++; } - //clear buffer - buffer.clear(); - //write header + buffer.clear(); writeHeader(buffer, MessageTypes::Initialization); - //write requester ID - buffer.insert(buffer.end(), reinterpret_cast(&requesterID), reinterpret_cast(&requesterID) + sizeof(requesterID)); - - - //write size of data chunk - buffer.insert(buffer.end(), reinterpret_cast(&totlen), reinterpret_cast(&totlen) + sizeof(totlen)); + //write total size of data chunk + uint32_t totlen = static_cast(scriptbuffer.size()); + buffer.insert(buffer.end(), reinterpret_cast(&totlen), reinterpret_cast(&totlen) + sizeof(uint32_t)); //write number of scripts - buffer.insert(buffer.end(), reinterpret_cast(&numScrips), reinterpret_cast(&numScrips) + sizeof(numScrips)); + buffer.insert(buffer.end(), reinterpret_cast(&numscripts), reinterpret_cast(&numscripts) + sizeof(uint16_t)); - //write all scripts and their lengths - buffer.insert(buffer.end(), scriptMsg.begin(), scriptMsg.end()); + //write all scripts + buffer.insert(buffer.end(), scriptbuffer.begin(), scriptbuffer.end()); - //send initialization message - queMessage(buffer); + //queue message + queueMessage(buffer); +// +// //construct init msg +// std::vector scripts = OsEng.scriptEngine()->cachedScripts(); +// +// +// uint16_t scriptlen; +// uint32_t totlen = 0; +// std::vector::const_iterator it; +// +// std::vector scriptMsg; +// +// //add a script of the current time to ensure all nodes are on the same page +// std::string timescript = "openspace.time.setTime(" + std::to_string(Time::ref().currentTime()) + ");"; +// scripts.push_back(timescript); +// +// //add a script of the current delta time to ensure all nodes are on the same page +// +// std::string dtscript = "openspace.time.setDeltaTime(" + std::to_string(Time::ref().deltaTime()) + ");"; +// scripts.push_back(dtscript); +// +// //add a terminating script letting the server know the client is fully initialized +// std::string donescript = "openspace.parallel.initialized();"; +// scripts.push_back(donescript); +// +// //total number of scripts +// uint16_t numScrips = static_cast(scripts.size()); +// +// //write all scripts +// for(it = scripts.cbegin(); +// it != scripts.cend(); +// ++it){ +// //write size of script in chars +// scriptlen = (*it).size(); +// scriptMsg.insert(scriptMsg.end(), reinterpret_cast(&scriptlen), reinterpret_cast(&scriptlen) + sizeof(scriptlen)); +// +// //write actual scripts +// scriptMsg.insert(scriptMsg.end(), (*it).begin(), (*it).end()); +// +// //add script length to total data length +// totlen += static_cast(scriptlen) + sizeof(scriptlen); +// } +// +// //clear buffer +// buffer.clear(); +// +// //write header +// writeHeader(buffer, MessageTypes::Initialization); +// +// //write requester ID +// buffer.insert(buffer.end(), reinterpret_cast(&requesterID), reinterpret_cast(&requesterID) + sizeof(requesterID)); +// +// +// //write size of data chunk +// buffer.insert(buffer.end(), reinterpret_cast(&totlen), reinterpret_cast(&totlen) + sizeof(totlen)); +// +// //write number of scripts +// buffer.insert(buffer.end(), reinterpret_cast(&numScrips), reinterpret_cast(&numScrips) + sizeof(numScrips)); +// +// //write all scripts and their lengths +// buffer.insert(buffer.end(), scriptMsg.begin(), scriptMsg.end()); +// +// //send initialization message +// queueMessage(buffer); } void ParallelConnection::listenCommunication(){ @@ -890,7 +1004,7 @@ namespace openspace { writeHeader(buffer, MessageTypes::HostshipRequest); //send message - queMessage(buffer); + queueMessage(buffer); } void ParallelConnection::setPassword(const std::string& pwd){ @@ -912,7 +1026,7 @@ namespace openspace { buffer.insert(buffer.end(), script.begin(), script.end()); //send message - queMessage(buffer); + queueMessage(buffer); } bool ParallelConnection::initNetworkAPI(){ @@ -979,7 +1093,7 @@ namespace openspace { buffer.insert(buffer.end(), tbuffer.begin(), tbuffer.end()); //send message - queMessage(buffer); + queueMessage(buffer); } else{ //if we're not the host and we have a valid keyframe (one that hasnt been used before) @@ -1010,10 +1124,13 @@ namespace openspace { void ParallelConnection::scriptMessage(const std::string propIdentifier, const std::string propValue){ //save script as current state - _currentState[propIdentifier] = propValue; + { + //mutex protect + std::lock_guard lock(_currentStateMutex); + _currentState[propIdentifier] = propValue; + } //if we're connected and we're the host, also send the script - if(_isConnected.load() && _isHost.load()){ //construct script std::string script = scriptFromPropertyAndValue(propIdentifier, propValue); @@ -1052,7 +1169,7 @@ namespace openspace { buffer.insert(buffer.end(), sbuffer.begin(), sbuffer.end()); //send message - queMessage(buffer); + queueMessage(buffer); } } @@ -1105,7 +1222,7 @@ namespace openspace { buffer.insert(buffer.end(), kfBuffer.begin(), kfBuffer.end()); //send message - queMessage(buffer); + queueMessage(buffer); //100 ms sleep - send keyframes 10 times per second std::this_thread::sleep_for(std::chrono::milliseconds(100)); @@ -1144,7 +1261,7 @@ namespace openspace { writeHeader(buffer, MessageTypes::InitializationCompleted); //queue script - queMessage(buffer); + queueMessage(buffer); } scripting::ScriptEngine::LuaLibrary ParallelConnection::luaLibrary() { From fa765b9dedcd0f70e7234f5a6f28c37456594d98 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 6 Jul 2015 14:50:55 +0200 Subject: [PATCH 285/329] Introducting hack to make Interactionhandler properties be settable by scripts --- src/query/query.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/query/query.cpp b/src/query/query.cpp index 4aa8003eb4..a2443091f7 100644 --- a/src/query/query.cpp +++ b/src/query/query.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -36,13 +37,11 @@ namespace { const std::string _loggerCat = "Query"; } -Scene* sceneGraph() -{ +Scene* sceneGraph() { return OsEng.renderEngine()->scene(); } -SceneGraphNode* sceneGraphNode(const std::string& name) -{ +SceneGraphNode* sceneGraphNode(const std::string& name) { const Scene* graph = sceneGraph(); return graph->sceneGraphNode(name); } @@ -52,8 +51,7 @@ Renderable* renderable(const std::string& name) { return node->renderable(); } -properties::Property* property(const std::string& uri) -{ +properties::Property* property(const std::string& uri) { // The URI consists of the following form at this stage: // .{.}^(0..n) @@ -65,6 +63,17 @@ properties::Property* property(const std::string& uri) } const std::string nodeName = uri.substr(0, nodeNameSeparator); const std::string remainingUri = uri.substr(nodeNameSeparator + 1); + + if (nodeName == "Interaction") { + properties::Property* p = OsEng.interactionHandler()->property(remainingUri); + + if (!p) { + LERROR("Node '" << nodeName << "' did not exist"); + return nullptr; + } + else + return p; + } SceneGraphNode* node = sceneGraphNode(nodeName); if (!node) { From f01278be609fb0a68f52e120d39f85fdaecfdc52 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Mon, 6 Jul 2015 14:56:02 +0200 Subject: [PATCH 286/329] test --- src/network/parallelconnection.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index 23b5e0cbfa..ff779c9657 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -780,7 +780,8 @@ namespace openspace { } void ParallelConnection::initializationRequestMessageReceived(){ - + //@FIX + return; //get current state as scripts std::vector scripts; std::map::iterator state_it; @@ -791,7 +792,7 @@ namespace openspace { for(state_it = _currentState.begin(); state_it != _currentState.end(); ++state_it){ - scripts.push_back(scriptFromPropertyAndValue(state_it->first, state_it->second)); +// scripts.push_back(scriptFromPropertyAndValue(state_it->first, state_it->second)); } } @@ -832,6 +833,9 @@ namespace openspace { buffer.clear(); writeHeader(buffer, MessageTypes::Initialization); + //write client ID to receive init message + buffer.insert(buffer.end(), reinterpret_cast(&requesterID), reinterpret_cast(&requesterID) + sizeof(uint32_t)); + //write total size of data chunk uint32_t totlen = static_cast(scriptbuffer.size()); buffer.insert(buffer.end(), reinterpret_cast(&totlen), reinterpret_cast(&totlen) + sizeof(uint32_t)); From 0550ffd0db4489584239e0bde6519d6b8a99503b Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Mon, 6 Jul 2015 15:06:48 +0200 Subject: [PATCH 287/329] recommit of previous fix --- src/network/parallelconnection.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index ff779c9657..582cfe21cf 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -378,7 +378,7 @@ namespace openspace { break; case MessageTypes::Data: dataMessageReceived(); -// break; + break; // case MessageTypes::Script: // //disabled for now //// decodeScriptMessage(); @@ -780,8 +780,7 @@ namespace openspace { } void ParallelConnection::initializationRequestMessageReceived(){ - //@FIX - return; + //get current state as scripts std::vector scripts; std::map::iterator state_it; @@ -792,7 +791,7 @@ namespace openspace { for(state_it = _currentState.begin(); state_it != _currentState.end(); ++state_it){ -// scripts.push_back(scriptFromPropertyAndValue(state_it->first, state_it->second)); + scripts.push_back(scriptFromPropertyAndValue(state_it->first, state_it->second)); } } From 36db7c37bf82a2c6687789fceebbea857ff9bf78 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Mon, 6 Jul 2015 15:09:08 +0200 Subject: [PATCH 288/329] minor fix --- src/network/parallelconnection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index 582cfe21cf..38cb2844c4 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -417,7 +417,7 @@ namespace openspace { uint16_t scriptlen; buffer.clear(); - buffer.resize(scriptlen); + buffer.resize(sizeof(scriptlen)); //holder for current script std::string script; From 0345cb2d2890bae3e88ec028ba2555a192222643 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 6 Jul 2015 15:16:02 +0200 Subject: [PATCH 289/329] Fix downloadmanager on Mac --- src/engine/downloadmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp index 499839e4b3..bd82114c05 100644 --- a/src/engine/downloadmanager.cpp +++ b/src/engine/downloadmanager.cpp @@ -226,7 +226,7 @@ std::vector DownloadManager::downloadRequestFiles( int nFinished = 0; while (std::getline(temporary, line)) { ++nFiles; - std::string file = ghoul::filesystem::File(line).filename(); + std::string file = ghoul::filesystem::File(line, true).filename(); LDEBUG("\tLine: " << line << " ; Dest: " << destination.path() + "/" + file); From 5c45815b9c509cbf98d1f3c7ad1e6035c4962a81 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 6 Jul 2015 15:16:21 +0200 Subject: [PATCH 290/329] Prefer FreeImage to DevIL as DevIL has issues on Mac --- src/rendering/renderengine.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 10fe760fc8..43992af703 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -178,12 +178,12 @@ bool RenderEngine::initialize() { _mainCamera->setPosition(psc(0.f, 0.f, 1.499823f, 11.f)); OsEng.interactionHandler()->setCamera(_mainCamera); +#ifdef GHOUL_USE_FREEIMAGE + ghoul::io::TextureReader::ref().addReader(new ghoul::io::impl::TextureReaderFreeImage); +#endif // GHOUL_USE_FREEIMAGE #ifdef GHOUL_USE_DEVIL ghoul::io::TextureReader::ref().addReader(new ghoul::io::impl::TextureReaderDevIL); #endif // GHOUL_USE_DEVIL -#ifdef GHOUL_USE_FREEIMAGE - ghoul::io::TextureReader::ref().addReader(new ghoul::io::impl::TextureReaderFreeImage); -#endif // GHOUL_USE_FREEIMAGE ghoul::io::TextureReader::ref().addReader(new ghoul::io::impl::TextureReaderCMAP); From 21225b1cb5c423a371f2dd703462984dd40a802b Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 6 Jul 2015 15:54:48 +0200 Subject: [PATCH 291/329] Making the setPropertyOwner method also add the calling property owner as a subowner --- include/openspace/properties/propertyowner.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openspace/properties/propertyowner.h b/include/openspace/properties/propertyowner.h index 4906d94915..14f2cd87de 100644 --- a/include/openspace/properties/propertyowner.h +++ b/include/openspace/properties/propertyowner.h @@ -114,7 +114,7 @@ public: */ bool hasProperty(const std::string& URI) const; - void setPropertyOwner(PropertyOwner* owner) { _owner = owner; } + void setPropertyOwner(PropertyOwner* owner) { _owner = owner; _owner->addPropertySubOwner(this); } PropertyOwner* owner() const { return _owner; } /** From 678362eee2317c5ad8905fb5954760d09c9cd6b0 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 6 Jul 2015 15:55:42 +0200 Subject: [PATCH 292/329] Add a global propertyowner namespace to which the InteractionHandler is added, removing the need for the earlier hack --- include/openspace/engine/openspaceengine.h | 12 ++++- .../interaction/interactionhandler.h | 2 +- src/engine/openspaceengine.cpp | 11 ++++- src/interaction/interactionhandler.cpp | 4 +- src/query/query.cpp | 47 +++++++++---------- 5 files changed, 45 insertions(+), 31 deletions(-) diff --git a/include/openspace/engine/openspaceengine.h b/include/openspace/engine/openspaceengine.h index a2160a7194..1da8cbc773 100644 --- a/include/openspace/engine/openspaceengine.h +++ b/include/openspace/engine/openspaceengine.h @@ -57,10 +57,14 @@ namespace scripting { class ScriptEngine; } -namespace network{ +namespace network { class ParallelConnection; } - + +namespace properties { + class PropertyOwner; +} + class OpenSpaceEngine { public: static bool create(int argc, char** argv, std::vector& sgctArguments); @@ -84,6 +88,7 @@ public: LuaConsole* console(); ModuleEngine* moduleEngine(); network::ParallelConnection* parallelConnection(); + properties::PropertyOwner* globalPropertyOwner(); gui::GUI* gui(); @@ -132,6 +137,9 @@ private: ModuleEngine* _moduleEngine; gui::GUI* _gui; network::ParallelConnection* _parallelConnection; + + properties::PropertyOwner* _globalPropertyNamespace; + bool _isMaster; double _runTime; diff --git a/include/openspace/interaction/interactionhandler.h b/include/openspace/interaction/interactionhandler.h index c0c0ecc12e..52948cfe87 100644 --- a/include/openspace/interaction/interactionhandler.h +++ b/include/openspace/interaction/interactionhandler.h @@ -89,7 +89,7 @@ class SceneGraphNode; namespace interaction { - class InteractionHandler : public properties::PropertyOwner{ +class InteractionHandler : public properties::PropertyOwner { public: InteractionHandler(); diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 2c2abf44c1..38a1b2a15d 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -110,11 +111,14 @@ OpenSpaceEngine::OpenSpaceEngine(std::string programName) , _console(new LuaConsole) , _moduleEngine(new ModuleEngine) , _gui(new gui::GUI) + , _parallelConnection(new network::ParallelConnection) + , _globalPropertyNamespace(new properties::PropertyOwner) , _isMaster(false) , _runTime(0.0) , _syncBuffer(nullptr) - , _parallelConnection(new network::ParallelConnection) { + _globalPropertyNamespace->setName("Global"); + _interactionHandler->setPropertyOwner(_globalPropertyNamespace); FactoryManager::initialize(); SpiceManager::initialize(); Time::initialize(); @@ -818,5 +822,10 @@ network::ParallelConnection* OpenSpaceEngine::parallelConnection() { ghoul_assert(_parallelConnection != nullptr, "ParallelConnection is nullptr"); return _parallelConnection; } + +properties::PropertyOwner* OpenSpaceEngine::globalPropertyOwner() { + ghoul_assert(_globalPropertyNamespace, "Global Property Namespace"); + return _globalPropertyNamespace; +} } // namespace openspace diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index 8b8745d55b..d72e95c7e7 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -245,7 +245,8 @@ namespace openspace { namespace interaction { InteractionHandler::InteractionHandler() - : _camera(nullptr) + : properties::PropertyOwner() + , _camera(nullptr) , _focusNode(nullptr) , _deltaTime(0.0) , _validKeyLua(false) @@ -257,6 +258,7 @@ InteractionHandler::InteractionHandler() , _currentKeyframeTime(-1.0) , _origin("origin", "Origin", "") { + setName("Interaction"); addProperty(_origin); _origin.onChange([this](){ diff --git a/src/query/query.cpp b/src/query/query.cpp index a2443091f7..f0fa4a087e 100644 --- a/src/query/query.cpp +++ b/src/query/query.cpp @@ -1,4 +1,4 @@ -/***************************************************************************************** + /***************************************************************************************** * * * OpenSpace * * * @@ -52,37 +52,32 @@ Renderable* renderable(const std::string& name) { } properties::Property* property(const std::string& uri) { - // The URI consists of the following form at this stage: - // .{.}^(0..n) - - const size_t nodeNameSeparator = uri.find(properties::PropertyOwner::URISeparator); - if (nodeNameSeparator == std::string::npos) { - LERROR("Malformed URI '" << uri << "': At least one '" << nodeNameSeparator - << "' separator must be present."); - return nullptr; + properties::Property* globalProp = OsEng.globalPropertyOwner()->property(uri); + if (globalProp) { + return globalProp; } - const std::string nodeName = uri.substr(0, nodeNameSeparator); - const std::string remainingUri = uri.substr(nodeNameSeparator + 1); - - if (nodeName == "Interaction") { - properties::Property* p = OsEng.interactionHandler()->property(remainingUri); + else { + // The URI consists of the following form at this stage: + // .{.}^(0..n) - if (!p) { + const size_t nodeNameSeparator = uri.find(properties::PropertyOwner::URISeparator); + if (nodeNameSeparator == std::string::npos) { + LERROR("Malformed URI '" << uri << "': At least one '" << nodeNameSeparator + << "' separator must be present."); + return nullptr; + } + const std::string nodeName = uri.substr(0, nodeNameSeparator); + const std::string remainingUri = uri.substr(nodeNameSeparator + 1); + + SceneGraphNode* node = sceneGraphNode(nodeName); + if (!node) { LERROR("Node '" << nodeName << "' did not exist"); return nullptr; } - else - return p; + + properties::Property* property = node->property(remainingUri); + return property; } - - SceneGraphNode* node = sceneGraphNode(nodeName); - if (!node) { - LERROR("Node '" << nodeName << "' did not exist"); - return nullptr; - } - - properties::Property* property = node->property(remainingUri); - return property; } } // namespace From bb32699f701c4ea7a41f1ee2951789e7deb6cce0 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Mon, 6 Jul 2015 15:57:51 +0200 Subject: [PATCH 293/329] commenting out older version of script sharing --- src/scripting/scriptengine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scripting/scriptengine.cpp b/src/scripting/scriptengine.cpp index bffbf962f5..097697c54a 100644 --- a/src/scripting/scriptengine.cpp +++ b/src/scripting/scriptengine.cpp @@ -160,8 +160,8 @@ bool ScriptEngine::runScript(const std::string& script) { std::string lib, func; if (parseLibraryAndFunctionNames(lib, func, script) && shouldScriptBeSent(lib, func)){ - OsEng.parallelConnection()->sendScript(script); - cacheScript(lib, func, script); +// OsEng.parallelConnection()->sendScript(script); +// cacheScript(lib, func, script); } } From 1de7a76cde41f58112c9c6b393d055dd7e13b394 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 6 Jul 2015 16:14:49 +0200 Subject: [PATCH 294/329] Making changing the coordinate system a property in InteractionHandler --- .../openspace/interaction/interactionhandler.h | 4 ++-- src/interaction/interactionhandler.cpp | 9 +++++++-- src/rendering/renderengine.cpp | 16 ++++++++-------- src/rendering/renderengine_lua.inl | 2 ++ 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/include/openspace/interaction/interactionhandler.h b/include/openspace/interaction/interactionhandler.h index 52948cfe87..8ddd17f56f 100644 --- a/include/openspace/interaction/interactionhandler.h +++ b/include/openspace/interaction/interactionhandler.h @@ -180,9 +180,9 @@ private: MouseController* _mouseController; std::vector _controllers; - properties::StringProperty _origin; - + properties::StringProperty _coordinateSystem; + //remote controller std::vector _keyframes; double _currentKeyframeTime; diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index d72e95c7e7..f0b2de9445 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -255,12 +255,12 @@ InteractionHandler::InteractionHandler() , _invertRotation(false) , _keyboardController(nullptr) , _mouseController(nullptr) - , _currentKeyframeTime(-1.0) , _origin("origin", "Origin", "") + , _coordinateSystem("coordinateSystem", "Coordinate System", "") + , _currentKeyframeTime(-1.0) { setName("Interaction"); - addProperty(_origin); _origin.onChange([this](){ SceneGraphNode* node = sceneGraphNode(_origin.value()); if (!node) { @@ -269,7 +269,12 @@ InteractionHandler::InteractionHandler() } setFocusNode(node); }); + addProperty(_origin); + _coordinateSystem.onChange([this](){ + OsEng.renderEngine()->changeViewPoint(_coordinateSystem.value()); + }); + addProperty(_coordinateSystem); } InteractionHandler::~InteractionHandler() { diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 24b3a374d0..8b91f6361c 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -789,13 +789,13 @@ scripting::ScriptEngine::LuaLibrary RenderEngine::luaLibrary() { "Sets the performance measurements" }, // These are temporary ---abock - { - "changeCoordinateSystem", - &luascriptfunctions::changeCoordinateSystem, - "string", - "Changes the origin of the coordinate system to the passed node", - true - }, +// { +// "changeCoordinateSystem", +// &luascriptfunctions::changeCoordinateSystem, +// "string", +// "Changes the origin of the coordinate system to the passed node", +// true +// }, { "fadeIn", &luascriptfunctions::fadeIn, @@ -1283,7 +1283,7 @@ void RenderEngine::changeViewPoint(std::string origin) { // //} - ghoul_assert(false, "This function is being misused"); + LFATAL("This function is being misused with an argument of '" << origin << "'"); } void RenderEngine::setSGCTRenderStatistics(bool visible) { diff --git a/src/rendering/renderengine_lua.inl b/src/rendering/renderengine_lua.inl index eb664afee7..70410eaa87 100644 --- a/src/rendering/renderengine_lua.inl +++ b/src/rendering/renderengine_lua.inl @@ -26,6 +26,7 @@ namespace openspace { namespace luascriptfunctions { +/** int changeCoordinateSystem(lua_State* L) { int nArguments = lua_gettop(L); if (nArguments != 1) @@ -35,6 +36,7 @@ int changeCoordinateSystem(lua_State* L) { OsEng.renderEngine()->changeViewPoint(newCenter); return 1; } +*/ /** * \ingroup LuaScripts From 2a3f0bbccac3dee5f5e20bd6d7a3c1cfe5e86d3b Mon Sep 17 00:00:00 2001 From: Michal Marcinkowski Date: Mon, 6 Jul 2015 10:35:03 -0400 Subject: [PATCH 295/329] Optimization quick-fix for projections: - Using queue to distribute imagesequencer-returned images 1 image/ frame - Imagesequencer no longer returns 'reversed' image-vector --- ext/ghoul | 2 +- .../rendering/renderableplanetprojection.cpp | 46 +++++++++++++++++-- .../rendering/renderableplanetprojection.h | 3 ++ modules/newhorizons/util/imagesequencer.cpp | 2 +- 4 files changed, 47 insertions(+), 6 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index 310b4ece36..3a095f1ce6 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 310b4ece3653b13e37f0d49cc31386545cf45737 +Subproject commit 3a095f1ce633e52b94877bde67e77775262e3dbd diff --git a/modules/newhorizons/rendering/renderableplanetprojection.cpp b/modules/newhorizons/rendering/renderableplanetprojection.cpp index a734d25496..22e14912f1 100644 --- a/modules/newhorizons/rendering/renderableplanetprojection.cpp +++ b/modules/newhorizons/rendering/renderableplanetprojection.cpp @@ -452,11 +452,35 @@ void RenderablePlanetProjection::textureBind() { } void RenderablePlanetProjection::project(){ - for (auto const &img : _imageTimes){ - RenderablePlanetProjection::attitudeParameters(img.startTime); - _projectionTexturePath = img.path; // path to current images - imageProjectGPU(); // fbopass + // If high dt -> results in GPU queue overflow + // switching to using a simple queue to distribute + // images 1 image / frame -> projections appear slower + // but less viewable lagg for the sim overall. + + // Comment out if not using queue and prefer old method ------------- + // + in update() function + if (!imageQueue.empty()){ + Image& img = imageQueue.front(); + RenderablePlanetProjection::attitudeParameters(img.startTime); + // if image has new path - ie actual image, NOT placeholder + if (_projectionTexturePath.value() != img.path){ + // rebind and upload + _projectionTexturePath = img.path; + } + imageProjectGPU(); // fbopass + imageQueue.pop(); } + // ------------------------------------------------------------------ + + //---- Old method --- // + // @mm + //for (auto const &img : _imageTimes){ + // RenderablePlanetProjection::attitudeParameters(img.startTime); + // if (_projectionTexturePath.value() != img.path){ + // _projectionTexturePath = img.path; // path to current images + // } + // imageProjectGPU(); // fbopass + //} _capture = false; } @@ -507,6 +531,11 @@ void RenderablePlanetProjection::render(const RenderData& data){ } void RenderablePlanetProjection::update(const UpdateData& data){ + if (_time > Time::ref().currentTime()){ + std::cout << "detected backjump, clearing queue" << std::endl; + imageQueue = {}; // if jump back in time -> empty queue. + } + _time = Time::ref().currentTime(); _capture = false; @@ -514,6 +543,15 @@ void RenderablePlanetProjection::update(const UpdateData& data){ openspace::ImageSequencer2::ref().updateSequencer(_time); _capture = openspace::ImageSequencer2::ref().getImagePaths(_imageTimes, _projecteeID, _instrumentID); } + + // remove these lines if not using queue ------------------------ + // @mm + _capture = true; + for (auto img : _imageTimes){ + imageQueue.push(img); + } + _imageTimes.clear(); + // -------------------------------------------------------------- if (_programObject->isDirty()) _programObject->rebuildFromFile(); diff --git a/modules/newhorizons/rendering/renderableplanetprojection.h b/modules/newhorizons/rendering/renderableplanetprojection.h index 09e1eb7177..031a67da06 100644 --- a/modules/newhorizons/rendering/renderableplanetprojection.h +++ b/modules/newhorizons/rendering/renderableplanetprojection.h @@ -48,6 +48,8 @@ #include #include +#include + namespace openspace { namespace planetgeometryprojection { @@ -149,6 +151,7 @@ private: GLuint _quad; GLuint _vertexPositionBuffer; + std::queue imageQueue; }; } // namespace openspace diff --git a/modules/newhorizons/util/imagesequencer.cpp b/modules/newhorizons/util/imagesequencer.cpp index 2bb8191511..a88bde7346 100644 --- a/modules/newhorizons/util/imagesequencer.cpp +++ b/modules/newhorizons/util/imagesequencer.cpp @@ -266,7 +266,7 @@ bool ImageSequencer2::getImagePaths(std::vector& captures, return i.activeInstruments[0] == instrumentRequest; }); - std::reverse(captureTimes.begin(), captureTimes.end()); + //std::reverse(captureTimes.begin(), captureTimes.end()); captures = captureTimes; if (!captures.empty()) _latestImages[captures.back().activeInstruments.front()] = captures.back(); From 202ec5e704a5017003d3b930368387dcab2790bd Mon Sep 17 00:00:00 2001 From: Michal Marcinkowski Date: Mon, 6 Jul 2015 11:02:42 -0400 Subject: [PATCH 296/329] remove std::cout message --- modules/newhorizons/rendering/renderableplanetprojection.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/newhorizons/rendering/renderableplanetprojection.cpp b/modules/newhorizons/rendering/renderableplanetprojection.cpp index 22e14912f1..a0a44ed640 100644 --- a/modules/newhorizons/rendering/renderableplanetprojection.cpp +++ b/modules/newhorizons/rendering/renderableplanetprojection.cpp @@ -532,7 +532,6 @@ void RenderablePlanetProjection::render(const RenderData& data){ void RenderablePlanetProjection::update(const UpdateData& data){ if (_time > Time::ref().currentTime()){ - std::cout << "detected backjump, clearing queue" << std::endl; imageQueue = {}; // if jump back in time -> empty queue. } From 5d8383d91f0d1ded25a3f47b958ec48e18fcf880 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 6 Jul 2015 18:30:26 +0200 Subject: [PATCH 297/329] Allowing PropertyOwners to have an empty name (pulling their properties and subowners into the owners namespace --- include/openspace/properties/propertyowner.h | 3 +-- src/engine/openspaceengine.cpp | 2 +- src/properties/property.cpp | 3 ++- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/openspace/properties/propertyowner.h b/include/openspace/properties/propertyowner.h index 14f2cd87de..80b5a3e6d7 100644 --- a/include/openspace/properties/propertyowner.h +++ b/include/openspace/properties/propertyowner.h @@ -114,7 +114,7 @@ public: */ bool hasProperty(const std::string& URI) const; - void setPropertyOwner(PropertyOwner* owner) { _owner = owner; _owner->addPropertySubOwner(this); } + void setPropertyOwner(PropertyOwner* owner) { _owner = owner; } PropertyOwner* owner() const { return _owner; } /** @@ -162,7 +162,6 @@ public: */ const std::string& propertyGroupName(const std::string& groupID) const; -protected: /** * Assigns the Property prop to this PropertyOwner. This method will * check if the name of the Property is unique amongst Propertys and sub-owners in diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 38a1b2a15d..c154334a07 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -117,8 +117,8 @@ OpenSpaceEngine::OpenSpaceEngine(std::string programName) , _runTime(0.0) , _syncBuffer(nullptr) { - _globalPropertyNamespace->setName("Global"); _interactionHandler->setPropertyOwner(_globalPropertyNamespace); + _globalPropertyNamespace->addPropertySubOwner(_interactionHandler); FactoryManager::initialize(); SpiceManager::initialize(); Time::initialize(); diff --git a/src/properties/property.cpp b/src/properties/property.cpp index 621191ef93..cb839c9c10 100644 --- a/src/properties/property.cpp +++ b/src/properties/property.cpp @@ -75,7 +75,8 @@ std::string Property::fullyQualifiedIdentifier() const { PropertyOwner* currentOwner = owner(); while (currentOwner) { std::string ownerId = currentOwner->name(); - identifier = ownerId + "." + identifier; + if (!ownerId.empty()) + identifier = ownerId + "." + identifier; currentOwner = currentOwner->owner(); } return identifier; From 07a3c6fb0cb51a63f15e73e9b8dcbe5eba50dbcf Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Mon, 6 Jul 2015 18:44:47 +0200 Subject: [PATCH 298/329] Fixing proper decoding of initialization messages --- src/network/parallelconnection.cpp | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index 38cb2844c4..383ad633f3 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -399,18 +399,33 @@ namespace openspace { int result; - //ID and size of data chunk are removed by the server + uint32_t id, datasize; uint16_t numscripts; std::vector buffer; - buffer.resize(sizeof(numscripts)); + buffer.resize(sizeof(id)); + + //read id + result = receiveData(_clientSocket, buffer, sizeof(id), 0); + if (result < 0){ + //error + } + id = *(reinterpret_cast(buffer.data())); + + //read datalength + result = receiveData(_clientSocket, buffer, sizeof(datasize), 0); + if (result < 0){ + //error + } + datasize = *(reinterpret_cast(buffer.data())); + buffer.clear(); + buffer.resize(sizeof(uint16_t)); //read number of scripts result = receiveData(_clientSocket, buffer, sizeof(numscripts), 0); if(result < 0){ //error - } - + } numscripts = *(reinterpret_cast(buffer.data())); //length of current script From b0eef21a2c04fdf6e5375be866fd52d8ced235d0 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Mon, 6 Jul 2015 18:49:17 +0200 Subject: [PATCH 299/329] removing obsolete functions and lua scripts --- .../openspace/network/parallelconnection.h | 4 --- src/network/parallelconnection.cpp | 36 ------------------- 2 files changed, 40 deletions(-) diff --git a/include/openspace/network/parallelconnection.h b/include/openspace/network/parallelconnection.h index 61483a418d..02fc6fcde5 100644 --- a/include/openspace/network/parallelconnection.h +++ b/include/openspace/network/parallelconnection.h @@ -83,10 +83,6 @@ namespace openspace{ void setPassword(const std::string &password); - void sendScript(const std::string script); - - void initDone(); - void signalDisconnect(); void preSynchronization(); diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index 38cb2844c4..f4e47817a1 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -1013,24 +1013,6 @@ namespace openspace { void ParallelConnection::setPassword(const std::string& pwd){ _passCode = hash(pwd); } - - void ParallelConnection::sendScript(const std::string script){ - uint16_t msglen = static_cast(script.length()); - std::vector buffer; - buffer.reserve(headerSize() + sizeof(msglen) + msglen); - - //write header - writeHeader(buffer, MessageTypes::Script); - - //size of message - buffer.insert(buffer.end(), reinterpret_cast(&msglen), reinterpret_cast(&msglen) + sizeof(msglen)); - - //actual message - buffer.insert(buffer.end(), script.begin(), script.end()); - - //send message - queueMessage(buffer); - } bool ParallelConnection::initNetworkAPI(){ #if defined(__WIN32__) @@ -1254,18 +1236,6 @@ namespace openspace { //minor and major version (as uint8_t -> 1 byte) + two bytes for the chars 'O' and 'S' + 4 bytes for type of message return 2 * sizeof(uint8_t) + 2 + sizeof(uint32_t); } - - void ParallelConnection::initDone(){ - //create buffer and reserve size - std::vector buffer; - buffer.reserve(headerSize()); - - //write header - writeHeader(buffer, MessageTypes::InitializationCompleted); - - //queue script - queueMessage(buffer); - } scripting::ScriptEngine::LuaLibrary ParallelConnection::luaLibrary() { return { @@ -1313,12 +1283,6 @@ namespace openspace { "", "Request to be the host for this session" }, - { - "initialized", - &luascriptfunctions::initialized, - "", - "Request to be the host for this session" - }, } }; } From 8efbf8e3da9582b026d3a8b7202ecb0eee26bdd9 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Mon, 6 Jul 2015 18:49:52 +0200 Subject: [PATCH 300/329] removing LUA script from .inl file --- src/network/parallelconnection_lua.inl | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/network/parallelconnection_lua.inl b/src/network/parallelconnection_lua.inl index edcc4c1868..caff3a2f56 100644 --- a/src/network/parallelconnection_lua.inl +++ b/src/network/parallelconnection_lua.inl @@ -169,17 +169,6 @@ int requestHostship(lua_State* L) { return 0; } -int initialized(lua_State* L) { - - int nArguments = lua_gettop(L); - if (nArguments != 0) - return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); - if(OsEng.isMaster()){ - OsEng.parallelConnection()->initDone(); - } - return 0; -} - } // namespace luascriptfunctions } // namespace openspace From 987af4fb76fad8acb2246064d52820b864aeda4e Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Mon, 6 Jul 2015 18:55:49 +0200 Subject: [PATCH 301/329] removing more obsolete functions --- .../openspace/network/parallelconnection.h | 2 - src/network/parallelconnection.cpp | 39 ------------------- 2 files changed, 41 deletions(-) diff --git a/include/openspace/network/parallelconnection.h b/include/openspace/network/parallelconnection.h index 02fc6fcde5..af5dc01842 100644 --- a/include/openspace/network/parallelconnection.h +++ b/include/openspace/network/parallelconnection.h @@ -151,8 +151,6 @@ namespace openspace{ void initializationMessageReceived(); void dataMessageReceived(); - - void decodeScriptMessage(); void hostInfoMessageReceived(); diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index cd2006902e..46c46dd978 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -655,45 +655,6 @@ namespace openspace { } } - void ParallelConnection::decodeScriptMessage(){ - int result; - uint16_t msglen; - - //create buffer to decode size of script - std::vector buffer; - buffer.resize(sizeof(msglen)); - - //read size of received script - result = receiveData(_clientSocket, buffer, sizeof(msglen), 0); - - if (result <= 0){ - //error - return; - } - - //size of recived script - msglen = (*(reinterpret_cast(buffer.data()))); - - //clear and resize buffer to decode actual script - buffer.clear(); - buffer.resize(msglen); - - //decode script - result = receiveData(_clientSocket, buffer, msglen, 0); - - if (result <= 0){ - //error - return; - } - - //construct a script (string) from the data contained in the buffer - std::string script; - script.assign(buffer.begin(), buffer.end()); - - //tell the script engine to execute the script when appropriate - OsEng.scriptEngine()->queueScript(script); - } - void ParallelConnection::queueMessage(std::vector message){ _sendBufferMutex.lock(); _sendBuffer.push_back(message); From 8f76f8adc6fccc43a2457e915f5ec23a7b2c7e33 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Mon, 6 Jul 2015 19:10:56 +0200 Subject: [PATCH 302/329] modified return value of toString method of string properties. It can now be used in scripts --- src/properties/stringproperty.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/properties/stringproperty.cpp b/src/properties/stringproperty.cpp index 0eeffd7a3b..7e7f8b5aca 100644 --- a/src/properties/stringproperty.cpp +++ b/src/properties/stringproperty.cpp @@ -46,7 +46,7 @@ REGISTER_TEMPLATEPROPERTY_SOURCE(StringProperty, std::string, "", return value; }, [](std::string& outValue, std::string inValue) -> bool { - outValue = inValue; + outValue = "\"" + inValue + "\""; return true; }, LUA_TSTRING From 74fb0512466b35c50e3a6505c1bd273d8e8e042a Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 6 Jul 2015 19:40:20 +0200 Subject: [PATCH 303/329] Remove extra " characters when a StringProperty is set by a string --- src/properties/stringproperty.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/properties/stringproperty.cpp b/src/properties/stringproperty.cpp index 7e7f8b5aca..0cc7fa33c8 100644 --- a/src/properties/stringproperty.cpp +++ b/src/properties/stringproperty.cpp @@ -42,7 +42,15 @@ REGISTER_TEMPLATEPROPERTY_SOURCE(StringProperty, std::string, "", return true; }, [](std::string value, bool& success) -> std::string { - success = true; + // An incoming string is of the form + // "value" + // so we want to remove the leading and trailing " characters + if (value.size() > 2 && (value[0] == '"' && value[value.size() - 1] == '"')) { + // Removing the first and last " + success = true; + return value.substr(1, value.size() - 2); + } + success = false; return value; }, [](std::string& outValue, std::string inValue) -> bool { From e1dcfd4780dab2442e675e4b6613509effcfdc42 Mon Sep 17 00:00:00 2001 From: Michal Marcinkowski Date: Mon, 6 Jul 2015 14:38:04 -0400 Subject: [PATCH 304/329] Adding / changing a few keybinds --- data | 2 +- scripts/bind_keys.lua | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/data b/data index 8665ad57ba..acf3b46580 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 8665ad57baba608771bfb0233f4502b941f516a1 +Subproject commit acf3b4658081da227fa8dc244a8b1814e70a0c11 diff --git a/scripts/bind_keys.lua b/scripts/bind_keys.lua index dc1042a637..57e10e8aef 100644 --- a/scripts/bind_keys.lua +++ b/scripts/bind_keys.lua @@ -40,7 +40,7 @@ openspace.bindKey("n", "openspace.time.setTime('2014 SEP 14 17:55:00'); openspac openspace.bindKey("i", "local b = openspace.getPropertyValue('ImagePlaneRosetta.renderable.enabled'); openspace.setPropertyValue('ImagePlaneRosetta.renderable.enabled', not b)") --]] -openspace.bindKey("F8", "openspace.time.setTime('2015-07-14T09:00:00.00'); openspace.setPropertyValue('PlutoProjection.renderable.clearAllProjections', true); openspace.setPropertyValue('Charon.renderable.clearAllProjections', true);") +openspace.bindKey("F8", "openspace.setPropertyValue('PlutoProjection.renderable.clearAllProjections', true); openspace.setPropertyValue('Charon.renderable.clearAllProjections', true);") -- Quickfix backjumps in pluto sequence openspace.bindKey("F9", "openspace.time.setTime('2015-07-14T09:00:00.00'); openspace.setPropertyValue('PlutoProjection.renderable.clearAllProjections', true); openspace.setPropertyValue('Charon.renderable.clearAllProjections', true);") @@ -59,7 +59,7 @@ openspace.bindKey("x", "openspace.setOrigin('Europa')") openspace.bindKey("g", "openspace.time.setTime('2007-02-28T11:40:00.00'); openspace.time.setDeltaTime(1);") -openspace.bindKey("h", "openspace.time.setTime('2015-07-14T09:00:00.00'); openspace.time.setDeltaTime(1); openspace.changeCoordinateSystem('Pluto'); openspace.setOrigin('PlutoProjection'); openspace.printInfo('Changing Viewpoint to Pluto-in-center');") +openspace.bindKey("h", "openspace.setPropertyValue('PlutoProjection.renderable.performProjection', false); openspace.setPropertyValue('Charon.renderable.performProjection', false);openspace.time.setTime('2015-07-14T09:00:00.00'); openspace.time.setDeltaTime(1); openspace.changeCoordinateSystem('Pluto'); openspace.setOrigin('PlutoProjection'); openspace.printInfo('Changing Viewpoint to Pluto-in-center');") --[[ openspace.bindKey("1", "openspace.time.setTime('2007-02-27T16:40:00.00'); openspace.time.setDeltaTime(10)") @@ -107,6 +107,8 @@ openspace.bindKey("p", "local b = openspace.getPropertyValue('Io.renderable.perf openspace.bindKey("p", "local b = openspace.getPropertyValue('Ganymede.renderable.performProjection'); openspace.setPropertyValue('Ganymede.renderable.performProjection', not b)") openspace.bindKey("p", "local b = openspace.getPropertyValue('Europa.renderable.performProjection'); openspace.setPropertyValue('Europa.renderable.performProjection', not b)") openspace.bindKey("p", "local b = openspace.getPropertyValue('Callisto.renderable.performProjection'); openspace.setPropertyValue('Callisto.renderable.performProjection', not b)") +openspace.bindKey("p", "local b = openspace.getPropertyValue('PlutoProjection.renderable.performProjection'); openspace.setPropertyValue('PlutoProjection.renderable.performProjection', not b)") +openspace.bindKey("p", "local b = openspace.getPropertyValue('Charon.renderable.performProjection'); openspace.setPropertyValue('Charon.renderable.performProjection', not b)") openspace.bindKey("PAUSE", "openspace.printInfo('F5: Toogle to Sun, F6: Toggle to Jupiter, F7: Toggle to Pluto'); openspace.printInfo('Bookmarks: 1: Jupter, 2-7: Pluto');") From 682e84942371cacb0c109317eacfd1afb914e9f5 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 6 Jul 2015 21:46:48 +0200 Subject: [PATCH 305/329] Update to use new Ghoul version --- ext/ghoul | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/ghoul b/ext/ghoul index 310b4ece36..7c746d7af3 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 310b4ece3653b13e37f0d49cc31386545cf45737 +Subproject commit 7c746d7af3dde6524e9e1a28ddf392824682d2ed From 19540a6f215d7b9b88f4305c97392736031c98c5 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 6 Jul 2015 22:47:55 +0200 Subject: [PATCH 306/329] Fixing a memory bug in SpiceManager --- ext/ghoul | 2 +- src/scene/scenegraphnode.cpp | 6 ++---- src/util/spicemanager.cpp | 11 ++++++----- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index 7c746d7af3..a61f50a8eb 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 7c746d7af3dde6524e9e1a28ddf392824682d2ed +Subproject commit a61f50a8eb9c038f27f8ee1a4dba79889e525c15 diff --git a/src/scene/scenegraphnode.cpp b/src/scene/scenegraphnode.cpp index e4c9fd37b4..9866635c6a 100644 --- a/src/scene/scenegraphnode.cpp +++ b/src/scene/scenegraphnode.cpp @@ -154,10 +154,8 @@ bool SceneGraphNode::deinitialize() { _renderable = nullptr; } - if (_ephemeris) { - delete _ephemeris; - _ephemeris = nullptr; - } + delete _ephemeris; + _ephemeris = nullptr; for (SceneGraphNode* child : _children) { child->deinitialize(); diff --git a/src/util/spicemanager.cpp b/src/util/spicemanager.cpp index 5a806ea244..d1c3bd0e81 100644 --- a/src/util/spicemanager.cpp +++ b/src/util/spicemanager.cpp @@ -954,9 +954,10 @@ bool SpiceManager::getTerminatorEllipse(const int numberOfPoints, double ephemerisTime, double& targetEpoch, glm::dvec3& observerPosition, - std::vector& terminatorPoints){ - - double(*tpoints)[3] = new double[numberOfPoints][3]; + std::vector& terminatorPoints) +{ + std::vector> tpoints(numberOfPoints); +// double (*tpoints)[3] = new double[numberOfPoints][3]; edterm_c(terminatorType.c_str(), lightSource.c_str(), @@ -968,7 +969,7 @@ bool SpiceManager::getTerminatorEllipse(const int numberOfPoints, numberOfPoints, &targetEpoch, glm::value_ptr(observerPosition), - (double(*)[3])tpoints ); + (double(*)[3])tpoints.data() ); bool hasError = checkForError("Error getting " + terminatorType + "terminator for'" + target + "'"); @@ -980,7 +981,7 @@ bool SpiceManager::getTerminatorEllipse(const int numberOfPoints, point[3] += 3; terminatorPoints.push_back(point); } - + return true; } From bacb1a46f909252933664507ce7d5184b4944756 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 6 Jul 2015 23:42:04 +0200 Subject: [PATCH 307/329] Fixing one more memory leak that leaked a StaticEphemeris per ScenegraphNode --- src/scene/scenegraphnode.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/scene/scenegraphnode.cpp b/src/scene/scenegraphnode.cpp index 9866635c6a..40d3817230 100644 --- a/src/scene/scenegraphnode.cpp +++ b/src/scene/scenegraphnode.cpp @@ -90,6 +90,7 @@ SceneGraphNode* SceneGraphNode::createFromDictionary(const ghoul::Dictionary& di if (dictionary.hasKey(KeyEphemeris)) { ghoul::Dictionary ephemerisDictionary; dictionary.getValue(KeyEphemeris, ephemerisDictionary); + delete result->_ephemeris; result->_ephemeris = Ephemeris::createFromDictionary(ephemerisDictionary); if (result->_ephemeris == nullptr) { LERROR("Failed to create ephemeris for SceneGraphNode '" From 2a0787386e143fd93b8fba60367ef02cc76a7baa Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Tue, 7 Jul 2015 08:21:10 +0200 Subject: [PATCH 308/329] adding a forced time jump to trigger recomputation of trails when an init message is received --- include/openspace/network/parallelconnection.h | 2 ++ src/network/parallelconnection.cpp | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/include/openspace/network/parallelconnection.h b/include/openspace/network/parallelconnection.h index af5dc01842..4662c2c6f4 100644 --- a/include/openspace/network/parallelconnection.h +++ b/include/openspace/network/parallelconnection.h @@ -185,6 +185,8 @@ namespace openspace{ std::atomic _performDisconnect; std::atomic _isRunning; std::atomic _tryConnect; + std::atomic _initializationTimejumpRequired; + std::vector> _sendBuffer; std::mutex _sendBufferMutex; diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index 46c46dd978..ff8ed0c6cc 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -88,7 +88,8 @@ namespace openspace { _isConnected(false), _tryConnect(false), _performDisconnect(false), - _latestTimeKeyframeValid(false) + _latestTimeKeyframeValid(false), + _initializationTimejumpRequired(false) { //create handler thread @@ -467,6 +468,9 @@ namespace openspace { //let the server know queueMessage(buffer); + //we also need to force a time jump just to ensure that the server and client are synced + _initializationTimejumpRequired.store(true); + // int result; // uint16_t numScripts; // uint32_t datalen; @@ -630,6 +634,12 @@ namespace openspace { _latestTimeKeyframe._requiresTimeJump = tf._requiresTimeJump; } + //if we're just initialized we need to perform a time jump as soon as a valid keyframe has been received + if(_initializationTimejumpRequired.load() && _latestTimeKeyframeValid){ + _latestTimeKeyframe._requiresTimeJump = true; + _initializationTimejumpRequired.store(false); + } + //unlock mutex _timeKeyframeMutex.unlock(); From b8c7f82b4d9b917158e7fbbbb5eb27f0f8641adb Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 7 Jul 2015 10:53:06 +0200 Subject: [PATCH 309/329] Updated OpenSpace version to 0.1.2 Updated Ghoul version --- CMakeLists.txt | 2 +- ext/ghoul | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 42cc95085a..cc6fef174b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,7 +40,7 @@ include(${OPENSPACE_EXT_DIR}/ghoul/ext/CopySharedLibraries.cmake) test_compiler_compatibility() cleanup_project() set_build_output_directories() -configure_openspace_version(0 1 1 "prerelease-6") +configure_openspace_version(0 1 2 "prerelease-6") option(OPENSPACE_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF) option(OPENSPACE_DISABLE_EXTERNAL_WARNINGS "Disable warnings in external libraries" ON) diff --git a/ext/ghoul b/ext/ghoul index 3a095f1ce6..ba03caa1d5 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 3a095f1ce633e52b94877bde67e77775262e3dbd +Subproject commit ba03caa1d50a990de4ff695e2e0577cdaa1d67b5 From 9e06b612a71d0336fcd7247020a365065302d79b Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 7 Jul 2015 11:17:56 +0200 Subject: [PATCH 310/329] Remove the unused location declarations in the shader to make OpenSpace compatible with OpenGL 4.0 --- modules/base/shaders/path_fs.glsl | 9 ++++---- modules/base/shaders/path_vs.glsl | 16 ++++++-------- modules/base/shaders/star_fs.glsl | 12 +++++----- modules/base/shaders/star_ge.glsl | 22 +++++++++---------- modules/base/shaders/star_vs.glsl | 18 +++++++-------- .../newhorizons/shaders/crawlingline_vs.glsl | 2 +- modules/onscreengui/shaders/gui_fs.glsl | 6 ++--- modules/onscreengui/shaders/gui_vs.glsl | 10 ++++----- 8 files changed, 46 insertions(+), 49 deletions(-) diff --git a/modules/base/shaders/path_fs.glsl b/modules/base/shaders/path_fs.glsl index 60535c0aba..25550a137e 100644 --- a/modules/base/shaders/path_fs.glsl +++ b/modules/base/shaders/path_fs.glsl @@ -23,11 +23,10 @@ ****************************************************************************************/ #version __CONTEXT__ -layout(location = 0) in vec4 vs_point_position; -layout(location = 1) flat in int isHour; -layout(location = 2) in vec4 vs_point_color; -//in flat int isHour; -//uniform vec4 campos; +in vec4 vs_point_position; +flat in int isHour; +in vec4 vs_point_color; + uniform vec3 color; #include "ABuffer/abufferStruct.hglsl" diff --git a/modules/base/shaders/path_vs.glsl b/modules/base/shaders/path_vs.glsl index 06f37f3876..5ac609c9ac 100644 --- a/modules/base/shaders/path_vs.glsl +++ b/modules/base/shaders/path_vs.glsl @@ -24,6 +24,13 @@ #version __CONTEXT__ +in vec4 in_point_position; +uniform vec3 color; + +out vec4 vs_point_position; +flat out int isHour; +out vec4 vs_point_color; + uniform mat4 ViewProjection; uniform mat4 ModelTransform; uniform vec4 objectVelocity; @@ -49,15 +56,6 @@ float psc_distance(vec4 v1, vec4 v2) { return distance(v1.xyz, v2.xyz); } -layout(location = 0) in vec4 in_point_position; -//layout(location = 1) in vec4 in_point_color; -uniform vec3 color; -//uniform vec4 campos; - -layout(location = 0) out vec4 vs_point_position; -layout(location = 1) flat out int isHour; -layout(location = 2) out vec4 vs_point_color; - #include "PowerScaling/powerScaling_vs.hglsl" void main() { diff --git a/modules/base/shaders/star_fs.glsl b/modules/base/shaders/star_fs.glsl index efde60046d..d791a21b74 100644 --- a/modules/base/shaders/star_fs.glsl +++ b/modules/base/shaders/star_fs.glsl @@ -35,12 +35,12 @@ uniform float minBillboardSize; uniform int colorOption; -layout(location = 0) in vec4 vs_position; -layout(location = 1) in vec3 ge_brightness; -layout(location = 2) in vec3 ge_velocity; -layout(location = 3) in float ge_speed; -layout(location = 4) in vec2 texCoord; -layout(location = 5) in float billboardSize; +in vec4 vs_position; +in vec3 ge_brightness; +in vec3 ge_velocity; +in float ge_speed; +in vec2 texCoord; +in float billboardSize; #include "ABuffer/abufferStruct.hglsl" #include "ABuffer/abufferAddToBuffer.hglsl" diff --git a/modules/base/shaders/star_ge.glsl b/modules/base/shaders/star_ge.glsl index 80c87ca9db..252ca50a79 100644 --- a/modules/base/shaders/star_ge.glsl +++ b/modules/base/shaders/star_ge.glsl @@ -37,18 +37,18 @@ const vec2 corners[4] = vec2[4]( layout(points) in; layout(triangle_strip, max_vertices = 4) out; -layout(location = 0) in vec4 psc_position[]; -layout(location = 1) in vec3 vs_brightness[]; -layout(location = 2) in vec3 vs_velocity[]; -layout(location = 3) in float vs_speed[]; -layout(location = 4) in vec4 cam_position[]; +in vec4 psc_position[]; +in vec3 vs_brightness[]; +in vec3 vs_velocity[]; +in float vs_speed[]; +in vec4 cam_position[]; -layout(location = 0) out vec4 vs_position; -layout(location = 1) out vec3 ge_brightness; -layout(location = 2) out vec3 ge_velocity; -layout(location = 3) out float ge_speed; -layout(location = 4) out vec2 texCoord; -layout(location = 5) out float billboardSize; +out vec4 vs_position; +out vec3 ge_brightness; +out vec3 ge_velocity; +out float ge_speed; +out vec2 texCoord; +out float billboardSize; uniform mat4 projection; diff --git a/modules/base/shaders/star_vs.glsl b/modules/base/shaders/star_vs.glsl index efad98230f..5efe1055bf 100644 --- a/modules/base/shaders/star_vs.glsl +++ b/modules/base/shaders/star_vs.glsl @@ -28,16 +28,16 @@ uniform mat4 model; uniform mat4 view; uniform mat4 projection; -layout(location = 0) in vec4 in_position; -layout(location = 1) in vec3 in_brightness; -layout(location = 2) in vec3 in_velocity; -layout(location = 3) in float in_speed; +in vec4 in_position; +in vec3 in_brightness; +in vec3 in_velocity; +in float in_speed; -layout(location = 0) out vec4 psc_position; -layout(location = 1) out vec3 vs_brightness; -layout(location = 2) out vec3 vs_velocity; -layout(location = 3) out float vs_speed; -layout(location = 4) out vec4 cam_position; +out vec4 psc_position; +out vec3 vs_brightness; +out vec3 vs_velocity; +out float vs_speed; +out vec4 cam_position; #include "PowerScaling/powerScaling_vs.hglsl" diff --git a/modules/newhorizons/shaders/crawlingline_vs.glsl b/modules/newhorizons/shaders/crawlingline_vs.glsl index 3614f54cf5..28bd994737 100644 --- a/modules/newhorizons/shaders/crawlingline_vs.glsl +++ b/modules/newhorizons/shaders/crawlingline_vs.glsl @@ -42,7 +42,7 @@ void main() { vec4 tmp = in_position; int id = gl_VertexID; - vec3 black = { 0.f, 0.f, 0.f }; + vec3 black = vec3(0.0); if(id == targetId) vs_color.xyz = black; diff --git a/modules/onscreengui/shaders/gui_fs.glsl b/modules/onscreengui/shaders/gui_fs.glsl index 7652b78703..d37e8c2b4a 100644 --- a/modules/onscreengui/shaders/gui_fs.glsl +++ b/modules/onscreengui/shaders/gui_fs.glsl @@ -26,10 +26,10 @@ uniform sampler2D tex; -layout(location = 1) in vec2 out_uv; -layout(location = 2) in vec4 out_color; +in vec2 out_uv; +in vec4 out_color; -layout(location = 0) out vec4 FragColor; +out vec4 FragColor; void main() { FragColor = out_color * texture(tex, out_uv); diff --git a/modules/onscreengui/shaders/gui_vs.glsl b/modules/onscreengui/shaders/gui_vs.glsl index 12a084717a..f8119ce939 100644 --- a/modules/onscreengui/shaders/gui_vs.glsl +++ b/modules/onscreengui/shaders/gui_vs.glsl @@ -26,12 +26,12 @@ uniform mat4 ortho; -layout(location = 0) in vec2 in_position; -layout(location = 1) in vec2 in_uv; -layout(location = 2) in vec4 in_color; +in vec2 in_position; +in vec2 in_uv; +in vec4 in_color; -layout(location = 1) out vec2 out_uv; -layout(location = 2) out vec4 out_color; +out vec2 out_uv; +out vec4 out_color; void main() { out_uv = in_uv; From 9cdc9c30d27e979271c50fe11ce6bd29bd1aa3a3 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Tue, 7 Jul 2015 12:49:48 +0200 Subject: [PATCH 311/329] code cleanup and optimisation in the way threads are handled --- .../openspace/network/parallelconnection.h | 5 + src/network/parallelconnection.cpp | 236 ++++-------------- 2 files changed, 56 insertions(+), 185 deletions(-) diff --git a/include/openspace/network/parallelconnection.h b/include/openspace/network/parallelconnection.h index 4662c2c6f4..0095503583 100644 --- a/include/openspace/network/parallelconnection.h +++ b/include/openspace/network/parallelconnection.h @@ -41,6 +41,7 @@ #include #include #include +#include #ifdef __WIN32__ #ifndef WIN32_LEAN_AND_MEAN @@ -186,9 +187,13 @@ namespace openspace{ std::atomic _isRunning; std::atomic _tryConnect; std::atomic _initializationTimejumpRequired; + + std::condition_variable _disconnectCondition; + std::mutex _disconnectMutex; std::vector> _sendBuffer; std::mutex _sendBufferMutex; + std::condition_variable _sendCondition; network::datamessagestructures::TimeKeyframe _latestTimeKeyframe; std::mutex _timeKeyframeMutex; diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index ff8ed0c6cc..044ee1c747 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -90,7 +90,6 @@ namespace openspace { _performDisconnect(false), _latestTimeKeyframeValid(false), _initializationTimejumpRequired(false) - { //create handler thread _handlerThread = new (std::nothrow) std::thread(&ParallelConnection::threadManagement, this); @@ -112,17 +111,25 @@ namespace openspace { } void ParallelConnection::threadManagement(){ - //while program is running + //while we're still running while(_isRunning.load()){ - //if we need to disconnect and clean up - if(_performDisconnect.load()){ + { + //lock disconnect mutex mutex + //not really needed since no data is modified but conditions need a mutex + std::unique_lock unqlock(_disconnectMutex); + //wait for a signal to disconnect + _disconnectCondition.wait(unqlock); + + //perform actual disconnect disconnect(); + } } } void ParallelConnection::signalDisconnect(){ - _performDisconnect.store(true); + //signal handler thread to disconnect + _disconnectCondition.notify_all(); } void ParallelConnection::closeSocket(){ @@ -167,6 +174,9 @@ namespace openspace { //tell broadcast thread to stop broadcasting (we're no longer host) _isHost.store(false); + //signal send thread to stop waiting and finish current run + _sendCondition.notify_all(); + //join connection thread and delete it if(_connectionThread != nullptr){ _connectionThread->join(); @@ -307,18 +317,17 @@ namespace openspace { //we're connected _isConnected.store(true); + //start sending messages + _sendThread = new (std::nothrow) std::thread(&ParallelConnection::sendLoop, this); + + //start listening for communication + _listenThread = new (std::nothrow) std::thread(&ParallelConnection::listenCommunication, this); + //we no longer need to try to establish connection _tryConnect.store(false); - - //start listening for communication - _listenThread = new (std::nothrow) std::thread(&ParallelConnection::listenCommunication, this); - - //start sending messages - _sendThread = new (std::nothrow) std::thread(&ParallelConnection::sendLoop, this); - - //send authentication - sendAuthentication(); - + + //send authentication + sendAuthentication(); } #ifdef __WIN32__ @@ -372,18 +381,17 @@ namespace openspace { void ParallelConnection::delegateDecoding(uint32_t type){ switch (type){ case MessageTypes::Authentication: - //do nothing for now + //not used break; case MessageTypes::Initialization: initializationMessageReceived(); break; case MessageTypes::Data: dataMessageReceived(); + break; + case MessageTypes::Script: + //not used break; -// case MessageTypes::Script: -// //disabled for now -//// decodeScriptMessage(); -// break; case MessageTypes::HostInfo: hostInfoMessageReceived(); break; @@ -471,86 +479,6 @@ namespace openspace { //we also need to force a time jump just to ensure that the server and client are synced _initializationTimejumpRequired.store(true); -// int result; -// uint16_t numScripts; -// uint32_t datalen; -// uint32_t id; -// -// //create a buffer to hold the number of scripts in the initialization message -// std::vector buffer; -// buffer.resize(sizeof(id)); -// -// //read recepient ID (mine) -// result = receiveData(_clientSocket, buffer, sizeof(id), 0); -// if (result <= 0){ -// //error -// return; -// } -// id = *(reinterpret_cast(buffer.data())); -// -// //read data length -// result = receiveData(_clientSocket, buffer, sizeof(datalen), 0); -// if (result <= 0){ -// //error -// return; -// } -// datalen = *(reinterpret_cast(buffer.data())); -// -// buffer.clear(); -// buffer.resize(sizeof(numScripts)); -// //read number of scripts -// result = receiveData(_clientSocket, buffer, sizeof(numScripts), 0); -// if (result <= 0){ -// //error -// return; -// } -// numScripts = *(reinterpret_cast(buffer.data())); -// -// //declare placeholder for all received scripts -// std::vector initScripts; -// initScripts.reserve(numScripts); -// -// //length of each script and resize receiveing buffer -// uint16_t scriptlen; -// buffer.clear(); -// buffer.resize(sizeof(scriptlen)); -// -// //buffer for holding received scripts -// std::vector scriptbuffer; -// -// for(int n = 0; n < numScripts; ++n){ -// -// //read size in chars of next script -// result = receiveData(_clientSocket, buffer, sizeof(scriptlen), 0); -// -// if (result <= 0){ -// //error -// return; -// } -// -// //assign size of next script -// scriptlen = *reinterpret_cast(buffer.data()); -// -// //resize buffer -// scriptbuffer.clear(); -// scriptbuffer.resize(scriptlen); -// -// //read next script -// result = receiveData(_clientSocket, scriptbuffer, scriptlen, 0); -// -// if (result <= 0){ -// //error -// return; -// } -// -// //create a string from received chars in buffer -// std::string script; -// script.assign(scriptbuffer.begin(), scriptbuffer.end()); -// -// //que script with the script engine -// OsEng.scriptEngine()->queueScript(script); -// } - } void ParallelConnection::dataMessageReceived(){ @@ -666,34 +594,35 @@ namespace openspace { } void ParallelConnection::queueMessage(std::vector message){ - _sendBufferMutex.lock(); - _sendBuffer.push_back(message); - _sendBufferMutex.unlock(); + { + std::unique_lock unqlock(_sendBufferMutex); + _sendBuffer.push_back(message); + _sendCondition.notify_all(); + } } void ParallelConnection::sendLoop(){ - int result; + int result; + //while we're connected while(_isConnected.load()){ - _sendBufferMutex.lock(); - if(_sendBuffer.size() > 0){ + { + //wait for signal then lock mutex and send first queued message + std::unique_lock unqlock(_sendBufferMutex); + _sendCondition.wait_for(unqlock, std::chrono::milliseconds(500)); - //send first queued message - result = send(_clientSocket, _sendBuffer.front().data(), _sendBuffer.front().size(), 0); - - //remove the message that was just sent - _sendBuffer.erase(_sendBuffer.begin()); - - //make sure everything went well - if (result == SOCKET_ERROR){ - //failed to send message - LERROR("Failed to send message.\nError: " << _ERRNO << " detected in connection, disconnecting."); - - //stop all threads and signal that a disconnect should be performed - signalDisconnect(); - } - + if(!_sendBuffer.front().empty()){ + result = send(_clientSocket, _sendBuffer.front().data(), _sendBuffer.front().size(), 0); + _sendBuffer.erase(_sendBuffer.begin()); + } + } + //make sure everything went well + if (result == SOCKET_ERROR){ + //failed to send message + LERROR("Failed to send message.\nError: " << _ERRNO << " detected in connection, disconnecting."); + + //signal that a disconnect should be performed + signalDisconnect(); } - _sendBufferMutex.unlock(); } } @@ -833,69 +762,6 @@ namespace openspace { //queue message queueMessage(buffer); -// -// //construct init msg -// std::vector scripts = OsEng.scriptEngine()->cachedScripts(); -// -// -// uint16_t scriptlen; -// uint32_t totlen = 0; -// std::vector::const_iterator it; -// -// std::vector scriptMsg; -// -// //add a script of the current time to ensure all nodes are on the same page -// std::string timescript = "openspace.time.setTime(" + std::to_string(Time::ref().currentTime()) + ");"; -// scripts.push_back(timescript); -// -// //add a script of the current delta time to ensure all nodes are on the same page -// -// std::string dtscript = "openspace.time.setDeltaTime(" + std::to_string(Time::ref().deltaTime()) + ");"; -// scripts.push_back(dtscript); -// -// //add a terminating script letting the server know the client is fully initialized -// std::string donescript = "openspace.parallel.initialized();"; -// scripts.push_back(donescript); -// -// //total number of scripts -// uint16_t numScrips = static_cast(scripts.size()); -// -// //write all scripts -// for(it = scripts.cbegin(); -// it != scripts.cend(); -// ++it){ -// //write size of script in chars -// scriptlen = (*it).size(); -// scriptMsg.insert(scriptMsg.end(), reinterpret_cast(&scriptlen), reinterpret_cast(&scriptlen) + sizeof(scriptlen)); -// -// //write actual scripts -// scriptMsg.insert(scriptMsg.end(), (*it).begin(), (*it).end()); -// -// //add script length to total data length -// totlen += static_cast(scriptlen) + sizeof(scriptlen); -// } -// -// //clear buffer -// buffer.clear(); -// -// //write header -// writeHeader(buffer, MessageTypes::Initialization); -// -// //write requester ID -// buffer.insert(buffer.end(), reinterpret_cast(&requesterID), reinterpret_cast(&requesterID) + sizeof(requesterID)); -// -// -// //write size of data chunk -// buffer.insert(buffer.end(), reinterpret_cast(&totlen), reinterpret_cast(&totlen) + sizeof(totlen)); -// -// //write number of scripts -// buffer.insert(buffer.end(), reinterpret_cast(&numScrips), reinterpret_cast(&numScrips) + sizeof(numScrips)); -// -// //write all scripts and their lengths -// buffer.insert(buffer.end(), scriptMsg.begin(), scriptMsg.end()); -// -// //send initialization message -// queueMessage(buffer); } void ParallelConnection::listenCommunication(){ From 414e8d1182d0e3788f603cbe5b5c870e8740c832 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Tue, 7 Jul 2015 13:23:32 +0200 Subject: [PATCH 312/329] renaming function for sending minor bugfix --- src/network/parallelconnection.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index 044ee1c747..b18d3819ff 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -318,7 +318,7 @@ namespace openspace { _isConnected.store(true); //start sending messages - _sendThread = new (std::nothrow) std::thread(&ParallelConnection::sendLoop, this); + _sendThread = new (std::nothrow) std::thread(&ParallelConnection::sendFunc, this); //start listening for communication _listenThread = new (std::nothrow) std::thread(&ParallelConnection::listenCommunication, this); @@ -601,16 +601,16 @@ namespace openspace { } } - void ParallelConnection::sendLoop(){ + void ParallelConnection::sendFunc(){ int result; //while we're connected while(_isConnected.load()){ { //wait for signal then lock mutex and send first queued message std::unique_lock unqlock(_sendBufferMutex); - _sendCondition.wait_for(unqlock, std::chrono::milliseconds(500)); + _sendCondition.wait_for(unqlock, std::chrono::milliseconds(1500)); - if(!_sendBuffer.front().empty()){ + if(!_sendBuffer.empty()){ result = send(_clientSocket, _sendBuffer.front().data(), _sendBuffer.front().size(), 0); _sendBuffer.erase(_sendBuffer.begin()); } From 75b1e806edc438053418f1f95b747dbe80901f1d Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Tue, 7 Jul 2015 13:24:42 +0200 Subject: [PATCH 313/329] including the header file missing from last commit... --- include/openspace/network/parallelconnection.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openspace/network/parallelconnection.h b/include/openspace/network/parallelconnection.h index 0095503583..f13037a842 100644 --- a/include/openspace/network/parallelconnection.h +++ b/include/openspace/network/parallelconnection.h @@ -163,7 +163,7 @@ namespace openspace{ int receiveData(_SOCKET & socket, std::vector &buffer, int length, int flags); - void sendLoop(); + void sendFunc(); bool parseHints(addrinfo &info); From b75743a0bae0b2791ac0fd80d0f3f2cf7a6e3adc Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Tue, 7 Jul 2015 13:37:23 +0200 Subject: [PATCH 314/329] stability fix to ensure socket isn't shutdown when first message has not yet been queued --- src/network/parallelconnection.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index b18d3819ff..bdcdc658c0 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -608,21 +608,23 @@ namespace openspace { { //wait for signal then lock mutex and send first queued message std::unique_lock unqlock(_sendBufferMutex); - _sendCondition.wait_for(unqlock, std::chrono::milliseconds(1500)); + _sendCondition.wait_for(unqlock, std::chrono::milliseconds(500)); if(!_sendBuffer.empty()){ result = send(_clientSocket, _sendBuffer.front().data(), _sendBuffer.front().size(), 0); _sendBuffer.erase(_sendBuffer.begin()); + + //make sure everything went well + if (result == SOCKET_ERROR){ + //failed to send message + LERROR("Failed to send message.\nError: " << _ERRNO << " detected in connection, disconnecting."); + + //signal that a disconnect should be performed + signalDisconnect(); + } } } - //make sure everything went well - if (result == SOCKET_ERROR){ - //failed to send message - LERROR("Failed to send message.\nError: " << _ERRNO << " detected in connection, disconnecting."); - - //signal that a disconnect should be performed - signalDisconnect(); - } + } } From 326f42f2240fc2d2d323952736e6584b175c02d1 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Tue, 7 Jul 2015 14:05:09 +0200 Subject: [PATCH 315/329] changed it so the buffer is completely flushed if there are messages queued when send function is called --- src/network/parallelconnection.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index bdcdc658c0..87857de2d5 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -611,16 +611,18 @@ namespace openspace { _sendCondition.wait_for(unqlock, std::chrono::milliseconds(500)); if(!_sendBuffer.empty()){ - result = send(_clientSocket, _sendBuffer.front().data(), _sendBuffer.front().size(), 0); - _sendBuffer.erase(_sendBuffer.begin()); - - //make sure everything went well - if (result == SOCKET_ERROR){ - //failed to send message - LERROR("Failed to send message.\nError: " << _ERRNO << " detected in connection, disconnecting."); + while(!_sendBuffer.empty()){ + result = send(_clientSocket, _sendBuffer.front().data(), _sendBuffer.front().size(), 0); + _sendBuffer.erase(_sendBuffer.begin()); - //signal that a disconnect should be performed - signalDisconnect(); + //make sure everything went well + if (result == SOCKET_ERROR){ + //failed to send message + LERROR("Failed to send message.\nError: " << _ERRNO << " detected in connection, disconnecting."); + + //signal that a disconnect should be performed + signalDisconnect(); + } } } } From 405aa703522dceba227a7bc9e815d3ca603d866c Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 7 Jul 2015 14:48:20 +0200 Subject: [PATCH 316/329] Refix Devil vs FreeImage Fix the downloading detection of filenames in Launcher --- apps/Launcher/mainwindow.cpp | 2 +- ext/ghoul | 2 +- src/engine/downloadmanager.cpp | 8 +++++++- src/rendering/renderengine.cpp | 6 +++--- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/apps/Launcher/mainwindow.cpp b/apps/Launcher/mainwindow.cpp index 9d97a0ffe3..7d17454a89 100644 --- a/apps/Launcher/mainwindow.cpp +++ b/apps/Launcher/mainwindow.cpp @@ -191,7 +191,7 @@ void MainWindow::initialize() { ghoul::logging::LogManager::initialize(ghoul::logging::LogManager::LogLevel::Debug); LogMgr.addLog(new ghoul::logging::ConsoleLog); - LogMgr.addLog(new ghoul::logging::HTMLLog("LauncherLog.html")); + LogMgr.addLog(new ghoul::logging::HTMLLog("LauncherLog.html", false)); LogMgr.addLog(new QLog); std::string configurationFile = _configurationFile; diff --git a/ext/ghoul b/ext/ghoul index ba03caa1d5..d2485fb363 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit ba03caa1d50a990de4ff695e2e0577cdaa1d67b5 +Subproject commit d2485fb363247193601a63eda0a90ec086ee602d diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp index bd82114c05..fc48441157 100644 --- a/src/engine/downloadmanager.cpp +++ b/src/engine/downloadmanager.cpp @@ -226,14 +226,20 @@ std::vector DownloadManager::downloadRequestFiles( int nFinished = 0; while (std::getline(temporary, line)) { ++nFiles; +#ifdef __APPLE__ + // @TODO: Fix this so that the ifdef is not necessary anymore ---abock std::string file = ghoul::filesystem::File(line, true).filename(); +#else + std::string file = ghoul::filesystem::File(line).filename(); +#endif LDEBUG("\tLine: " << line << " ; Dest: " << destination.path() + "/" + file); FileFuture* future = DlManager.downloadFile( line, destination.path() + "/" + file, - overrideFiles + overrideFiles, + [](const FileFuture& f) { LDEBUG("Finished: " << f.filePath); } ); if (future) futures.push_back(future); diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 43992af703..f4a7d7b417 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -178,12 +178,12 @@ bool RenderEngine::initialize() { _mainCamera->setPosition(psc(0.f, 0.f, 1.499823f, 11.f)); OsEng.interactionHandler()->setCamera(_mainCamera); -#ifdef GHOUL_USE_FREEIMAGE - ghoul::io::TextureReader::ref().addReader(new ghoul::io::impl::TextureReaderFreeImage); -#endif // GHOUL_USE_FREEIMAGE #ifdef GHOUL_USE_DEVIL ghoul::io::TextureReader::ref().addReader(new ghoul::io::impl::TextureReaderDevIL); #endif // GHOUL_USE_DEVIL +#ifdef GHOUL_USE_FREEIMAGE + ghoul::io::TextureReader::ref().addReader(new ghoul::io::impl::TextureReaderFreeImage); +#endif // GHOUL_USE_FREEIMAGE ghoul::io::TextureReader::ref().addReader(new ghoul::io::impl::TextureReaderCMAP); From 2f801a417125ee3ef5ecb26522d88b2abc73397b Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Tue, 7 Jul 2015 15:29:56 +0200 Subject: [PATCH 317/329] adding a password check when changing hosts --- .../openspace/network/parallelconnection.h | 2 +- src/network/parallelconnection.cpp | 9 +++++-- src/network/parallelconnection_lua.inl | 25 +++++++++++++++---- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/include/openspace/network/parallelconnection.h b/include/openspace/network/parallelconnection.h index f13037a842..5297654c31 100644 --- a/include/openspace/network/parallelconnection.h +++ b/include/openspace/network/parallelconnection.h @@ -80,7 +80,7 @@ namespace openspace{ bool isHost(); - void requestHostship(); + void requestHostship(const std::string &password); void setPassword(const std::string &password); diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index 87857de2d5..26329206e7 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -855,13 +855,18 @@ namespace openspace { return _isHost.load(); } - void ParallelConnection::requestHostship(){ + void ParallelConnection::requestHostship(const std::string &password){ std::vector buffer; buffer.reserve(headerSize()); + uint32_t passcode = hash(password); + //write header writeHeader(buffer, MessageTypes::HostshipRequest); + //write passcode + buffer.insert(buffer.end(), reinterpret_cast(&passcode), reinterpret_cast(&passcode) + sizeof(uint32_t)); + //send message queueMessage(buffer); } @@ -1136,7 +1141,7 @@ namespace openspace { { "requestHostship", &luascriptfunctions::requestHostship, - "", + "string", "Request to be the host for this session" }, } diff --git a/src/network/parallelconnection_lua.inl b/src/network/parallelconnection_lua.inl index caff3a2f56..059129e20a 100644 --- a/src/network/parallelconnection_lua.inl +++ b/src/network/parallelconnection_lua.inl @@ -160,12 +160,27 @@ int disconnect(lua_State* L) { int requestHostship(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 0) - return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); - if(OsEng.isMaster()){ - OsEng.parallelConnection()->requestHostship(); + const bool isFunction = (lua_isfunction(L, -1) != 0); + if (isFunction) { + // If the top of the stack is a function, it is ourself + const char* msg = lua_pushfstring(L, "method called without argument"); + return luaL_error(L, "bad argument (%s)", msg); } + + const int type = lua_type(L, -1); + if (type == LUA_TSTRING) { + std::string pwd = luaL_checkstring(L, -1); + if(OsEng.isMaster()){ + OsEng.parallelConnection()->requestHostship(pwd); + } + return 0; + } + else { + const char* msg = lua_pushfstring(L, "%s expected, got %s", + lua_typename(L, LUA_TSTRING), luaL_typename(L, -1)); + return luaL_error(L, "bad argument #%d (%s)", 1, msg); + } + return 0; } From cf120a95ef9c2cf48e647979fd4879cf0fb636e5 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 7 Jul 2015 16:36:39 +0200 Subject: [PATCH 318/329] Updated to new Ghoul version --- ext/ghoul | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/ghoul b/ext/ghoul index d2485fb363..926b7c6b0d 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit d2485fb363247193601a63eda0a90ec086ee602d +Subproject commit 926b7c6b0d44ad72b7df8217ac7f65dfb6c59479 From 953e4c51610729f8d1432b234d4c1d0168bc505f Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 7 Jul 2015 16:54:16 +0200 Subject: [PATCH 319/329] Minor code change --- shaders/ABuffer/abufferStruct.hglsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shaders/ABuffer/abufferStruct.hglsl b/shaders/ABuffer/abufferStruct.hglsl index 88701823de..452cdbafb0 100644 --- a/shaders/ABuffer/abufferStruct.hglsl +++ b/shaders/ABuffer/abufferStruct.hglsl @@ -62,7 +62,7 @@ const uint mask_29 = 536870911; const uint mask_30 = 1073741823; const uint mask_31 = 2147483647; const uint mask_32 = 4294967295; -const uint mask_id = mask_16; +const uint mask_id = mask_16; const uint shift_id = 0; const uint mask_type = mask_24 - mask_16; const uint shift_type = 16; From 1882bdaea37c373997d54c3f13404e4bb9e6993c Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 7 Jul 2015 18:27:22 +0200 Subject: [PATCH 320/329] One more minor code change --- shaders/ABuffer/abufferStruct.hglsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shaders/ABuffer/abufferStruct.hglsl b/shaders/ABuffer/abufferStruct.hglsl index 452cdbafb0..bd016fd8d8 100644 --- a/shaders/ABuffer/abufferStruct.hglsl +++ b/shaders/ABuffer/abufferStruct.hglsl @@ -64,7 +64,7 @@ const uint mask_31 = 2147483647; const uint mask_32 = 4294967295; const uint mask_id = mask_16; const uint shift_id = 0; -const uint mask_type = mask_24 - mask_16; +const uint mask_type = mask_24 - mask_16; const uint shift_type = 16; /* const uint mask_zid_z = mask_24; From 52146a79a65da0970569808504127623fa6135f3 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 7 Jul 2015 18:52:09 +0200 Subject: [PATCH 321/329] Remove old script messages for changing the origin and the coordinate system Adapt the onscreen-gui to use the new property mechanism Adapt the Timeline GUI to use the new property mechanism --- apps/TimelineView/controlwidget.cpp | 11 +++--- apps/TimelineView/mainwindow.cpp | 6 +-- modules/onscreengui/src/gui.cpp | 6 +-- .../onscreengui/src/guiorigincomponent.cpp | 4 +- src/interaction/interactionhandler.cpp | 7 ---- src/rendering/renderengine.cpp | 38 ++++++++----------- 6 files changed, 28 insertions(+), 44 deletions(-) diff --git a/apps/TimelineView/controlwidget.cpp b/apps/TimelineView/controlwidget.cpp index cd1e57af8e..b082de3f13 100644 --- a/apps/TimelineView/controlwidget.cpp +++ b/apps/TimelineView/controlwidget.cpp @@ -253,8 +253,8 @@ void ControlWidget::onDateChange() { QString coordinateSystem = ImportantDates[index].coordinateSystem; QString script = "openspace.time.setTime('" + date + "');\ - openspace.setOrigin('" + focus + "');\ - openspace.changeCoordinateSystem('" + coordinateSystem + "');"; + openspace.setPropertyValue('Interaction.origin', '" + focus + "');\ + openspace.setPropertyValue('Interaction.coordinateSystem', '" + coordinateSystem + "')"; emit scriptActivity(script); } _setTime->blockSignals(true); @@ -266,7 +266,7 @@ void ControlWidget::onFocusChange() { int index = _focusNode->currentIndex(); QString name = FocusNodes[index].name; QString coordinateSystem = FocusNodes[index].coordinateSystem; - QString script = "openspace.setOrigin('" + name + "');openspace.changeCoordinateSystem('" + coordinateSystem + "');"; + QString script = "openspace.setPropertyValue('Interaction.origin', '" + name + "');openspace.setPropertyValue('Interaction.coordinateSystem', '" + coordinateSystem + "');"; emit scriptActivity(script); } @@ -277,7 +277,7 @@ void ControlWidget::onFocusToTargetButton() { if (it != std::end(FocusNodes)) { QString name = it->name; QString coordinateSystem = it->coordinateSystem; - QString script = "openspace.setOrigin('" + name + "');openspace.changeCoordinateSystem('" + coordinateSystem + "');"; + QString script = "openspace.setPropertyValue('Interaction.origin', '" + name + "');openspace.setPropertyValue('Interaction.coordinateSystem', '" + coordinateSystem + "');"; emit scriptActivity(script); } } @@ -293,8 +293,7 @@ void ControlWidget::onFocusToNewHorizonsButton() { else coordinateSystem = "Pluto"; - - QString script = "openspace.setOrigin('NewHorizons');openspace.changeCoordinateSystem('" + coordinateSystem + "');"; + QString script = "openspace.setPropertyValue('Interaction.origin', 'NewHorizons');openspace.setPropertyValue('Interaction.coordinateSystem', '" + coordinateSystem + "');"; emit scriptActivity(script); } diff --git a/apps/TimelineView/mainwindow.cpp b/apps/TimelineView/mainwindow.cpp index 6fdc04fc4e..bbf66f7e8c 100644 --- a/apps/TimelineView/mainwindow.cpp +++ b/apps/TimelineView/mainwindow.cpp @@ -140,9 +140,6 @@ void MainWindow::readTcpData() { QByteArray data = continuousData.append(_socket->readAll()); int d = data.size(); - if (data.size() != 42) - qDebug() << QString(data); - if (QString(data) == "Connected to SGCT!\r\n") { continuousData.clear(); return; @@ -152,6 +149,9 @@ void MainWindow::readTcpData() { return; } + if (data.size() != 42) + qDebug() << QString(data); + QByteArray messageTypeData = data.left(2); union { uint16_t value; diff --git a/modules/onscreengui/src/gui.cpp b/modules/onscreengui/src/gui.cpp index b5ba647efb..752881d563 100644 --- a/modules/onscreengui/src/gui.cpp +++ b/modules/onscreengui/src/gui.cpp @@ -381,11 +381,11 @@ void GUI::renderMainWindow() { bool toJupiter = ImGui::Button("Coordinate System to Jupiter"); if (toSun) - OsEng.scriptEngine()->queueScript("openspace.changeCoordinateSystem('Sun');"); + OsEng.scriptEngine()->queueScript("openspace.setPropertyValue('Interaction.coordinateSystem', 'Sun');"); if (toPluto) - OsEng.scriptEngine()->queueScript("openspace.changeCoordinateSystem('Pluto');"); + OsEng.scriptEngine()->queueScript("openspace.setPropertyValue('Interaction.coordinateSystem', 'Pluto');"); if (toJupiter) - OsEng.scriptEngine()->queueScript("openspace.changeCoordinateSystem('Jupiter');"); + OsEng.scriptEngine()->queueScript("openspace.setPropertyValue('Interaction.coordinateSystem', 'Jupiter');"); ImGui::Checkbox("Help", &_help._isEnabled); diff --git a/modules/onscreengui/src/guiorigincomponent.cpp b/modules/onscreengui/src/guiorigincomponent.cpp index 7322367890..e07a1c9aee 100644 --- a/modules/onscreengui/src/guiorigincomponent.cpp +++ b/modules/onscreengui/src/guiorigincomponent.cpp @@ -56,8 +56,8 @@ void GuiOriginComponent::render() { bool result = ImGui::Combo("Origin", &position, nodeNames.c_str()); if (result) { - LINFO("openspace.setOrigin('" + nodes[position]->name() + "');"); - OsEng.scriptEngine()->queueScript("openspace.setOrigin('" + nodes[position]->name() + "');"); + LINFO("openspace.setPropertyValue('Interaction.origin', '" + nodes[position]->name() + "');"); + OsEng.scriptEngine()->queueScript("openspace.setPropertyValue('Interaction.origin', '" + nodes[position]->name() + "');"); } } diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index f0b2de9445..f8f3b37c0d 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -894,13 +894,6 @@ scripting::ScriptEngine::LuaLibrary InteractionHandler::luaLibrary() { return { "", { - { - "setOrigin", - &luascriptfunctions::setOrigin, - "string", - "Set the camera origin node by name", - true - }, { "clearKeys", &luascriptfunctions::clearKeys, diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 906d479f55..be7f1e4e1f 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -788,29 +788,21 @@ scripting::ScriptEngine::LuaLibrary RenderEngine::luaLibrary() { "bool", "Sets the performance measurements" }, - // These are temporary ---abock -// { -// "changeCoordinateSystem", -// &luascriptfunctions::changeCoordinateSystem, -// "string", -// "Changes the origin of the coordinate system to the passed node", -// true -// }, - { - "fadeIn", - &luascriptfunctions::fadeIn, - "number", - "", - true - }, - //also temporary @JK - { - "fadeOut", - &luascriptfunctions::fadeOut, - "number", - "", - true - }, + { + "fadeIn", + &luascriptfunctions::fadeIn, + "number", + "", + true + }, + //also temporary @JK + { + "fadeOut", + &luascriptfunctions::fadeOut, + "number", + "", + true + }, }, }; } From ed91cdcf6053a9ec18097237ee32400f19373fc1 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 7 Jul 2015 20:31:40 +0200 Subject: [PATCH 322/329] Update version number --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cc6fef174b..fddfbac00d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,7 +40,7 @@ include(${OPENSPACE_EXT_DIR}/ghoul/ext/CopySharedLibraries.cmake) test_compiler_compatibility() cleanup_project() set_build_output_directories() -configure_openspace_version(0 1 2 "prerelease-6") +configure_openspace_version(0 2 0 "prerelease-6") option(OPENSPACE_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF) option(OPENSPACE_DISABLE_EXTERNAL_WARNINGS "Disable warnings in external libraries" ON) From 73c758d4d68eb54fb59f4657311a7a0769c3a352 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 7 Jul 2015 20:32:35 +0200 Subject: [PATCH 323/329] Update submodules --- data | 2 +- ext/ghoul | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data b/data index 8665ad57ba..acf3b46580 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 8665ad57baba608771bfb0233f4502b941f516a1 +Subproject commit acf3b4658081da227fa8dc244a8b1814e70a0c11 diff --git a/ext/ghoul b/ext/ghoul index a61f50a8eb..926b7c6b0d 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit a61f50a8eb9c038f27f8ee1a4dba79889e525c15 +Subproject commit 926b7c6b0d44ad72b7df8217ac7f65dfb6c59479 From 284989d48c6b6258199a3e06520e1c0c014066cc Mon Sep 17 00:00:00 2001 From: Michal Marcinkowski Date: Wed, 8 Jul 2015 00:14:01 -0400 Subject: [PATCH 324/329] Cleanup and refractoring of RenderableFov.cpp - Removed code causing memleaks - Improved MVIC intersection when no cornerpoints intersect - General readability and logical fixes (long overdue) - Fix so arbitrary amount of fov-bound-vectors can be loaded - Updated keybinds & data etc --- data | 2 +- ext/ghoul | 2 +- .../newhorizons/rendering/renderablefov.cpp | 541 ++++++++---------- modules/newhorizons/rendering/renderablefov.h | 74 +-- .../rendering/renderableplanetprojection.cpp | 51 +- scripts/bind_keys.lua | 25 +- scripts/default_startup.lua | 2 +- src/util/spicemanager.cpp | 25 +- 8 files changed, 318 insertions(+), 404 deletions(-) diff --git a/data b/data index acf3b46580..ddcba938fb 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit acf3b4658081da227fa8dc244a8b1814e70a0c11 +Subproject commit ddcba938fbda7ec5cc1e8d06be0fc47e199b0f4f diff --git a/ext/ghoul b/ext/ghoul index 926b7c6b0d..310b4ece36 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 926b7c6b0d44ad72b7df8217ac7f65dfb6c59479 +Subproject commit 310b4ece3653b13e37f0d49cc31386545cf45737 diff --git a/modules/newhorizons/rendering/renderablefov.cpp b/modules/newhorizons/rendering/renderablefov.cpp index fca2aba4f2..421a9d59dd 100644 --- a/modules/newhorizons/rendering/renderablefov.cpp +++ b/modules/newhorizons/rendering/renderablefov.cpp @@ -55,24 +55,17 @@ namespace { const std::string keyInstrumentMethod = "Instrument.Method"; const std::string keyInstrumentAberration = "Instrument.Aberration"; const std::string keyPotentialTargets = "PotentialTargets"; - - // colors, move later - glm::vec4 col_sq; - glm::vec4 c_project; - glm::vec4 col_end; - glm::vec4 blue; - glm::vec4 col_gray; - glm::vec4 col_start; } namespace openspace { RenderableFov::RenderableFov(const ghoul::Dictionary& dictionary) - : Renderable(dictionary) - , _lineWidth("lineWidth", "Line Width", 1.f, 1.f, 20.f) - , _drawSolid("solidDraw", "Draw as Quads", false) - , _programObject(nullptr) - , _texture(nullptr) + : Renderable(dictionary) + , _lineWidth("lineWidth", "Line Width", 1.f, 1.f, 20.f) + , _drawSolid("solidDraw", "Draw as Quads", false) + , _programObject(nullptr) + , _texture(nullptr) + , _drawFOV(false) , _mode(GL_LINES) { bool success = dictionary.getValue(keyBody, _spacecraft); @@ -106,40 +99,26 @@ RenderableFov::RenderableFov(const ghoul::Dictionary& dictionary) } void RenderableFov::allocateData() { - int points = 20; - _stride[0] = points; - _isize[0] = points; - _iarray1[0] = new int[_isize[0]]; - for (int i = 0; i < points; i++){ - for (int j = 0; j < 4; j++){ - _varray1.push_back(0); // pos - } - for (int j = 0; j < 4; j++){ - _varray1.push_back(0); // col - } - _iarray1[0][i] = i; + std::string shape, instrument; + // fetch data for specific instrument (shape, boresight, bounds etc) + bool found = openspace::SpiceManager::ref().getFieldOfView(_instrumentID, shape, instrument, _boresight, _bounds); + if (!found) { + LERROR("Could not locate instrument"); + return; } + _stride = 8; - _stride[0] = 8; - _vsize[0] = static_cast(_varray1.size()); - _vtotal[0] = static_cast(_vsize[0] / _stride[0]); - + _projectionBounds.resize(_bounds.size()); + int initBoundPoints = 2 * (_bounds.size() + 1); + _fovBounds.resize(initBoundPoints*_stride); + _vBoundsSize = static_cast(_fovBounds.size()); // allocate second vbo data - int cornerPoints = 12; - _isize[1] = cornerPoints; - _iarray1[1] = new int[_isize[1]]; - for (unsigned int i = 0; i < _isize[1]; i++){ - _iarray1[1][i] = i; - } - _varray2.resize(40); - _vsize[1] = 40; - _vtotal[1] = 5; - _isteps = 10; + _fovPlane.resize(40); + _vPlaneSize = 40; + _isteps = 10; // Interpolation steps per intersecting segment } RenderableFov::~RenderableFov() { - delete[] _iarray1[0]; - delete[] _iarray1[1]; deinitialize(); } @@ -152,10 +131,8 @@ bool RenderableFov::initialize() { if (!_programObject) return false; } - allocateData(); sendToGPU(); - return completeSuccess; } @@ -169,48 +146,67 @@ bool RenderableFov::isReady() const { void RenderableFov::sendToGPU() { // Initialize and upload to graphics card - glGenVertexArrays(1, &_vaoID[0]); - glGenBuffers(1, &_vboID[0]); - glGenBuffers(1, &_iboID[0]); - glBindVertexArray(_vaoID[0]); - glBindBuffer(GL_ARRAY_BUFFER, _vboID[0]); - glBufferData(GL_ARRAY_BUFFER, _vsize[0] * sizeof(GLfloat), NULL, GL_STATIC_DRAW); // orphaning the buffer, sending NULL data. - glBufferSubData(GL_ARRAY_BUFFER, 0, _vsize[0] * sizeof(GLfloat), &_varray1[0]); + // FOV lines + glGenVertexArrays(1, &_fovBoundsVAO); + glGenBuffers(1, &_fovBoundsVBO); + glBindVertexArray(_fovBoundsVAO); + glBindBuffer(GL_ARRAY_BUFFER, _fovBoundsVBO); + glBufferData(GL_ARRAY_BUFFER, _vBoundsSize * sizeof(GLfloat), NULL, GL_STATIC_DRAW); // orphaning the buffer, sending NULL data. + glBufferSubData(GL_ARRAY_BUFFER, 0, _vBoundsSize * sizeof(GLfloat), _fovBounds.data()); - GLsizei st = sizeof(GLfloat) * _stride[0]; + GLsizei st = sizeof(GLfloat) * _stride; glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, st, (void*)0); glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, st, (void*)(4 * sizeof(GLfloat))); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iboID[0]); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, _isize[0] * sizeof(int), _iarray1, GL_STATIC_DRAW); glBindVertexArray(0); - // second vbo - glGenVertexArrays(1, &_vaoID[1]); - glGenBuffers(1, &_vboID[1]); - glGenBuffers(1, &_iboID[1]); + // Orthogonal Plane + glGenVertexArrays(1, &_fovPlaneVAO); + glGenBuffers(1, &_fovPlaneVBO); - glBindVertexArray(_vaoID[1]); - glBindBuffer(GL_ARRAY_BUFFER, _vboID[1]); - glBufferData(GL_ARRAY_BUFFER, _vsize[1] * sizeof(GLfloat), NULL, GL_STATIC_DRAW); // orphaning the buffer, sending NULL data. - glBufferSubData(GL_ARRAY_BUFFER, 0, _vsize[1] * sizeof(GLfloat), &_varray2[0]); + glBindVertexArray(_fovPlaneVAO); + glBindBuffer(GL_ARRAY_BUFFER, _fovPlaneVBO); + glBufferData(GL_ARRAY_BUFFER, _vPlaneSize * sizeof(GLfloat), NULL, GL_STATIC_DRAW); // orphaning the buffer, sending NULL data. + glBufferSubData(GL_ARRAY_BUFFER, 0, _vPlaneSize * sizeof(GLfloat), _fovPlane.data()); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, st, (void*)0); glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, st, (void*)(4 * sizeof(GLfloat))); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iboID[1]); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, _isize[1] * sizeof(int), _iarray1[1], GL_STATIC_DRAW); glBindVertexArray(0); } -// various helper methods -void RenderableFov::insertPoint(std::vector& arr, psc p, glm::vec4 c) { +void RenderableFov::updateGPU() { + glBindBuffer(GL_ARRAY_BUFFER, _fovBoundsVBO); + glBufferSubData(GL_ARRAY_BUFFER, 0, _vBoundsSize * sizeof(GLfloat), _fovBounds.data()); + if (!_rebuild){ + // no new points + glBindBuffer(GL_ARRAY_BUFFER, _fovPlaneVBO); + glBufferSubData(GL_ARRAY_BUFFER, 0, _vPlaneSize * sizeof(GLfloat), _fovPlane.data()); + }else{ + // new points - memory change + glBindVertexArray(_fovPlaneVAO); + glBindBuffer(GL_ARRAY_BUFFER, _fovPlaneVBO); + glBufferData(GL_ARRAY_BUFFER, _vPlaneSize * sizeof(GLfloat), NULL, GL_STATIC_DRAW); // orphaning the buffer, sending NULL data. + glBufferSubData(GL_ARRAY_BUFFER, 0, _vPlaneSize * sizeof(GLfloat), _fovPlane.data()); + + GLsizei st = sizeof(GLfloat) * _stride; + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, st, (void*)0); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, st, (void*)(4 * sizeof(GLfloat))); + } + + glBindVertexArray(0); +} + +// various helper methods +void RenderableFov::insertPoint(std::vector& arr, glm::vec4 p, glm::vec4 c) { for (int i = 0; i < 4; i++){ arr.push_back(p[i]); } @@ -226,37 +222,24 @@ glm::dvec3 RenderableFov::interpolate(glm::dvec3 p0, glm::dvec3 p1, float t) { return glm::dvec3(p0.x*t2 + p1.x*t, p0.y*t2 + p1.y*t, p0.z*t2 + p1.z*t); } -glm::dvec3 RenderableFov::pscSlerp(glm::dvec3 p0, glm::dvec3 p1, float t){ - assert(t >= 0 && t <= 1); - float t2 = (1.f - t); - float omega = acosf(glm::dot(p0, p1)); - if (omega > 0.f){ - float s1 = sin(t*omega) / sin(omega); - float s2 = sin(t2*omega) / sin(omega); - return glm::dvec3(p0.x*s2 + p1.x*s1, p0.y*s2 + p1.y*s1, p0.z*s2 + p1.z*s1); - - } - return p0;//tmp -} // This method is the current bottleneck. psc RenderableFov::checkForIntercept(glm::dvec3 ray) { - double targetEt; bool intercepted = false; - openspace::SpiceManager::ref().getSurfaceIntercept(_fovTarget, _spacecraft, _instrumentID, - _frame, _method, _aberrationCorrection, - _time, targetEt, ray, ipoint, ivec, intercepted); - ivec *= 0.9999; + openspace::SpiceManager::ref().getSurfaceIntercept(_fovTarget, _spacecraft, _instrumentID, + _frame, _method, _aberrationCorrection, + _time, _targetEpoch, ray, ipoint, ivec, intercepted); + + ivec *= 0.9999;// because fov lands exactly on top of surface we need to move it out slightly _interceptVector = PowerScaledCoordinate::CreatePowerScaledCoordinate(ivec[0], ivec[1], ivec[2]); _interceptVector[3] += 3; return _interceptVector; } -// Orthogonal projection next to planets surface, can also be optimized. +// Orthogonal projection next to planets surface psc RenderableFov::orthogonalProjection(glm::dvec3 vecFov) { glm::dvec3 vecToTarget; - double lt; - SpiceManager::ref().getTargetPosition(_fovTarget, _spacecraft, _frame, _aberrationCorrection, _time, vecToTarget, lt); + SpiceManager::ref().getTargetPosition(_fovTarget, _spacecraft, _frame, _aberrationCorrection, _time, vecToTarget, _lt); openspace::SpiceManager::ref().frameConversion(vecFov, _instrumentID, _frame, _time); glm::dvec3 p = openspace::SpiceManager::ref().orthogonalProjection(vecToTarget, vecFov); @@ -268,12 +251,11 @@ psc RenderableFov::orthogonalProjection(glm::dvec3 vecFov) { // Bisection method, simple recurtion glm::dvec3 RenderableFov::bisection(glm::dvec3 p1, glm::dvec3 p2, double tolerance) { //check if point is on surface - double targetEt; glm::dvec3 half = interpolate(p1, p2, 0.5f); bool intercepted = false; - openspace::SpiceManager::ref().getSurfaceIntercept(_fovTarget, _spacecraft, _instrumentID, - _frame, _method, _aberrationCorrection, - _time, targetEt, half, ipoint, ivec, intercepted); + openspace::SpiceManager::ref().getSurfaceIntercept(_fovTarget, _spacecraft, _instrumentID, + _frame, _method, _aberrationCorrection, + _time, _targetEpoch, half, ipoint, ivec, intercepted); if (glm::distance(_previousHalf, half) < tolerance){ _previousHalf = glm::dvec3(0); return half; @@ -282,46 +264,56 @@ glm::dvec3 RenderableFov::bisection(glm::dvec3 p1, glm::dvec3 p2, double toleran //recursive search if (!intercepted){ return bisection(p1, half, tolerance); - }else{ + } + else{ return bisection(half, p2, tolerance); } } -/* - README: - There are 4 different cases as each boundary vector can either have detected - an intercept or is outside of the planets surface. When no such intercepts are - detected the algorithm performs an orthogonal projection to 'clip' the current - fov vector next to the planets surface. If two or more intercepts are detected - the algorithm continues with the bisection method O(logn) for points [Pn, Pn+1] - to locate the point Pb where the orthogonal plane meets the planets surface - (within ~20 iterations this will narrow down to centimeter resolution). - Upon finding Pb a linear interpolation is performed for [Pn, Pb], at this stage - the points are located on a straight line between the surface intercept and the - surface-bound fov-corner. In order to correctly place these points on the - targets surface, each consecutive point is queried for a surface intercept and - thereby moved to the hull. -*/ -void RenderableFov::fovProjection(bool H[], std::vector bounds) { - _nrInserted = 0; - _varray2.clear();// empty the array - double tolerance = 0.0000001; // very low tolerance factor - - glm::dvec3 mid; +void RenderableFov::fovSurfaceIntercept(bool H[], std::vector bounds) { + _nrInserted = 0; + _fovPlane.clear();// empty the array + + double tolerance = 0.000000001; // very low tolerance factor + + glm::dvec3 mid; glm::dvec3 interpolated; glm::dvec3 current; glm::dvec3 next; glm::vec4 tmp(1); - glm::vec4 test_col(0, 0, 1, 1); if (bounds.size() > 1){ for (int i = 0; i < bounds.size(); i++){ int k = (i + 1 > bounds.size() - 1) ? 0 : i + 1; current = bounds[i]; next = bounds[k]; + if (H[i] == false){ // If point is non-interceptive, project it. - insertPoint(_varray2, orthogonalProjection(current), tmp); + insertPoint(_fovPlane, orthogonalProjection(current).vec4(), tmp); + if (H[i + 1] == false && _withinFOV){ + // IFF incident point is also non-interceptive BUT something is within FOV + // we need then to check if this segment makes contact with surface + glm::dvec3 half = interpolate(current, next, 0.5f); + bool intercepted; + openspace::SpiceManager::ref().getSurfaceIntercept(_fovTarget, _spacecraft, _instrumentID, + _frame, _method, _aberrationCorrection, + _time, _targetEpoch, half, ipoint, ivec, intercepted); + if (intercepted){ + // find the two outer most points of intersection + glm::dvec3 root1 = bisection(half, current, tolerance); + glm::dvec3 root2 = bisection(half, next, tolerance); + + insertPoint(_fovPlane, orthogonalProjection(root1).vec4(), col_sq); + for (int j = 1; j < _isteps; j++){ + float t = (static_cast(j) / _isteps); + interpolated = interpolate(root1, root2, t); + _interceptVector = checkForIntercept(interpolated); + insertPoint(_fovPlane, _interceptVector.vec4(), col_sq); + } + insertPoint(_fovPlane, orthogonalProjection(root2).vec4(), col_sq); + } + } } if (H[i] == true && H[i + 1] == false){ // current point is interceptive, next is not // find outer most point for interpolation @@ -330,7 +322,7 @@ void RenderableFov::fovProjection(bool H[], std::vector bounds) { float t = (static_cast(j) / _isteps); interpolated = interpolate(current, mid, t); _interceptVector = (j < _isteps) ? checkForIntercept(interpolated) : orthogonalProjection(interpolated); - insertPoint(_varray2, _interceptVector, col_sq); + insertPoint(_fovPlane, _interceptVector.vec4(), col_sq); } } if (H[i] == false && H[i + 1] == true){ // current point is non-interceptive, next is @@ -339,7 +331,7 @@ void RenderableFov::fovProjection(bool H[], std::vector bounds) { float t = (static_cast(j) / _isteps); interpolated = interpolate(mid, next, t); _interceptVector = (j > 1) ? checkForIntercept(interpolated) : orthogonalProjection(interpolated); - insertPoint(_varray2, _interceptVector, col_sq); + insertPoint(_fovPlane, _interceptVector.vec4(), col_sq); } } if (H[i] == true && H[i + 1] == true){ // both points intercept @@ -347,7 +339,7 @@ void RenderableFov::fovProjection(bool H[], std::vector bounds) { float t = (static_cast(j) / _isteps); interpolated = interpolate(current, next, t); _interceptVector = checkForIntercept(interpolated); - insertPoint(_varray2, _interceptVector, col_sq); + insertPoint(_fovPlane, _interceptVector.vec4(), col_sq); } } } @@ -358,235 +350,152 @@ void RenderableFov::fovProjection(bool H[], std::vector bounds) { else { _rebuild = true; //update size etc; - _vtotal[1] = _nrInserted; - _isize[1] = _nrInserted; - _vsize[1] = static_cast(_varray2.size()); - _iarray1[1] = new int[_isize[1]]; - for (unsigned int i = 0; i < _isize[1]; i++) - _iarray1[1][i] = i; - } - -} - -void RenderableFov::updateData() { - glBindBuffer(GL_ARRAY_BUFFER, _vboID[0]); - glBufferSubData(GL_ARRAY_BUFFER, 0, _vsize[0] * sizeof(GLfloat), &_varray1[0]); - - if (!_rebuild){ - glBindBuffer(GL_ARRAY_BUFFER, _vboID[1]); - glBufferSubData(GL_ARRAY_BUFFER, 0, _vsize[1] * sizeof(GLfloat), &_varray2[0]); - }else{ - glBindVertexArray(_vaoID[1]); - glBindBuffer(GL_ARRAY_BUFFER, _vboID[1]); - glBufferData(GL_ARRAY_BUFFER, _vsize[1] * sizeof(GLfloat), NULL, GL_STATIC_DRAW); // orphaning the buffer, sending NULL data. - glBufferSubData(GL_ARRAY_BUFFER, 0, _vsize[1] * sizeof(GLfloat), &_varray2[0]); - - GLsizei st = sizeof(GLfloat) * _stride[0]; - - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, st, (void*)0); - glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, st, (void*)(4 * sizeof(GLfloat))); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iboID[1]); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, _isize[1] * sizeof(int), _iarray1[1], GL_STATIC_DRAW); - glBindVertexArray(0); + _vPlaneSize = static_cast(_fovPlane.size()); } } + +// This method is purely cosmetics, can very well be removed +// but be sure to set colors somewhere. void RenderableFov::computeColors() { - double t2 = openspace::ImageSequencer2::ref().getNextCaptureTime(); + double t2 = (openspace::ImageSequencer2::ref().getNextCaptureTime()); double diff = (t2 - _time); float t = 0.0; - if (diff <= 7.0) - t = static_cast(1.0 - (diff / 7.0)); + float interpolationStart = 7.0; //seconds before + if (diff <= interpolationStart) + t = static_cast(1.0 - (diff / interpolationStart)); - if (diff < 0.0) - t = 0.f; - // i need to add an *.h file with colortables.... - c_project = glm::vec4(0.0, 1.0, 0.00,1); - col_end = glm::vec4(1.00, 0.29, 0.00, 1); - blue = glm::vec4(0, 0.5, 0.7, 1); - col_gray = glm::vec4(0.7); - col_start = glm::vec4(1.00, 0.89, 0.00, 1); - col_sq = glm::vec4(1.00, 0.29, 0.00, 1); + if (diff < 0.0) t = 0.f; - col_end.x = c_project.x*t + col_end.x*(1 - t); - col_end.y = c_project.y*t + col_end.y*(1 - t); - col_end.z = c_project.z*t + col_end.z*(1 - t); + // This is a bit hardcoded - either we go for color tables + // or make these properties. + col_gray = glm::vec4(0.7); + col_project = glm::vec4(0.0, 1.0, 0.00, 1); + col_start = glm::vec4(1.00, 0.89, 0.00, 1); + col_end = glm::vec4(1.00, 0.29, 0.00, 1); + col_blue = glm::vec4(0, 0.5, 0.7, 1); + col_sq = glm::vec4(1.00, 0.29, 0.00, 1); + + col_end = col_project*t + col_end*(1 - t); + col_blue = col_project*t + col_blue*(1 - t); + col_sq = col_project*t + col_sq*(1 - t); + + float alpha; + alpha = _drawSolid ? 0.5f : 0.8f; - blue.x = c_project.x*t + blue.x*(1 - t); - blue.y = c_project.y*t + blue.y*(1 - t); - blue.z = c_project.z*t + blue.z*(1 - t); + col_blue.w = alpha; + col_project.w = alpha; + col_end.w = alpha; +} - col_sq.x = c_project.x*t + col_sq.x*(1 - t); - col_sq.y = c_project.y*t + col_sq.y*(1 - t); - col_sq.z = c_project.z*t + col_sq.z*(1 - t); +void RenderableFov::determineTarget(){ + _fovTarget = _potentialTargets[0]; //default; + for (int i = 0; i < _potentialTargets.size(); i++){ + bool success = openspace::SpiceManager::ref().targetWithinFieldOfView( + _instrumentID, + _potentialTargets[i], + _spacecraft, + _method, + _aberrationCorrection, + _time, + _withinFOV); + if (success && _withinFOV){ + _fovTarget = _potentialTargets[i]; + break; + } + } +} - blue.w = 0.5; - c_project.w = 0.5; - col_end.w = 0.5; +void RenderableFov::computeIntercepts(const RenderData& data){ + // for each FOV vector + _fovBounds.clear(); + for (int i = 0; i <= _bounds.size(); i++){ + int r = (i == _bounds.size()) ? 0 : i; + // compute surface intercept + openspace::SpiceManager::ref().getSurfaceIntercept(_fovTarget, _spacecraft, _instrumentID, + _frame, _method, _aberrationCorrection, + _time, _targetEpoch, _bounds[r], ipoint, ivec, _interceptTag[r]); + // if not found, use the orthogonal projected point + if (!_interceptTag[r]) _projectionBounds[r] = orthogonalProjection(_bounds[r]); + + glm::vec4 fovOrigin = glm::vec4(0); //This will have to be fixed once spacecraft is 1:1! + + if (_interceptTag[r]){ + _interceptVector = PowerScaledCoordinate::CreatePowerScaledCoordinate(ivec[0], ivec[1], ivec[2]); + _interceptVector[3] += 3; + // INTERCEPTIONS + insertPoint(_fovBounds, fovOrigin, col_start); + insertPoint(_fovBounds, _interceptVector.vec4(), col_end); + } + else if (_withinFOV){ + // OBJECT IN FOV, NO INTERCEPT FOR THIS FOV-RAY + insertPoint(_fovBounds, fovOrigin, glm::vec4(0, 0, 1, 1)); + insertPoint(_fovBounds, _projectionBounds[r].vec4(), col_blue); + } + else{ + glm::vec4 corner(_bounds[r][0], _bounds[r][1], _bounds[r][2], data.position[3] + 2); + corner = _spacecraftRotation*corner; + // NONE OF THE FOV-RAYS INTERCEPT AND NO OBJECT IN FOV + insertPoint(_fovBounds, fovOrigin, col_gray); + insertPoint(_fovBounds, corner, glm::vec4(0)); + } + } + _interceptTag[_bounds.size()] = _interceptTag[0]; + fovSurfaceIntercept(_interceptTag, _bounds); + + glm::vec3 aim = (_spacecraftRotation * glm::vec4(_boresight, 1)).xyz; + psc position; + SpiceManager::ref().getTargetPosition(_fovTarget, + _spacecraft, + _frame, + _aberrationCorrection, + _time, + position, + _lt); + pss length = position.length(); + if (length[0] < DBL_EPSILON) { + _drawFOV = false; + return; + } + //if aimed 80 deg away from target, dont draw white square + if (glm::dot(glm::normalize(aim), glm::normalize(position.vec3())) < 0.2){ + _drawFOV = false; + } } void RenderableFov::render(const RenderData& data) { assert(_programObject); _programObject->activate(); - // fetch data - glm::mat4 transform(1); - glm::mat4 spacecraftRot = glm::mat4(1); - for (int i = 0; i < 3; i++){ - for (int j = 0; j < 3; j++){ - spacecraftRot[i][j] = static_cast(_stateMatrix[i][j]); - } - } - bool drawFOV = false; - + _drawFOV = false; // setup the data to the shader _programObject->setUniform("ViewProjection", data.camera.viewProjectionMatrix()); - _programObject->setUniform("ModelTransform", transform); + _programObject->setUniform("ModelTransform", glm::mat4(1)); setPscUniforms(_programObject, &data.camera, data.position); - if (openspace::ImageSequencer2::ref().isReady()){ - drawFOV = ImageSequencer2::ref().instrumentActive(_instrumentID); - } + if (openspace::ImageSequencer2::ref().isReady()) + _drawFOV = ImageSequencer2::ref().instrumentActive(_instrumentID); - - if (drawFOV){ + if (_drawFOV){ // update only when time progresses. if (_oldTime != _time){ - std::string shape, instrument; - std::vector bounds; - glm::dvec3 boresight; - - // fetch data for specific instrument (shape, boresight, bounds etc) - bool found = openspace::SpiceManager::ref().getFieldOfView(_instrumentID, shape, instrument, boresight, bounds); - if (!found) { - LERROR("Could not locate instrument"); - return; - } - const unsigned int size = 4 * sizeof(float); - int indx = 0; - - _fovTarget = _potentialTargets[0]; //default - for (int i = 0; i < _potentialTargets.size(); i++){ - bool success = openspace::SpiceManager::ref().targetWithinFieldOfView( - _instrumentID, - _potentialTargets[i], - _spacecraft, - _method, - _aberrationCorrection, - _time, - _withinFOV); - if (success && _withinFOV){ - _fovTarget = _potentialTargets[i]; - break; - } - } - + determineTarget(); computeColors(); - - double targetEpoch; - // for each FOV vector - for (int i = 0; i <= bounds.size(); i++){ - int r = (i == bounds.size()) ? 0 : i; - - // compute surface intercept - openspace::SpiceManager::ref().getSurfaceIntercept(_fovTarget, _spacecraft, _instrumentID, - _frame, _method, _aberrationCorrection, - _time, targetEpoch, bounds[r], ipoint, ivec, _interceptTag[r]); - // if not found, use the orthogonal projected point - if (!_interceptTag[r]) _projectionBounds[r] = orthogonalProjection(bounds[r]); - - // VBO1 : draw vectors representing outer points of FOV. - if (_interceptTag[r]){ - _interceptVector = PowerScaledCoordinate::CreatePowerScaledCoordinate(ivec[0], ivec[1], ivec[2]); - _interceptVector[3] += 3; - // INTERCEPTIONS - memcpy(&_varray1[indx], glm::value_ptr(glm::vec4(0)), size); - indx += 4; - memcpy(&_varray1[indx], glm::value_ptr(col_start), size); - indx += 4; - memcpy(&_varray1[indx], glm::value_ptr(_interceptVector.vec4()), size); - indx += 4; - memcpy(&_varray1[indx], glm::value_ptr(col_end), size); - indx += 4; - } - else if (_withinFOV){ - // FOV OUTSIDE OBJECT - memcpy(&_varray1[indx], glm::value_ptr(glm::vec4(0)), size); - indx += 4; - memcpy(&_varray1[indx], glm::value_ptr(glm::vec4(0, 0, 1, 1)), size); - indx += 4; - memcpy(&_varray1[indx], glm::value_ptr(_projectionBounds[r].vec4()), size); - indx += 4; - memcpy(&_varray1[indx], glm::value_ptr(blue), size); - indx += 4; - } - else{ - glm::vec4 corner(bounds[r][0], bounds[r][1], bounds[r][2], data.position[3] + 2); - corner = spacecraftRot*corner; - // "INFINITE" FOV - memcpy(&_varray1[indx], glm::value_ptr(glm::vec4(0)), size); - indx += 4; - memcpy(&_varray1[indx], glm::value_ptr(col_gray), size); - indx += 4; - memcpy(&_varray1[indx], glm::value_ptr(corner), size); - indx += 4; - memcpy(&_varray1[indx], glm::value_ptr(glm::vec4(0)), size); - indx += 4; - } - } - - _interceptTag[bounds.size()] = _interceptTag[0]; - - - if (!(_instrumentID == "NH_LORRI") && !(_instrumentID == "ROS_NAVCAM-A")) // image plane replaces fov square - fovProjection(_interceptTag, bounds); - - updateData(); - glm::vec3 aim = (spacecraftRot * glm::vec4(boresight, 1)).xyz; - psc position; - double lt; - SpiceManager::ref().getTargetPosition(_fovTarget, - _spacecraft, - _frame, - _aberrationCorrection, - _time, - position, - lt); - pss length = position.length(); - if (length[0] < DBL_EPSILON) { - drawFOV = false; - return; - } - //if aimed 80 deg away from target, dont draw white square - if (glm::dot(glm::normalize(aim), glm::normalize(position.vec3())) < 0.2){ - drawFOV = false; - } - + computeIntercepts(data); + updateGPU(); } _oldTime = _time; - - if (!_drawSolid) _mode = GL_LINES; - else _mode = GL_TRIANGLE_STRIP; + _mode = _drawSolid ? GL_TRIANGLE_STRIP : GL_LINES; glLineWidth(_lineWidth); - glBindVertexArray(_vaoID[0]); - glDrawArrays(_mode, 0, _vtotal[0]); + glBindVertexArray(_fovBoundsVAO); + glDrawArrays(_mode, 0, static_cast(_vBoundsSize / _stride)); glBindVertexArray(0); - if (drawFOV){ + if (_drawFOV){ glLineWidth(2.f); - glBindVertexArray(_vaoID[1]); - glDrawArrays(GL_LINE_LOOP, 0, _vtotal[1]); + glBindVertexArray(_fovPlaneVAO); + glDrawArrays(GL_LINE_LOOP, 0, static_cast(_vPlaneSize / _stride)); glBindVertexArray(0); - - //trying to improve interpolation altorithm @mm - //glPointSize(5.f); - //glBindVertexArray(_vaoID[1]); - //glDrawArrays(GL_POINTS, 0, _vtotal[1]); - //glBindVertexArray(0); - //glPointSize(1.f); } glLineWidth(1.f); } @@ -596,6 +505,12 @@ void RenderableFov::render(const RenderData& data) { void RenderableFov::update(const UpdateData& data) { _time = data.time; openspace::SpiceManager::ref().getPositionTransformMatrix(_instrumentID, _frame, data.time, _stateMatrix); + _spacecraftRotation = glm::mat4(1); + for (int i = 0; i < 3; i++){ + for (int j = 0; j < 3; j++){ + _spacecraftRotation[i][j] = static_cast(_stateMatrix[i][j]); + } + } } } // namespace openspace diff --git a/modules/newhorizons/rendering/renderablefov.h b/modules/newhorizons/rendering/renderablefov.h index 682aae7f36..5d16737c22 100644 --- a/modules/newhorizons/rendering/renderablefov.h +++ b/modules/newhorizons/rendering/renderablefov.h @@ -52,37 +52,44 @@ public: void update(const UpdateData& data) override; private: + // properties properties::FloatProperty _lineWidth; properties::BoolProperty _drawSolid; ghoul::opengl::ProgramObject* _programObject; ghoul::opengl::Texture* _texture; openspace::SceneGraphNode* _targetNode; + // class methods void loadTexture(); void allocateData(); - void insertPoint(std::vector& arr, psc p, glm::vec4 c); - void fovProjection(bool H[], std::vector bounds); + void insertPoint(std::vector& arr, glm::vec4 p, glm::vec4 c); + void fovSurfaceIntercept(bool H[], std::vector bounds); + void determineTarget(); + void updateGPU(); + void sendToGPU(); + + // helper methods + void computeColors(); + void computeIntercepts(const RenderData& data); psc orthogonalProjection(glm::dvec3 camvec); psc checkForIntercept(glm::dvec3 ray); psc pscInterpolate(psc p0, psc p1, float t); - psc sphericalInterpolate(glm::dvec3 p0, glm::dvec3 p1, float t); - glm::dvec3 interpolate(glm::dvec3 p0, glm::dvec3 p1, float t); - glm::dvec3 pscSlerp(glm::dvec3 p0, glm::dvec3 p1, float t); glm::dvec3 bisection(glm::dvec3 p1, glm::dvec3 p2, double tolerance); - void computeColors(); - // instance variables int _nrInserted = 0; int _isteps; bool _rebuild = false; - bool _interceptTag[9]; + bool _interceptTag[8]; bool _withinFOV; - psc _projectionBounds[8]; + std::vector _projectionBounds; psc _interceptVector; + std::vector _fovBounds; + std::vector _fovPlane; + // spice std::string _spacecraft; std::string _observer; @@ -91,38 +98,39 @@ public: std::string _method; std::string _aberrationCorrection; std::string _fovTarget; - std::vector _potentialTargets; - glm::dvec3 ipoint, ivec; glm::dvec3 _previousHalf; - glm::vec3 _c; - double _r, _g, _b; - - // GPU stuff - GLuint _vaoID[2] ; - GLuint _vboID[2] ; - GLuint _iboID[2]; - GLenum _mode; - unsigned int _isize[2]; - unsigned int _vsize[2]; - unsigned int _vtotal[2]; - unsigned int _stride[2]; - std::vector _varray1; - std::vector _varray2; - int* _iarray1[2]; - - void updateData(); - void sendToGPU(); - + glm::dvec3 _boresight; glm::dmat3 _stateMatrix; + glm::mat4 _spacecraftRotation; + std::vector _bounds; + std::vector _potentialTargets; + bool _drawFOV; + double _targetEpoch; + double _lt; + + // GPU + GLuint _fovBoundsVAO; + GLuint _fovBoundsVBO; + unsigned int _vBoundsSize; + GLuint _fovPlaneVAO; + GLuint _fovPlaneVBO; + unsigned int _vPlaneSize; + GLenum _mode; + unsigned int _stride; // time double _time = 0; double _oldTime = 0; - // capturetime - double _timeInterval; - double _nextCaptureTime; + // colors + glm::vec4 col_sq; // orthogonal white square + glm::vec4 col_project; // color when projections occur + glm::vec4 col_start; // intersection start color + glm::vec4 col_end; // intersection end color + glm::vec4 col_blue; // withinFov color + glm::vec4 col_gray; // no intersection color + }; } #endif diff --git a/modules/newhorizons/rendering/renderableplanetprojection.cpp b/modules/newhorizons/rendering/renderableplanetprojection.cpp index a0a44ed640..4c9cb3018e 100644 --- a/modules/newhorizons/rendering/renderableplanetprojection.cpp +++ b/modules/newhorizons/rendering/renderableplanetprojection.cpp @@ -96,6 +96,7 @@ RenderablePlanetProjection::RenderablePlanetProjection(const ghoul::Dictionary& , _textureProj(nullptr) , _textureWhiteSquare(nullptr) , _geometry(nullptr) + , _capture(false) , _clearingImage(absPath("${OPENSPACE_DATA}/scene/common/textures/clear.png")) { std::string name; @@ -459,28 +460,28 @@ void RenderablePlanetProjection::project(){ // Comment out if not using queue and prefer old method ------------- // + in update() function - if (!imageQueue.empty()){ - Image& img = imageQueue.front(); - RenderablePlanetProjection::attitudeParameters(img.startTime); - // if image has new path - ie actual image, NOT placeholder - if (_projectionTexturePath.value() != img.path){ - // rebind and upload - _projectionTexturePath = img.path; - } - imageProjectGPU(); // fbopass - imageQueue.pop(); - } + //if (!imageQueue.empty()){ + // Image& img = imageQueue.front(); + // RenderablePlanetProjection::attitudeParameters(img.startTime); + // // if image has new path - ie actual image, NOT placeholder + // if (_projectionTexturePath.value() != img.path){ + // // rebind and upload + // _projectionTexturePath = img.path; + // } + // imageProjectGPU(); // fbopass + // imageQueue.pop(); + //} // ------------------------------------------------------------------ //---- Old method --- // // @mm - //for (auto const &img : _imageTimes){ - // RenderablePlanetProjection::attitudeParameters(img.startTime); - // if (_projectionTexturePath.value() != img.path){ - // _projectionTexturePath = img.path; // path to current images - // } - // imageProjectGPU(); // fbopass - //} + for (auto const &img : _imageTimes){ + RenderablePlanetProjection::attitudeParameters(img.startTime); + if (_projectionTexturePath.value() != img.path){ + _projectionTexturePath = img.path; // path to current images + } + imageProjectGPU(); // fbopass + } _capture = false; } @@ -531,13 +532,13 @@ void RenderablePlanetProjection::render(const RenderData& data){ } void RenderablePlanetProjection::update(const UpdateData& data){ - if (_time > Time::ref().currentTime()){ + if (_time >= Time::ref().currentTime()){ imageQueue = {}; // if jump back in time -> empty queue. } _time = Time::ref().currentTime(); _capture = false; - + if (openspace::ImageSequencer2::ref().isReady() && _performProjection){ openspace::ImageSequencer2::ref().updateSequencer(_time); _capture = openspace::ImageSequencer2::ref().getImagePaths(_imageTimes, _projecteeID, _instrumentID); @@ -545,11 +546,11 @@ void RenderablePlanetProjection::update(const UpdateData& data){ // remove these lines if not using queue ------------------------ // @mm - _capture = true; - for (auto img : _imageTimes){ - imageQueue.push(img); - } - _imageTimes.clear(); + //_capture = true; + //for (auto img : _imageTimes){ + // imageQueue.push(img); + //} + //_imageTimes.clear(); // -------------------------------------------------------------- if (_programObject->isDirty()) diff --git a/scripts/bind_keys.lua b/scripts/bind_keys.lua index 57e10e8aef..4d11c322bb 100644 --- a/scripts/bind_keys.lua +++ b/scripts/bind_keys.lua @@ -17,12 +17,12 @@ openspace.bindKey("SPACE", "openspace.time.togglePause()") openspace.bindKey("1", "openspace.time.setDeltaTime(1)") openspace.bindKey("2", "openspace.time.setDeltaTime(5)") openspace.bindKey("3", "openspace.time.setDeltaTime(10)") -openspace.bindKey("4", "openspace.time.setDeltaTime(30)") -openspace.bindKey("5", "openspace.time.setDeltaTime(60)") -openspace.bindKey("6", "openspace.time.setDeltaTime(120)") -openspace.bindKey("7", "openspace.time.setDeltaTime(360)") -openspace.bindKey("8", "openspace.time.setDeltaTime(540)") -openspace.bindKey("9", "openspace.time.setDeltaTime(720)") +openspace.bindKey("4", "openspace.time.setDeltaTime(20)") +openspace.bindKey("5", "openspace.time.setDeltaTime(40)") +openspace.bindKey("6", "openspace.time.setDeltaTime(60)") +openspace.bindKey("7", "openspace.time.setDeltaTime(120)") +openspace.bindKey("8", "openspace.time.setDeltaTime(360)") +openspace.bindKey("9", "openspace.time.setDeltaTime(540)") --[[openspace.bindKey("2", "openspace.time.setDeltaTime(30)") openspace.bindKey("3", "openspace.time.setDeltaTime(180)") -- 3m @@ -59,17 +59,7 @@ openspace.bindKey("x", "openspace.setOrigin('Europa')") openspace.bindKey("g", "openspace.time.setTime('2007-02-28T11:40:00.00'); openspace.time.setDeltaTime(1);") -openspace.bindKey("h", "openspace.setPropertyValue('PlutoProjection.renderable.performProjection', false); openspace.setPropertyValue('Charon.renderable.performProjection', false);openspace.time.setTime('2015-07-14T09:00:00.00'); openspace.time.setDeltaTime(1); openspace.changeCoordinateSystem('Pluto'); openspace.setOrigin('PlutoProjection'); openspace.printInfo('Changing Viewpoint to Pluto-in-center');") - ---[[ -openspace.bindKey("1", "openspace.time.setTime('2007-02-27T16:40:00.00'); openspace.time.setDeltaTime(10)") -openspace.bindKey("2", "openspace.time.setTime('2015-07-14T10:50:00.00'); openspace.time.setDeltaTime(10)") -openspace.bindKey("3", "openspace.time.setTime('2015-07-14T11:22:00.00'); openspace.time.setDeltaTime(1)") -openspace.bindKey("4", "openspace.time.setTime('2015-07-14T11:36:40.00'); openspace.time.setDeltaTime(1)") -openspace.bindKey("5", "openspace.time.setTime('2015-07-14T11:48:43.00'); openspace.time.setDeltaTime(1)") -openspace.bindKey("6", "openspace.time.setTime('2015-07-14T12:04:35.00'); openspace.time.setDeltaTime(1)") -openspace.bindKey("7", "openspace.time.setTime('2015-07-14T15:02:46.00'); openspace.time.setDeltaTime(100)") -]]-- +openspace.bindKey("h", "openspace.time.setTime('2015-07-14T10:00:00.00'); openspace.time.setDeltaTime(1); openspace.changeCoordinateSystem('Pluto'); openspace.setOrigin('PlutoProjection'); openspace.printInfo('Changing Viewpoint to Pluto-in-center');") openspace.bindKey("i", "local b = openspace.getPropertyValue('PlutoTexture.renderable.enabled'); openspace.setPropertyValue('PlutoTexture.renderable.enabled', not b)") @@ -85,7 +75,6 @@ openspace.bindKey("k", "local b = openspace.getPropertyValue('KerberosText.rende openspace.bindKey("k", "local b = openspace.getPropertyValue('StyxText.renderable.enabled'); openspace.setPropertyValue('StyxText.renderable.enabled', not b)") openspace.bindKey("j", "local b = openspace.getPropertyValue('PlutoText.renderable.enabled'); openspace.setPropertyValue('PlutoText.renderable.enabled', not b)") - openspace.bindKey("l", "local b = openspace.getPropertyValue('Labels.renderable.performFading'); openspace.setPropertyValue('Labels.renderable.performFading', not b)") openspace.bindKey("m", "local b = openspace.getPropertyValue('NH_LORRI.renderable.solidDraw'); openspace.setPropertyValue('NH_LORRI.renderable.solidDraw', not b)") diff --git a/scripts/default_startup.lua b/scripts/default_startup.lua index 6b7cbadcf2..a6ce0fb9cd 100644 --- a/scripts/default_startup.lua +++ b/scripts/default_startup.lua @@ -13,7 +13,7 @@ openspace.time.setTime("2007 FEB 27 16:30:00") -- This is the start tim --TESTING ALICE --openspace.time.setTime("2015-07-13T00:00:00.00") ---openspace.time.setTime("2015-07-14T08:00:00.00") +--openspace.time.setTime("2015-07-14T10:00:00.00") openspace.time.setDeltaTime(0) -- How many seconds pass per second of realtime, changeable in the GUI diff --git a/src/util/spicemanager.cpp b/src/util/spicemanager.cpp index 5a806ea244..c5adc80a23 100644 --- a/src/util/spicemanager.cpp +++ b/src/util/spicemanager.cpp @@ -956,19 +956,20 @@ bool SpiceManager::getTerminatorEllipse(const int numberOfPoints, glm::dvec3& observerPosition, std::vector& terminatorPoints){ - double(*tpoints)[3] = new double[numberOfPoints][3]; + std::vector> tpoints(numberOfPoints); + // double (*tpoints)[3] = new double[numberOfPoints][3]; - edterm_c(terminatorType.c_str(), - lightSource.c_str(), - target.c_str(), - ephemerisTime, - frame.c_str(), - aberrationCorrection.c_str(), - observer.c_str(), - numberOfPoints, - &targetEpoch, - glm::value_ptr(observerPosition), - (double(*)[3])tpoints ); + edterm_c(terminatorType.c_str(), + lightSource.c_str(), + target.c_str(), + ephemerisTime, + frame.c_str(), + aberrationCorrection.c_str(), + observer.c_str(), + numberOfPoints, + &targetEpoch, + glm::value_ptr(observerPosition), + (double(*)[3])tpoints.data()); bool hasError = checkForError("Error getting " + terminatorType + "terminator for'" + target + "'"); From 9ef34a6e5471f46085a093d870b9391026a07fbb Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 8 Jul 2015 15:54:42 +0200 Subject: [PATCH 325/329] Only define the masks when using the single linked lists Define uints using the proper constructors --- shaders/ABuffer/abufferStruct.hglsl | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/shaders/ABuffer/abufferStruct.hglsl b/shaders/ABuffer/abufferStruct.hglsl index bd016fd8d8..ce17dffd94 100644 --- a/shaders/ABuffer/abufferStruct.hglsl +++ b/shaders/ABuffer/abufferStruct.hglsl @@ -54,14 +54,15 @@ struct ABufferStruct_t { //======================================================================================== // Bitwise operations //======================================================================================== -const uint mask_1 = 1; -const uint mask_8 = 255; -const uint mask_16 = 65535; -const uint mask_24 = 16777215; -const uint mask_29 = 536870911; -const uint mask_30 = 1073741823; -const uint mask_31 = 2147483647; -const uint mask_32 = 4294967295; +#if ABUFFER_IMPLEMENTATION == ABUFFER_SINGLE_LINKED +const uint mask_1 = uint(1); +const uint mask_8 = uint(255); +const uint mask_16 = uint(65535); +const uint mask_24 = uint(16777215); +const uint mask_29 = uint(536870911); +const uint mask_30 = uint(1073741823); +const uint mask_31 = uint(2147483647); +const uint mask_32 = uint(4294967295); const uint mask_id = mask_16; const uint shift_id = 0; const uint mask_type = mask_24 - mask_16; @@ -81,6 +82,7 @@ const uint mask_id_next = mask_29; const uint shift_id_next = 0; const uint mask_id_type = mask_32 - mask_29; const uint shift_id_type = 29; +#endif void bitinsert_u(inout uint pack, uint val, uint mask, uint shift) { pack &= ~mask; From 2947a6f85ad0a53d98c561e47fb8a2680259bc28 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 8 Jul 2015 16:45:08 +0200 Subject: [PATCH 326/329] Introducing hack in ABuffer to make the backside of planetprojection not be transparent --- ext/ghoul | 2 +- shaders/ABuffer/abufferResolveFragment.glsl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index 310b4ece36..926b7c6b0d 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 310b4ece3653b13e37f0d49cc31386545cf45737 +Subproject commit 926b7c6b0d44ad72b7df8217ac7f65dfb6c59479 diff --git a/shaders/ABuffer/abufferResolveFragment.glsl b/shaders/ABuffer/abufferResolveFragment.glsl index dc6fedcb07..c03ef9039d 100644 --- a/shaders/ABuffer/abufferResolveFragment.glsl +++ b/shaders/ABuffer/abufferResolveFragment.glsl @@ -54,7 +54,7 @@ // constants const float stepSize = 0.01; const float samplingRate = 1.0; -uniform float ALPHA_LIMIT = 0.99; +uniform float ALPHA_LIMIT = 0.7; uniform float blackoutFactor = 0.0; From b6fe3a004a28d75930bb4206da83f988e9a7d40f Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 9 Jul 2015 11:36:31 +0200 Subject: [PATCH 327/329] Fixed bind_keys script --- scripts/bind_keys.lua | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/scripts/bind_keys.lua b/scripts/bind_keys.lua index 4d11c322bb..8d40c3df9e 100644 --- a/scripts/bind_keys.lua +++ b/scripts/bind_keys.lua @@ -5,9 +5,9 @@ openspace.clearKeys() openspace.bindKey("F1", "openspace.gui.toggle()") openspace.bindKey("F2", "openspace.setPerformanceMeasurement(true)") openspace.bindKey("F3", "openspace.setPerformanceMeasurement(false)") -openspace.bindKey("F5", "openspace.changeCoordinateSystem('Sun'); openspace.printInfo('Changing Viewpoint to Sun-in-center');"); -openspace.bindKey("F6", "openspace.changeCoordinateSystem('Jupiter'); openspace.printInfo('Changing Viewpoint to Jupiter-in-center');"); -openspace.bindKey("F7", "openspace.changeCoordinateSystem('Pluto'); openspace.printInfo('Changing Viewpoint to Pluto-in-center');"); +openspace.bindKey("F5", "openspace.setPropertyValue('Interaction.coordinateSystem', 'Sun'); openspace.printInfo('Changing Viewpoint to Sun-in-center');"); +openspace.bindKey("F6", "openspace.setPropertyValue('Interaction.coordinateSystem', 'Jupiter'); openspace.printInfo('Changing Viewpoint to Jupiter-in-center');"); +openspace.bindKey("F7", "openspace.setPropertyValue('Interaction.coordinateSystem', 'Pluto'); openspace.printInfo('Changing Viewpoint to Pluto-in-center');"); openspace.bindKey("PRINT_SCREEN", "openspace.takeScreenshot()") @@ -50,16 +50,16 @@ openspace.bindKey("F12", "openspace.time.setTime('2015-07-14T12:45:00.00'); open openspace.bindKey("r", "local b = openspace.getPropertyValue('PlutoProjection.renderable.fk'); openspace.setPropertyValue('PlutoProjection.renderable.fk', not b)") -openspace.bindKey("a", "openspace.setOrigin('NewHorizons')") -openspace.bindKey("s", "openspace.setOrigin('PlutoProjection')") -openspace.bindKey("d", "openspace.setOrigin('Charon')") -openspace.bindKey("z", "openspace.setOrigin('JupiterProjection')") -openspace.bindKey("x", "openspace.setOrigin('Europa')") +openspace.bindKey("a", "openspace.setPropertyValue('Interaction.origin', 'NewHorizons')") +openspace.bindKey("s", "openspace.setPropertyValue('Interaction.origin', 'PlutoProjection')") +openspace.bindKey("d", "openspace.setPropertyValue('Interaction.origin', 'Charon')") +openspace.bindKey("z", "openspace.setPropertyValue('Interaction.origin', 'JupiterProjection')") +openspace.bindKey("x", "openspace.setPropertyValue('Interaction.origin', 'Europa')") openspace.bindKey("g", "openspace.time.setTime('2007-02-28T11:40:00.00'); openspace.time.setDeltaTime(1);") -openspace.bindKey("h", "openspace.time.setTime('2015-07-14T10:00:00.00'); openspace.time.setDeltaTime(1); openspace.changeCoordinateSystem('Pluto'); openspace.setOrigin('PlutoProjection'); openspace.printInfo('Changing Viewpoint to Pluto-in-center');") +openspace.bindKey("h", "openspace.time.setTime('2015-07-14T10:00:00.00'); openspace.time.setDeltaTime(1); openspace.setPropertyValue('Interaction.coordinateSystem', 'Pluto');openspace.setPropertyValue('Interaction.origin', 'PlutoProjection'); openspace.printInfo('Changing Viewpoint to Pluto-in-center');") openspace.bindKey("i", "local b = openspace.getPropertyValue('PlutoTexture.renderable.enabled'); openspace.setPropertyValue('PlutoTexture.renderable.enabled', not b)") @@ -99,5 +99,4 @@ openspace.bindKey("p", "local b = openspace.getPropertyValue('Callisto.renderabl openspace.bindKey("p", "local b = openspace.getPropertyValue('PlutoProjection.renderable.performProjection'); openspace.setPropertyValue('PlutoProjection.renderable.performProjection', not b)") openspace.bindKey("p", "local b = openspace.getPropertyValue('Charon.renderable.performProjection'); openspace.setPropertyValue('Charon.renderable.performProjection', not b)") - -openspace.bindKey("PAUSE", "openspace.printInfo('F5: Toogle to Sun, F6: Toggle to Jupiter, F7: Toggle to Pluto'); openspace.printInfo('Bookmarks: 1: Jupter, 2-7: Pluto');") +openspace.bindKey("c", "openspace.parallel.setAddress('130.236.142.51');openspace.parallel.setPassword('newhorizons-20150714');openspace.parallel.connect();") \ No newline at end of file From 2bbb7996824313e96895725a7c3414174aa6dbd2 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sun, 12 Jul 2015 20:43:04 +0200 Subject: [PATCH 328/329] Updated ghoul version --- ext/ghoul | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/ghoul b/ext/ghoul index 926b7c6b0d..edae006863 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 926b7c6b0d44ad72b7df8217ac7f65dfb6c59479 +Subproject commit edae006863ea174600f45451f1b1c9eb7b317e0e From 995fff222b6338e1cd271b505c5505ded8d15a4c Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 13 Jul 2015 01:15:56 +0200 Subject: [PATCH 329/329] Increase the font size of the time printing --- data | 2 +- src/rendering/renderengine.cpp | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/data b/data index acf3b46580..ddcba938fb 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit acf3b4658081da227fa8dc244a8b1814e70a0c11 +Subproject commit ddcba938fbda7ec5cc1e8d06be0fc47e199b0f4f diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 416e4bcf13..97bf046d7c 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -419,16 +419,18 @@ void RenderEngine::render(const glm::mat4 &projectionMatrix, const glm::mat4 &vi // TODO: Adjust font_size properly when using retina screen const int font_size_mono = 10; + const int font_size_time = 15; const int font_size_light = 8; const int font_with_light = static_cast(font_size_light*0.7); const sgct_text::Font* fontLight = sgct_text::FontManager::instance()->getFont(constants::fonts::keyLight, font_size_light); const sgct_text::Font* fontMono = sgct_text::FontManager::instance()->getFont(constants::fonts::keyMono, font_size_mono); + const sgct_text::Font* fontTime = sgct_text::FontManager::instance()->getFont(constants::fonts::keyMono, font_size_time); if (_showInfo) { const sgct_text::Font* font = fontMono; int x1, xSize, y1, ySize; sgct::Engine::instance()->getActiveWindowPtr()->getCurrentViewportPixelCoords(x1, y1, xSize, ySize); - int startY = ySize - 2 * font_size_mono; + int startY = ySize - 2 * font_size_time; //const glm::vec2& scaling = _mainCamera->scaling(); //const glm::vec3& viewdirection = _mainCamera->viewDirection(); //const psc& position = _mainCamera->position(); @@ -444,8 +446,13 @@ void RenderEngine::render(const glm::mat4 &projectionMatrix, const glm::mat4 &vi double currentTime = Time::ref().currentTime(); //PrintText(line++, "Date: %s", Time::ref().currentTimeUTC().c_str()); - PrintColorTextArg(line++, "Date: %s", 10, glm::vec4(1), Time::ref().currentTimeUTC().c_str()); - glm::vec4 targetColor(0.00, 0.75, 1.00, 1); + std::string timeString = Time::ref().currentTimeUTC(); + if (timeString.size() > 11) + // This should never happen, but it's an emergency hack ---abock + timeString[11] = ' '; + Freetype::print(fontTime, 10, static_cast(startY - font_size_mono * line++ * 2), glm::vec4(1), "Date: %s", timeString.c_str()); + + glm::vec4 targetColor(0.00, 0.75, 1.00, 1); double dt = Time::ref().deltaTime(); PrintColorTextArg(line++, "Simulation increment (s): %.0f", 10, glm::vec4(1), dt);