diff --git a/.hgtags b/.hgtags
index 0aa708c977..730e86e16a 100755
--- a/.hgtags
+++ b/.hgtags
@@ -519,3 +519,6 @@ e9d350764dfbf5a46229e627547ef5c1b1eeef00 4.0.2-release
4070611edd95eb3a683d1cd97c4c07fe67793812 4.0.6-release
33981d8130f031597b4c7f4c981b18359afb61a0 4.0.7-release
45eaee56883df7a439ed3300c44d3126f7e3a41e 4.0.8-release
+b280a1c797a3891e68dbc237e73de9cf19f426e9 4.1.1-release
+bfbba2244320dc2ae47758cd7edd8fa3b67dc756 4.1.2-release
+b41e1e7c7876f7656c505f552b5888b4e478f92b 5.0.0-release
diff --git a/README.md b/README.md
index ad040fb077..6eddb9aa9e 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
Second Life Viewer
====================
-
+
This project manages the source code for the
[Second Life](https://www.secondlife.com) Viewer.
diff --git a/autobuild.xml b/autobuild.xml
old mode 100755
new mode 100644
index e9928908be..f485aa0cb7
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -1484,11 +1484,11 @@
archive
name
darwin
@@ -1498,18 +1498,18 @@
archive
name
windows
version
- 1.5.3.311349
+ 1.5.3.317959
llphysicsextensions_source
+ vlc-bin
+
xmlrpc-epi
copyright
@@ -2617,14 +2657,6 @@
arguments
..\indra
- &&
- ..\indra\tools\vstool\VSTool.exe
- --solution
- SecondLife.sln
- --config
- RelWithDebInfo
- --startup
- secondlife-bin
options
@@ -2663,20 +2695,11 @@
arguments
..\indra
- &&
- ..\indra\tools\vstool\VSTool.exe
- --solution
- SecondLife.sln
- --config
- RelWithDebInfo
- --startup
- secondlife-bin
options
-G
"Visual Studio 12"
- -DUNATTENDED:BOOL=ON
-DINSTALL_PROPRIETARY=FALSE
-DUSE_KDU=FALSE
@@ -2705,14 +2728,6 @@
arguments
..\indra
- &&
- ..\indra\tools\vstool\VSTool.exe
- --solution
- SecondLife.sln
- --config
- Release
- --startup
- secondlife-bin
options
@@ -2749,20 +2764,11 @@
arguments
..\indra
- &&
- ..\indra\tools\vstool\VSTool.exe
- --solution
- SecondLife.sln
- --config
- Release
- --startup
- secondlife-bin
options
-G
"Visual Studio 12"
- -DUNATTENDED:BOOL=ON
-DINSTALL_PROPRIETARY=FALSE
-DUSE_KDU=FALSE
diff --git a/build.sh b/build.sh
index cd2a9ebf5e..ab7a1faa37 100755
--- a/build.sh
+++ b/build.sh
@@ -97,6 +97,7 @@ pre_build()
"$autobuild" configure --quiet -c $variant -- \
-DPACKAGE:BOOL=ON \
+ -DUNATTENDED:BOOL=ON \
-DRELEASE_CRASH_REPORTING:BOOL=ON \
-DVIEWER_CHANNEL:STRING="\"$viewer_channel\"" \
-DGRID:STRING="\"$viewer_grid\"" \
diff --git a/doc/contributions.txt b/doc/contributions.txt
index 8ed41ddc34..521be7b7e2 100755
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -12,6 +12,7 @@ Able Whitman
Adam Marker
VWR-2755
Adeon Writer
+ MAINT-1211
Aeonix Aeon
Agathos Frascati
CT-246
@@ -190,9 +191,19 @@ Ansariel Hiller
STORM-2094
MAINT-5756
MAINT-4677
+ MAINT-6300
+ MAINT-6397
MAINT-6432
+ MAINT-6513
+ MAINT-6514
+ MAINT-6552
STORM-2133
MAINT-6511
+ MAINT-6612
+ MAINT-6637
+ MAINT-6636
+ MAINT-6744
+ MAINT-6752
Aralara Rajal
Arare Chantilly
CHUIBUG-191
@@ -307,6 +318,7 @@ Catherine Pfeffer
VWR-1282
VWR-8624
VWR-10854
+Cathy Foil
Cayu Cluny
Celierra Darling
VWR-1274
@@ -790,6 +802,7 @@ Kitty Barnett
MAINT-6152
MAINT-6153
MAINT-6154
+ MAINT-6568
Kolor Fall
Komiko Okamoto
Korvel Noh
@@ -859,6 +872,7 @@ MartinRJ Fayray
STORM-1845
STORM-1911
STORM-1934
+Matrice Laville
Matthew Anthony
Matthew Dowd
VWR-1344
@@ -1021,6 +1035,7 @@ Nicky Dasmijn
OPEN-187
STORM-2010
STORM-2082
+ MAINT-6665
Nicky Perian
OPEN-1
STORM-1087
@@ -1232,6 +1247,7 @@ Shnurui Troughton
Shyotl Kuhr
MAINT-1138
MAINT-2334
+ MAINT-6913
Siana Gearz
STORM-960
STORM-1088
@@ -1265,6 +1281,7 @@ Sovereign Engineer
MAINT-6107
STORM-2107
MAINT-6218
+ MAINT-6913
SpacedOut Frye
VWR-34
VWR-45
diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake
index 86fc2dfff5..adc134c48c 100644
--- a/indra/cmake/00-Common.cmake
+++ b/indra/cmake/00-Common.cmake
@@ -83,8 +83,8 @@ if (WINDOWS)
add_definitions(/WX)
endif (NOT VS_DISABLE_FATAL_WARNINGS)
- # configure win32 API for windows XP+ compatibility
- set(WINVER "0x0501" CACHE STRING "Win32 API Target version (see http://msdn.microsoft.com/en-us/library/aa383745%28v=VS.85%29.aspx)")
+ # configure Win32 API for Windows Vista+ compatibility
+ set(WINVER "0x0600" CACHE STRING "Win32 API Target version (see http://msdn.microsoft.com/en-us/library/aa383745%28v=VS.85%29.aspx)")
add_definitions("/DWINVER=${WINVER}" "/D_WIN32_WINNT=${WINVER}")
endif (WINDOWS)
diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt
index 6dc8e3dfbf..13a31cbce7 100644
--- a/indra/cmake/CMakeLists.txt
+++ b/indra/cmake/CMakeLists.txt
@@ -24,7 +24,6 @@ set(cmake_SOURCE_FILES
DirectX.cmake
DragDrop.cmake
EXPAT.cmake
-## ExamplePlugin.cmake
FindAPR.cmake
FindAutobuild.cmake
FindBerkeleyDB.cmake
@@ -100,6 +99,7 @@ set(cmake_SOURCE_FILES
Variables.cmake
ViewerMiscLibs.cmake
VisualLeakDetector.cmake
+ LibVLCPlugin.cmake
XmlRpcEpi.cmake
ZLIB.cmake
)
diff --git a/indra/cmake/LLWindow.cmake b/indra/cmake/LLWindow.cmake
index ba07a80f05..80af7ff2ab 100644
--- a/indra/cmake/LLWindow.cmake
+++ b/indra/cmake/LLWindow.cmake
@@ -18,7 +18,7 @@ else (USESYSTEMLIBS)
use_prebuilt_binary(SDL)
set (SDL_FOUND TRUE)
set (SDL_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/i686-linux)
- set (SDL_LIBRARY SDL directfb fusion direct)
+ set (SDL_LIBRARY SDL directfb fusion direct X11)
endif (LINUX)
endif (USESYSTEMLIBS)
diff --git a/indra/cmake/LibVLCPlugin.cmake b/indra/cmake/LibVLCPlugin.cmake
new file mode 100644
index 0000000000..4472676fb4
--- /dev/null
+++ b/indra/cmake/LibVLCPlugin.cmake
@@ -0,0 +1,27 @@
+# -*- cmake -*-
+include(Linking)
+include(Prebuilt)
+
+if (USESYSTEMLIBS)
+ set(LIBVLCPLUGIN OFF CACHE BOOL
+ "LIBVLCPLUGIN support for the llplugin/llmedia test apps.")
+else (USESYSTEMLIBS)
+ use_prebuilt_binary(vlc-bin)
+ set(LIBVLCPLUGIN ON CACHE BOOL
+ "LIBVLCPLUGIN support for the llplugin/llmedia test apps.")
+ set(VLC_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/vlc)
+endif (USESYSTEMLIBS)
+
+if (WINDOWS)
+ set(VLC_PLUGIN_LIBRARIES
+ libvlc.lib
+ libvlccore.lib
+ )
+elseif (DARWIN)
+elseif (LINUX)
+ # Specify a full path to make sure we get a static link
+ set(VLC_PLUGIN_LIBRARIES
+ ${LIBS_PREBUILT_DIR}/lib/libvlc.a
+ ${LIBS_PREBUILT_DIR}/lib/libvlccore.a
+ )
+endif (WINDOWS)
diff --git a/indra/edit-me-to-trigger-new-build.txt b/indra/edit-me-to-trigger-new-build.txt
index 32aa08a2b7..9f9ca6c39c 100644
--- a/indra/edit-me-to-trigger-new-build.txt
+++ b/indra/edit-me-to-trigger-new-build.txt
@@ -1,3 +1,4 @@
2014-02-25 10:34
+
diff --git a/indra/lib/python/indra/base/__init__.py b/indra/lib/python/indra/base/__init__.py
deleted file mode 100755
index 2904fd3380..0000000000
--- a/indra/lib/python/indra/base/__init__.py
+++ /dev/null
@@ -1,27 +0,0 @@
-"""\
-@file __init__.py
-@brief Initialization file for the indra.base module.
-
-$LicenseInfo:firstyear=2007&license=mit$
-
-Copyright (c) 2007-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-"""
diff --git a/indra/lib/python/indra/base/cllsd_test.py b/indra/lib/python/indra/base/cllsd_test.py
deleted file mode 100755
index 1f06898ffd..0000000000
--- a/indra/lib/python/indra/base/cllsd_test.py
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/usr/bin/python
-##
-## $LicenseInfo:firstyear=2011&license=viewerlgpl$
-## Second Life Viewer Source Code
-## Copyright (C) 2011, Linden Research, Inc.
-##
-## 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;
-## version 2.1 of the License only.
-##
-## 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
-##
-## Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
-## $/LicenseInfo$
-from indra.base import llsd, lluuid
-from datetime import datetime
-import cllsd
-import time, sys
-
-class myint(int):
- pass
-
-values = (
- '&<>',
- u'\u81acj',
- llsd.uri('http://foo<'),
- lluuid.UUID(),
- llsd.LLSD(['thing']),
- 1,
- myint(31337),
- sys.maxint + 10,
- llsd.binary('foo'),
- [],
- {},
- {u'f&\u1212': 3},
- 3.1,
- True,
- None,
- datetime.fromtimestamp(time.time()),
- )
-
-def valuator(values):
- for v in values:
- yield v
-
-longvalues = () # (values, list(values), iter(values), valuator(values))
-
-for v in values + longvalues:
- print '%r => %r' % (v, cllsd.llsd_to_xml(v))
-
-a = [[{'a':3}]] * 1000000
-
-s = time.time()
-print hash(cllsd.llsd_to_xml(a))
-e = time.time()
-t1 = e - s
-print t1
-
-s = time.time()
-print hash(llsd.LLSDXMLFormatter()._format(a))
-e = time.time()
-t2 = e - s
-print t2
-
-print 'Speedup:', t2 / t1
diff --git a/indra/lib/python/indra/base/config.py b/indra/lib/python/indra/base/config.py
deleted file mode 100755
index adafa29b51..0000000000
--- a/indra/lib/python/indra/base/config.py
+++ /dev/null
@@ -1,266 +0,0 @@
-"""\
-@file config.py
-@brief Utility module for parsing and accessing the indra.xml config file.
-
-$LicenseInfo:firstyear=2006&license=mit$
-
-Copyright (c) 2006-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-"""
-
-import copy
-import errno
-import os
-import traceback
-import time
-import types
-
-from os.path import dirname, getmtime, join, realpath
-from indra.base import llsd
-
-_g_config = None
-
-class IndraConfig(object):
- """
- IndraConfig loads a 'indra' xml configuration file
- and loads into memory. This representation in memory
- can get updated to overwrite values or add new values.
-
- The xml configuration file is considered a live file and changes
- to the file are checked and reloaded periodically. If a value had
- been overwritten via the update or set method, the loaded values
- from the file are ignored (the values from the update/set methods
- override)
- """
- def __init__(self, indra_config_file):
- self._indra_config_file = indra_config_file
- self._reload_check_interval = 30 # seconds
- self._last_check_time = 0
- self._last_mod_time = 0
-
- self._config_overrides = {}
- self._config_file_dict = {}
- self._combined_dict = {}
-
- self._load()
-
- def _load(self):
- # if you initialize the IndraConfig with None, no attempt
- # is made to load any files
- if self._indra_config_file is None:
- return
-
- config_file = open(self._indra_config_file)
- self._config_file_dict = llsd.parse(config_file.read())
- self._combine_dictionaries()
- config_file.close()
-
- self._last_mod_time = self._get_last_modified_time()
- self._last_check_time = time.time() # now
-
- def _get_last_modified_time(self):
- """
- Returns the mtime (last modified time) of the config file,
- if such exists.
- """
- if self._indra_config_file is not None:
- return os.path.getmtime(self._indra_config_file)
-
- return 0
-
- def _combine_dictionaries(self):
- self._combined_dict = {}
- self._combined_dict.update(self._config_file_dict)
- self._combined_dict.update(self._config_overrides)
-
- def _reload_if_necessary(self):
- now = time.time()
-
- if (now - self._last_check_time) > self._reload_check_interval:
- self._last_check_time = now
- try:
- modtime = self._get_last_modified_time()
- if modtime > self._last_mod_time:
- self._load()
- except OSError, e:
- if e.errno == errno.ENOENT: # file not found
- # someone messed with our internal state
- # or removed the file
-
- print 'WARNING: Configuration file has been removed ' + (self._indra_config_file)
- print 'Disabling reloading of configuration file.'
-
- traceback.print_exc()
-
- self._indra_config_file = None
- self._last_check_time = 0
- self._last_mod_time = 0
- else:
- raise # pass the exception along to the caller
-
- def __getitem__(self, key):
- self._reload_if_necessary()
-
- return self._combined_dict[key]
-
- def get(self, key, default = None):
- try:
- return self.__getitem__(key)
- except KeyError:
- return default
-
- def __setitem__(self, key, value):
- """
- Sets the value of the config setting of key to be newval
-
- Once any key/value pair is changed via the set method,
- that key/value pair will remain set with that value until
- change via the update or set method
- """
- self._config_overrides[key] = value
- self._combine_dictionaries()
-
- def set(self, key, newval):
- return self.__setitem__(key, newval)
-
- def update(self, new_conf):
- """
- Load an XML file and apply its map as overrides or additions
- to the existing config. Update can be a file or a dict.
-
- Once any key/value pair is changed via the update method,
- that key/value pair will remain set with that value until
- change via the update or set method
- """
- if isinstance(new_conf, dict):
- overrides = new_conf
- else:
- # assuming that it is a filename
- config_file = open(new_conf)
- overrides = llsd.parse(config_file.read())
- config_file.close()
-
- self._config_overrides.update(overrides)
- self._combine_dictionaries()
-
- def as_dict(self):
- """
- Returns immutable copy of the IndraConfig as a dictionary
- """
- return copy.deepcopy(self._combined_dict)
-
-def load(config_xml_file = None):
- global _g_config
-
- load_default_files = config_xml_file is None
- if load_default_files:
- ## going from:
- ## "/opt/linden/indra/lib/python/indra/base/config.py"
- ## to:
- ## "/opt/linden/etc/indra.xml"
- config_xml_file = realpath(
- dirname(realpath(__file__)) + "../../../../../../etc/indra.xml")
-
- try:
- _g_config = IndraConfig(config_xml_file)
- except IOError:
- # Failure to load passed in file
- # or indra.xml default file
- if load_default_files:
- try:
- config_xml_file = realpath(
- dirname(realpath(__file__)) + "../../../../../../etc/globals.xml")
- _g_config = IndraConfig(config_xml_file)
- return
- except IOError:
- # Failure to load globals.xml
- # fall to code below
- pass
-
- # Either failed to load passed in file
- # or failed to load all default files
- _g_config = IndraConfig(None)
-
-def dump(indra_xml_file, indra_cfg = None, update_in_mem=False):
- '''
- Dump config contents into a file
- Kindof reverse of load.
- Optionally takes a new config to dump.
- Does NOT update global config unless requested.
- '''
- global _g_config
-
- if not indra_cfg:
- if _g_config is None:
- return
-
- indra_cfg = _g_config.as_dict()
-
- if not indra_cfg:
- return
-
- config_file = open(indra_xml_file, 'w')
- _config_xml = llsd.format_xml(indra_cfg)
- config_file.write(_config_xml)
- config_file.close()
-
- if update_in_mem:
- update(indra_cfg)
-
-def update(new_conf):
- global _g_config
-
- if _g_config is None:
- # To keep with how this function behaved
- # previously, a call to update
- # before the global is defined
- # make a new global config which does not
- # load data from a file.
- _g_config = IndraConfig(None)
-
- return _g_config.update(new_conf)
-
-def get(key, default = None):
- global _g_config
-
- if _g_config is None:
- load()
-
- return _g_config.get(key, default)
-
-def set(key, newval):
- """
- Sets the value of the config setting of key to be newval
-
- Once any key/value pair is changed via the set method,
- that key/value pair will remain set with that value until
- change via the update or set method or program termination
- """
- global _g_config
-
- if _g_config is None:
- _g_config = IndraConfig(None)
-
- _g_config.set(key, newval)
-
-def get_config():
- global _g_config
- return _g_config
diff --git a/indra/lib/python/indra/base/llsd.py b/indra/lib/python/indra/base/llsd.py
deleted file mode 100755
index 4527b115f9..0000000000
--- a/indra/lib/python/indra/base/llsd.py
+++ /dev/null
@@ -1,1052 +0,0 @@
-"""\
-@file llsd.py
-@brief Types as well as parsing and formatting functions for handling LLSD.
-
-$LicenseInfo:firstyear=2006&license=mit$
-
-Copyright (c) 2006-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-"""
-
-import datetime
-import base64
-import string
-import struct
-import time
-import types
-import re
-
-from indra.util.fastest_elementtree import ElementTreeError, fromstring
-from indra.base import lluuid
-
-# cllsd.c in server/server-1.25 has memory leaks,
-# so disabling cllsd for now
-#try:
-# import cllsd
-#except ImportError:
-# cllsd = None
-cllsd = None
-
-int_regex = re.compile(r"[-+]?\d+")
-real_regex = re.compile(r"[-+]?(\d+(\.\d*)?|\d*\.\d+)([eE][-+]?\d+)?")
-alpha_regex = re.compile(r"[a-zA-Z]+")
-date_regex = re.compile(r"(?P\d{4})-(?P\d{2})-(?P\d{2})T"
- r"(?P\d{2}):(?P\d{2}):(?P\d{2})"
- r"(?P(\.\d+)?)Z")
-#date: d"YYYY-MM-DDTHH:MM:SS.FFFFFFZ"
-
-class LLSDParseError(Exception):
- pass
-
-class LLSDSerializationError(TypeError):
- pass
-
-
-class binary(str):
- pass
-
-class uri(str):
- pass
-
-
-BOOL_TRUE = ('1', '1.0', 'true')
-BOOL_FALSE = ('0', '0.0', 'false', '')
-
-
-def format_datestr(v):
- """ Formats a datetime or date object into the string format shared by xml and notation serializations."""
- if hasattr(v, 'microsecond'):
- return v.isoformat() + 'Z'
- else:
- return v.strftime('%Y-%m-%dT%H:%M:%SZ')
-
-def parse_datestr(datestr):
- """Parses a datetime object from the string format shared by xml and notation serializations."""
- if datestr == "":
- return datetime.datetime(1970, 1, 1)
-
- match = re.match(date_regex, datestr)
- if not match:
- raise LLSDParseError("invalid date string '%s'." % datestr)
-
- year = int(match.group('year'))
- month = int(match.group('month'))
- day = int(match.group('day'))
- hour = int(match.group('hour'))
- minute = int(match.group('minute'))
- second = int(match.group('second'))
- seconds_float = match.group('second_float')
- microsecond = 0
- if seconds_float:
- microsecond = int(float('0' + seconds_float) * 1e6)
- return datetime.datetime(year, month, day, hour, minute, second, microsecond)
-
-
-def bool_to_python(node):
- val = node.text or ''
- if val in BOOL_TRUE:
- return True
- else:
- return False
-
-def int_to_python(node):
- val = node.text or ''
- if not val.strip():
- return 0
- return int(val)
-
-def real_to_python(node):
- val = node.text or ''
- if not val.strip():
- return 0.0
- return float(val)
-
-def uuid_to_python(node):
- return lluuid.UUID(node.text)
-
-def str_to_python(node):
- return node.text or ''
-
-def bin_to_python(node):
- return binary(base64.decodestring(node.text or ''))
-
-def date_to_python(node):
- val = node.text or ''
- if not val:
- val = "1970-01-01T00:00:00Z"
- return parse_datestr(val)
-
-
-def uri_to_python(node):
- val = node.text or ''
- if not val:
- return None
- return uri(val)
-
-def map_to_python(node):
- result = {}
- for index in range(len(node))[::2]:
- result[node[index].text] = to_python(node[index+1])
- return result
-
-def array_to_python(node):
- return [to_python(child) for child in node]
-
-
-NODE_HANDLERS = dict(
- undef=lambda x: None,
- boolean=bool_to_python,
- integer=int_to_python,
- real=real_to_python,
- uuid=uuid_to_python,
- string=str_to_python,
- binary=bin_to_python,
- date=date_to_python,
- uri=uri_to_python,
- map=map_to_python,
- array=array_to_python,
- )
-
-def to_python(node):
- return NODE_HANDLERS[node.tag](node)
-
-class Nothing(object):
- pass
-
-
-class LLSDXMLFormatter(object):
- def __init__(self):
- self.type_map = {
- type(None) : self.UNDEF,
- bool : self.BOOLEAN,
- int : self.INTEGER,
- long : self.INTEGER,
- float : self.REAL,
- lluuid.UUID : self.UUID,
- binary : self.BINARY,
- str : self.STRING,
- unicode : self.STRING,
- uri : self.URI,
- datetime.datetime : self.DATE,
- datetime.date : self.DATE,
- list : self.ARRAY,
- tuple : self.ARRAY,
- types.GeneratorType : self.ARRAY,
- dict : self.MAP,
- LLSD : self.LLSD
- }
-
- def elt(self, name, contents=None):
- if(contents is None or contents is ''):
- return "<%s />" % (name,)
- else:
- if type(contents) is unicode:
- contents = contents.encode('utf-8')
- return "<%s>%s%s>" % (name, contents, name)
-
- def xml_esc(self, v):
- if type(v) is unicode:
- v = v.encode('utf-8')
- return v.replace('&', '&').replace('<', '<').replace('>', '>')
-
- def LLSD(self, v):
- return self.generate(v.thing)
- def UNDEF(self, v):
- return self.elt('undef')
- def BOOLEAN(self, v):
- if v:
- return self.elt('boolean', 'true')
- else:
- return self.elt('boolean', 'false')
- def INTEGER(self, v):
- return self.elt('integer', v)
- def REAL(self, v):
- return self.elt('real', v)
- def UUID(self, v):
- if(v.isNull()):
- return self.elt('uuid')
- else:
- return self.elt('uuid', v)
- def BINARY(self, v):
- return self.elt('binary', base64.encodestring(v))
- def STRING(self, v):
- return self.elt('string', self.xml_esc(v))
- def URI(self, v):
- return self.elt('uri', self.xml_esc(str(v)))
- def DATE(self, v):
- return self.elt('date', format_datestr(v))
- def ARRAY(self, v):
- return self.elt('array', ''.join([self.generate(item) for item in v]))
- def MAP(self, v):
- return self.elt(
- 'map',
- ''.join(["%s%s" % (self.elt('key', self.xml_esc(str(key))), self.generate(value))
- for key, value in v.items()]))
-
- typeof = type
- def generate(self, something):
- t = self.typeof(something)
- if self.type_map.has_key(t):
- return self.type_map[t](something)
- else:
- raise LLSDSerializationError("Cannot serialize unknown type: %s (%s)" % (
- t, something))
-
- def _format(self, something):
- return '' + self.elt("llsd", self.generate(something))
-
- def format(self, something):
- if cllsd:
- return cllsd.llsd_to_xml(something)
- return self._format(something)
-
-_g_xml_formatter = None
-def format_xml(something):
- global _g_xml_formatter
- if _g_xml_formatter is None:
- _g_xml_formatter = LLSDXMLFormatter()
- return _g_xml_formatter.format(something)
-
-class LLSDXMLPrettyFormatter(LLSDXMLFormatter):
- def __init__(self, indent_atom = None):
- # Call the super class constructor so that we have the type map
- super(LLSDXMLPrettyFormatter, self).__init__()
-
- # Override the type map to use our specialized formatters to
- # emit the pretty output.
- self.type_map[list] = self.PRETTY_ARRAY
- self.type_map[tuple] = self.PRETTY_ARRAY
- self.type_map[types.GeneratorType] = self.PRETTY_ARRAY,
- self.type_map[dict] = self.PRETTY_MAP
-
- # Private data used for indentation.
- self._indent_level = 1
- if indent_atom is None:
- self._indent_atom = ' '
- else:
- self._indent_atom = indent_atom
-
- def _indent(self):
- "Return an indentation based on the atom and indentation level."
- return self._indent_atom * self._indent_level
-
- def PRETTY_ARRAY(self, v):
- rv = []
- rv.append('\n')
- self._indent_level = self._indent_level + 1
- rv.extend(["%s%s\n" %
- (self._indent(),
- self.generate(item))
- for item in v])
- self._indent_level = self._indent_level - 1
- rv.append(self._indent())
- rv.append('')
- return ''.join(rv)
-
- def PRETTY_MAP(self, v):
- rv = []
- rv.append('\n')
- self._indent_level = self._indent_level + 1
- keys = v.keys()
- keys.sort()
- rv.extend(["%s%s\n%s%s\n" %
- (self._indent(),
- self.elt('key', key),
- self._indent(),
- self.generate(v[key]))
- for key in keys])
- self._indent_level = self._indent_level - 1
- rv.append(self._indent())
- rv.append('')
- return ''.join(rv)
-
- def format(self, something):
- data = []
- data.append('\n')
- data.append(self.generate(something))
- data.append('\n')
- return '\n'.join(data)
-
-def format_pretty_xml(something):
- """@brief Serialize a python object as 'pretty' llsd xml.
-
- The output conforms to the LLSD DTD, unlike the output from the
- standard python xml.dom DOM::toprettyxml() method which does not
- preserve significant whitespace.
- This function is not necessarily suited for serializing very large
- objects. It is not optimized by the cllsd module, and sorts on
- dict (llsd map) keys alphabetically to ease human reading.
- """
- return LLSDXMLPrettyFormatter().format(something)
-
-class LLSDNotationFormatter(object):
- def __init__(self):
- self.type_map = {
- type(None) : self.UNDEF,
- bool : self.BOOLEAN,
- int : self.INTEGER,
- long : self.INTEGER,
- float : self.REAL,
- lluuid.UUID : self.UUID,
- binary : self.BINARY,
- str : self.STRING,
- unicode : self.STRING,
- uri : self.URI,
- datetime.datetime : self.DATE,
- datetime.date : self.DATE,
- list : self.ARRAY,
- tuple : self.ARRAY,
- types.GeneratorType : self.ARRAY,
- dict : self.MAP,
- LLSD : self.LLSD
- }
-
- def LLSD(self, v):
- return self.generate(v.thing)
- def UNDEF(self, v):
- return '!'
- def BOOLEAN(self, v):
- if v:
- return 'true'
- else:
- return 'false'
- def INTEGER(self, v):
- return "i%s" % v
- def REAL(self, v):
- return "r%s" % v
- def UUID(self, v):
- return "u%s" % v
- def BINARY(self, v):
- return 'b64"' + base64.encodestring(v) + '"'
- def STRING(self, v):
- if isinstance(v, unicode):
- v = v.encode('utf-8')
- return "'%s'" % v.replace("\\", "\\\\").replace("'", "\\'")
- def URI(self, v):
- return 'l"%s"' % str(v).replace("\\", "\\\\").replace('"', '\\"')
- def DATE(self, v):
- return 'd"%s"' % format_datestr(v)
- def ARRAY(self, v):
- return "[%s]" % ','.join([self.generate(item) for item in v])
- def MAP(self, v):
- def fix(key):
- if isinstance(key, unicode):
- return key.encode('utf-8')
- return key
- return "{%s}" % ','.join(["'%s':%s" % (fix(key).replace("\\", "\\\\").replace("'", "\\'"), self.generate(value))
- for key, value in v.items()])
-
- def generate(self, something):
- t = type(something)
- handler = self.type_map.get(t)
- if handler:
- return handler(something)
- else:
- try:
- return self.ARRAY(iter(something))
- except TypeError:
- raise LLSDSerializationError(
- "Cannot serialize unknown type: %s (%s)" % (t, something))
-
- def format(self, something):
- return self.generate(something)
-
-def format_notation(something):
- return LLSDNotationFormatter().format(something)
-
-def _hex_as_nybble(hex):
- if (hex >= '0') and (hex <= '9'):
- return ord(hex) - ord('0')
- elif (hex >= 'a') and (hex <='f'):
- return 10 + ord(hex) - ord('a')
- elif (hex >= 'A') and (hex <='F'):
- return 10 + ord(hex) - ord('A');
-
-class LLSDBinaryParser(object):
- def __init__(self):
- pass
-
- def parse(self, buffer, ignore_binary = False):
- """
- This is the basic public interface for parsing.
-
- @param buffer the binary data to parse in an indexable sequence.
- @param ignore_binary parser throws away data in llsd binary nodes.
- @return returns a python object.
- """
- self._buffer = buffer
- self._index = 0
- self._keep_binary = not ignore_binary
- return self._parse()
-
- def _parse(self):
- cc = self._buffer[self._index]
- self._index += 1
- if cc == '{':
- return self._parse_map()
- elif cc == '[':
- return self._parse_array()
- elif cc == '!':
- return None
- elif cc == '0':
- return False
- elif cc == '1':
- return True
- elif cc == 'i':
- # 'i' = integer
- idx = self._index
- self._index += 4
- return struct.unpack("!i", self._buffer[idx:idx+4])[0]
- elif cc == ('r'):
- # 'r' = real number
- idx = self._index
- self._index += 8
- return struct.unpack("!d", self._buffer[idx:idx+8])[0]
- elif cc == 'u':
- # 'u' = uuid
- idx = self._index
- self._index += 16
- return lluuid.uuid_bits_to_uuid(self._buffer[idx:idx+16])
- elif cc == 's':
- # 's' = string
- return self._parse_string()
- elif cc in ("'", '"'):
- # delimited/escaped string
- return self._parse_string_delim(cc)
- elif cc == 'l':
- # 'l' = uri
- return uri(self._parse_string())
- elif cc == ('d'):
- # 'd' = date in seconds since epoch
- idx = self._index
- self._index += 8
- seconds = struct.unpack("!d", self._buffer[idx:idx+8])[0]
- return datetime.datetime.fromtimestamp(seconds)
- elif cc == 'b':
- binary = self._parse_string()
- if self._keep_binary:
- return binary
- # *NOTE: maybe have a binary placeholder which has the
- # length.
- return None
- else:
- raise LLSDParseError("invalid binary token at byte %d: %d" % (
- self._index - 1, ord(cc)))
-
- def _parse_map(self):
- rv = {}
- size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0]
- self._index += 4
- count = 0
- cc = self._buffer[self._index]
- self._index += 1
- key = ''
- while (cc != '}') and (count < size):
- if cc == 'k':
- key = self._parse_string()
- elif cc in ("'", '"'):
- key = self._parse_string_delim(cc)
- else:
- raise LLSDParseError("invalid map key at byte %d." % (
- self._index - 1,))
- value = self._parse()
- rv[key] = value
- count += 1
- cc = self._buffer[self._index]
- self._index += 1
- if cc != '}':
- raise LLSDParseError("invalid map close token at byte %d." % (
- self._index,))
- return rv
-
- def _parse_array(self):
- rv = []
- size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0]
- self._index += 4
- count = 0
- cc = self._buffer[self._index]
- while (cc != ']') and (count < size):
- rv.append(self._parse())
- count += 1
- cc = self._buffer[self._index]
- if cc != ']':
- raise LLSDParseError("invalid array close token at byte %d." % (
- self._index,))
- self._index += 1
- return rv
-
- def _parse_string(self):
- size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0]
- self._index += 4
- rv = self._buffer[self._index:self._index+size]
- self._index += size
- return rv
-
- def _parse_string_delim(self, delim):
- list = []
- found_escape = False
- found_hex = False
- found_digit = False
- byte = 0
- while True:
- cc = self._buffer[self._index]
- self._index += 1
- if found_escape:
- if found_hex:
- if found_digit:
- found_escape = False
- found_hex = False
- found_digit = False
- byte <<= 4
- byte |= _hex_as_nybble(cc)
- list.append(chr(byte))
- byte = 0
- else:
- found_digit = True
- byte = _hex_as_nybble(cc)
- elif cc == 'x':
- found_hex = True
- else:
- if cc == 'a':
- list.append('\a')
- elif cc == 'b':
- list.append('\b')
- elif cc == 'f':
- list.append('\f')
- elif cc == 'n':
- list.append('\n')
- elif cc == 'r':
- list.append('\r')
- elif cc == 't':
- list.append('\t')
- elif cc == 'v':
- list.append('\v')
- else:
- list.append(cc)
- found_escape = False
- elif cc == '\\':
- found_escape = True
- elif cc == delim:
- break
- else:
- list.append(cc)
- return ''.join(list)
-
-class LLSDNotationParser(object):
- """ Parse LLSD notation:
- map: { string:object, string:object }
- array: [ object, object, object ]
- undef: !
- boolean: true | false | 1 | 0 | T | F | t | f | TRUE | FALSE
- integer: i####
- real: r####
- uuid: u####
- string: "g\'day" | 'have a "nice" day' | s(size)"raw data"
- uri: l"escaped"
- date: d"YYYY-MM-DDTHH:MM:SS.FFZ"
- binary: b##"ff3120ab1" | b(size)"raw data"
- """
- def __init__(self):
- pass
-
- def parse(self, buffer, ignore_binary = False):
- """
- This is the basic public interface for parsing.
-
- @param buffer the notation string to parse.
- @param ignore_binary parser throws away data in llsd binary nodes.
- @return returns a python object.
- """
- if buffer == "":
- return False
-
- self._buffer = buffer
- self._index = 0
- return self._parse()
-
- def _parse(self):
- cc = self._buffer[self._index]
- self._index += 1
- if cc == '{':
- return self._parse_map()
- elif cc == '[':
- return self._parse_array()
- elif cc == '!':
- return None
- elif cc == '0':
- return False
- elif cc == '1':
- return True
- elif cc in ('F', 'f'):
- self._skip_alpha()
- return False
- elif cc in ('T', 't'):
- self._skip_alpha()
- return True
- elif cc == 'i':
- # 'i' = integer
- return self._parse_integer()
- elif cc == ('r'):
- # 'r' = real number
- return self._parse_real()
- elif cc == 'u':
- # 'u' = uuid
- return self._parse_uuid()
- elif cc in ("'", '"', 's'):
- return self._parse_string(cc)
- elif cc == 'l':
- # 'l' = uri
- delim = self._buffer[self._index]
- self._index += 1
- val = uri(self._parse_string(delim))
- if len(val) == 0:
- return None
- return val
- elif cc == ('d'):
- # 'd' = date in seconds since epoch
- return self._parse_date()
- elif cc == 'b':
- return self._parse_binary()
- else:
- raise LLSDParseError("invalid token at index %d: %d" % (
- self._index - 1, ord(cc)))
-
- def _parse_binary(self):
- i = self._index
- if self._buffer[i:i+2] == '64':
- q = self._buffer[i+2]
- e = self._buffer.find(q, i+3)
- try:
- return base64.decodestring(self._buffer[i+3:e])
- finally:
- self._index = e + 1
- else:
- raise LLSDParseError('random horrible binary format not supported')
-
- def _parse_map(self):
- """ map: { string:object, string:object } """
- rv = {}
- cc = self._buffer[self._index]
- self._index += 1
- key = ''
- found_key = False
- while (cc != '}'):
- if not found_key:
- if cc in ("'", '"', 's'):
- key = self._parse_string(cc)
- found_key = True
- elif cc.isspace() or cc == ',':
- cc = self._buffer[self._index]
- self._index += 1
- else:
- raise LLSDParseError("invalid map key at byte %d." % (
- self._index - 1,))
- elif cc.isspace() or cc == ':':
- cc = self._buffer[self._index]
- self._index += 1
- continue
- else:
- self._index += 1
- value = self._parse()
- rv[key] = value
- found_key = False
- cc = self._buffer[self._index]
- self._index += 1
-
- return rv
-
- def _parse_array(self):
- """ array: [ object, object, object ] """
- rv = []
- cc = self._buffer[self._index]
- while (cc != ']'):
- if cc.isspace() or cc == ',':
- self._index += 1
- cc = self._buffer[self._index]
- continue
- rv.append(self._parse())
- cc = self._buffer[self._index]
-
- if cc != ']':
- raise LLSDParseError("invalid array close token at index %d." % (
- self._index,))
- self._index += 1
- return rv
-
- def _parse_uuid(self):
- match = re.match(lluuid.UUID.uuid_regex, self._buffer[self._index:])
- if not match:
- raise LLSDParseError("invalid uuid token at index %d." % self._index)
-
- (start, end) = match.span()
- start += self._index
- end += self._index
- self._index = end
- return lluuid.UUID(self._buffer[start:end])
-
- def _skip_alpha(self):
- match = re.match(alpha_regex, self._buffer[self._index:])
- if match:
- self._index += match.end()
-
- def _parse_date(self):
- delim = self._buffer[self._index]
- self._index += 1
- datestr = self._parse_string(delim)
- return parse_datestr(datestr)
-
- def _parse_real(self):
- match = re.match(real_regex, self._buffer[self._index:])
- if not match:
- raise LLSDParseError("invalid real token at index %d." % self._index)
-
- (start, end) = match.span()
- start += self._index
- end += self._index
- self._index = end
- return float( self._buffer[start:end] )
-
- def _parse_integer(self):
- match = re.match(int_regex, self._buffer[self._index:])
- if not match:
- raise LLSDParseError("invalid integer token at index %d." % self._index)
-
- (start, end) = match.span()
- start += self._index
- end += self._index
- self._index = end
- return int( self._buffer[start:end] )
-
- def _parse_string(self, delim):
- """ string: "g\'day" | 'have a "nice" day' | s(size)"raw data" """
- rv = ""
-
- if delim in ("'", '"'):
- rv = self._parse_string_delim(delim)
- elif delim == 's':
- rv = self._parse_string_raw()
- else:
- raise LLSDParseError("invalid string token at index %d." % self._index)
-
- return rv
-
-
- def _parse_string_delim(self, delim):
- """ string: "g'day 'un" | 'have a "nice" day' """
- list = []
- found_escape = False
- found_hex = False
- found_digit = False
- byte = 0
- while True:
- cc = self._buffer[self._index]
- self._index += 1
- if found_escape:
- if found_hex:
- if found_digit:
- found_escape = False
- found_hex = False
- found_digit = False
- byte <<= 4
- byte |= _hex_as_nybble(cc)
- list.append(chr(byte))
- byte = 0
- else:
- found_digit = True
- byte = _hex_as_nybble(cc)
- elif cc == 'x':
- found_hex = True
- else:
- if cc == 'a':
- list.append('\a')
- elif cc == 'b':
- list.append('\b')
- elif cc == 'f':
- list.append('\f')
- elif cc == 'n':
- list.append('\n')
- elif cc == 'r':
- list.append('\r')
- elif cc == 't':
- list.append('\t')
- elif cc == 'v':
- list.append('\v')
- else:
- list.append(cc)
- found_escape = False
- elif cc == '\\':
- found_escape = True
- elif cc == delim:
- break
- else:
- list.append(cc)
- return ''.join(list)
-
- def _parse_string_raw(self):
- """ string: s(size)"raw data" """
- # Read the (size) portion.
- cc = self._buffer[self._index]
- self._index += 1
- if cc != '(':
- raise LLSDParseError("invalid string token at index %d." % self._index)
-
- rparen = self._buffer.find(')', self._index)
- if rparen == -1:
- raise LLSDParseError("invalid string token at index %d." % self._index)
-
- size = int(self._buffer[self._index:rparen])
-
- self._index = rparen + 1
- delim = self._buffer[self._index]
- self._index += 1
- if delim not in ("'", '"'):
- raise LLSDParseError("invalid string token at index %d." % self._index)
-
- rv = self._buffer[self._index:(self._index + size)]
- self._index += size
- cc = self._buffer[self._index]
- self._index += 1
- if cc != delim:
- raise LLSDParseError("invalid string token at index %d." % self._index)
-
- return rv
-
-def format_binary(something):
- return '\n' + _format_binary_recurse(something)
-
-def _format_binary_recurse(something):
- def _format_list(something):
- array_builder = []
- array_builder.append('[' + struct.pack('!i', len(something)))
- for item in something:
- array_builder.append(_format_binary_recurse(item))
- array_builder.append(']')
- return ''.join(array_builder)
-
- if something is None:
- return '!'
- elif isinstance(something, LLSD):
- return _format_binary_recurse(something.thing)
- elif isinstance(something, bool):
- if something:
- return '1'
- else:
- return '0'
- elif isinstance(something, (int, long)):
- return 'i' + struct.pack('!i', something)
- elif isinstance(something, float):
- return 'r' + struct.pack('!d', something)
- elif isinstance(something, lluuid.UUID):
- return 'u' + something._bits
- elif isinstance(something, binary):
- return 'b' + struct.pack('!i', len(something)) + something
- elif isinstance(something, str):
- return 's' + struct.pack('!i', len(something)) + something
- elif isinstance(something, unicode):
- something = something.encode('utf-8')
- return 's' + struct.pack('!i', len(something)) + something
- elif isinstance(something, uri):
- return 'l' + struct.pack('!i', len(something)) + something
- elif isinstance(something, datetime.datetime):
- seconds_since_epoch = time.mktime(something.timetuple())
- return 'd' + struct.pack('!d', seconds_since_epoch)
- elif isinstance(something, (list, tuple)):
- return _format_list(something)
- elif isinstance(something, dict):
- map_builder = []
- map_builder.append('{' + struct.pack('!i', len(something)))
- for key, value in something.items():
- if isinstance(key, unicode):
- key = key.encode('utf-8')
- map_builder.append('k' + struct.pack('!i', len(key)) + key)
- map_builder.append(_format_binary_recurse(value))
- map_builder.append('}')
- return ''.join(map_builder)
- else:
- try:
- return _format_list(list(something))
- except TypeError:
- raise LLSDSerializationError(
- "Cannot serialize unknown type: %s (%s)" %
- (type(something), something))
-
-
-def parse_binary(binary):
- if binary.startswith(''):
- just_binary = binary.split('\n', 1)[1]
- else:
- just_binary = binary
- return LLSDBinaryParser().parse(just_binary)
-
-def parse_xml(something):
- try:
- return to_python(fromstring(something)[0])
- except ElementTreeError, err:
- raise LLSDParseError(*err.args)
-
-def parse_notation(something):
- return LLSDNotationParser().parse(something)
-
-def parse(something):
- try:
- something = string.lstrip(something) #remove any pre-trailing whitespace
- if something.startswith(''):
- return parse_binary(something)
- # This should be better.
- elif something.startswith('<'):
- return parse_xml(something)
- else:
- return parse_notation(something)
- except KeyError, e:
- raise Exception('LLSD could not be parsed: %s' % (e,))
-
-class LLSD(object):
- def __init__(self, thing=None):
- self.thing = thing
-
- def __str__(self):
- return self.toXML(self.thing)
-
- parse = staticmethod(parse)
- toXML = staticmethod(format_xml)
- toPrettyXML = staticmethod(format_pretty_xml)
- toBinary = staticmethod(format_binary)
- toNotation = staticmethod(format_notation)
-
-
-undef = LLSD(None)
-
-XML_MIME_TYPE = 'application/llsd+xml'
-BINARY_MIME_TYPE = 'application/llsd+binary'
-
-# register converters for llsd in mulib, if it is available
-try:
- from mulib import stacked, mu
- stacked.NoProducer() # just to exercise stacked
- mu.safe_load(None) # just to exercise mu
-except:
- # mulib not available, don't print an error message since this is normal
- pass
-else:
- mu.add_parser(parse, XML_MIME_TYPE)
- mu.add_parser(parse, 'application/llsd+binary')
-
- def llsd_convert_xml(llsd_stuff, request):
- request.write(format_xml(llsd_stuff))
-
- def llsd_convert_binary(llsd_stuff, request):
- request.write(format_binary(llsd_stuff))
-
- for typ in [LLSD, dict, list, tuple, str, int, long, float, bool, unicode, type(None)]:
- stacked.add_producer(typ, llsd_convert_xml, XML_MIME_TYPE)
- stacked.add_producer(typ, llsd_convert_xml, 'application/xml')
- stacked.add_producer(typ, llsd_convert_xml, 'text/xml')
-
- stacked.add_producer(typ, llsd_convert_binary, 'application/llsd+binary')
-
- stacked.add_producer(LLSD, llsd_convert_xml, '*/*')
-
- # in case someone is using the legacy mu.xml wrapper, we need to
- # tell mu to produce application/xml or application/llsd+xml
- # (based on the accept header) from raw xml. Phoenix 2008-07-21
- stacked.add_producer(mu.xml, mu.produce_raw, XML_MIME_TYPE)
- stacked.add_producer(mu.xml, mu.produce_raw, 'application/xml')
-
-
-
-# mulib wsgi stuff
-# try:
-# from mulib import mu, adapters
-#
-# # try some known attributes from mulib to be ultra-sure we've imported it
-# mu.get_current
-# adapters.handlers
-# except:
-# # mulib not available, don't print an error message since this is normal
-# pass
-# else:
-# def llsd_xml_handler(content_type):
-# def handle_llsd_xml(env, start_response):
-# llsd_stuff, _ = mu.get_current(env)
-# result = format_xml(llsd_stuff)
-# start_response("200 OK", [('Content-Type', content_type)])
-# env['mu.negotiated_type'] = content_type
-# yield result
-# return handle_llsd_xml
-#
-# def llsd_binary_handler(content_type):
-# def handle_llsd_binary(env, start_response):
-# llsd_stuff, _ = mu.get_current(env)
-# result = format_binary(llsd_stuff)
-# start_response("200 OK", [('Content-Type', content_type)])
-# env['mu.negotiated_type'] = content_type
-# yield result
-# return handle_llsd_binary
-#
-# adapters.DEFAULT_PARSERS[XML_MIME_TYPE] = parse
-
-# for typ in [LLSD, dict, list, tuple, str, int, float, bool, unicode, type(None)]:
-# for content_type in (XML_MIME_TYPE, 'application/xml'):
-# adapters.handlers.set_handler(typ, llsd_xml_handler(content_type), content_type)
-#
-# adapters.handlers.set_handler(typ, llsd_binary_handler(BINARY_MIME_TYPE), BINARY_MIME_TYPE)
-#
-# adapters.handlers.set_handler(LLSD, llsd_xml_handler(XML_MIME_TYPE), '*/*')
diff --git a/indra/lib/python/indra/base/lluuid.py b/indra/lib/python/indra/base/lluuid.py
deleted file mode 100755
index 7413ffe10d..0000000000
--- a/indra/lib/python/indra/base/lluuid.py
+++ /dev/null
@@ -1,319 +0,0 @@
-"""\
-@file lluuid.py
-@brief UUID parser/generator.
-
-$LicenseInfo:firstyear=2004&license=mit$
-
-Copyright (c) 2004-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-"""
-
-import random, socket, string, time, re
-import uuid
-try:
- # Python 2.6
- from hashlib import md5
-except ImportError:
- # Python 2.5 and earlier
- from md5 import new as md5
-
-def _int2binstr(i,l):
- s=''
- for a in range(l):
- s=chr(i&0xFF)+s
- i>>=8
- return s
-
-def _binstr2int(s):
- i = long(0)
- for c in s:
- i = (i<<8) + ord(c)
- return i
-
-class UUID(object):
- """
- A class which represents a 16 byte integer. Stored as a 16 byte 8
- bit character string.
-
- The string version is to be of the form:
- AAAAAAAA-AAAA-BBBB-BBBB-BBBBBBCCCCCC (a 128-bit number in hex)
- where A=network address, B=timestamp, C=random.
- """
-
- NULL_STR = "00000000-0000-0000-0000-000000000000"
-
- # the UUIDREGEX_STRING is helpful for parsing UUID's in text
- hex_wildcard = r"[0-9a-fA-F]"
- word = hex_wildcard + r"{4,4}-"
- long_word = hex_wildcard + r"{8,8}-"
- very_long_word = hex_wildcard + r"{12,12}"
- UUID_REGEX_STRING = long_word + word + word + word + very_long_word
- uuid_regex = re.compile(UUID_REGEX_STRING)
-
- rand = random.Random()
- ip = ''
- try:
- ip = socket.gethostbyname(socket.gethostname())
- except(socket.gaierror, socket.error):
- # no ip address, so just default to somewhere in 10.x.x.x
- ip = '10'
- for i in range(3):
- ip += '.' + str(rand.randrange(1,254))
- hexip = ''.join(["%04x" % long(i) for i in ip.split('.')])
- lastid = ''
-
- def __init__(self, possible_uuid=None):
- """
- Initialize to first valid UUID in argument (if a string),
- or to null UUID if none found or argument is not supplied.
-
- If the argument is a UUID, the constructed object will be a copy of it.
- """
- self._bits = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- if possible_uuid is None:
- return
-
- if isinstance(possible_uuid, type(self)):
- self.set(possible_uuid)
- return
-
- uuid_match = UUID.uuid_regex.search(possible_uuid)
- if uuid_match:
- uuid_string = uuid_match.group()
- s = string.replace(uuid_string, '-', '')
- self._bits = _int2binstr(string.atol(s[:8],16),4) + \
- _int2binstr(string.atol(s[8:16],16),4) + \
- _int2binstr(string.atol(s[16:24],16),4) + \
- _int2binstr(string.atol(s[24:],16),4)
-
- def __len__(self):
- """
- Used by the len() builtin.
- """
- return 36
-
- def __nonzero__(self):
- return self._bits != "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
-
- def __str__(self):
- uuid_string = self.toString()
- return uuid_string
-
- __repr__ = __str__
-
- def __getitem__(self, index):
- return str(self)[index]
-
- def __eq__(self, other):
- if isinstance(other, (str, unicode)):
- return other == str(self)
- return self._bits == getattr(other, '_bits', '')
-
- def __ne__(self, other):
- return not self.__eq__(other)
-
- def __le__(self, other):
- return self._bits <= other._bits
-
- def __ge__(self, other):
- return self._bits >= other._bits
-
- def __lt__(self, other):
- return self._bits < other._bits
-
- def __gt__(self, other):
- return self._bits > other._bits
-
- def __hash__(self):
- return hash(self._bits)
-
- def set(self, uuid):
- self._bits = uuid._bits
-
- def setFromString(self, uuid_string):
- """
- Given a string version of a uuid, set self bits
- appropriately. Returns self.
- """
- s = string.replace(uuid_string, '-', '')
- self._bits = _int2binstr(string.atol(s[:8],16),4) + \
- _int2binstr(string.atol(s[8:16],16),4) + \
- _int2binstr(string.atol(s[16:24],16),4) + \
- _int2binstr(string.atol(s[24:],16),4)
- return self
-
- def setFromMemoryDump(self, gdb_string):
- """
- We expect to get gdb_string as four hex units. eg:
- 0x147d54db 0xc34b3f1b 0x714f989b 0x0a892fd2
- Which will be translated to:
- db547d14-1b3f4bc3-9b984f71-d22f890a
- Returns self.
- """
- s = string.replace(gdb_string, '0x', '')
- s = string.replace(s, ' ', '')
- t = ''
- for i in range(8,40,8):
- for j in range(0,8,2):
- t = t + s[i-j-2:i-j]
- self.setFromString(t)
-
- def toString(self):
- """
- Return as a string matching the LL standard
- AAAAAAAA-AAAA-BBBB-BBBB-BBBBBBCCCCCC (a 128-bit number in hex)
- where A=network address, B=timestamp, C=random.
- """
- return uuid_bits_to_string(self._bits)
-
- def getAsString(self):
- """
- Return a different string representation of the form
- AAAAAAAA-AAAABBBB-BBBBBBBB-BBCCCCCC (a 128-bit number in hex)
- where A=network address, B=timestamp, C=random.
- """
- i1 = _binstr2int(self._bits[0:4])
- i2 = _binstr2int(self._bits[4:8])
- i3 = _binstr2int(self._bits[8:12])
- i4 = _binstr2int(self._bits[12:16])
- return '%08lx-%08lx-%08lx-%08lx' % (i1,i2,i3,i4)
-
- def generate(self):
- """
- Generate a new uuid. This algorithm is slightly different
- from c++ implementation for portability reasons.
- Returns self.
- """
- m = md5()
- m.update(uuid.uuid1().bytes)
- self._bits = m.digest()
- return self
-
- def isNull(self):
- """
- Returns 1 if the uuid is null - ie, equal to default uuid.
- """
- return (self._bits == "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0")
-
- def xor(self, rhs):
- """
- xors self with rhs.
- """
- v1 = _binstr2int(self._bits[0:4]) ^ _binstr2int(rhs._bits[0:4])
- v2 = _binstr2int(self._bits[4:8]) ^ _binstr2int(rhs._bits[4:8])
- v3 = _binstr2int(self._bits[8:12]) ^ _binstr2int(rhs._bits[8:12])
- v4 = _binstr2int(self._bits[12:16]) ^ _binstr2int(rhs._bits[12:16])
- self._bits = _int2binstr(v1,4) + \
- _int2binstr(v2,4) + \
- _int2binstr(v3,4) + \
- _int2binstr(v4,4)
-
-
-# module-level null constant
-NULL = UUID()
-
-def printTranslatedMemory(four_hex_uints):
- """
- We expect to get the string as four hex units. eg:
- 0x147d54db 0xc34b3f1b 0x714f989b 0x0a892fd2
- Which will be translated to:
- db547d14-1b3f4bc3-9b984f71-d22f890a
- """
- uuid = UUID()
- uuid.setFromMemoryDump(four_hex_uints)
- print uuid.toString()
-
-def isUUID(id_str):
- """
- This function returns:
- - 1 if the string passed is a UUID
- - 0 is the string passed is not a UUID
- - None if it neither of the if's below is satisfied
- """
- if not id_str or len(id_str) < 5 or len(id_str) > 36:
- return 0
-
- if isinstance(id_str, UUID) or UUID.uuid_regex.match(id_str):
- return 1
-
- return None
-
-def isPossiblyID(id_str):
- """
- This function returns 1 if the string passed has some uuid-like
- characteristics. Otherwise returns 0.
- """
-
- is_uuid = isUUID(id_str)
- if is_uuid is not None:
- return is_uuid
-
- # build a string which matches every character.
- hex_wildcard = r"[0-9a-fA-F]"
- chars = len(id_str)
- next = min(chars, 8)
- matcher = hex_wildcard+"{"+str(next)+","+str(next)+"}"
- chars = chars - next
- if chars > 0:
- matcher = matcher + "-"
- chars = chars - 1
- for block in range(3):
- next = max(min(chars, 4), 0)
- if next:
- matcher = matcher + hex_wildcard+"{"+str(next)+","+str(next)+"}"
- chars = chars - next
- if chars > 0:
- matcher = matcher + "-"
- chars = chars - 1
- if chars > 0:
- next = min(chars, 12)
- matcher = matcher + hex_wildcard+"{"+str(next)+","+str(next)+"}"
- #print matcher
- uuid_matcher = re.compile(matcher)
- if uuid_matcher.match(id_str):
- return 1
- return 0
-
-def uuid_bits_to_string(bits):
- i1 = _binstr2int(bits[0:4])
- i2 = _binstr2int(bits[4:6])
- i3 = _binstr2int(bits[6:8])
- i4 = _binstr2int(bits[8:10])
- i5 = _binstr2int(bits[10:12])
- i6 = _binstr2int(bits[12:16])
- return '%08lx-%04lx-%04lx-%04lx-%04lx%08lx' % (i1,i2,i3,i4,i5,i6)
-
-def uuid_bits_to_uuid(bits):
- return UUID(uuid_bits_to_string(bits))
-
-
-try:
- from mulib import stacked
- stacked.NoProducer() # just to exercise stacked
-except:
- #print "Couldn't import mulib.stacked, not registering UUID converter"
- pass
-else:
- def convertUUID(uuid, req):
- req.write(str(uuid))
-
- stacked.add_producer(UUID, convertUUID, "*/*")
- stacked.add_producer(UUID, convertUUID, "text/html")
diff --git a/indra/lib/python/indra/base/metrics.py b/indra/lib/python/indra/base/metrics.py
deleted file mode 100755
index ff8380265f..0000000000
--- a/indra/lib/python/indra/base/metrics.py
+++ /dev/null
@@ -1,121 +0,0 @@
-"""\
-@file metrics.py
-@author Phoenix
-@date 2007-11-27
-@brief simple interface for logging metrics
-
-$LicenseInfo:firstyear=2007&license=mit$
-
-Copyright (c) 2007-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-"""
-
-import sys
-try:
- import syslog
-except ImportError:
- # Windows
- import sys
- class syslog(object):
- # wrap to a lame syslog for windows
- _logfp = sys.stderr
- def syslog(msg):
- _logfp.write(msg)
- if not msg.endswith('\n'):
- _logfp.write('\n')
- syslog = staticmethod(syslog)
-
-from indra.base.llsd import format_notation
-
-def record_metrics(table, stats):
- "Write a standard metrics log"
- _log("LLMETRICS", table, stats)
-
-def record_event(table, data):
- "Write a standard logmessage log"
- _log("LLLOGMESSAGE", table, data)
-
-def set_destination(dest):
- """Set the destination of metrics logs for this process.
-
- If you do not call this function prior to calling a logging
- method, that function will open sys.stdout as a destination.
- Attempts to set dest to None will throw a RuntimeError.
- @param dest a file-like object which will be the destination for logs."""
- if dest is None:
- raise RuntimeError("Attempt to unset metrics destination.")
- global _destination
- _destination = dest
-
-def destination():
- """Get the destination of the metrics logs for this process.
- Returns None if no destination is set"""
- global _destination
- return _destination
-
-class SysLogger(object):
- "A file-like object which writes to syslog."
- def __init__(self, ident='indra', logopt = None, facility = None):
- try:
- if logopt is None:
- logopt = syslog.LOG_CONS | syslog.LOG_PID
- if facility is None:
- facility = syslog.LOG_LOCAL0
- syslog.openlog(ident, logopt, facility)
- import atexit
- atexit.register(syslog.closelog)
- except AttributeError:
- # No syslog module on Windows
- pass
-
- def write(str):
- syslog.syslog(str)
- write = staticmethod(write)
-
- def flush():
- pass
- flush = staticmethod(flush)
-
-#
-# internal API
-#
-_sequence_id = 0
-_destination = None
-
-def _next_id():
- global _sequence_id
- next = _sequence_id
- _sequence_id += 1
- return next
-
-def _dest():
- global _destination
- if _destination is None:
- # this default behavior is documented in the metrics functions above.
- _destination = sys.stdout
- return _destination
-
-def _log(header, table, data):
- log_line = "%s (%d) %s %s" \
- % (header, _next_id(), table, format_notation(data))
- dest = _dest()
- dest.write(log_line)
- dest.flush()
diff --git a/indra/lib/python/indra/ipc/httputil.py b/indra/lib/python/indra/ipc/httputil.py
deleted file mode 100755
index d53f34a771..0000000000
--- a/indra/lib/python/indra/ipc/httputil.py
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/usr/bin/python
-## $LicenseInfo:firstyear=2011&license=viewerlgpl$
-## Second Life Viewer Source Code
-## Copyright (C) 2011, Linden Research, Inc.
-##
-## 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;
-## version 2.1 of the License only.
-##
-## 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
-##
-## Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
-## $/LicenseInfo$
-
-import warnings
-
-warnings.warn("indra.ipc.httputil has been deprecated; use eventlet.httpc instead", DeprecationWarning, 2)
-
-from eventlet.httpc import *
-
-
-makeConnection = make_connection
diff --git a/indra/lib/python/indra/ipc/llsdhttp.py b/indra/lib/python/indra/ipc/llsdhttp.py
deleted file mode 100755
index cbe8ee1eca..0000000000
--- a/indra/lib/python/indra/ipc/llsdhttp.py
+++ /dev/null
@@ -1,100 +0,0 @@
-"""\
-@file llsdhttp.py
-@brief Functions to ease moving llsd over http
-
-$LicenseInfo:firstyear=2006&license=mit$
-
-Copyright (c) 2006-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-"""
-
-import os.path
-import os
-import urlparse
-
-from indra.base import llsd
-
-from eventlet import httpc
-
-suite = httpc.HttpSuite(llsd.format_xml, llsd.parse, 'application/llsd+xml')
-delete = suite.delete
-delete_ = suite.delete_
-get = suite.get
-get_ = suite.get_
-head = suite.head
-head_ = suite.head_
-post = suite.post
-post_ = suite.post_
-put = suite.put
-put_ = suite.put_
-request = suite.request
-request_ = suite.request_
-
-# import every httpc error exception into our namespace for convenience
-for x in httpc.status_to_error_map.itervalues():
- globals()[x.__name__] = x
-ConnectionError = httpc.ConnectionError
-Retriable = httpc.Retriable
-
-for x in (httpc.ConnectionError,):
- globals()[x.__name__] = x
-
-
-def postFile(url, filename):
- f = open(filename)
- body = f.read()
- f.close()
- llsd_body = llsd.parse(body)
- return post_(url, llsd_body)
-
-
-# deprecated in favor of get_
-def getStatus(url, use_proxy=False):
- status, _headers, _body = get_(url, use_proxy=use_proxy)
- return status
-
-# deprecated in favor of put_
-def putStatus(url, data):
- status, _headers, _body = put_(url, data)
- return status
-
-# deprecated in favor of delete_
-def deleteStatus(url):
- status, _headers, _body = delete_(url)
- return status
-
-# deprecated in favor of post_
-def postStatus(url, data):
- status, _headers, _body = post_(url, data)
- return status
-
-
-def postFileStatus(url, filename):
- status, _headers, body = postFile(url, filename)
- return status, body
-
-
-def getFromSimulator(path, use_proxy=False):
- return get('http://' + simulatorHostAndPort + path, use_proxy=use_proxy)
-
-
-def postToSimulator(path, data=None):
- return post('http://' + simulatorHostAndPort + path, data)
diff --git a/indra/lib/python/indra/ipc/mysql_pool.py b/indra/lib/python/indra/ipc/mysql_pool.py
deleted file mode 100755
index e5855a3091..0000000000
--- a/indra/lib/python/indra/ipc/mysql_pool.py
+++ /dev/null
@@ -1,81 +0,0 @@
-"""\
-@file mysql_pool.py
-@brief Thin wrapper around eventlet.db_pool that chooses MySQLdb and Tpool.
-
-$LicenseInfo:firstyear=2007&license=mit$
-
-Copyright (c) 2007-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-"""
-
-import MySQLdb
-from eventlet import db_pool
-
-class DatabaseConnector(db_pool.DatabaseConnector):
- def __init__(self, credentials, *args, **kwargs):
- super(DatabaseConnector, self).__init__(MySQLdb, credentials,
- conn_pool=db_pool.ConnectionPool,
- *args, **kwargs)
-
- # get is extended relative to eventlet.db_pool to accept a port argument
- def get(self, host, dbname, port=3306):
- key = (host, dbname, port)
- if key not in self._databases:
- new_kwargs = self._kwargs.copy()
- new_kwargs['db'] = dbname
- new_kwargs['host'] = host
- new_kwargs['port'] = port
- new_kwargs.update(self.credentials_for(host))
- dbpool = ConnectionPool(*self._args, **new_kwargs)
- self._databases[key] = dbpool
-
- return self._databases[key]
-
-class ConnectionPool(db_pool.TpooledConnectionPool):
- """A pool which gives out saranwrapped MySQLdb connections from a pool
- """
-
- def __init__(self, *args, **kwargs):
- super(ConnectionPool, self).__init__(MySQLdb, *args, **kwargs)
-
- def get(self):
- conn = super(ConnectionPool, self).get()
- # annotate the connection object with the details on the
- # connection; this is used elsewhere to check that you haven't
- # suddenly changed databases in midstream while making a
- # series of queries on a connection.
- arg_names = ['host','user','passwd','db','port','unix_socket','conv','connect_timeout',
- 'compress', 'named_pipe', 'init_command', 'read_default_file', 'read_default_group',
- 'cursorclass', 'use_unicode', 'charset', 'sql_mode', 'client_flag', 'ssl',
- 'local_infile']
- # you could have constructed this connectionpool with a mix of
- # keyword and non-keyword arguments, but we want to annotate
- # the connection object with a dict so it's easy to check
- # against so here we are converting the list of non-keyword
- # arguments (in self._args) into a dict of keyword arguments,
- # and merging that with the actual keyword arguments
- # (self._kwargs). The arg_names variable lists the
- # constructor arguments for MySQLdb Connection objects.
- converted_kwargs = dict([ (arg_names[i], arg) for i, arg in enumerate(self._args) ])
- converted_kwargs.update(self._kwargs)
- conn.connection_parameters = converted_kwargs
- return conn
-
diff --git a/indra/lib/python/indra/ipc/russ.py b/indra/lib/python/indra/ipc/russ.py
deleted file mode 100755
index ac780f128b..0000000000
--- a/indra/lib/python/indra/ipc/russ.py
+++ /dev/null
@@ -1,165 +0,0 @@
-"""\
-@file russ.py
-@brief Recursive URL Substitution Syntax helpers
-@author Phoenix
-
-Many details on how this should work is available on the wiki:
-https://wiki.secondlife.com/wiki/Recursive_URL_Substitution_Syntax
-
-Adding features to this should be reflected in that page in the
-implementations section.
-
-$LicenseInfo:firstyear=2007&license=mit$
-
-Copyright (c) 2007-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-"""
-
-import urllib
-from indra.ipc import llsdhttp
-
-class UnbalancedBraces(Exception):
- pass
-
-class UnknownDirective(Exception):
- pass
-
-class BadDirective(Exception):
- pass
-
-def format_value_for_path(value):
- if type(value) in [list, tuple]:
- # *NOTE: treat lists as unquoted path components so that the quoting
- # doesn't get out-of-hand. This is a workaround for the fact that
- # russ always quotes, even if the data it's given is already quoted,
- # and it's not safe to simply unquote a path directly, so if we want
- # russ to substitute urls parts inside other url parts we always
- # have to do so via lists of unquoted path components.
- return '/'.join([urllib.quote(str(item)) for item in value])
- else:
- return urllib.quote(str(value))
-
-def format(format_str, context):
- """@brief Format format string according to rules for RUSS.
-@see https://osiris.lindenlab.com/mediawiki/index.php/Recursive_URL_Substitution_Syntax
-@param format_str The input string to format.
-@param context A map used for string substitutions.
-@return Returns the formatted string. If no match, the braces remain intact.
-"""
- while True:
- #print "format_str:", format_str
- all_matches = _find_sub_matches(format_str)
- if not all_matches:
- break
- substitutions = 0
- while True:
- matches = all_matches.pop()
- # we work from right to left to make sure we do not
- # invalidate positions earlier in format_str
- matches.reverse()
- for pos in matches:
- # Use index since _find_sub_matches should have raised
- # an exception, and failure to find now is an exception.
- end = format_str.index('}', pos)
- #print "directive:", format_str[pos+1:pos+5]
- if format_str[pos + 1] == '$':
- value = context[format_str[pos + 2:end]]
- if value is not None:
- value = format_value_for_path(value)
- elif format_str[pos + 1] == '%':
- value = _build_query_string(
- context.get(format_str[pos + 2:end]))
- elif format_str[pos+1:pos+5] == 'http' or format_str[pos+1:pos+5] == 'file':
- value = _fetch_url_directive(format_str[pos + 1:end])
- else:
- raise UnknownDirective, format_str[pos:end + 1]
- if value is not None:
- format_str = format_str[:pos]+str(value)+format_str[end+1:]
- substitutions += 1
-
- # If there were any substitutions at this depth, re-parse
- # since this may have revealed new things to substitute
- if substitutions:
- break
- if not all_matches:
- break
-
- # If there were no substitutions at all, and we have exhausted
- # the possible matches, bail.
- if not substitutions:
- break
- return format_str
-
-def _find_sub_matches(format_str):
- """@brief Find all of the substitution matches.
-@param format_str the RUSS conformant format string.
-@return Returns an array of depths of arrays of positional matches in input.
-"""
- depth = 0
- matches = []
- for pos in range(len(format_str)):
- if format_str[pos] == '{':
- depth += 1
- if not len(matches) == depth:
- matches.append([])
- matches[depth - 1].append(pos)
- continue
- if format_str[pos] == '}':
- depth -= 1
- continue
- if not depth == 0:
- raise UnbalancedBraces, format_str
- return matches
-
-def _build_query_string(query_dict):
- """\
- @breif given a dict, return a query string. utility wrapper for urllib.
- @param query_dict input query dict
- @returns Returns an urlencoded query string including leading '?'.
- """
- if query_dict:
- keys = query_dict.keys()
- keys.sort()
- def stringize(value):
- if type(value) in (str,unicode):
- return value
- else:
- return str(value)
- query_list = [urllib.quote(str(key)) + '=' + urllib.quote(stringize(query_dict[key])) for key in keys]
- return '?' + '&'.join(query_list)
- else:
- return ''
-
-def _fetch_url_directive(directive):
- "*FIX: This only supports GET"
- commands = directive.split('|')
- resource = llsdhttp.get(commands[0])
- if len(commands) == 3:
- resource = _walk_resource(resource, commands[2])
- return resource
-
-def _walk_resource(resource, path):
- path = path.split('/')
- for child in path:
- if not child:
- continue
- resource = resource[child]
- return resource
diff --git a/indra/lib/python/indra/ipc/servicebuilder.py b/indra/lib/python/indra/ipc/servicebuilder.py
deleted file mode 100755
index 0a0ce2b4e2..0000000000
--- a/indra/lib/python/indra/ipc/servicebuilder.py
+++ /dev/null
@@ -1,134 +0,0 @@
-"""\
-@file servicebuilder.py
-@author Phoenix
-@brief Class which will generate service urls.
-
-$LicenseInfo:firstyear=2007&license=mit$
-
-Copyright (c) 2007-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-"""
-
-from indra.base import config
-from indra.ipc import llsdhttp
-from indra.ipc import russ
-
-# *NOTE: agent presence relies on this variable existing and being current, it is a huge hack
-services_config = {}
-try:
- services_config = llsdhttp.get(config.get('services-config'))
-except:
- pass
-
-_g_builder = None
-def _builder():
- global _g_builder
- if _g_builder is None:
- _g_builder = ServiceBuilder()
- return _g_builder
-
-def build(name, context={}, **kwargs):
- """ Convenience method for using a global, singleton, service builder. Pass arguments either via a dict or via python keyword arguments, or both!
-
- Example use:
- > context = {'channel':'Second Life Release', 'version':'1.18.2.0'}
- > servicebuilder.build('version-manager-version', context)
- 'http://int.util.vaak.lindenlab.com/channel/Second%20Life%20Release/1.18.2.0'
- > servicebuilder.build('version-manager-version', channel='Second Life Release', version='1.18.2.0')
- 'http://int.util.vaak.lindenlab.com/channel/Second%20Life%20Release/1.18.2.0'
- > servicebuilder.build('version-manager-version', context, version='1.18.1.2')
- 'http://int.util.vaak.lindenlab.com/channel/Second%20Life%20Release/1.18.1.2'
- """
- global _g_builder
- if _g_builder is None:
- _g_builder = ServiceBuilder()
- return _g_builder.buildServiceURL(name, context, **kwargs)
-
-def build_path(name, context={}, **kwargs):
- context = context.copy() # shouldn't modify the caller's dictionary
- context.update(kwargs)
- return _builder().buildPath(name, context)
-
-class ServiceBuilder(object):
- def __init__(self, services_definition = services_config):
- """\
- @brief
- @brief Create a ServiceBuilder.
- @param services_definition Complete services definition, services.xml.
- """
- # no need to keep a copy of the services section of the
- # complete services definition, but it doesn't hurt much.
- self.services = services_definition['services']
- self.builders = {}
- for service in self.services:
- service_builder = service.get('service-builder')
- if not service_builder:
- continue
- if isinstance(service_builder, dict):
- # We will be constructing several builders
- for name, builder in service_builder.iteritems():
- full_builder_name = service['name'] + '-' + name
- self.builders[full_builder_name] = builder
- else:
- self.builders[service['name']] = service_builder
-
- def buildPath(self, name, context):
- """\
- @brief given the environment on construction, return a service path.
- @param name The name of the service.
- @param context A dict of name value lookups for the service.
- @returns Returns the
- """
- return russ.format(self.builders[name], context)
-
- def buildServiceURL(self, name, context={}, **kwargs):
- """\
- @brief given the environment on construction, return a service URL.
- @param name The name of the service.
- @param context A dict of name value lookups for the service.
- @param kwargs Any keyword arguments are treated as members of the
- context, this allows you to be all 31337 by writing shit like:
- servicebuilder.build('name', param=value)
- @returns Returns the
- """
- context = context.copy() # shouldn't modify the caller's dictionary
- context.update(kwargs)
- base_url = config.get('services-base-url')
- svc_path = russ.format(self.builders[name], context)
- return base_url + svc_path
-
-
-def on_in(query_name, host_key, schema_key):
- """\
- @brief Constructs an on/in snippet (for running named queries)
- from a schema name and two keys referencing values stored in
- indra.xml.
-
- @param query_name Name of the query.
- @param host_key Logical name of destination host. Will be
- looked up in indra.xml.
- @param schema_key Logical name of destination schema. Will
- be looked up in indra.xml.
- """
- return "on/config:%s/in/config:%s/%s" % (host_key.strip('/'),
- schema_key.strip('/'),
- query_name.lstrip('/'))
-
diff --git a/indra/lib/python/indra/ipc/siesta.py b/indra/lib/python/indra/ipc/siesta.py
deleted file mode 100755
index d867e71537..0000000000
--- a/indra/lib/python/indra/ipc/siesta.py
+++ /dev/null
@@ -1,468 +0,0 @@
-"""\
-@file siesta.py
-@brief A tiny llsd based RESTful web services framework
-
-$LicenseInfo:firstyear=2008&license=mit$
-
-Copyright (c) 2008, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-"""
-
-from indra.base import config
-from indra.base import llsd
-from webob import exc
-import webob
-import re, socket
-
-try:
- from cStringIO import StringIO
-except ImportError:
- from StringIO import StringIO
-
-try:
- import cjson
- json_decode = cjson.decode
- json_encode = cjson.encode
- JsonDecodeError = cjson.DecodeError
- JsonEncodeError = cjson.EncodeError
-except ImportError:
- import simplejson
- json_decode = simplejson.loads
- json_encode = simplejson.dumps
- JsonDecodeError = ValueError
- JsonEncodeError = TypeError
-
-
-llsd_parsers = {
- 'application/json': json_decode,
- llsd.BINARY_MIME_TYPE: llsd.parse_binary,
- 'application/llsd+notation': llsd.parse_notation,
- llsd.XML_MIME_TYPE: llsd.parse_xml,
- 'application/xml': llsd.parse_xml,
- }
-
-
-def mime_type(content_type):
- '''Given a Content-Type header, return only the MIME type.'''
-
- return content_type.split(';', 1)[0].strip().lower()
-
-class BodyLLSD(object):
- '''Give a webob Request or Response an llsd based "content" property.
-
- Getting the content property parses the body, and caches the result.
-
- Setting the content property formats a payload, and the body property
- is set.'''
-
- def _llsd__get(self):
- '''Get, set, or delete the LLSD value stored in this object.'''
-
- try:
- return self._llsd
- except AttributeError:
- if not self.body:
- raise AttributeError('No llsd attribute has been set')
- else:
- mtype = mime_type(self.content_type)
- try:
- parser = llsd_parsers[mtype]
- except KeyError:
- raise exc.HTTPUnsupportedMediaType(
- 'Content type %s not supported' % mtype).exception
- try:
- self._llsd = parser(self.body)
- except (llsd.LLSDParseError, JsonDecodeError, TypeError), err:
- raise exc.HTTPBadRequest(
- 'Could not parse body: %r' % err.args).exception
- return self._llsd
-
- def _llsd__set(self, val):
- req = getattr(self, 'request', None)
- if req is not None:
- formatter, ctype = formatter_for_request(req)
- self.content_type = ctype
- else:
- formatter, ctype = formatter_for_mime_type(
- mime_type(self.content_type))
- self.body = formatter(val)
-
- def _llsd__del(self):
- if hasattr(self, '_llsd'):
- del self._llsd
-
- content = property(_llsd__get, _llsd__set, _llsd__del)
-
-
-class Response(webob.Response, BodyLLSD):
- '''Response class with LLSD support.
-
- A sensible default content type is used.
-
- Setting the llsd property also sets the body. Getting the llsd
- property parses the body if necessary.
-
- If you set the body property directly, the llsd property will be
- deleted.'''
-
- default_content_type = 'application/llsd+xml'
-
- def _body__set(self, body):
- if hasattr(self, '_llsd'):
- del self._llsd
- super(Response, self)._body__set(body)
-
- def cache_forever(self):
- self.cache_expires(86400 * 365)
-
- body = property(webob.Response._body__get, _body__set,
- webob.Response._body__del,
- webob.Response._body__get.__doc__)
-
-
-class Request(webob.Request, BodyLLSD):
- '''Request class with LLSD support.
-
- Sensible content type and accept headers are used by default.
-
- Setting the content property also sets the body. Getting the content
- property parses the body if necessary.
-
- If you set the body property directly, the content property will be
- deleted.'''
-
- default_content_type = 'application/llsd+xml'
- default_accept = ('application/llsd+xml; q=0.5, '
- 'application/llsd+notation; q=0.3, '
- 'application/llsd+binary; q=0.2, '
- 'application/xml; q=0.1, '
- 'application/json; q=0.0')
-
- def __init__(self, environ=None, *args, **kwargs):
- if environ is None:
- environ = {}
- else:
- environ = environ.copy()
- if 'CONTENT_TYPE' not in environ:
- environ['CONTENT_TYPE'] = self.default_content_type
- if 'HTTP_ACCEPT' not in environ:
- environ['HTTP_ACCEPT'] = self.default_accept
- super(Request, self).__init__(environ, *args, **kwargs)
-
- def _body__set(self, body):
- if hasattr(self, '_llsd'):
- del self._llsd
- super(Request, self)._body__set(body)
-
- def path_urljoin(self, *parts):
- return '/'.join([path_url.rstrip('/')] + list(parts))
-
- body = property(webob.Request._body__get, _body__set,
- webob.Request._body__del, webob.Request._body__get.__doc__)
-
- def create_response(self, content=None, status='200 OK',
- conditional_response=webob.NoDefault):
- resp = self.ResponseClass(status=status, request=self,
- conditional_response=conditional_response)
- resp.content = content
- return resp
-
- def curl(self):
- '''Create and fill out a pycurl easy object from this request.'''
-
- import pycurl
- c = pycurl.Curl()
- c.setopt(pycurl.URL, self.url())
- if self.headers:
- c.setopt(pycurl.HTTPHEADER,
- ['%s: %s' % (k, self.headers[k]) for k in self.headers])
- c.setopt(pycurl.FOLLOWLOCATION, True)
- c.setopt(pycurl.AUTOREFERER, True)
- c.setopt(pycurl.MAXREDIRS, 16)
- c.setopt(pycurl.NOSIGNAL, True)
- c.setopt(pycurl.READFUNCTION, self.body_file.read)
- c.setopt(pycurl.SSL_VERIFYHOST, 2)
-
- if self.method == 'POST':
- c.setopt(pycurl.POST, True)
- post301 = getattr(pycurl, 'POST301', None)
- if post301 is not None:
- # Added in libcurl 7.17.1.
- c.setopt(post301, True)
- elif self.method == 'PUT':
- c.setopt(pycurl.PUT, True)
- elif self.method != 'GET':
- c.setopt(pycurl.CUSTOMREQUEST, self.method)
- return c
-
-Request.ResponseClass = Response
-Response.RequestClass = Request
-
-
-llsd_formatters = {
- 'application/json': json_encode,
- 'application/llsd+binary': llsd.format_binary,
- 'application/llsd+notation': llsd.format_notation,
- 'application/llsd+xml': llsd.format_xml,
- 'application/xml': llsd.format_xml,
- }
-
-formatter_qualities = (
- ('application/llsd+xml', 1.0),
- ('application/llsd+notation', 0.5),
- ('application/llsd+binary', 0.4),
- ('application/xml', 0.3),
- ('application/json', 0.2),
- )
-
-def formatter_for_mime_type(mime_type):
- '''Return a formatter that encodes to the given MIME type.
-
- The result is a pair of function and MIME type.'''
- try:
- return llsd_formatters[mime_type], mime_type
- except KeyError:
- raise exc.HTTPInternalServerError(
- 'Could not use MIME type %r to format response' %
- mime_type).exception
-
-
-def formatter_for_request(req):
- '''Return a formatter that encodes to the preferred type of the client.
-
- The result is a pair of function and actual MIME type.'''
- ctype = req.accept.best_match(formatter_qualities)
- try:
- return llsd_formatters[ctype], ctype
- except KeyError:
- raise exc.HTTPNotAcceptable().exception
-
-
-def wsgi_adapter(func, environ, start_response):
- '''Adapt a Siesta callable to act as a WSGI application.'''
- # Process the request as appropriate.
- try:
- req = Request(environ)
- #print req.urlvars
- resp = func(req, **req.urlvars)
- if not isinstance(resp, webob.Response):
- try:
- formatter, ctype = formatter_for_request(req)
- resp = req.ResponseClass(formatter(resp), content_type=ctype)
- resp._llsd = resp
- except (JsonEncodeError, TypeError), err:
- resp = exc.HTTPInternalServerError(
- detail='Could not format response')
- except exc.HTTPException, e:
- resp = e
- except socket.error, e:
- resp = exc.HTTPInternalServerError(detail=e.args[1])
- return resp(environ, start_response)
-
-
-def llsd_callable(func):
- '''Turn a callable into a Siesta application.'''
-
- def replacement(environ, start_response):
- return wsgi_adapter(func, environ, start_response)
-
- return replacement
-
-
-def llsd_method(http_method, func):
- def replacement(environ, start_response):
- if environ['REQUEST_METHOD'] == http_method:
- return wsgi_adapter(func, environ, start_response)
- return exc.HTTPMethodNotAllowed()(environ, start_response)
-
- return replacement
-
-
-http11_methods = 'OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT'.split()
-http11_methods.sort()
-
-def llsd_class(cls):
- '''Turn a class into a Siesta application.
-
- A new instance is created for each request. A HTTP method FOO is
- turned into a call to the handle_foo method of the instance.'''
-
- def foo(req, **kwargs):
- instance = cls()
- method = req.method.lower()
- try:
- handler = getattr(instance, 'handle_' + method)
- except AttributeError:
- allowed = [m for m in http11_methods
- if hasattr(instance, 'handle_' + m.lower())]
- raise exc.HTTPMethodNotAllowed(
- headers={'Allow': ', '.join(allowed)}).exception
- #print "kwargs: ", kwargs
- return handler(req, **kwargs)
-
- def replacement(environ, start_response):
- return wsgi_adapter(foo, environ, start_response)
-
- return replacement
-
-
-def curl(reqs):
- import pycurl
-
- m = pycurl.CurlMulti()
- curls = [r.curl() for r in reqs]
- io = {}
- for c in curls:
- fp = StringIO()
- hdr = StringIO()
- c.setopt(pycurl.WRITEFUNCTION, fp.write)
- c.setopt(pycurl.HEADERFUNCTION, hdr.write)
- io[id(c)] = fp, hdr
- m.handles = curls
- try:
- while True:
- ret, num_handles = m.perform()
- if ret != pycurl.E_CALL_MULTI_PERFORM:
- break
- finally:
- m.close()
-
- for req, c in zip(reqs, curls):
- fp, hdr = io[id(c)]
- hdr.seek(0)
- status = hdr.readline().rstrip()
- headers = []
- name, values = None, None
-
- # XXX We don't currently handle bogus header data.
-
- for line in hdr.readlines():
- if not line[0].isspace():
- if name:
- headers.append((name, ' '.join(values)))
- name, value = line.strip().split(':', 1)
- value = [value]
- else:
- values.append(line.strip())
- if name:
- headers.append((name, ' '.join(values)))
-
- resp = c.ResponseClass(fp.getvalue(), status, headers, request=req)
-
-
-route_re = re.compile(r'''
- \{ # exact character "{"
- (\w*) # "config" or variable (restricted to a-z, 0-9, _)
- (?:([:~])([^}]+))? # optional :type or ~regex part
- \} # exact character "}"
- ''', re.VERBOSE)
-
-predefined_regexps = {
- 'uuid': r'[a-f0-9][a-f0-9-]{31,35}',
- 'int': r'\d+',
- 'host': r'[a-z0-9][a-z0-9\-\.]*',
- }
-
-def compile_route(route):
- fp = StringIO()
- last_pos = 0
- for match in route_re.finditer(route):
- #print "matches: ", match.groups()
- fp.write(re.escape(route[last_pos:match.start()]))
- var_name = match.group(1)
- sep = match.group(2)
- expr = match.group(3)
- if var_name == 'config':
- expr = re.escape(str(config.get(var_name)))
- else:
- if expr:
- if sep == ':':
- expr = predefined_regexps[expr]
- # otherwise, treat what follows '~' as a regexp
- else:
- expr = '[^/]+'
- if var_name != '':
- expr = '(?P<%s>%s)' % (var_name, expr)
- else:
- expr = '(%s)' % (expr,)
- fp.write(expr)
- last_pos = match.end()
- fp.write(re.escape(route[last_pos:]))
- compiled_route = '^%s$' % fp.getvalue()
- #print route, "->", compiled_route
- return compiled_route
-
-class Router(object):
- '''WSGI routing class. Parses a URL and hands off a request to
- some other WSGI application. If no suitable application is found,
- responds with a 404.'''
-
- def __init__(self):
- self._new_routes = []
- self._routes = []
- self._paths = []
-
- def add(self, route, app, methods=None):
- self._new_routes.append((route, app, methods))
-
- def _create_routes(self):
- for route, app, methods in self._new_routes:
- self._paths.append(route)
- self._routes.append(
- (re.compile(compile_route(route)),
- app,
- methods and dict.fromkeys(methods)))
- self._new_routes = []
-
- def __call__(self, environ, start_response):
- # load up the config from the config file. Only needs to be
- # done once per interpreter. This is the entry point of all
- # siesta applications, so this is where we trap it.
- _conf = config.get_config()
- if _conf is None:
- import os.path
- fname = os.path.join(
- environ.get('ll.config_dir', '/local/linden/etc'),
- 'indra.xml')
- config.load(fname)
-
- # proceed with handling the request
- self._create_routes()
- path_info = environ['PATH_INFO']
- request_method = environ['REQUEST_METHOD']
- allowed = []
- for regex, app, methods in self._routes:
- m = regex.match(path_info)
- if m:
- #print "groupdict:",m.groupdict()
- if not methods or request_method in methods:
- environ['paste.urlvars'] = m.groupdict()
- return app(environ, start_response)
- else:
- allowed += methods
- if allowed:
- allowed = dict.fromkeys(allows).keys()
- allowed.sort()
- resp = exc.HTTPMethodNotAllowed(
- headers={'Allow': ', '.join(allowed)})
- else:
- resp = exc.HTTPNotFound()
- return resp(environ, start_response)
diff --git a/indra/lib/python/indra/ipc/siesta_test.py b/indra/lib/python/indra/ipc/siesta_test.py
deleted file mode 100755
index a35eed2460..0000000000
--- a/indra/lib/python/indra/ipc/siesta_test.py
+++ /dev/null
@@ -1,235 +0,0 @@
-#!/usr/bin/python
-## $LicenseInfo:firstyear=2011&license=viewerlgpl$
-## Second Life Viewer Source Code
-## Copyright (C) 2011, Linden Research, Inc.
-##
-## 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;
-## version 2.1 of the License only.
-##
-## 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
-##
-## Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
-## $/LicenseInfo$
-from indra.base import llsd, lluuid
-from indra.ipc import siesta
-import datetime, math, unittest
-from webob import exc
-
-
-class ClassApp(object):
- def handle_get(self, req):
- pass
-
- def handle_post(self, req):
- return req.llsd
-
-
-def callable_app(req):
- if req.method == 'UNDERPANTS':
- raise exc.HTTPMethodNotAllowed()
- elif req.method == 'GET':
- return None
- return req.llsd
-
-
-class TestBase:
- def test_basic_get(self):
- req = siesta.Request.blank('/')
- self.assertEquals(req.get_response(self.server).body,
- llsd.format_xml(None))
-
- def test_bad_method(self):
- req = siesta.Request.blank('/')
- req.environ['REQUEST_METHOD'] = 'UNDERPANTS'
- self.assertEquals(req.get_response(self.server).status_int,
- exc.HTTPMethodNotAllowed.code)
-
- json_safe = {
- 'none': None,
- 'bool_true': True,
- 'bool_false': False,
- 'int_zero': 0,
- 'int_max': 2147483647,
- 'int_min': -2147483648,
- 'long_zero': 0,
- 'long_max': 2147483647L,
- 'long_min': -2147483648L,
- 'float_zero': 0,
- 'float': math.pi,
- 'float_huge': 3.14159265358979323846e299,
- 'str_empty': '',
- 'str': 'foo',
- u'unic\u1e51de_empty': u'',
- u'unic\u1e51de': u'\u1e4exx\u10480',
- }
- json_safe['array'] = json_safe.values()
- json_safe['tuple'] = tuple(json_safe.values())
- json_safe['dict'] = json_safe.copy()
-
- json_unsafe = {
- 'uuid_empty': lluuid.UUID(),
- 'uuid_full': lluuid.UUID('dc61ab0530200d7554d23510559102c1a98aab1b'),
- 'binary_empty': llsd.binary(),
- 'binary': llsd.binary('f\0\xff'),
- 'uri_empty': llsd.uri(),
- 'uri': llsd.uri('http://www.secondlife.com/'),
- 'datetime_empty': datetime.datetime(1970,1,1),
- 'datetime': datetime.datetime(1999,9,9,9,9,9),
- }
- json_unsafe.update(json_safe)
- json_unsafe['array'] = json_unsafe.values()
- json_unsafe['tuple'] = tuple(json_unsafe.values())
- json_unsafe['dict'] = json_unsafe.copy()
- json_unsafe['iter'] = iter(json_unsafe.values())
-
- def _test_client_content_type_good(self, content_type, ll):
- def run(ll):
- req = siesta.Request.blank('/')
- req.environ['REQUEST_METHOD'] = 'POST'
- req.content_type = content_type
- req.llsd = ll
- req.accept = content_type
- resp = req.get_response(self.server)
- self.assertEquals(resp.status_int, 200)
- return req, resp
-
- if False and isinstance(ll, dict):
- def fixup(v):
- if isinstance(v, float):
- return '%.5f' % v
- if isinstance(v, long):
- return int(v)
- if isinstance(v, (llsd.binary, llsd.uri)):
- return v
- if isinstance(v, (tuple, list)):
- return [fixup(i) for i in v]
- if isinstance(v, dict):
- return dict([(k, fixup(i)) for k, i in v.iteritems()])
- return v
- for k, v in ll.iteritems():
- l = [k, v]
- req, resp = run(l)
- self.assertEquals(fixup(resp.llsd), fixup(l))
-
- run(ll)
-
- def test_client_content_type_json_good(self):
- self._test_client_content_type_good('application/json', self.json_safe)
-
- def test_client_content_type_llsd_xml_good(self):
- self._test_client_content_type_good('application/llsd+xml',
- self.json_unsafe)
-
- def test_client_content_type_llsd_notation_good(self):
- self._test_client_content_type_good('application/llsd+notation',
- self.json_unsafe)
-
- def test_client_content_type_llsd_binary_good(self):
- self._test_client_content_type_good('application/llsd+binary',
- self.json_unsafe)
-
- def test_client_content_type_xml_good(self):
- self._test_client_content_type_good('application/xml',
- self.json_unsafe)
-
- def _test_client_content_type_bad(self, content_type):
- req = siesta.Request.blank('/')
- req.environ['REQUEST_METHOD'] = 'POST'
- req.body = '\0invalid nonsense under all encodings'
- req.content_type = content_type
- self.assertEquals(req.get_response(self.server).status_int,
- exc.HTTPBadRequest.code)
-
- def test_client_content_type_json_bad(self):
- self._test_client_content_type_bad('application/json')
-
- def test_client_content_type_llsd_xml_bad(self):
- self._test_client_content_type_bad('application/llsd+xml')
-
- def test_client_content_type_llsd_notation_bad(self):
- self._test_client_content_type_bad('application/llsd+notation')
-
- def test_client_content_type_llsd_binary_bad(self):
- self._test_client_content_type_bad('application/llsd+binary')
-
- def test_client_content_type_xml_bad(self):
- self._test_client_content_type_bad('application/xml')
-
- def test_client_content_type_bad(self):
- req = siesta.Request.blank('/')
- req.environ['REQUEST_METHOD'] = 'POST'
- req.body = 'XXX'
- req.content_type = 'application/nonsense'
- self.assertEquals(req.get_response(self.server).status_int,
- exc.HTTPUnsupportedMediaType.code)
-
- def test_request_default_content_type(self):
- req = siesta.Request.blank('/')
- self.assertEquals(req.content_type, req.default_content_type)
-
- def test_request_default_accept(self):
- req = siesta.Request.blank('/')
- from webob import acceptparse
- self.assertEquals(str(req.accept).replace(' ', ''),
- req.default_accept.replace(' ', ''))
-
- def test_request_llsd_auto_body(self):
- req = siesta.Request.blank('/')
- req.llsd = {'a': 2}
- self.assertEquals(req.body, ''
- 'a2')
-
- def test_request_llsd_mod_body_changes_llsd(self):
- req = siesta.Request.blank('/')
- req.llsd = {'a': 2}
- req.body = '1337'
- self.assertEquals(req.llsd, 1337)
-
- def test_request_bad_llsd_fails(self):
- def crashme(ctype):
- def boom():
- class foo(object): pass
- req = siesta.Request.blank('/')
- req.content_type = ctype
- req.llsd = foo()
- for mime_type in siesta.llsd_parsers:
- self.assertRaises(TypeError, crashme(mime_type))
-
-
-class ClassServer(TestBase, unittest.TestCase):
- def __init__(self, *args, **kwargs):
- unittest.TestCase.__init__(self, *args, **kwargs)
- self.server = siesta.llsd_class(ClassApp)
-
-
-class CallableServer(TestBase, unittest.TestCase):
- def __init__(self, *args, **kwargs):
- unittest.TestCase.__init__(self, *args, **kwargs)
- self.server = siesta.llsd_callable(callable_app)
-
-
-class RouterServer(unittest.TestCase):
- def test_router(self):
- def foo(req, quux):
- print quux
-
- r = siesta.Router()
- r.add('/foo/{quux:int}', siesta.llsd_callable(foo), methods=['GET'])
- req = siesta.Request.blank('/foo/33')
- req.get_response(r)
-
- req = siesta.Request.blank('/foo/bar')
- self.assertEquals(req.get_response(r).status_int,
- exc.HTTPNotFound.code)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/indra/lib/python/indra/ipc/webdav.py b/indra/lib/python/indra/ipc/webdav.py
deleted file mode 100755
index 98b8499b6a..0000000000
--- a/indra/lib/python/indra/ipc/webdav.py
+++ /dev/null
@@ -1,597 +0,0 @@
-"""
-@file webdav.py
-@brief Classes to make manipulation of a webdav store easier.
-
-$LicenseInfo:firstyear=2007&license=mit$
-
-Copyright (c) 2007-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-"""
-
-import sys, os, httplib, urlparse
-import socket, time
-import xml.dom.minidom
-import syslog
-# import signal
-
-__revision__ = '0'
-
-dav_debug = False
-
-
-# def urlsafe_b64decode (enc):
-# return base64.decodestring (enc.replace ('_', '/').replace ('-', '+'))
-
-# def urlsafe_b64encode (str):
-# return base64.encodestring (str).replace ('+', '-').replace ('/', '_')
-
-
-class DAVError (Exception):
- """ Base class for exceptions in this module. """
- def __init__ (self, status=0, message='', body='', details=''):
- self.status = status
- self.message = message
- self.body = body
- self.details = details
- Exception.__init__ (self, '%d:%s:%s%s' % (self.status, self.message,
- self.body, self.details))
-
- def print_to_stderr (self):
- """ print_to_stderr docstring """
- print >> sys.stderr, str (self.status) + ' ' + self.message
- print >> sys.stderr, str (self.details)
-
-
-class Timeout (Exception):
- """ Timeout docstring """
- def __init__ (self, arg=''):
- Exception.__init__ (self, arg)
-
-
-def alarm_handler (signum, frame):
- """ alarm_handler docstring """
- raise Timeout ('caught alarm')
-
-
-class WebDAV:
- """ WebDAV docstring """
- def __init__ (self, url, proxy=None, retries_before_fail=6):
- self.init_url = url
- self.init_proxy = proxy
- self.retries_before_fail = retries_before_fail
- url_parsed = urlparse.urlsplit (url)
-
- self.top_path = url_parsed[ 2 ]
- # make sure top_path has a trailing /
- if self.top_path == None or self.top_path == '':
- self.top_path = '/'
- elif len (self.top_path) > 1 and self.top_path[-1:] != '/':
- self.top_path += '/'
-
- if dav_debug:
- syslog.syslog ('new WebDAV %s : %s' % (str (url), str (proxy)))
-
- if proxy:
- proxy_parsed = urlparse.urlsplit (proxy)
- self.host_header = url_parsed[ 1 ]
- host_and_port = proxy_parsed[ 1 ].split (':')
- self.host = host_and_port[ 0 ]
- if len (host_and_port) > 1:
- self.port = int(host_and_port[ 1 ])
- else:
- self.port = 80
- else: # no proxy
- host_and_port = url_parsed[ 1 ].split (':')
- self.host_header = None
- self.host = host_and_port[ 0 ]
- if len (host_and_port) > 1:
- self.port = int(host_and_port[ 1 ])
- else:
- self.port = 80
-
- self.connection = False
- self.connect ()
-
-
- def log (self, msg, depth=0):
- """ log docstring """
- if dav_debug and depth == 0:
- host = str (self.init_url)
- if host == 'http://int.tuco.lindenlab.com:80/asset/':
- host = 'tuco'
- if host == 'http://harriet.lindenlab.com/asset-keep/':
- host = 'harriet/asset-keep'
- if host == 'http://harriet.lindenlab.com/asset-flag/':
- host = 'harriet/asset-flag'
- if host == 'http://harriet.lindenlab.com/asset/':
- host = 'harriet/asset'
- if host == 'http://ozzy.lindenlab.com/asset/':
- host = 'ozzy/asset'
- if host == 'http://station11.lindenlab.com:12041/:':
- host = 'station11:12041'
- proxy = str (self.init_proxy)
- if proxy == 'None':
- proxy = ''
- if proxy == 'http://int.tuco.lindenlab.com:3128/':
- proxy = 'tuco'
- syslog.syslog ('WebDAV (%s:%s) %s' % (host, proxy, str (msg)))
-
-
- def connect (self):
- """ connect docstring """
- self.log ('connect')
- self.connection = httplib.HTTPConnection (self.host, self.port)
-
- def __err (self, response, details):
- """ __err docstring """
- raise DAVError (response.status, response.reason, response.read (),
- str (self.init_url) + ':' + \
- str (self.init_proxy) + ':' + str (details))
-
- def request (self, method, path, body=None, headers=None,
- read_all=True, body_hook = None, recurse=0, allow_cache=True):
- """ request docstring """
- # self.log ('request %s %s' % (method, path))
- if headers == None:
- headers = {}
- if not allow_cache:
- headers['Pragma'] = 'no-cache'
- headers['cache-control'] = 'no-cache'
- try:
- if method.lower () != 'purge':
- if path.startswith ('/'):
- path = path[1:]
- if self.host_header: # use proxy
- headers[ 'host' ] = self.host_header
- fullpath = 'http://%s%s%s' % (self.host_header,
- self.top_path, path)
- else: # no proxy
- fullpath = self.top_path + path
- else:
- fullpath = path
-
- self.connection.request (method, fullpath, body, headers)
- if body_hook:
- body_hook ()
-
- # signal.signal (signal.SIGALRM, alarm_handler)
- # try:
- # signal.alarm (120)
- # signal.alarm (0)
- # except Timeout, e:
- # if recurse < 6:
- # return self.retry_request (method, path, body, headers,
- # read_all, body_hook, recurse)
- # else:
- # raise DAVError (0, 'timeout', self.host,
- # (method, path, body, headers, recurse))
-
- response = self.connection.getresponse ()
-
- if read_all:
- while len (response.read (1024)) > 0:
- pass
- if (response.status == 500 or \
- response.status == 503 or \
- response.status == 403) and \
- recurse < self.retries_before_fail:
- return self.retry_request (method, path, body, headers,
- read_all, body_hook, recurse)
- return response
- except (httplib.ResponseNotReady,
- httplib.BadStatusLine,
- socket.error):
- # if the server hangs up on us (keepalive off, broken pipe),
- # we need to reconnect and try again.
- if recurse < self.retries_before_fail:
- return self.retry_request (method, path, body, headers,
- read_all, body_hook, recurse)
- raise DAVError (0, 'reconnect failed', self.host,
- (method, path, body, headers, recurse))
-
-
- def retry_request (self, method, path, body, headers,
- read_all, body_hook, recurse):
- """ retry_request docstring """
- time.sleep (10.0 * recurse)
- self.connect ()
- return self.request (method, path, body, headers,
- read_all, body_hook, recurse+1)
-
-
-
- def propfind (self, path, body=None, depth=1):
- """ propfind docstring """
- # self.log ('propfind %s' % path)
- headers = {'Content-Type':'text/xml; charset="utf-8"',
- 'Depth':str(depth)}
- response = self.request ('PROPFIND', path, body, headers, False)
- if response.status == 207:
- return response # Multi-Status
- self.__err (response, ('PROPFIND', path, body, headers, 0))
-
-
- def purge (self, path):
- """ issue a squid purge command """
- headers = {'Accept':'*/*'}
- response = self.request ('PURGE', path, None, headers)
- if response.status == 200 or response.status == 404:
- # 200 if it was purge, 404 if it wasn't there.
- return response
- self.__err (response, ('PURGE', path, None, headers))
-
-
- def get_file_size (self, path):
- """
- Use propfind to ask a webdav server what the size of
- a file is. If used on a directory (collection) return 0
- """
- self.log ('get_file_size %s' % path)
- # "getcontentlength" property
- # 8.1.1 Example - Retrieving Named Properties
- # http://docs.python.org/lib/module-xml.dom.html
- nsurl = 'http://apache.org/dav/props/'
- doc = xml.dom.minidom.Document ()
- propfind_element = doc.createElementNS (nsurl, "D:propfind")
- propfind_element.setAttributeNS (nsurl, 'xmlns:D', 'DAV:')
- doc.appendChild (propfind_element)
- prop_element = doc.createElementNS (nsurl, "D:prop")
- propfind_element.appendChild (prop_element)
- con_len_element = doc.createElementNS (nsurl, "D:getcontentlength")
- prop_element.appendChild (con_len_element)
-
- response = self.propfind (path, doc.toxml ())
- doc.unlink ()
-
- resp_doc = xml.dom.minidom.parseString (response.read ())
- cln = resp_doc.getElementsByTagNameNS ('DAV:','getcontentlength')[ 0 ]
- try:
- content_length = int (cln.childNodes[ 0 ].nodeValue)
- except IndexError:
- return 0
- resp_doc.unlink ()
- return content_length
-
-
- def file_exists (self, path):
- """
- do an http head on the given file. return True if it succeeds
- """
- self.log ('file_exists %s' % path)
- expect_gzip = path.endswith ('.gz')
- response = self.request ('HEAD', path)
- got_gzip = response.getheader ('Content-Encoding', '').strip ()
- if got_gzip.lower () == 'x-gzip' and expect_gzip == False:
- # the asset server fakes us out if we ask for the non-gzipped
- # version of an asset, but the server has the gzipped version.
- return False
- return response.status == 200
-
-
- def mkdir (self, path):
- """ mkdir docstring """
- self.log ('mkdir %s' % path)
- headers = {}
- response = self.request ('MKCOL', path, None, headers)
- if response.status == 201:
- return # success
- if response.status == 405:
- return # directory already existed?
- self.__err (response, ('MKCOL', path, None, headers, 0))
-
-
- def delete (self, path):
- """ delete docstring """
- self.log ('delete %s' % path)
- headers = {'Depth':'infinity'} # collections require infinity
- response = self.request ('DELETE', path, None, headers)
- if response.status == 204:
- return # no content
- if response.status == 404:
- return # hmm
- self.__err (response, ('DELETE', path, None, headers, 0))
-
-
- def list_directory (self, path, dir_filter=None, allow_cache=True,
- minimum_cache_time=False):
- """
- Request an http directory listing and parse the filenames out of lines
- like: ' X'. If a filter function is provided,
- only return filenames that the filter returns True for.
-
- This is sort of grody, but it seems faster than other ways of getting
- this information from an isilon.
- """
- self.log ('list_directory %s' % path)
-
- def try_match (lline, before, after):
- """ try_match docstring """
- try:
- blen = len (before)
- asset_start_index = lline.index (before)
- asset_end_index = lline.index (after, asset_start_index + blen)
- asset = line[ asset_start_index + blen : asset_end_index ]
-
- if not dir_filter or dir_filter (asset):
- return [ asset ]
- return []
- except ValueError:
- return []
-
- if len (path) > 0 and path[-1:] != '/':
- path += '/'
-
- response = self.request ('GET', path, None, {}, False,
- allow_cache=allow_cache)
-
- if allow_cache and minimum_cache_time: # XXX
- print response.getheader ('Date')
- # s = "2005-12-06T12:13:14"
- # from datetime import datetime
- # from time import strptime
- # datetime(*strptime(s, "%Y-%m-%dT%H:%M:%S")[0:6])
- # datetime.datetime(2005, 12, 6, 12, 13, 14)
-
- if response.status != 200:
- self.__err (response, ('GET', path, None, {}, 0))
- assets = []
- for line in response.read ().split ('\n'):
- lline = line.lower ()
- if lline.find ("parent directory") == -1:
- # isilon file
- assets += try_match (lline, ' ')
- # apache dir
- assets += try_match (lline, 'alt="[dir]"> ')
- # apache file
- assets += try_match (lline, 'alt="[ ]"> ')
- return assets
-
-
- def __tmp_filename (self, path_and_file):
- """ __tmp_filename docstring """
- head, tail = os.path.split (path_and_file)
- if head != '':
- return head + '/.' + tail + '.' + str (os.getpid ())
- else:
- return head + '.' + tail + '.' + str (os.getpid ())
-
-
- def __put__ (self, filesize, body_hook, remotefile):
- """ __put__ docstring """
- headers = {'Content-Length' : str (filesize)}
- remotefile_tmp = self.__tmp_filename (remotefile)
- response = self.request ('PUT', remotefile_tmp, None,
- headers, True, body_hook)
- if not response.status in (201, 204): # created, no content
- self.__err (response, ('PUT', remotefile, None, headers, 0))
- if filesize != self.get_file_size (remotefile_tmp):
- try:
- self.delete (remotefile_tmp)
- except:
- pass
- raise DAVError (0, 'tmp upload error', remotefile_tmp)
- # move the file to its final location
- try:
- self.rename (remotefile_tmp, remotefile)
- except DAVError, exc:
- if exc.status == 403: # try to clean up the tmp file
- try:
- self.delete (remotefile_tmp)
- except:
- pass
- raise
- if filesize != self.get_file_size (remotefile):
- raise DAVError (0, 'file upload error', str (remotefile_tmp))
-
-
- def put_string (self, strng, remotefile):
- """ put_string docstring """
- self.log ('put_string %d -> %s' % (len (strng), remotefile))
- filesize = len (strng)
- def body_hook ():
- """ body_hook docstring """
- self.connection.send (strng)
- self.__put__ (filesize, body_hook, remotefile)
-
-
- def put_file (self, localfile, remotefile):
- """
- Send a local file to a remote webdav store. First, upload to
- a temporary filename. Next make sure the file is the size we
- expected. Next, move the file to its final location. Next,
- check the file size at the final location.
- """
- self.log ('put_file %s -> %s' % (localfile, remotefile))
- filesize = os.path.getsize (localfile)
- def body_hook ():
- """ body_hook docstring """
- handle = open (localfile)
- while True:
- data = handle.read (1300)
- if len (data) == 0:
- break
- self.connection.send (data)
- handle.close ()
- self.__put__ (filesize, body_hook, remotefile)
-
-
- def create_empty_file (self, remotefile):
- """ create an empty file """
- self.log ('touch_file %s' % (remotefile))
- headers = {'Content-Length' : '0'}
- response = self.request ('PUT', remotefile, None, headers)
- if not response.status in (201, 204): # created, no content
- self.__err (response, ('PUT', remotefile, None, headers, 0))
- if self.get_file_size (remotefile) != 0:
- raise DAVError (0, 'file upload error', str (remotefile))
-
-
- def __get_file_setup (self, remotefile, check_size=True):
- """ __get_file_setup docstring """
- if check_size:
- remotesize = self.get_file_size (remotefile)
- response = self.request ('GET', remotefile, None, {}, False)
- if response.status != 200:
- self.__err (response, ('GET', remotefile, None, {}, 0))
- try:
- content_length = int (response.getheader ("Content-Length"))
- except TypeError:
- content_length = None
- if check_size:
- if content_length != remotesize:
- raise DAVError (0, 'file DL size error', remotefile)
- return (response, content_length)
-
-
- def __get_file_read (self, writehandle, response, content_length):
- """ __get_file_read docstring """
- if content_length != None:
- so_far_length = 0
- while so_far_length < content_length:
- data = response.read (content_length - so_far_length)
- if len (data) == 0:
- raise DAVError (0, 'short file download')
- so_far_length += len (data)
- writehandle.write (data)
- while len (response.read ()) > 0:
- pass
- else:
- while True:
- data = response.read ()
- if (len (data) < 1):
- break
- writehandle.write (data)
-
-
- def get_file (self, remotefile, localfile, check_size=True):
- """
- Get a remote file from a webdav server. Download to a local
- tmp file, then move into place. Sanity check file sizes as
- we go.
- """
- self.log ('get_file %s -> %s' % (remotefile, localfile))
- (response, content_length) = \
- self.__get_file_setup (remotefile, check_size)
- localfile_tmp = self.__tmp_filename (localfile)
- handle = open (localfile_tmp, 'w')
- self.__get_file_read (handle, response, content_length)
- handle.close ()
- if check_size:
- if content_length != os.path.getsize (localfile_tmp):
- raise DAVError (0, 'file DL size error',
- remotefile+','+localfile)
- os.rename (localfile_tmp, localfile)
-
-
- def get_file_as_string (self, remotefile, check_size=True):
- """
- download a file from a webdav server and return it as a string.
- """
- self.log ('get_file_as_string %s' % remotefile)
- (response, content_length) = \
- self.__get_file_setup (remotefile, check_size)
- # (tmp_handle, tmp_filename) = tempfile.mkstemp ()
- tmp_handle = os.tmpfile ()
- self.__get_file_read (tmp_handle, response, content_length)
- tmp_handle.seek (0)
- ret = tmp_handle.read ()
- tmp_handle.close ()
- # os.unlink (tmp_filename)
- return ret
-
-
- def get_post_as_string (self, remotefile, body):
- """
- Do an http POST, send body, get response and return it.
- """
- self.log ('get_post_as_string %s' % remotefile)
- # headers = {'Content-Type':'application/x-www-form-urlencoded'}
- headers = {'Content-Type':'text/xml; charset="utf-8"'}
- # b64body = urlsafe_b64encode (asset_url)
- response = self.request ('POST', remotefile, body, headers, False)
- if response.status != 200:
- self.__err (response, ('POST', remotefile, body, headers, 0))
- try:
- content_length = int (response.getheader ('Content-Length'))
- except TypeError:
- content_length = None
- tmp_handle = os.tmpfile ()
- self.__get_file_read (tmp_handle, response, content_length)
- tmp_handle.seek (0)
- ret = tmp_handle.read ()
- tmp_handle.close ()
- return ret
-
-
- def __destination_command (self, verb, remotesrc, dstdav, remotedst):
- """
- self and dstdav should point to the same http server.
- """
- if len (remotedst) > 0 and remotedst[ 0 ] == '/':
- remotedst = remotedst[1:]
- headers = {'Destination': 'http://%s:%d%s%s' % (dstdav.host,
- dstdav.port,
- dstdav.top_path,
- remotedst)}
- response = self.request (verb, remotesrc, None, headers)
- if response.status == 201:
- return # created
- if response.status == 204:
- return # no content
- self.__err (response, (verb, remotesrc, None, headers, 0))
-
-
- def rename (self, remotesrc, remotedst):
- """ rename a file on a webdav server """
- self.log ('rename %s -> %s' % (remotesrc, remotedst))
- self.__destination_command ('MOVE', remotesrc, self, remotedst)
- def xrename (self, remotesrc, dstdav, remotedst):
- """ rename a file on a webdav server """
- self.log ('xrename %s -> %s' % (remotesrc, remotedst))
- self.__destination_command ('MOVE', remotesrc, dstdav, remotedst)
-
-
- def copy (self, remotesrc, remotedst):
- """ copy a file on a webdav server """
- self.log ('copy %s -> %s' % (remotesrc, remotedst))
- self.__destination_command ('COPY', remotesrc, self, remotedst)
- def xcopy (self, remotesrc, dstdav, remotedst):
- """ copy a file on a webdav server """
- self.log ('xcopy %s -> %s' % (remotesrc, remotedst))
- self.__destination_command ('COPY', remotesrc, dstdav, remotedst)
-
-
-def put_string (data, url):
- """
- upload string s to a url
- """
- url_parsed = urlparse.urlsplit (url)
- dav = WebDAV ('%s://%s/' % (url_parsed[ 0 ], url_parsed[ 1 ]))
- dav.put_string (data, url_parsed[ 2 ])
-
-
-def get_string (url, check_size=True):
- """
- return the contents of a url as a string
- """
- url_parsed = urlparse.urlsplit (url)
- dav = WebDAV ('%s://%s/' % (url_parsed[ 0 ], url_parsed[ 1 ]))
- return dav.get_file_as_string (url_parsed[ 2 ], check_size)
diff --git a/indra/lib/python/indra/ipc/xml_rpc.py b/indra/lib/python/indra/ipc/xml_rpc.py
deleted file mode 100755
index 47536c10c3..0000000000
--- a/indra/lib/python/indra/ipc/xml_rpc.py
+++ /dev/null
@@ -1,273 +0,0 @@
-"""\
-@file xml_rpc.py
-@brief An implementation of a parser/generator for the XML-RPC xml format.
-
-$LicenseInfo:firstyear=2006&license=mit$
-
-Copyright (c) 2006-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-"""
-
-
-from greenlet import greenlet
-
-from mulib import mu
-
-from xml.sax import handler
-from xml.sax import parseString
-
-
-# States
-class Expected(object):
- def __init__(self, tag):
- self.tag = tag
-
- def __getattr__(self, name):
- return type(self)(name)
-
- def __repr__(self):
- return '%s(%r)' % (
- type(self).__name__, self.tag)
-
-
-class START(Expected):
- pass
-
-
-class END(Expected):
- pass
-
-
-class STR(object):
- tag = ''
-
-
-START = START('')
-END = END('')
-
-
-class Malformed(Exception):
- pass
-
-
-class XMLParser(handler.ContentHandler):
- def __init__(self, state_machine, next_states):
- handler.ContentHandler.__init__(self)
- self.state_machine = state_machine
- if not isinstance(next_states, tuple):
- next_states = (next_states, )
- self.next_states = next_states
- self._character_buffer = ''
-
- def assertState(self, state, name, *rest):
- if not isinstance(self.next_states, tuple):
- self.next_states = (self.next_states, )
- for next in self.next_states:
- if type(state) == type(next):
- if next.tag and next.tag != name:
- raise Malformed(
- "Expected %s, got %s %s %s" % (
- next, state, name, rest))
- break
- else:
- raise Malformed(
- "Expected %s, got %s %s %s" % (
- self.next_states, state, name, rest))
-
- def startElement(self, name, attrs):
- self.assertState(START, name.lower(), attrs)
- self.next_states = self.state_machine.switch(START, (name.lower(), dict(attrs)))
-
- def endElement(self, name):
- if self._character_buffer.strip():
- characters = self._character_buffer.strip()
- self._character_buffer = ''
- self.assertState(STR, characters)
- self.next_states = self.state_machine.switch(characters)
- self.assertState(END, name.lower())
- self.next_states = self.state_machine.switch(END, name.lower())
-
- def error(self, exc):
- self.bozo = 1
- self.exc = exc
-
- def fatalError(self, exc):
- self.error(exc)
- raise exc
-
- def characters(self, characters):
- self._character_buffer += characters
-
-
-def parse(what):
- child = greenlet(xml_rpc)
- me = greenlet.getcurrent()
- startup_states = child.switch(me)
- parser = XMLParser(child, startup_states)
- try:
- parseString(what, parser)
- except Malformed:
- print what
- raise
- return child.switch()
-
-
-def xml_rpc(yielder):
- yielder.switch(START.methodcall)
- yielder.switch(START.methodname)
- methodName = yielder.switch(STR)
- yielder.switch(END.methodname)
-
- yielder.switch(START.params)
-
- root = None
- params = []
- while True:
- state, _ = yielder.switch(START.param, END.params)
- if state == END:
- break
-
- yielder.switch(START.value)
-
- params.append(
- handle(yielder))
-
- yielder.switch(END.value)
- yielder.switch(END.param)
-
- yielder.switch(END.methodcall)
- ## Resume parse
- yielder.switch()
- ## Return result to parse
- return methodName.strip(), params
-
-
-def handle(yielder):
- _, (tag, attrs) = yielder.switch(START)
- if tag in ['int', 'i4']:
- result = int(yielder.switch(STR))
- elif tag == 'boolean':
- result = bool(int(yielder.switch(STR)))
- elif tag == 'string':
- result = yielder.switch(STR)
- elif tag == 'double':
- result = float(yielder.switch(STR))
- elif tag == 'datetime.iso8601':
- result = yielder.switch(STR)
- elif tag == 'base64':
- result = base64.b64decode(yielder.switch(STR))
- elif tag == 'struct':
- result = {}
- while True:
- state, _ = yielder.switch(START.member, END.struct)
- if state == END:
- break
-
- yielder.switch(START.name)
- key = yielder.switch(STR)
- yielder.switch(END.name)
-
- yielder.switch(START.value)
- result[key] = handle(yielder)
- yielder.switch(END.value)
-
- yielder.switch(END.member)
- ## We already handled above, don't want to handle it below
- return result
- elif tag == 'array':
- result = []
- yielder.switch(START.data)
- while True:
- state, _ = yielder.switch(START.value, END.data)
- if state == END:
- break
-
- result.append(handle(yielder))
-
- yielder.switch(END.value)
-
- yielder.switch(getattr(END, tag))
-
- return result
-
-
-VALUE = mu.tag_factory('value')
-BOOLEAN = mu.tag_factory('boolean')
-INT = mu.tag_factory('int')
-STRUCT = mu.tag_factory('struct')
-MEMBER = mu.tag_factory('member')
-NAME = mu.tag_factory('name')
-ARRAY = mu.tag_factory('array')
-DATA = mu.tag_factory('data')
-STRING = mu.tag_factory('string')
-DOUBLE = mu.tag_factory('double')
-METHODRESPONSE = mu.tag_factory('methodResponse')
-PARAMS = mu.tag_factory('params')
-PARAM = mu.tag_factory('param')
-
-mu.inline_elements['string'] = True
-mu.inline_elements['boolean'] = True
-mu.inline_elements['name'] = True
-
-
-def _generate(something):
- if isinstance(something, dict):
- result = STRUCT()
- for key, value in something.items():
- result[
- MEMBER[
- NAME[key], _generate(value)]]
- return VALUE[result]
- elif isinstance(something, list):
- result = DATA()
- for item in something:
- result[_generate(item)]
- return VALUE[ARRAY[[result]]]
- elif isinstance(something, basestring):
- return VALUE[STRING[something]]
- elif isinstance(something, bool):
- if something:
- return VALUE[BOOLEAN['1']]
- return VALUE[BOOLEAN['0']]
- elif isinstance(something, int):
- return VALUE[INT[something]]
- elif isinstance(something, float):
- return VALUE[DOUBLE[something]]
-
-def generate(*args):
- params = PARAMS()
- for arg in args:
- params[PARAM[_generate(arg)]]
- return METHODRESPONSE[params]
-
-
-if __name__ == '__main__':
- print parse(""" examples.getStateName 41
-""")
-
-
-
-
-
-
-
-
-
diff --git a/indra/lib/python/indra/util/fastest_elementtree.py b/indra/lib/python/indra/util/fastest_elementtree.py
deleted file mode 100755
index 4fcf662dd9..0000000000
--- a/indra/lib/python/indra/util/fastest_elementtree.py
+++ /dev/null
@@ -1,64 +0,0 @@
-"""\
-@file fastest_elementtree.py
-@brief Concealing some gnarly import logic in here. This should export the interface of elementtree.
-
-$LicenseInfo:firstyear=2008&license=mit$
-
-Copyright (c) 2008-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-"""
-
-# The parsing exception raised by the underlying library depends
-# on the ElementTree implementation we're using, so we provide an
-# alias here.
-#
-# Use ElementTreeError as the exception type for catching parsing
-# errors.
-
-
-# Using cElementTree might cause some unforeseen problems, so here's a
-# convenient off switch.
-use_celementree = True
-
-try:
- if not use_celementree:
- raise ImportError()
- # Python 2.3 and 2.4.
- from cElementTree import *
- ElementTreeError = SyntaxError
-except ImportError:
- try:
- if not use_celementree:
- raise ImportError()
- # Python 2.5 and above.
- from xml.etree.cElementTree import *
- ElementTreeError = SyntaxError
- except ImportError:
- # Pure Python code.
- try:
- # Python 2.3 and 2.4.
- from elementtree.ElementTree import *
- except ImportError:
- # Python 2.5 and above.
- from xml.etree.ElementTree import *
-
- # The pure Python ElementTree module uses Expat for parsing.
- from xml.parsers.expat import ExpatError as ElementTreeError
diff --git a/indra/lib/python/indra/util/helpformatter.py b/indra/lib/python/indra/util/helpformatter.py
deleted file mode 100755
index ba5c9b67d1..0000000000
--- a/indra/lib/python/indra/util/helpformatter.py
+++ /dev/null
@@ -1,52 +0,0 @@
-"""\
-@file helpformatter.py
-@author Phoenix
-@brief Class for formatting optparse descriptions.
-
-$LicenseInfo:firstyear=2007&license=mit$
-
-Copyright (c) 2007-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-"""
-
-import optparse
-import textwrap
-
-class Formatter(optparse.IndentedHelpFormatter):
- def __init__(
- self,
- p_indentIncrement = 2,
- p_maxHelpPosition = 24,
- p_width = 79,
- p_shortFirst = 1) :
- optparse.HelpFormatter.__init__(
- self,
- p_indentIncrement,
- p_maxHelpPosition,
- p_width,
- p_shortFirst)
- def format_description(self, p_description):
- t_descWidth = self.width - self.current_indent
- t_indent = " " * (self.current_indent + 2)
- return "\n".join(
- [textwrap.fill(descr, t_descWidth, initial_indent = t_indent,
- subsequent_indent = t_indent)
- for descr in p_description.split("\n")] )
diff --git a/indra/lib/python/indra/util/iterators.py b/indra/lib/python/indra/util/iterators.py
deleted file mode 100755
index 9013fa6303..0000000000
--- a/indra/lib/python/indra/util/iterators.py
+++ /dev/null
@@ -1,63 +0,0 @@
-"""\
-@file iterators.py
-@brief Useful general-purpose iterators.
-
-$LicenseInfo:firstyear=2008&license=mit$
-
-Copyright (c) 2008-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-"""
-
-from __future__ import nested_scopes
-
-def iter_chunks(rows, aggregate_size=100):
- """
- Given an iterable set of items (@p rows), produces lists of up to @p
- aggregate_size items at a time, for example:
-
- iter_chunks([1,2,3,4,5,6,7,8,9,10], 3)
-
- Values for @p aggregate_size < 1 will raise ValueError.
-
- Will return a generator that produces, in the following order:
- - [1, 2, 3]
- - [4, 5, 6]
- - [7, 8, 9]
- - [10]
- """
- if aggregate_size < 1:
- raise ValueError()
-
- def iter_chunks_inner():
- row_iter = iter(rows)
- done = False
- agg = []
- while not done:
- try:
- row = row_iter.next()
- agg.append(row)
- except StopIteration:
- done = True
- if agg and (len(agg) >= aggregate_size or done):
- yield agg
- agg = []
-
- return iter_chunks_inner()
diff --git a/indra/lib/python/indra/util/iterators_test.py b/indra/lib/python/indra/util/iterators_test.py
deleted file mode 100755
index 66928c8e7d..0000000000
--- a/indra/lib/python/indra/util/iterators_test.py
+++ /dev/null
@@ -1,72 +0,0 @@
-"""\
-@file iterators_test.py
-@brief Test cases for iterators module.
-
-$LicenseInfo:firstyear=2008&license=mit$
-
-Copyright (c) 2008-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-"""
-
-import unittest
-
-from indra.util.iterators import iter_chunks
-
-class TestIterChunks(unittest.TestCase):
- """Unittests for iter_chunks"""
- def test_bad_agg_size(self):
- rows = [1,2,3,4]
- self.assertRaises(ValueError, iter_chunks, rows, 0)
- self.assertRaises(ValueError, iter_chunks, rows, -1)
-
- try:
- for i in iter_chunks(rows, 0):
- pass
- except ValueError:
- pass
- else:
- self.fail()
-
- try:
- result = list(iter_chunks(rows, 0))
- except ValueError:
- pass
- else:
- self.fail()
- def test_empty(self):
- rows = []
- result = list(iter_chunks(rows))
- self.assertEqual(result, [])
- def test_small(self):
- rows = [[1]]
- result = list(iter_chunks(rows, 2))
- self.assertEqual(result, [[[1]]])
- def test_size(self):
- rows = [[1],[2]]
- result = list(iter_chunks(rows, 2))
- self.assertEqual(result, [[[1],[2]]])
- def test_multi_agg(self):
- rows = [[1],[2],[3],[4],[5]]
- result = list(iter_chunks(rows, 2))
- self.assertEqual(result, [[[1],[2]],[[3],[4]],[[5]]])
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/indra/lib/python/indra/util/llperformance.py b/indra/lib/python/indra/util/llperformance.py
deleted file mode 100755
index 57dd64de3f..0000000000
--- a/indra/lib/python/indra/util/llperformance.py
+++ /dev/null
@@ -1,182 +0,0 @@
-#!/usr/bin/env python
-"""\
-@file llperformance.py
-
-$LicenseInfo:firstyear=2010&license=viewerlgpl$
-Second Life Viewer Source Code
-Copyright (C) 2010-2011, Linden Research, Inc.
-
-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;
-version 2.1 of the License only.
-
-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
-
-Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
-$/LicenseInfo$
-"""
-
-# ------------------------------------------------
-# Sim metrics utility functions.
-
-import glob, os, time, sys, stat, exceptions
-
-from indra.base import llsd
-
-gBlockMap = {} #Map of performance metric data with function hierarchy information.
-gCurrentStatPath = ""
-
-gIsLoggingEnabled=False
-
-class LLPerfStat:
- def __init__(self,key):
- self.mTotalTime = 0
- self.mNumRuns = 0
- self.mName=key
- self.mTimeStamp = int(time.time()*1000)
- self.mUTCTime = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
-
- def __str__(self):
- return "%f" % self.mTotalTime
-
- def start(self):
- self.mStartTime = int(time.time() * 1000000)
- self.mNumRuns += 1
-
- def stop(self):
- execution_time = int(time.time() * 1000000) - self.mStartTime
- self.mTotalTime += execution_time
-
- def get_map(self):
- results={}
- results['name']=self.mName
- results['utc_time']=self.mUTCTime
- results['timestamp']=self.mTimeStamp
- results['us']=self.mTotalTime
- results['count']=self.mNumRuns
- return results
-
-class PerfError(exceptions.Exception):
- def __init__(self):
- return
-
- def __Str__(self):
- print "","Unfinished LLPerfBlock"
-
-class LLPerfBlock:
- def __init__( self, key ):
- global gBlockMap
- global gCurrentStatPath
- global gIsLoggingEnabled
-
- #Check to see if we're running metrics right now.
- if gIsLoggingEnabled:
- self.mRunning = True #Mark myself as running.
-
- self.mPreviousStatPath = gCurrentStatPath
- gCurrentStatPath += "/" + key
- if gCurrentStatPath not in gBlockMap:
- gBlockMap[gCurrentStatPath] = LLPerfStat(key)
-
- self.mStat = gBlockMap[gCurrentStatPath]
- self.mStat.start()
-
- def finish( self ):
- global gBlockMap
- global gIsLoggingEnabled
-
- if gIsLoggingEnabled:
- self.mStat.stop()
- self.mRunning = False
- gCurrentStatPath = self.mPreviousStatPath
-
-# def __del__( self ):
-# if self.mRunning:
-# #SPATTERS FIXME
-# raise PerfError
-
-class LLPerformance:
- #--------------------------------------------------
- # Determine whether or not we want to log statistics
-
- def __init__( self, process_name = "python" ):
- self.process_name = process_name
- self.init_testing()
- self.mTimeStamp = int(time.time()*1000)
- self.mUTCTime = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
-
- def init_testing( self ):
- global gIsLoggingEnabled
-
- host_performance_file = "/dev/shm/simperf/simperf_proc_config.llsd"
-
- #If file exists, open
- if os.path.exists(host_performance_file):
- file = open (host_performance_file,'r')
-
- #Read serialized LLSD from file.
- body = llsd.parse(file.read())
-
- #Calculate time since file last modified.
- stats = os.stat(host_performance_file)
- now = time.time()
- mod = stats[stat.ST_MTIME]
- age = now - mod
-
- if age < ( body['duration'] ):
- gIsLoggingEnabled = True
-
-
- def get ( self ):
- global gIsLoggingEnabled
- return gIsLoggingEnabled
-
- #def output(self,ptr,path):
- # if 'stats' in ptr:
- # stats = ptr['stats']
- # self.mOutputPtr[path] = stats.get_map()
-
- # if 'children' in ptr:
- # children=ptr['children']
-
- # curptr = self.mOutputPtr
- # curchildren={}
- # curptr['children'] = curchildren
-
- # for key in children:
- # curchildren[key]={}
- # self.mOutputPtr = curchildren[key]
- # self.output(children[key],path + '/' + key)
-
- def done(self):
- global gBlockMap
-
- if not self.get():
- return
-
- output_name = "/dev/shm/simperf/%s_proc.%d.llsd" % (self.process_name, os.getpid())
- output_file = open(output_name, 'w')
- process_info = {
- "name" : self.process_name,
- "pid" : os.getpid(),
- "ppid" : os.getppid(),
- "timestamp" : self.mTimeStamp,
- "utc_time" : self.mUTCTime,
- }
- output_file.write(llsd.format_notation(process_info))
- output_file.write('\n')
-
- for key in gBlockMap.keys():
- gBlockMap[key] = gBlockMap[key].get_map()
- output_file.write(llsd.format_notation(gBlockMap))
- output_file.write('\n')
- output_file.close()
-
diff --git a/indra/lib/python/indra/util/llsubprocess.py b/indra/lib/python/indra/util/llsubprocess.py
deleted file mode 100755
index 7e0e115d14..0000000000
--- a/indra/lib/python/indra/util/llsubprocess.py
+++ /dev/null
@@ -1,117 +0,0 @@
-"""\
-@file llsubprocess.py
-@author Phoenix
-@date 2008-01-18
-@brief The simplest possible wrapper for a common sub-process paradigm.
-
-$LicenseInfo:firstyear=2007&license=mit$
-
-Copyright (c) 2007-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-"""
-
-import os
-import popen2
-import time
-import select
-
-class Timeout(RuntimeError):
- "Exception raised when a subprocess times out."
- pass
-
-def run(command, args=None, data=None, timeout=None):
- """\
-@brief Run command with arguments
-
-This is it. This is the function I want to run all the time when doing
-subprocces, but end up copying the code everywhere. none of the
-standard commands are secure and provide a way to specify input, get
-all the output, and get the result.
-@param command A string specifying a process to launch.
-@param args Arguments to be passed to command. Must be list, tuple or None.
-@param data input to feed to the command.
-@param timeout Maximum number of many seconds to run.
-@return Returns (result, stdout, stderr) from process.
-"""
- cmd = [command]
- if args:
- cmd.extend([str(arg) for arg in args])
- #print "cmd: ","' '".join(cmd)
- child = popen2.Popen3(cmd, True)
- #print child.pid
- out = []
- err = []
- result = -1
- time_left = timeout
- tochild = [child.tochild.fileno()]
- while True:
- time_start = time.time()
- #print "time:",time_left
- p_in, p_out, p_err = select.select(
- [child.fromchild.fileno(), child.childerr.fileno()],
- tochild,
- [],
- time_left)
- if p_in:
- new_line = os.read(child.fromchild.fileno(), 32 * 1024)
- if new_line:
- #print "line:",new_line
- out.append(new_line)
- new_line = os.read(child.childerr.fileno(), 32 * 1024)
- if new_line:
- #print "error:", new_line
- err.append(new_line)
- if p_out:
- if data:
- #print "p_out"
- bytes = os.write(child.tochild.fileno(), data)
- data = data[bytes:]
- if len(data) == 0:
- data = None
- tochild = []
- child.tochild.close()
- result = child.poll()
- if result != -1:
- # At this point, the child process has exited and result
- # is the return value from the process. Between the time
- # we called select() and poll() the process may have
- # exited so read all the data left on the child process
- # stdout and stderr.
- last = child.fromchild.read()
- if last:
- out.append(last)
- last = child.childerr.read()
- if last:
- err.append(last)
- child.tochild.close()
- child.fromchild.close()
- child.childerr.close()
- break
- if time_left is not None:
- time_left -= (time.time() - time_start)
- if time_left < 0:
- raise Timeout
- #print "result:",result
- out = ''.join(out)
- #print "stdout:", out
- err = ''.join(err)
- #print "stderr:", err
- return result, out, err
diff --git a/indra/lib/python/indra/util/named_query.py b/indra/lib/python/indra/util/named_query.py
deleted file mode 100755
index 6bf956107d..0000000000
--- a/indra/lib/python/indra/util/named_query.py
+++ /dev/null
@@ -1,592 +0,0 @@
-"""\
-@file named_query.py
-@author Ryan Williams, Phoenix
-@date 2007-07-31
-@brief An API for running named queries.
-
-$LicenseInfo:firstyear=2007&license=mit$
-
-Copyright (c) 2007-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-"""
-
-import errno
-import MySQLdb
-import MySQLdb.cursors
-import os
-import os.path
-import re
-import time
-
-from indra.base import llsd
-from indra.base import config
-
-DEBUG = False
-NQ_FILE_SUFFIX = config.get('named-query-file-suffix', '.nq')
-NQ_FILE_SUFFIX_LEN = len(NQ_FILE_SUFFIX)
-
-_g_named_manager = None
-
-def _init_g_named_manager(sql_dir = None):
- """Initializes a global NamedManager object to point at a
- specified named queries hierarchy.
-
- This function is intended entirely for testing purposes,
- because it's tricky to control the config from inside a test."""
- global NQ_FILE_SUFFIX
- NQ_FILE_SUFFIX = config.get('named-query-file-suffix', '.nq')
- global NQ_FILE_SUFFIX_LEN
- NQ_FILE_SUFFIX_LEN = len(NQ_FILE_SUFFIX)
-
- if sql_dir is None:
- sql_dir = config.get('named-query-base-dir')
-
- # extra fallback directory in case config doesn't return what we want
- if sql_dir is None:
- sql_dir = os.path.abspath(
- os.path.join(
- os.path.realpath(os.path.dirname(__file__)), "..", "..", "..", "..", "web", "dataservice", "sql"))
-
- global _g_named_manager
- _g_named_manager = NamedQueryManager(
- os.path.abspath(os.path.realpath(sql_dir)))
-
-def get(name, schema = None):
- "Get the named query object to be used to perform queries"
- if _g_named_manager is None:
- _init_g_named_manager()
- return _g_named_manager.get(name).for_schema(schema)
-
-def sql(connection, name, params):
- # use module-global NamedQuery object to perform default substitution
- return get(name).sql(connection, params)
-
-def run(connection, name, params, expect_rows = None):
- """\
-@brief given a connection, run a named query with the params
-
-Note that this function will fetch ALL rows.
-@param connection The connection to use
-@param name The name of the query to run
-@param params The parameters passed into the query
-@param expect_rows The number of rows expected. Set to 1 if return_as_map is true. Raises ExpectationFailed if the number of returned rows doesn't exactly match. Kind of a hack.
-@return Returns the result set as a list of dicts.
-"""
- return get(name).run(connection, params, expect_rows)
-
-class ExpectationFailed(Exception):
- """ Exception that is raised when an expectation for an sql query
- is not met."""
- def __init__(self, message):
- Exception.__init__(self, message)
- self.message = message
-
-class NamedQuery(object):
- def __init__(self, name, filename):
- """ Construct a NamedQuery object. The name argument is an
- arbitrary name as a handle for the query, and the filename is
- a path to a file or a file-like object containing an llsd named
- query document."""
- self._stat_interval_seconds = 5 # 5 seconds
- self._name = name
- if (filename is not None and isinstance(filename, (str, unicode))
- and NQ_FILE_SUFFIX != filename[-NQ_FILE_SUFFIX_LEN:]):
- filename = filename + NQ_FILE_SUFFIX
- self._location = filename
- self._alternative = dict()
- self._last_mod_time = 0
- self._last_check_time = 0
- self.deleted = False
- self.load_contents()
-
- def name(self):
- """ The name of the query. """
- return self._name
-
- def get_modtime(self):
- """ Returns the mtime (last modified time) of the named query
- filename. For file-like objects, expect a modtime of 0"""
- if self._location and isinstance(self._location, (str, unicode)):
- return os.path.getmtime(self._location)
- return 0
-
- def load_contents(self):
- """ Loads and parses the named query file into self. Does
- nothing if self.location is nonexistant."""
- if self._location:
- if isinstance(self._location, (str, unicode)):
- contents = llsd.parse(open(self._location).read())
- else:
- # we probably have a file-like object. Godspeed!
- contents = llsd.parse(self._location.read())
- self._reference_contents(contents)
- # Check for alternative implementations
- try:
- for name, alt in self._contents['alternative'].items():
- nq = NamedQuery(name, None)
- nq._reference_contents(alt)
- self._alternative[name] = nq
- except KeyError, e:
- pass
- self._last_mod_time = self.get_modtime()
- self._last_check_time = time.time()
-
- def _reference_contents(self, contents):
- "Helper method which builds internal structure from parsed contents"
- self._contents = contents
- self._ttl = int(self._contents.get('ttl', 0))
- self._return_as_map = bool(self._contents.get('return_as_map', False))
- self._legacy_dbname = self._contents.get('legacy_dbname', None)
-
- # reset these before doing the sql conversion because we will
- # read them there. reset these while loading so we pick up
- # changes.
- self._around = set()
- self._append = set()
- self._integer = set()
- self._options = self._contents.get('dynamic_where', {})
- for key in self._options:
- if isinstance(self._options[key], basestring):
- self._options[key] = self._convert_sql(self._options[key])
- elif isinstance(self._options[key], list):
- lines = []
- for line in self._options[key]:
- lines.append(self._convert_sql(line))
- self._options[key] = lines
- else:
- moreopt = {}
- for kk in self._options[key]:
- moreopt[kk] = self._convert_sql(self._options[key][kk])
- self._options[key] = moreopt
- self._base_query = self._convert_sql(self._contents['base_query'])
- self._query_suffix = self._convert_sql(
- self._contents.get('query_suffix', ''))
-
- def _convert_sql(self, sql):
- """convert the parsed sql into a useful internal structure.
-
- This function has to turn the named query format into a pyformat
- style. It also has to look for %:name% and :name% and
- ready them for use in LIKE statements"""
- if sql:
- # This first sub is to properly escape any % signs that
- # are meant to be literally passed through to mysql in the
- # query. It leaves any %'s that are used for
- # like-expressions.
- expr = re.compile("(?<=[^a-zA-Z0-9_-])%(?=[^:])")
- sql = expr.sub('%%', sql)
-
- # This should tackle the rest of the %'s in the query, by
- # converting them to LIKE clauses.
- expr = re.compile("(%?):([a-zA-Z][a-zA-Z0-9_-]*)%")
- sql = expr.sub(self._prepare_like, sql)
- expr = re.compile("#:([a-zA-Z][a-zA-Z0-9_-]*)")
- sql = expr.sub(self._prepare_integer, sql)
- expr = re.compile(":([a-zA-Z][a-zA-Z0-9_-]*)")
- sql = expr.sub("%(\\1)s", sql)
- return sql
-
- def _prepare_like(self, match):
- """This function changes LIKE statement replace behavior
-
- It works by turning %:name% to %(_name_around)s and :name% to
- %(_name_append)s. Since a leading '_' is not a valid keyname
- input (enforced via unit tests), it will never clash with
- existing keys. Then, when building the statement, the query
- runner will generate corrected strings."""
- if match.group(1) == '%':
- # there is a leading % so this is treated as prefix/suffix
- self._around.add(match.group(2))
- return "%(" + self._build_around_key(match.group(2)) + ")s"
- else:
- # there is no leading %, so this is suffix only
- self._append.add(match.group(2))
- return "%(" + self._build_append_key(match.group(2)) + ")s"
-
- def _build_around_key(self, key):
- return "_" + key + "_around"
-
- def _build_append_key(self, key):
- return "_" + key + "_append"
-
- def _prepare_integer(self, match):
- """This function adjusts the sql for #:name replacements
-
- It works by turning #:name to %(_name_as_integer)s. Since a
- leading '_' is not a valid keyname input (enforced via unit
- tests), it will never clash with existing keys. Then, when
- building the statement, the query runner will generate
- corrected strings."""
- self._integer.add(match.group(1))
- return "%(" + self._build_integer_key(match.group(1)) + ")s"
-
- def _build_integer_key(self, key):
- return "_" + key + "_as_integer"
-
- def _strip_wildcards_to_list(self, value):
- """Take string, and strip out the LIKE special characters.
-
- Technically, this is database dependant, but postgresql and
- mysql use the same wildcards, and I am not aware of a general
- way to handle this. I think you need a sql statement of the
- form:
-
- LIKE_STRING( [ANY,ONE,str]... )
-
- which would treat ANY as their any string, and ONE as their
- single glyph, and str as something that needs database
- specific encoding to not allow any % or _ to affect the query.
-
- As it stands, I believe it's impossible to write a named query
- style interface which uses like to search the entire space of
- text available. Imagine the query:
-
- % of brain used by average linden
-
- In order to search for %, it must be escaped, so once you have
- escaped the string to not do wildcard searches, and be escaped
- for the database, and then prepended the wildcard you come
- back with one of:
-
- 1) %\% of brain used by average linden
- 2) %%% of brain used by average linden
-
- Then, when passed to the database to be escaped to be database
- safe, you get back:
-
- 1) %\\% of brain used by average linden
- : which means search for any character sequence, followed by a
- backslash, followed by any sequence, followed by ' of
- brain...'
- 2) %%% of brain used by average linden
- : which (I believe) means search for a % followed by any
- character sequence followed by 'of brain...'
-
- Neither of which is what we want!
-
- So, we need a vendor (or extention) for LIKE_STRING. Anyone
- want to write it?"""
- if isinstance(value, unicode):
- utf8_value = value
- else:
- utf8_value = unicode(value, "utf-8")
- esc_list = []
- remove_chars = set(u"%_")
- for glyph in utf8_value:
- if glyph in remove_chars:
- continue
- esc_list.append(glyph.encode("utf-8"))
- return esc_list
-
- def delete(self):
- """ Makes this query unusable by deleting all the members and
- setting the deleted member. This is desired when the on-disk
- query has been deleted but the in-memory copy remains."""
- # blow away all members except _name, _location, and deleted
- name, location = self._name, self._location
- for key in self.__dict__.keys():
- del self.__dict__[key]
- self.deleted = True
- self._name, self._location = name, location
-
- def ttl(self):
- """ Estimated time to live of this query. Used for web
- services to set the Expires header."""
- return self._ttl
-
- def legacy_dbname(self):
- return self._legacy_dbname
-
- def return_as_map(self):
- """ Returns true if this query is configured to return its
- results as a single map (as opposed to a list of maps, the
- normal behavior)."""
-
- return self._return_as_map
-
- def for_schema(self, db_name):
- "Look trough the alternates and return the correct query"
- if db_name is None:
- return self
- try:
- return self._alternative[db_name]
- except KeyError, e:
- pass
- return self
-
- def run(self, connection, params, expect_rows = None, use_dictcursor = True):
- """given a connection, run a named query with the params
-
- Note that this function will fetch ALL rows. We do this because it
- opens and closes the cursor to generate the values, and this
- isn't a generator so the cursor has no life beyond the method call.
-
- @param cursor The connection to use (this generates its own cursor for the query)
- @param name The name of the query to run
- @param params The parameters passed into the query
- @param expect_rows The number of rows expected. Set to 1 if return_as_map is true. Raises ExpectationFailed if the number of returned rows doesn't exactly match. Kind of a hack.
- @param use_dictcursor Set to false to use a normal cursor and manually convert the rows to dicts.
- @return Returns the result set as a list of dicts, or, if the named query has return_as_map set to true, returns a single dict.
- """
- if use_dictcursor:
- cursor = connection.cursor(MySQLdb.cursors.DictCursor)
- else:
- cursor = connection.cursor()
-
- full_query, params = self._construct_sql(params)
- if DEBUG:
- print "SQL:", self.sql(connection, params)
- rows = cursor.execute(full_query, params)
-
- # *NOTE: the expect_rows argument is a very cheesy way to get some
- # validation on the result set. If you want to add more expectation
- # logic, do something more object-oriented and flexible. Or use an ORM.
- if(self._return_as_map):
- expect_rows = 1
- if expect_rows is not None and rows != expect_rows:
- cursor.close()
- raise ExpectationFailed("Statement expected %s rows, got %s. Sql: '%s' %s" % (
- expect_rows, rows, full_query, params))
-
- # convert to dicts manually if we're not using a dictcursor
- if use_dictcursor:
- result_set = cursor.fetchall()
- else:
- if cursor.description is None:
- # an insert or something
- x = cursor.fetchall()
- cursor.close()
- return x
-
- names = [x[0] for x in cursor.description]
-
- result_set = []
- for row in cursor.fetchall():
- converted_row = {}
- for idx, col_name in enumerate(names):
- converted_row[col_name] = row[idx]
- result_set.append(converted_row)
-
- cursor.close()
- if self._return_as_map:
- return result_set[0]
- return result_set
-
- def _construct_sql(self, params):
- """ Returns a query string and a dictionary of parameters,
- suitable for directly passing to the execute() method."""
- self.refresh()
-
- # build the query from the options available and the params
- base_query = []
- base_query.append(self._base_query)
- for opt, extra_where in self._options.items():
- if type(extra_where) in (dict, list, tuple):
- if opt in params:
- base_query.append(extra_where[params[opt]])
- else:
- if opt in params and params[opt]:
- base_query.append(extra_where)
- if self._query_suffix:
- base_query.append(self._query_suffix)
- full_query = '\n'.join(base_query)
-
- # Go through the query and rewrite all of the ones with the
- # @:name syntax.
- rewrite = _RewriteQueryForArray(params)
- expr = re.compile("@%\(([a-zA-Z][a-zA-Z0-9_-]*)\)s")
- full_query = expr.sub(rewrite.operate, full_query)
- params.update(rewrite.new_params)
-
- # build out the params for like. We only have to do this
- # parameters which were detected to have ued the where syntax
- # during load.
- #
- # * treat the incoming string as utf-8
- # * strip wildcards
- # * append or prepend % as appropriate
- new_params = {}
- for key in params:
- if key in self._around:
- new_value = ['%']
- new_value.extend(self._strip_wildcards_to_list(params[key]))
- new_value.append('%')
- new_params[self._build_around_key(key)] = ''.join(new_value)
- if key in self._append:
- new_value = self._strip_wildcards_to_list(params[key])
- new_value.append('%')
- new_params[self._build_append_key(key)] = ''.join(new_value)
- if key in self._integer:
- new_params[self._build_integer_key(key)] = int(params[key])
- params.update(new_params)
-
- return full_query, params
-
- def sql(self, connection, params):
- """ Generates an SQL statement from the named query document
- and a dictionary of parameters.
-
- *NOTE: Only use for debugging, because it uses the
- non-standard MySQLdb 'literal' method.
- """
- if not DEBUG:
- import warnings
- warnings.warn("Don't use named_query.sql() when not debugging. Used on %s" % self._location)
- # do substitution using the mysql (non-standard) 'literal'
- # function to do the escaping.
- full_query, params = self._construct_sql(params)
- return full_query % connection.literal(params)
-
-
- def refresh(self):
- """ Refresh self from the file on the filesystem.
-
- This is optimized to be callable as frequently as you wish,
- without adding too much load. It does so by only stat-ing the
- file every N seconds, where N defaults to 5 and is
- configurable through the member _stat_interval_seconds. If the stat
- reveals that the file has changed, refresh will re-parse the
- contents of the file and use them to update the named query
- instance. If the stat reveals that the file has been deleted,
- refresh will call self.delete to make the in-memory
- representation unusable."""
- now = time.time()
- if(now - self._last_check_time > self._stat_interval_seconds):
- self._last_check_time = now
- try:
- modtime = self.get_modtime()
- if(modtime > self._last_mod_time):
- self.load_contents()
- except OSError, e:
- if e.errno == errno.ENOENT: # file not found
- self.delete() # clean up self
- raise # pass the exception along to the caller so they know that this query disappeared
-
-class NamedQueryManager(object):
- """ Manages the lifespan of NamedQuery objects, drawing from a
- directory hierarchy of named query documents.
-
- In practice this amounts to a memory cache of NamedQuery objects."""
-
- def __init__(self, named_queries_dir):
- """ Initializes a manager to look for named queries in a
- directory."""
- self._dir = os.path.abspath(os.path.realpath(named_queries_dir))
- self._cached_queries = {}
-
- def sql(self, connection, name, params):
- nq = self.get(name)
- return nq.sql(connection, params)
-
- def get(self, name):
- """ Returns a NamedQuery instance based on the name, either
- from memory cache, or by parsing from disk.
-
- The name is simply a relative path to the directory associated
- with the manager object. Before returning the instance, the
- NamedQuery object is cached in memory, so that subsequent
- accesses don't have to read from disk or do any parsing. This
- means that NamedQuery objects returned by this method are
- shared across all users of the manager object.
- NamedQuery.refresh is used to bring the NamedQuery objects in
- sync with the actual files on disk."""
- nq = self._cached_queries.get(name)
- if nq is None:
- nq = NamedQuery(name, os.path.join(self._dir, name))
- self._cached_queries[name] = nq
- else:
- try:
- nq.refresh()
- except OSError, e:
- if e.errno == errno.ENOENT: # file not found
- del self._cached_queries[name]
- raise # pass exception along to caller so they know that the query disappeared
-
- return nq
-
-class _RewriteQueryForArray(object):
- "Helper class for rewriting queries with the @:name syntax"
- def __init__(self, params):
- self.params = params
- self.new_params = dict()
-
- def operate(self, match):
- "Given a match, return the string that should be in use"
- key = match.group(1)
- value = self.params[key]
- if type(value) in (list,tuple):
- rv = []
- for idx in range(len(value)):
- # if the value@idx is array-like, we are
- # probably dealing with a VALUES
- new_key = "_%s_%s"%(key, str(idx))
- val_item = value[idx]
- if type(val_item) in (list, tuple, dict):
- if type(val_item) is dict:
- # this is because in Python, the order of
- # key, value retrieval from the dict is not
- # guaranteed to match what the input intended
- # and for VALUES, order is important.
- # TODO: Implemented ordered dict in LLSD parser?
- raise ExpectationFailed('Only lists/tuples allowed,\
- received dict')
- values_keys = []
- for value_idx, item in enumerate(val_item):
- # we want a key of the format :
- # key_#replacement_#value_row_#value_col
- # ugh... so if we are replacing 10 rows in user_note,
- # the first values clause would read (for @:user_notes) :-
- # ( :_user_notes_0_1_1, :_user_notes_0_1_2, :_user_notes_0_1_3 )
- # the input LLSD for VALUES will look like:
- # ...
- #
- # user_notes
- #
- #
- # ...
- # ...
- # ...
- #
- # ...
- #
- #
- # ...
- values_key = "%s_%s"%(new_key, value_idx)
- self.new_params[values_key] = item
- values_keys.append("%%(%s)s"%values_key)
- # now collapse all these new place holders enclosed in ()
- # from [':_key_0_1_1', ':_key_0_1_2', ':_key_0_1_3,...]
- # rv will have [ '(:_key_0_1_1, :_key_0_1_2, :_key_0_1_3)', ]
- # which is flattened a few lines below join(rv)
- rv.append('(%s)' % ','.join(values_keys))
- else:
- self.new_params[new_key] = val_item
- rv.append("%%(%s)s"%new_key)
- return ','.join(rv)
- else:
- # not something that can be expanded, so just drop the
- # leading @ in the front of the match. This will mean that
- # the single value we have, be it a string, int, whatever
- # (other than dict) will correctly show up, eg:
- #
- # where foo in (@:foobar) -- foobar is a string, so we get
- # where foo in (:foobar)
- return match.group(0)[1:]
diff --git a/indra/lib/python/indra/util/shutil2.py b/indra/lib/python/indra/util/shutil2.py
deleted file mode 100755
index 9e2e7a6ded..0000000000
--- a/indra/lib/python/indra/util/shutil2.py
+++ /dev/null
@@ -1,84 +0,0 @@
-'''
-@file shutil2.py
-@brief a better shutil.copytree replacement
-
-$LicenseInfo:firstyear=2007&license=mit$
-
-Copyright (c) 2007-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-'''
-
-#
-# shutil2.py
-# Taken from http://www.scons.org/wiki/AccumulateBuilder
-# the stock copytree sucks because it insists that the
-# target dir not exist
-#
-
-import os.path
-import shutil
-
-def copytree(src, dest, symlinks=False):
- """My own copyTree which does not fail if the directory exists.
-
- Recursively copy a directory tree using copy2().
-
- If the optional symlinks flag is true, symbolic links in the
- source tree result in symbolic links in the destination tree; if
- it is false, the contents of the files pointed to by symbolic
- links are copied.
-
- Behavior is meant to be identical to GNU 'cp -R'.
- """
- def copyItems(src, dest, symlinks=False):
- """Function that does all the work.
-
- It is necessary to handle the two 'cp' cases:
- - destination does exist
- - destination does not exist
-
- See 'cp -R' documentation for more details
- """
- for item in os.listdir(src):
- srcPath = os.path.join(src, item)
- if os.path.isdir(srcPath):
- srcBasename = os.path.basename(srcPath)
- destDirPath = os.path.join(dest, srcBasename)
- if not os.path.exists(destDirPath):
- os.makedirs(destDirPath)
- copyItems(srcPath, destDirPath)
- elif os.path.islink(item) and symlinks:
- linkto = os.readlink(item)
- os.symlink(linkto, dest)
- else:
- shutil.copy2(srcPath, dest)
-
- # case 'cp -R src/ dest/' where dest/ already exists
- if os.path.exists(dest):
- destPath = os.path.join(dest, os.path.basename(src))
- if not os.path.exists(destPath):
- os.makedirs(destPath)
- # case 'cp -R src/ dest/' where dest/ does not exist
- else:
- os.makedirs(dest)
- destPath = dest
- # actually copy the files
- copyItems(src, destPath)
diff --git a/indra/lib/python/indra/util/simperf_host_xml_parser.py b/indra/lib/python/indra/util/simperf_host_xml_parser.py
deleted file mode 100755
index 672c1050c2..0000000000
--- a/indra/lib/python/indra/util/simperf_host_xml_parser.py
+++ /dev/null
@@ -1,338 +0,0 @@
-#!/usr/bin/env python
-"""\
-@file simperf_host_xml_parser.py
-@brief Digest collector's XML dump and convert to simple dict/list structure
-
-$LicenseInfo:firstyear=2008&license=mit$
-
-Copyright (c) 2008-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-"""
-
-import sys, os, getopt, time
-import simplejson
-from xml import sax
-
-
-def usage():
- print "Usage:"
- print sys.argv[0] + " [options]"
- print " Convert RRD's XML dump to JSON. Script to convert the simperf_host_collector-"
- print " generated RRD dump into JSON. Steps include converting selected named"
- print " fields from GAUGE type to COUNTER type by computing delta with preceding"
- print " values. Top-level named fields are:"
- print
- print " lastupdate Time (javascript timestamp) of last data sample"
- print " step Time in seconds between samples"
- print " ds Data specification (name/type) for each column"
- print " database Table of data samples, one time step per row"
- print
- print "Options:"
- print " -i, --in Input settings filename. (Default: stdin)"
- print " -o, --out Output settings filename. (Default: stdout)"
- print " -h, --help Print this message and exit."
- print
- print "Example: %s -i rrddump.xml -o rrddump.json" % sys.argv[0]
- print
- print "Interfaces:"
- print " class SimPerfHostXMLParser() # SAX content handler"
- print " def simperf_host_xml_fixup(parser) # post-parse value fixup"
-
-class SimPerfHostXMLParser(sax.handler.ContentHandler):
-
- def __init__(self):
- pass
-
- def startDocument(self):
- self.rrd_last_update = 0 # public
- self.rrd_step = 0 # public
- self.rrd_ds = [] # public
- self.rrd_records = [] # public
- self._rrd_level = 0
- self._rrd_parse_state = 0
- self._rrd_chars = ""
- self._rrd_capture = False
- self._rrd_ds_val = {}
- self._rrd_data_row = []
- self._rrd_data_row_has_nan = False
-
- def endDocument(self):
- pass
-
- # Nasty little ad-hoc state machine to extract the elements that are
- # necessary from the 'rrdtool dump' XML output. The same element
- # name '' is used for two different data sets so we need to pay
- # some attention to the actual structure to get the ones we want
- # and ignore the ones we don't.
-
- def startElement(self, name, attrs):
- self._rrd_level = self._rrd_level + 1
- self._rrd_capture = False
- if self._rrd_level == 1:
- if name == "rrd" and self._rrd_parse_state == 0:
- self._rrd_parse_state = 1 # In
- self._rrd_capture = True
- self._rrd_chars = ""
- elif self._rrd_level == 2:
- if self._rrd_parse_state == 1:
- if name == "lastupdate":
- self._rrd_parse_state = 2 # In
- self._rrd_capture = True
- self._rrd_chars = ""
- elif name == "step":
- self._rrd_parse_state = 3 # In
- self._rrd_capture = True
- self._rrd_chars = ""
- elif name == "ds":
- self._rrd_parse_state = 4 # In
- self._rrd_ds_val = {}
- self._rrd_chars = ""
- elif name == "rra":
- self._rrd_parse_state = 5 # In
- elif self._rrd_level == 3:
- if self._rrd_parse_state == 4:
- if name == "name":
- self._rrd_parse_state = 6 # In
- self._rrd_capture = True
- self._rrd_chars = ""
- elif name == "type":
- self._rrd_parse_state = 7 # In
- self._rrd_capture = True
- self._rrd_chars = ""
- elif self._rrd_parse_state == 5:
- if name == "database":
- self._rrd_parse_state = 8 # In
- elif self._rrd_level == 4:
- if self._rrd_parse_state == 8:
- if name == "row":
- self._rrd_parse_state = 9 # In
- self._rrd_data_row = []
- self._rrd_data_row_has_nan = False
- elif self._rrd_level == 5:
- if self._rrd_parse_state == 9:
- if name == "v":
- self._rrd_parse_state = 10 # In
- self._rrd_capture = True
- self._rrd_chars = ""
-
- def endElement(self, name):
- self._rrd_capture = False
- if self._rrd_parse_state == 10:
- self._rrd_capture = self._rrd_level == 6
- if self._rrd_level == 5:
- if self._rrd_chars == "NaN":
- self._rrd_data_row_has_nan = True
- else:
- self._rrd_data_row.append(self._rrd_chars)
- self._rrd_parse_state = 9 # In
- elif self._rrd_parse_state == 9:
- if self._rrd_level == 4:
- if not self._rrd_data_row_has_nan:
- self.rrd_records.append(self._rrd_data_row)
- self._rrd_parse_state = 8 # In
- elif self._rrd_parse_state == 8:
- if self._rrd_level == 3:
- self._rrd_parse_state = 5 # In
- elif self._rrd_parse_state == 7:
- if self._rrd_level == 3:
- self._rrd_ds_val["type"] = self._rrd_chars
- self._rrd_parse_state = 4 # In
- elif self._rrd_parse_state == 6:
- if self._rrd_level == 3:
- self._rrd_ds_val["name"] = self._rrd_chars
- self._rrd_parse_state = 4 # In
- elif self._rrd_parse_state == 5:
- if self._rrd_level == 2:
- self._rrd_parse_state = 1 # In
- elif self._rrd_parse_state == 4:
- if self._rrd_level == 2:
- self.rrd_ds.append(self._rrd_ds_val)
- self._rrd_parse_state = 1 # In
- elif self._rrd_parse_state == 3:
- if self._rrd_level == 2:
- self.rrd_step = long(self._rrd_chars)
- self._rrd_parse_state = 1 # In
- elif self._rrd_parse_state == 2:
- if self._rrd_level == 2:
- self.rrd_last_update = long(self._rrd_chars)
- self._rrd_parse_state = 1 # In
- elif self._rrd_parse_state == 1:
- if self._rrd_level == 1:
- self._rrd_parse_state = 0 # At top
-
- if self._rrd_level:
- self._rrd_level = self._rrd_level - 1
-
- def characters(self, content):
- if self._rrd_capture:
- self._rrd_chars = self._rrd_chars + content.strip()
-
-def _make_numeric(value):
- try:
- value = float(value)
- except:
- value = ""
- return value
-
-def simperf_host_xml_fixup(parser, filter_start_time = None, filter_end_time = None):
- # Fixup for GAUGE fields that are really COUNTS. They
- # were forced to GAUGE to try to disable rrdtool's
- # data interpolation/extrapolation for non-uniform time
- # samples.
- fixup_tags = [ "cpu_user",
- "cpu_nice",
- "cpu_sys",
- "cpu_idle",
- "cpu_waitio",
- "cpu_intr",
- # "file_active",
- # "file_free",
- # "inode_active",
- # "inode_free",
- "netif_in_kb",
- "netif_in_pkts",
- "netif_in_errs",
- "netif_in_drop",
- "netif_out_kb",
- "netif_out_pkts",
- "netif_out_errs",
- "netif_out_drop",
- "vm_page_in",
- "vm_page_out",
- "vm_swap_in",
- "vm_swap_out",
- #"vm_mem_total",
- #"vm_mem_used",
- #"vm_mem_active",
- #"vm_mem_inactive",
- #"vm_mem_free",
- #"vm_mem_buffer",
- #"vm_swap_cache",
- #"vm_swap_total",
- #"vm_swap_used",
- #"vm_swap_free",
- "cpu_interrupts",
- "cpu_switches",
- "cpu_forks" ]
-
- col_count = len(parser.rrd_ds)
- row_count = len(parser.rrd_records)
-
- # Process the last row separately, just to make all values numeric.
- for j in range(col_count):
- parser.rrd_records[row_count - 1][j] = _make_numeric(parser.rrd_records[row_count - 1][j])
-
- # Process all other row/columns.
- last_different_row = row_count - 1
- current_row = row_count - 2
- while current_row >= 0:
- # Check for a different value than the previous row. If everything is the same
- # then this is probably just a filler/bogus entry.
- is_different = False
- for j in range(col_count):
- parser.rrd_records[current_row][j] = _make_numeric(parser.rrd_records[current_row][j])
- if parser.rrd_records[current_row][j] != parser.rrd_records[last_different_row][j]:
- # We're good. This is a different row.
- is_different = True
-
- if not is_different:
- # This is a filler/bogus entry. Just ignore it.
- for j in range(col_count):
- parser.rrd_records[current_row][j] = float('nan')
- else:
- # Some tags need to be converted into deltas.
- for j in range(col_count):
- if parser.rrd_ds[j]["name"] in fixup_tags:
- parser.rrd_records[last_different_row][j] = \
- parser.rrd_records[last_different_row][j] - parser.rrd_records[current_row][j]
- last_different_row = current_row
-
- current_row -= 1
-
- # Set fixup_tags in the first row to 'nan' since they aren't useful anymore.
- for j in range(col_count):
- if parser.rrd_ds[j]["name"] in fixup_tags:
- parser.rrd_records[0][j] = float('nan')
-
- # Add a timestamp to each row and to the catalog. Format and name
- # chosen to match other simulator logging (hopefully).
- start_time = parser.rrd_last_update - (parser.rrd_step * (row_count - 1))
- # Build a filtered list of rrd_records if we are limited to a time range.
- filter_records = False
- if filter_start_time is not None or filter_end_time is not None:
- filter_records = True
- filtered_rrd_records = []
- if filter_start_time is None:
- filter_start_time = start_time * 1000
- if filter_end_time is None:
- filter_end_time = parser.rrd_last_update * 1000
-
- for i in range(row_count):
- record_timestamp = (start_time + (i * parser.rrd_step)) * 1000
- parser.rrd_records[i].insert(0, record_timestamp)
- if filter_records:
- if filter_start_time <= record_timestamp and record_timestamp <= filter_end_time:
- filtered_rrd_records.append(parser.rrd_records[i])
-
- if filter_records:
- parser.rrd_records = filtered_rrd_records
-
- parser.rrd_ds.insert(0, {"type": "GAUGE", "name": "javascript_timestamp"})
-
-
-def main(argv=None):
- opts, args = getopt.getopt(sys.argv[1:], "i:o:h", ["in=", "out=", "help"])
- input_file = sys.stdin
- output_file = sys.stdout
- for o, a in opts:
- if o in ("-i", "--in"):
- input_file = open(a, 'r')
- if o in ("-o", "--out"):
- output_file = open(a, 'w')
- if o in ("-h", "--help"):
- usage()
- sys.exit(0)
-
- # Using the SAX parser as it is at least 4X faster and far, far
- # smaller on this dataset than the DOM-based interface in xml.dom.minidom.
- # With SAX and a 5.4MB xml file, this requires about seven seconds of
- # wall-clock time and 32MB VSZ. With the DOM interface, about 22 seconds
- # and over 270MB VSZ.
-
- handler = SimPerfHostXMLParser()
- sax.parse(input_file, handler)
- if input_file != sys.stdin:
- input_file.close()
-
- # Various format fixups: string-to-num, gauge-to-counts, add
- # a time stamp, etc.
- simperf_host_xml_fixup(handler)
-
- # Create JSONable dict with interesting data and format/print it
- print >>output_file, simplejson.dumps({ "step" : handler.rrd_step,
- "lastupdate": handler.rrd_last_update * 1000,
- "ds" : handler.rrd_ds,
- "database" : handler.rrd_records })
-
- return 0
-
-if __name__ == "__main__":
- sys.exit(main())
diff --git a/indra/lib/python/indra/util/simperf_oprof_interface.py b/indra/lib/python/indra/util/simperf_oprof_interface.py
deleted file mode 100755
index 547d2f9980..0000000000
--- a/indra/lib/python/indra/util/simperf_oprof_interface.py
+++ /dev/null
@@ -1,167 +0,0 @@
-#!/usr/bin/env python
-"""\
-@file simperf_oprof_interface.py
-@brief Manage OProfile data collection on a host
-
-$LicenseInfo:firstyear=2008&license=mit$
-
-Copyright (c) 2008-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-"""
-
-import sys, os, getopt
-import simplejson
-
-
-def usage():
- print "Usage:"
- print sys.argv[0] + " [options]"
- print " Digest the OProfile report forms that come out of the"
- print " simperf_oprof_ctl program's -r/--report command. The result"
- print " is an array of dictionaires with the following keys:"
- print
- print " symbol Name of sampled, calling, or called procedure"
- print " file Executable or library where symbol resides"
- print " percentage Percentage contribution to profile, calls or called"
- print " samples Sample count"
- print " calls Methods called by the method in question (full only)"
- print " called_by Methods calling the method (full only)"
- print
- print " For 'full' reports the two keys 'calls' and 'called_by' are"
- print " themselves arrays of dictionaries based on the first four keys."
- print
- print "Return Codes:"
- print " None. Aggressively digests everything. Will likely mung results"
- print " if a program or library has whitespace in its name."
- print
- print "Options:"
- print " -i, --in Input settings filename. (Default: stdin)"
- print " -o, --out Output settings filename. (Default: stdout)"
- print " -h, --help Print this message and exit."
- print
- print "Interfaces:"
- print " class SimPerfOProfileInterface()"
-
-class SimPerfOProfileInterface:
- def __init__(self):
- self.isBrief = True # public
- self.isValid = False # public
- self.result = [] # public
-
- def parse(self, input):
- in_samples = False
- for line in input:
- if in_samples:
- if line[0:6] == "------":
- self.isBrief = False
- self._parseFull(input)
- else:
- self._parseBrief(input, line)
- self.isValid = True
- return
- try:
- hd1, remain = line.split(None, 1)
- if hd1 == "samples":
- in_samples = True
- except ValueError:
- pass
-
- def _parseBrief(self, input, line1):
- try:
- fld1, fld2, fld3, fld4 = line1.split(None, 3)
- self.result.append({"samples" : fld1,
- "percentage" : fld2,
- "file" : fld3,
- "symbol" : fld4.strip("\n")})
- except ValueError:
- pass
- for line in input:
- try:
- fld1, fld2, fld3, fld4 = line.split(None, 3)
- self.result.append({"samples" : fld1,
- "percentage" : fld2,
- "file" : fld3,
- "symbol" : fld4.strip("\n")})
- except ValueError:
- pass
-
- def _parseFull(self, input):
- state = 0 # In 'called_by' section
- calls = []
- called_by = []
- current = {}
- for line in input:
- if line[0:6] == "------":
- if len(current):
- current["calls"] = calls
- current["called_by"] = called_by
- self.result.append(current)
- state = 0
- calls = []
- called_by = []
- current = {}
- else:
- try:
- fld1, fld2, fld3, fld4 = line.split(None, 3)
- tmp = {"samples" : fld1,
- "percentage" : fld2,
- "file" : fld3,
- "symbol" : fld4.strip("\n")}
- except ValueError:
- continue
- if line[0] != " ":
- current = tmp
- state = 1 # In 'calls' section
- elif state == 0:
- called_by.append(tmp)
- else:
- calls.append(tmp)
- if len(current):
- current["calls"] = calls
- current["called_by"] = called_by
- self.result.append(current)
-
-
-def main(argv=None):
- opts, args = getopt.getopt(sys.argv[1:], "i:o:h", ["in=", "out=", "help"])
- input_file = sys.stdin
- output_file = sys.stdout
- for o, a in opts:
- if o in ("-i", "--in"):
- input_file = open(a, 'r')
- if o in ("-o", "--out"):
- output_file = open(a, 'w')
- if o in ("-h", "--help"):
- usage()
- sys.exit(0)
-
- oprof = SimPerfOProfileInterface()
- oprof.parse(input_file)
- if input_file != sys.stdin:
- input_file.close()
-
- # Create JSONable dict with interesting data and format/print it
- print >>output_file, simplejson.dumps(oprof.result)
-
- return 0
-
-if __name__ == "__main__":
- sys.exit(main())
diff --git a/indra/lib/python/indra/util/simperf_proc_interface.py b/indra/lib/python/indra/util/simperf_proc_interface.py
deleted file mode 100755
index de061f68cc..0000000000
--- a/indra/lib/python/indra/util/simperf_proc_interface.py
+++ /dev/null
@@ -1,191 +0,0 @@
-#!/usr/bin/env python
-"""\
-@file simperf_proc_interface.py
-@brief Utility to extract log messages from *..llsd files containing performance statistics.
-
-$LicenseInfo:firstyear=2008&license=mit$
-
-Copyright (c) 2008-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-"""
-
-# ----------------------------------------------------
-# Utility to extract log messages from *..llsd
-# files that contain performance statistics.
-
-# ----------------------------------------------------
-import sys, os
-
-if os.path.exists("setup-path.py"):
- execfile("setup-path.py")
-
-from indra.base import llsd
-
-DEFAULT_PATH="/dev/shm/simperf/"
-
-
-# ----------------------------------------------------
-# Pull out the stats and return a single document
-def parse_logfile(filename, target_column=None, verbose=False):
- full_doc = []
- # Open source temp log file. Let exceptions percolate up.
- sourcefile = open( filename,'r')
-
- if verbose:
- print "Reading " + filename
-
- # Parse and output all lines from the temp file
- for line in sourcefile.xreadlines():
- partial_doc = llsd.parse(line)
- if partial_doc is not None:
- if target_column is None:
- full_doc.append(partial_doc)
- else:
- trim_doc = { target_column: partial_doc[target_column] }
- if target_column != "fps":
- trim_doc[ 'fps' ] = partial_doc[ 'fps' ]
- trim_doc[ '/total_time' ] = partial_doc[ '/total_time' ]
- trim_doc[ 'utc_time' ] = partial_doc[ 'utc_time' ]
- full_doc.append(trim_doc)
-
- sourcefile.close()
- return full_doc
-
-# Extract just the meta info line, and the timestamp of the first/last frame entry.
-def parse_logfile_info(filename, verbose=False):
- # Open source temp log file. Let exceptions percolate up.
- sourcefile = open(filename, 'rU') # U is to open with Universal newline support
-
- if verbose:
- print "Reading " + filename
-
- # The first line is the meta info line.
- info_line = sourcefile.readline()
- if not info_line:
- sourcefile.close()
- return None
-
- # The rest of the lines are frames. Read the first and last to get the time range.
- info = llsd.parse( info_line )
- info['start_time'] = None
- info['end_time'] = None
- first_frame = sourcefile.readline()
- if first_frame:
- try:
- info['start_time'] = int(llsd.parse(first_frame)['timestamp'])
- except:
- pass
-
- # Read the file backwards to find the last two lines.
- sourcefile.seek(0, 2)
- file_size = sourcefile.tell()
- offset = 1024
- num_attempts = 0
- end_time = None
- if file_size < offset:
- offset = file_size
- while 1:
- sourcefile.seek(-1*offset, 2)
- read_str = sourcefile.read(offset)
- # Remove newline at the end
- if read_str[offset - 1] == '\n':
- read_str = read_str[0:-1]
- lines = read_str.split('\n')
- full_line = None
- if len(lines) > 2: # Got two line
- try:
- end_time = llsd.parse(lines[-1])['timestamp']
- except:
- # We couldn't parse this line. Try once more.
- try:
- end_time = llsd.parse(lines[-2])['timestamp']
- except:
- # Nope. Just move on.
- pass
- break
- if len(read_str) == file_size: # Reached the beginning
- break
- offset += 1024
-
- info['end_time'] = int(end_time)
-
- sourcefile.close()
- return info
-
-
-def parse_proc_filename(filename):
- try:
- name_as_list = filename.split(".")
- cur_stat_type = name_as_list[0].split("_")[0]
- cur_pid = name_as_list[1]
- except IndexError, ValueError:
- return (None, None)
- return (cur_pid, cur_stat_type)
-
-# ----------------------------------------------------
-def get_simstats_list(path=None):
- """ Return stats (pid, type) listed in _proc..llsd """
- if path is None:
- path = DEFAULT_PATH
- simstats_list = []
- for file_name in os.listdir(path):
- if file_name.endswith(".llsd") and file_name != "simperf_proc_config.llsd":
- simstats_info = parse_logfile_info(path + file_name)
- if simstats_info is not None:
- simstats_list.append(simstats_info)
- return simstats_list
-
-def get_log_info_list(pid=None, stat_type=None, path=None, target_column=None, verbose=False):
- """ Return data from all llsd files matching the pid and stat type """
- if path is None:
- path = DEFAULT_PATH
- log_info_list = {}
- for file_name in os.listdir ( path ):
- if file_name.endswith(".llsd") and file_name != "simperf_proc_config.llsd":
- (cur_pid, cur_stat_type) = parse_proc_filename(file_name)
- if cur_pid is None:
- continue
- if pid is not None and pid != cur_pid:
- continue
- if stat_type is not None and stat_type != cur_stat_type:
- continue
- log_info_list[cur_pid] = parse_logfile(path + file_name, target_column, verbose)
- return log_info_list
-
-def delete_simstats_files(pid=None, stat_type=None, path=None):
- """ Delete *..llsd files """
- if path is None:
- path = DEFAULT_PATH
- del_list = []
- for file_name in os.listdir(path):
- if file_name.endswith(".llsd") and file_name != "simperf_proc_config.llsd":
- (cur_pid, cur_stat_type) = parse_proc_filename(file_name)
- if cur_pid is None:
- continue
- if pid is not None and pid != cur_pid:
- continue
- if stat_type is not None and stat_type != cur_stat_type:
- continue
- del_list.append(cur_pid)
- # Allow delete related exceptions to percolate up if this fails.
- os.unlink(os.path.join(DEFAULT_PATH, file_name))
- return del_list
-
diff --git a/indra/lib/python/indra/util/term.py b/indra/lib/python/indra/util/term.py
deleted file mode 100755
index 8c316a1f12..0000000000
--- a/indra/lib/python/indra/util/term.py
+++ /dev/null
@@ -1,222 +0,0 @@
-'''
-@file term.py
-@brief a better shutil.copytree replacement
-
-$LicenseInfo:firstyear=2007&license=mit$
-
-Copyright (c) 2007-2009, Linden Research, Inc.
-
-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.
-$/LicenseInfo$
-'''
-
-#http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/475116
-
-import sys, re
-
-class TerminalController:
- """
- A class that can be used to portably generate formatted output to
- a terminal.
-
- `TerminalController` defines a set of instance variables whose
- values are initialized to the control sequence necessary to
- perform a given action. These can be simply included in normal
- output to the terminal:
-
- >>> term = TerminalController()
- >>> print 'This is '+term.GREEN+'green'+term.NORMAL
-
- Alternatively, the `render()` method can used, which replaces
- '${action}' with the string required to perform 'action':
-
- >>> term = TerminalController()
- >>> print term.render('This is ${GREEN}green${NORMAL}')
-
- If the terminal doesn't support a given action, then the value of
- the corresponding instance variable will be set to ''. As a
- result, the above code will still work on terminals that do not
- support color, except that their output will not be colored.
- Also, this means that you can test whether the terminal supports a
- given action by simply testing the truth value of the
- corresponding instance variable:
-
- >>> term = TerminalController()
- >>> if term.CLEAR_SCREEN:
- ... print 'This terminal supports clearning the screen.'
-
- Finally, if the width and height of the terminal are known, then
- they will be stored in the `COLS` and `LINES` attributes.
- """
- # Cursor movement:
- BOL = '' #: Move the cursor to the beginning of the line
- UP = '' #: Move the cursor up one line
- DOWN = '' #: Move the cursor down one line
- LEFT = '' #: Move the cursor left one char
- RIGHT = '' #: Move the cursor right one char
-
- # Deletion:
- CLEAR_SCREEN = '' #: Clear the screen and move to home position
- CLEAR_EOL = '' #: Clear to the end of the line.
- CLEAR_BOL = '' #: Clear to the beginning of the line.
- CLEAR_EOS = '' #: Clear to the end of the screen
-
- # Output modes:
- BOLD = '' #: Turn on bold mode
- BLINK = '' #: Turn on blink mode
- DIM = '' #: Turn on half-bright mode
- REVERSE = '' #: Turn on reverse-video mode
- NORMAL = '' #: Turn off all modes
-
- # Cursor display:
- HIDE_CURSOR = '' #: Make the cursor invisible
- SHOW_CURSOR = '' #: Make the cursor visible
-
- # Terminal size:
- COLS = None #: Width of the terminal (None for unknown)
- LINES = None #: Height of the terminal (None for unknown)
-
- # Foreground colors:
- BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = ''
-
- # Background colors:
- BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = ''
- BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = ''
-
- _STRING_CAPABILITIES = """
- BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1
- CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1 CLEAR_EOS=ed BOLD=bold
- BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul NORMAL=sgr0
- HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split()
- _COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split()
- _ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split()
-
- def __init__(self, term_stream=sys.stdout):
- """
- Create a `TerminalController` and initialize its attributes
- with appropriate values for the current terminal.
- `term_stream` is the stream that will be used for terminal
- output; if this stream is not a tty, then the terminal is
- assumed to be a dumb terminal (i.e., have no capabilities).
- """
- # Curses isn't available on all platforms
- try: import curses
- except: return
-
- # If the stream isn't a tty, then assume it has no capabilities.
- if not term_stream.isatty(): return
-
- # Check the terminal type. If we fail, then assume that the
- # terminal has no capabilities.
- try: curses.setupterm()
- except: return
-
- # Look up numeric capabilities.
- self.COLS = curses.tigetnum('cols')
- self.LINES = curses.tigetnum('lines')
-
- # Look up string capabilities.
- for capability in self._STRING_CAPABILITIES:
- (attrib, cap_name) = capability.split('=')
- setattr(self, attrib, self._tigetstr(cap_name) or '')
-
- # Colors
- set_fg = self._tigetstr('setf')
- if set_fg:
- for i,color in zip(range(len(self._COLORS)), self._COLORS):
- setattr(self, color, curses.tparm(set_fg, i) or '')
- set_fg_ansi = self._tigetstr('setaf')
- if set_fg_ansi:
- for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
- setattr(self, color, curses.tparm(set_fg_ansi, i) or '')
- set_bg = self._tigetstr('setb')
- if set_bg:
- for i,color in zip(range(len(self._COLORS)), self._COLORS):
- setattr(self, 'BG_'+color, curses.tparm(set_bg, i) or '')
- set_bg_ansi = self._tigetstr('setab')
- if set_bg_ansi:
- for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
- setattr(self, 'BG_'+color, curses.tparm(set_bg_ansi, i) or '')
-
- def _tigetstr(self, cap_name):
- # String capabilities can include "delays" of the form "$<2>".
- # For any modern terminal, we should be able to just ignore
- # these, so strip them out.
- import curses
- cap = curses.tigetstr(cap_name) or ''
- return re.sub(r'\$<\d+>[/*]?', '', cap)
-
- def render(self, template):
- """
- Replace each $-substitutions in the given template string with
- the corresponding terminal control string (if it's defined) or
- '' (if it's not).
- """
- return re.sub(r'\$\$|\${\w+}', self._render_sub, template)
-
- def _render_sub(self, match):
- s = match.group()
- if s == '$$': return s
- else: return getattr(self, s[2:-1])
-
-#######################################################################
-# Example use case: progress bar
-#######################################################################
-
-class ProgressBar:
- """
- A 3-line progress bar, which looks like::
-
- Header
- 20% [===========----------------------------------]
- progress message
-
- The progress bar is colored, if the terminal supports color
- output; and adjusts to the width of the terminal.
- """
- BAR = '%3d%% ${GREEN}[${BOLD}%s%s${NORMAL}${GREEN}]${NORMAL}\n'
- HEADER = '${BOLD}${CYAN}%s${NORMAL}\n\n'
-
- def __init__(self, term, header):
- self.term = term
- if not (self.term.CLEAR_EOL and self.term.UP and self.term.BOL):
- raise ValueError("Terminal isn't capable enough -- you "
- "should use a simpler progress dispaly.")
- self.width = self.term.COLS or 75
- self.bar = term.render(self.BAR)
- self.header = self.term.render(self.HEADER % header.center(self.width))
- self.cleared = 1 #: true if we haven't drawn the bar yet.
- self.update(0, '')
-
- def update(self, percent, message):
- if self.cleared:
- sys.stdout.write(self.header)
- self.cleared = 0
- n = int((self.width-10)*percent)
- sys.stdout.write(
- self.term.BOL + self.term.UP + self.term.CLEAR_EOL +
- (self.bar % (100*percent, '='*n, '-'*(self.width-10-n))) +
- self.term.CLEAR_EOL + message.center(self.width))
-
- def clear(self):
- if not self.cleared:
- sys.stdout.write(self.term.BOL + self.term.CLEAR_EOL +
- self.term.UP + self.term.CLEAR_EOL +
- self.term.UP + self.term.CLEAR_EOL)
- self.cleared = 1
diff --git a/indra/lib/python/uuid.py b/indra/lib/python/uuid.py
deleted file mode 100755
index e956383cca..0000000000
--- a/indra/lib/python/uuid.py
+++ /dev/null
@@ -1,508 +0,0 @@
-#!/usr/bin/python
-## $LicenseInfo:firstyear=2011&license=viewerlgpl$
-## Second Life Viewer Source Code
-## Copyright (C) 2011, Linden Research, Inc.
-##
-## 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;
-## version 2.1 of the License only.
-##
-## 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
-##
-## Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
-## $/LicenseInfo$
-r"""UUID objects (universally unique identifiers) according to RFC 4122.
-
-This module provides immutable UUID objects (class UUID) and the functions
-uuid1(), uuid3(), uuid4(), uuid5() for generating version 1, 3, 4, and 5
-UUIDs as specified in RFC 4122.
-
-If all you want is a unique ID, you should probably call uuid1() or uuid4().
-Note that uuid1() may compromise privacy since it creates a UUID containing
-the computer's network address. uuid4() creates a random UUID.
-
-Typical usage:
-
- >>> import uuid
-
- # make a UUID based on the host ID and current time
- >>> uuid.uuid1()
- UUID('a8098c1a-f86e-11da-bd1a-00112444be1e')
-
- # make a UUID using an MD5 hash of a namespace UUID and a name
- >>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org')
- UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e')
-
- # make a random UUID
- >>> uuid.uuid4()
- UUID('16fd2706-8baf-433b-82eb-8c7fada847da')
-
- # make a UUID using a SHA-1 hash of a namespace UUID and a name
- >>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')
- UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')
-
- # make a UUID from a string of hex digits (braces and hyphens ignored)
- >>> x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}')
-
- # convert a UUID to a string of hex digits in standard form
- >>> str(x)
- '00010203-0405-0607-0809-0a0b0c0d0e0f'
-
- # get the raw 16 bytes of the UUID
- >>> x.bytes
- '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f'
-
- # make a UUID from a 16-byte string
- >>> uuid.UUID(bytes=x.bytes)
- UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')
-
-This module works with Python 2.3 or higher."""
-
-__author__ = 'Ka-Ping Yee '
-__date__ = '$Date: 2006/06/12 23:15:40 $'.split()[1].replace('/', '-')
-__version__ = '$Revision: 1.30 $'.split()[1]
-
-RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE = [
- 'reserved for NCS compatibility', 'specified in RFC 4122',
- 'reserved for Microsoft compatibility', 'reserved for future definition']
-
-class UUID(object):
- """Instances of the UUID class represent UUIDs as specified in RFC 4122.
- UUID objects are immutable, hashable, and usable as dictionary keys.
- Converting a UUID to a string with str() yields something in the form
- '12345678-1234-1234-1234-123456789abc'. The UUID constructor accepts
- four possible forms: a similar string of hexadecimal digits, or a
- string of 16 raw bytes as an argument named 'bytes', or a tuple of
- six integer fields (with 32-bit, 16-bit, 16-bit, 8-bit, 8-bit, and
- 48-bit values respectively) as an argument named 'fields', or a single
- 128-bit integer as an argument named 'int'.
-
- UUIDs have these read-only attributes:
-
- bytes the UUID as a 16-byte string
-
- fields a tuple of the six integer fields of the UUID,
- which are also available as six individual attributes
- and two derived attributes:
-
- time_low the first 32 bits of the UUID
- time_mid the next 16 bits of the UUID
- time_hi_version the next 16 bits of the UUID
- clock_seq_hi_variant the next 8 bits of the UUID
- clock_seq_low the next 8 bits of the UUID
- node the last 48 bits of the UUID
-
- time the 60-bit timestamp
- clock_seq the 14-bit sequence number
-
- hex the UUID as a 32-character hexadecimal string
-
- int the UUID as a 128-bit integer
-
- urn the UUID as a URN as specified in RFC 4122
-
- variant the UUID variant (one of the constants RESERVED_NCS,
- RFC_4122, RESERVED_MICROSOFT, or RESERVED_FUTURE)
-
- version the UUID version number (1 through 5, meaningful only
- when the variant is RFC_4122)
- """
-
- def __init__(self, hex=None, bytes=None, fields=None, int=None,
- version=None):
- r"""Create a UUID from either a string of 32 hexadecimal digits,
- a string of 16 bytes as the 'bytes' argument, a tuple of six
- integers (32-bit time_low, 16-bit time_mid, 16-bit time_hi_version,
- 8-bit clock_seq_hi_variant, 8-bit clock_seq_low, 48-bit node) as
- the 'fields' argument, or a single 128-bit integer as the 'int'
- argument. When a string of hex digits is given, curly braces,
- hyphens, and a URN prefix are all optional. For example, these
- expressions all yield the same UUID:
-
- UUID('{12345678-1234-5678-1234-567812345678}')
- UUID('12345678123456781234567812345678')
- UUID('urn:uuid:12345678-1234-5678-1234-567812345678')
- UUID(bytes='\x12\x34\x56\x78'*4)
- UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678))
- UUID(int=0x12345678123456781234567812345678)
-
- Exactly one of 'hex', 'bytes', 'fields', or 'int' must be given.
- The 'version' argument is optional; if given, the resulting UUID
- will have its variant and version number set according to RFC 4122,
- overriding bits in the given 'hex', 'bytes', 'fields', or 'int'.
- """
-
- if [hex, bytes, fields, int].count(None) != 3:
- raise TypeError('need just one of hex, bytes, fields, or int')
- if hex is not None:
- hex = hex.replace('urn:', '').replace('uuid:', '')
- hex = hex.strip('{}').replace('-', '')
- if len(hex) != 32:
- raise ValueError('badly formed hexadecimal UUID string')
- int = long(hex, 16)
- if bytes is not None:
- if len(bytes) != 16:
- raise ValueError('bytes is not a 16-char string')
- int = long(('%02x'*16) % tuple(map(ord, bytes)), 16)
- if fields is not None:
- if len(fields) != 6:
- raise ValueError('fields is not a 6-tuple')
- (time_low, time_mid, time_hi_version,
- clock_seq_hi_variant, clock_seq_low, node) = fields
- if not 0 <= time_low < 1<<32L:
- raise ValueError('field 1 out of range (need a 32-bit value)')
- if not 0 <= time_mid < 1<<16L:
- raise ValueError('field 2 out of range (need a 16-bit value)')
- if not 0 <= time_hi_version < 1<<16L:
- raise ValueError('field 3 out of range (need a 16-bit value)')
- if not 0 <= clock_seq_hi_variant < 1<<8L:
- raise ValueError('field 4 out of range (need an 8-bit value)')
- if not 0 <= clock_seq_low < 1<<8L:
- raise ValueError('field 5 out of range (need an 8-bit value)')
- if not 0 <= node < 1<<48L:
- raise ValueError('field 6 out of range (need a 48-bit value)')
- clock_seq = (clock_seq_hi_variant << 8L) | clock_seq_low
- int = ((time_low << 96L) | (time_mid << 80L) |
- (time_hi_version << 64L) | (clock_seq << 48L) | node)
- if int is not None:
- if not 0 <= int < 1<<128L:
- raise ValueError('int is out of range (need a 128-bit value)')
- if version is not None:
- if not 1 <= version <= 5:
- raise ValueError('illegal version number')
- # Set the variant to RFC 4122.
- int &= ~(0xc000 << 48L)
- int |= 0x8000 << 48L
- # Set the version number.
- int &= ~(0xf000 << 64L)
- int |= version << 76L
- self.__dict__['int'] = int
-
- def __cmp__(self, other):
- if isinstance(other, UUID):
- return cmp(self.int, other.int)
- return NotImplemented
-
- def __hash__(self):
- return hash(self.int)
-
- def __int__(self):
- return self.int
-
- def __repr__(self):
- return 'UUID(%r)' % str(self)
-
- def __setattr__(self, name, value):
- raise TypeError('UUID objects are immutable')
-
- def __str__(self):
- hex = '%032x' % self.int
- return '%s-%s-%s-%s-%s' % (
- hex[:8], hex[8:12], hex[12:16], hex[16:20], hex[20:])
-
- def get_bytes(self):
- bytes = ''
- for shift in range(0, 128, 8):
- bytes = chr((self.int >> shift) & 0xff) + bytes
- return bytes
-
- bytes = property(get_bytes)
-
- def get_fields(self):
- return (self.time_low, self.time_mid, self.time_hi_version,
- self.clock_seq_hi_variant, self.clock_seq_low, self.node)
-
- fields = property(get_fields)
-
- def get_time_low(self):
- return self.int >> 96L
-
- time_low = property(get_time_low)
-
- def get_time_mid(self):
- return (self.int >> 80L) & 0xffff
-
- time_mid = property(get_time_mid)
-
- def get_time_hi_version(self):
- return (self.int >> 64L) & 0xffff
-
- time_hi_version = property(get_time_hi_version)
-
- def get_clock_seq_hi_variant(self):
- return (self.int >> 56L) & 0xff
-
- clock_seq_hi_variant = property(get_clock_seq_hi_variant)
-
- def get_clock_seq_low(self):
- return (self.int >> 48L) & 0xff
-
- clock_seq_low = property(get_clock_seq_low)
-
- def get_time(self):
- return (((self.time_hi_version & 0x0fffL) << 48L) |
- (self.time_mid << 32L) | self.time_low)
-
- time = property(get_time)
-
- def get_clock_seq(self):
- return (((self.clock_seq_hi_variant & 0x3fL) << 8L) |
- self.clock_seq_low)
-
- clock_seq = property(get_clock_seq)
-
- def get_node(self):
- return self.int & 0xffffffffffff
-
- node = property(get_node)
-
- def get_hex(self):
- return '%032x' % self.int
-
- hex = property(get_hex)
-
- def get_urn(self):
- return 'urn:uuid:' + str(self)
-
- urn = property(get_urn)
-
- def get_variant(self):
- if not self.int & (0x8000 << 48L):
- return RESERVED_NCS
- elif not self.int & (0x4000 << 48L):
- return RFC_4122
- elif not self.int & (0x2000 << 48L):
- return RESERVED_MICROSOFT
- else:
- return RESERVED_FUTURE
-
- variant = property(get_variant)
-
- def get_version(self):
- # The version bits are only meaningful for RFC 4122 UUIDs.
- if self.variant == RFC_4122:
- return int((self.int >> 76L) & 0xf)
-
- version = property(get_version)
-
-def _ifconfig_getnode():
- """Get the hardware address on Unix by running ifconfig."""
- import os
- for dir in ['', '/sbin/', '/usr/sbin']:
- try:
- path = os.path.join(dir, 'ifconfig')
- if os.path.exists(path):
- pipe = os.popen(path)
- else:
- continue
- except IOError:
- continue
- for line in pipe:
- words = line.lower().split()
- for i in range(len(words)):
- if words[i] in ['hwaddr', 'ether']:
- return int(words[i + 1].replace(':', ''), 16)
-
-def _ipconfig_getnode():
- """Get the hardware address on Windows by running ipconfig.exe."""
- import os, re
- dirs = ['', r'c:\windows\system32', r'c:\winnt\system32']
- try:
- import ctypes
- buffer = ctypes.create_string_buffer(300)
- ctypes.windll.kernel32.GetSystemDirectoryA(buffer, 300)
- dirs.insert(0, buffer.value.decode('mbcs'))
- except:
- pass
- for dir in dirs:
- try:
- pipe = os.popen(os.path.join(dir, 'ipconfig') + ' /all')
- except IOError:
- continue
- for line in pipe:
- value = line.split(':')[-1].strip().lower()
- if re.match('([0-9a-f][0-9a-f]-){5}[0-9a-f][0-9a-f]', value):
- return int(value.replace('-', ''), 16)
-
-def _netbios_getnode():
- """Get the hardware address on Windows using NetBIOS calls.
- See http://support.microsoft.com/kb/118623 for details."""
- import win32wnet, netbios
- ncb = netbios.NCB()
- ncb.Command = netbios.NCBENUM
- ncb.Buffer = adapters = netbios.LANA_ENUM()
- adapters._pack()
- if win32wnet.Netbios(ncb) != 0:
- return
- adapters._unpack()
- for i in range(adapters.length):
- ncb.Reset()
- ncb.Command = netbios.NCBRESET
- ncb.Lana_num = ord(adapters.lana[i])
- if win32wnet.Netbios(ncb) != 0:
- continue
- ncb.Reset()
- ncb.Command = netbios.NCBASTAT
- ncb.Lana_num = ord(adapters.lana[i])
- ncb.Callname = '*'.ljust(16)
- ncb.Buffer = status = netbios.ADAPTER_STATUS()
- if win32wnet.Netbios(ncb) != 0:
- continue
- status._unpack()
- bytes = map(ord, status.adapter_address)
- return ((bytes[0]<<40L) + (bytes[1]<<32L) + (bytes[2]<<24L) +
- (bytes[3]<<16L) + (bytes[4]<<8L) + bytes[5])
-
-# Thanks to Thomas Heller for ctypes and for his help with its use here.
-
-# If ctypes is available, use it to find system routines for UUID generation.
-_uuid_generate_random = _uuid_generate_time = _UuidCreate = None
-try:
- import ctypes, ctypes.util
- _buffer = ctypes.create_string_buffer(16)
-
- # The uuid_generate_* routines are provided by libuuid on at least
- # Linux and FreeBSD, and provided by libc on Mac OS X.
- for libname in ['uuid', 'c']:
- try:
- lib = ctypes.CDLL(ctypes.util.find_library(libname))
- except:
- continue
- if hasattr(lib, 'uuid_generate_random'):
- _uuid_generate_random = lib.uuid_generate_random
- if hasattr(lib, 'uuid_generate_time'):
- _uuid_generate_time = lib.uuid_generate_time
-
- # On Windows prior to 2000, UuidCreate gives a UUID containing the
- # hardware address. On Windows 2000 and later, UuidCreate makes a
- # random UUID and UuidCreateSequential gives a UUID containing the
- # hardware address. These routines are provided by the RPC runtime.
- try:
- lib = ctypes.windll.rpcrt4
- except:
- lib = None
- _UuidCreate = getattr(lib, 'UuidCreateSequential',
- getattr(lib, 'UuidCreate', None))
-except:
- pass
-
-def _unixdll_getnode():
- """Get the hardware address on Unix using ctypes."""
- _uuid_generate_time(_buffer)
- return UUID(bytes=_buffer.raw).node
-
-def _windll_getnode():
- """Get the hardware address on Windows using ctypes."""
- if _UuidCreate(_buffer) == 0:
- return UUID(bytes=_buffer.raw).node
-
-def _random_getnode():
- """Get a random node ID, with eighth bit set as suggested by RFC 4122."""
- import random
- return random.randrange(0, 1<<48L) | 0x010000000000L
-
-_node = None
-
-def getnode():
- """Get the hardware address as a 48-bit integer. The first time this
- runs, it may launch a separate program, which could be quite slow. If
- all attempts to obtain the hardware address fail, we choose a random
- 48-bit number with its eighth bit set to 1 as recommended in RFC 4122."""
-
- global _node
- if _node is not None:
- return _node
-
- import sys
- if sys.platform == 'win32':
- getters = [_windll_getnode, _netbios_getnode, _ipconfig_getnode]
- else:
- getters = [_unixdll_getnode, _ifconfig_getnode]
-
- for getter in getters + [_random_getnode]:
- try:
- _node = getter()
- except:
- continue
- if _node is not None:
- return _node
-
-def uuid1(node=None, clock_seq=None):
- """Generate a UUID from a host ID, sequence number, and the current time.
- If 'node' is not given, getnode() is used to obtain the hardware
- address. If 'clock_seq' is given, it is used as the sequence number;
- otherwise a random 14-bit sequence number is chosen."""
-
- # When the system provides a version-1 UUID generator, use it (but don't
- # use UuidCreate here because its UUIDs don't conform to RFC 4122).
- if _uuid_generate_time and node is clock_seq is None:
- _uuid_generate_time(_buffer)
- return UUID(bytes=_buffer.raw)
-
- import time
- nanoseconds = int(time.time() * 1e9)
- # 0x01b21dd213814000 is the number of 100-ns intervals between the
- # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
- timestamp = int(nanoseconds/100) + 0x01b21dd213814000L
- if clock_seq is None:
- import random
- clock_seq = random.randrange(1<<14L) # instead of stable storage
- time_low = timestamp & 0xffffffffL
- time_mid = (timestamp >> 32L) & 0xffffL
- time_hi_version = (timestamp >> 48L) & 0x0fffL
- clock_seq_low = clock_seq & 0xffL
- clock_seq_hi_variant = (clock_seq >> 8L) & 0x3fL
- if node is None:
- node = getnode()
- return UUID(fields=(time_low, time_mid, time_hi_version,
- clock_seq_hi_variant, clock_seq_low, node), version=1)
-
-def uuid3(namespace, name):
- """Generate a UUID from the MD5 hash of a namespace UUID and a name."""
- try:
- # Python 2.6
- from hashlib import md5
- except ImportError:
- # Python 2.5 and earlier
- from md5 import new as md5
-
- hash = md5(namespace.bytes + name).digest()
- return UUID(bytes=hash[:16], version=3)
-
-def uuid4():
- """Generate a random UUID."""
-
- # When the system provides a version-4 UUID generator, use it.
- if _uuid_generate_random:
- _uuid_generate_random(_buffer)
- return UUID(bytes=_buffer.raw)
-
- # Otherwise, get randomness from urandom or the 'random' module.
- try:
- import os
- return UUID(bytes=os.urandom(16), version=4)
- except:
- import random
- bytes = [chr(random.randrange(256)) for i in range(16)]
- return UUID(bytes=bytes, version=4)
-
-def uuid5(namespace, name):
- """Generate a UUID from the SHA-1 hash of a namespace UUID and a name."""
- import sha
- hash = sha.sha(namespace.bytes + name).digest()
- return UUID(bytes=hash[:16], version=5)
-
-# The following standard UUIDs are for use with uuid3() or uuid5().
-
-NAMESPACE_DNS = UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8')
-NAMESPACE_URL = UUID('6ba7b811-9dad-11d1-80b4-00c04fd430c8')
-NAMESPACE_OID = UUID('6ba7b812-9dad-11d1-80b4-00c04fd430c8')
-NAMESPACE_X500 = UUID('6ba7b814-9dad-11d1-80b4-00c04fd430c8')
diff --git a/indra/llappearance/llavatarappearance.cpp b/indra/llappearance/llavatarappearance.cpp
index d1eb389013..94c431feb4 100644
--- a/indra/llappearance/llavatarappearance.cpp
+++ b/indra/llappearance/llavatarappearance.cpp
@@ -44,6 +44,7 @@
#include "lltexglobalcolor.h"
#include "llwearabledata.h"
#include "boost/bind.hpp"
+#include "boost/tokenizer.hpp"
#if LL_MSVC
@@ -87,8 +88,11 @@ public:
private:
std::string mName;
+ std::string mSupport;
+ std::string mAliases;
BOOL mIsJoint;
LLVector3 mPos;
+ LLVector3 mEnd;
LLVector3 mRot;
LLVector3 mScale;
LLVector3 mPivot;
@@ -118,6 +122,7 @@ public:
private:
S32 mNumBones;
S32 mNumCollisionVolumes;
+ LLAvatarAppearance::joint_alias_map_t mJointAliasMap;
typedef std::vector bone_info_list_t;
bone_info_list_t mBoneInfoList;
};
@@ -181,7 +186,11 @@ LLAvatarAppearance::LLAvatarAppearance(LLWearableData* wearable_data) :
mPelvisToFoot(0.f),
mHeadOffset(),
mRoot(NULL),
- mWearableData(wearable_data)
+ mWearableData(wearable_data),
+ mNumBones(0),
+ mNumCollisionVolumes(0),
+ mCollisionVolumes(NULL),
+ mIsBuilt(FALSE)
{
llassert_always(mWearableData);
mBakedTextureDatas.resize(LLAvatarAppearanceDefines::BAKED_NUM_INDICES);
@@ -194,11 +203,6 @@ LLAvatarAppearance::LLAvatarAppearance(LLWearableData* wearable_data) :
mBakedTextureDatas[i].mMaskTexName = 0;
mBakedTextureDatas[i].mTextureIndex = LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::bakedToLocalTextureIndex((LLAvatarAppearanceDefines::EBakedTextureIndex)i);
}
-
- mIsBuilt = FALSE;
-
- mNumCollisionVolumes = 0;
- mCollisionVolumes = NULL;
}
// virtual
@@ -323,36 +327,49 @@ LLAvatarAppearance::~LLAvatarAppearance()
//static
void LLAvatarAppearance::initClass()
{
- std::string xmlFile;
+ initClass("","");
+}
- xmlFile = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,AVATAR_DEFAULT_CHAR) + "_lad.xml";
- BOOL success = sXMLTree.parseFile( xmlFile, FALSE );
+//static
+void LLAvatarAppearance::initClass(const std::string& avatar_file_name_arg, const std::string& skeleton_file_name_arg)
+{
+ std::string avatar_file_name;
+
+ if (!avatar_file_name_arg.empty())
+ {
+ avatar_file_name = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,avatar_file_name_arg);
+ }
+ else
+ {
+ avatar_file_name = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,AVATAR_DEFAULT_CHAR + "_lad.xml");
+ }
+ BOOL success = sXMLTree.parseFile( avatar_file_name, FALSE );
if (!success)
{
- LL_ERRS() << "Problem reading avatar configuration file:" << xmlFile << LL_ENDL;
+ LL_ERRS() << "Problem reading avatar configuration file:" << avatar_file_name << LL_ENDL;
}
// now sanity check xml file
LLXmlTreeNode* root = sXMLTree.getRoot();
if (!root)
{
- LL_ERRS() << "No root node found in avatar configuration file: " << xmlFile << LL_ENDL;
+ LL_ERRS() << "No root node found in avatar configuration file: " << avatar_file_name << LL_ENDL;
return;
}
//-------------------------------------------------------------------------
- // (root)
+ // (root)
//-------------------------------------------------------------------------
if( !root->hasName( "linden_avatar" ) )
{
- LL_ERRS() << "Invalid avatar file header: " << xmlFile << LL_ENDL;
+ LL_ERRS() << "Invalid avatar file header: " << avatar_file_name << LL_ENDL;
}
std::string version;
static LLStdStringHandle version_string = LLXmlTree::addAttributeString("version");
- if( !root->getFastAttributeString( version_string, version ) || (version != "1.0") )
+ if( !root->getFastAttributeString( version_string, version ) || ((version != "1.0") && (version != "2.0")))
{
- LL_ERRS() << "Invalid avatar file version: " << version << " in file: " << xmlFile << LL_ENDL;
+ LL_ERRS() << "Invalid avatar file version: " << version << " in file: " << avatar_file_name << LL_ENDL;
}
S32 wearable_def_version = 1;
@@ -365,16 +382,19 @@ void LLAvatarAppearance::initClass()
LLXmlTreeNode* skeleton_node = root->getChildByName( "skeleton" );
if (!skeleton_node)
{
- LL_ERRS() << "No skeleton in avatar configuration file: " << xmlFile << LL_ENDL;
+ LL_ERRS() << "No skeleton in avatar configuration file: " << avatar_file_name << LL_ENDL;
return;
}
-
- std::string skeleton_file_name;
- static LLStdStringHandle file_name_string = LLXmlTree::addAttributeString("file_name");
- if (!skeleton_node->getFastAttributeString(file_name_string, skeleton_file_name))
- {
- LL_ERRS() << "No file name in skeleton node in avatar config file: " << xmlFile << LL_ENDL;
- }
+
+ std::string skeleton_file_name = skeleton_file_name_arg;
+ if (skeleton_file_name.empty())
+ {
+ static LLStdStringHandle file_name_string = LLXmlTree::addAttributeString("file_name");
+ if (!skeleton_node->getFastAttributeString(file_name_string, skeleton_file_name))
+ {
+ LL_ERRS() << "No file name in skeleton node in avatar config file: " << avatar_file_name << LL_ENDL;
+ }
+ }
std::string skeleton_path;
skeleton_path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,skeleton_file_name);
@@ -437,11 +457,56 @@ void LLAvatarAppearance::cleanupClass()
using namespace LLAvatarAppearanceDefines;
+void LLAvatarAppearance::compareJointStateMaps(joint_state_map_t& last_state,
+ joint_state_map_t& curr_state)
+{
+ if (!last_state.empty() && (last_state != curr_state))
+ {
+ S32 diff_count = 0;
+ joint_state_map_t::iterator it;
+ for (it=last_state.begin(); it != last_state.end(); ++it)
+ {
+ const std::string& key = it->first;
+ if (last_state[key] != curr_state[key])
+ {
+ LL_DEBUGS("AvatarBodySize") << "BodySize change " << key << " " << last_state[key] << "->" << curr_state[key] << LL_ENDL;
+ diff_count++;
+ }
+ }
+ if (diff_count > 0)
+ {
+ LL_DEBUGS("AvatarBodySize") << "Total of BodySize changes " << diff_count << LL_ENDL;
+ }
+
+ }
+}
+
//------------------------------------------------------------------------
// The viewer can only suggest a good size for the agent,
// the simulator will keep it inside a reasonable range.
void LLAvatarAppearance::computeBodySize()
{
+ mLastBodySizeState = mCurrBodySizeState;
+
+ mCurrBodySizeState["mPelvis scale"] = mPelvisp->getScale();
+ mCurrBodySizeState["mSkull pos"] = mSkullp->getPosition();
+ mCurrBodySizeState["mSkull scale"] = mSkullp->getScale();
+ mCurrBodySizeState["mNeck pos"] = mNeckp->getPosition();
+ mCurrBodySizeState["mNeck scale"] = mNeckp->getScale();
+ mCurrBodySizeState["mChest pos"] = mChestp->getPosition();
+ mCurrBodySizeState["mChest scale"] = mChestp->getScale();
+ mCurrBodySizeState["mHead pos"] = mHeadp->getPosition();
+ mCurrBodySizeState["mHead scale"] = mHeadp->getScale();
+ mCurrBodySizeState["mTorso pos"] = mTorsop->getPosition();
+ mCurrBodySizeState["mTorso scale"] = mTorsop->getScale();
+ mCurrBodySizeState["mHipLeft pos"] = mHipLeftp->getPosition();
+ mCurrBodySizeState["mHipLeft scale"] = mHipLeftp->getScale();
+ mCurrBodySizeState["mKneeLeft pos"] = mKneeLeftp->getPosition();
+ mCurrBodySizeState["mKneeLeft scale"] = mKneeLeftp->getScale();
+ mCurrBodySizeState["mAnkleLeft pos"] = mAnkleLeftp->getPosition();
+ mCurrBodySizeState["mAnkleLeft scale"] = mAnkleLeftp->getScale();
+ mCurrBodySizeState["mFootLeft pos"] = mFootLeftp->getPosition();
+
LLVector3 pelvis_scale = mPelvisp->getScale();
// some of the joints have not been cached
@@ -501,6 +566,8 @@ void LLAvatarAppearance::computeBodySize()
if (new_body_size != mBodySize || old_offset != mAvatarOffset.mV[VZ])
{
mBodySize = new_body_size;
+
+ compareJointStateMaps(mLastBodySizeState, mCurrBodySizeState);
}
}
@@ -536,7 +603,7 @@ BOOL LLAvatarAppearance::parseSkeletonFile(const std::string& filename)
std::string version;
static LLStdStringHandle version_string = LLXmlTree::addAttributeString("version");
- if( !root->getFastAttributeString( version_string, version ) || (version != "1.0") )
+ if( !root->getFastAttributeString( version_string, version ) || ((version != "1.0") && (version != "2.0")))
{
LL_ERRS() << "Invalid avatar skeleton file version: " << version << " in file: " << filename << LL_ENDL;
return FALSE;
@@ -552,6 +619,12 @@ BOOL LLAvatarAppearance::setupBone(const LLAvatarBoneInfo* info, LLJoint* parent
{
LLJoint* joint = NULL;
+ LL_DEBUGS("BVH") << "bone info: name " << info->mName
+ << " isJoint " << info->mIsJoint
+ << " volume_num " << volume_num
+ << " joint_num " << joint_num
+ << LL_ENDL;
+
if (info->mIsJoint)
{
joint = getCharacterJoint(joint_num);
@@ -566,7 +639,7 @@ BOOL LLAvatarAppearance::setupBone(const LLAvatarBoneInfo* info, LLJoint* parent
{
if (volume_num >= (S32)mNumCollisionVolumes)
{
- LL_WARNS() << "Too many bones" << LL_ENDL;
+ LL_WARNS() << "Too many collision volumes" << LL_ENDL;
return FALSE;
}
joint = (&mCollisionVolumes[volume_num]);
@@ -574,26 +647,34 @@ BOOL LLAvatarAppearance::setupBone(const LLAvatarBoneInfo* info, LLJoint* parent
}
// add to parent
- if (parent)
+ if (parent && (joint->getParent()!=parent))
{
parent->addChild( joint );
}
+ // SL-315
joint->setPosition(info->mPos);
+ joint->setDefaultPosition(info->mPos);
joint->setRotation(mayaQ(info->mRot.mV[VX], info->mRot.mV[VY],
info->mRot.mV[VZ], LLQuaternion::XYZ));
joint->setScale(info->mScale);
+ joint->setDefaultScale(info->mScale);
+ joint->setSupport(info->mSupport);
+ joint->setEnd(info->mEnd);
if (info->mIsJoint)
{
joint->setSkinOffset( info->mPivot );
+ joint->setJointNum(joint_num);
joint_num++;
}
else // collision volume
{
+ joint->setJointNum(mNumBones+volume_num);
volume_num++;
}
+
// setup children
LLAvatarBoneInfo::child_list_t::const_iterator iter;
for (iter = info->mChildList.begin(); iter != info->mChildList.end(); ++iter)
@@ -613,12 +694,12 @@ BOOL LLAvatarAppearance::setupBone(const LLAvatarBoneInfo* info, LLJoint* parent
//-----------------------------------------------------------------------------
BOOL LLAvatarAppearance::allocateCharacterJoints( U32 num )
{
- clearSkeleton();
-
- for(S32 joint_num = 0; joint_num < (S32)num; joint_num++)
- {
- mSkeleton.push_back(createAvatarJoint(joint_num));
- }
+ if (mSkeleton.size() != num)
+ {
+ clearSkeleton();
+ mSkeleton = avatar_joint_list_t(num,NULL);
+ mNumBones = num;
+ }
return TRUE;
}
@@ -629,18 +710,16 @@ BOOL LLAvatarAppearance::allocateCharacterJoints( U32 num )
//-----------------------------------------------------------------------------
BOOL LLAvatarAppearance::buildSkeleton(const LLAvatarSkeletonInfo *info)
{
- //-------------------------------------------------------------------------
+ LL_DEBUGS("BVH") << "numBones " << info->mNumBones << " numCollisionVolumes " << info->mNumCollisionVolumes << LL_ENDL;
+
// allocate joints
- //-------------------------------------------------------------------------
if (!allocateCharacterJoints(info->mNumBones))
{
LL_ERRS() << "Can't allocate " << info->mNumBones << " joints" << LL_ENDL;
return FALSE;
}
- //-------------------------------------------------------------------------
// allocate volumes
- //-------------------------------------------------------------------------
if (info->mNumCollisionVolumes)
{
if (!allocateCollisionVolumes(info->mNumCollisionVolumes))
@@ -655,8 +734,8 @@ BOOL LLAvatarAppearance::buildSkeleton(const LLAvatarSkeletonInfo *info)
LLAvatarSkeletonInfo::bone_info_list_t::const_iterator iter;
for (iter = info->mBoneInfoList.begin(); iter != info->mBoneInfoList.end(); ++iter)
{
- LLAvatarBoneInfo *info = *iter;
- if (!setupBone(info, NULL, current_volume_num, current_joint_num))
+ LLAvatarBoneInfo *bone_info = *iter;
+ if (!setupBone(bone_info, NULL, current_volume_num, current_joint_num))
{
LL_ERRS() << "Error parsing bone in skeleton file" << LL_ENDL;
return FALSE;
@@ -820,6 +899,7 @@ void LLAvatarAppearance::buildCharacter()
//-------------------------------------------------------------------------
// initialize the pelvis
//-------------------------------------------------------------------------
+ // SL-315
mPelvisp->setPosition( LLVector3(0.0f, 0.0f, 0.0f) );
mIsBuilt = TRUE;
@@ -834,21 +914,21 @@ BOOL LLAvatarAppearance::loadAvatar()
// avatar_skeleton.xml
if( !buildSkeleton(sAvatarSkeletonInfo) )
{
- LL_WARNS() << "avatar file: buildSkeleton() failed" << LL_ENDL;
+ LL_ERRS() << "avatar file: buildSkeleton() failed" << LL_ENDL;
return FALSE;
}
// avatar_lad.xml :
if( !loadSkeletonNode() )
{
- LL_WARNS() << "avatar file: loadNodeSkeleton() failed" << LL_ENDL;
+ LL_ERRS() << "avatar file: loadNodeSkeleton() failed" << LL_ENDL;
return FALSE;
}
// avatar_lad.xml :
if( !loadMeshNodes() )
{
- LL_WARNS() << "avatar file: loadNodeMesh() failed" << LL_ENDL;
+ LL_ERRS() << "avatar file: loadNodeMesh() failed" << LL_ENDL;
return FALSE;
}
@@ -858,13 +938,13 @@ BOOL LLAvatarAppearance::loadAvatar()
mTexSkinColor = new LLTexGlobalColor( this );
if( !mTexSkinColor->setInfo( sAvatarXmlInfo->mTexSkinColorInfo ) )
{
- LL_WARNS() << "avatar file: mTexSkinColor->setInfo() failed" << LL_ENDL;
+ LL_ERRS() << "avatar file: mTexSkinColor->setInfo() failed" << LL_ENDL;
return FALSE;
}
}
else
{
- LL_WARNS() << " name=\"skin_color\" not found" << LL_ENDL;
+ LL_ERRS() << " name=\"skin_color\" not found" << LL_ENDL;
return FALSE;
}
if( sAvatarXmlInfo->mTexHairColorInfo )
@@ -872,13 +952,13 @@ BOOL LLAvatarAppearance::loadAvatar()
mTexHairColor = new LLTexGlobalColor( this );
if( !mTexHairColor->setInfo( sAvatarXmlInfo->mTexHairColorInfo ) )
{
- LL_WARNS() << "avatar file: mTexHairColor->setInfo() failed" << LL_ENDL;
+ LL_ERRS() << "avatar file: mTexHairColor->setInfo() failed" << LL_ENDL;
return FALSE;
}
}
else
{
- LL_WARNS() << " name=\"hair_color\" not found" << LL_ENDL;
+ LL_ERRS() << " name=\"hair_color\" not found" << LL_ENDL;
return FALSE;
}
if( sAvatarXmlInfo->mTexEyeColorInfo )
@@ -886,26 +966,26 @@ BOOL LLAvatarAppearance::loadAvatar()
mTexEyeColor = new LLTexGlobalColor( this );
if( !mTexEyeColor->setInfo( sAvatarXmlInfo->mTexEyeColorInfo ) )
{
- LL_WARNS() << "avatar file: mTexEyeColor->setInfo() failed" << LL_ENDL;
+ LL_ERRS() << "avatar file: mTexEyeColor->setInfo() failed" << LL_ENDL;
return FALSE;
}
}
else
{
- LL_WARNS() << " name=\"eye_color\" not found" << LL_ENDL;
+ LL_ERRS() << " name=\"eye_color\" not found" << LL_ENDL;
return FALSE;
}
// avatar_lad.xml :
if (sAvatarXmlInfo->mLayerInfoList.empty())
{
- LL_WARNS() << "avatar file: missing node" << LL_ENDL;
+ LL_ERRS() << "avatar file: missing node" << LL_ENDL;
return FALSE;
}
if (sAvatarXmlInfo->mMorphMaskInfoList.empty())
{
- LL_WARNS() << "avatar file: missing node" << LL_ENDL;
+ LL_ERRS() << "avatar file: missing node" << LL_ENDL;
return FALSE;
}
@@ -1104,6 +1184,7 @@ BOOL LLAvatarAppearance::loadMeshNodes()
{
// This should never happen
LL_WARNS("Avatar") << "Could not find avatar mesh: " << info->mReferenceMeshName << LL_ENDL;
+ return FALSE;
}
}
else
@@ -1240,6 +1321,10 @@ LLJoint *LLAvatarAppearance::getCharacterJoint( U32 num )
{
return NULL;
}
+ if (!mSkeleton[num])
+ {
+ mSkeleton[num] = createAvatarJoint();
+ }
return mSkeleton[num];
}
@@ -1476,16 +1561,19 @@ LLTexLayerSet* LLAvatarAppearance::getAvatarLayerSet(EBakedTextureIndex baked_in
//-----------------------------------------------------------------------------
BOOL LLAvatarAppearance::allocateCollisionVolumes( U32 num )
{
- delete_and_clear_array(mCollisionVolumes);
- mNumCollisionVolumes = 0;
+ if (mNumCollisionVolumes !=num)
+ {
+ delete_and_clear_array(mCollisionVolumes);
+ mNumCollisionVolumes = 0;
- mCollisionVolumes = new LLAvatarJointCollisionVolume[num];
- if (!mCollisionVolumes)
- {
- return FALSE;
- }
-
- mNumCollisionVolumes = num;
+ mCollisionVolumes = new LLAvatarJointCollisionVolume[num];
+ if (!mCollisionVolumes)
+ {
+ return FALSE;
+ }
+
+ mNumCollisionVolumes = num;
+ }
return TRUE;
}
@@ -1503,6 +1591,9 @@ BOOL LLAvatarBoneInfo::parseXml(LLXmlTreeNode* node)
LL_WARNS() << "Bone without name" << LL_ENDL;
return FALSE;
}
+
+ static LLStdStringHandle aliases_string = LLXmlTree::addAttributeString("aliases");
+ node->getFastAttributeString(aliases_string, mAliases ); //Aliases are not required.
}
else if (node->hasName("collision_volume"))
{
@@ -1540,6 +1631,20 @@ BOOL LLAvatarBoneInfo::parseXml(LLXmlTreeNode* node)
return FALSE;
}
+ static LLStdStringHandle end_string = LLXmlTree::addAttributeString("end");
+ if (!node->getFastAttributeVector3(end_string, mEnd))
+ {
+ LL_WARNS() << "Bone without end " << mName << LL_ENDL;
+ mEnd = LLVector3(0.0f, 0.0f, 0.0f);
+ }
+
+ static LLStdStringHandle support_string = LLXmlTree::addAttributeString("support");
+ if (!node->getFastAttributeString(support_string,mSupport))
+ {
+ LL_WARNS() << "Bone without support " << mName << LL_ENDL;
+ mSupport = "base";
+ }
+
if (mIsJoint)
{
static LLStdStringHandle pivot_string = LLXmlTree::addAttributeString("pivot");
@@ -1595,6 +1700,54 @@ BOOL LLAvatarSkeletonInfo::parseXml(LLXmlTreeNode* node)
return TRUE;
}
+//Make aliases for joint and push to map.
+void LLAvatarAppearance::makeJointAliases(LLAvatarBoneInfo *bone_info)
+{
+ if (! bone_info->mIsJoint )
+ {
+ return;
+ }
+
+ std::string bone_name = bone_info->mName;
+ mJointAliasMap[bone_name] = bone_name; //Actual name is a valid alias.
+
+ std::string aliases = bone_info->mAliases;
+
+ boost::char_separator sep(" ");
+ boost::tokenizer > tok(aliases, sep);
+ for(boost::tokenizer >::iterator i = tok.begin(); i != tok.end(); ++i)
+ {
+ if ( mJointAliasMap.find(*i) != mJointAliasMap.end() )
+ {
+ LL_WARNS() << "avatar skeleton: Joint alias \"" << *i << "\" remapped from " << mJointAliasMap[*i] << " to " << bone_name << LL_ENDL;
+ }
+ mJointAliasMap[*i] = bone_name;
+ }
+
+ LLAvatarBoneInfo::child_list_t::const_iterator iter;
+ for (iter = bone_info->mChildList.begin(); iter != bone_info->mChildList.end(); ++iter)
+ {
+ makeJointAliases( *iter );
+ }
+}
+
+const LLAvatarAppearance::joint_alias_map_t& LLAvatarAppearance::getJointAliases ()
+{
+ LLAvatarAppearance::joint_alias_map_t alias_map;
+ if (mJointAliasMap.empty())
+ {
+
+ LLAvatarSkeletonInfo::bone_info_list_t::const_iterator iter;
+ for (iter = sAvatarSkeletonInfo->mBoneInfoList.begin(); iter != sAvatarSkeletonInfo->mBoneInfoList.end(); ++iter)
+ {
+ //LLAvatarBoneInfo *bone_info = *iter;
+ makeJointAliases( *iter );
+ }
+ }
+
+ return mJointAliasMap;
+}
+
//-----------------------------------------------------------------------------
// parseXmlSkeletonNode(): parses nodes from XML tree
@@ -1625,7 +1778,7 @@ BOOL LLAvatarAppearance::LLAvatarXmlInfo::parseXmlSkeletonNode(LLXmlTreeNode* ro
{
LL_WARNS() << "Unknown param type." << LL_ENDL;
}
- continue;
+ return FALSE;
}
LLPolySkeletalDistortionInfo *info = new LLPolySkeletalDistortionInfo;
@@ -1650,7 +1803,7 @@ BOOL LLAvatarAppearance::LLAvatarXmlInfo::parseXmlSkeletonNode(LLXmlTreeNode* ro
{
LL_WARNS() << "No name supplied for attachment point." << LL_ENDL;
delete info;
- continue;
+ return FALSE;
}
static LLStdStringHandle joint_string = LLXmlTree::addAttributeString("joint");
@@ -1658,7 +1811,7 @@ BOOL LLAvatarAppearance::LLAvatarXmlInfo::parseXmlSkeletonNode(LLXmlTreeNode* ro
{
LL_WARNS() << "No bone declared in attachment point " << info->mName << LL_ENDL;
delete info;
- continue;
+ return FALSE;
}
static LLStdStringHandle position_string = LLXmlTree::addAttributeString("position");
@@ -1684,7 +1837,7 @@ BOOL LLAvatarAppearance::LLAvatarXmlInfo::parseXmlSkeletonNode(LLXmlTreeNode* ro
{
LL_WARNS() << "No id supplied for attachment point " << info->mName << LL_ENDL;
delete info;
- continue;
+ return FALSE;
}
static LLStdStringHandle slot_string = LLXmlTree::addAttributeString("pie_slice");
@@ -1770,7 +1923,7 @@ BOOL LLAvatarAppearance::LLAvatarXmlInfo::parseXmlMeshNodes(LLXmlTreeNode* root)
{
LL_WARNS() << "Unknown param type." << LL_ENDL;
}
- continue;
+ return FALSE;
}
LLPolyMorphTargetInfo *morphinfo = new LLPolyMorphTargetInfo();
@@ -1931,7 +2084,7 @@ BOOL LLAvatarAppearance::LLAvatarXmlInfo::parseXmlMorphNodes(LLXmlTreeNode* root
{
LL_WARNS() << "No name supplied for morph mask." << LL_ENDL;
delete info;
- continue;
+ return FALSE;
}
static LLStdStringHandle region_string = LLXmlTree::addAttributeString("body_region");
@@ -1939,7 +2092,7 @@ BOOL LLAvatarAppearance::LLAvatarXmlInfo::parseXmlMorphNodes(LLXmlTreeNode* root
{
LL_WARNS() << "No region supplied for morph mask." << LL_ENDL;
delete info;
- continue;
+ return FALSE;
}
static LLStdStringHandle layer_string = LLXmlTree::addAttributeString("layer");
@@ -1947,7 +2100,7 @@ BOOL LLAvatarAppearance::LLAvatarXmlInfo::parseXmlMorphNodes(LLXmlTreeNode* root
{
LL_WARNS() << "No layer supplied for morph mask." << LL_ENDL;
delete info;
- continue;
+ return FALSE;
}
// optional parameter. don't throw a warning if not present.
diff --git a/indra/llappearance/llavatarappearance.h b/indra/llappearance/llavatarappearance.h
index a0ef49b7cb..ccd6323ed8 100644
--- a/indra/llappearance/llavatarappearance.h
+++ b/indra/llappearance/llavatarappearance.h
@@ -66,7 +66,8 @@ public:
LLAvatarAppearance(LLWearableData* wearable_data);
virtual ~LLAvatarAppearance();
- static void initClass(); // initializes static members
+ static void initClass(const std::string& avatar_file_name, const std::string& skeleton_file_name); // initializes static members
+ static void initClass();
static void cleanupClass(); // Cleanup data that's only init'd once per class.
virtual void initInstance(); // Called after construction to initialize the instance.
virtual BOOL loadSkeletonNode();
@@ -124,8 +125,11 @@ public:
protected:
virtual LLAvatarJoint* createAvatarJoint() = 0;
- virtual LLAvatarJoint* createAvatarJoint(S32 joint_num) = 0;
+ virtual LLAvatarJoint* createAvatarJoint(S32 joint_num) = 0;
virtual LLAvatarJointMesh* createAvatarJointMesh() = 0;
+ void makeJointAliases(LLAvatarBoneInfo *bone_info);
+
+
public:
F32 getPelvisToFoot() const { return mPelvisToFoot; }
/*virtual*/ LLJoint* getRootJoint() { return mRoot; }
@@ -135,9 +139,20 @@ public:
typedef std::map joint_map_t;
joint_map_t mJointMap;
-
+
+ typedef std::map joint_state_map_t;
+ joint_state_map_t mLastBodySizeState;
+ joint_state_map_t mCurrBodySizeState;
+ void compareJointStateMaps(joint_state_map_t& last_state,
+ joint_state_map_t& curr_state);
void computeBodySize();
+public:
+ typedef std::vector avatar_joint_list_t;
+ const avatar_joint_list_t& getSkeleton() { return mSkeleton; }
+ typedef std::map joint_alias_map_t;
+ const joint_alias_map_t& getJointAliases();
+
protected:
static BOOL parseSkeletonFile(const std::string& filename);
@@ -147,12 +162,12 @@ protected:
BOOL setupBone(const LLAvatarBoneInfo* info, LLJoint* parent, S32 ¤t_volume_num, S32 ¤t_joint_num);
BOOL allocateCharacterJoints(U32 num);
BOOL buildSkeleton(const LLAvatarSkeletonInfo *info);
-protected:
+
void clearSkeleton();
BOOL mIsBuilt; // state of deferred character building
- typedef std::vector avatar_joint_list_t;
avatar_joint_list_t mSkeleton;
- LLPosOverrideMap mPelvisFixups;
+ LLVector3OverrideMap mPelvisFixups;
+ joint_alias_map_t mJointAliasMap;
//--------------------------------------------------------------------
// Pelvis height adjustment members.
@@ -335,6 +350,7 @@ protected:
// Collision volumes
//--------------------------------------------------------------------
public:
+ S32 mNumBones;
S32 mNumCollisionVolumes;
LLAvatarJointCollisionVolume* mCollisionVolumes;
protected:
diff --git a/indra/llappearance/llavatarjoint.cpp b/indra/llappearance/llavatarjoint.cpp
index 2ee3c65a01..29642be099 100644
--- a/indra/llappearance/llavatarjoint.cpp
+++ b/indra/llappearance/llavatarjoint.cpp
@@ -52,19 +52,18 @@ LLAvatarJoint::LLAvatarJoint() :
init();
}
+LLAvatarJoint::LLAvatarJoint(S32 joint_num) :
+ LLJoint(joint_num)
+{
+ init();
+}
+
LLAvatarJoint::LLAvatarJoint(const std::string &name, LLJoint *parent) :
LLJoint(name, parent)
{
init();
}
-LLAvatarJoint::LLAvatarJoint(S32 joint_num) :
- LLJoint(joint_num)
-{
- init();
-}
-
-
void LLAvatarJoint::init()
{
mValid = FALSE;
diff --git a/indra/llappearance/llavatarjointmesh.cpp b/indra/llappearance/llavatarjointmesh.cpp
index 520ad775db..7ca0928171 100644
--- a/indra/llappearance/llavatarjointmesh.cpp
+++ b/indra/llappearance/llavatarjointmesh.cpp
@@ -33,22 +33,7 @@
#include "llavatarjointmesh.h"
#include "llavatarappearance.h"
-//#include "llapr.h"
-//#include "llbox.h"
-//#include "lldrawable.h"
-//#include "lldrawpoolavatar.h"
-//#include "lldrawpoolbump.h"
-//#include "lldynamictexture.h"
-//#include "llface.h"
-//#include "llgldbg.h"
-//#include "llglheaders.h"
#include "lltexlayer.h"
-//#include "llviewercamera.h"
-//#include "llviewercontrol.h"
-//#include "llviewertexturelist.h"
-//#include "llsky.h"
-//#include "pipeline.h"
-//#include "llviewershadermgr.h"
#include "llmath.h"
#include "v4math.h"
#include "m3math.h"
@@ -56,6 +41,41 @@
#include "llmatrix4a.h"
+// Utility functions added with Bento to simplify handling of extra
+// spine joints, or other new joints internal to the original
+// skeleton, and unknown to the system avatar.
+
+//-----------------------------------------------------------------------------
+// getBaseSkeletonAncestor()
+//-----------------------------------------------------------------------------
+LLAvatarJoint *getBaseSkeletonAncestor(LLAvatarJoint* joint)
+{
+ LLJoint *ancestor = joint->getParent();
+ while (ancestor->getParent() && (ancestor->getSupport() != LLJoint::SUPPORT_BASE))
+ {
+ LL_DEBUGS("Avatar") << "skipping non-base ancestor " << ancestor->getName() << LL_ENDL;
+ ancestor = ancestor->getParent();
+ }
+ return (LLAvatarJoint*) ancestor;
+}
+
+//-----------------------------------------------------------------------------
+// totalSkinOffset()
+//-----------------------------------------------------------------------------
+LLVector3 totalSkinOffset(LLAvatarJoint *joint)
+{
+ LLVector3 totalOffset;
+ while (joint)
+ {
+ if (joint->getSupport() == LLJoint::SUPPORT_BASE)
+ {
+ totalOffset += joint->getSkinOffset();
+ }
+ joint = (LLAvatarJoint*)joint->getParent();
+ }
+ return totalOffset;
+}
+
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// LLAvatarJointMesh::LLSkinJoint
@@ -92,18 +112,12 @@ BOOL LLSkinJoint::setupSkinJoint( LLAvatarJoint *joint)
}
// compute the inverse root skin matrix
- mRootToJointSkinOffset.clearVec();
+ mRootToJointSkinOffset = totalSkinOffset(joint);
+ mRootToJointSkinOffset = -mRootToJointSkinOffset;
- LLVector3 rootSkinOffset;
- while (joint)
- {
- rootSkinOffset += joint->getSkinOffset();
- joint = (LLAvatarJoint*)joint->getParent();
- }
-
- mRootToJointSkinOffset = -rootSkinOffset;
- mRootToParentJointSkinOffset = mRootToJointSkinOffset;
- mRootToParentJointSkinOffset += mJoint->getSkinOffset();
+ //mRootToParentJointSkinOffset = totalSkinOffset((LLAvatarJoint*)joint->getParent());
+ mRootToParentJointSkinOffset = totalSkinOffset(getBaseSkeletonAncestor(joint));
+ mRootToParentJointSkinOffset = -mRootToParentJointSkinOffset;
return TRUE;
}
@@ -289,6 +303,7 @@ void LLAvatarJointMesh::setMesh( LLPolyMesh *mesh )
}
// acquire the transform from the mesh object
+ // SL-315
setPosition( mMesh->getPosition() );
setRotation( mMesh->getRotation() );
setScale( mMesh->getScale() );
@@ -314,9 +329,9 @@ void LLAvatarJointMesh::setMesh( LLPolyMesh *mesh )
if (!mMesh->isLOD())
{
setupJoint((LLAvatarJoint*)getRoot());
+ LL_DEBUGS("Avatar") << getName() << " joint render entries: " << mMesh->mJointRenderData.size() << LL_ENDL;
}
- LL_DEBUGS() << "joint render entries: " << mMesh->mJointRenderData.size() << LL_ENDL;
}
//-----------------------------------------------------------------------------
@@ -324,9 +339,6 @@ void LLAvatarJointMesh::setMesh( LLPolyMesh *mesh )
//-----------------------------------------------------------------------------
void LLAvatarJointMesh::setupJoint(LLAvatarJoint* current_joint)
{
- LL_DEBUGS() << "Mesh: " << getName() << LL_ENDL;
-
- S32 joint_count = 0;
U32 sj;
for (sj=0; sjgetName() << " matches skinjoint " << sj << LL_ENDL;
// is the last joint in the array our parent?
- if(mMesh->mJointRenderData.size() && mMesh->mJointRenderData[mMesh->mJointRenderData.size() - 1]->mWorldMatrix == ¤t_joint->getParent()->getWorldMatrix())
+
+ std::vector &jrd = mMesh->mJointRenderData;
+
+ // SL-287 - need to update this so the results are the same if
+ // additional extended-skeleton joints lie between this joint
+ // and the original parent.
+ LLJoint *ancestor = getBaseSkeletonAncestor(current_joint);
+ if(jrd.size() && jrd.back()->mWorldMatrix == &ancestor->getWorldMatrix())
{
// ...then just add ourselves
LLAvatarJoint* jointp = js.mJoint;
- mMesh->mJointRenderData.push_back(new LLJointRenderData(&jointp->getWorldMatrix(), &js));
- LL_DEBUGS() << "joint " << joint_count++ << js.mJoint->getName() << LL_ENDL;
+ jrd.push_back(new LLJointRenderData(&jointp->getWorldMatrix(), &js));
+ LL_DEBUGS("Avatar") << "add joint[" << (jrd.size()-1) << "] = " << js.mJoint->getName() << LL_ENDL;
}
- // otherwise add our parent and ourselves
+ // otherwise add our ancestor and ourselves
else
{
- mMesh->mJointRenderData.push_back(new LLJointRenderData(¤t_joint->getParent()->getWorldMatrix(), NULL));
- LL_DEBUGS() << "joint " << joint_count++ << current_joint->getParent()->getName() << LL_ENDL;
- mMesh->mJointRenderData.push_back(new LLJointRenderData(¤t_joint->getWorldMatrix(), &js));
- LL_DEBUGS() << "joint " << joint_count++ << current_joint->getName() << LL_ENDL;
+ jrd.push_back(new LLJointRenderData(&ancestor->getWorldMatrix(), NULL));
+ LL_DEBUGS("Avatar") << "add2 ancestor joint[" << (jrd.size()-1) << "] = " << ancestor->getName() << LL_ENDL;
+ jrd.push_back(new LLJointRenderData(¤t_joint->getWorldMatrix(), &js));
+ LL_DEBUGS("Avatar") << "add2 joint[" << (jrd.size()-1) << "] = " << current_joint->getName() << LL_ENDL;
}
}
diff --git a/indra/llappearance/lldriverparam.cpp b/indra/llappearance/lldriverparam.cpp
index e630c1118b..e5e502b158 100644
--- a/indra/llappearance/lldriverparam.cpp
+++ b/indra/llappearance/lldriverparam.cpp
@@ -110,6 +110,14 @@ void LLDriverParamInfo::toStream(std::ostream &out)
out << std::endl;
+ // FIXME - this mDriverParam backlink makes no sense, because the
+ // LLDriverParamInfos are static objects - there's only one copy
+ // for each param type, so the backlink will just reference the
+ // corresponding param in the most recently created
+ // avatar. Apparently these toStream() methods are not currently
+ // used anywhere, so it's not an urgent problem.
+ LL_WARNS_ONCE() << "Invalid usage of mDriverParam." << LL_ENDL;
+
if(mDriverParam && mDriverParam->getAvatarAppearance()->isSelf() &&
mDriverParam->getAvatarAppearance()->isValid())
{
diff --git a/indra/llappearance/lldriverparam.h b/indra/llappearance/lldriverparam.h
index f71c930e5e..f278dcc2e2 100644
--- a/indra/llappearance/lldriverparam.h
+++ b/indra/llappearance/lldriverparam.h
@@ -128,6 +128,10 @@ public:
S32 getDrivenParamsCount() const;
const LLViewerVisualParam* getDrivenParam(S32 index) const;
+ typedef std::vector entry_list_t;
+ entry_list_t& getDrivenList() { return mDriven; }
+ void setDrivenList(entry_list_t& driven_list) { mDriven = driven_list; }
+
protected:
LLDriverParam(const LLDriverParam& pOther);
F32 getDrivenWeight(const LLDrivenEntry* driven, F32 input_weight);
@@ -135,7 +139,6 @@ protected:
LL_ALIGN_16(LLVector4a mDefaultVec); // temp holder
- typedef std::vector entry_list_t;
entry_list_t mDriven;
LLViewerVisualParam* mCurrentDistortionParam;
// Backlink only; don't make this an LLPointer.
diff --git a/indra/llappearance/llpolymorph.cpp b/indra/llappearance/llpolymorph.cpp
index e3992a080e..2cb4c65d7c 100644
--- a/indra/llappearance/llpolymorph.cpp
+++ b/indra/llappearance/llpolymorph.cpp
@@ -369,7 +369,8 @@ BOOL LLPolyMorphTarget::setInfo(LLPolyMorphTargetInfo* info)
{
if (avatarp->mCollisionVolumes[i].getName() == volume_info->mName)
{
- mVolumeMorphs.push_back(LLPolyVolumeMorph(&avatarp->mCollisionVolumes[i],
+ mVolumeMorphs.push_back(
+ LLPolyVolumeMorph(&avatarp->mCollisionVolumes[i],
volume_info->mScale,
volume_info->mPos));
break;
@@ -647,6 +648,7 @@ void LLPolyMorphTarget::apply( ESex avatar_sex )
LLVector3 pos_delta = volume_morph->mPos * delta_weight;
volume_morph->mVolume->setScale(volume_morph->mVolume->getScale() + scale_delta);
+ // SL-315
volume_morph->mVolume->setPosition(volume_morph->mVolume->getPosition() + pos_delta);
}
}
@@ -730,6 +732,20 @@ void LLPolyMorphTarget::applyMask(U8 *maskTextureData, S32 width, S32 height, S3
apply(mLastSex);
}
+void LLPolyMorphTarget::applyVolumeChanges(F32 delta_weight)
+{
+ // now apply volume changes
+ for( volume_list_t::iterator iter = mVolumeMorphs.begin(); iter != mVolumeMorphs.end(); iter++ )
+ {
+ LLPolyVolumeMorph* volume_morph = &(*iter);
+ LLVector3 scale_delta = volume_morph->mScale * delta_weight;
+ LLVector3 pos_delta = volume_morph->mPos * delta_weight;
+
+ volume_morph->mVolume->setScale(volume_morph->mVolume->getScale() + scale_delta);
+ // SL-315
+ volume_morph->mVolume->setPosition(volume_morph->mVolume->getPosition() + pos_delta);
+ }
+}
//-----------------------------------------------------------------------------
// LLPolyVertexMask()
diff --git a/indra/llappearance/llpolymorph.h b/indra/llappearance/llpolymorph.h
index 3c2c68079c..c6133cd831 100644
--- a/indra/llappearance/llpolymorph.h
+++ b/indra/llappearance/llpolymorph.h
@@ -182,6 +182,8 @@ public:
void applyMask(U8 *maskData, S32 width, S32 height, S32 num_components, BOOL invert);
void addPendingMorphMask() { mNumMorphMasksPending++; }
+ void applyVolumeChanges(F32 delta_weight); // SL-315 - for resetSkeleton()
+
void* operator new(size_t size)
{
return ll_aligned_malloc_16(size);
diff --git a/indra/llappearance/llpolyskeletaldistortion.cpp b/indra/llappearance/llpolyskeletaldistortion.cpp
index fbc312c426..5b77a7433a 100644
--- a/indra/llappearance/llpolyskeletaldistortion.cpp
+++ b/indra/llappearance/llpolyskeletaldistortion.cpp
@@ -34,6 +34,7 @@
#include "llpolymorph.h"
#include "llwearable.h"
#include "llfasttimer.h"
+#include "llcallstack.h"
#include "llpolyskeletaldistortion.h"
@@ -134,55 +135,49 @@ LLPolySkeletalDistortion::~LLPolySkeletalDistortion()
BOOL LLPolySkeletalDistortion::setInfo(LLPolySkeletalDistortionInfo *info)
{
- llassert(mInfo == NULL);
- if (info->mID < 0)
- return FALSE;
- mInfo = info;
- mID = info->mID;
- setWeight(getDefaultWeight());
+ if (info->mID < 0)
+ {
+ return FALSE;
+ }
+ mInfo = info;
+ mID = info->mID;
+ setWeight(getDefaultWeight());
- LLPolySkeletalDistortionInfo::bone_info_list_t::iterator iter;
- for (iter = getInfo()->mBoneInfoList.begin(); iter != getInfo()->mBoneInfoList.end(); iter++)
+ LLPolySkeletalDistortionInfo::bone_info_list_t::iterator iter;
+ for (iter = getInfo()->mBoneInfoList.begin(); iter != getInfo()->mBoneInfoList.end(); iter++)
+ {
+ LLPolySkeletalBoneInfo *bone_info = &(*iter);
+ LLJoint* joint = mAvatar->getJoint(bone_info->mBoneName);
+ if (!joint)
{
- LLPolySkeletalBoneInfo *bone_info = &(*iter);
- LLJoint* joint = mAvatar->getJoint(bone_info->mBoneName);
- if (!joint)
- {
- LL_WARNS() << "Joint " << bone_info->mBoneName << " not found." << LL_ENDL;
- continue;
- }
-
- if (mJointScales.find(joint) != mJointScales.end())
- {
- LL_WARNS() << "Scale deformation already supplied for joint " << joint->getName() << "." << LL_ENDL;
- }
-
- // store it
- mJointScales[joint] = bone_info->mScaleDeformation;
-
- // apply to children that need to inherit it
- for (LLJoint::child_list_t::iterator iter = joint->mChildren.begin();
- iter != joint->mChildren.end(); ++iter)
- {
- LLAvatarJoint* child_joint = (LLAvatarJoint*)(*iter);
- if (child_joint->inheritScale())
- {
- LLVector3 childDeformation = LLVector3(child_joint->getScale());
- childDeformation.scaleVec(bone_info->mScaleDeformation);
- mJointScales[child_joint] = childDeformation;
- }
- }
-
- if (bone_info->mHasPositionDeformation)
- {
- if (mJointOffsets.find(joint) != mJointOffsets.end())
- {
- LL_WARNS() << "Offset deformation already supplied for joint " << joint->getName() << "." << LL_ENDL;
- }
- mJointOffsets[joint] = bone_info->mPositionDeformation;
- }
+ // There's no point continuing after this error - means
+ // that either the skeleton or lad file is broken.
+ LL_WARNS() << "Joint " << bone_info->mBoneName << " not found." << LL_ENDL;
+ return FALSE;
}
- return TRUE;
+
+ // store it
+ mJointScales[joint] = bone_info->mScaleDeformation;
+
+ // apply to children that need to inherit it
+ for (LLJoint::child_list_t::iterator iter = joint->mChildren.begin();
+ iter != joint->mChildren.end(); ++iter)
+ {
+ LLAvatarJoint* child_joint = (LLAvatarJoint*)(*iter);
+ if (child_joint->inheritScale())
+ {
+ LLVector3 childDeformation = LLVector3(child_joint->getScale());
+ childDeformation.scaleVec(bone_info->mScaleDeformation);
+ mJointScales[child_joint] = childDeformation;
+ }
+ }
+
+ if (bone_info->mHasPositionDeformation)
+ {
+ mJointOffsets[joint] = bone_info->mPositionDeformation;
+ }
+ }
+ return TRUE;
}
/*virtual*/ LLViewerVisualParam* LLPolySkeletalDistortion::cloneParam(LLWearable* wearable) const
@@ -197,42 +192,52 @@ static LLTrace::BlockTimerStatHandle FTM_POLYSKELETAL_DISTORTION_APPLY("Skeletal
void LLPolySkeletalDistortion::apply( ESex avatar_sex )
{
- LL_RECORD_BLOCK_TIME(FTM_POLYSKELETAL_DISTORTION_APPLY);
+ LL_RECORD_BLOCK_TIME(FTM_POLYSKELETAL_DISTORTION_APPLY);
- F32 effective_weight = ( getSex() & avatar_sex ) ? mCurWeight : getDefaultWeight();
+ F32 effective_weight = ( getSex() & avatar_sex ) ? mCurWeight : getDefaultWeight();
- LLJoint* joint;
- joint_vec_map_t::iterator iter;
+ LLJoint* joint;
+ joint_vec_map_t::iterator iter;
- for (iter = mJointScales.begin();
- iter != mJointScales.end();
- iter++)
- {
- joint = iter->first;
- LLVector3 newScale = joint->getScale();
- LLVector3 scaleDelta = iter->second;
- newScale = newScale + (effective_weight * scaleDelta) - (mLastWeight * scaleDelta);
- //An aspect of attached mesh objects (which contain joint offsets) that need to be cleaned up when detached
- // needed? // joint->storeScaleForReset( newScale );
- joint->setScale(newScale);
- }
+ for (iter = mJointScales.begin();
+ iter != mJointScales.end();
+ iter++)
+ {
+ joint = iter->first;
+ LLVector3 newScale = joint->getScale();
+ LLVector3 scaleDelta = iter->second;
+ LLVector3 offset = (effective_weight - mLastWeight) * scaleDelta;
+ newScale = newScale + offset;
+ //An aspect of attached mesh objects (which contain joint offsets) that need to be cleaned up when detached
+ // needed?
+ // joint->storeScaleForReset( newScale );
- for (iter = mJointOffsets.begin();
- iter != mJointOffsets.end();
- iter++)
- {
- joint = iter->first;
- LLVector3 newPosition = joint->getPosition();
- LLVector3 positionDelta = iter->second;
- newPosition = newPosition + (effective_weight * positionDelta) - (mLastWeight * positionDelta);
- joint->setPosition(newPosition);
- }
+ // BENTO for detailed stack tracing of params.
+ std::stringstream ostr;
+ ostr << "LLPolySkeletalDistortion::apply, id " << getID() << " " << getName() << " effective wt " << effective_weight << " last wt " << mLastWeight << " scaleDelta " << scaleDelta << " offset " << offset;
+ LLScopedContextString str(ostr.str());
- if (mLastWeight != mCurWeight && !mIsAnimating)
- {
- mAvatar->setSkeletonSerialNum(mAvatar->getSkeletonSerialNum() + 1);
- }
- mLastWeight = mCurWeight;
+ joint->setScale(newScale, true);
+ }
+
+ for (iter = mJointOffsets.begin();
+ iter != mJointOffsets.end();
+ iter++)
+ {
+ joint = iter->first;
+ LLVector3 newPosition = joint->getPosition();
+ LLVector3 positionDelta = iter->second;
+ newPosition = newPosition + (effective_weight * positionDelta) - (mLastWeight * positionDelta);
+ // SL-315
+ bool allow_attachment_pos_overrides = true;
+ joint->setPosition(newPosition, allow_attachment_pos_overrides);
+ }
+
+ if (mLastWeight != effective_weight && !mIsAnimating)
+ {
+ mAvatar->setSkeletonSerialNum(mAvatar->getSkeletonSerialNum() + 1);
+ }
+ mLastWeight = effective_weight;
}
diff --git a/indra/llappearance/llwearable.cpp b/indra/llappearance/llwearable.cpp
index 5ca9f55ac8..63069adcc8 100644
--- a/indra/llappearance/llwearable.cpp
+++ b/indra/llappearance/llwearable.cpp
@@ -173,7 +173,7 @@ void LLWearable::createVisualParams(LLAvatarAppearance *avatarp)
{
if( !param->linkDrivenParams(boost::bind(param_function,avatarp,_1 ), true))
{
- LL_WARNS() << "could not link driven params for wearable " << getName() << " id: " << param->getID() << LL_ENDL;
+ LL_DEBUGS("Avatar") << "could not link driven params for wearable " << getName() << " id: " << param->getID() << LL_ENDL;
continue;
}
}
diff --git a/indra/llcharacter/CMakeLists.txt b/indra/llcharacter/CMakeLists.txt
index 2573417b26..a17a5b0aa6 100644
--- a/indra/llcharacter/CMakeLists.txt
+++ b/indra/llcharacter/CMakeLists.txt
@@ -91,12 +91,12 @@ target_link_libraries(
# Add tests
-if (LL_TESTS)
- include(LLAddBuildTest)
- # UNIT TESTS
- SET(llcharacter_TEST_SOURCE_FILES
- lljoint.cpp
- )
- LL_ADD_PROJECT_UNIT_TESTS(llcharacter "${llcharacter_TEST_SOURCE_FILES}")
-endif (LL_TESTS)
+#if (LL_TESTS)
+# include(LLAddBuildTest)
+# # UNIT TESTS
+# SET(llcharacter_TEST_SOURCE_FILES
+# lljoint.cpp
+# )
+# LL_ADD_PROJECT_UNIT_TESTS(llcharacter "${llcharacter_TEST_SOURCE_FILES}")
+#endif (LL_TESTS)
diff --git a/indra/llcharacter/llbvhloader.cpp b/indra/llcharacter/llbvhloader.cpp
index 0d558aeaa2..e906d81ce1 100644
--- a/indra/llcharacter/llbvhloader.cpp
+++ b/indra/llcharacter/llbvhloader.cpp
@@ -11,7 +11,7 @@
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
- * This library is distributed in the hope that it will be useful,
+ * This library is distributed in the hope that it will be useful,7
* 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.
@@ -29,6 +29,7 @@
#include "llbvhloader.h"
#include
+#include
#include "lldatapacker.h"
#include "lldir.h"
@@ -36,6 +37,7 @@
#include "llquantize.h"
#include "llstl.h"
#include "llapr.h"
+#include "llsdserialize.h"
using namespace std;
@@ -121,52 +123,16 @@ LLQuaternion::Order bvhStringToOrder( char *str )
// LLBVHLoader()
//-----------------------------------------------------------------------------
-/*
- LLBVHLoader::LLBVHLoader(const char* buffer)
-{
- reset();
-
- mStatus = loadTranslationTable("anim.ini");
-
- if (mStatus == LLBVHLoader::ST_NO_XLT_FILE)
- {
- LL_WARNS() << "NOTE: No translation table found." << LL_ENDL;
- return;
- }
- else
- {
- if (mStatus != LLBVHLoader::ST_OK)
- {
- LL_WARNS() << "ERROR: [line: " << getLineNumber() << "] " << mStatus << LL_ENDL;
- return;
- }
- }
-
- char error_text[128]; // Flawfinder: ignore
- S32 error_line;
- mStatus = loadBVHFile(buffer, error_text, error_line);
- if (mStatus != LLBVHLoader::ST_OK)
- {
- LL_WARNS() << "ERROR: [line: " << getLineNumber() << "] " << mStatus << LL_ENDL;
- return;
- }
-
- applyTranslations();
- optimize();
-
- mInitialized = TRUE;
-}
-*/
-LLBVHLoader::LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &errorLine)
+LLBVHLoader::LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &errorLine, std::map& joint_alias_map )
{
reset();
errorLine = 0;
mStatus = loadTranslationTable("anim.ini");
loadStatus = mStatus;
- LL_INFOS()<<"Load Status 00 : "<< loadStatus << LL_ENDL;
+ LL_INFOS("BVH") << "Load Status 00 : " << loadStatus << LL_ENDL;
if (mStatus == E_ST_NO_XLT_FILE)
{
- //LL_WARNS() << "NOTE: No translation table found." << LL_ENDL;
+ LL_WARNS("BVH") << "NOTE: No translation table found." << LL_ENDL;
loadStatus = mStatus;
return;
}
@@ -174,28 +140,43 @@ LLBVHLoader::LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &error
{
if (mStatus != E_ST_OK)
{
- //LL_WARNS() << "ERROR: [line: " << getLineNumber() << "] " << mStatus << LL_ENDL;
+ LL_WARNS("BVH") << "ERROR: [line: " << getLineNumber() << "] " << mStatus << LL_ENDL;
errorLine = getLineNumber();
loadStatus = mStatus;
return;
}
}
+
+ // Recognize all names we've been told are legal.
+ std::map::iterator iter;
+ for (iter = joint_alias_map.begin(); iter != joint_alias_map.end(); iter++)
+ {
+ makeTranslation( iter->first , iter->second );
+ }
char error_text[128]; /* Flawfinder: ignore */
S32 error_line;
- mStatus = loadBVHFile(buffer, error_text, error_line);
+ mStatus = loadBVHFile(buffer, error_text, error_line); //Reads all joints in BVH file.
+
+ LL_DEBUGS("BVH") << "============================================================" << LL_ENDL;
+ LL_DEBUGS("BVH") << "Raw data from file" << LL_ENDL;
+ dumpBVHInfo();
if (mStatus != E_ST_OK)
{
- //LL_WARNS() << "ERROR: [line: " << getLineNumber() << "] " << mStatus << LL_ENDL;
+ LL_WARNS("BVH") << "ERROR: [line: " << getLineNumber() << "] " << mStatus << LL_ENDL;
loadStatus = mStatus;
errorLine = getLineNumber();
return;
}
- applyTranslations();
+ applyTranslations(); //Maps between joints found in file and the aliased names.
optimize();
+ LL_DEBUGS("BVH") << "============================================================" << LL_ENDL;
+ LL_DEBUGS("BVH") << "After translations and optimize" << LL_ENDL;
+ dumpBVHInfo();
+
mInitialized = TRUE;
}
@@ -211,10 +192,6 @@ LLBVHLoader::~LLBVHLoader()
//------------------------------------------------------------------------
ELoadStatus LLBVHLoader::loadTranslationTable(const char *fileName)
{
- mLineNumber = 0;
- mTranslations.clear();
- mConstraints.clear();
-
//--------------------------------------------------------------------
// open file
//--------------------------------------------------------------------
@@ -226,7 +203,7 @@ ELoadStatus LLBVHLoader::loadTranslationTable(const char *fileName)
if (!fp)
return E_ST_NO_XLT_FILE;
- LL_INFOS() << "NOTE: Loading translation table: " << fileName << LL_ENDL;
+ LL_INFOS("BVH") << "NOTE: Loading translation table: " << fileName << LL_ENDL;
//--------------------------------------------------------------------
// register file to be closed on function exit
@@ -244,7 +221,6 @@ ELoadStatus LLBVHLoader::loadTranslationTable(const char *fileName)
// load data one line at a time
//--------------------------------------------------------------------
BOOL loadingGlobals = FALSE;
- Translation *trans = NULL;
while ( getLine(fp) )
{
//----------------------------------------------------------------
@@ -271,13 +247,6 @@ ELoadStatus LLBVHLoader::loadTranslationTable(const char *fileName)
loadingGlobals = TRUE;
continue;
}
- else
- {
- loadingGlobals = FALSE;
- Translation &newTrans = mTranslations[ name ];
- trans = &newTrans;
- continue;
- }
}
//----------------------------------------------------------------
@@ -499,173 +468,101 @@ ELoadStatus LLBVHLoader::loadTranslationTable(const char *fileName)
mConstraints.push_back(constraint);
continue;
}
-
-
- //----------------------------------------------------------------
- // at this point there must be a valid trans pointer
- //----------------------------------------------------------------
- if ( ! trans )
- return E_ST_NO_XLT_NAME;
-
- //----------------------------------------------------------------
- // check for ignore flag
- //----------------------------------------------------------------
- if ( LLStringUtil::compareInsensitive(token, "ignore")==0 )
- {
- char trueFalse[128]; /* Flawfinder: ignore */
- if ( sscanf(mLine, " %*s = %127s", trueFalse) != 1 ) /* Flawfinder: ignore */
- return E_ST_NO_XLT_IGNORE;
-
- trans->mIgnore = (LLStringUtil::compareInsensitive(trueFalse, "true")==0);
- continue;
- }
-
- //----------------------------------------------------------------
- // check for relativepos flag
- //----------------------------------------------------------------
- if ( LLStringUtil::compareInsensitive(token, "relativepos")==0 )
- {
- F32 x, y, z;
- char relpos[128]; /* Flawfinder: ignore */
- if ( sscanf(mLine, " %*s = %f %f %f", &x, &y, &z) == 3 )
- {
- trans->mRelativePosition.setVec( x, y, z );
- }
- else if ( sscanf(mLine, " %*s = %127s", relpos) == 1 ) /* Flawfinder: ignore */
- {
- if ( LLStringUtil::compareInsensitive(relpos, "firstkey")==0 )
- {
- trans->mRelativePositionKey = TRUE;
- }
- else
- {
- return E_ST_NO_XLT_RELATIVE;
- }
- }
- else
- {
- return E_ST_NO_XLT_RELATIVE;
- }
-
- continue;
- }
-
- //----------------------------------------------------------------
- // check for relativerot flag
- //----------------------------------------------------------------
- if ( LLStringUtil::compareInsensitive(token, "relativerot")==0 )
- {
- //F32 x, y, z;
- char relpos[128]; /* Flawfinder: ignore */
- if ( sscanf(mLine, " %*s = %127s", relpos) == 1 ) /* Flawfinder: ignore */
- {
- if ( LLStringUtil::compareInsensitive(relpos, "firstkey")==0 )
- {
- trans->mRelativeRotationKey = TRUE;
- }
- else
- {
- return E_ST_NO_XLT_RELATIVE;
- }
- }
- else
- {
- return E_ST_NO_XLT_RELATIVE;
- }
-
- continue;
- }
-
- //----------------------------------------------------------------
- // check for outname value
- //----------------------------------------------------------------
- if ( LLStringUtil::compareInsensitive(token, "outname")==0 )
- {
- char outName[128]; /* Flawfinder: ignore */
- if ( sscanf(mLine, " %*s = %127s", outName) != 1 ) /* Flawfinder: ignore */
- return E_ST_NO_XLT_OUTNAME;
-
- trans->mOutName = outName;
- continue;
- }
-
- //----------------------------------------------------------------
- // check for frame matrix value
- //----------------------------------------------------------------
- if ( LLStringUtil::compareInsensitive(token, "frame")==0 )
- {
- LLMatrix3 fm;
- if ( sscanf(mLine, " %*s = %f %f %f, %f %f %f, %f %f %f",
- &fm.mMatrix[0][0], &fm.mMatrix[0][1], &fm.mMatrix[0][2],
- &fm.mMatrix[1][0], &fm.mMatrix[1][1], &fm.mMatrix[1][2],
- &fm.mMatrix[2][0], &fm.mMatrix[2][1], &fm.mMatrix[2][2] ) != 9 )
- return E_ST_NO_XLT_MATRIX;
-
- trans->mFrameMatrix = fm;
- continue;
- }
-
- //----------------------------------------------------------------
- // check for offset matrix value
- //----------------------------------------------------------------
- if ( LLStringUtil::compareInsensitive(token, "offset")==0 )
- {
- LLMatrix3 om;
- if ( sscanf(mLine, " %*s = %f %f %f, %f %f %f, %f %f %f",
- &om.mMatrix[0][0], &om.mMatrix[0][1], &om.mMatrix[0][2],
- &om.mMatrix[1][0], &om.mMatrix[1][1], &om.mMatrix[1][2],
- &om.mMatrix[2][0], &om.mMatrix[2][1], &om.mMatrix[2][2] ) != 9 )
- return E_ST_NO_XLT_MATRIX;
-
- trans->mOffsetMatrix = om;
- continue;
- }
-
- //----------------------------------------------------------------
- // check for mergeparent value
- //----------------------------------------------------------------
- if ( LLStringUtil::compareInsensitive(token, "mergeparent")==0 )
- {
- char mergeParentName[128]; /* Flawfinder: ignore */
- if ( sscanf(mLine, " %*s = %127s", mergeParentName) != 1 ) /* Flawfinder: ignore */
- return E_ST_NO_XLT_MERGEPARENT;
-
- trans->mMergeParentName = mergeParentName;
- continue;
- }
-
- //----------------------------------------------------------------
- // check for mergechild value
- //----------------------------------------------------------------
- if ( LLStringUtil::compareInsensitive(token, "mergechild")==0 )
- {
- char mergeChildName[128]; /* Flawfinder: ignore */
- if ( sscanf(mLine, " %*s = %127s", mergeChildName) != 1 ) /* Flawfinder: ignore */
- return E_ST_NO_XLT_MERGECHILD;
-
- trans->mMergeChildName = mergeChildName;
- continue;
- }
-
- //----------------------------------------------------------------
- // check for per-joint priority
- //----------------------------------------------------------------
- if ( LLStringUtil::compareInsensitive(token, "priority")==0 )
- {
- S32 priority;
- if ( sscanf(mLine, " %*s = %d", &priority) != 1 )
- return E_ST_NO_XLT_PRIORITY;
-
- trans->mPriorityModifier = priority;
- continue;
- }
-
}
infile.close() ;
return E_ST_OK;
}
+void LLBVHLoader::makeTranslation(std::string alias_name, std::string joint_name)
+{
+ //Translation &newTrans = (foomap.insert(value_type(alias_name, Translation()))).first();
+ Translation &newTrans = mTranslations[ alias_name ]; //Uses []'s implicit call to ctor.
+
+ newTrans.mOutName = joint_name;
+ LLMatrix3 fm;
+ LLVector3 vect1(0, 1, 0);
+ LLVector3 vect2(0, 0, 1);
+ LLVector3 vect3(1, 0, 0);
+ fm.setRows(vect1, vect2, vect3);
+
+ newTrans.mFrameMatrix = fm;
+
+if (joint_name == "mPelvis")
+ {
+ newTrans.mRelativePositionKey = TRUE;
+ newTrans.mRelativeRotationKey = TRUE;
+ }
+}
+
+ELoadStatus LLBVHLoader::loadAliases(const char * filename)
+{
+ LLSD aliases_sd;
+
+ std::string fullpath = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,filename);
+
+ llifstream input_stream;
+ input_stream.open(fullpath.c_str(), std::ios::in | std::ios::binary);
+
+ if(input_stream.is_open())
+ {
+ if ( LLSDSerialize::fromXML(aliases_sd, input_stream) )
+ {
+ for(LLSD::map_iterator alias_iter = aliases_sd.beginMap();
+ alias_iter != aliases_sd.endMap();
+ ++alias_iter)
+ {
+ LLSD::String alias_name = alias_iter->first;
+ LLSD::String joint_name = alias_iter->second;
+ makeTranslation(alias_name, joint_name);
+
+ }
+ }
+ else
+ {
+ return E_ST_NO_XLT_HEADER;
+ }
+ input_stream.close();
+ }
+ else
+ {
+ LL_WARNS("BVH") << "Can't open joint alias file " << fullpath << LL_ENDL;
+ return E_ST_NO_XLT_FILE;
+ }
+
+ return E_ST_OK;
+}
+
+void LLBVHLoader::dumpBVHInfo()
+{
+ for (U32 j=0; jmName << LL_ENDL;
+ for (S32 i=0; imKeys.size()) // Check this in case file load failed.
+ {
+ Key &prevkey = joint->mKeys[llmax(i-1,0)];
+ Key &key = joint->mKeys[i];
+ if ((i==0) ||
+ (key.mPos[0] != prevkey.mPos[0]) ||
+ (key.mPos[1] != prevkey.mPos[1]) ||
+ (key.mPos[2] != prevkey.mPos[2]) ||
+ (key.mRot[0] != prevkey.mRot[0]) ||
+ (key.mRot[1] != prevkey.mRot[1]) ||
+ (key.mRot[2] != prevkey.mRot[2])
+ )
+ {
+ LL_DEBUGS("BVH") << "FRAME " << i
+ << " POS " << key.mPos[0] << "," << key.mPos[1] << "," << key.mPos[2]
+ << " ROT " << key.mRot[0] << "," << key.mRot[1] << "," << key.mRot[2] << LL_ENDL;
+ }
+ }
+ }
+ }
+
+}
//------------------------------------------------------------------------
// LLBVHLoader::loadBVHFile()
@@ -746,6 +643,7 @@ ELoadStatus LLBVHLoader::loadBVHFile(const char *buffer, char* error_text, S32 &
{
iter++; // {
iter++; // OFFSET
+ iter++; // }
S32 depth = 0;
for (S32 j = (S32)parent_joints.size() - 1; j >= 0; j--)
{
@@ -777,12 +675,19 @@ ELoadStatus LLBVHLoader::loadBVHFile(const char *buffer, char* error_text, S32 &
//---------------------------------------------------------------
// we require the root joint be "hip" - DEV-26188
//---------------------------------------------------------------
- const char* FORCED_ROOT_NAME = "hip";
- if ( (mJoints.size() == 0 ) && ( !strstr(jointName, FORCED_ROOT_NAME) ) )
- {
- strncpy(error_text, line.c_str(), 127); /* Flawfinder: ignore */
- return E_ST_BAD_ROOT;
- }
+ if (mJoints.size() == 0 )
+ {
+ //The root joint of the BVH file must be hip (mPelvis) or an alias of mPelvis.
+ const char* FORCED_ROOT_NAME = "hip";
+
+ TranslationMap::iterator hip_joint = mTranslations.find( FORCED_ROOT_NAME );
+ TranslationMap::iterator root_joint = mTranslations.find( jointName );
+ if ( hip_joint == mTranslations.end() || root_joint == mTranslations.end() || root_joint->second.mOutName != hip_joint->second.mOutName )
+ {
+ strncpy(error_text, line.c_str(), 127); /* Flawfinder: ignore */
+ return E_ST_BAD_ROOT;
+ }
+ }
//----------------------------------------------------------------
@@ -790,11 +695,14 @@ ELoadStatus LLBVHLoader::loadBVHFile(const char *buffer, char* error_text, S32 &
//----------------------------------------------------------------
mJoints.push_back( new Joint( jointName ) );
Joint *joint = mJoints.back();
+ LL_DEBUGS("BVH") << "Created joint " << jointName << LL_ENDL;
+ LL_DEBUGS("BVH") << "- index " << mJoints.size()-1 << LL_ENDL;
S32 depth = 1;
for (S32 j = (S32)parent_joints.size() - 1; j >= 0; j--)
{
Joint *pjoint = mJoints[parent_joints[j]];
+ LL_DEBUGS("BVH") << "- ancestor " << pjoint->mName << LL_ENDL;
if (depth > pjoint->mChildTreeMaxDepth)
{
pjoint->mChildTreeMaxDepth = depth;
@@ -863,6 +771,21 @@ ELoadStatus LLBVHLoader::loadBVHFile(const char *buffer, char* error_text, S32 &
return E_ST_NO_CHANNELS;
}
+ // Animating position (via mNumChannels = 6) is only supported for mPelvis.
+ int res = sscanf(line.c_str(), " CHANNELS %d", &joint->mNumChannels);
+ if ( res != 1 )
+ {
+ // Assume default if not otherwise specified.
+ if (mJoints.size()==1)
+ {
+ joint->mNumChannels = 6;
+ }
+ else
+ {
+ joint->mNumChannels = 3;
+ }
+ }
+
//----------------------------------------------------------------
// get rotation order
//----------------------------------------------------------------
@@ -961,57 +884,49 @@ ELoadStatus LLBVHLoader::loadBVHFile(const char *buffer, char* error_text, S32 &
line = (*(iter++));
err_line++;
- // read and store values
- const char *p = line.c_str();
+ // Split line into a collection of floats.
+ std::deque floats;
+ boost::char_separator whitespace_sep("\t ");
+ tokenizer float_tokens(line, whitespace_sep);
+ tokenizer::iterator float_token_iter = float_tokens.begin();
+ while (float_token_iter != float_tokens.end())
+ {
+ try
+ {
+ F32 val = boost::lexical_cast(*float_token_iter);
+ floats.push_back(val);
+ }
+ catch (const boost::bad_lexical_cast&)
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return E_ST_NO_POS;
+ }
+ float_token_iter++;
+ }
+ LL_DEBUGS("BVH") << "Got " << floats.size() << " floats " << LL_ENDL;
for (U32 j=0; jmKeys.push_back( Key() );
Key &key = joint->mKeys.back();
- // get 3 pos values for root joint only
- if (j==0)
+ if (floats.size() < joint->mNumChannels)
{
- if ( sscanf(p, "%f %f %f", key.mPos, key.mPos+1, key.mPos+2) != 3 )
- {
- strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
- return E_ST_NO_POS;
- }
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return E_ST_NO_POS;
}
- // skip to next 3 values in the line
- p = find_next_whitespace(p);
- if (!p)
+ // assume either numChannels == 6, in which case we have pos + rot,
+ // or numChannels == 3, in which case we have only rot.
+ if (joint->mNumChannels == 6)
{
- strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
- return E_ST_NO_ROT;
+ key.mPos[0] = floats.front(); floats.pop_front();
+ key.mPos[1] = floats.front(); floats.pop_front();
+ key.mPos[2] = floats.front(); floats.pop_front();
}
- p = find_next_whitespace(++p);
- if (!p)
- {
- strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
- return E_ST_NO_ROT;
- }
- p = find_next_whitespace(++p);
- if (!p)
- {
- strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
- return E_ST_NO_ROT;
- }
-
- // get 3 rot values for joint
- F32 rot[3];
- if ( sscanf(p, " %f %f %f", rot, rot+1, rot+2) != 3 )
- {
- strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
- return E_ST_NO_ROT;
- }
-
- p++;
-
- key.mRot[ joint->mOrder[0]-'X' ] = rot[0];
- key.mRot[ joint->mOrder[1]-'X' ] = rot[1];
- key.mRot[ joint->mOrder[2]-'X' ] = rot[2];
+ key.mRot[ joint->mOrder[0]-'X' ] = floats.front(); floats.pop_front();
+ key.mRot[ joint->mOrder[1]-'X' ] = floats.front(); floats.pop_front();
+ key.mRot[ joint->mOrder[2]-'X' ] = floats.front(); floats.pop_front();
}
}
@@ -1045,7 +960,7 @@ void LLBVHLoader::applyTranslations()
//----------------------------------------------------------------
if ( trans.mIgnore )
{
- //LL_INFOS() << "NOTE: Ignoring " << joint->mName.c_str() << LL_ENDL;
+ //LL_INFOS() << "NOTE: Ignoring " << joint->mName.c_str() << LL_ENDL;
joint->mIgnore = TRUE;
continue;
}
@@ -1059,13 +974,12 @@ void LLBVHLoader::applyTranslations()
joint->mOutName = trans.mOutName;
}
- //----------------------------------------------------------------
- // Set the ignorepos flag if necessary
- //----------------------------------------------------------------
- if ( joint->mOutName == std::string("mPelvis") )
- {
- joint->mIgnorePositions = FALSE;
- }
+ //Allow joint position changes as of SL-318
+ joint->mIgnorePositions = FALSE;
+ if (joint->mNumChannels == 3)
+ {
+ joint->mIgnorePositions = TRUE;
+ }
//----------------------------------------------------------------
// Set the relativepos flags if necessary
@@ -1334,6 +1248,9 @@ void LLBVHLoader::reset()
mInitialized = FALSE;
mEmoteName = "";
+ mLineNumber = 0;
+ mTranslations.clear();
+ mConstraints.clear();
}
//------------------------------------------------------------------------
@@ -1508,8 +1425,8 @@ BOOL LLBVHLoader::serialize(LLDataPacker& dp)
frame++;
}
- // output position keys (only for 1st joint)
- if ( ji == mJoints.begin() && !joint->mIgnorePositions )
+ // output position keys if joint has motion.
+ if ( !joint->mIgnorePositions )
{
dp.packS32(joint->mNumPosKeys, "num_pos_keys");
@@ -1539,6 +1456,7 @@ BOOL LLBVHLoader::serialize(LLDataPacker& dp)
outPos *= INCHES_TO_METERS;
+ //SL-318 Pelvis position can only move 5m. Limiting all joint position offsets to this dist.
outPos -= relPos;
outPos.clamp(-LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
@@ -1586,5 +1504,6 @@ BOOL LLBVHLoader::serialize(LLDataPacker& dp)
dp.packF32(constraint_it->mEaseOutStop, "ease_out_stop");
}
+
return TRUE;
}
diff --git a/indra/llcharacter/llbvhloader.h b/indra/llcharacter/llbvhloader.h
index f816b76277..47fe409047 100644
--- a/indra/llcharacter/llbvhloader.h
+++ b/indra/llcharacter/llbvhloader.h
@@ -102,6 +102,7 @@ struct Joint
mNumRotKeys = 0;
mChildTreeMaxDepth = 0;
mPriority = 0;
+ mNumChannels = 3;
}
// Include aligned members first
@@ -123,6 +124,7 @@ struct Joint
S32 mNumRotKeys;
S32 mChildTreeMaxDepth;
S32 mPriority;
+ S32 mNumChannels;
};
@@ -225,8 +227,7 @@ class LLBVHLoader
friend class LLKeyframeMotion;
public:
// Constructor
-// LLBVHLoader(const char* buffer);
- LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &errorLine);
+ LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &errorLine, std::map& joint_alias_map );
~LLBVHLoader();
/*
@@ -265,13 +266,22 @@ public:
static const char *ST_NO_XLT_EMOTE;
static const char *ST_BAD_ROOT;
*/
+
// Loads the specified translation table.
ELoadStatus loadTranslationTable(const char *fileName);
+ //Create a new joint alias
+ void makeTranslation(std::string key, std::string value);
+
+ // Loads joint aliases from XML file.
+ ELoadStatus loadAliases(const char * filename);
+
// Load the specified BVH file.
// Returns status code.
ELoadStatus loadBVHFile(const char *buffer, char *error_text, S32 &error_line);
+ void dumpBVHInfo();
+
// Applies translations to BVH data loaded.
void applyTranslations();
diff --git a/indra/llcharacter/lleditingmotion.cpp b/indra/llcharacter/lleditingmotion.cpp
index f4a37a139a..ddf89f30f2 100644
--- a/indra/llcharacter/lleditingmotion.cpp
+++ b/indra/llcharacter/lleditingmotion.cpp
@@ -117,6 +117,7 @@ LLMotion::LLMotionInitStatus LLEditingMotion::onInitialize(LLCharacter *characte
addJointState( mWristState );
// propagate joint positions to kinematic chain
+ // SL-315
mParentJoint.setPosition( mParentState->getJoint()->getWorldPosition() );
mShoulderJoint.setPosition( mShoulderState->getJoint()->getPosition() );
mElbowJoint.setPosition( mElbowState->getJoint()->getPosition() );
@@ -143,6 +144,7 @@ LLMotion::LLMotionInitStatus LLEditingMotion::onInitialize(LLCharacter *characte
BOOL LLEditingMotion::onActivate()
{
// propagate joint positions to kinematic chain
+ // SL-315
mParentJoint.setPosition( mParentState->getJoint()->getWorldPosition() );
mShoulderJoint.setPosition( mShoulderState->getJoint()->getPosition() );
mElbowJoint.setPosition( mElbowState->getJoint()->getPosition() );
@@ -181,6 +183,7 @@ BOOL LLEditingMotion::onUpdate(F32 time, U8* joint_mask)
focus_pt += mCharacter->getCharacterPosition();
// propagate joint positions to kinematic chain
+ // SL-315
mParentJoint.setPosition( mParentState->getJoint()->getWorldPosition() );
mShoulderJoint.setPosition( mShoulderState->getJoint()->getPosition() );
mElbowJoint.setPosition( mElbowState->getJoint()->getPosition() );
@@ -217,7 +220,8 @@ BOOL LLEditingMotion::onUpdate(F32 time, U8* joint_mask)
" and focus point " << focus_pt << LL_ENDL;
target.setVec(1.f, 1.f, 1.f);
}
-
+
+ // SL-315
mTarget.setPosition( target + mParentJoint.getPosition());
// LL_INFOS() << "Point At: " << mTarget.getPosition() << LL_ENDL;
diff --git a/indra/llcharacter/llheadrotmotion.cpp b/indra/llcharacter/llheadrotmotion.cpp
index 812c4201af..e91de7a11d 100644
--- a/indra/llcharacter/llheadrotmotion.cpp
+++ b/indra/llcharacter/llheadrotmotion.cpp
@@ -285,7 +285,10 @@ LLEyeMotion::LLEyeMotion(const LLUUID &id) : LLMotion(id)
mName = "eye_rot";
mLeftEyeState = new LLJointState;
+ mAltLeftEyeState = new LLJointState;
+
mRightEyeState = new LLJointState;
+ mAltRightEyeState = new LLJointState;
}
@@ -318,18 +321,38 @@ LLMotion::LLMotionInitStatus LLEyeMotion::onInitialize(LLCharacter *character)
return STATUS_FAILURE;
}
+ mAltLeftEyeState->setJoint( character->getJoint("mFaceEyeAltLeft") );
+ if ( ! mAltLeftEyeState->getJoint() )
+ {
+ LL_INFOS() << getName() << ": Can't get alt left eyeball joint." << LL_ENDL;
+ return STATUS_FAILURE;
+ }
+
mRightEyeState->setJoint( character->getJoint("mEyeRight") );
if ( ! mRightEyeState->getJoint() )
{
- LL_INFOS() << getName() << ": Can't get Right eyeball joint." << LL_ENDL;
+ LL_INFOS() << getName() << ": Can't get right eyeball joint." << LL_ENDL;
+ return STATUS_FAILURE;
+ }
+
+ mAltRightEyeState->setJoint( character->getJoint("mFaceEyeAltRight") );
+ if ( ! mAltRightEyeState->getJoint() )
+ {
+ LL_INFOS() << getName() << ": Can't get alt right eyeball joint." << LL_ENDL;
return STATUS_FAILURE;
}
mLeftEyeState->setUsage(LLJointState::ROT);
+ mAltLeftEyeState->setUsage(LLJointState::ROT);
+
mRightEyeState->setUsage(LLJointState::ROT);
+ mAltRightEyeState->setUsage(LLJointState::ROT);
addJointState( mLeftEyeState );
+ addJointState( mAltLeftEyeState );
+
addJointState( mRightEyeState );
+ addJointState( mAltRightEyeState );
return STATUS_SUCCESS;
}
@@ -343,17 +366,98 @@ BOOL LLEyeMotion::onActivate()
return TRUE;
}
+//-----------------------------------------------------------------------------
+// LLEyeMotion::adjustEyeTarget()
+//-----------------------------------------------------------------------------
+void LLEyeMotion::adjustEyeTarget(LLVector3* targetPos, LLJointState& left_eye_state, LLJointState& right_eye_state)
+{
+ // Compute eye rotation.
+ BOOL has_eye_target = FALSE;
+ LLQuaternion target_eye_rot;
+ LLVector3 eye_look_at;
+ F32 vergence;
+
+ if (targetPos)
+ {
+ LLVector3 skyward(0.f, 0.f, 1.f);
+ LLVector3 left;
+ LLVector3 up;
+
+ eye_look_at = *targetPos;
+ has_eye_target = TRUE;
+ F32 lookAtDistance = eye_look_at.normVec();
+
+ left.setVec(skyward % eye_look_at);
+ up.setVec(eye_look_at % left);
+
+ target_eye_rot = LLQuaternion(eye_look_at, left, up);
+ // convert target rotation to head-local coordinates
+ target_eye_rot *= ~mHeadJoint->getWorldRotation();
+ // eliminate any Euler roll - we're lucky that roll is applied last.
+ F32 roll, pitch, yaw;
+ target_eye_rot.getEulerAngles(&roll, &pitch, &yaw);
+ target_eye_rot.setQuat(0.0f, pitch, yaw);
+ // constrain target orientation to be in front of avatar's face
+ target_eye_rot.constrain(EYE_ROT_LIMIT_ANGLE);
+
+ // calculate vergence
+ F32 interocular_dist = (left_eye_state.getJoint()->getWorldPosition() - right_eye_state.getJoint()->getWorldPosition()).magVec();
+ vergence = -atan2((interocular_dist / 2.f), lookAtDistance);
+ llclamp(vergence, -F_PI_BY_TWO, 0.f);
+ }
+ else
+ {
+ target_eye_rot = LLQuaternion::DEFAULT;
+ vergence = 0.f;
+ }
+
+ //RN: subtract 4 degrees to account for foveal angular offset relative to pupil
+ vergence += 4.f * DEG_TO_RAD;
+
+ // calculate eye jitter
+ LLQuaternion eye_jitter_rot;
+
+ // vergence not too high...
+ if (vergence > -0.05f)
+ {
+ //...go ahead and jitter
+ eye_jitter_rot.setQuat(0.f, mEyeJitterPitch + mEyeLookAwayPitch, mEyeJitterYaw + mEyeLookAwayYaw);
+ }
+ else
+ {
+ //...or don't
+ eye_jitter_rot.loadIdentity();
+ }
+
+ // calculate vergence of eyes as an object gets closer to the avatar's head
+ LLQuaternion vergence_quat;
+
+ if (has_eye_target)
+ {
+ vergence_quat.setQuat(vergence, LLVector3(0.f, 0.f, 1.f));
+ }
+ else
+ {
+ vergence_quat.loadIdentity();
+ }
+
+ // calculate eye rotations
+ LLQuaternion left_eye_rot = target_eye_rot;
+ left_eye_rot = vergence_quat * eye_jitter_rot * left_eye_rot;
+
+ LLQuaternion right_eye_rot = target_eye_rot;
+ vergence_quat.transQuat();
+ right_eye_rot = vergence_quat * eye_jitter_rot * right_eye_rot;
+
+ left_eye_state.setRotation( left_eye_rot );
+ right_eye_state.setRotation( right_eye_rot );
+}
//-----------------------------------------------------------------------------
// LLEyeMotion::onUpdate()
//-----------------------------------------------------------------------------
BOOL LLEyeMotion::onUpdate(F32 time, U8* joint_mask)
{
- // Compute eye rotation.
- LLQuaternion target_eye_rot;
- LLVector3 eye_look_at;
- F32 vergence;
-
//calculate jitter
if (mEyeJitterTimer.getElapsedTimeF32() > mEyeJitterTime)
{
@@ -426,83 +530,10 @@ BOOL LLEyeMotion::onUpdate(F32 time, U8* joint_mask)
}
}
- BOOL has_eye_target = FALSE;
LLVector3* targetPos = (LLVector3*)mCharacter->getAnimationData("LookAtPoint");
- if (targetPos)
- {
- LLVector3 skyward(0.f, 0.f, 1.f);
- LLVector3 left;
- LLVector3 up;
-
- eye_look_at = *targetPos;
- has_eye_target = TRUE;
- F32 lookAtDistance = eye_look_at.normVec();
-
- left.setVec(skyward % eye_look_at);
- up.setVec(eye_look_at % left);
-
- target_eye_rot = LLQuaternion(eye_look_at, left, up);
- // convert target rotation to head-local coordinates
- target_eye_rot *= ~mHeadJoint->getWorldRotation();
- // eliminate any Euler roll - we're lucky that roll is applied last.
- F32 roll, pitch, yaw;
- target_eye_rot.getEulerAngles(&roll, &pitch, &yaw);
- target_eye_rot.setQuat(0.0f, pitch, yaw);
- // constrain target orientation to be in front of avatar's face
- target_eye_rot.constrain(EYE_ROT_LIMIT_ANGLE);
-
- // calculate vergence
- F32 interocular_dist = (mLeftEyeState->getJoint()->getWorldPosition() - mRightEyeState->getJoint()->getWorldPosition()).magVec();
- vergence = -atan2((interocular_dist / 2.f), lookAtDistance);
- llclamp(vergence, -F_PI_BY_TWO, 0.f);
- }
- else
- {
- target_eye_rot = LLQuaternion::DEFAULT;
- vergence = 0.f;
- }
-
- //RN: subtract 4 degrees to account for foveal angular offset relative to pupil
- vergence += 4.f * DEG_TO_RAD;
-
- // calculate eye jitter
- LLQuaternion eye_jitter_rot;
-
- // vergence not too high...
- if (vergence > -0.05f)
- {
- //...go ahead and jitter
- eye_jitter_rot.setQuat(0.f, mEyeJitterPitch + mEyeLookAwayPitch, mEyeJitterYaw + mEyeLookAwayYaw);
- }
- else
- {
- //...or don't
- eye_jitter_rot.loadIdentity();
- }
-
- // calculate vergence of eyes as an object gets closer to the avatar's head
- LLQuaternion vergence_quat;
-
- if (has_eye_target)
- {
- vergence_quat.setQuat(vergence, LLVector3(0.f, 0.f, 1.f));
- }
- else
- {
- vergence_quat.loadIdentity();
- }
-
- // calculate eye rotations
- LLQuaternion left_eye_rot = target_eye_rot;
- left_eye_rot = vergence_quat * eye_jitter_rot * left_eye_rot;
-
- LLQuaternion right_eye_rot = target_eye_rot;
- vergence_quat.transQuat();
- right_eye_rot = vergence_quat * eye_jitter_rot * right_eye_rot;
-
- mLeftEyeState->setRotation( left_eye_rot );
- mRightEyeState->setRotation( right_eye_rot );
+ adjustEyeTarget(targetPos, *mLeftEyeState, *mRightEyeState);
+ adjustEyeTarget(targetPos, *mAltLeftEyeState, *mAltRightEyeState);
return TRUE;
}
@@ -519,11 +550,23 @@ void LLEyeMotion::onDeactivate()
joint->setRotation(LLQuaternion::DEFAULT);
}
+ joint = mAltLeftEyeState->getJoint();
+ if (joint)
+ {
+ joint->setRotation(LLQuaternion::DEFAULT);
+ }
+
joint = mRightEyeState->getJoint();
if (joint)
{
joint->setRotation(LLQuaternion::DEFAULT);
}
+
+ joint = mAltRightEyeState->getJoint();
+ if (joint)
+ {
+ joint->setRotation(LLQuaternion::DEFAULT);
+ }
}
// End
diff --git a/indra/llcharacter/llheadrotmotion.h b/indra/llcharacter/llheadrotmotion.h
index 569dbef2dd..53ae1813bc 100644
--- a/indra/llcharacter/llheadrotmotion.h
+++ b/indra/llcharacter/llheadrotmotion.h
@@ -176,6 +176,8 @@ public:
// it will be deactivated
virtual BOOL onActivate();
+ void adjustEyeTarget(LLVector3* targetPos, LLJointState& left_eye_state, LLJointState& right_eye_state);
+
// called per time step
// must return TRUE while it is active, and
// must return FALSE when the motion is completed.
@@ -193,6 +195,8 @@ public:
LLJoint *mHeadJoint;
LLPointer mLeftEyeState;
LLPointer mRightEyeState;
+ LLPointer mAltLeftEyeState;
+ LLPointer mAltRightEyeState;
LLFrameTimer mEyeJitterTimer;
F32 mEyeJitterTime;
diff --git a/indra/llcharacter/lljoint.cpp b/indra/llcharacter/lljoint.cpp
index 8fa08a2a6c..a3d5679f65 100644
--- a/indra/llcharacter/lljoint.cpp
+++ b/indra/llcharacter/lljoint.cpp
@@ -32,6 +32,8 @@
#include "lljoint.h"
#include "llmath.h"
+#include "llcallstack.h"
+#include
S32 LLJoint::sNumUpdates = 0;
S32 LLJoint::sNumTouches = 0;
@@ -42,7 +44,7 @@ bool attachment_map_iter_compare_key(const T& a, const T& b)
return a.first < b.first;
}
-bool LLPosOverrideMap::findActiveOverride(LLUUID& mesh_id, LLVector3& pos) const
+bool LLVector3OverrideMap::findActiveOverride(LLUUID& mesh_id, LLVector3& pos) const
{
pos = LLVector3(0,0,0);
mesh_id = LLUUID();
@@ -60,7 +62,7 @@ bool LLPosOverrideMap::findActiveOverride(LLUUID& mesh_id, LLVector3& pos) const
return found;
}
-void LLPosOverrideMap::showJointPosOverrides( std::ostringstream& os ) const
+void LLVector3OverrideMap::showJointVector3Overrides( std::ostringstream& os ) const
{
map_type::const_iterator max_it = std::max_element(m_map.begin(),
m_map.end(),
@@ -73,23 +75,23 @@ void LLPosOverrideMap::showJointPosOverrides( std::ostringstream& os ) const
}
}
-U32 LLPosOverrideMap::count() const
+U32 LLVector3OverrideMap::count() const
{
return m_map.size();
}
-void LLPosOverrideMap::add(const LLUUID& mesh_id, const LLVector3& pos)
+void LLVector3OverrideMap::add(const LLUUID& mesh_id, const LLVector3& pos)
{
m_map[mesh_id] = pos;
}
-bool LLPosOverrideMap::remove(const LLUUID& mesh_id)
+bool LLVector3OverrideMap::remove(const LLUUID& mesh_id)
{
U32 remove_count = m_map.erase(mesh_id);
return (remove_count > 0);
}
-void LLPosOverrideMap::clear()
+void LLVector3OverrideMap::clear()
{
m_map.clear();
}
@@ -108,6 +110,8 @@ void LLJoint::init()
mXform.setScale(LLVector3(1.0f, 1.0f, 1.0f));
mDirtyFlags = MATRIX_DIRTY | ROTATION_DIRTY | POSITION_DIRTY;
mUpdateXform = TRUE;
+ mSupport = SUPPORT_BASE;
+ mEnd = LLVector3(0.0f, 0.0f, 0.0f);
}
LLJoint::LLJoint() :
@@ -124,13 +128,12 @@ LLJoint::LLJoint(S32 joint_num) :
touch();
}
-
//-----------------------------------------------------------------------------
// LLJoint()
// Class Constructor
//-----------------------------------------------------------------------------
LLJoint::LLJoint(const std::string &name, LLJoint *parent) :
- mJointNum(0)
+ mJointNum(-2)
{
init();
mUpdateXform = FALSE;
@@ -169,6 +172,27 @@ void LLJoint::setup(const std::string &name, LLJoint *parent)
}
}
+//-----------------------------------------------------------------------------
+// setSupport()
+//-----------------------------------------------------------------------------
+void LLJoint::setSupport(const std::string& support_name)
+{
+ if (support_name == "extended")
+ {
+ setSupport(SUPPORT_EXTENDED);
+ }
+ else if (support_name == "base")
+ {
+ setSupport(SUPPORT_BASE);
+ }
+ else
+ {
+ LL_WARNS() << "unknown support string " << support_name << LL_ENDL;
+ setSupport(SUPPORT_BASE);
+ }
+}
+
+
//-----------------------------------------------------------------------------
// touch()
// Sets all dirty flags for all children, recursively.
@@ -194,6 +218,18 @@ void LLJoint::touch(U32 flags)
}
}
+//-----------------------------------------------------------------------------
+// setJointNum()
+//-----------------------------------------------------------------------------
+void LLJoint::setJointNum(S32 joint_num)
+{
+ mJointNum = joint_num;
+ if (mJointNum + 2 >= LL_CHARACTER_MAX_ANIMATED_JOINTS)
+ {
+ LL_INFOS() << "LL_CHARACTER_MAX_ANIMATED_JOINTS needs to be increased" << LL_ENDL;
+ LL_ERRS() << "joint_num " << joint_num << " + 2 is too large for " << LL_CHARACTER_MAX_ANIMATED_JOINTS << LL_ENDL;
+ }
+}
//-----------------------------------------------------------------------------
// getRoot()
//-----------------------------------------------------------------------------
@@ -290,43 +326,119 @@ const LLVector3& LLJoint::getPosition()
bool do_debug_joint(const std::string& name)
{
- return false;
+ if (std::find(LLJoint::s_debugJointNames.begin(), LLJoint::s_debugJointNames.end(),name) != LLJoint::s_debugJointNames.end())
+ {
+ return true;
+ }
+ return false;
}
//--------------------------------------------------------------------
// setPosition()
//--------------------------------------------------------------------
-void LLJoint::setPosition( const LLVector3& pos )
+void LLJoint::setPosition( const LLVector3& requested_pos, bool apply_attachment_overrides )
{
- if (pos != getPosition())
+ LLVector3 pos(requested_pos);
+
+ LLVector3 active_override;
+ LLUUID mesh_id;
+ if (apply_attachment_overrides && m_attachmentPosOverrides.findActiveOverride(mesh_id,active_override))
+ {
+ if (pos != active_override && do_debug_joint(getName()))
+ {
+ LLScopedContextString str("setPosition");
+ LL_DEBUGS("Avatar") << " joint " << getName() << " requested_pos " << requested_pos
+ << " overriden by attachment " << active_override << LL_ENDL;
+ }
+ pos = active_override;
+ }
+ if ((pos != getPosition()) && do_debug_joint(getName()))
{
- if (do_debug_joint(getName()))
- {
- LL_DEBUGS("Avatar") << " joint " << getName() << " set pos " << pos << LL_ENDL;
- }
+ LLScopedContextString str("setPosition");
+ LLCallStack cs;
+ LLContextStatus con_status;
+ LL_DEBUGS("Avatar") << " joint " << getName() << " set pos " << pos << LL_ENDL;
+ LL_DEBUGS("Avatar") << "CONTEXT:\n" << "====================\n" << con_status << "====================" << LL_ENDL;
+ LL_DEBUGS("Avatar") << "STACK:\n" << "====================\n" << cs << "====================" << LL_ENDL;
}
- mXform.setPosition(pos);
- touch(MATRIX_DIRTY | POSITION_DIRTY);
+ if (pos != getPosition())
+ {
+ mXform.setPosition(pos);
+ touch(MATRIX_DIRTY | POSITION_DIRTY);
+ }
+}
+
+void LLJoint::setDefaultPosition( const LLVector3& pos )
+{
+ mDefaultPosition = pos;
+}
+
+const LLVector3& LLJoint::getDefaultPosition() const
+{
+ return mDefaultPosition;
+}
+
+void LLJoint::setDefaultScale( const LLVector3& scale )
+{
+ mDefaultScale = scale;
+}
+
+const LLVector3& LLJoint::getDefaultScale() const
+{
+ return mDefaultScale;
}
void showJointPosOverrides( const LLJoint& joint, const std::string& note, const std::string& av_info )
{
std::ostringstream os;
os << joint.m_posBeforeOverrides;
- joint.m_attachmentOverrides.showJointPosOverrides(os);
+ joint.m_attachmentPosOverrides.showJointVector3Overrides(os);
LL_DEBUGS("Avatar") << av_info << " joint " << joint.getName() << " " << note << " " << os.str() << LL_ENDL;
}
+void showJointScaleOverrides( const LLJoint& joint, const std::string& note, const std::string& av_info )
+{
+ std::ostringstream os;
+ os << joint.m_scaleBeforeOverrides;
+ joint.m_attachmentScaleOverrides.showJointVector3Overrides(os);
+ LL_DEBUGS("Avatar") << av_info << " joint " << joint.getName() << " " << note << " " << os.str() << LL_ENDL;
+}
+
+bool LLJoint::aboveJointPosThreshold(const LLVector3& pos) const
+{
+ LLVector3 diff = pos - getDefaultPosition();
+ const F32 max_joint_pos_offset = 0.0001f; // 0.1 mm
+ return diff.lengthSquared() > max_joint_pos_offset * max_joint_pos_offset;
+}
+
+bool LLJoint::aboveJointScaleThreshold(const LLVector3& scale) const
+{
+ LLVector3 diff = scale - getDefaultScale();
+ const F32 max_joint_scale_offset = 0.0001f; // 0.1 mm
+ return diff.lengthSquared() > max_joint_scale_offset * max_joint_scale_offset;
+}
+
//--------------------------------------------------------------------
// addAttachmentPosOverride()
//--------------------------------------------------------------------
-void LLJoint::addAttachmentPosOverride( const LLVector3& pos, const LLUUID& mesh_id, const std::string& av_info )
+void LLJoint::addAttachmentPosOverride( const LLVector3& pos, const LLUUID& mesh_id, const std::string& av_info, bool& active_override_changed )
{
+ active_override_changed = false;
if (mesh_id.isNull())
{
return;
}
- if (!m_attachmentOverrides.count())
+ // BENTO
+ // Not clear pelvis overrides are meaningful/useful.
+ //if (mName == "mPelvis")
+ //{
+ // return;
+ //}
+
+ LLVector3 before_pos;
+ LLUUID before_mesh_id;
+ bool has_active_override_before = hasAttachmentPosOverride( before_pos, before_mesh_id );
+ if (!m_attachmentPosOverrides.count())
{
if (do_debug_joint(getName()))
{
@@ -334,32 +446,50 @@ void LLJoint::addAttachmentPosOverride( const LLVector3& pos, const LLUUID& mesh
}
m_posBeforeOverrides = getPosition();
}
- m_attachmentOverrides.add(mesh_id,pos);
- if (do_debug_joint(getName()))
- {
- LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " addAttachmentPosOverride for mesh " << mesh_id << " pos " << pos << LL_ENDL;
- }
- updatePos(av_info);
+ m_attachmentPosOverrides.add(mesh_id,pos);
+ LLVector3 after_pos;
+ LLUUID after_mesh_id;
+ hasAttachmentPosOverride(after_pos, after_mesh_id);
+ if (!has_active_override_before || (after_pos != before_pos))
+ {
+ active_override_changed = true;
+ if (do_debug_joint(getName()))
+ {
+ LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " addAttachmentPosOverride for mesh " << mesh_id << " pos " << pos << LL_ENDL;
+ }
+ updatePos(av_info);
+ }
}
//--------------------------------------------------------------------
// removeAttachmentPosOverride()
//--------------------------------------------------------------------
-void LLJoint::removeAttachmentPosOverride( const LLUUID& mesh_id, const std::string& av_info )
+void LLJoint::removeAttachmentPosOverride( const LLUUID& mesh_id, const std::string& av_info, bool& active_override_changed )
{
+ active_override_changed = false;
if (mesh_id.isNull())
{
return;
}
- if (m_attachmentOverrides.remove(mesh_id))
+ LLVector3 before_pos;
+ LLUUID before_mesh_id;
+ hasAttachmentPosOverride( before_pos, before_mesh_id );
+ if (m_attachmentPosOverrides.remove(mesh_id))
{
- if (do_debug_joint(getName()))
- {
- LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName()
- << " removeAttachmentPosOverride for " << mesh_id << LL_ENDL;
- showJointPosOverrides(*this, "remove", av_info);
- }
- updatePos(av_info);
+ LLVector3 after_pos;
+ LLUUID after_mesh_id;
+ bool has_active_override_after = hasAttachmentPosOverride(after_pos, after_mesh_id);
+ if (!has_active_override_after || (after_pos != before_pos))
+ {
+ active_override_changed = true;
+ if (do_debug_joint(getName()))
+ {
+ LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName()
+ << " removeAttachmentPosOverride for " << mesh_id << LL_ENDL;
+ showJointPosOverrides(*this, "remove", av_info);
+ }
+ updatePos(av_info);
+ }
}
}
@@ -368,7 +498,7 @@ void LLJoint::removeAttachmentPosOverride( const LLUUID& mesh_id, const std::str
//--------------------------------------------------------------------
bool LLJoint::hasAttachmentPosOverride( LLVector3& pos, LLUUID& mesh_id ) const
{
- return m_attachmentOverrides.findActiveOverride(mesh_id,pos);
+ return m_attachmentPosOverrides.findActiveOverride(mesh_id,pos);
}
//--------------------------------------------------------------------
@@ -376,11 +506,81 @@ bool LLJoint::hasAttachmentPosOverride( LLVector3& pos, LLUUID& mesh_id ) const
//--------------------------------------------------------------------
void LLJoint::clearAttachmentPosOverrides()
{
- if (m_attachmentOverrides.count())
+ if (m_attachmentPosOverrides.count())
{
- m_attachmentOverrides.clear();
+ m_attachmentPosOverrides.clear();
setPosition(m_posBeforeOverrides);
- setId( LLUUID::null );
+ }
+}
+
+//--------------------------------------------------------------------
+// getAllAttachmentPosOverrides()
+//--------------------------------------------------------------------
+void LLJoint::getAllAttachmentPosOverrides(S32& num_pos_overrides,
+ std::set& distinct_pos_overrides)
+{
+ num_pos_overrides = m_attachmentPosOverrides.count();
+ LLVector3OverrideMap::map_type::const_iterator it = m_attachmentPosOverrides.getMap().begin();
+ for (; it != m_attachmentPosOverrides.getMap().end(); ++it)
+ {
+ distinct_pos_overrides.insert(it->second);
+ }
+}
+
+//--------------------------------------------------------------------
+// getAllAttachmentScaleOverrides()
+//--------------------------------------------------------------------
+void LLJoint::getAllAttachmentScaleOverrides(S32& num_scale_overrides,
+ std::set& distinct_scale_overrides)
+{
+ num_scale_overrides = m_attachmentScaleOverrides.count();
+ LLVector3OverrideMap::map_type::const_iterator it = m_attachmentScaleOverrides.getMap().begin();
+ for (; it != m_attachmentScaleOverrides.getMap().end(); ++it)
+ {
+ distinct_scale_overrides.insert(it->second);
+ }
+}
+
+//--------------------------------------------------------------------
+// showAttachmentPosOverrides()
+//--------------------------------------------------------------------
+void LLJoint::showAttachmentPosOverrides(const std::string& av_info) const
+{
+ LLVector3 active_override;
+ bool has_active_override;
+ LLUUID mesh_id;
+ has_active_override = m_attachmentPosOverrides.findActiveOverride(mesh_id,active_override);
+ U32 count = m_attachmentPosOverrides.count();
+ if (count==1)
+ {
+ LLVector3OverrideMap::map_type::const_iterator it = m_attachmentPosOverrides.getMap().begin();
+ std::string highlight = (has_active_override && (it->second == active_override)) ? "*" : "";
+ LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName()
+ << " has single attachment pos override " << highlight << "" << it->second << " default " << mDefaultPosition << LL_ENDL;
+ }
+ else if (count>1)
+ {
+ LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " has " << count << " attachment pos overrides" << LL_ENDL;
+ std::set distinct_offsets;
+ LLVector3OverrideMap::map_type::const_iterator it = m_attachmentPosOverrides.getMap().begin();
+ for (; it != m_attachmentPosOverrides.getMap().end(); ++it)
+ {
+ distinct_offsets.insert(it->second);
+ }
+ if (distinct_offsets.size()>1)
+ {
+ LL_DEBUGS("Avatar") << "CONFLICTS, " << distinct_offsets.size() << " different values" << LL_ENDL;
+ }
+ else
+ {
+ LL_DEBUGS("Avatar") << "no conflicts" << LL_ENDL;
+ }
+ std::set::iterator dit = distinct_offsets.begin();
+ for ( ; dit != distinct_offsets.end(); ++dit)
+ {
+ std::string highlight = (has_active_override && *dit == active_override) ? "*" : "";
+ LL_DEBUGS("Avatar") << " POS " << highlight << "" << (*dit) << " default " << mDefaultPosition << LL_ENDL;
+ }
}
}
@@ -391,19 +591,177 @@ void LLJoint::updatePos(const std::string& av_info)
{
LLVector3 pos, found_pos;
LLUUID mesh_id;
- if (m_attachmentOverrides.findActiveOverride(mesh_id,found_pos))
+ if (m_attachmentPosOverrides.findActiveOverride(mesh_id,found_pos))
{
- LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " updatePos, winner of " << m_attachmentOverrides.count() << " is mesh " << mesh_id << " pos " << found_pos << LL_ENDL;
+ if (do_debug_joint(getName()))
+ {
+ LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " updatePos, winner of " << m_attachmentPosOverrides.count() << " is mesh " << mesh_id << " pos " << found_pos << LL_ENDL;
+ }
pos = found_pos;
}
else
{
- LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " updatePos, winner is posBeforeOverrides " << m_posBeforeOverrides << LL_ENDL;
+ if (do_debug_joint(getName()))
+ {
+ LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " updatePos, winner is posBeforeOverrides " << m_posBeforeOverrides << LL_ENDL;
+ }
pos = m_posBeforeOverrides;
}
setPosition(pos);
}
+//--------------------------------------------------------------------
+// updateScale()
+//--------------------------------------------------------------------
+void LLJoint::updateScale(const std::string& av_info)
+{
+ LLVector3 scale, found_scale;
+ LLUUID mesh_id;
+ if (m_attachmentScaleOverrides.findActiveOverride(mesh_id,found_scale))
+ {
+ if (do_debug_joint(getName()))
+ {
+ LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " updateScale, winner of " << m_attachmentScaleOverrides.count() << " is mesh " << mesh_id << " scale " << found_scale << LL_ENDL;
+ }
+ scale = found_scale;
+ }
+ else
+ {
+ if (do_debug_joint(getName()))
+ {
+ LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " updateScale, winner is scaleBeforeOverrides " << m_scaleBeforeOverrides << LL_ENDL;
+ }
+ scale = m_scaleBeforeOverrides;
+ }
+ setScale(scale);
+}
+
+//--------------------------------------------------------------------
+// addAttachmentScaleOverride()
+//--------------------------------------------------------------------
+void LLJoint::addAttachmentScaleOverride( const LLVector3& scale, const LLUUID& mesh_id, const std::string& av_info )
+{
+ if (mesh_id.isNull())
+ {
+ return;
+ }
+ if (!m_attachmentScaleOverrides.count())
+ {
+ if (do_debug_joint(getName()))
+ {
+ LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " saving m_scaleBeforeOverrides " << getScale() << LL_ENDL;
+ }
+ m_scaleBeforeOverrides = getScale();
+ }
+ m_attachmentScaleOverrides.add(mesh_id,scale);
+ if (do_debug_joint(getName()))
+ {
+ LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " addAttachmentScaleOverride for mesh " << mesh_id << " scale " << scale << LL_ENDL;
+ }
+ updateScale(av_info);
+}
+
+//--------------------------------------------------------------------
+// removeAttachmentScaleOverride()
+//--------------------------------------------------------------------
+void LLJoint::removeAttachmentScaleOverride( const LLUUID& mesh_id, const std::string& av_info )
+{
+ if (mesh_id.isNull())
+ {
+ return;
+ }
+ if (m_attachmentScaleOverrides.remove(mesh_id))
+ {
+ if (do_debug_joint(getName()))
+ {
+ LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName()
+ << " removeAttachmentScaleOverride for " << mesh_id << LL_ENDL;
+ showJointScaleOverrides(*this, "remove", av_info);
+ }
+ updateScale(av_info);
+ }
+}
+
+//--------------------------------------------------------------------
+ // hasAttachmentScaleOverride()
+ //--------------------------------------------------------------------
+bool LLJoint::hasAttachmentScaleOverride( LLVector3& scale, LLUUID& mesh_id ) const
+{
+ return m_attachmentScaleOverrides.findActiveOverride(mesh_id,scale);
+}
+
+//--------------------------------------------------------------------
+// clearAttachmentScaleOverrides()
+//--------------------------------------------------------------------
+void LLJoint::clearAttachmentScaleOverrides()
+{
+ if (m_attachmentScaleOverrides.count())
+ {
+ m_attachmentScaleOverrides.clear();
+ setScale(m_scaleBeforeOverrides);
+ }
+}
+
+//--------------------------------------------------------------------
+// showAttachmentScaleOverrides()
+//--------------------------------------------------------------------
+void LLJoint::showAttachmentScaleOverrides(const std::string& av_info) const
+{
+ LLVector3 active_override;
+ bool has_active_override;
+ LLUUID mesh_id;
+ has_active_override = m_attachmentScaleOverrides.findActiveOverride(mesh_id,active_override);
+ U32 count = m_attachmentScaleOverrides.count();
+ if (count==1)
+ {
+ LLVector3OverrideMap::map_type::const_iterator it = m_attachmentScaleOverrides.getMap().begin();
+ std::string highlight = (has_active_override && (it->second == active_override)) ? "*" : "";
+ LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName()
+ << " has single attachment scale override " << highlight << "" << it->second << " default " << mDefaultScale << LL_ENDL;
+ }
+ else if (count>1)
+ {
+ LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " has " << count << " attachment scale overrides" << LL_ENDL;
+ std::set distinct_offsets;
+ LLVector3OverrideMap::map_type::const_iterator it = m_attachmentScaleOverrides.getMap().begin();
+ for (; it != m_attachmentScaleOverrides.getMap().end(); ++it)
+ {
+ distinct_offsets.insert(it->second);
+ }
+ if (distinct_offsets.size()>1)
+ {
+ LL_DEBUGS("Avatar") << "CONFLICTS, " << distinct_offsets.size() << " different values" << LL_ENDL;
+ }
+ else
+ {
+ LL_DEBUGS("Avatar") << "no conflicts" << LL_ENDL;
+ }
+ std::set::iterator dit = distinct_offsets.begin();
+ for ( ; dit != distinct_offsets.end(); ++dit)
+ {
+ std::string highlight = (has_active_override && *dit == active_override) ? "*" : "";
+ LL_DEBUGS("Avatar") << " POS " << highlight << "" << (*dit) << " default " << mDefaultScale << LL_ENDL;
+ }
+ }
+}
+
+// init static
+LLJoint::debug_joint_name_t LLJoint::s_debugJointNames = debug_joint_name_t();
+
+//--------------------------------------------------------------------
+// setDebugJointNames
+//--------------------------------------------------------------------
+void LLJoint::setDebugJointNames(const debug_joint_name_t& names)
+{
+ s_debugJointNames = names;
+}
+void LLJoint::setDebugJointNames(const std::string& names_string)
+{
+ debug_joint_name_t names;
+ boost::split(names, names_string, boost::is_any_of(" :,"));
+ setDebugJointNames(names);
+}
+
//--------------------------------------------------------------------
// getWorldPosition()
//--------------------------------------------------------------------
@@ -529,13 +887,32 @@ const LLVector3& LLJoint::getScale()
//--------------------------------------------------------------------
// setScale()
//--------------------------------------------------------------------
-void LLJoint::setScale( const LLVector3& scale )
+void LLJoint::setScale( const LLVector3& requested_scale, bool apply_attachment_overrides )
{
-// if (mXform.getScale() != scale)
+ LLVector3 scale(requested_scale);
+ LLUUID mesh_id;
+ LLVector3 active_override;
+ if (apply_attachment_overrides && m_attachmentScaleOverrides.findActiveOverride(mesh_id,active_override))
+ {
+ if (scale != active_override && do_debug_joint(getName()))
+ {
+ LLScopedContextString str("setScale");
+ LL_DEBUGS("Avatar") << " joint " << getName() << " requested_scale " << requested_scale
+ << " overriden by attachment " << active_override << LL_ENDL;
+ }
+ scale = active_override;
+ }
+ if ((mXform.getScale() != scale) && do_debug_joint(getName()))
{
- mXform.setScale(scale);
- touch();
+ LLScopedContextString str("setScale");
+ LLCallStack cs;
+ LLContextStatus con_status;
+ LL_DEBUGS("Avatar") << " joint " << getName() << " set scale " << scale << LL_ENDL;
+ LL_DEBUGS("Avatar") << "CONTEXT:\n" << "====================\n" << con_status << LL_ENDL;
+ LL_DEBUGS("Avatar") << "STACK:\n" << "====================\n" << cs << "====================" << LL_ENDL;
}
+ mXform.setScale(scale);
+ touch();
}
diff --git a/indra/llcharacter/lljoint.h b/indra/llcharacter/lljoint.h
index 2abe1d6db1..0c8fbfebb0 100644
--- a/indra/llcharacter/lljoint.h
+++ b/indra/llcharacter/lljoint.h
@@ -40,24 +40,33 @@
#include "xform.h"
const S32 LL_CHARACTER_MAX_JOINTS_PER_MESH = 15;
-const U32 LL_CHARACTER_MAX_JOINTS = 32; // must be divisible by 4!
-const U32 LL_HAND_JOINT_NUM = 31;
-const U32 LL_FACE_JOINT_NUM = 30;
+// Need to set this to count of animate-able joints,
+// currently = #bones + #collision_volumes + #attachments + 2,
+// rounded to next multiple of 4.
+const U32 LL_CHARACTER_MAX_ANIMATED_JOINTS = 216; // must be divisible by 4!
+const U32 LL_MAX_JOINTS_PER_MESH_OBJECT = 110;
+
+// These should be higher than the joint_num of any
+// other joint, to avoid conflicts in updateMotionsByType()
+const U32 LL_HAND_JOINT_NUM = (LL_CHARACTER_MAX_ANIMATED_JOINTS-1);
+const U32 LL_FACE_JOINT_NUM = (LL_CHARACTER_MAX_ANIMATED_JOINTS-2);
const S32 LL_CHARACTER_MAX_PRIORITY = 7;
const F32 LL_MAX_PELVIS_OFFSET = 5.f;
-class LLPosOverrideMap
+class LLVector3OverrideMap
{
public:
- LLPosOverrideMap() {}
+ LLVector3OverrideMap() {}
bool findActiveOverride(LLUUID& mesh_id, LLVector3& pos) const;
- void showJointPosOverrides(std::ostringstream& os) const;
+ void showJointVector3Overrides(std::ostringstream& os) const;
U32 count() const;
void add(const LLUUID& mesh_id, const LLVector3& pos);
bool remove(const LLUUID& mesh_id);
void clear();
-private:
+
typedef std::map map_type;
+ const map_type& getMap() const { return m_map; }
+private:
map_type m_map;
};
@@ -86,17 +95,26 @@ public:
POSITION_DIRTY = 0x1 << 2,
ALL_DIRTY = 0x7
};
+public:
+ enum SupportCategory
+ {
+ SUPPORT_BASE,
+ SUPPORT_EXTENDED
+ };
protected:
std::string mName;
+ SupportCategory mSupport;
+
// parent joint
LLJoint *mParent;
// explicit transformation members
LLXformMatrix mXform;
- LLUUID mId;
-
+ LLVector3 mDefaultPosition;
+ LLVector3 mDefaultScale;
+
public:
U32 mDirtyFlags;
BOOL mUpdateXform;
@@ -104,6 +122,10 @@ public:
// describes the skin binding pose
LLVector3 mSkinOffset;
+ // Endpoint of the bone, if applicable. This is only relevant for
+ // external programs like Blender, and for diagnostic display.
+ LLVector3 mEnd;
+
S32 mJointNum;
// child joints
@@ -113,15 +135,40 @@ public:
// debug statics
static S32 sNumTouches;
static S32 sNumUpdates;
+ typedef std::set debug_joint_name_t;
+ static debug_joint_name_t s_debugJointNames;
+ static void setDebugJointNames(const debug_joint_name_t& names);
+ static void setDebugJointNames(const std::string& names_string);
- LLPosOverrideMap m_attachmentOverrides;
+ // Position overrides
+ LLVector3OverrideMap m_attachmentPosOverrides;
LLVector3 m_posBeforeOverrides;
+ // Scale overrides
+ LLVector3OverrideMap m_attachmentScaleOverrides;
+ LLVector3 m_scaleBeforeOverrides;
+
void updatePos(const std::string& av_info);
+ void updateScale(const std::string& av_info);
public:
LLJoint();
- LLJoint(S32 joint_num);
+
+ // Note: these joint_num constructors are a bad idea because there
+ // are only a couple of places in the code where it is useful to
+ // have a joint num for a joint (for joints that are used in
+ // animations), and including them as part of the constructor then
+ // forces us to maintain an alternate path through the entire
+ // large-ish class hierarchy of joint types. The only reason they
+ // are still here now is to avoid breaking the baking service
+ // (appearanceutility) builds; these constructors are not used in
+ // the viewer. Once the appearance utility is updated to remove
+ // these joint num references, which it shouldn't ever need, from
+ // its own classes, we can also remove all the joint_num
+ // constructors from LLJoint, LLViewerJoint, LLAvatarJoint, and
+ // createAvatarJoint.
+ LLJoint(S32 joint_num);
+
// *TODO: Only used for LLVOAvatarSelf::mScreenp. *DOES NOT INITIALIZE mResetAfterRestoreOldXform*
LLJoint( const std::string &name, LLJoint *parent=NULL );
virtual ~LLJoint();
@@ -139,6 +186,19 @@ public:
const std::string& getName() const { return mName; }
void setName( const std::string &name ) { mName = name; }
+ // joint num
+ S32 getJointNum() const { return mJointNum; }
+ void setJointNum(S32 joint_num);
+
+ // get/set support
+ SupportCategory getSupport() const { return mSupport; }
+ void setSupport( const SupportCategory& support) { mSupport = support; }
+ void setSupport( const std::string& support_string);
+
+ // get/set end point
+ void setEnd( const LLVector3& end) { mEnd = end; }
+ const LLVector3& getEnd() const { return mEnd; }
+
// getParent
LLJoint *getParent() { return mParent; }
@@ -155,10 +215,16 @@ public:
// get/set local position
const LLVector3& getPosition();
- void setPosition( const LLVector3& pos );
-
+ void setPosition( const LLVector3& pos, bool apply_attachment_overrides = false );
+
+ // Tracks the default position defined by the skeleton
void setDefaultPosition( const LLVector3& pos );
-
+ const LLVector3& getDefaultPosition() const;
+
+ // Tracks the default scale defined by the skeleton
+ void setDefaultScale( const LLVector3& scale );
+ const LLVector3& getDefaultScale() const;
+
// get/set world position
LLVector3 getWorldPosition();
LLVector3 getLastWorldPosition();
@@ -175,7 +241,7 @@ public:
// get/set local scale
const LLVector3& getScale();
- void setScale( const LLVector3& scale );
+ void setScale( const LLVector3& scale, bool apply_attachment_overrides = false );
// get/set world matrix
const LLMatrix4 &getWorldMatrix();
@@ -198,17 +264,26 @@ public:
virtual BOOL isAnimatable() const { return TRUE; }
- S32 getJointNum() const { return mJointNum; }
-
- void addAttachmentPosOverride( const LLVector3& pos, const LLUUID& mesh_id, const std::string& av_info );
- void removeAttachmentPosOverride( const LLUUID& mesh_id, const std::string& av_info );
+ void addAttachmentPosOverride( const LLVector3& pos, const LLUUID& mesh_id, const std::string& av_info, bool& active_override_changed );
+ void removeAttachmentPosOverride( const LLUUID& mesh_id, const std::string& av_info, bool& active_override_changed );
bool hasAttachmentPosOverride( LLVector3& pos, LLUUID& mesh_id ) const;
void clearAttachmentPosOverrides();
+ void showAttachmentPosOverrides(const std::string& av_info) const;
- //Accessor for the joint id
- LLUUID getId( void ) { return mId; }
- //Setter for the joints id
- void setId( const LLUUID& id ) { mId = id;}
+ void addAttachmentScaleOverride( const LLVector3& scale, const LLUUID& mesh_id, const std::string& av_info );
+ void removeAttachmentScaleOverride( const LLUUID& mesh_id, const std::string& av_info );
+ bool hasAttachmentScaleOverride( LLVector3& scale, LLUUID& mesh_id ) const;
+ void clearAttachmentScaleOverrides();
+ void showAttachmentScaleOverrides(const std::string& av_info) const;
+
+ void getAllAttachmentPosOverrides(S32& num_pos_overrides,
+ std::set& distinct_pos_overrides);
+ void getAllAttachmentScaleOverrides(S32& num_scale_overrides,
+ std::set& distinct_scale_overrides);
+
+ // These are used in checks of whether a pos/scale override is considered significant.
+ bool aboveJointPosThreshold(const LLVector3& pos) const;
+ bool aboveJointScaleThreshold(const LLVector3& scale) const;
};
#endif // LL_LLJOINT_H
diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp
index cd201a65b4..89f34a92c8 100644
--- a/indra/llcharacter/llkeyframemotion.cpp
+++ b/indra/llcharacter/llkeyframemotion.cpp
@@ -498,6 +498,7 @@ LLMotion::LLMotionInitStatus LLKeyframeMotion::onInitialize(LLCharacter *charact
// request asset
mAssetStatus = ASSET_FETCHED;
+ LL_DEBUGS("Animation") << "Requesting data fetch for: " << mID << LL_ENDL;
character_id = new LLUUID(mCharacter->getID());
gAssetStorage->getAssetData(mID,
LLAssetType::AT_ANIMATION,
@@ -1380,7 +1381,7 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp)
LL_WARNS() << "no joints in animation" << LL_ENDL;
return FALSE;
}
- else if (num_motions > LL_CHARACTER_MAX_JOINTS)
+ else if (num_motions > LL_CHARACTER_MAX_ANIMATED_JOINTS)
{
LL_WARNS() << "too many joints in animation" << LL_ENDL;
return FALSE;
@@ -1419,7 +1420,14 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp)
LLJoint *joint = mCharacter->getJoint( joint_name );
if (joint)
{
+ S32 joint_num = joint->getJointNum();
// LL_INFOS() << " joint: " << joint_name << LL_ENDL;
+ if ((joint_num >= (S32)LL_CHARACTER_MAX_ANIMATED_JOINTS) || (joint_num < 0))
+ {
+ LL_WARNS() << "Joint will be omitted from animation: joint_num " << joint_num << " is outside of legal range [0-"
+ << LL_CHARACTER_MAX_ANIMATED_JOINTS << ") for joint " << joint->getName() << LL_ENDL;
+ joint = NULL;
+ }
}
else
{
@@ -1603,6 +1611,12 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp)
if (old_version)
{
success = dp.unpackVector3(pos_key.mPosition, "pos");
+
+ //MAINT-6162
+ pos_key.mPosition.mV[VX] = llclamp( pos_key.mPosition.mV[VX], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ pos_key.mPosition.mV[VY] = llclamp( pos_key.mPosition.mV[VY], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ pos_key.mPosition.mV[VZ] = llclamp( pos_key.mPosition.mV[VZ], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+
}
else
{
@@ -1868,6 +1882,8 @@ BOOL LLKeyframeMotion::serialize(LLDataPacker& dp) const
{
BOOL success = TRUE;
+ LL_DEBUGS("BVH") << "serializing" << LL_ENDL;
+
success &= dp.packU16(KEYFRAME_MOTION_VERSION, "version");
success &= dp.packU16(KEYFRAME_MOTION_SUBVERSION, "sub_version");
success &= dp.packS32(mJointMotionList->mBasePriority, "base_priority");
@@ -1881,6 +1897,19 @@ BOOL LLKeyframeMotion::serialize(LLDataPacker& dp) const
success &= dp.packU32(mJointMotionList->mHandPose, "hand_pose");
success &= dp.packU32(mJointMotionList->getNumJointMotions(), "num_joints");
+ LL_DEBUGS("BVH") << "version " << KEYFRAME_MOTION_VERSION << LL_ENDL;
+ LL_DEBUGS("BVH") << "sub_version " << KEYFRAME_MOTION_SUBVERSION << LL_ENDL;
+ LL_DEBUGS("BVH") << "base_priority " << mJointMotionList->mBasePriority << LL_ENDL;
+ LL_DEBUGS("BVH") << "duration " << mJointMotionList->mDuration << LL_ENDL;
+ LL_DEBUGS("BVH") << "emote_name " << mJointMotionList->mEmoteName << LL_ENDL;
+ LL_DEBUGS("BVH") << "loop_in_point " << mJointMotionList->mLoopInPoint << LL_ENDL;
+ LL_DEBUGS("BVH") << "loop_out_point " << mJointMotionList->mLoopOutPoint << LL_ENDL;
+ LL_DEBUGS("BVH") << "loop " << mJointMotionList->mLoop << LL_ENDL;
+ LL_DEBUGS("BVH") << "ease_in_duration " << mJointMotionList->mEaseInDuration << LL_ENDL;
+ LL_DEBUGS("BVH") << "ease_out_duration " << mJointMotionList->mEaseOutDuration << LL_ENDL;
+ LL_DEBUGS("BVH") << "hand_pose " << mJointMotionList->mHandPose << LL_ENDL;
+ LL_DEBUGS("BVH") << "num_joints " << mJointMotionList->getNumJointMotions() << LL_ENDL;
+
for (U32 i = 0; i < mJointMotionList->getNumJointMotions(); i++)
{
JointMotion* joint_motionp = mJointMotionList->getJointMotion(i);
@@ -1888,6 +1917,7 @@ BOOL LLKeyframeMotion::serialize(LLDataPacker& dp) const
success &= dp.packS32(joint_motionp->mPriority, "joint_priority");
success &= dp.packS32(joint_motionp->mRotationCurve.mNumKeys, "num_rot_keys");
+ LL_DEBUGS("BVH") << "Joint " << joint_motionp->mJointName << LL_ENDL;
for (RotationCurve::key_map_t::iterator iter = joint_motionp->mRotationCurve.mKeys.begin();
iter != joint_motionp->mRotationCurve.mKeys.end(); ++iter)
{
@@ -1905,6 +1935,8 @@ BOOL LLKeyframeMotion::serialize(LLDataPacker& dp) const
success &= dp.packU16(x, "rot_angle_x");
success &= dp.packU16(y, "rot_angle_y");
success &= dp.packU16(z, "rot_angle_z");
+
+ LL_DEBUGS("BVH") << " rot: t " << rot_key.mTime << " angles " << rot_angles.mV[VX] <<","<< rot_angles.mV[VY] <<","<< rot_angles.mV[VZ] << LL_ENDL;
}
success &= dp.packS32(joint_motionp->mPositionCurve.mNumKeys, "num_pos_keys");
@@ -1923,37 +1955,54 @@ BOOL LLKeyframeMotion::serialize(LLDataPacker& dp) const
success &= dp.packU16(x, "pos_x");
success &= dp.packU16(y, "pos_y");
success &= dp.packU16(z, "pos_z");
+
+ LL_DEBUGS("BVH") << " pos: t " << pos_key.mTime << " pos " << pos_key.mPosition.mV[VX] <<","<< pos_key.mPosition.mV[VY] <<","<< pos_key.mPosition.mV[VZ] << LL_ENDL;
}
}
success &= dp.packS32(mJointMotionList->mConstraints.size(), "num_constraints");
+ LL_DEBUGS("BVH") << "num_constraints " << mJointMotionList->mConstraints.size() << LL_ENDL;
for (JointMotionList::constraint_list_t::const_iterator iter = mJointMotionList->mConstraints.begin();
iter != mJointMotionList->mConstraints.end(); ++iter)
{
JointConstraintSharedData* shared_constraintp = *iter;
success &= dp.packU8(shared_constraintp->mChainLength, "chain_length");
success &= dp.packU8(shared_constraintp->mConstraintType, "constraint_type");
- char volume_name[16]; /* Flawfinder: ignore */
- snprintf(volume_name, sizeof(volume_name), "%s", /* Flawfinder: ignore */
+ char source_volume[16]; /* Flawfinder: ignore */
+ snprintf(source_volume, sizeof(source_volume), "%s", /* Flawfinder: ignore */
mCharacter->findCollisionVolume(shared_constraintp->mSourceConstraintVolume)->getName().c_str());
- success &= dp.packBinaryDataFixed((U8*)volume_name, 16, "source_volume");
+
+ success &= dp.packBinaryDataFixed((U8*)source_volume, 16, "source_volume");
success &= dp.packVector3(shared_constraintp->mSourceConstraintOffset, "source_offset");
+ char target_volume[16]; /* Flawfinder: ignore */
if (shared_constraintp->mConstraintTargetType == CONSTRAINT_TARGET_TYPE_GROUND)
{
- snprintf(volume_name,sizeof(volume_name), "%s", "GROUND"); /* Flawfinder: ignore */
+ snprintf(target_volume,sizeof(target_volume), "%s", "GROUND"); /* Flawfinder: ignore */
}
else
{
- snprintf(volume_name, sizeof(volume_name),"%s", /* Flawfinder: ignore */
+ snprintf(target_volume, sizeof(target_volume),"%s", /* Flawfinder: ignore */
mCharacter->findCollisionVolume(shared_constraintp->mTargetConstraintVolume)->getName().c_str());
}
- success &= dp.packBinaryDataFixed((U8*)volume_name, 16, "target_volume");
+ success &= dp.packBinaryDataFixed((U8*)target_volume, 16, "target_volume");
success &= dp.packVector3(shared_constraintp->mTargetConstraintOffset, "target_offset");
success &= dp.packVector3(shared_constraintp->mTargetConstraintDir, "target_dir");
success &= dp.packF32(shared_constraintp->mEaseInStartTime, "ease_in_start");
success &= dp.packF32(shared_constraintp->mEaseInStopTime, "ease_in_stop");
success &= dp.packF32(shared_constraintp->mEaseOutStartTime, "ease_out_start");
success &= dp.packF32(shared_constraintp->mEaseOutStopTime, "ease_out_stop");
+
+ LL_DEBUGS("BVH") << " chain_length " << shared_constraintp->mChainLength << LL_ENDL;
+ LL_DEBUGS("BVH") << " constraint_type " << (S32)shared_constraintp->mConstraintType << LL_ENDL;
+ LL_DEBUGS("BVH") << " source_volume " << source_volume << LL_ENDL;
+ LL_DEBUGS("BVH") << " source_offset " << shared_constraintp->mSourceConstraintOffset << LL_ENDL;
+ LL_DEBUGS("BVH") << " target_volume " << target_volume << LL_ENDL;
+ LL_DEBUGS("BVH") << " target_offset " << shared_constraintp->mTargetConstraintOffset << LL_ENDL;
+ LL_DEBUGS("BVH") << " target_dir " << shared_constraintp->mTargetConstraintDir << LL_ENDL;
+ LL_DEBUGS("BVH") << " ease_in_start " << shared_constraintp->mEaseInStartTime << LL_ENDL;
+ LL_DEBUGS("BVH") << " ease_in_stop " << shared_constraintp->mEaseInStopTime << LL_ENDL;
+ LL_DEBUGS("BVH") << " ease_out_start " << shared_constraintp->mEaseOutStartTime << LL_ENDL;
+ LL_DEBUGS("BVH") << " ease_out_stop " << shared_constraintp->mEaseOutStopTime << LL_ENDL;
}
return success;
@@ -1971,6 +2020,51 @@ U32 LLKeyframeMotion::getFileSize()
return dp.getCurrentSize();
}
+//-----------------------------------------------------------------------------
+// dumpToFile()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::dumpToFile(const std::string& name)
+{
+ if (isLoaded())
+ {
+ std::string outfile_base;
+ if (!name.empty())
+ {
+ outfile_base = name;
+ }
+ else if (!getName().empty())
+ {
+ outfile_base = getName();
+ }
+ else
+ {
+ const LLUUID& id = getID();
+ outfile_base = id.asString();
+ }
+ std::string outfilename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,outfile_base + ".anim");
+ if (LLFile::isfile(outfilename))
+ {
+ return;
+ }
+
+ S32 file_size = getFileSize();
+ U8* buffer = new U8[file_size];
+
+ LL_DEBUGS("BVH") << "Dumping " << outfilename << LL_ENDL;
+ LLDataPackerBinaryBuffer dp(buffer, file_size);
+ if (serialize(dp))
+ {
+ LLAPRFile outfile;
+ outfile.open(outfilename, LL_APR_WPB);
+ if (outfile.getFileHandle())
+ {
+ outfile.write(buffer, file_size);
+ }
+ }
+ delete [] buffer;
+ }
+}
+
//-----------------------------------------------------------------------------
// getPelvisBBox()
//-----------------------------------------------------------------------------
@@ -2149,7 +2243,7 @@ void LLKeyframeMotion::onLoadComplete(LLVFS *vfs,
LLCharacter* character = *char_iter;
// look for an existing instance of this motion
- LLKeyframeMotion* motionp = (LLKeyframeMotion*) character->findMotion(asset_uuid);
+ LLKeyframeMotion* motionp = dynamic_cast (character->findMotion(asset_uuid));
if (motionp)
{
if (0 == status)
@@ -2164,8 +2258,8 @@ void LLKeyframeMotion::onLoadComplete(LLVFS *vfs,
U8* buffer = new U8[size];
file.read((U8*)buffer, size); /*Flawfinder: ignore*/
-
- LL_DEBUGS() << "Loading keyframe data for: " << motionp->getName() << ":" << motionp->getID() << " (" << size << " bytes)" << LL_ENDL;
+
+ LL_DEBUGS("Animation") << "Loading keyframe data for: " << motionp->getName() << ":" << motionp->getID() << " (" << size << " bytes)" << LL_ENDL;
LLDataPackerBinaryBuffer dp(buffer, size);
if (motionp->deserialize(dp))
diff --git a/indra/llcharacter/llkeyframemotion.h b/indra/llcharacter/llkeyframemotion.h
index b1422b2b90..f1fa56d731 100644
--- a/indra/llcharacter/llkeyframemotion.h
+++ b/indra/llcharacter/llkeyframemotion.h
@@ -153,6 +153,7 @@ public:
BOOL serialize(LLDataPacker& dp) const;
BOOL deserialize(LLDataPacker& dp);
BOOL isLoaded() { return mJointMotionList != NULL; }
+ void dumpToFile(const std::string& name);
// setters for modifying a keyframe animation
diff --git a/indra/llcharacter/llkeyframestandmotion.cpp b/indra/llcharacter/llkeyframestandmotion.cpp
index fdeddf55e1..02c1d3cdbd 100644
--- a/indra/llcharacter/llkeyframestandmotion.cpp
+++ b/indra/llcharacter/llkeyframestandmotion.cpp
@@ -201,10 +201,12 @@ BOOL LLKeyframeStandMotion::onUpdate(F32 time, U8* joint_mask)
//-------------------------------------------------------------------------
// propagate joint positions to internal versions
//-------------------------------------------------------------------------
+ // SL-315
mPelvisJoint.setPosition(
root_world_pos +
mPelvisState->getPosition() );
+ // SL-315
mHipLeftJoint.setPosition( mHipLeftState->getJoint()->getPosition() );
mKneeLeftJoint.setPosition( mKneeLeftState->getJoint()->getPosition() );
mAnkleLeftJoint.setPosition( mAnkleLeftState->getJoint()->getPosition() );
@@ -213,6 +215,7 @@ BOOL LLKeyframeStandMotion::onUpdate(F32 time, U8* joint_mask)
mKneeLeftJoint.setScale( mKneeLeftState->getJoint()->getScale() );
mAnkleLeftJoint.setScale( mAnkleLeftState->getJoint()->getScale() );
+ // SL-315
mHipRightJoint.setPosition( mHipRightState->getJoint()->getPosition() );
mKneeRightJoint.setPosition( mKneeRightState->getJoint()->getPosition() );
mAnkleRightJoint.setPosition( mAnkleRightState->getJoint()->getPosition() );
@@ -265,6 +268,7 @@ BOOL LLKeyframeStandMotion::onUpdate(F32 time, U8* joint_mask)
mCharacter->getGround( mAnkleLeftJoint.getWorldPosition(), mPositionLeft, mNormalLeft);
mCharacter->getGround( mAnkleRightJoint.getWorldPosition(), mPositionRight, mNormalRight);
+ // SL-315
mTargetLeft.setPosition( mPositionLeft );
mTargetRight.setPosition( mPositionRight );
}
diff --git a/indra/llcharacter/llmotion.cpp b/indra/llcharacter/llmotion.cpp
index 4803f855de..697efc8157 100644
--- a/indra/llcharacter/llmotion.cpp
+++ b/indra/llcharacter/llmotion.cpp
@@ -55,7 +55,7 @@ LLMotion::LLMotion( const LLUUID &id ) :
mDeactivateCallbackUserData(NULL)
{
for (S32 i=0; i<3; ++i)
- memset(&mJointSignature[i][0], 0, sizeof(U8) * LL_CHARACTER_MAX_JOINTS);
+ memset(&mJointSignature[i][0], 0, sizeof(U8) * LL_CHARACTER_MAX_ANIMATED_JOINTS);
}
//-----------------------------------------------------------------------------
@@ -111,9 +111,15 @@ void LLMotion::addJointState(const LLPointer& jointState)
U32 usage = jointState->getUsage();
// for now, usage is everything
- mJointSignature[0][jointState->getJoint()->getJointNum()] = (usage & LLJointState::POS) ? (0xff >> (7 - priority)) : 0;
- mJointSignature[1][jointState->getJoint()->getJointNum()] = (usage & LLJointState::ROT) ? (0xff >> (7 - priority)) : 0;
- mJointSignature[2][jointState->getJoint()->getJointNum()] = (usage & LLJointState::SCALE) ? (0xff >> (7 - priority)) : 0;
+ S32 joint_num = jointState->getJoint()->getJointNum();
+ if ((joint_num >= (S32)LL_CHARACTER_MAX_ANIMATED_JOINTS) || (joint_num < 0))
+ {
+ LL_WARNS() << "joint_num " << joint_num << " is outside of legal range [0-" << LL_CHARACTER_MAX_ANIMATED_JOINTS << ") for joint " << jointState->getJoint()->getName() << LL_ENDL;
+ return;
+ }
+ mJointSignature[0][joint_num] = (usage & LLJointState::POS) ? (0xff >> (7 - priority)) : 0;
+ mJointSignature[1][joint_num] = (usage & LLJointState::ROT) ? (0xff >> (7 - priority)) : 0;
+ mJointSignature[2][joint_num] = (usage & LLJointState::SCALE) ? (0xff >> (7 - priority)) : 0;
}
void LLMotion::setDeactivateCallback( void (*cb)(void *), void* userdata )
diff --git a/indra/llcharacter/llmotion.h b/indra/llcharacter/llmotion.h
index 5e37f094b8..2dfc3afc7f 100644
--- a/indra/llcharacter/llmotion.h
+++ b/indra/llcharacter/llmotion.h
@@ -181,7 +181,7 @@ protected:
F32 mSendStopTimestamp; // time when simulator should be told to stop this motion
F32 mResidualWeight; // blend weight at beginning of stop motion phase
F32 mFadeWeight; // for fading in and out based on LOD
- U8 mJointSignature[3][LL_CHARACTER_MAX_JOINTS]; // signature of which joints are animated at what priority
+ U8 mJointSignature[3][LL_CHARACTER_MAX_ANIMATED_JOINTS]; // signature of which joints are animated at what priority
void (*mDeactivateCallback)(void* data);
void* mDeactivateCallbackUserData;
};
diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp
index e02b139608..d8185aa693 100644
--- a/indra/llcharacter/llmotioncontroller.cpp
+++ b/indra/llcharacter/llmotioncontroller.cpp
@@ -37,7 +37,8 @@
#include "llanimationstates.h"
#include "llstl.h"
-const S32 NUM_JOINT_SIGNATURE_STRIDES = LL_CHARACTER_MAX_JOINTS / 4;
+// This is why LL_CHARACTER_MAX_ANIMATED_JOINTS needs to be a multiple of 4.
+const S32 NUM_JOINT_SIGNATURE_STRIDES = LL_CHARACTER_MAX_ANIMATED_JOINTS / 4;
const U32 MAX_MOTION_INSTANCES = 32;
//-----------------------------------------------------------------------------
@@ -488,8 +489,8 @@ void LLMotionController::updateAdditiveMotions()
//-----------------------------------------------------------------------------
void LLMotionController::resetJointSignatures()
{
- memset(&mJointSignature[0][0], 0, sizeof(U8) * LL_CHARACTER_MAX_JOINTS);
- memset(&mJointSignature[1][0], 0, sizeof(U8) * LL_CHARACTER_MAX_JOINTS);
+ memset(&mJointSignature[0][0], 0, sizeof(U8) * LL_CHARACTER_MAX_ANIMATED_JOINTS);
+ memset(&mJointSignature[1][0], 0, sizeof(U8) * LL_CHARACTER_MAX_ANIMATED_JOINTS);
}
//-----------------------------------------------------------------------------
@@ -553,9 +554,9 @@ static LLTrace::BlockTimerStatHandle FTM_MOTION_ON_UPDATE("Motion onUpdate");
void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_type)
{
BOOL update_result = TRUE;
- U8 last_joint_signature[LL_CHARACTER_MAX_JOINTS];
+ U8 last_joint_signature[LL_CHARACTER_MAX_ANIMATED_JOINTS];
- memset(&last_joint_signature, 0, sizeof(U8) * LL_CHARACTER_MAX_JOINTS);
+ memset(&last_joint_signature, 0, sizeof(U8) * LL_CHARACTER_MAX_ANIMATED_JOINTS);
// iterate through active motions in chronological order
for (motion_list_t::iterator iter = mActiveMotions.begin();
@@ -576,7 +577,6 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
}
else
{
- // NUM_JOINT_SIGNATURE_STRIDES should be multiple of 4
for (S32 i = 0; i < NUM_JOINT_SIGNATURE_STRIDES; i++)
{
U32 *current_signature = (U32*)&(mJointSignature[0][i * 4]);
diff --git a/indra/llcharacter/llmotioncontroller.h b/indra/llcharacter/llmotioncontroller.h
index 2bd5271c4f..72de331694 100644
--- a/indra/llcharacter/llmotioncontroller.h
+++ b/indra/llcharacter/llmotioncontroller.h
@@ -223,7 +223,7 @@ protected:
S32 mTimeStepCount;
F32 mLastInterp;
- U8 mJointSignature[2][LL_CHARACTER_MAX_JOINTS];
+ U8 mJointSignature[2][LL_CHARACTER_MAX_ANIMATED_JOINTS];
};
//-----------------------------------------------------------------------------
diff --git a/indra/llcharacter/llpose.cpp b/indra/llcharacter/llpose.cpp
index b1a7ebb159..fc95fafd61 100644
--- a/indra/llcharacter/llpose.cpp
+++ b/indra/llcharacter/llpose.cpp
@@ -386,6 +386,7 @@ void LLJointStateBlender::blendJointStates(BOOL apply_now)
}
// apply transforms
+ // SL-315
target_joint->setPosition(blended_pos + added_pos);
target_joint->setScale(blended_scale + added_scale);
target_joint->setRotation(added_rot * blended_rot);
@@ -417,6 +418,7 @@ void LLJointStateBlender::interpolate(F32 u)
return;
}
+ // SL-315
target_joint->setPosition(lerp(target_joint->getPosition(), mJointCache.getPosition(), u));
target_joint->setScale(lerp(target_joint->getScale(), mJointCache.getScale(), u));
target_joint->setRotation(nlerp(u, target_joint->getRotation(), mJointCache.getRotation()));
@@ -444,6 +446,7 @@ void LLJointStateBlender::resetCachedJoint()
return;
}
LLJoint* source_joint = mJointStates[0]->getJoint();
+ // SL-315
mJointCache.setPosition(source_joint->getPosition());
mJointCache.setScale(source_joint->getScale());
mJointCache.setRotation(source_joint->getRotation());
diff --git a/indra/llcharacter/llvisualparam.h b/indra/llcharacter/llvisualparam.h
index c6b97d7e8b..0ad063fd1e 100644
--- a/indra/llcharacter/llvisualparam.h
+++ b/indra/llcharacter/llvisualparam.h
@@ -150,6 +150,7 @@ public:
F32 getWeight() const { return mIsAnimating ? mTargetWeight : mCurWeight; }
F32 getCurrentWeight() const { return mCurWeight; }
F32 getLastWeight() const { return mLastWeight; }
+ void setLastWeight(F32 val) { mLastWeight = val; }
BOOL isAnimating() const { return mIsAnimating; }
BOOL isTweakable() const { return (getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE) || (getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT); }
diff --git a/indra/llcharacter/tests/lljoint_test.cpp b/indra/llcharacter/tests/lljoint_test.cpp
index da151808f2..617f31b0e4 100644
--- a/indra/llcharacter/tests/lljoint_test.cpp
+++ b/indra/llcharacter/tests/lljoint_test.cpp
@@ -88,6 +88,7 @@ namespace tut
{
LLJoint lljoint;
LLVector3 vec3(2.3f,30.f,10.f);
+ // SL-315
lljoint.setPosition(vec3);
LLVector3 pos = lljoint.getPosition();
ensure("setPosition()/getPosition() failed ", (vec3 == pos));
@@ -98,6 +99,7 @@ namespace tut
{
LLJoint lljoint;
LLVector3 vec3(2.3f,30.f,10.f);
+ // SL-315
lljoint.setWorldPosition(vec3);
LLVector3 pos = lljoint.getWorldPosition();
ensure("1:setWorldPosition()/getWorldPosition() failed ", (vec3 == pos));
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 44f45144e5..5bce2b8809 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -40,6 +40,7 @@ set(llcommon_SOURCE_FILES
llbase64.cpp
llbitpack.cpp
llcallbacklist.cpp
+ llcallstack.cpp
llcommon.cpp
llcommonutils.cpp
llcoros.cpp
@@ -58,6 +59,7 @@ set(llcommon_SOURCE_FILES
lleventfilter.cpp
llevents.cpp
lleventtimer.cpp
+ llexception.cpp
llfasttimer.cpp
llfile.cpp
llfindlocale.cpp
@@ -114,6 +116,7 @@ set(llcommon_SOURCE_FILES
llworkerthread.cpp
timing.cpp
u64.cpp
+ StackWalker.cpp
)
set(llcommon_HEADER_FILES
@@ -134,6 +137,7 @@ set(llcommon_HEADER_FILES
llbitpack.h
llboost.h
llcallbacklist.h
+ llcallstack.h
llcommon.h
llcommonutils.h
llcoros.h
@@ -236,6 +240,7 @@ set(llcommon_HEADER_FILES
stringize.h
timer.h
u64.h
+ StackWalker.h
)
set_source_files_properties(${llcommon_HEADER_FILES}
@@ -316,7 +321,7 @@ if (LL_TESTS)
LL_ADD_INTEGRATION_TEST(llprocinfo "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llrand "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}")
- LL_ADD_INTEGRATION_TEST(llsingleton "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(llsingleton "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llstring "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lltrace "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lltreeiterators "" "${test_libs}")
@@ -329,6 +334,11 @@ if (LL_TESTS)
LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}")
+## llexception_test.cpp isn't a regression test, and doesn't need to be run
+## every build. It's to help a developer make implementation choices about
+## throwing and catching exceptions.
+##LL_ADD_INTEGRATION_TEST(llexception "" "${test_libs}")
+
# *TODO - reenable these once tcmalloc libs no longer break the build.
#ADD_BUILD_TEST(llallocator llcommon)
#ADD_BUILD_TEST(llallocator_heap_profile llcommon)
diff --git a/indra/llcommon/StackWalker.cpp b/indra/llcommon/StackWalker.cpp
new file mode 100644
index 0000000000..c0d3104099
--- /dev/null
+++ b/indra/llcommon/StackWalker.cpp
@@ -0,0 +1,1388 @@
+/**********************************************************************
+ *
+ * StackWalker.cpp
+ * http://stackwalker.codeplex.com/
+ *
+ *
+ * $LicenseInfo:firstyear=2016&license=bsd$
+ *
+ * Linden notes: Small modifications from the original source at https://stackwalker.codeplex.com/
+ *
+ * History:
+ * 2005-07-27 v1 - First public release on http://www.codeproject.com/
+ * http://www.codeproject.com/threads/StackWalker.asp
+ * 2005-07-28 v2 - Changed the params of the constructor and ShowCallstack
+ * (to simplify the usage)
+ * 2005-08-01 v3 - Changed to use 'CONTEXT_FULL' instead of CONTEXT_ALL
+ * (should also be enough)
+ * - Changed to compile correctly with the PSDK of VC7.0
+ * (GetFileVersionInfoSizeA and GetFileVersionInfoA is wrongly defined:
+ * it uses LPSTR instead of LPCSTR as first paremeter)
+ * - Added declarations to support VC5/6 without using 'dbghelp.h'
+ * - Added a 'pUserData' member to the ShowCallstack function and the
+ * PReadProcessMemoryRoutine declaration (to pass some user-defined data,
+ * which can be used in the readMemoryFunction-callback)
+ * 2005-08-02 v4 - OnSymInit now also outputs the OS-Version by default
+ * - Added example for doing an exception-callstack-walking in main.cpp
+ * (thanks to owillebo: http://www.codeproject.com/script/profile/whos_who.asp?id=536268)
+ * 2005-08-05 v5 - Removed most Lint (http://www.gimpel.com/) errors... thanks to Okko Willeboordse!
+ * 2008-08-04 v6 - Fixed Bug: Missing LEAK-end-tag
+ * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2502890#xx2502890xx
+ * Fixed Bug: Compiled with "WIN32_LEAN_AND_MEAN"
+ * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=1824718#xx1824718xx
+ * Fixed Bug: Compiling with "/Wall"
+ * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2638243#xx2638243xx
+ * Fixed Bug: Now checking SymUseSymSrv
+ * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1388979#xx1388979xx
+ * Fixed Bug: Support for recursive function calls
+ * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1434538#xx1434538xx
+ * Fixed Bug: Missing FreeLibrary call in "GetModuleListTH32"
+ * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1326923#xx1326923xx
+ * Fixed Bug: SymDia is number 7, not 9!
+ * 2008-09-11 v7 For some (undocumented) reason, dbhelp.h is needing a packing of 8!
+ * Thanks to Teajay which reported the bug...
+ * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2718933#xx2718933xx
+ * 2008-11-27 v8 Debugging Tools for Windows are now stored in a different directory
+ * Thanks to Luiz Salamon which reported this "bug"...
+ * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2822736#xx2822736xx
+ * 2009-04-10 v9 License slihtly corrected ( replaced)
+ * 2009-11-01 v10 Moved to http://stackwalker.codeplex.com/
+ * 2009-11-02 v11 Now try to use IMAGEHLP_MODULE64_V3 if available
+ * 2010-04-15 v12 Added support for VS2010 RTM
+ * 2010-05-25 v13 Now using secure MyStrcCpy. Thanks to luke.simon:
+ * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=3477467#xx3477467xx
+ * 2013-01-07 v14 Runtime Check Error VS2010 Debug Builds fixed:
+ * http://stackwalker.codeplex.com/workitem/10511
+ *
+ *
+ * LICENSE (http://www.opensource.org/licenses/bsd-license.php)
+ *
+ * Copyright (c) 2005-2013, Jochen Kalmbach
+ * 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 Jochen Kalmbach 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.
+ *
+ **********************************************************************/
+#if LL_WINDOWS
+
+#include
+#include
+#include
+#include
+#pragma comment(lib, "version.lib") // for "VerQueryValue"
+#pragma warning(disable:4826)
+
+#include "StackWalker.h"
+
+
+// If VC7 and later, then use the shipped 'dbghelp.h'-file
+#pragma pack(push,8)
+#if _MSC_VER >= 1300
+#include
+#else
+// inline the important dbghelp.h-declarations...
+typedef enum {
+ SymNone = 0,
+ SymCoff,
+ SymCv,
+ SymPdb,
+ SymExport,
+ SymDeferred,
+ SymSym,
+ SymDia,
+ SymVirtual,
+ NumSymTypes
+} SYM_TYPE;
+typedef struct _IMAGEHLP_LINE64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64)
+ PVOID Key; // internal
+ DWORD LineNumber; // line number in file
+ PCHAR FileName; // full filename
+ DWORD64 Address; // first instruction of line
+} IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;
+typedef struct _IMAGEHLP_MODULE64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
+ DWORD64 BaseOfImage; // base load address of module
+ DWORD ImageSize; // virtual size of the loaded module
+ DWORD TimeDateStamp; // date/time stamp from pe header
+ DWORD CheckSum; // checksum from the pe header
+ DWORD NumSyms; // number of symbols in the symbol table
+ SYM_TYPE SymType; // type of symbols loaded
+ CHAR ModuleName[32]; // module name
+ CHAR ImageName[256]; // image name
+ CHAR LoadedImageName[256]; // symbol file name
+} IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64;
+typedef struct _IMAGEHLP_SYMBOL64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL64)
+ DWORD64 Address; // virtual address including dll base address
+ DWORD Size; // estimated size of symbol, can be zero
+ DWORD Flags; // info about the symbols, see the SYMF defines
+ DWORD MaxNameLength; // maximum size of symbol name in 'Name'
+ CHAR Name[1]; // symbol name (null terminated string)
+} IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64;
+typedef enum {
+ AddrMode1616,
+ AddrMode1632,
+ AddrModeReal,
+ AddrModeFlat
+} ADDRESS_MODE;
+typedef struct _tagADDRESS64 {
+ DWORD64 Offset;
+ WORD Segment;
+ ADDRESS_MODE Mode;
+} ADDRESS64, *LPADDRESS64;
+typedef struct _KDHELP64 {
+ DWORD64 Thread;
+ DWORD ThCallbackStack;
+ DWORD ThCallbackBStore;
+ DWORD NextCallback;
+ DWORD FramePointer;
+ DWORD64 KiCallUserMode;
+ DWORD64 KeUserCallbackDispatcher;
+ DWORD64 SystemRangeStart;
+ DWORD64 Reserved[8];
+} KDHELP64, *PKDHELP64;
+typedef struct _tagSTACKFRAME64 {
+ ADDRESS64 AddrPC; // program counter
+ ADDRESS64 AddrReturn; // return address
+ ADDRESS64 AddrFrame; // frame pointer
+ ADDRESS64 AddrStack; // stack pointer
+ ADDRESS64 AddrBStore; // backing store pointer
+ PVOID FuncTableEntry; // pointer to pdata/fpo or NULL
+ DWORD64 Params[4]; // possible arguments to the function
+ BOOL Far; // WOW far call
+ BOOL Virtual; // is this a virtual frame?
+ DWORD64 Reserved[3];
+ KDHELP64 KdHelp;
+} STACKFRAME64, *LPSTACKFRAME64;
+typedef
+BOOL
+(__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)(
+ HANDLE hProcess,
+ DWORD64 qwBaseAddress,
+ PVOID lpBuffer,
+ DWORD nSize,
+ LPDWORD lpNumberOfBytesRead
+ );
+typedef
+PVOID
+(__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)(
+ HANDLE hProcess,
+ DWORD64 AddrBase
+ );
+typedef
+DWORD64
+(__stdcall *PGET_MODULE_BASE_ROUTINE64)(
+ HANDLE hProcess,
+ DWORD64 Address
+ );
+typedef
+DWORD64
+(__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(
+ HANDLE hProcess,
+ HANDLE hThread,
+ LPADDRESS64 lpaddr
+ );
+#define SYMOPT_CASE_INSENSITIVE 0x00000001
+#define SYMOPT_UNDNAME 0x00000002
+#define SYMOPT_DEFERRED_LOADS 0x00000004
+#define SYMOPT_NO_CPP 0x00000008
+#define SYMOPT_LOAD_LINES 0x00000010
+#define SYMOPT_OMAP_FIND_NEAREST 0x00000020
+#define SYMOPT_LOAD_ANYTHING 0x00000040
+#define SYMOPT_IGNORE_CVREC 0x00000080
+#define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100
+#define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200
+#define SYMOPT_EXACT_SYMBOLS 0x00000400
+#define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800
+#define SYMOPT_IGNORE_NT_SYMPATH 0x00001000
+#define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000
+#define SYMOPT_PUBLICS_ONLY 0x00004000
+#define SYMOPT_NO_PUBLICS 0x00008000
+#define SYMOPT_AUTO_PUBLICS 0x00010000
+#define SYMOPT_NO_IMAGE_SEARCH 0x00020000
+#define SYMOPT_SECURE 0x00040000
+#define SYMOPT_DEBUG 0x80000000
+#define UNDNAME_COMPLETE (0x0000) // Enable full undecoration
+#define UNDNAME_NAME_ONLY (0x1000) // Crack only the name for primary declaration;
+#endif // _MSC_VER < 1300
+#pragma pack(pop)
+
+// Some missing defines (for VC5/6):
+#ifndef INVALID_FILE_ATTRIBUTES
+#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
+#endif
+
+
+// secure-CRT_functions are only available starting with VC8
+#if _MSC_VER < 1400
+#define strcpy_s(dst, len, src) strcpy(dst, src)
+#define strncpy_s(dst, len, src, maxLen) strncpy(dst, len, src)
+#define strcat_s(dst, len, src) strcat(dst, src)
+#define _snprintf_s _snprintf
+#define _tcscat_s _tcscat
+#endif
+
+static void MyStrCpy(char* szDest, size_t nMaxDestSize, const char* szSrc)
+{
+ if (nMaxDestSize <= 0) return;
+ strncpy_s(szDest, nMaxDestSize, szSrc, _TRUNCATE);
+ szDest[nMaxDestSize-1] = 0; // INFO: _TRUNCATE will ensure that it is nul-terminated; but with older compilers (<1400) it uses "strncpy" and this does not!)
+} // MyStrCpy
+
+// Normally it should be enough to use 'CONTEXT_FULL' (better would be 'CONTEXT_ALL')
+#define USED_CONTEXT_FLAGS CONTEXT_FULL
+
+
+class StackWalkerInternal
+{
+public:
+ StackWalkerInternal(StackWalker *parent, HANDLE hProcess)
+ {
+ m_parent = parent;
+ m_hDbhHelp = NULL;
+ pSC = NULL;
+ m_hProcess = hProcess;
+ m_szSymPath = NULL;
+ pSFTA = NULL;
+ pSGLFA = NULL;
+ pSGMB = NULL;
+ pSGMI = NULL;
+ pSGO = NULL;
+ pSGSFA = NULL;
+ pSI = NULL;
+ pSLM = NULL;
+ pSSO = NULL;
+ pSW = NULL;
+ pUDSN = NULL;
+ pSGSP = NULL;
+ }
+ ~StackWalkerInternal()
+ {
+ if (pSC != NULL)
+ pSC(m_hProcess); // SymCleanup
+ if (m_hDbhHelp != NULL)
+ FreeLibrary(m_hDbhHelp);
+ m_hDbhHelp = NULL;
+ m_parent = NULL;
+ if(m_szSymPath != NULL)
+ free(m_szSymPath);
+ m_szSymPath = NULL;
+ }
+ BOOL Init(LPCSTR szSymPath)
+ {
+ if (m_parent == NULL)
+ return FALSE;
+ // Dynamically load the Entry-Points for dbghelp.dll:
+ // First try to load the newsest one from
+ TCHAR szTemp[4096];
+ // But before wqe do this, we first check if the ".local" file exists
+ if (GetModuleFileName(NULL, szTemp, 4096) > 0)
+ {
+ _tcscat_s(szTemp, _T(".local"));
+ if (GetFileAttributes(szTemp) == INVALID_FILE_ATTRIBUTES)
+ {
+ // ".local" file does not exist, so we can try to load the dbghelp.dll from the "Debugging Tools for Windows"
+ // Ok, first try the new path according to the archtitecture:
+#ifdef _M_IX86
+ if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) )
+ {
+ _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x86)\\dbghelp.dll"));
+ // now check if the file exists:
+ if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
+ {
+ m_hDbhHelp = LoadLibrary(szTemp);
+ }
+ }
+#elif _M_X64
+ if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) )
+ {
+ _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x64)\\dbghelp.dll"));
+ // now check if the file exists:
+ if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
+ {
+ m_hDbhHelp = LoadLibrary(szTemp);
+ }
+ }
+#elif _M_IA64
+ if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) )
+ {
+ _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (ia64)\\dbghelp.dll"));
+ // now check if the file exists:
+ if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
+ {
+ m_hDbhHelp = LoadLibrary(szTemp);
+ }
+ }
+#endif
+ // If still not found, try the old directories...
+ if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) )
+ {
+ _tcscat_s(szTemp, _T("\\Debugging Tools for Windows\\dbghelp.dll"));
+ // now check if the file exists:
+ if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
+ {
+ m_hDbhHelp = LoadLibrary(szTemp);
+ }
+ }
+#if defined _M_X64 || defined _M_IA64
+ // Still not found? Then try to load the (old) 64-Bit version:
+ if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) )
+ {
+ _tcscat_s(szTemp, _T("\\Debugging Tools for Windows 64-Bit\\dbghelp.dll"));
+ if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
+ {
+ m_hDbhHelp = LoadLibrary(szTemp);
+ }
+ }
+#endif
+ }
+ }
+ if (m_hDbhHelp == NULL) // if not already loaded, try to load a default-one
+ m_hDbhHelp = LoadLibrary( _T("dbghelp.dll") );
+ if (m_hDbhHelp == NULL)
+ return FALSE;
+ pSI = (tSI) GetProcAddress(m_hDbhHelp, "SymInitialize" );
+ pSC = (tSC) GetProcAddress(m_hDbhHelp, "SymCleanup" );
+
+ pSW = (tSW) GetProcAddress(m_hDbhHelp, "StackWalk64" );
+ pSGO = (tSGO) GetProcAddress(m_hDbhHelp, "SymGetOptions" );
+ pSSO = (tSSO) GetProcAddress(m_hDbhHelp, "SymSetOptions" );
+
+ pSFTA = (tSFTA) GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64" );
+ pSGLFA = (tSGLFA) GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64" );
+ pSGMB = (tSGMB) GetProcAddress(m_hDbhHelp, "SymGetModuleBase64" );
+ pSGMI = (tSGMI) GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64" );
+ pSGSFA = (tSGSFA) GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64" );
+ pUDSN = (tUDSN) GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName" );
+ pSLM = (tSLM) GetProcAddress(m_hDbhHelp, "SymLoadModule64" );
+ pSGSP =(tSGSP) GetProcAddress(m_hDbhHelp, "SymGetSearchPath" );
+
+ if ( pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL ||
+ pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL ||
+ pSW == NULL || pUDSN == NULL || pSLM == NULL )
+ {
+ FreeLibrary(m_hDbhHelp);
+ m_hDbhHelp = NULL;
+ pSC = NULL;
+ return FALSE;
+ }
+
+ // SymInitialize
+ if (szSymPath != NULL)
+ m_szSymPath = _strdup(szSymPath);
+ if (this->pSI(m_hProcess, m_szSymPath, FALSE) == FALSE)
+ this->m_parent->OnDbgHelpErr("SymInitialize", GetLastError(), 0);
+
+ DWORD symOptions = this->pSGO(); // SymGetOptions
+ symOptions |= SYMOPT_LOAD_LINES;
+ symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS;
+ //symOptions |= SYMOPT_NO_PROMPTS;
+ // SymSetOptions
+ symOptions = this->pSSO(symOptions);
+
+ char buf[StackWalker::STACKWALK_MAX_NAMELEN] = {0};
+ if (this->pSGSP != NULL)
+ {
+ if (this->pSGSP(m_hProcess, buf, StackWalker::STACKWALK_MAX_NAMELEN) == FALSE)
+ this->m_parent->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0);
+ }
+ char szUserName[1024] = {0};
+ DWORD dwSize = 1024;
+ GetUserNameA(szUserName, &dwSize);
+ this->m_parent->OnSymInit(buf, symOptions, szUserName);
+
+ return TRUE;
+ }
+
+ StackWalker *m_parent;
+
+ HMODULE m_hDbhHelp;
+ HANDLE m_hProcess;
+ LPSTR m_szSymPath;
+
+#pragma pack(push,8)
+typedef struct IMAGEHLP_MODULE64_V3 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
+ DWORD64 BaseOfImage; // base load address of module
+ DWORD ImageSize; // virtual size of the loaded module
+ DWORD TimeDateStamp; // date/time stamp from pe header
+ DWORD CheckSum; // checksum from the pe header
+ DWORD NumSyms; // number of symbols in the symbol table
+ SYM_TYPE SymType; // type of symbols loaded
+ CHAR ModuleName[32]; // module name
+ CHAR ImageName[256]; // image name
+ CHAR LoadedImageName[256]; // symbol file name
+ // new elements: 07-Jun-2002
+ CHAR LoadedPdbName[256]; // pdb file name
+ DWORD CVSig; // Signature of the CV record in the debug directories
+ CHAR CVData[MAX_PATH * 3]; // Contents of the CV record
+ DWORD PdbSig; // Signature of PDB
+ GUID PdbSig70; // Signature of PDB (VC 7 and up)
+ DWORD PdbAge; // DBI age of pdb
+ BOOL PdbUnmatched; // loaded an unmatched pdb
+ BOOL DbgUnmatched; // loaded an unmatched dbg
+ BOOL LineNumbers; // we have line number information
+ BOOL GlobalSymbols; // we have internal symbol information
+ BOOL TypeInfo; // we have type information
+ // new elements: 17-Dec-2003
+ BOOL SourceIndexed; // pdb supports source server
+ BOOL Publics; // contains public symbols
+};
+
+typedef struct IMAGEHLP_MODULE64_V2 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
+ DWORD64 BaseOfImage; // base load address of module
+ DWORD ImageSize; // virtual size of the loaded module
+ DWORD TimeDateStamp; // date/time stamp from pe header
+ DWORD CheckSum; // checksum from the pe header
+ DWORD NumSyms; // number of symbols in the symbol table
+ SYM_TYPE SymType; // type of symbols loaded
+ CHAR ModuleName[32]; // module name
+ CHAR ImageName[256]; // image name
+ CHAR LoadedImageName[256]; // symbol file name
+};
+#pragma pack(pop)
+
+
+ // SymCleanup()
+ typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess );
+ tSC pSC;
+
+ // SymFunctionTableAccess64()
+ typedef PVOID (__stdcall *tSFTA)( HANDLE hProcess, DWORD64 AddrBase );
+ tSFTA pSFTA;
+
+ // SymGetLineFromAddr64()
+ typedef BOOL (__stdcall *tSGLFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,
+ OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line );
+ tSGLFA pSGLFA;
+
+ // SymGetModuleBase64()
+ typedef DWORD64 (__stdcall *tSGMB)( IN HANDLE hProcess, IN DWORD64 dwAddr );
+ tSGMB pSGMB;
+
+ // SymGetModuleInfo64()
+ typedef BOOL (__stdcall *tSGMI)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V3 *ModuleInfo );
+ tSGMI pSGMI;
+
+ // SymGetOptions()
+ typedef DWORD (__stdcall *tSGO)( VOID );
+ tSGO pSGO;
+
+ // SymGetSymFromAddr64()
+ typedef BOOL (__stdcall *tSGSFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,
+ OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol );
+ tSGSFA pSGSFA;
+
+ // SymInitialize()
+ typedef BOOL (__stdcall *tSI)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess );
+ tSI pSI;
+
+ // SymLoadModule64()
+ typedef DWORD64 (__stdcall *tSLM)( IN HANDLE hProcess, IN HANDLE hFile,
+ IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll );
+ tSLM pSLM;
+
+ // SymSetOptions()
+ typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions );
+ tSSO pSSO;
+
+ // StackWalk64()
+ typedef BOOL (__stdcall *tSW)(
+ DWORD MachineType,
+ HANDLE hProcess,
+ HANDLE hThread,
+ LPSTACKFRAME64 StackFrame,
+ PVOID ContextRecord,
+ PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
+ PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
+ PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
+ PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress );
+ tSW pSW;
+
+ // UnDecorateSymbolName()
+ typedef DWORD (__stdcall WINAPI *tUDSN)( PCSTR DecoratedName, PSTR UnDecoratedName,
+ DWORD UndecoratedLength, DWORD Flags );
+ tUDSN pUDSN;
+
+ typedef BOOL (__stdcall WINAPI *tSGSP)(HANDLE hProcess, PSTR SearchPath, DWORD SearchPathLength);
+ tSGSP pSGSP;
+
+
+private:
+ // **************************************** ToolHelp32 ************************
+ #define MAX_MODULE_NAME32 255
+ #define TH32CS_SNAPMODULE 0x00000008
+ #pragma pack( push, 8 )
+ typedef struct tagMODULEENTRY32
+ {
+ DWORD dwSize;
+ DWORD th32ModuleID; // This module
+ DWORD th32ProcessID; // owning process
+ DWORD GlblcntUsage; // Global usage count on the module
+ DWORD ProccntUsage; // Module usage count in th32ProcessID's context
+ BYTE * modBaseAddr; // Base address of module in th32ProcessID's context
+ DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr
+ HMODULE hModule; // The hModule of this module in th32ProcessID's context
+ char szModule[MAX_MODULE_NAME32 + 1];
+ char szExePath[MAX_PATH];
+ } MODULEENTRY32;
+ typedef MODULEENTRY32 * PMODULEENTRY32;
+ typedef MODULEENTRY32 * LPMODULEENTRY32;
+ #pragma pack( pop )
+
+ BOOL GetModuleListTH32(HANDLE hProcess, DWORD pid)
+ {
+ // CreateToolhelp32Snapshot()
+ typedef HANDLE (__stdcall *tCT32S)(DWORD dwFlags, DWORD th32ProcessID);
+ // Module32First()
+ typedef BOOL (__stdcall *tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
+ // Module32Next()
+ typedef BOOL (__stdcall *tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
+
+ // try both dlls...
+ const TCHAR *dllname[] = { _T("kernel32.dll"), _T("tlhelp32.dll") };
+ HINSTANCE hToolhelp = NULL;
+ tCT32S pCT32S = NULL;
+ tM32F pM32F = NULL;
+ tM32N pM32N = NULL;
+
+ HANDLE hSnap;
+ MODULEENTRY32 me;
+ me.dwSize = sizeof(me);
+ BOOL keepGoing;
+ size_t i;
+
+ for (i = 0; i<(sizeof(dllname) / sizeof(dllname[0])); i++ )
+ {
+ hToolhelp = LoadLibrary( dllname[i] );
+ if (hToolhelp == NULL)
+ continue;
+ pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
+ pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First");
+ pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next");
+ if ( (pCT32S != NULL) && (pM32F != NULL) && (pM32N != NULL) )
+ break; // found the functions!
+ FreeLibrary(hToolhelp);
+ hToolhelp = NULL;
+ }
+
+ if (hToolhelp == NULL)
+ return FALSE;
+
+ hSnap = pCT32S( TH32CS_SNAPMODULE, pid );
+ if (hSnap == (HANDLE) -1)
+ {
+ FreeLibrary(hToolhelp);
+ return FALSE;
+ }
+
+ keepGoing = !!pM32F( hSnap, &me );
+ int cnt = 0;
+ while (keepGoing)
+ {
+ this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64) me.modBaseAddr, me.modBaseSize);
+ cnt++;
+ keepGoing = !!pM32N( hSnap, &me );
+ }
+ CloseHandle(hSnap);
+ FreeLibrary(hToolhelp);
+ if (cnt <= 0)
+ return FALSE;
+ return TRUE;
+ } // GetModuleListTH32
+
+ // **************************************** PSAPI ************************
+ typedef struct _MODULEINFO {
+ LPVOID lpBaseOfDll;
+ DWORD SizeOfImage;
+ LPVOID EntryPoint;
+ } MODULEINFO, *LPMODULEINFO;
+
+ BOOL GetModuleListPSAPI(HANDLE hProcess)
+ {
+ // EnumProcessModules()
+ typedef BOOL (__stdcall *tEPM)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded );
+ // GetModuleFileNameEx()
+ typedef DWORD (__stdcall *tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
+ // GetModuleBaseName()
+ typedef DWORD (__stdcall *tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
+ // GetModuleInformation()
+ typedef BOOL (__stdcall *tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize );
+
+ HINSTANCE hPsapi;
+ tEPM pEPM;
+ tGMFNE pGMFNE;
+ tGMBN pGMBN;
+ tGMI pGMI;
+
+ DWORD i;
+ //ModuleEntry e;
+ DWORD cbNeeded;
+ MODULEINFO mi;
+ HMODULE *hMods = 0;
+ char *tt = NULL;
+ char *tt2 = NULL;
+ const SIZE_T TTBUFLEN = 8096;
+ int cnt = 0;
+
+ hPsapi = LoadLibrary( _T("psapi.dll") );
+ if (hPsapi == NULL)
+ return FALSE;
+
+ pEPM = (tEPM) GetProcAddress( hPsapi, "EnumProcessModules" );
+ pGMFNE = (tGMFNE) GetProcAddress( hPsapi, "GetModuleFileNameExA" );
+ pGMBN = (tGMFNE) GetProcAddress( hPsapi, "GetModuleBaseNameA" );
+ pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" );
+ if ( (pEPM == NULL) || (pGMFNE == NULL) || (pGMBN == NULL) || (pGMI == NULL) )
+ {
+ // we couldn´t find all functions
+ FreeLibrary(hPsapi);
+ return FALSE;
+ }
+
+ hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof(HMODULE)));
+ tt = (char*) malloc(sizeof(char) * TTBUFLEN);
+ tt2 = (char*) malloc(sizeof(char) * TTBUFLEN);
+ if ( (hMods == NULL) || (tt == NULL) || (tt2 == NULL) )
+ goto cleanup;
+
+ if ( ! pEPM( hProcess, hMods, TTBUFLEN, &cbNeeded ) )
+ {
+ //_ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle );
+ goto cleanup;
+ }
+
+ if ( cbNeeded > TTBUFLEN )
+ {
+ //_ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) );
+ goto cleanup;
+ }
+
+ for ( i = 0; i < cbNeeded / sizeof(hMods[0]); i++ )
+ {
+ // base address, size
+ pGMI(hProcess, hMods[i], &mi, sizeof(mi));
+ // image file name
+ tt[0] = 0;
+ pGMFNE(hProcess, hMods[i], tt, TTBUFLEN );
+ // module name
+ tt2[0] = 0;
+ pGMBN(hProcess, hMods[i], tt2, TTBUFLEN );
+
+ DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64) mi.lpBaseOfDll, mi.SizeOfImage);
+ if (dwRes != ERROR_SUCCESS)
+ this->m_parent->OnDbgHelpErr("LoadModule", dwRes, 0);
+ cnt++;
+ }
+
+ cleanup:
+ if (hPsapi != NULL) FreeLibrary(hPsapi);
+ if (tt2 != NULL) free(tt2);
+ if (tt != NULL) free(tt);
+ if (hMods != NULL) free(hMods);
+
+ return cnt != 0;
+ } // GetModuleListPSAPI
+
+ DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size)
+ {
+ CHAR *szImg = _strdup(img);
+ CHAR *szMod = _strdup(mod);
+ DWORD result = ERROR_SUCCESS;
+ if ( (szImg == NULL) || (szMod == NULL) )
+ result = ERROR_NOT_ENOUGH_MEMORY;
+ else
+ {
+ if (pSLM(hProcess, 0, szImg, szMod, baseAddr, size) == 0)
+ result = GetLastError();
+ }
+ ULONGLONG fileVersion = 0;
+ if ( (m_parent != NULL) && (szImg != NULL) )
+ {
+ // try to retrive the file-version:
+ if ( (this->m_parent->m_options & StackWalker::RetrieveFileVersion) != 0)
+ {
+ VS_FIXEDFILEINFO *fInfo = NULL;
+ DWORD dwHandle;
+ DWORD dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle);
+ if (dwSize > 0)
+ {
+ LPVOID vData = malloc(dwSize);
+ if (vData != NULL)
+ {
+ if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0)
+ {
+ UINT len;
+ TCHAR szSubBlock[] = _T("\\");
+ if (VerQueryValue(vData, szSubBlock, (LPVOID*) &fInfo, &len) == 0)
+ fInfo = NULL;
+ else
+ {
+ fileVersion = ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32);
+ }
+ }
+ free(vData);
+ }
+ }
+ }
+
+ // Retrive some additional-infos about the module
+ IMAGEHLP_MODULE64_V3 Module;
+ const char *szSymType = "-unknown-";
+ if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE)
+ {
+ switch(Module.SymType)
+ {
+ case SymNone:
+ szSymType = "-nosymbols-";
+ break;
+ case SymCoff: // 1
+ szSymType = "COFF";
+ break;
+ case SymCv: // 2
+ szSymType = "CV";
+ break;
+ case SymPdb: // 3
+ szSymType = "PDB";
+ break;
+ case SymExport: // 4
+ szSymType = "-exported-";
+ break;
+ case SymDeferred: // 5
+ szSymType = "-deferred-";
+ break;
+ case SymSym: // 6
+ szSymType = "SYM";
+ break;
+ case 7: // SymDia:
+ szSymType = "DIA";
+ break;
+ case 8: //SymVirtual:
+ szSymType = "Virtual";
+ break;
+ }
+ }
+ LPCSTR pdbName = Module.LoadedImageName;
+ if (Module.LoadedPdbName[0] != 0)
+ pdbName = Module.LoadedPdbName;
+ this->m_parent->OnLoadModule(img, mod, baseAddr, size, result, szSymType, pdbName, fileVersion);
+ }
+ if (szImg != NULL) free(szImg);
+ if (szMod != NULL) free(szMod);
+ return result;
+ }
+public:
+ BOOL LoadModules(HANDLE hProcess, DWORD dwProcessId)
+ {
+ // first try toolhelp32
+ if (GetModuleListTH32(hProcess, dwProcessId))
+ return true;
+ // then try psapi
+ return GetModuleListPSAPI(hProcess);
+ }
+
+
+ BOOL GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64_V3 *pModuleInfo)
+ {
+ memset(pModuleInfo, 0, sizeof(IMAGEHLP_MODULE64_V3));
+ if(this->pSGMI == NULL)
+ {
+ SetLastError(ERROR_DLL_INIT_FAILED);
+ return FALSE;
+ }
+ // First try to use the larger ModuleInfo-Structure
+ pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3);
+ void *pData = malloc(4096); // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites...
+ if (pData == NULL)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+ memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V3));
+ static bool s_useV3Version = true;
+ if (s_useV3Version)
+ {
+ if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*) pData) != FALSE)
+ {
+ // only copy as much memory as is reserved...
+ memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V3));
+ pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3);
+ free(pData);
+ return TRUE;
+ }
+ s_useV3Version = false; // to prevent unneccessarry calls with the larger struct...
+ }
+
+ // could not retrive the bigger structure, try with the smaller one (as defined in VC7.1)...
+ pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
+ memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V2));
+ if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*) pData) != FALSE)
+ {
+ // only copy as much memory as is reserved...
+ memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V2));
+ pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
+ free(pData);
+ return TRUE;
+ }
+ free(pData);
+ SetLastError(ERROR_DLL_INIT_FAILED);
+ return FALSE;
+ }
+};
+
+// #############################################################
+StackWalker::StackWalker(DWORD dwProcessId, HANDLE hProcess)
+{
+ this->m_verbose = true;
+ this->m_options = OptionsAll;
+ this->m_modulesLoaded = FALSE;
+ this->m_hProcess = hProcess;
+ this->m_sw = new StackWalkerInternal(this, this->m_hProcess);
+ this->m_dwProcessId = dwProcessId;
+ this->m_szSymPath = NULL;
+ this->m_MaxRecursionCount = 1000;
+}
+StackWalker::StackWalker(bool verbose, int options, LPCSTR szSymPath, DWORD dwProcessId, HANDLE hProcess)
+{
+ this->m_verbose = verbose;
+ this->m_options = options;
+ this->m_modulesLoaded = FALSE;
+ this->m_hProcess = hProcess;
+ this->m_sw = new StackWalkerInternal(this, this->m_hProcess);
+ this->m_dwProcessId = dwProcessId;
+ if (szSymPath != NULL)
+ {
+ this->m_szSymPath = _strdup(szSymPath);
+ this->m_options |= SymBuildPath;
+ }
+ else
+ this->m_szSymPath = NULL;
+ this->m_MaxRecursionCount = 1000;
+}
+
+StackWalker::~StackWalker()
+{
+ if (m_szSymPath != NULL)
+ free(m_szSymPath);
+ m_szSymPath = NULL;
+ if (this->m_sw != NULL)
+ delete this->m_sw;
+ this->m_sw = NULL;
+}
+
+BOOL StackWalker::LoadModules()
+{
+ if (this->m_sw == NULL)
+ {
+ SetLastError(ERROR_DLL_INIT_FAILED);
+ return FALSE;
+ }
+ if (m_modulesLoaded != FALSE)
+ return TRUE;
+
+ // Build the sym-path:
+ char *szSymPath = NULL;
+ if ( (this->m_options & SymBuildPath) != 0)
+ {
+ const size_t nSymPathLen = 4096;
+ szSymPath = (char*) malloc(nSymPathLen);
+ if (szSymPath == NULL)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+ szSymPath[0] = 0;
+ // Now first add the (optional) provided sympath:
+ if (this->m_szSymPath != NULL)
+ {
+ strcat_s(szSymPath, nSymPathLen, this->m_szSymPath);
+ strcat_s(szSymPath, nSymPathLen, ";");
+ }
+
+ strcat_s(szSymPath, nSymPathLen, ".;");
+
+ const size_t nTempLen = 1024;
+ char szTemp[nTempLen];
+ // Now add the current directory:
+ if (GetCurrentDirectoryA(nTempLen, szTemp) > 0)
+ {
+ szTemp[nTempLen-1] = 0;
+ strcat_s(szSymPath, nSymPathLen, szTemp);
+ strcat_s(szSymPath, nSymPathLen, ";");
+ }
+
+ // Now add the path for the main-module:
+ if (GetModuleFileNameA(NULL, szTemp, nTempLen) > 0)
+ {
+ szTemp[nTempLen-1] = 0;
+ for (char *p = (szTemp+strlen(szTemp)-1); p >= szTemp; --p)
+ {
+ // locate the rightmost path separator
+ if ( (*p == '\\') || (*p == '/') || (*p == ':') )
+ {
+ *p = 0;
+ break;
+ }
+ } // for (search for path separator...)
+ if (strlen(szTemp) > 0)
+ {
+ strcat_s(szSymPath, nSymPathLen, szTemp);
+ strcat_s(szSymPath, nSymPathLen, ";");
+ }
+ }
+ if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0)
+ {
+ szTemp[nTempLen-1] = 0;
+ strcat_s(szSymPath, nSymPathLen, szTemp);
+ strcat_s(szSymPath, nSymPathLen, ";");
+ }
+ if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0)
+ {
+ szTemp[nTempLen-1] = 0;
+ strcat_s(szSymPath, nSymPathLen, szTemp);
+ strcat_s(szSymPath, nSymPathLen, ";");
+ }
+ if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0)
+ {
+ szTemp[nTempLen-1] = 0;
+ strcat_s(szSymPath, nSymPathLen, szTemp);
+ strcat_s(szSymPath, nSymPathLen, ";");
+ // also add the "system32"-directory:
+ strcat_s(szTemp, nTempLen, "\\system32");
+ strcat_s(szSymPath, nSymPathLen, szTemp);
+ strcat_s(szSymPath, nSymPathLen, ";");
+ }
+
+ if ( (this->m_options & SymUseSymSrv) != 0)
+ {
+ if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0)
+ {
+ szTemp[nTempLen-1] = 0;
+ strcat_s(szSymPath, nSymPathLen, "SRV*");
+ strcat_s(szSymPath, nSymPathLen, szTemp);
+ strcat_s(szSymPath, nSymPathLen, "\\websymbols");
+ strcat_s(szSymPath, nSymPathLen, "*http://msdl.microsoft.com/download/symbols;");
+ }
+ else
+ strcat_s(szSymPath, nSymPathLen, "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;");
+ }
+ } // if SymBuildPath
+
+ // First Init the whole stuff...
+ BOOL bRet = this->m_sw->Init(szSymPath);
+ if (szSymPath != NULL) free(szSymPath); szSymPath = NULL;
+ if (bRet == FALSE)
+ {
+ this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0);
+ SetLastError(ERROR_DLL_INIT_FAILED);
+ return FALSE;
+ }
+
+ bRet = this->m_sw->LoadModules(this->m_hProcess, this->m_dwProcessId);
+ if (bRet != FALSE)
+ m_modulesLoaded = TRUE;
+ return bRet;
+}
+
+
+// The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction
+// This has to be done due to a problem with the "hProcess"-parameter in x64...
+// Because this class is in no case multi-threading-enabled (because of the limitations
+// of dbghelp.dll) it is "safe" to use a static-variable
+static StackWalker::PReadProcessMemoryRoutine s_readMemoryFunction = NULL;
+static LPVOID s_readMemoryFunction_UserData = NULL;
+
+BOOL StackWalker::ShowCallstack(bool verbose, HANDLE hThread, const CONTEXT *context, PReadProcessMemoryRoutine readMemoryFunction, LPVOID pUserData)
+{
+ m_verbose = verbose;
+ CONTEXT c;
+ CallstackEntry csEntry;
+ IMAGEHLP_SYMBOL64 *pSym = NULL;
+ StackWalkerInternal::IMAGEHLP_MODULE64_V3 Module;
+ IMAGEHLP_LINE64 Line;
+ int frameNum;
+ bool bLastEntryCalled = true;
+ int curRecursionCount = 0;
+
+ if (m_modulesLoaded == FALSE)
+ this->LoadModules(); // ignore the result...
+
+ if (this->m_sw->m_hDbhHelp == NULL)
+ {
+ SetLastError(ERROR_DLL_INIT_FAILED);
+ return FALSE;
+ }
+
+ s_readMemoryFunction = readMemoryFunction;
+ s_readMemoryFunction_UserData = pUserData;
+
+ if (context == NULL)
+ {
+ // If no context is provided, capture the context
+ // See: https://stackwalker.codeplex.com/discussions/446958
+#if _WIN32_WINNT <= 0x0501
+ // If we need to support XP, we need to use the "old way", because "GetThreadId" is not available!
+ if (hThread == GetCurrentThread())
+#else
+ if (GetThreadId(hThread) == GetCurrentThreadId())
+#endif
+ {
+ GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, USED_CONTEXT_FLAGS);
+ }
+ else
+ {
+ SuspendThread(hThread);
+ memset(&c, 0, sizeof(CONTEXT));
+ c.ContextFlags = USED_CONTEXT_FLAGS;
+ if (GetThreadContext(hThread, &c) == FALSE)
+ {
+ ResumeThread(hThread);
+ return FALSE;
+ }
+ }
+ }
+ else
+ c = *context;
+
+ // init STACKFRAME for first call
+ STACKFRAME64 s; // in/out stackframe
+ memset(&s, 0, sizeof(s));
+ DWORD imageType;
+#ifdef _M_IX86
+ // normally, call ImageNtHeader() and use machine info from PE header
+ imageType = IMAGE_FILE_MACHINE_I386;
+ s.AddrPC.Offset = c.Eip;
+ s.AddrPC.Mode = AddrModeFlat;
+ s.AddrFrame.Offset = c.Ebp;
+ s.AddrFrame.Mode = AddrModeFlat;
+ s.AddrStack.Offset = c.Esp;
+ s.AddrStack.Mode = AddrModeFlat;
+#elif _M_X64
+ imageType = IMAGE_FILE_MACHINE_AMD64;
+ s.AddrPC.Offset = c.Rip;
+ s.AddrPC.Mode = AddrModeFlat;
+ s.AddrFrame.Offset = c.Rsp;
+ s.AddrFrame.Mode = AddrModeFlat;
+ s.AddrStack.Offset = c.Rsp;
+ s.AddrStack.Mode = AddrModeFlat;
+#elif _M_IA64
+ imageType = IMAGE_FILE_MACHINE_IA64;
+ s.AddrPC.Offset = c.StIIP;
+ s.AddrPC.Mode = AddrModeFlat;
+ s.AddrFrame.Offset = c.IntSp;
+ s.AddrFrame.Mode = AddrModeFlat;
+ s.AddrBStore.Offset = c.RsBSP;
+ s.AddrBStore.Mode = AddrModeFlat;
+ s.AddrStack.Offset = c.IntSp;
+ s.AddrStack.Mode = AddrModeFlat;
+#else
+#error "Platform not supported!"
+#endif
+
+ pSym = (IMAGEHLP_SYMBOL64 *) malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
+ if (!pSym) goto cleanup; // not enough memory...
+ memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
+ pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
+ pSym->MaxNameLength = STACKWALK_MAX_NAMELEN;
+
+ memset(&Line, 0, sizeof(Line));
+ Line.SizeOfStruct = sizeof(Line);
+
+ memset(&Module, 0, sizeof(Module));
+ Module.SizeOfStruct = sizeof(Module);
+
+ for (frameNum = 0; ; ++frameNum )
+ {
+ // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64())
+ // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
+ // assume that either you are done, or that the stack is so hosed that the next
+ // deeper frame could not be found.
+ // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386!
+ if ( ! this->m_sw->pSW(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem, this->m_sw->pSFTA, this->m_sw->pSGMB, NULL) )
+ {
+ // INFO: "StackWalk64" does not set "GetLastError"...
+ this->OnDbgHelpErr("StackWalk64", 0, s.AddrPC.Offset);
+ break;
+ }
+
+ csEntry.offset = s.AddrPC.Offset;
+ csEntry.name[0] = 0;
+ csEntry.undName[0] = 0;
+ csEntry.undFullName[0] = 0;
+ csEntry.offsetFromSmybol = 0;
+ csEntry.offsetFromLine = 0;
+ csEntry.lineFileName[0] = 0;
+ csEntry.lineNumber = 0;
+ csEntry.loadedImageName[0] = 0;
+ csEntry.moduleName[0] = 0;
+ if (s.AddrPC.Offset == s.AddrReturn.Offset)
+ {
+ if ( (this->m_MaxRecursionCount > 0) && (curRecursionCount > m_MaxRecursionCount) )
+ {
+ this->OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset);
+ break;
+ }
+ curRecursionCount++;
+ }
+ else
+ curRecursionCount = 0;
+ if (s.AddrPC.Offset != 0)
+ {
+ // we seem to have a valid PC
+ // show procedure info (SymGetSymFromAddr64())
+ if (this->m_sw->pSGSFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol), pSym) != FALSE)
+ {
+ MyStrCpy(csEntry.name, STACKWALK_MAX_NAMELEN, pSym->Name);
+ // UnDecorateSymbolName()
+ this->m_sw->pUDSN( pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY );
+ this->m_sw->pUDSN( pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE );
+ }
+ else
+ {
+ this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset);
+ }
+
+ // show line number info, NT5.0-method (SymGetLineFromAddr64())
+ if (this->m_sw->pSGLFA != NULL )
+ { // yes, we have SymGetLineFromAddr64()
+ if (this->m_sw->pSGLFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine), &Line) != FALSE)
+ {
+ csEntry.lineNumber = Line.LineNumber;
+ MyStrCpy(csEntry.lineFileName, STACKWALK_MAX_NAMELEN, Line.FileName);
+ }
+ else
+ {
+ this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset);
+ }
+ } // yes, we have SymGetLineFromAddr64()
+
+ if (m_verbose) // getting module info is very slow, skip unless we're being verbose.
+ {
+ // show module info (SymGetModuleInfo64())
+ if (this->m_sw->GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module ) != FALSE)
+ { // got module info OK
+ switch ( Module.SymType )
+ {
+ case SymNone:
+ csEntry.symTypeString = "-nosymbols-";
+ break;
+ case SymCoff:
+ csEntry.symTypeString = "COFF";
+ break;
+ case SymCv:
+ csEntry.symTypeString = "CV";
+ break;
+ case SymPdb:
+ csEntry.symTypeString = "PDB";
+ break;
+ case SymExport:
+ csEntry.symTypeString = "-exported-";
+ break;
+ case SymDeferred:
+ csEntry.symTypeString = "-deferred-";
+ break;
+ case SymSym:
+ csEntry.symTypeString = "SYM";
+ break;
+#if API_VERSION_NUMBER >= 9
+ case SymDia:
+ csEntry.symTypeString = "DIA";
+ break;
+#endif
+ case 8: //SymVirtual:
+ csEntry.symTypeString = "Virtual";
+ break;
+ default:
+ //_snprintf( ty, sizeof(ty), "symtype=%ld", (long) Module.SymType );
+ csEntry.symTypeString = NULL;
+ break;
+ }
+
+ MyStrCpy(csEntry.moduleName, STACKWALK_MAX_NAMELEN, Module.ModuleName);
+ csEntry.baseOfImage = Module.BaseOfImage;
+ MyStrCpy(csEntry.loadedImageName, STACKWALK_MAX_NAMELEN, Module.LoadedImageName);
+ } // got module info OK
+ else
+ {
+ this->OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset);
+ }
+ }
+ else
+ {
+ MyStrCpy(csEntry.moduleName, STACKWALK_MAX_NAMELEN, "MODULE?");
+ }
+ } // we seem to have a valid PC
+
+ CallstackEntryType et = nextEntry;
+ if (frameNum == 0)
+ et = firstEntry;
+ bLastEntryCalled = false;
+ this->OnCallstackEntry(et, csEntry);
+
+ if (s.AddrReturn.Offset == 0)
+ {
+ bLastEntryCalled = true;
+ this->OnCallstackEntry(lastEntry, csEntry);
+ SetLastError(ERROR_SUCCESS);
+ break;
+ }
+ } // for ( frameNum )
+
+ cleanup:
+ if (pSym) free( pSym );
+
+ if (bLastEntryCalled == false)
+ this->OnCallstackEntry(lastEntry, csEntry);
+
+ if (context == NULL)
+ ResumeThread(hThread);
+
+ return TRUE;
+}
+
+BOOL __stdcall StackWalker::myReadProcMem(
+ HANDLE hProcess,
+ DWORD64 qwBaseAddress,
+ PVOID lpBuffer,
+ DWORD nSize,
+ LPDWORD lpNumberOfBytesRead
+ )
+{
+ if (s_readMemoryFunction == NULL)
+ {
+ SIZE_T st;
+ BOOL bRet = ReadProcessMemory(hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, &st);
+ *lpNumberOfBytesRead = (DWORD) st;
+ //printf("ReadMemory: hProcess: %p, baseAddr: %p, buffer: %p, size: %d, read: %d, result: %d\n", hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, (DWORD) st, (DWORD) bRet);
+ return bRet;
+ }
+ else
+ {
+ return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_readMemoryFunction_UserData);
+ }
+}
+
+void StackWalker::OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion)
+{
+ CHAR buffer[STACKWALK_MAX_NAMELEN];
+ if (fileVersion == 0)
+ _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName);
+ else
+ {
+ DWORD v4 = (DWORD) (fileVersion & 0xFFFF);
+ DWORD v3 = (DWORD) ((fileVersion>>16) & 0xFFFF);
+ DWORD v2 = (DWORD) ((fileVersion>>32) & 0xFFFF);
+ DWORD v1 = (DWORD) ((fileVersion>>48) & 0xFFFF);
+ _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName, v1, v2, v3, v4);
+ }
+ if (m_verbose)
+ {
+ OnOutput(buffer);
+ }
+}
+
+void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry)
+{
+ CHAR buffer[STACKWALK_MAX_NAMELEN];
+ if ( (eType != lastEntry) && (entry.offset != 0) )
+ {
+ if (entry.name[0] == 0)
+ MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, "(function-name not available)");
+ if (entry.undName[0] != 0)
+ MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undName);
+ if (entry.undFullName[0] != 0)
+ MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undFullName);
+ if (entry.lineFileName[0] == 0)
+ {
+ MyStrCpy(entry.lineFileName, STACKWALK_MAX_NAMELEN, "(filename not available)");
+ if (entry.moduleName[0] == 0)
+ MyStrCpy(entry.moduleName, STACKWALK_MAX_NAMELEN, "(module-name not available)");
+ _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%p (%s): %s: %s\n", (LPVOID) entry.offset, entry.moduleName, entry.lineFileName, entry.name);
+ }
+ else
+ _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s (%d): %s\n", entry.lineFileName, entry.lineNumber, entry.name);
+ buffer[STACKWALK_MAX_NAMELEN-1] = 0;
+ OnOutput(buffer);
+ }
+}
+
+void StackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr)
+{
+ CHAR buffer[STACKWALK_MAX_NAMELEN];
+ _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle, (LPVOID) addr);
+ if (m_verbose)
+ {
+ OnOutput(buffer);
+ }
+}
+
+#pragma warning (disable : 4996)
+
+void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName)
+{
+ CHAR buffer[STACKWALK_MAX_NAMELEN];
+ _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n", szSearchPath, symOptions, szUserName);
+ if (m_verbose)
+ {
+ OnOutput(buffer);
+ }
+ // Also display the OS-version
+#if _MSC_VER <= 1200
+ OSVERSIONINFOA ver;
+ ZeroMemory(&ver, sizeof(OSVERSIONINFOA));
+ ver.dwOSVersionInfoSize = sizeof(ver);
+ if (GetVersionExA(&ver) != FALSE)
+ {
+ _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s)\n",
+ ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber,
+ ver.szCSDVersion);
+ if (m_verbose)
+ {
+ OnOutput(buffer);
+ }
+ }
+#else
+ OSVERSIONINFOEXA ver;
+ ZeroMemory(&ver, sizeof(OSVERSIONINFOEXA));
+ ver.dwOSVersionInfoSize = sizeof(ver);
+ if (GetVersionExA( (OSVERSIONINFOA*) &ver) != FALSE)
+ {
+ _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n",
+ ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber,
+ ver.szCSDVersion, ver.wSuiteMask, ver.wProductType);
+ if (m_verbose)
+ {
+ OnOutput(buffer);
+ }
+ }
+#endif
+}
+
+void StackWalker::OnOutput(LPCSTR buffer)
+{
+ OutputDebugStringA(buffer);
+}
+
+#endif // LL_WINDOWS
diff --git a/indra/llcommon/StackWalker.h b/indra/llcommon/StackWalker.h
new file mode 100644
index 0000000000..834f89c471
--- /dev/null
+++ b/indra/llcommon/StackWalker.h
@@ -0,0 +1,226 @@
+/**********************************************************************
+ *
+ * StackWalker.h
+ *
+ *
+ *
+ * $LicenseInfo:firstyear=2016&license=bsd$
+ *
+ * Linden notes: Small modifications from the original source at https://stackwalker.codeplex.com/
+ *
+ * LICENSE (http://www.opensource.org/licenses/bsd-license.php)
+ *
+ * Copyright (c) 2005-2009, Jochen Kalmbach
+ * 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 Jochen Kalmbach 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.
+ *
+ * **********************************************************************/
+
+#if LL_WINDOWS
+
+// #pragma once is supported starting with _MCS_VER 1000,
+// so we need not to check the version (because we only support _MSC_VER >= 1100)!
+#pragma once
+
+#include
+
+// special defines for VC5/6 (if no actual PSDK is installed):
+#if _MSC_VER < 1300
+typedef unsigned __int64 DWORD64, *PDWORD64;
+#if defined(_WIN64)
+typedef unsigned __int64 SIZE_T, *PSIZE_T;
+#else
+typedef unsigned long SIZE_T, *PSIZE_T;
+#endif
+#endif // _MSC_VER < 1300
+
+class StackWalkerInternal; // forward
+class StackWalker
+{
+public:
+ typedef enum StackWalkOptions
+ {
+ // No addition info will be retrived
+ // (only the address is available)
+ RetrieveNone = 0,
+
+ // Try to get the symbol-name
+ RetrieveSymbol = 1,
+
+ // Try to get the line for this symbol
+ RetrieveLine = 2,
+
+ // Try to retrieve the module-infos
+ RetrieveModuleInfo = 4,
+
+ // Also retrieve the version for the DLL/EXE
+ RetrieveFileVersion = 8,
+
+ // Contains all the abouve
+ RetrieveVerbose = 0xF,
+
+ // Generate a "good" symbol-search-path
+ SymBuildPath = 0x10,
+
+ // Also use the public Microsoft-Symbol-Server
+ SymUseSymSrv = 0x20,
+
+ // Contains all the abouve "Sym"-options
+ SymAll = 0x30,
+
+ // Contains all options (default)
+ OptionsAll = 0x3F
+ } StackWalkOptions;
+
+ StackWalker(
+ bool verbose = true,
+ int options = OptionsAll, // 'int' is by design, to combine the enum-flags
+ LPCSTR szSymPath = NULL,
+ DWORD dwProcessId = GetCurrentProcessId(),
+ HANDLE hProcess = GetCurrentProcess()
+ );
+ StackWalker(DWORD dwProcessId, HANDLE hProcess);
+ virtual ~StackWalker();
+
+ typedef BOOL (__stdcall *PReadProcessMemoryRoutine)(
+ HANDLE hProcess,
+ DWORD64 qwBaseAddress,
+ PVOID lpBuffer,
+ DWORD nSize,
+ LPDWORD lpNumberOfBytesRead,
+ LPVOID pUserData // optional data, which was passed in "ShowCallstack"
+ );
+
+ BOOL LoadModules();
+
+ BOOL ShowCallstack(
+ bool verbose,
+ HANDLE hThread = GetCurrentThread(),
+ const CONTEXT *context = NULL,
+ PReadProcessMemoryRoutine readMemoryFunction = NULL,
+ LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback
+ );
+
+#if _MSC_VER >= 1300
+// due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public"
+// in older compilers in order to use it... starting with VC7 we can declare it as "protected"
+protected:
+#endif
+ enum { STACKWALK_MAX_NAMELEN = 4096 }; // max name length for found symbols
+
+protected:
+ // Entry for each Callstack-Entry
+ typedef struct CallstackEntry
+ {
+ DWORD64 offset; // if 0, we have no valid entry
+ CHAR name[STACKWALK_MAX_NAMELEN];
+ CHAR undName[STACKWALK_MAX_NAMELEN];
+ CHAR undFullName[STACKWALK_MAX_NAMELEN];
+ DWORD64 offsetFromSmybol;
+ DWORD offsetFromLine;
+ DWORD lineNumber;
+ CHAR lineFileName[STACKWALK_MAX_NAMELEN];
+ DWORD symType;
+ LPCSTR symTypeString;
+ CHAR moduleName[STACKWALK_MAX_NAMELEN];
+ DWORD64 baseOfImage;
+ CHAR loadedImageName[STACKWALK_MAX_NAMELEN];
+ } CallstackEntry;
+
+ typedef enum CallstackEntryType {firstEntry, nextEntry, lastEntry};
+
+ virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
+ virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion);
+ virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry);
+ virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
+ virtual void OnOutput(LPCSTR szText);
+
+ StackWalkerInternal *m_sw;
+ HANDLE m_hProcess;
+ DWORD m_dwProcessId;
+ BOOL m_modulesLoaded;
+ LPSTR m_szSymPath;
+
+ bool m_verbose;
+ int m_options;
+ int m_MaxRecursionCount;
+
+ static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead);
+
+ friend StackWalkerInternal;
+}; // class StackWalker
+
+
+// The "ugly" assembler-implementation is needed for systems before XP
+// If you have a new PSDK and you only compile for XP and later, then you can use
+// the "RtlCaptureContext"
+// Currently there is no define which determines the PSDK-Version...
+// So we just use the compiler-version (and assumes that the PSDK is
+// the one which was installed by the VS-IDE)
+
+// INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
+// But I currently use it in x64/IA64 environments...
+//#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)
+
+#if defined(_M_IX86)
+#ifdef CURRENT_THREAD_VIA_EXCEPTION
+// TODO: The following is not a "good" implementation,
+// because the callstack is only valid in the "__except" block...
+#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
+ do { \
+ memset(&c, 0, sizeof(CONTEXT)); \
+ EXCEPTION_POINTERS *pExp = NULL; \
+ __try { \
+ throw 0; \
+ } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \
+ if (pExp != NULL) \
+ memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \
+ c.ContextFlags = contextFlags; \
+ } while(0);
+#else
+// The following should be enough for walking the callstack...
+#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
+ do { \
+ memset(&c, 0, sizeof(CONTEXT)); \
+ c.ContextFlags = contextFlags; \
+ __asm call x \
+ __asm x: pop eax \
+ __asm mov c.Eip, eax \
+ __asm mov c.Ebp, ebp \
+ __asm mov c.Esp, esp \
+ } while(0);
+#endif
+
+#else
+
+// The following is defined for x86 (XP and higher), x64 and IA64:
+#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
+ do { \
+ memset(&c, 0, sizeof(CONTEXT)); \
+ c.ContextFlags = contextFlags; \
+ RtlCaptureContext(&c); \
+} while(0);
+#endif
+
+#endif // LL_WINDOWS
diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp
index a548c96002..86f407cdb0 100644
--- a/indra/llcommon/llapr.cpp
+++ b/indra/llcommon/llapr.cpp
@@ -294,9 +294,11 @@ void LLScopedLock::unlock()
bool ll_apr_warn_status(apr_status_t status)
{
if(APR_SUCCESS == status) return false;
+#if !LL_LINUX
char buf[MAX_STRING]; /* Flawfinder: ignore */
apr_strerror(status, buf, sizeof(buf));
LL_WARNS("APR") << "APR: " << buf << LL_ENDL;
+#endif
return true;
}
diff --git a/indra/llcommon/llcallstack.cpp b/indra/llcommon/llcallstack.cpp
new file mode 100644
index 0000000000..8db291eed1
--- /dev/null
+++ b/indra/llcommon/llcallstack.cpp
@@ -0,0 +1,190 @@
+/**
+ * @file llcallstack.cpp
+ * @brief run-time extraction of the current callstack
+ *
+ * $LicenseInfo:firstyear=2016&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2016, Linden Research, Inc.
+ *
+ * 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;
+ * version 2.1 of the License only.
+ *
+ * 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
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llcommon.h"
+#include "llcallstack.h"
+#include "StackWalker.h"
+#include "llthreadlocalstorage.h"
+
+#if LL_WINDOWS
+class LLCallStackImpl: public StackWalker
+{
+public:
+ LLCallStackImpl():
+ StackWalker(false,0) // non-verbose, options = 0
+ {
+ }
+ ~LLCallStackImpl()
+ {
+ }
+ void getStack(std::vector& stack, S32 skip_count=0, bool verbose=false)
+ {
+ m_stack.clear();
+ ShowCallstack(verbose);
+ // Skip the first few lines because they're just bookkeeping for LLCallStack,
+ // plus any additional lines requested to skip.
+ S32 first_line = skip_count + 3;
+ for (S32 i=first_line; i m_stack;
+};
+#else
+// Stub - not implemented currently on other platforms.
+class LLCallStackImpl
+{
+public:
+ LLCallStackImpl() {}
+ ~LLCallStackImpl() {}
+ void getStack(std::vector& stack, S32 skip_count=0, bool verbose=false)
+ {
+ stack.clear();
+ }
+};
+#endif
+
+LLCallStackImpl *LLCallStack::s_impl = NULL;
+
+LLCallStack::LLCallStack(S32 skip_count, bool verbose):
+ m_skipCount(skip_count),
+ m_verbose(verbose)
+{
+ if (!s_impl)
+ {
+ s_impl = new LLCallStackImpl;
+ }
+ LLTimer t;
+ s_impl->getStack(m_strings, m_skipCount, m_verbose);
+}
+
+bool LLCallStack::contains(const std::string& str)
+{
+ for (std::vector::const_iterator it = m_strings.begin();
+ it != m_strings.end(); ++it)
+ {
+ if (it->find(str) != std::string::npos)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+std::ostream& operator<<(std::ostream& s, const LLCallStack& call_stack)
+{
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ std::vector::const_iterator it;
+ for (it=call_stack.m_strings.begin(); it!=call_stack.m_strings.end(); ++it)
+ {
+ s << *it;
+ }
+#else
+ s << "UNAVAILABLE IN RELEASE";
+#endif
+ return s;
+}
+
+LLContextStrings::LLContextStrings()
+{
+}
+
+// static
+LLContextStrings* LLContextStrings::getThreadLocalInstance()
+{
+ LLContextStrings *cons = LLThreadLocalSingletonPointer::getInstance();
+ if (!cons)
+ {
+ LLThreadLocalSingletonPointer::setInstance(new LLContextStrings);
+ }
+ return LLThreadLocalSingletonPointer::getInstance();
+}
+
+// static
+void LLContextStrings::addContextString(const std::string& str)
+{
+ LLContextStrings *cons = getThreadLocalInstance();
+ //LL_INFOS() << "CTX " << (S32)cons << " ADD " << str << " CNT " << cons->m_contextStrings[str] << LL_ENDL;
+ cons->m_contextStrings[str]++;
+}
+
+// static
+void LLContextStrings::removeContextString(const std::string& str)
+{
+ LLContextStrings *cons = getThreadLocalInstance();
+ cons->m_contextStrings[str]--;
+ //LL_INFOS() << "CTX " << (S32)cons << " REMOVE " << str << " CNT " << cons->m_contextStrings[str] << LL_ENDL;
+ if (cons->m_contextStrings[str] == 0)
+ {
+ cons->m_contextStrings.erase(str);
+ }
+}
+
+// static
+bool LLContextStrings::contains(const std::string& str)
+{
+ const std::map& strings =
+ LLThreadLocalSingletonPointer::getInstance()->m_contextStrings;
+ for (std::map::const_iterator it = strings.begin(); it!=strings.end(); ++it)
+ {
+ if (it->first.find(str) != std::string::npos)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+// static
+void LLContextStrings::output(std::ostream& os)
+{
+ const std::map& strings =
+ LLThreadLocalSingletonPointer::getInstance()->m_contextStrings;
+ for (std::map::const_iterator it = strings.begin(); it!=strings.end(); ++it)
+ {
+ os << it->first << "[" << it->second << "]" << "\n";
+ }
+}
+
+// static
+std::ostream& operator<<(std::ostream& s, const LLContextStatus& context_status)
+{
+ LLThreadLocalSingletonPointer::getInstance()->output(s);
+ return s;
+}
+
+bool LLContextStatus::contains(const std::string& str)
+{
+ return LLThreadLocalSingletonPointer::getInstance()->contains(str);
+}
diff --git a/indra/llcommon/llcallstack.h b/indra/llcommon/llcallstack.h
new file mode 100644
index 0000000000..1f7a7689d7
--- /dev/null
+++ b/indra/llcommon/llcallstack.h
@@ -0,0 +1,80 @@
+/**
+ * @file llcallstack.h
+ * @brief run-time extraction of the current callstack
+ *
+ * $LicenseInfo:firstyear=2016&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2016, Linden Research, Inc.
+ *
+ * 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;
+ * version 2.1 of the License only.
+ *
+ * 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
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include
+
+class LLCallStackImpl;
+
+class LLCallStack
+{
+public:
+ LLCallStack(S32 skip_count=0, bool verbose=false);
+ std::vector m_strings;
+ bool m_verbose;
+ bool contains(const std::string& str);
+private:
+ static LLCallStackImpl *s_impl;
+ S32 m_skipCount;
+};
+
+LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLCallStack& call_stack);
+
+class LLContextStrings
+{
+public:
+ LLContextStrings();
+ static void addContextString(const std::string& str);
+ static void removeContextString(const std::string& str);
+ static void output(std::ostream& os);
+ static LLContextStrings* getThreadLocalInstance();
+ static bool contains(const std::string& str);
+private:
+ std::map m_contextStrings;
+};
+
+class LLScopedContextString
+{
+public:
+ LLScopedContextString(const std::string& str):
+ m_str(str)
+ {
+ LLContextStrings::addContextString(m_str);
+ }
+ ~LLScopedContextString()
+ {
+ LLContextStrings::removeContextString(m_str);
+ }
+private:
+ std::string m_str;
+};
+
+// Mostly exists as a class to hook an ostream override to.
+struct LLContextStatus
+{
+ bool contains(const std::string& str);
+};
+
+LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLContextStatus& context_status);
diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp
index d16bf0160b..8e516d8beb 100644
--- a/indra/llcommon/llcoros.cpp
+++ b/indra/llcommon/llcoros.cpp
@@ -38,6 +38,7 @@
#include "llevents.h"
#include "llerror.h"
#include "stringize.h"
+#include "llexception.h"
// do nothing, when we need nothing done
void LLCoros::no_cleanup(CoroData*) {}
@@ -131,9 +132,9 @@ bool LLCoros::cleanup(const LLSD&)
if ((previousCount < 5) || !(previousCount % 50))
{
if (previousCount < 5)
- LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL;
+ LL_DEBUGS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL;
else
- LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << "("<< previousCount << ")" << LL_ENDL;
+ LL_DEBUGS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << "("<< previousCount << ")" << LL_ENDL;
}
// The erase() call will invalidate its passed iterator value --
@@ -185,9 +186,9 @@ std::string LLCoros::generateDistinctName(const std::string& prefix) const
if ((previousCount < 5) || !(previousCount % 50))
{
if (previousCount < 5)
- LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL;
+ LL_DEBUGS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL;
else
- LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << "(" << previousCount << ")" << LL_ENDL;
+ LL_DEBUGS("LLCoros") << "LLCoros: launching coroutine " << name << "(" << previousCount << ")" << LL_ENDL;
}
@@ -223,7 +224,7 @@ std::string LLCoros::getName() const
void LLCoros::setStackSize(S32 stacksize)
{
- LL_INFOS("LLCoros") << "Setting coroutine stack size to " << stacksize << LL_ENDL;
+ LL_DEBUGS("LLCoros") << "Setting coroutine stack size to " << stacksize << LL_ENDL;
mStackSize = stacksize;
}
@@ -235,7 +236,23 @@ void LLCoros::toplevel(coro::self& self, CoroData* data, const callable_t& calla
// capture the 'self' param in CoroData
data->mSelf = &self;
// run the code the caller actually wants in the coroutine
- callable();
+ try
+ {
+ callable();
+ }
+ catch (const LLContinueError&)
+ {
+ // Any uncaught exception derived from LLContinueError will be caught
+ // here and logged. This coroutine will terminate but the rest of the
+ // viewer will carry on.
+ LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << data->mName));
+ }
+ catch (...)
+ {
+ // Any OTHER kind of uncaught exception will cause the viewer to
+ // crash, hopefully informatively.
+ CRASH_ON_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << data->mName));
+ }
// This cleanup isn't perfectly symmetrical with the way we initially set
// data->mPrev, but this is our last chance to reset mCurrentCoro.
sCurrentCoro.reset(data->mPrev);
diff --git a/indra/llcommon/lldependencies.cpp b/indra/llcommon/lldependencies.cpp
index 87a699ff14..0d5757effd 100644
--- a/indra/llcommon/lldependencies.cpp
+++ b/indra/llcommon/lldependencies.cpp
@@ -39,8 +39,8 @@
#include
#include
#include
-#include
// other Linden headers
+#include "llexception.h"
LLDependenciesBase::VertexList LLDependenciesBase::topo_sort(int vertices, const EdgeList& edges) const
{
@@ -77,7 +77,7 @@ LLDependenciesBase::VertexList LLDependenciesBase::topo_sort(int vertices, const
// Omit independent nodes: display only those that might contribute to
// the cycle.
describe(out, false);
- BOOST_THROW_EXCEPTION(Cycle(out.str()));
+ LLTHROW(Cycle(out.str()));
}
// A peculiarity of boost::topological_sort() is that it emits results in
// REVERSE topological order: to get the result you want, you must
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
index 5ed348e13c..7887a942e9 100644
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -921,11 +921,6 @@ namespace
std::ostringstream message_stream;
- if (show_location && (r->wantsLocation() || level == LLError::LEVEL_ERROR || s->mPrintLocation))
- {
- message_stream << site.mLocationString << " ";
- }
-
if (show_time && r->wantsTime() && s->mTimeFunction != NULL)
{
message_stream << s->mTimeFunction() << " ";
@@ -933,17 +928,17 @@ namespace
if (show_level && r->wantsLevel())
{
- message_stream << site.mLevelString;
+ message_stream << site.mLevelString << " ";
}
if (show_tags && r->wantsTags())
{
message_stream << site.mTagString;
}
- if ((show_level && r->wantsLevel())||
- (show_tags && r->wantsTags()))
+
+ if (show_location && (r->wantsLocation() || level == LLError::LEVEL_ERROR || s->mPrintLocation))
{
- message_stream << " ";
+ message_stream << site.mLocationString << " ";
}
if (show_function && r->wantsFunctionName())
@@ -1493,3 +1488,20 @@ namespace LLError
}
}
+bool debugLoggingEnabled(const std::string& tag)
+{
+ const char* tags[] = {tag.c_str()};
+ ::size_t tag_count = 1;
+ LLError::CallSite _site(LLError::LEVEL_DEBUG, __FILE__, __LINE__,
+ typeid(_LL_CLASS_TO_LOG), __FUNCTION__, false, tags, tag_count);
+ if (LL_UNLIKELY(_site.shouldLog()))
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h
index 3beef65723..3573b3f44e 100644
--- a/indra/llcommon/llerror.h
+++ b/indra/llcommon/llerror.h
@@ -174,7 +174,8 @@ namespace LLError
// not really a level
// used to indicate that no messages should be logged
};
-
+ // If you change ELevel, please update llvlog() macro below.
+
/* Macro support
The classes CallSite and Log are used by the logging macros below.
They are not intended for general use.
@@ -305,24 +306,38 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
/////////////////////////////////
// Error Logging Macros
-// See top of file for common usage.
+// See top of file for common usage.
/////////////////////////////////
-// this macro uses a one-shot do statement to avoid parsing errors when writing control flow statements
-// without braces:
-// if (condition) LL_INFOS() << "True" << LL_ENDL; else LL_INFOS()() << "False" << LL_ENDL
+// Instead of using LL_DEBUGS(), LL_INFOS() et al., it may be tempting to
+// directly code the lllog() macro so you can pass in the LLError::ELevel as a
+// variable. DON'T DO IT! The reason is that the first time control passes
+// through lllog(), it initializes a local static LLError::CallSite with that
+// *first* ELevel value. All subsequent visits will decide whether or not to
+// emit output based on the *first* ELevel value bound into that static
+// CallSite instance. Use LL_VLOGS() instead. lllog() assumes its ELevel
+// argument never varies.
-#define lllog(level, once, ...) \
- do { \
- const char* tags[] = {"", ##__VA_ARGS__}; \
- ::size_t tag_count = LL_ARRAY_SIZE(tags) - 1; \
- static LLError::CallSite _site( \
- level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), __FUNCTION__, once, &tags[1], tag_count);\
- if (LL_UNLIKELY(_site.shouldLog())) \
- { \
- std::ostringstream* _out = LLError::Log::out(); \
+// this macro uses a one-shot do statement to avoid parsing errors when
+// writing control flow statements without braces:
+// if (condition) LL_INFOS() << "True" << LL_ENDL; else LL_INFOS()() << "False" << LL_ENDL;
+
+#define lllog(level, once, ...) \
+ do { \
+ const char* tags[] = {"", ##__VA_ARGS__}; \
+ static LLError::CallSite _site(lllog_site_args_(level, once, tags)); \
+ lllog_test_()
+
+#define lllog_test_() \
+ if (LL_UNLIKELY(_site.shouldLog())) \
+ { \
+ std::ostringstream* _out = LLError::Log::out(); \
(*_out)
+#define lllog_site_args_(level, once, tags) \
+ level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), \
+ __FUNCTION__, once, &tags[1], LL_ARRAY_SIZE(tags)-1
+
//Use this construct if you need to do computation in the middle of a
//message:
//
@@ -363,4 +378,49 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
#define LL_INFOS_ONCE(...) lllog(LLError::LEVEL_INFO, true, ##__VA_ARGS__)
#define LL_WARNS_ONCE(...) lllog(LLError::LEVEL_WARN, true, ##__VA_ARGS__)
+// Use this if you need to pass LLError::ELevel as a variable.
+#define LL_VLOGS(level, ...) llvlog(level, false, ##__VA_ARGS__)
+#define LL_VLOGS_ONCE(level, ...) llvlog(level, true, ##__VA_ARGS__)
+
+// The problem with using lllog() with a variable level is that the first time
+// through, it initializes a static CallSite instance with whatever level you
+// pass. That first level is bound into the CallSite; the level parameter is
+// never again examined. One approach to variable level would be to
+// dynamically construct a CallSite instance every call -- which could get
+// expensive, depending on context. So instead, initialize a static CallSite
+// for each level value we support, then dynamically select the CallSite
+// instance for the passed level value.
+// Compare implementation to lllog() above.
+#define llvlog(level, once, ...) \
+ do { \
+ const char* tags[] = {"", ##__VA_ARGS__}; \
+ /* Need a static CallSite instance per expected ELevel value. */ \
+ /* Since we intend to index this array with the ELevel, */ \
+ /* _sites[0] should be ELevel(0), and so on -- avoid using */ \
+ /* ELevel symbolic names when initializing -- except for */ \
+ /* the last entry, which handles anything beyond the end. */ \
+ /* (Commented ELevel value names are from 2016-09-01.) */ \
+ /* Passing an ELevel past the end of this array is itself */ \
+ /* a fatal error, so ensure the last is LEVEL_ERROR. */ \
+ static LLError::CallSite _sites[] = \
+ { \
+ /* LEVEL_DEBUG */ \
+ LLError::CallSite(lllog_site_args_(LLError::ELevel(0), once, tags)), \
+ /* LEVEL_INFO */ \
+ LLError::CallSite(lllog_site_args_(LLError::ELevel(1), once, tags)), \
+ /* LEVEL_WARN */ \
+ LLError::CallSite(lllog_site_args_(LLError::ELevel(2), once, tags)), \
+ /* LEVEL_ERROR */ \
+ LLError::CallSite(lllog_site_args_(LLError::LEVEL_ERROR, once, tags)) \
+ }; \
+ /* Clamp the passed 'level' to at most last entry */ \
+ std::size_t which((std::size_t(level) >= LL_ARRAY_SIZE(_sites)) ? \
+ (LL_ARRAY_SIZE(_sites) - 1) : std::size_t(level)); \
+ /* selected CallSite *must* be named _site for LL_ENDL */ \
+ LLError::CallSite& _site(_sites[which]); \
+ lllog_test_()
+
+// Check at run-time whether logging is enabled, without generating output
+bool debugLoggingEnabled(const std::string& tag);
+
#endif // LL_LLERROR_H
diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp
index f444530a17..56367b8f54 100644
--- a/indra/llcommon/lleventcoro.cpp
+++ b/indra/llcommon/lleventcoro.cpp
@@ -34,12 +34,12 @@
#include
// std headers
// external library headers
-#include
// other Linden headers
#include "llsdserialize.h"
#include "llerror.h"
#include "llcoros.h"
#include "llmake.h"
+#include "llexception.h"
#include "lleventfilter.h"
@@ -352,7 +352,7 @@ LLSD errorException(const LLEventWithID& result, const std::string& desc)
// returning it, deliver it via exception.
if (result.second)
{
- BOOST_THROW_EXCEPTION(LLErrorEvent(desc, result.first));
+ LLTHROW(LLErrorEvent(desc, result.first));
}
// That way, our caller knows a simple return must be from the reply
// pump (pump 0).
diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index 50919edb8e..97270e4931 100644
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -45,7 +45,6 @@
#include
// external library headers
#include
-#include
#if LL_WINDOWS
#pragma warning (push)
#pragma warning (disable : 4701) // compiler thinks might use uninitialized var, but no
@@ -58,6 +57,7 @@
#include "stringize.h"
#include "llerror.h"
#include "llsdutil.h"
+#include "llexception.h"
#if LL_MSVC
#pragma warning (disable : 4702)
#endif
@@ -175,7 +175,7 @@ std::string LLEventPumps::registerNew(const LLEventPump& pump, const std::string
// Unless we're permitted to tweak it, that's Bad.
if (! tweak)
{
- BOOST_THROW_EXCEPTION(LLEventPump::DupPumpName(std::string("Duplicate LLEventPump name '") + name + "'"));
+ LLTHROW(LLEventPump::DupPumpName("Duplicate LLEventPump name '" + name + "'"));
}
// The passed name isn't unique, but we're permitted to tweak it. Find the
// first decimal-integer suffix not already taken. The insert() attempt
@@ -276,6 +276,8 @@ LLEventPumps::~LLEventPumps()
#pragma warning (push)
#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
#endif
+const std::string LLEventPump::ANONYMOUS = std::string();
+
LLEventPump::LLEventPump(const std::string& name, bool tweak):
// Register every new instance with LLEventPumps
@@ -314,147 +316,162 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL
const NameList& after,
const NameList& before)
{
- // Check for duplicate name before connecting listener to mSignal
- ConnectionMap::const_iterator found = mConnections.find(name);
- // In some cases the user might disconnect a connection explicitly -- or
- // might use LLEventTrackable to disconnect implicitly. Either way, we can
- // end up retaining in mConnections a zombie connection object that's
- // already been disconnected. Such a connection object can't be
- // reconnected -- nor, in the case of LLEventTrackable, would we want to
- // try, since disconnection happens with the destruction of the listener
- // object. That means it's safe to overwrite a disconnected connection
- // object with the new one we're attempting. The case we want to prevent
- // is only when the existing connection object is still connected.
- if (found != mConnections.end() && found->second.connected())
+ float nodePosition = 1.0;
+
+ // if the supplied name is empty we are not interested in the ordering mechanism
+ // and can bypass attempting to find the optimal location to insert the new
+ // listener. We'll just tack it on to the end.
+ if (!name.empty()) // should be the same as testing against ANONYMOUS
{
- BOOST_THROW_EXCEPTION(
- DupListenerName(std::string("Attempt to register duplicate listener name '") + name +
- "' on " + typeid(*this).name() + " '" + getName() + "'"));
- }
- // Okay, name is unique, try to reconcile its dependencies. Specify a new
- // "node" value that we never use for an mSignal placement; we'll fix it
- // later.
- DependencyMap::node_type& newNode = mDeps.add(name, -1.0, after, before);
- // What if this listener has been added, removed and re-added? In that
- // case newNode already has a non-negative value because we never remove a
- // listener from mDeps. But keep processing uniformly anyway in case the
- // listener was added back with different dependencies. Then mDeps.sort()
- // would put it in a different position, and the old newNode placement
- // value would be wrong, so we'd have to reassign it anyway. Trust that
- // re-adding a listener with the same dependencies is the trivial case for
- // mDeps.sort(): it can just replay its cache.
- DependencyMap::sorted_range sorted_range;
- try
- {
- // Can we pick an order that works including this new entry?
- sorted_range = mDeps.sort();
- }
- catch (const DependencyMap::Cycle& e)
- {
- // No: the new node's after/before dependencies have made mDeps
- // unsortable. If we leave the new node in mDeps, it will continue
- // to screw up all future attempts to sort()! Pull it out.
- mDeps.remove(name);
- BOOST_THROW_EXCEPTION(
- Cycle(std::string("New listener '") + name + "' on " + typeid(*this).name() +
- " '" + getName() + "' would cause cycle: " + e.what()));
- }
- // Walk the list to verify that we haven't changed the order.
- float previous = 0.0, myprev = 0.0;
- DependencyMap::sorted_iterator mydmi = sorted_range.end(); // need this visible after loop
- for (DependencyMap::sorted_iterator dmi = sorted_range.begin();
- dmi != sorted_range.end(); ++dmi)
- {
- // Since we've added the new entry with an invalid placement,
- // recognize it and skip it.
- if (dmi->first == name)
+ // Check for duplicate name before connecting listener to mSignal
+ ConnectionMap::const_iterator found = mConnections.find(name);
+ // In some cases the user might disconnect a connection explicitly -- or
+ // might use LLEventTrackable to disconnect implicitly. Either way, we can
+ // end up retaining in mConnections a zombie connection object that's
+ // already been disconnected. Such a connection object can't be
+ // reconnected -- nor, in the case of LLEventTrackable, would we want to
+ // try, since disconnection happens with the destruction of the listener
+ // object. That means it's safe to overwrite a disconnected connection
+ // object with the new one we're attempting. The case we want to prevent
+ // is only when the existing connection object is still connected.
+ if (found != mConnections.end() && found->second.connected())
{
- // Remember the iterator belonging to our new node, and which
- // placement value was 'previous' at that point.
- mydmi = dmi;
- myprev = previous;
- continue;
+ LLTHROW(DupListenerName("Attempt to register duplicate listener name '" + name +
+ "' on " + typeid(*this).name() + " '" + getName() + "'"));
}
- // If the new node has rearranged the existing nodes, we'll find
- // that their placement values are no longer in increasing order.
- if (dmi->second < previous)
+ // Okay, name is unique, try to reconcile its dependencies. Specify a new
+ // "node" value that we never use for an mSignal placement; we'll fix it
+ // later.
+ DependencyMap::node_type& newNode = mDeps.add(name, -1.0, after, before);
+ // What if this listener has been added, removed and re-added? In that
+ // case newNode already has a non-negative value because we never remove a
+ // listener from mDeps. But keep processing uniformly anyway in case the
+ // listener was added back with different dependencies. Then mDeps.sort()
+ // would put it in a different position, and the old newNode placement
+ // value would be wrong, so we'd have to reassign it anyway. Trust that
+ // re-adding a listener with the same dependencies is the trivial case for
+ // mDeps.sort(): it can just replay its cache.
+ DependencyMap::sorted_range sorted_range;
+ try
{
- // This is another scenario in which we'd better back out the
- // newly-added node from mDeps -- but don't do it yet, we want to
- // traverse the existing mDeps to report on it!
- // Describe the change to the order of our listeners. Copy
- // everything but the newest listener to a vector we can sort to
- // obtain the old order.
- typedef std::vector< std::pair > SortNameList;
- SortNameList sortnames;
- for (DependencyMap::sorted_iterator cdmi(sorted_range.begin()), cdmend(sorted_range.end());
- cdmi != cdmend; ++cdmi)
- {
- if (cdmi->first != name)
- {
- sortnames.push_back(SortNameList::value_type(cdmi->second, cdmi->first));
- }
- }
- std::sort(sortnames.begin(), sortnames.end());
- std::ostringstream out;
- out << "New listener '" << name << "' on " << typeid(*this).name() << " '" << getName()
- << "' would move previous listener '" << dmi->first << "'\nwas: ";
- SortNameList::const_iterator sni(sortnames.begin()), snend(sortnames.end());
- if (sni != snend)
- {
- out << sni->second;
- while (++sni != snend)
- {
- out << ", " << sni->second;
- }
- }
- out << "\nnow: ";
- DependencyMap::sorted_iterator ddmi(sorted_range.begin()), ddmend(sorted_range.end());
- if (ddmi != ddmend)
- {
- out << ddmi->first;
- while (++ddmi != ddmend)
- {
- out << ", " << ddmi->first;
- }
- }
- // NOW remove the offending listener node.
+ // Can we pick an order that works including this new entry?
+ sorted_range = mDeps.sort();
+ }
+ catch (const DependencyMap::Cycle& e)
+ {
+ // No: the new node's after/before dependencies have made mDeps
+ // unsortable. If we leave the new node in mDeps, it will continue
+ // to screw up all future attempts to sort()! Pull it out.
mDeps.remove(name);
- // Having constructed a description of the order change, inform caller.
- BOOST_THROW_EXCEPTION(OrderChange(out.str()));
+ LLTHROW(Cycle("New listener '" + name + "' on " + typeid(*this).name() +
+ " '" + getName() + "' would cause cycle: " + e.what()));
}
- // This node becomes the previous one.
- previous = dmi->second;
- }
- // We just got done with a successful mDeps.add(name, ...) call. We'd
- // better have found 'name' somewhere in that sorted list!
- assert(mydmi != sorted_range.end());
- // Four cases:
- // 0. name is the only entry: placement 1.0
- // 1. name is the first of several entries: placement (next placement)/2
- // 2. name is between two other entries: placement (myprev + (next placement))/2
- // 3. name is the last entry: placement ceil(myprev) + 1.0
- // Since we've cleverly arranged for myprev to be 0.0 if name is the
- // first entry, this folds down to two cases. Case 1 is subsumed by
- // case 2, and case 0 is subsumed by case 3. So we need only handle
- // cases 2 and 3, which means we need only detect whether name is the
- // last entry. Increment mydmi to see if there's anything beyond.
- if (++mydmi != sorted_range.end())
- {
- // The new node isn't last. Place it between the previous node and
- // the successor.
- newNode = (myprev + mydmi->second)/2.f;
- }
- else
- {
- // The new node is last. Bump myprev up to the next integer, add
- // 1.0 and use that.
- newNode = std::ceil(myprev) + 1.f;
+ // Walk the list to verify that we haven't changed the order.
+ float previous = 0.0, myprev = 0.0;
+ DependencyMap::sorted_iterator mydmi = sorted_range.end(); // need this visible after loop
+ for (DependencyMap::sorted_iterator dmi = sorted_range.begin();
+ dmi != sorted_range.end(); ++dmi)
+ {
+ // Since we've added the new entry with an invalid placement,
+ // recognize it and skip it.
+ if (dmi->first == name)
+ {
+ // Remember the iterator belonging to our new node, and which
+ // placement value was 'previous' at that point.
+ mydmi = dmi;
+ myprev = previous;
+ continue;
+ }
+ // If the new node has rearranged the existing nodes, we'll find
+ // that their placement values are no longer in increasing order.
+ if (dmi->second < previous)
+ {
+ // This is another scenario in which we'd better back out the
+ // newly-added node from mDeps -- but don't do it yet, we want to
+ // traverse the existing mDeps to report on it!
+ // Describe the change to the order of our listeners. Copy
+ // everything but the newest listener to a vector we can sort to
+ // obtain the old order.
+ typedef std::vector< std::pair > SortNameList;
+ SortNameList sortnames;
+ for (DependencyMap::sorted_iterator cdmi(sorted_range.begin()), cdmend(sorted_range.end());
+ cdmi != cdmend; ++cdmi)
+ {
+ if (cdmi->first != name)
+ {
+ sortnames.push_back(SortNameList::value_type(cdmi->second, cdmi->first));
+ }
+ }
+ std::sort(sortnames.begin(), sortnames.end());
+ std::ostringstream out;
+ out << "New listener '" << name << "' on " << typeid(*this).name() << " '" << getName()
+ << "' would move previous listener '" << dmi->first << "'\nwas: ";
+ SortNameList::const_iterator sni(sortnames.begin()), snend(sortnames.end());
+ if (sni != snend)
+ {
+ out << sni->second;
+ while (++sni != snend)
+ {
+ out << ", " << sni->second;
+ }
+ }
+ out << "\nnow: ";
+ DependencyMap::sorted_iterator ddmi(sorted_range.begin()), ddmend(sorted_range.end());
+ if (ddmi != ddmend)
+ {
+ out << ddmi->first;
+ while (++ddmi != ddmend)
+ {
+ out << ", " << ddmi->first;
+ }
+ }
+ // NOW remove the offending listener node.
+ mDeps.remove(name);
+ // Having constructed a description of the order change, inform caller.
+ LLTHROW(OrderChange(out.str()));
+ }
+ // This node becomes the previous one.
+ previous = dmi->second;
+ }
+ // We just got done with a successful mDeps.add(name, ...) call. We'd
+ // better have found 'name' somewhere in that sorted list!
+ assert(mydmi != sorted_range.end());
+ // Four cases:
+ // 0. name is the only entry: placement 1.0
+ // 1. name is the first of several entries: placement (next placement)/2
+ // 2. name is between two other entries: placement (myprev + (next placement))/2
+ // 3. name is the last entry: placement ceil(myprev) + 1.0
+ // Since we've cleverly arranged for myprev to be 0.0 if name is the
+ // first entry, this folds down to two cases. Case 1 is subsumed by
+ // case 2, and case 0 is subsumed by case 3. So we need only handle
+ // cases 2 and 3, which means we need only detect whether name is the
+ // last entry. Increment mydmi to see if there's anything beyond.
+ if (++mydmi != sorted_range.end())
+ {
+ // The new node isn't last. Place it between the previous node and
+ // the successor.
+ newNode = (myprev + mydmi->second) / 2.f;
+ }
+ else
+ {
+ // The new node is last. Bump myprev up to the next integer, add
+ // 1.0 and use that.
+ newNode = std::ceil(myprev) + 1.f;
+ }
+
+ nodePosition = newNode;
}
// Now that newNode has a value that places it appropriately in mSignal,
// connect it.
- LLBoundListener bound = mSignal->connect(newNode, listener);
- mConnections[name] = bound;
+ LLBoundListener bound = mSignal->connect(nodePosition, listener);
+
+ if (!name.empty())
+ { // note that we are not tracking anonymous listeners here either.
+ // This means that it is the caller's responsibility to either assign
+ // to a TempBoundListerer (scoped_connection) or manually disconnect
+ // when done.
+ mConnections[name] = bound;
+ }
return bound;
}
@@ -611,7 +628,7 @@ bool LLListenerOrPumpName::operator()(const LLSD& event) const
{
if (! mListener)
{
- BOOST_THROW_EXCEPTION(Empty("attempting to call uninitialized"));
+ LLTHROW(Empty("attempting to call uninitialized"));
}
return (*mListener)(event);
}
diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h
index 8ff337911d..a3b9ec02e0 100644
--- a/indra/llcommon/llevents.h
+++ b/indra/llcommon/llevents.h
@@ -95,12 +95,32 @@ struct LLStopWhenHandled
result_type operator()(InputIterator first, InputIterator last) const
{
for (InputIterator si = first; si != last; ++si)
- {
- if (*si)
- {
- return true;
- }
- }
+ {
+ try
+ {
+ if (*si)
+ {
+ return true;
+ }
+ }
+ catch (const LLContinueError&)
+ {
+ // We catch LLContinueError here because an LLContinueError-
+ // based exception means the viewer as a whole should carry on
+ // to the best of our ability. Therefore subsequent listeners
+ // on the same LLEventPump should still receive this event.
+
+ // The iterator passed to a boost::signals2 Combiner is very
+ // clever, but provides no contextual information. We would
+ // very much like to be able to log the name of the LLEventPump
+ // plus the name of this particular listener, but alas.
+ LOG_UNHANDLED_EXCEPTION("LLEventPump");
+ }
+ // We do NOT catch (...) here because we might as well let it
+ // propagate out to the generic handler. If we were able to log
+ // context information here, that would be great, but we can't, so
+ // there's no point.
+ }
return false;
}
};
@@ -365,6 +385,8 @@ typedef boost::signals2::trackable LLEventTrackable;
class LL_COMMON_API LLEventPump: public LLEventTrackable
{
public:
+ static const std::string ANONYMOUS; // constant for anonymous listeners.
+
/**
* Exception thrown by LLEventPump(). You are trying to instantiate an
* LLEventPump (subclass) using the same name as some other instance, and
@@ -476,6 +498,12 @@ public:
* instantiate your listener, then passing the same name on each listen()
* call, allows us to optimize away the second and subsequent dependency
* sorts.
+ *
+ * If name is set to LLEventPump::ANONYMOUS listen will bypass the entire
+ * dependency and ordering calculation. In this case, it is critical that
+ * the result be assigned to a LLTempBoundListener or the listener is
+ * manually disconnected when no longer needed since there will be no
+ * way to later find and disconnect this listener manually.
*
* If (as is typical) you pass a boost::bind() expression as @a
* listener, listen() will inspect the components of that expression. If a
diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp
new file mode 100644
index 0000000000..b32ec2c9c9
--- /dev/null
+++ b/indra/llcommon/llexception.cpp
@@ -0,0 +1,55 @@
+/**
+ * @file llexception.cpp
+ * @author Nat Goodspeed
+ * @date 2016-08-12
+ * @brief Implementation for llexception.
+ *
+ * $LicenseInfo:firstyear=2016&license=viewerlgpl$
+ * Copyright (c) 2016, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llexception.h"
+// STL headers
+// std headers
+#include
+// external library headers
+#include
+// other Linden headers
+#include "llerror.h"
+#include "llerrorcontrol.h"
+
+namespace {
+// used by crash_on_unhandled_exception_() and log_unhandled_exception_()
+void log_unhandled_exception_(LLError::ELevel level,
+ const char* file, int line, const char* pretty_function,
+ const std::string& context)
+{
+ // log same message but allow caller-specified severity level
+ LL_VLOGS(level, "LLException") << LLError::abbreviateFile(file)
+ << "(" << line << "): Unhandled exception caught in " << pretty_function;
+ if (! context.empty())
+ {
+ LL_CONT << ": " << context;
+ }
+ LL_CONT << ":\n" << boost::current_exception_diagnostic_information() << LL_ENDL;
+}
+}
+
+void crash_on_unhandled_exception_(const char* file, int line, const char* pretty_function,
+ const std::string& context)
+{
+ // LL_ERRS() terminates and propagates message into crash dump.
+ log_unhandled_exception_(LLError::LEVEL_ERROR, file, line, pretty_function, context);
+}
+
+void log_unhandled_exception_(const char* file, int line, const char* pretty_function,
+ const std::string& context)
+{
+ // Use LL_WARNS() because we seriously do not expect this to happen
+ // routinely, but we DO expect to return from this function.
+ log_unhandled_exception_(LLError::LEVEL_WARN, file, line, pretty_function, context);
+}
diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h
index 3ac2f4762f..dfcb7c192f 100644
--- a/indra/llcommon/llexception.h
+++ b/indra/llcommon/llexception.h
@@ -14,22 +14,24 @@
#include
#include
+#include
+#include
+
+// "Found someone who can comfort me
+// But there are always exceptions..."
+// - Empty Pages, Traffic, from John Barleycorn (1970)
+// https://www.youtube.com/watch?v=dRH0CGVK7ic
/**
* LLException is intended as the common base class from which all
- * viewer-specific exceptions are derived. It is itself a subclass of
- * boost::exception; use catch (const boost::exception& e) clause to log the
- * string from boost::diagnostic_information(e).
+ * viewer-specific exceptions are derived. Rationale for why it's derived from
+ * both std::exception and boost::exception is explained in
+ * tests/llexception_test.cpp.
*
- * Since it is also derived from std::exception, a generic catch (const
- * std::exception&) should also work, though what() is unlikely to be as
- * informative as boost::diagnostic_information().
- *
- * Please use BOOST_THROW_EXCEPTION()
- * http://www.boost.org/doc/libs/release/libs/exception/doc/BOOST_THROW_EXCEPTION.html
- * to throw viewer exceptions whenever possible. This enriches the exception's
- * diagnostic_information() with the source file, line and containing function
- * of the BOOST_THROW_EXCEPTION() macro.
+ * boost::current_exception_diagnostic_information() is quite wonderful: if
+ * all we need to do with an exception is log it, in most places we should
+ * catch (...) and log boost::current_exception_diagnostic_information().
+ * See CRASH_ON_UNHANDLED_EXCEPTION() and LOG_UNHANDLED_EXCEPTION() below.
*
* There may be circumstances in which it would be valuable to distinguish an
* exception explicitly thrown by viewer code from an exception thrown by
@@ -60,4 +62,24 @@ struct LLContinueError: public LLException
{}
};
+/**
+ * Please use LLTHROW() to throw viewer exceptions whenever possible. This
+ * enriches the exception's diagnostic_information() with the source file,
+ * line and containing function of the LLTHROW() macro.
+ */
+// Currently we implement that using BOOST_THROW_EXCEPTION(). Wrap it in
+// LLTHROW() in case we ever want to revisit that implementation decision.
+#define LLTHROW(x) BOOST_THROW_EXCEPTION(x)
+
+/// Call this macro from a catch (...) clause
+#define CRASH_ON_UNHANDLED_EXCEPTION(CONTEXT) \
+ crash_on_unhandled_exception_(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION, CONTEXT)
+void crash_on_unhandled_exception_(const char*, int, const char*, const std::string&);
+
+/// Call this from a catch (const LLContinueError&) clause, or from a catch
+/// (...) clause in which you do NOT want the viewer to crash.
+#define LOG_UNHANDLED_EXCEPTION(CONTEXT) \
+ log_unhandled_exception_(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION, CONTEXT)
+void log_unhandled_exception_(const char*, int, const char*, const std::string&);
+
#endif /* ! defined(LL_LLEXCEPTION_H) */
diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h
index 2370253078..f56e5596f5 100644
--- a/indra/llcommon/llfasttimer.h
+++ b/indra/llcommon/llfasttimer.h
@@ -296,7 +296,16 @@ LL_FORCE_INLINE BlockTimer::BlockTimer(BlockTimerStatHandle& timer)
{
#if LL_FAST_TIMER_ON
BlockTimerStackRecord* cur_timer_data = LLThreadLocalSingletonPointer::getInstance();
- if (!cur_timer_data) return;
+ if (!cur_timer_data)
+ {
+ // How likely is it that
+ // LLThreadLocalSingletonPointer::getInstance() will return NULL?
+ // Even without researching, what we can say is that if we exit
+ // without setting mStartTime at all, gcc 4.7 produces (fatal)
+ // warnings about a possibly-uninitialized data member.
+ mStartTime = 0;
+ return;
+ }
TimeBlockAccumulator& accumulator = timer.getCurrentAccumulator();
accumulator.mActiveCount++;
// keep current parent as long as it is active when we are
diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp
index a8bb9bc53a..c87d2a3e58 100644
--- a/indra/llcommon/llleap.cpp
+++ b/indra/llcommon/llleap.cpp
@@ -21,7 +21,6 @@
#include
#include
#include
-#include
// other Linden headers
#include "llerror.h"
#include "llstring.h"
@@ -34,6 +33,7 @@
#include "lltimer.h"
#include "lluuid.h"
#include "llleaplistener.h"
+#include "llexception.h"
#if LL_MSVC
#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
@@ -70,7 +70,7 @@ public:
// Rule out empty vector
if (plugin.empty())
{
- BOOST_THROW_EXCEPTION(Error("no plugin command"));
+ LLTHROW(Error("no plugin command"));
}
// Don't leave desc empty either, but in this case, if we weren't
@@ -113,7 +113,7 @@ public:
// If that didn't work, no point in keeping this LLLeap object.
if (! mChild)
{
- BOOST_THROW_EXCEPTION(Error(STRINGIZE("failed to run " << mDesc)));
+ LLTHROW(Error(STRINGIZE("failed to run " << mDesc)));
}
// Okay, launch apparently worked. Change our mDonePump listener.
diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h
index 0fb257aab1..575edddc43 100644
--- a/indra/llcommon/llmemory.h
+++ b/indra/llcommon/llmemory.h
@@ -110,11 +110,15 @@ template T* LL_NEXT_ALIGNED_ADDRESS_64(T* address)
#if defined(LL_WINDOWS)
return _aligned_malloc(size, align);
#else
+ char* aligned = NULL;
void* mem = malloc( size + (align - 1) + sizeof(void*) );
- char* aligned = ((char*)mem) + sizeof(void*);
- aligned += align - ((uintptr_t)aligned & (align - 1));
+ if (mem)
+ {
+ aligned = ((char*)mem) + sizeof(void*);
+ aligned += align - ((uintptr_t)aligned & (align - 1));
- ((void**)aligned)[-1] = mem;
+ ((void**)aligned)[-1] = mem;
+ }
return aligned;
#endif
}
diff --git a/indra/llcommon/llprocess.cpp b/indra/llcommon/llprocess.cpp
index ca19c94736..8c321d06b9 100644
--- a/indra/llcommon/llprocess.cpp
+++ b/indra/llcommon/llprocess.cpp
@@ -34,12 +34,12 @@
#include "llapr.h"
#include "apr_signal.h"
#include "llevents.h"
+#include "llexception.h"
#include
#include
#include
#include
-#include
#include
#include
#include
@@ -531,9 +531,8 @@ LLProcess::LLProcess(const LLSDOrParams& params):
if (! params.validateBlock(true))
{
- BOOST_THROW_EXCEPTION(
- LLProcessError(STRINGIZE("not launched: failed parameter validation\n"
- << LLSDNotationStreamer(params))));
+ LLTHROW(LLProcessError(STRINGIZE("not launched: failed parameter validation\n"
+ << LLSDNotationStreamer(params))));
}
mPostend = params.postend;
@@ -598,11 +597,10 @@ LLProcess::LLProcess(const LLSDOrParams& params):
}
else
{
- BOOST_THROW_EXCEPTION(
- LLProcessError(STRINGIZE("For " << params.executable()
- << ": unsupported FileParam for " << which
- << ": type='" << fparam.type()
- << "', name='" << fparam.name() << "'")));
+ LLTHROW(LLProcessError(STRINGIZE("For " << params.executable()
+ << ": unsupported FileParam for " << which
+ << ": type='" << fparam.type()
+ << "', name='" << fparam.name() << "'")));
}
}
// By default, pass APR_NO_PIPE for unspecified slots.
@@ -681,7 +679,7 @@ LLProcess::LLProcess(const LLSDOrParams& params):
if (ll_apr_warn_status(apr_proc_create(&mProcess, argv[0], &argv[0], NULL, procattr,
gAPRPoolp)))
{
- BOOST_THROW_EXCEPTION(LLProcessError(STRINGIZE(params << " failed")));
+ LLTHROW(LLProcessError(STRINGIZE(params << " failed")));
}
// arrange to call status_callback()
@@ -1066,7 +1064,7 @@ PIPETYPE& LLProcess::getPipe(FILESLOT slot)
PIPETYPE* wp = getPipePtr(error, slot);
if (! wp)
{
- BOOST_THROW_EXCEPTION(NoPipe(error));
+ LLTHROW(NoPipe(error));
}
return *wp;
}
diff --git a/indra/llcommon/llstl.h b/indra/llcommon/llstl.h
index 0435cb8a08..b024b47225 100644
--- a/indra/llcommon/llstl.h
+++ b/indra/llcommon/llstl.h
@@ -242,7 +242,6 @@ inline T* get_ptr_in_map(const std::map& inmap, const K& key)
template
inline bool is_in_map(const std::map& inmap, const K& key)
{
- typedef typename std::map::const_iterator map_iter;
if(inmap.find(key) == inmap.end())
{
return false;
diff --git a/indra/llcommon/llthreadsafequeue.cpp b/indra/llcommon/llthreadsafequeue.cpp
index a004618e96..491f920c0f 100644
--- a/indra/llcommon/llthreadsafequeue.cpp
+++ b/indra/llcommon/llthreadsafequeue.cpp
@@ -26,8 +26,8 @@
#include "linden_common.h"
#include
#include
-#include
#include "llthreadsafequeue.h"
+#include "llexception.h"
@@ -42,13 +42,13 @@ LLThreadSafeQueueImplementation::LLThreadSafeQueueImplementation(apr_pool_t * po
{
if(mOwnsPool) {
apr_status_t status = apr_pool_create(&mPool, 0);
- if(status != APR_SUCCESS) BOOST_THROW_EXCEPTION(LLThreadSafeQueueError("failed to allocate pool"));
+ if(status != APR_SUCCESS) LLTHROW(LLThreadSafeQueueError("failed to allocate pool"));
} else {
; // No op.
}
apr_status_t status = apr_queue_create(&mQueue, capacity, mPool);
- if(status != APR_SUCCESS) BOOST_THROW_EXCEPTION(LLThreadSafeQueueError("failed to allocate queue"));
+ if(status != APR_SUCCESS) LLTHROW(LLThreadSafeQueueError("failed to allocate queue"));
}
@@ -69,9 +69,9 @@ void LLThreadSafeQueueImplementation::pushFront(void * element)
apr_status_t status = apr_queue_push(mQueue, element);
if(status == APR_EINTR) {
- BOOST_THROW_EXCEPTION(LLThreadSafeQueueInterrupt());
+ LLTHROW(LLThreadSafeQueueInterrupt());
} else if(status != APR_SUCCESS) {
- BOOST_THROW_EXCEPTION(LLThreadSafeQueueError("push failed"));
+ LLTHROW(LLThreadSafeQueueError("push failed"));
} else {
; // Success.
}
@@ -89,9 +89,9 @@ void * LLThreadSafeQueueImplementation::popBack(void)
apr_status_t status = apr_queue_pop(mQueue, &element);
if(status == APR_EINTR) {
- BOOST_THROW_EXCEPTION(LLThreadSafeQueueInterrupt());
+ LLTHROW(LLThreadSafeQueueInterrupt());
} else if(status != APR_SUCCESS) {
- BOOST_THROW_EXCEPTION(LLThreadSafeQueueError("pop failed"));
+ LLTHROW(LLThreadSafeQueueError("pop failed"));
} else {
return element;
}
diff --git a/indra/llcommon/lltracerecording.cpp b/indra/llcommon/lltracerecording.cpp
index 0b10438b9f..3094b627a2 100644
--- a/indra/llcommon/lltracerecording.cpp
+++ b/indra/llcommon/lltracerecording.cpp
@@ -257,7 +257,13 @@ F64Kilobytes Recording::getMean(const StatType& stat)
if (active_accumulator && active_accumulator->mSize.hasValue())
{
- return F64Bytes(lerp(accumulator.mSize.getMean(), active_accumulator->mSize.getMean(), active_accumulator->mSize.getSampleCount() / (accumulator.mSize.getSampleCount() + active_accumulator->mSize.getSampleCount())));
+ F32 t = 0.0f;
+ S32 div = accumulator.mSize.getSampleCount() + active_accumulator->mSize.getSampleCount();
+ if (div > 0)
+ {
+ t = active_accumulator->mSize.getSampleCount() / div;
+ }
+ return F64Bytes(lerp(accumulator.mSize.getMean(), active_accumulator->mSize.getMean(), t));
}
else
{
@@ -426,7 +432,13 @@ F64 Recording::getMean( const StatType& stat )
const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL;
if (active_accumulator && active_accumulator->hasValue())
{
- return lerp(accumulator.getMean(), active_accumulator->getMean(), active_accumulator->getSampleCount() / (accumulator.getSampleCount() + active_accumulator->getSampleCount()));
+ F32 t = 0.0f;
+ S32 div = accumulator.getSampleCount() + active_accumulator->getSampleCount();
+ if (div > 0)
+ {
+ t = active_accumulator->getSampleCount() / div;
+ }
+ return lerp(accumulator.getMean(), active_accumulator->getMean(), t);
}
else
{
@@ -506,7 +518,13 @@ F64 Recording::getMean( const StatType& stat )
const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL;
if (active_accumulator && active_accumulator->hasValue())
{
- return lerp(accumulator.getMean(), active_accumulator->getMean(), active_accumulator->getSampleCount() / (accumulator.getSampleCount() + active_accumulator->getSampleCount()));
+ F32 t = 0.0f;
+ S32 div = accumulator.getSampleCount() + active_accumulator->getSampleCount();
+ if (div > 0)
+ {
+ t = active_accumulator->getSampleCount() / div;
+ }
+ return lerp(accumulator.getMean(), active_accumulator->getMean(), t);
}
else
{
diff --git a/indra/llcommon/lluriparser.cpp b/indra/llcommon/lluriparser.cpp
index 82d0dc8b4b..c275b90120 100644
--- a/indra/llcommon/lluriparser.cpp
+++ b/indra/llcommon/lluriparser.cpp
@@ -205,9 +205,9 @@ void LLUriParser::glue(std::string& uri) const
uri = first_part + second_part;
}
-void LLUriParser::glueFirst(std::string& uri) const
+void LLUriParser::glueFirst(std::string& uri, bool use_scheme) const
{
- if (mScheme.size())
+ if (use_scheme && mScheme.size())
{
uri = mScheme;
uri += "://";
diff --git a/indra/llcommon/lluriparser.h b/indra/llcommon/lluriparser.h
index 2df8085ae6..cfbf54f3c8 100644
--- a/indra/llcommon/lluriparser.h
+++ b/indra/llcommon/lluriparser.h
@@ -60,7 +60,7 @@ public:
void extractParts();
void glue(std::string& uri) const;
- void glueFirst(std::string& uri) const;
+ void glueFirst(std::string& uri, bool use_scheme = true) const;
void glueSecond(std::string& uri) const;
bool test() const;
S32 normalize();
diff --git a/indra/llcommon/lluuid.cpp b/indra/llcommon/lluuid.cpp
index 785cf47926..d4af2c6b01 100644
--- a/indra/llcommon/lluuid.cpp
+++ b/indra/llcommon/lluuid.cpp
@@ -83,7 +83,7 @@ unsigned int decode( char const * fiveChars ) throw( bad_input_data )
unsigned int ret = 0;
for( int ix = 0; ix < 5; ++ix ) {
char * s = strchr( encodeTable, fiveChars[ ix ] );
-if( s == 0 ) BOOST_THROW_EXCEPTION(bad_input_data());
+if( s == 0 ) LLTHROW(bad_input_data());
ret = ret * 85 + (s-encodeTable);
}
return ret;
diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp
index f51279e817..20de205454 100644
--- a/indra/llcommon/tests/llerror_test.cpp
+++ b/indra/llcommon/tests/llerror_test.cpp
@@ -237,8 +237,21 @@ namespace tut
void ErrorTestObject::test<4>()
// file abbreviation
{
- std::string thisFile = __FILE__;
- std::string abbreviateFile = LLError::abbreviateFile(thisFile);
+ std::string prev, abbreviateFile = __FILE__;
+ do
+ {
+ prev = abbreviateFile;
+ abbreviateFile = LLError::abbreviateFile(abbreviateFile);
+ // __FILE__ is assumed to end with
+ // indra/llcommon/tests/llerror_test.cpp. This test used to call
+ // abbreviateFile() exactly once, then check below whether it
+ // still contained the string 'indra'. That fails if the FIRST
+ // part of the pathname also contains indra! Certain developer
+ // machine images put local directory trees under
+ // /ngi-persist/indra, which is where we observe the problem. So
+ // now, keep calling abbreviateFile() until it returns its
+ // argument unchanged, THEN check.
+ } while (abbreviateFile != prev);
ensure_ends_with("file name abbreviation",
abbreviateFile,
@@ -551,9 +564,9 @@ namespace tut
function;
writeReturningLocationAndFunction(location, function);
- ensure_equals("order is location time type function message",
+ ensure_equals("order is time location type function message",
message(0),
- location + roswell() + " INFO: " + function + ": apple");
+ roswell() + " INFO: " + location + function + ": apple");
}
template<> template<>
diff --git a/indra/llcommon/tests/llexception_test.cpp b/indra/llcommon/tests/llexception_test.cpp
new file mode 100644
index 0000000000..6bee1943c2
--- /dev/null
+++ b/indra/llcommon/tests/llexception_test.cpp
@@ -0,0 +1,308 @@
+/**
+ * @file llexception_test.cpp
+ * @author Nat Goodspeed
+ * @date 2016-08-12
+ * @brief Tests for throwing exceptions.
+ *
+ * This isn't a regression test: it doesn't need to be run every build, which
+ * is why the corresponding line in llcommon/CMakeLists.txt is commented out.
+ * Rather it's a head-to-head test of what kind of exception information we
+ * can collect from various combinations of exception base classes, type of
+ * throw verb and sequences of catch clauses.
+ *
+ * This "test" makes no ensure() calls: its output goes to stdout for human
+ * examination.
+ *
+ * As of 2016-08-12 with Boost 1.57, we come to the following conclusions.
+ * These should probably be re-examined from time to time as we update Boost.
+ *
+ * - It is indisputably beneficial to use BOOST_THROW_EXCEPTION() rather than
+ * plain throw. The macro annotates the exception object with the filename,
+ * line number and function name from which the exception was thrown.
+ *
+ * - That being the case, deriving only from boost::exception isn't an option.
+ * Every exception object passed to BOOST_THROW_EXCEPTION() must be derived
+ * directly or indirectly from std::exception. The only question is whether
+ * to also derive from boost::exception. We decided to derive LLException
+ * from both, as it makes message output slightly cleaner, but this is a
+ * trivial reason: if a strong reason emerges to prefer single inheritance,
+ * dropping the boost::exception base class shouldn't be a problem.
+ *
+ * - (As you will have guessed, ridiculous things like a char* or int or a
+ * class derived from neither boost::exception nor std::exception can only
+ * be caught by that specific type or (...), and
+ * boost::current_exception_diagnostic_information() simply throws up its
+ * hands and confesses utter ignorance. Stay away from such nonsense.)
+ *
+ * - But if you derive from std::exception, to nat's surprise,
+ * boost::current_exception_diagnostic_information() gives as much
+ * information about exceptions in a catch (...) clause as you can get from
+ * a specific catch (const std::exception&) clause, notably the concrete
+ * exception class and the what() string. So instead of a sequence like
+ *
+ * try { ... }
+ * catch (const boost::exception& e) { ... boost-flavored logging ... }
+ * catch (const std::exception& e) { ... std::exception logging ... }
+ * catch (...) { ... generic logging ... }
+ *
+ * we should be able to get away with only a catch (...) clause that logs
+ * boost::current_exception_diagnostic_information().
+ *
+ * - Going further: boost::current_exception_diagnostic_information() provides
+ * just as much information even within a std::set_terminate() handler. So
+ * it might not even be strictly necessary to include a catch (...) clause
+ * since the viewer does use std::set_terminate().
+ *
+ * - (We might consider adding a catch (int) clause because Kakadu internally
+ * throws ints, and who knows if one of those might leak out. If it does,
+ * boost::current_exception_diagnostic_information() can do nothing with it.
+ * A catch (int) clause could at least log the value and rethrow.)
+ *
+ * $LicenseInfo:firstyear=2016&license=viewerlgpl$
+ * Copyright (c) 2016, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llexception.h"
+// STL headers
+// std headers
+#include
+// external library headers
+#include
+// other Linden headers
+#include "../test/lltut.h"
+
+// helper for display output
+// usage: std::cout << center(some string value, fill char, width) << std::endl;
+// (assumes it's the only thing on that particular line)
+struct center
+{
+ center(const std::string& label, char fill, std::size_t width):
+ mLabel(label),
+ mFill(fill),
+ mWidth(width)
+ {}
+
+ // Use friend declaration not because we need to grant access, but because
+ // it lets us declare a free operator like a member function.
+ friend std::ostream& operator<<(std::ostream& out, const center& ctr)
+ {
+ std::size_t padded = ctr.mLabel.length() + 2;
+ std::size_t left = (ctr.mWidth - padded) / 2;
+ std::size_t right = ctr.mWidth - left - padded;
+ return out << std::string(left, ctr.mFill) << ' ' << ctr.mLabel << ' '
+ << std::string(right, ctr.mFill);
+ }
+
+ std::string mLabel;
+ char mFill;
+ std::size_t mWidth;
+};
+
+/*****************************************************************************
+* Four kinds of exceptions: derived from boost::exception, from
+* std::exception, from both, from neither
+*****************************************************************************/
+// Interestingly, we can't use this variant with BOOST_THROW_EXCEPTION()
+// (which we want) -- we reach a failure topped by this comment:
+// //All boost exceptions are required to derive from std::exception,
+// //to ensure compatibility with BOOST_NO_EXCEPTIONS.
+struct FromBoost: public boost::exception
+{
+ FromBoost(const std::string& what): mWhat(what) {}
+ ~FromBoost() throw() {}
+ std::string what() const { return mWhat; }
+ std::string mWhat;
+};
+
+struct FromStd: public std::runtime_error
+{
+ FromStd(const std::string& what): std::runtime_error(what) {}
+};
+
+struct FromBoth: public boost::exception, public std::runtime_error
+{
+ FromBoth(const std::string& what): std::runtime_error(what) {}
+};
+
+// Same deal with FromNeither: can't use with BOOST_THROW_EXCEPTION().
+struct FromNeither
+{
+ FromNeither(const std::string& what): mWhat(what) {}
+ std::string what() const { return mWhat; }
+ std::string mWhat;
+};
+
+/*****************************************************************************
+* Two kinds of throws: plain throw and BOOST_THROW_EXCEPTION()
+*****************************************************************************/
+template
+void plain_throw(const std::string& what)
+{
+ throw EXC(what);
+}
+
+template
+void boost_throw(const std::string& what)
+{
+ BOOST_THROW_EXCEPTION(EXC(what));
+}
+
+// Okay, for completeness, functions that throw non-class values. We wouldn't
+// even deign to consider these if we hadn't found examples in our own source
+// code! (Note that Kakadu's internal exception support is still based on
+// throwing ints.)
+void throw_char_ptr(const std::string& what)
+{
+ throw what.c_str(); // umm...
+}
+
+void throw_int(const std::string& what)
+{
+ throw int(what.length());
+}
+
+/*****************************************************************************
+* Three sequences of catch clauses:
+* boost::exception then ...,
+* std::exception then ...,
+* or just ...
+*****************************************************************************/
+void catch_boost_dotdotdot(void (*thrower)(const std::string&), const std::string& what)
+{
+ try
+ {
+ thrower(what);
+ }
+ catch (const boost::exception& e)
+ {
+ std::cout << "catch (const boost::exception& e)" << std::endl;
+ std::cout << "e is " << typeid(e).name() << std::endl;
+ std::cout << "boost::diagnostic_information(e):\n'"
+ << boost::diagnostic_information(e) << "'" << std::endl;
+ // no way to report e.what()
+ }
+ catch (...)
+ {
+ std::cout << "catch (...)" << std::endl;
+ std::cout << "boost::current_exception_diagnostic_information():\n'"
+ << boost::current_exception_diagnostic_information() << "'"
+ << std::endl;
+ }
+}
+
+void catch_std_dotdotdot(void (*thrower)(const std::string&), const std::string& what)
+{
+ try
+ {
+ thrower(what);
+ }
+ catch (const std::exception& e)
+ {
+ std::cout << "catch (const std::exception& e)" << std::endl;
+ std::cout << "e is " << typeid(e).name() << std::endl;
+ std::cout << "boost::diagnostic_information(e):\n'"
+ << boost::diagnostic_information(e) << "'" << std::endl;
+ std::cout << "e.what: '"
+ << e.what() << "'" << std::endl;
+ }
+ catch (...)
+ {
+ std::cout << "catch (...)" << std::endl;
+ std::cout << "boost::current_exception_diagnostic_information():\n'"
+ << boost::current_exception_diagnostic_information() << "'"
+ << std::endl;
+ }
+}
+
+void catch_dotdotdot(void (*thrower)(const std::string&), const std::string& what)
+{
+ try
+ {
+ thrower(what);
+ }
+ catch (...)
+ {
+ std::cout << "catch (...)" << std::endl;
+ std::cout << "boost::current_exception_diagnostic_information():\n'"
+ << boost::current_exception_diagnostic_information() << "'"
+ << std::endl;
+ }
+}
+
+/*****************************************************************************
+* Try a particular kind of throw against each of three catch sequences
+*****************************************************************************/
+void catch_several(void (*thrower)(const std::string&), const std::string& what)
+{
+ std::cout << std::string(20, '-') << "catch_boost_dotdotdot(" << what << ")" << std::endl;
+ catch_boost_dotdotdot(thrower, "catch_boost_dotdotdot(" + what + ")");
+
+ std::cout << std::string(20, '-') << "catch_std_dotdotdot(" << what << ")" << std::endl;
+ catch_std_dotdotdot(thrower, "catch_std_dotdotdot(" + what + ")");
+
+ std::cout << std::string(20, '-') << "catch_dotdotdot(" << what << ")" << std::endl;
+ catch_dotdotdot(thrower, "catch_dotdotdot(" + what + ")");
+}
+
+/*****************************************************************************
+* For a particular kind of exception, try both kinds of throw against all
+* three catch sequences
+*****************************************************************************/
+template
+void catch_both_several(const std::string& what)
+{
+ std::cout << std::string(20, '*') << "plain_throw<" << what << ">" << std::endl;
+ catch_several(plain_throw, "plain_throw<" + what + ">");
+
+ std::cout << std::string(20, '*') << "boost_throw<" << what << ">" << std::endl;
+ catch_several(boost_throw, "boost_throw<" + what + ">");
+}
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ struct llexception_data
+ {
+ };
+ typedef test_group llexception_group;
+ typedef llexception_group::object object;
+ llexception_group llexceptiongrp("llexception");
+
+ template<> template<>
+ void object::test<1>()
+ {
+ set_test_name("throwing exceptions");
+
+ // For each kind of exception, try both kinds of throw against all
+ // three catch sequences
+ std::size_t margin = 72;
+ std::cout << center("FromStd", '=', margin) << std::endl;
+ catch_both_several("FromStd");
+
+ std::cout << center("FromBoth", '=', margin) << std::endl;
+ catch_both_several("FromBoth");
+
+ std::cout << center("FromBoost", '=', margin) << std::endl;
+ // can't throw with BOOST_THROW_EXCEPTION(), just use catch_several()
+ catch_several(plain_throw, "plain_throw");
+
+ std::cout << center("FromNeither", '=', margin) << std::endl;
+ // can't throw this with BOOST_THROW_EXCEPTION() either
+ catch_several(plain_throw, "plain_throw");
+
+ std::cout << center("const char*", '=', margin) << std::endl;
+ // We don't expect BOOST_THROW_EXCEPTION() to throw anything so daft
+ // as a const char* or an int, so don't bother with
+ // catch_both_several() -- just catch_several().
+ catch_several(throw_char_ptr, "throw_char_ptr");
+
+ std::cout << center("int", '=', margin) << std::endl;
+ catch_several(throw_int, "throw_int");
+ }
+} // namespace tut
diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp
index d342dece84..c387da6c48 100644
--- a/indra/llcommon/tests/llleap_test.cpp
+++ b/indra/llcommon/tests/llleap_test.cpp
@@ -110,10 +110,7 @@ namespace tut
// finding indra/lib/python. Use our __FILE__, with
// raw-string syntax to deal with Windows pathnames.
"mydir = os.path.dirname(r'" << __FILE__ << "')\n"
- // We expect mydir to be .../indra/llcommon/tests.
- "sys.path.insert(0,\n"
- " os.path.join(mydir, os.pardir, os.pardir, 'lib', 'python'))\n"
- "from indra.base import llsd\n"
+ "from llbase import llsd\n"
"\n"
"class ProtocolError(Exception):\n"
" def __init__(self, msg, data):\n"
diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp
index 6fbb9abfc0..81b930e1e2 100644
--- a/indra/llcommon/tests/llsdserialize_test.cpp
+++ b/indra/llcommon/tests/llsdserialize_test.cpp
@@ -1518,10 +1518,7 @@ namespace tut
// scanner.
import_llsd("import os.path\n"
"import sys\n"
- "sys.path.insert(0,\n"
- " os.path.join(os.path.dirname(r'" __FILE__ "'),\n"
- " os.pardir, os.pardir, 'lib', 'python'))\n"
- "from indra.base import llsd\n")
+ "from llbase import llsd\n")
{}
~TestPythonCompatible() {}
diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h
index fa16fd6915..9a4bbbd630 100644
--- a/indra/llcommon/tests/wrapllerrs.h
+++ b/indra/llcommon/tests/wrapllerrs.h
@@ -40,7 +40,6 @@
#include
#include
#include
-#include
#include
#include
@@ -79,7 +78,7 @@ struct WrapLLErrs
error = message;
// Also throw an appropriate exception since calling code is likely to
// assume that control won't continue beyond LL_ERRS.
- BOOST_THROW_EXCEPTION(FatalException(message));
+ LLTHROW(FatalException(message));
}
std::string error;
diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py
index 04cde651c4..6c5f37d407 100755
--- a/indra/llcorehttp/tests/test_llcorehttp_peer.py
+++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py
@@ -42,10 +42,8 @@ except ImportError:
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
from SocketServer import ThreadingMixIn
-mydir = os.path.dirname(__file__) # expected to be .../indra/llcorehttp/tests/
-sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "lib", "python"))
-from indra.util.fastest_elementtree import parse as xml_parse
-from indra.base import llsd
+from llbase.fastest_elementtree import parse as xml_parse
+from llbase import llsd
from testrunner import freeport, run, debug, VERBOSE
class TestHTTPRequestHandler(BaseHTTPRequestHandler):
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index feb97ec2ab..f71607096c 100644
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -1426,7 +1426,12 @@ void LLImageRaw::copyScaled( LLImageRaw* src )
bool LLImageRaw::scale( S32 new_width, S32 new_height, bool scale_image_data )
{
- llassert((1 == getComponents()) || (3 == getComponents()) || (4 == getComponents()) );
+ S32 components = getComponents();
+ if (! ((1 == components) || (3 == components) || (4 == components) ))
+ {
+ LL_WARNS() << "Invalid getComponents value (" << components << ")" << LL_ENDL;
+ return false;
+ }
if (isBufferInvalid())
{
@@ -1446,67 +1451,53 @@ bool LLImageRaw::scale( S32 new_width, S32 new_height, bool scale_image_data )
if (scale_image_data)
{
- /*
- S32 temp_data_size = old_width * new_height * getComponents();
- llassert_always(temp_data_size > 0);
- std::vector temp_buffer(temp_data_size);
+ S32 new_data_size = new_width * new_height * components;
- // Vertical
- for( S32 col = 0; col < old_width; col++ )
- {
- copyLineScaled( getData() + (getComponents() * col), &temp_buffer[0] + (getComponents() * col), old_height, new_height, old_width, old_width );
+ if (new_data_size > 0)
+ {
+ U8 *new_data = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), new_data_size);
+ if(NULL == new_data)
+ {
+ return false;
+ }
+
+ bilinear_scale(getData(), old_width, old_height, components, old_width*components, new_data, new_width, new_height, components, new_width*components);
+ setDataAndSize(new_data, new_width, new_height, components);
}
-
- deleteData();
-
- U8* new_buffer = allocateDataSize(new_width, new_height, getComponents());
-
- // Horizontal
- for( S32 row = 0; row < new_height; row++ )
- {
- copyLineScaled( &temp_buffer[0] + (getComponents() * old_width * row), new_buffer + (getComponents() * new_width * row), old_width, new_width, 1, 1 );
- }
- */
-
- S32 new_data_size = new_width * new_height * getComponents();
- llassert_always(new_data_size > 0);
-
- U8 *new_data = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), new_data_size);
- if(NULL == new_data)
- {
- return false;
- }
-
- bilinear_scale(getData(), old_width, old_height, getComponents(), old_width*getComponents(), new_data, new_width, new_height, getComponents(), new_width*getComponents());
- setDataAndSize(new_data, new_width, new_height, getComponents());
}
else
{
// copy out existing image data
- S32 temp_data_size = old_width * old_height * getComponents();
+ S32 temp_data_size = old_width * old_height * components;
std::vector temp_buffer(temp_data_size);
memcpy(&temp_buffer[0], getData(), temp_data_size);
// allocate new image data, will delete old data
- U8* new_buffer = allocateDataSize(new_width, new_height, getComponents());
+ U8* new_buffer = allocateDataSize(new_width, new_height, components);
- for( S32 row = 0; row < new_height; row++ )
- {
- if (row < old_height)
- {
- memcpy(new_buffer + (new_width * row * getComponents()), &temp_buffer[0] + (old_width * row * getComponents()), getComponents() * llmin(old_width, new_width));
- if (old_width < new_width)
- {
- // pad out rest of row with black
- memset(new_buffer + (getComponents() * ((new_width * row) + old_width)), 0, getComponents() * (new_width - old_width));
- }
- }
- else
- {
- // pad remaining rows with black
- memset(new_buffer + (new_width * row * getComponents()), 0, new_width * getComponents());
- }
- }
+ if (!new_buffer)
+ {
+ LL_WARNS() << "Failed to allocate new image data buffer" << LL_ENDL;
+ return false;
+ }
+
+ for( S32 row = 0; row < new_height; row++ )
+ {
+ if (row < old_height)
+ {
+ memcpy(new_buffer + (new_width * row * components), &temp_buffer[0] + (old_width * row * components), components * llmin(old_width, new_width));
+ if (old_width < new_width)
+ {
+ // pad out rest of row with black
+ memset(new_buffer + (components * ((new_width * row) + old_width)), 0, components * (new_width - old_width));
+ }
+ }
+ else
+ {
+ // pad remaining rows with black
+ memset(new_buffer + (new_width * row * components), 0, new_width * components);
+ }
+ }
}
return true ;
diff --git a/indra/llimage/llpngwrapper.cpp b/indra/llimage/llpngwrapper.cpp
index 0b7d4c717f..da289ea889 100644
--- a/indra/llimage/llpngwrapper.cpp
+++ b/indra/llimage/llpngwrapper.cpp
@@ -32,12 +32,12 @@
#include "llpngwrapper.h"
#include "llexception.h"
-#include
namespace {
-struct PngError: public LLException
+// Failure to load an image shouldn't crash the whole viewer.
+struct PngError: public LLContinueError
{
- PngError(png_const_charp msg): LLException(msg) {}
+ PngError(png_const_charp msg): LLContinueError(msg) {}
};
} // anonymous namespace
@@ -88,7 +88,7 @@ BOOL LLPngWrapper::isValidPng(U8* src)
// occurs. We throw PngError and let our try/catch block clean up.
void LLPngWrapper::errorHandler(png_structp png_ptr, png_const_charp msg)
{
- BOOST_THROW_EXCEPTION(PngError(msg));
+ LLTHROW(PngError(msg));
}
// Called by the libpng library when reading (decoding) the PNG file. We
@@ -138,7 +138,7 @@ BOOL LLPngWrapper::readPng(U8* src, S32 dataSize, LLImageRaw* rawImage, ImageInf
this, &errorHandler, NULL);
if (mReadPngPtr == NULL)
{
- BOOST_THROW_EXCEPTION(PngError("Problem creating png read structure"));
+ LLTHROW(PngError("Problem creating png read structure"));
}
// Allocate/initialize the memory for image information.
@@ -297,14 +297,14 @@ BOOL LLPngWrapper::writePng(const LLImageRaw* rawImage, U8* dest)
if (mColorType == -1)
{
- BOOST_THROW_EXCEPTION(PngError("Unsupported image: unexpected number of channels"));
+ LLTHROW(PngError("Unsupported image: unexpected number of channels"));
}
mWritePngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
NULL, &errorHandler, NULL);
if (!mWritePngPtr)
{
- BOOST_THROW_EXCEPTION(PngError("Problem creating png write structure"));
+ LLTHROW(PngError("Problem creating png write structure"));
}
mWriteInfoPtr = png_create_info_struct(mWritePngPtr);
diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp
index d9f0935712..dce4216320 100644
--- a/indra/llkdu/llimagej2ckdu.cpp
+++ b/indra/llkdu/llimagej2ckdu.cpp
@@ -1,4 +1,4 @@
- /**
+/**
* @file llimagej2ckdu.cpp
* @brief This is an implementation of JPEG2000 encode/decode using Kakadu
*
@@ -35,18 +35,51 @@
#include "kdu_block_coding.h"
-#include
-#include
#include "llexception.h"
-#include
+#include
+#include
+#include
namespace {
-// exception used to keep KDU from terminating entire program -- see comments
-// in LLKDUMessageError::flush()
-struct KDUError: public LLException
+// Failure to load an image shouldn't crash the whole viewer.
+struct KDUError: public LLContinueError
{
- KDUError(const std::string& msg): LLException(msg) {}
+ KDUError(const std::string& msg): LLContinueError(msg) {}
};
+
+// KDU defines int error codes as hex values, so we should log them in hex
+// so we can grep KDU headers for the hex. However those hex values
+// generally "happen" to encode big-endian multibyte character sequences,
+// e.g. KDU_ERROR_EXCEPTION is 0x6b647545: 'kduE'
+// But beware because KDU_NULL_EXCEPTION is simply 0 -- which doesn't
+// preclude somebody from throwing it.
+std::string report_kdu_exception(kdu_exception mb)
+{
+ std::ostringstream out;
+ // always report mb in hex
+ out << "kdu_exception " << std::hex << mb;
+
+ // Also display as many chars as are encoded in the kdu_exception
+ // value. Make a char array; reserve 1 extra byte for nul terminator.
+ char bytes[sizeof(kdu_exception) + 1];
+ // Back up through 'bytes'
+ char *bptr = bytes + sizeof(bytes);
+ *(--bptr) = '\0';
+ while (mb)
+ {
+ // store low-order byte of mb in next-left char
+ *(--bptr) = char(mb & 0xFF);
+ // then shift mb right by one byte
+ mb >>= 8;
+ }
+ // did that produce any characters?
+ if (*bptr)
+ {
+ out << " (" << bptr << ')';
+ }
+
+ return out.str();
+}
} // anonymous namespace
// stream kdu_dims to std::ostream
@@ -198,7 +231,7 @@ struct LLKDUMessageError : public LLKDUMessage
// shutdown will NOT engage the behavior described above.
if (end_of_message)
{
- throw "KDU throwing an exception";
+ LLTHROW(KDUError("LLKDUMessageError::flush()"));
}
}
};
@@ -312,10 +345,10 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, bool keep_codestream, ECod
{
// This method is only called from methods that catch KDUError.
// We want to fail the image load, not crash the viewer.
- throw KDUError(STRINGIZE("Component " << idx << " dimensions "
+ LLTHROW(KDUError(STRINGIZE("Component " << idx << " dimensions "
<< other_dims
<< " do not match component 0 dimensions "
- << dims << "!"));
+ << dims << "!")));
}
}
@@ -442,9 +475,19 @@ bool LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
base.setLastError(msg.what());
return false;
}
+ catch (kdu_exception kdu_value)
+ {
+ // KDU internally throws kdu_exception. It's possible that such an
+ // exception might leak out into our code. Catch kdu_exception
+ // specially because boost::current_exception_diagnostic_information()
+ // could do nothing with it.
+ base.setLastError(report_kdu_exception(kdu_value));
+ return false;
+ }
catch (...)
{
- base.setLastError("Unknown J2C error");
+ base.setLastError("Unknown J2C error: " +
+ boost::current_exception_diagnostic_information());
return false;
}
@@ -540,9 +583,21 @@ bool LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
cleanupCodeStream();
return true; // done
}
+ catch (kdu_exception kdu_value)
+ {
+ // KDU internally throws kdu_exception. It's possible that such an
+ // exception might leak out into our code. Catch kdu_exception
+ // specially because boost::current_exception_diagnostic_information()
+ // could do nothing with it.
+ base.setLastError(report_kdu_exception(kdu_value));
+ base.decodeFailed();
+ cleanupCodeStream();
+ return true; // done
+ }
catch (...)
{
- base.setLastError( "Unknown J2C error" );
+ base.setLastError("Unknown J2C error: " +
+ boost::current_exception_diagnostic_information());
base.decodeFailed();
cleanupCodeStream();
return true; // done
@@ -731,9 +786,19 @@ bool LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
base.setLastError(msg.what());
return false;
}
+ catch (kdu_exception kdu_value)
+ {
+ // KDU internally throws kdu_exception. It's possible that such an
+ // exception might leak out into our code. Catch kdu_exception
+ // specially because boost::current_exception_diagnostic_information()
+ // could do nothing with it.
+ base.setLastError(report_kdu_exception(kdu_value));
+ return false;
+ }
catch( ... )
{
- base.setLastError( "Unknown J2C error" );
+ base.setLastError("Unknown J2C error: " +
+ boost::current_exception_diagnostic_information());
return false;
}
@@ -755,9 +820,19 @@ bool LLImageJ2CKDU::getMetadata(LLImageJ2C &base)
base.setLastError(msg.what());
return false;
}
+ catch (kdu_exception kdu_value)
+ {
+ // KDU internally throws kdu_exception. It's possible that such an
+ // exception might leak out into our code. Catch kdu_exception
+ // specially because boost::current_exception_diagnostic_information()
+ // could do nothing with it.
+ base.setLastError(report_kdu_exception(kdu_value));
+ return false;
+ }
catch (...)
{
- base.setLastError( "Unknown J2C error" );
+ base.setLastError("Unknown J2C error: " +
+ boost::current_exception_diagnostic_information());
return false;
}
}
diff --git a/indra/llmath/llmatrix4a.h b/indra/llmath/llmatrix4a.h
index d141298f69..216334752a 100644
--- a/indra/llmath/llmatrix4a.h
+++ b/indra/llmath/llmatrix4a.h
@@ -121,7 +121,7 @@ public:
res.add(z);
}
- inline void affineTransform(const LLVector4a& v, LLVector4a& res)
+ inline void affineTransformSSE(const LLVector4a& v, LLVector4a& res)
{
LLVector4a x,y,z;
@@ -137,6 +137,43 @@ public:
z.add(mMatrix[3]);
res.setAdd(x,z);
}
+
+ inline void affineTransformNonSSE(const LLVector4a& v, LLVector4a& res)
+ {
+ F32 x = v[0] * mMatrix[0][0] + v[1] * mMatrix[1][0] + v[2] * mMatrix[2][0] + mMatrix[3][0];
+ F32 y = v[0] * mMatrix[0][1] + v[1] * mMatrix[1][1] + v[2] * mMatrix[2][1] + mMatrix[3][1];
+ F32 z = v[0] * mMatrix[0][2] + v[1] * mMatrix[1][2] + v[2] * mMatrix[2][2] + mMatrix[3][2];
+ F32 w = 1.0f;
+ res.set(x,y,z,w);
+ }
+
+ inline void affineTransform(const LLVector4a& v, LLVector4a& res)
+ {
+ affineTransformSSE(v,res);
+ }
};
+inline LLVector4a rowMul(const LLVector4a &row, const LLMatrix4a &mat)
+{
+ LLVector4a result;
+ result = _mm_mul_ps(_mm_shuffle_ps(row, row, _MM_SHUFFLE(0, 0, 0, 0)), mat.mMatrix[0]);
+ result = _mm_add_ps(result, _mm_mul_ps(_mm_shuffle_ps(row, row, _MM_SHUFFLE(1, 1, 1, 1)), mat.mMatrix[1]));
+ result = _mm_add_ps(result, _mm_mul_ps(_mm_shuffle_ps(row, row, _MM_SHUFFLE(2, 2, 2, 2)), mat.mMatrix[2]));
+ result = _mm_add_ps(result, _mm_mul_ps(_mm_shuffle_ps(row, row, _MM_SHUFFLE(3, 3, 3, 3)), mat.mMatrix[3]));
+ return result;
+}
+
+inline void matMul(const LLMatrix4a &a, const LLMatrix4a &b, LLMatrix4a &res)
+{
+ LLVector4a row0 = rowMul(a.mMatrix[0], b);
+ LLVector4a row1 = rowMul(a.mMatrix[1], b);
+ LLVector4a row2 = rowMul(a.mMatrix[2], b);
+ LLVector4a row3 = rowMul(a.mMatrix[3], b);
+
+ res.mMatrix[0] = row0;
+ res.mMatrix[1] = row1;
+ res.mMatrix[2] = row2;
+ res.mMatrix[3] = row3;
+}
+
#endif
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index d932eb53a0..6f0b4b2410 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -2143,19 +2143,22 @@ BOOL LLVolume::generate()
F32 profile_detail = mDetail;
F32 path_detail = mDetail;
-
- U8 path_type = mParams.getPathParams().getCurveType();
- U8 profile_type = mParams.getProfileParams().getCurveType();
-
- if (path_type == LL_PCODE_PATH_LINE && profile_type == LL_PCODE_PROFILE_CIRCLE)
- { //cylinders don't care about Z-Axis
- mLODScaleBias.setVec(0.6f, 0.6f, 0.0f);
+
+ if ((mParams.getSculptType() & LL_SCULPT_TYPE_MASK) != LL_SCULPT_TYPE_MESH)
+ {
+ U8 path_type = mParams.getPathParams().getCurveType();
+ U8 profile_type = mParams.getProfileParams().getCurveType();
+ if (path_type == LL_PCODE_PATH_LINE && profile_type == LL_PCODE_PROFILE_CIRCLE)
+ {
+ //cylinders don't care about Z-Axis
+ mLODScaleBias.setVec(0.6f, 0.6f, 0.0f);
+ }
+ else if (path_type == LL_PCODE_PATH_CIRCLE)
+ {
+ mLODScaleBias.setVec(0.6f, 0.6f, 0.6f);
+ }
}
- else if (path_type == LL_PCODE_PATH_CIRCLE)
- {
- mLODScaleBias.setVec(0.6f, 0.6f, 0.6f);
- }
-
+
BOOL regenPath = mPathp->generate(mParams.getPathParams(), path_detail, split);
BOOL regenProf = mProfilep->generate(mParams.getProfileParams(), mPathp->isOpen(),profile_detail, split);
@@ -2544,7 +2547,7 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
U16 influence = weights[idx++];
influence |= ((U16) weights[idx++] << 8);
- F32 w = llclamp((F32) influence / 65535.f, 0.f, 0.99999f);
+ F32 w = llclamp((F32) influence / 65535.f, 0.001f, 0.999f);
wght.mV[cur_influence] = w;
joints[cur_influence] = joint;
cur_influence++;
@@ -2561,11 +2564,15 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
F32 wsum = wght.mV[VX] + wght.mV[VY] + wght.mV[VZ] + wght.mV[VW];
if (wsum <= 0.f)
{
- wght = LLVector4(0.99999f,0.f,0.f,0.f);
+ wght = LLVector4(0.999f,0.f,0.f,0.f);
}
for (U32 k=0; k<4; k++)
{
- joints_with_weights[k] = (F32) joints[k] + wght[k];
+ F32 f_combined = (F32) joints[k] + wght[k];
+ joints_with_weights[k] = f_combined;
+ // Any weights we added above should wind up non-zero and applied to a specific bone.
+ // A failure here would indicate a floating point precision error in the math.
+ llassert((k >= cur_influence) || (f_combined - S32(f_combined) > 0.0f));
}
face.mWeights[cur_vertex].loadua(joints_with_weights.mV);
@@ -4568,6 +4575,7 @@ LLVolumeFace::LLVolumeFace() :
mTexCoords(NULL),
mIndices(NULL),
mWeights(NULL),
+ mWeightsScrubbed(FALSE),
mOctree(NULL),
mOptimized(FALSE)
{
@@ -4593,6 +4601,7 @@ LLVolumeFace::LLVolumeFace(const LLVolumeFace& src)
mTexCoords(NULL),
mIndices(NULL),
mWeights(NULL),
+ mWeightsScrubbed(FALSE),
mOctree(NULL)
{
mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3);
@@ -4664,6 +4673,7 @@ LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src)
ll_aligned_free_16(mWeights);
mWeights = NULL;
}
+ mWeightsScrubbed = src.mWeightsScrubbed;
}
if (mNumIndices)
diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h
index 1da2d0c6b1..d66004cdad 100644
--- a/indra/llmath/llvolume.h
+++ b/indra/llmath/llvolume.h
@@ -953,6 +953,8 @@ public:
// mWeights.size() should be empty or match mVertices.size()
LLVector4a* mWeights;
+ mutable BOOL mWeightsScrubbed;
+
LLOctreeNode* mOctree;
//whether or not face has been cache optimized
diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp
index 1ca5f58ae2..004db546b7 100644
--- a/indra/llmessage/llavatarnamecache.cpp
+++ b/indra/llmessage/llavatarnamecache.cpp
@@ -43,6 +43,8 @@
#include "llcoros.h"
#include "lleventcoro.h"
#include "llcorehttputil.h"
+#include "llexception.h"
+#include "stringize.h"
#include
#include
@@ -231,13 +233,12 @@ void LLAvatarNameCache::requestAvatarNameCache_(std::string url, std::vector
//=========================================================================
@@ -388,14 +390,14 @@ void LLCoprocedurePool::coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdap
{
coproc->mProc(httpAdapter, coproc->mId);
}
- catch (std::exception &e)
- {
- LL_WARNS() << "Coprocedure(" << coproc->mName << ") id=" << coproc->mId.asString() <<
- " threw an exception! Message=\"" << e.what() << "\"" << LL_ENDL;
- }
catch (...)
{
- LL_WARNS() << "A non std::exception was thrown from " << coproc->mName << " with id=" << coproc->mId << "." << " in pool \"" << mPoolName << "\"" << LL_ENDL;
+ LOG_UNHANDLED_EXCEPTION(STRINGIZE("Coprocedure('" << coproc->mName
+ << "', id=" << coproc->mId.asString()
+ << ") in pool '" << mPoolName << "'"));
+ // must NOT omit this or we deplete the pool
+ mActiveCoprocs.erase(itActive);
+ throw;
}
LL_INFOS() << "Finished coprocedure(" << coproc->mName << ")" << " in pool \"" << mPoolName << "\"" << LL_ENDL;
diff --git a/indra/llmessage/llcorehttputil.cpp b/indra/llmessage/llcorehttputil.cpp
index 7742cbc182..9bf38fb336 100644
--- a/indra/llmessage/llcorehttputil.cpp
+++ b/indra/llmessage/llcorehttputil.cpp
@@ -642,7 +642,7 @@ HttpRequestPumper::HttpRequestPumper(const LLCore::HttpRequest::ptr_t &request)
mHttpRequest(request)
{
mBoundListener = LLEventPumps::instance().obtain("mainloop").
- listen(LLEventPump::inventName(), boost::bind(&HttpRequestPumper::pollRequest, this, _1));
+ listen(LLEventPump::ANONYMOUS, boost::bind(&HttpRequestPumper::pollRequest, this, _1));
}
HttpRequestPumper::~HttpRequestPumper()
diff --git a/indra/llmessage/llhttpnode.cpp b/indra/llmessage/llhttpnode.cpp
index 48ce258ba2..04b34a296c 100644
--- a/indra/llmessage/llhttpnode.cpp
+++ b/indra/llmessage/llhttpnode.cpp
@@ -32,7 +32,6 @@
#include "llstl.h"
#include "llhttpconstants.h"
#include "llexception.h"
-#include
const std::string CONTEXT_HEADERS("headers");
const std::string CONTEXT_PATH("path");
@@ -103,19 +102,19 @@ namespace {
// virtual
LLSD LLHTTPNode::simpleGet() const
{
- BOOST_THROW_EXCEPTION(NotImplemented());
+ LLTHROW(NotImplemented());
}
// virtual
LLSD LLHTTPNode::simplePut(const LLSD& input) const
{
- BOOST_THROW_EXCEPTION(NotImplemented());
+ LLTHROW(NotImplemented());
}
// virtual
LLSD LLHTTPNode::simplePost(const LLSD& input) const
{
- BOOST_THROW_EXCEPTION(NotImplemented());
+ LLTHROW(NotImplemented());
}
@@ -175,7 +174,7 @@ void LLHTTPNode::del(LLHTTPNode::ResponsePtr response, const LLSD& context) cons
// virtual
LLSD LLHTTPNode::simpleDel(const LLSD&) const
{
- BOOST_THROW_EXCEPTION(NotImplemented());
+ LLTHROW(NotImplemented());
}
// virtual
diff --git a/indra/llmessage/llxfer.cpp b/indra/llmessage/llxfer.cpp
index 4aba5cae72..e0590dfdff 100644
--- a/indra/llmessage/llxfer.cpp
+++ b/indra/llmessage/llxfer.cpp
@@ -294,7 +294,7 @@ S32 LLXfer::processEOF()
}
else
{
- LL_INFOS() << "xfer from " << mRemoteHost << " failed, code "
+ LL_INFOS() << "xfer from " << mRemoteHost << " failed or aborted, code "
<< mCallbackResult << ": " << getFileName() << LL_ENDL;
}
diff --git a/indra/llmessage/llxfermanager.cpp b/indra/llmessage/llxfermanager.cpp
index 0ab67b8dda..272dbbc785 100644
--- a/indra/llmessage/llxfermanager.cpp
+++ b/indra/llmessage/llxfermanager.cpp
@@ -401,7 +401,7 @@ U64 LLXferManager::registerXfer(const void *datap, const S32 length)
///////////////////////////////////////////////////////////
-void LLXferManager::requestFile(const std::string& local_filename,
+U64 LLXferManager::requestFile(const std::string& local_filename,
const std::string& remote_filename,
ELLPath remote_path,
const LLHost& remote_host,
@@ -424,10 +424,12 @@ void LLXferManager::requestFile(const std::string& local_filename,
{
// cout << "requested a xfer already in progress" << endl;
- return;
+ return xferp->mID;
}
}
+ U64 xfer_id = 0;
+
S32 chunk_size = use_big_packets ? LL_XFER_LARGE_PAYLOAD : -1;
xferp = (LLXfer *) new LLXfer_File(chunk_size);
if (xferp)
@@ -438,13 +440,15 @@ void LLXferManager::requestFile(const std::string& local_filename,
// around.
// Note: according to AaronB, this is here to deal with locks on files that were
// in transit during a crash,
- if(delete_remote_on_completion &&
- (remote_filename.substr(remote_filename.length()-4) == ".tmp"))
+ if( delete_remote_on_completion
+ && (remote_filename.substr(remote_filename.length()-4) == ".tmp")
+ && gDirUtilp->fileExists(local_filename))
{
LLFile::remove(local_filename);
}
+ xfer_id = getNextID();
((LLXfer_File *)xferp)->initializeRequest(
- getNextID(),
+ xfer_id,
local_filename,
remote_filename,
remote_path,
@@ -457,6 +461,7 @@ void LLXferManager::requestFile(const std::string& local_filename,
{
LL_ERRS() << "Xfer allocation error" << LL_ENDL;
}
+ return xfer_id;
}
void LLXferManager::requestFile(const std::string& remote_filename,
@@ -616,7 +621,7 @@ void LLXferManager::processReceiveData (LLMessageSystem *mesgsys, void ** /*user
if (!xferp)
{
char U64_BUF[MAX_STRING]; /* Flawfinder : ignore */
- LL_WARNS() << "received xfer data from " << mesgsys->getSender()
+ LL_INFOS() << "received xfer data from " << mesgsys->getSender()
<< " for non-existent xfer id: "
<< U64_to_str(id, U64_BUF, sizeof(U64_BUF)) << LL_ENDL;
return;
@@ -1103,6 +1108,29 @@ void LLXferManager::retransmitUnackedPackets ()
}
}
+///////////////////////////////////////////////////////////
+
+void LLXferManager::abortRequestById(U64 xfer_id, S32 result_code)
+{
+ LLXfer * xferp = findXfer(xfer_id, mReceiveList);
+ if (xferp)
+ {
+ if (xferp->mStatus == e_LL_XFER_IN_PROGRESS)
+ {
+ // causes processAbort();
+ xferp->abort(result_code);
+ }
+ else
+ {
+ xferp->mCallbackResult = result_code;
+ xferp->processEOF(); //should notify requester
+ removeXfer(xferp, &mReceiveList);
+ }
+ // Since already removed or marked as aborted no need
+ // to wait for processAbort() to start new download
+ startPendingDownloads();
+ }
+}
///////////////////////////////////////////////////////////
diff --git a/indra/llmessage/llxfermanager.h b/indra/llmessage/llxfermanager.h
index b3d110e7a1..d258f0a5ce 100644
--- a/indra/llmessage/llxfermanager.h
+++ b/indra/llmessage/llxfermanager.h
@@ -140,7 +140,7 @@ class LLXferManager
// file requesting routines
// .. to file
- virtual void requestFile(const std::string& local_filename,
+ virtual U64 requestFile(const std::string& local_filename,
const std::string& remote_filename,
ELLPath remote_path,
const LLHost& remote_host,
@@ -202,6 +202,7 @@ class LLXferManager
virtual void retransmitUnackedPackets ();
// error handling
+ void abortRequestById(U64 xfer_id, S32 result_code);
virtual void processAbort (LLMessageSystem *mesgsys, void **user_data);
};
diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp
index cecb2021e7..290b67feb3 100644
--- a/indra/llmessage/message.cpp
+++ b/indra/llmessage/message.cpp
@@ -4007,7 +4007,7 @@ void LLMessageSystem::sendUntrustedSimulatorMessageCoro(std::string url, std::st
{
LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
- httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("groupMembersRequest", httpPolicy));
+ httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("untrustedSimulatorMessage", httpPolicy));
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions);
diff --git a/indra/llmessage/tests/commtest.h b/indra/llmessage/tests/commtest.h
index 5dff56b44f..7c8f27bbd2 100644
--- a/indra/llmessage/tests/commtest.h
+++ b/indra/llmessage/tests/commtest.h
@@ -38,7 +38,6 @@
#include
#include
#include
-#include
struct CommtestError: public LLException
{
@@ -69,7 +68,7 @@ static int query_port(const std::string& var)
const char* cport = getenv(var.c_str());
if (! cport)
{
- BOOST_THROW_EXCEPTION(CommtestError(STRINGIZE("missing environment variable" << var)));
+ LLTHROW(CommtestError(STRINGIZE("missing environment variable" << var)));
}
// This will throw, too, if the value of PORT isn't numeric.
int port(boost::lexical_cast(cport));
diff --git a/indra/llmessage/tests/networkio.h b/indra/llmessage/tests/networkio.h
index 6aaecf9bac..5eb739393f 100644
--- a/indra/llmessage/tests/networkio.h
+++ b/indra/llmessage/tests/networkio.h
@@ -35,7 +35,6 @@
#include "llpumpio.h"
#include "llhttpclient.h"
#include "llexception.h"
-#include
/*****************************************************************************
* NetworkIO
@@ -53,7 +52,7 @@ public:
ll_init_apr();
if (! gAPRPoolp)
{
- BOOST_THROW_EXCEPTION(LLException("Can't initialize APR"));
+ LLTHROW(LLException("Can't initialize APR"));
}
// Create IO Pump to use for HTTP Requests.
@@ -61,7 +60,7 @@ public:
LLHTTPClient::setPump(*mServicePump);
if (ll_init_ares() == NULL || !gAres->isInitialized())
{
- BOOST_THROW_EXCEPTION(LLException("Can't start DNS resolver"));
+ LLTHROW(LLException("Can't start DNS resolver"));
}
// You can interrupt pump() without waiting the full timeout duration
diff --git a/indra/llmessage/tests/test_llsdmessage_peer.py b/indra/llmessage/tests/test_llsdmessage_peer.py
index e45249b1cb..bac18fa374 100755
--- a/indra/llmessage/tests/test_llsdmessage_peer.py
+++ b/indra/llmessage/tests/test_llsdmessage_peer.py
@@ -34,10 +34,8 @@ import sys
from threading import Thread
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
-mydir = os.path.dirname(__file__) # expected to be .../indra/llmessage/tests/
-sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "lib", "python"))
-from indra.util.fastest_elementtree import parse as xml_parse
-from indra.base import llsd
+from llbase.fastest_elementtree import parse as xml_parse
+from llbase import llsd
from testrunner import freeport, run, debug, VERBOSE
import time
diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp
index 3d173d0459..f1b6fe0a12 100644
--- a/indra/llplugin/llpluginclassmedia.cpp
+++ b/indra/llplugin/llpluginclassmedia.cpp
@@ -73,6 +73,7 @@ bool LLPluginClassMedia::init(const std::string &launcher_filename, const std::s
// Queue up the media init message -- it will be sent after all the currently queued messages.
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "init");
message.setValue("target", mTarget);
+ message.setValueReal("factor", mZoomFactor);
sendMessage(message);
mPlugin->init(launcher_filename, plugin_dir, plugin_filename, debug);
@@ -116,7 +117,7 @@ void LLPluginClassMedia::reset()
mMediaHeight = 0;
mDirtyRect = LLRect::null;
mAutoScaleMedia = false;
- mRequestedVolume = 1.0f;
+ mRequestedVolume = 0.0f;
mPriority = PRIORITY_NORMAL;
mLowPrioritySizeLimit = LOW_PRIORITY_TEXTURE_SIZE_DEFAULT;
mAllowDownsample = false;
@@ -1259,7 +1260,7 @@ void LLPluginClassMedia::focus(bool focused)
sendMessage(message);
}
-void LLPluginClassMedia::set_page_zoom_factor( double factor )
+void LLPluginClassMedia::set_page_zoom_factor( F64 factor )
{
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "set_page_zoom_factor");
diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h
index fc27b7bea3..3b0739d044 100644
--- a/indra/llplugin/llpluginclassmedia.h
+++ b/indra/llplugin/llpluginclassmedia.h
@@ -68,6 +68,7 @@ public:
int getTextureHeight() const;
int getFullWidth() const { return mFullMediaWidth; };
int getFullHeight() const { return mFullMediaHeight; };
+ F64 getZoomFactor() const { return mZoomFactor; };
// This may return NULL. Callers need to check for and handle this case.
unsigned char* getBitsData();
@@ -83,7 +84,8 @@ public:
void setSize(int width, int height);
void setAutoScale(bool auto_scale);
-
+ void setZoomFactor(F64 zoom_factor) { mZoomFactor = zoom_factor; }
+
void setBackgroundColor(LLColor4 color) { mBackgroundColor = color; };
void setOwner(LLPluginClassMediaOwner *owner) { mOwner = owner; };
@@ -204,7 +206,7 @@ public:
bool pluginSupportsMediaBrowser(void);
void focus(bool focused);
- void set_page_zoom_factor( double factor );
+ void set_page_zoom_factor( F64 factor );
void clear_cache();
void clear_cookies();
void set_cookies(const std::string &cookies);
@@ -367,6 +369,8 @@ protected:
int mTextureHeight;
int mMediaWidth;
int mMediaHeight;
+
+ F64 mZoomFactor;
float mRequestedVolume;
diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 00bde8dbc3..d73bdd7693 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -807,17 +807,19 @@ LLModel::EModelStatus load_face_from_dom_polygons(std::vector& fac
// LLDAELoader
//-----------------------------------------------------------------------------
LLDAELoader::LLDAELoader(
- std::string filename,
- S32 lod,
+ std::string filename,
+ S32 lod,
load_callback_t load_cb,
joint_lookup_func_t joint_lookup_func,
texture_load_func_t texture_load_func,
- state_callback_t state_cb,
- void* opaque_userdata,
- JointTransformMap& jointMap,
- JointSet& jointsFromNodes,
+ state_callback_t state_cb,
+ void* opaque_userdata,
+ JointTransformMap& jointTransformMap,
+ JointNameSet& jointsFromNodes,
+ std::map& jointAliasMap,
+ U32 maxJointsPerMesh,
U32 modelLimit,
- bool preprocess)
+ bool preprocess)
: LLModelLoader(
filename,
lod,
@@ -826,10 +828,12 @@ LLDAELoader::LLDAELoader(
texture_load_func,
state_cb,
opaque_userdata,
- jointMap,
- jointsFromNodes),
-mGeneratedModelLimit(modelLimit),
-mPreprocessDAE(preprocess)
+ jointTransformMap,
+ jointsFromNodes,
+ jointAliasMap,
+ maxJointsPerMesh),
+ mGeneratedModelLimit(modelLimit),
+ mPreprocessDAE(preprocess)
{
}
@@ -1071,7 +1075,7 @@ bool LLDAELoader::OpenFile(const std::string& filename)
std::string LLDAELoader::preprocessDAE(std::string filename)
{
// Open a DAE file for some preprocessing (like removing space characters in IDs), see MAINT-5678
- std::ifstream inFile;
+ llifstream inFile;
inFile.open(filename.c_str(), std::ios_base::in);
std::stringstream strStream;
strStream << inFile.rdbuf();
@@ -1148,28 +1152,29 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
//Some collada setup for accessing the skeleton
- daeElement* pElement = 0;
- dae->getDatabase()->getElement( &pElement, 0, 0, "skeleton" );
+ U32 skeleton_count = dae->getDatabase()->getElementCount( NULL, "skeleton" );
+ std::vector skeletons;
+ for (S32 i=0; igetDatabase()->getElement( &pElement, i, 0, "skeleton" );
- //Try to get at the skeletal instance controller
- domInstance_controller::domSkeleton* pSkeleton = daeSafeCast( pElement );
+ //Try to get at the skeletal instance controller
+ domInstance_controller::domSkeleton* pSkeleton = daeSafeCast( pElement );
+ daeElement* pSkeletonRootNode = NULL;
+ if (pSkeleton)
+ {
+ pSkeletonRootNode = pSkeleton->getValue().getElement();
+ }
+ if (pSkeleton && pSkeletonRootNode)
+ {
+ skeletons.push_back(pSkeleton);
+ }
+ }
bool missingSkeletonOrScene = false;
//If no skeleton, do a breadth-first search to get at specific joints
- bool rootNode = false;
-
- //Need to test for a skeleton that does not have a root node
- //This occurs when your instance controller does not have an associated scene
- if ( pSkeleton )
- {
- daeElement* pSkeletonRootNode = pSkeleton->getValue().getElement();
- if ( pSkeletonRootNode )
- {
- rootNode = true;
- }
-
- }
- if ( !pSkeleton || !rootNode )
+ if ( skeletons.size() == 0 )
{
daeElement* pScene = root->getDescendant("visual_scene");
if ( !pScene )
@@ -1184,7 +1189,7 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
S32 childCount = children.getCount();
//Process any children that are joints
- //Not all children are joints, some code be ambient lights, cameras, geometry etc..
+ //Not all children are joints, some could be ambient lights, cameras, geometry etc..
for (S32 i = 0; i < childCount; ++i)
{
domNode* pNode = daeSafeCast(children[i]);
@@ -1196,83 +1201,90 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
}
}
else
- //Has Skeleton
- {
- //Get the root node of the skeleton
- daeElement* pSkeletonRootNode = pSkeleton->getValue().getElement();
- if ( pSkeletonRootNode )
- {
- //Once we have the root node - start acccessing it's joint components
- const int jointCnt = mJointMap.size();
- JointMap :: const_iterator jointIt = mJointMap.begin();
+ //Has one or more skeletons
+ for (std::vector::iterator skel_it = skeletons.begin();
+ skel_it != skeletons.end(); ++skel_it)
+ {
+ domInstance_controller::domSkeleton* pSkeleton = *skel_it;
+ //Get the root node of the skeleton
+ daeElement* pSkeletonRootNode = pSkeleton->getValue().getElement();
+ if ( pSkeletonRootNode )
+ {
+ //Once we have the root node - start acccessing it's joint components
+ const int jointCnt = mJointMap.size();
+ JointMap :: const_iterator jointIt = mJointMap.begin();
- //Loop over all the possible joints within the .dae - using the allowed joint list in the ctor.
- for ( int i=0; i( resolver.getElement() );
- if ( pJoint )
- {
- //Pull out the translate id and store it in the jointTranslations map
- daeSIDResolver jointResolverA( pJoint, "./translate" );
- domTranslate* pTranslateA = daeSafeCast( jointResolverA.getElement() );
- daeSIDResolver jointResolverB( pJoint, "./location" );
- domTranslate* pTranslateB = daeSafeCast( jointResolverB.getElement() );
+ //Look for the joint
+ domNode* pJoint = daeSafeCast( resolver.getElement() );
+ if ( pJoint )
+ {
+ // FIXME this has a lot of overlap with processJointNode(), would be nice to refactor.
- LLMatrix4 workingTransform;
+ //Pull out the translate id and store it in the jointTranslations map
+ daeSIDResolver jointResolverA( pJoint, "./translate" );
+ domTranslate* pTranslateA = daeSafeCast( jointResolverA.getElement() );
+ daeSIDResolver jointResolverB( pJoint, "./location" );
+ domTranslate* pTranslateB = daeSafeCast( jointResolverB.getElement() );
- //Translation via SID
- if ( pTranslateA )
- {
- extractTranslation( pTranslateA, workingTransform );
- }
- else
- if ( pTranslateB )
- {
- extractTranslation( pTranslateB, workingTransform );
- }
- else
- {
- //Translation via child from element
- daeElement* pTranslateElement = getChildFromElement( pJoint, "translate" );
- if ( pTranslateElement && pTranslateElement->typeID() != domTranslate::ID() )
- {
- LL_WARNS()<< "The found element is not a translate node" <typeID() != domTranslate::ID() )
+ {
+ LL_WARNS()<< "The found element is not a translate node" <getJoints();
@@ -1307,7 +1319,7 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
name = mJointMap[name];
}
model->mSkinInfo.mJointNames.push_back(name);
- model->mSkinInfo.mJointMap[name] = j;
+ model->mSkinInfo.mJointNums.push_back(-1);
}
}
else
@@ -1325,7 +1337,7 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
name = mJointMap[name];
}
model->mSkinInfo.mJointNames.push_back(name);
- model->mSkinInfo.mJointMap[name] = j;
+ model->mSkinInfo.mJointNums.push_back(-1);
}
}
}
@@ -1353,8 +1365,7 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
mat.mMatrix[i][j] = transform[k*16 + i + j*4];
}
}
-
- model->mSkinInfo.mInvBindMatrix.push_back(mat);
+ model->mSkinInfo.mInvBindMatrix.push_back(mat);
}
}
}
@@ -1370,35 +1381,48 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
if ( !missingSkeletonOrScene )
{
- //Set the joint translations on the avatar - if it's a full mapping
- //The joints are reset in the dtor
- if ( getRigWithSceneParity() )
- {
- JointMap :: const_iterator masterJointIt = mJointMap.begin();
- JointMap :: const_iterator masterJointItEnd = mJointMap.end();
- for (;masterJointIt!=masterJointItEnd;++masterJointIt )
- {
- std::string lookingForJoint = (*masterJointIt).first.c_str();
+ // FIXME: mesh_id is used to determine which mesh gets to
+ // set the joint offset, in the event of a conflict. Since
+ // we don't know the mesh id yet, we can't guarantee that
+ // joint offsets will be applied with the same priority as
+ // in the uploaded model. If the file contains multiple
+ // meshes with conflicting joint offsets, preview may be
+ // incorrect.
+ LLUUID fake_mesh_id;
+ fake_mesh_id.generate();
- if ( mJointList.find( lookingForJoint ) != mJointList.end() )
- {
- //LL_INFOS()<<"joint "<addAttachmentPosOverride( jointTransform.getTranslation(), fake_mesh_id, "");
- }
- else
- {
- //Most likely an error in the asset.
- LL_WARNS()<<"Tried to apply joint position from .dae, but it did not exist in the avatar rig." << LL_ENDL;
- }
- }
- }
- }
+ //Set the joint translations on the avatar
+ JointMap :: const_iterator masterJointIt = mJointMap.begin();
+ JointMap :: const_iterator masterJointItEnd = mJointMap.end();
+ for (;masterJointIt!=masterJointItEnd;++masterJointIt )
+ {
+ std::string lookingForJoint = (*masterJointIt).first.c_str();
+
+ if ( mJointList.find( lookingForJoint ) != mJointList.end() )
+ {
+ //LL_INFOS()<<"joint "<aboveJointPosThreshold(joint_pos))
+ {
+ bool override_changed; // not used
+ pJoint->addAttachmentPosOverride(joint_pos, fake_mesh_id, "", override_changed);
+ if (model->mSkinInfo.mLockScaleIfJointPosition)
+ {
+ pJoint->addAttachmentScaleOverride(pJoint->getDefaultScale(), fake_mesh_id, "");
+ }
+ }
+ }
+ else
+ {
+ //Most likely an error in the asset.
+ LL_WARNS()<<"Tried to apply joint position from .dae, but it did not exist in the avatar rig." << LL_ENDL;
+ }
+ }
+ }
} //missingSkeletonOrScene
//We need to construct the alternate bind matrix (which contains the new joint positions)
@@ -1412,16 +1436,15 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
std::string lookingForJoint = (*jointIt).c_str();
//Look for the joint xform that we extracted from the skeleton, using the jointIt as the key
//and store it in the alternate bind matrix
- if ( mJointList.find( lookingForJoint ) != mJointList.end() )
+ if ( mJointMap.find( lookingForJoint ) != mJointMap.end() )
{
- LLMatrix4 jointTransform = mJointList[lookingForJoint];
LLMatrix4 newInverse = model->mSkinInfo.mInvBindMatrix[i];
newInverse.setTranslation( mJointList[lookingForJoint].getTranslation() );
model->mSkinInfo.mAlternateBindMatrix.push_back( newInverse );
- }
+ }
else
{
- LL_WARNS()<<"Possibly misnamed/missing joint [" <getAttribute("id") << "\"" << LL_ENDL;
+ LL_DEBUGS("Mesh")<< "Could not find a child [" << name << "] for the element: \"" << pElement->getAttribute("id") << "\"" << LL_ENDL;
return NULL;
}
diff --git a/indra/llprimitive/lldaeloader.h b/indra/llprimitive/lldaeloader.h
index 27db5326d5..4e990dbe5e 100644
--- a/indra/llprimitive/lldaeloader.h
+++ b/indra/llprimitive/lldaeloader.h
@@ -47,17 +47,19 @@ public:
dae_model_map mModelsMap;
LLDAELoader(
- std::string filename,
- S32 lod,
+ std::string filename,
+ S32 lod,
LLModelLoader::load_callback_t load_cb,
LLModelLoader::joint_lookup_func_t joint_lookup_func,
LLModelLoader::texture_load_func_t texture_load_func,
LLModelLoader::state_callback_t state_cb,
- void* opaque_userdata,
- JointTransformMap& jointMap,
- JointSet& jointsFromNodes,
+ void* opaque_userdata,
+ JointTransformMap& jointTransformMap,
+ JointNameSet& jointsFromNodes,
+ std::map& jointAliasMap,
+ U32 maxJointsPerMesh,
U32 modelLimit,
- bool preprocess);
+ bool preprocess);
virtual ~LLDAELoader() ;
virtual bool OpenFile(const std::string& filename);
diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
index e494c55250..db6d00bc2c 100644
--- a/indra/llprimitive/llmodel.cpp
+++ b/indra/llprimitive/llmodel.cpp
@@ -50,8 +50,12 @@ std::string model_names[] =
const int MODEL_NAMES_LENGTH = sizeof(model_names) / sizeof(std::string);
LLModel::LLModel(LLVolumeParams& params, F32 detail)
- : LLVolume(params, detail), mNormalizedScale(1,1,1), mNormalizedTranslation(0,0,0)
- , mPelvisOffset( 0.0f ), mStatus(NO_ERRORS), mSubmodelID(0)
+ : LLVolume(params, detail),
+ mNormalizedScale(1,1,1),
+ mNormalizedTranslation(0,0,0),
+ mPelvisOffset( 0.0f ),
+ mStatus(NO_ERRORS),
+ mSubmodelID(0)
{
mDecompID = -1;
mLocalID = -1;
@@ -667,6 +671,7 @@ LLSD LLModel::writeModel(
const LLModel::Decomposition& decomp,
BOOL upload_skin,
BOOL upload_joints,
+ BOOL lock_scale_if_joint_position,
BOOL nowrite,
BOOL as_slm,
int submodel_id)
@@ -686,7 +691,7 @@ LLSD LLModel::writeModel(
if (skinning)
{ //write skinning block
- mdl["skin"] = high->mSkinInfo.asLLSD(upload_joints);
+ mdl["skin"] = high->mSkinInfo.asLLSD(upload_joints, lock_scale_if_joint_position);
}
if (!decomp.mBaseHull.empty() ||
@@ -867,6 +872,7 @@ LLSD LLModel::writeModel(
S32 count = 0;
for (weight_list::iterator iter = weights.begin(); iter != weights.end(); ++iter)
{
+ // Note joint index cannot exceed 255.
if (iter->mJointIdx < 255 && iter->mJointIdx >= 0)
{
U8 idx = (U8) iter->mJointIdx;
@@ -1000,7 +1006,7 @@ LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite, BO
LLModel::weight_list& LLModel::getJointInfluences(const LLVector3& pos)
{
- //1. If a vertex has been weighted then we'll find it via pos and return it's weight list
+ //1. If a vertex has been weighted then we'll find it via pos and return its weight list
weight_map::iterator iterPos = mSkinWeights.begin();
weight_map::iterator iterEnd = mSkinWeights.end();
@@ -1223,7 +1229,6 @@ bool LLModel::loadModel(std::istream& is)
}
return false;
-
}
bool LLModel::isMaterialListSubset( LLModel* ref )
@@ -1338,7 +1343,6 @@ bool LLModel::matchMaterialOrder(LLModel* ref, int& refFaceCnt, int& modelFaceCn
return true;
}
-
bool LLModel::loadSkinInfo(LLSD& header, std::istream &is)
{
S32 offset = header["skin"]["offset"].asInteger();
@@ -1381,8 +1385,17 @@ bool LLModel::loadDecomposition(LLSD& header, std::istream& is)
return true;
}
+LLMeshSkinInfo::LLMeshSkinInfo():
+ mPelvisOffset(0.0),
+ mLockScaleIfJointPosition(false),
+ mInvalidJointsScrubbed(false)
+{
+}
-LLMeshSkinInfo::LLMeshSkinInfo(LLSD& skin)
+LLMeshSkinInfo::LLMeshSkinInfo(LLSD& skin):
+ mPelvisOffset(0.0),
+ mLockScaleIfJointPosition(false),
+ mInvalidJointsScrubbed(false)
{
fromLLSD(skin);
}
@@ -1394,6 +1407,7 @@ void LLMeshSkinInfo::fromLLSD(LLSD& skin)
for (U32 i = 0; i < skin["joint_names"].size(); ++i)
{
mJointNames.push_back(skin["joint_names"][i]);
+ mJointNums.push_back(-1);
}
}
@@ -1446,9 +1460,18 @@ void LLMeshSkinInfo::fromLLSD(LLSD& skin)
{
mPelvisOffset = skin["pelvis_offset"].asReal();
}
+
+ if (skin.has("lock_scale_if_joint_position"))
+ {
+ mLockScaleIfJointPosition = skin["lock_scale_if_joint_position"].asBoolean();
+ }
+ else
+ {
+ mLockScaleIfJointPosition = false;
+ }
}
-LLSD LLMeshSkinInfo::asLLSD(bool include_joints) const
+LLSD LLMeshSkinInfo::asLLSD(bool include_joints, bool lock_scale_if_joint_position) const
{
LLSD ret;
@@ -1486,6 +1509,11 @@ LLSD LLMeshSkinInfo::asLLSD(bool include_joints) const
}
}
+ if (lock_scale_if_joint_position)
+ {
+ ret["lock_scale_if_joint_position"] = lock_scale_if_joint_position;
+ }
+
ret["pelvis_offset"] = mPelvisOffset;
}
diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h
index ae602c09df..097558ef67 100644
--- a/indra/llprimitive/llmodel.h
+++ b/indra/llprimitive/llmodel.h
@@ -42,18 +42,21 @@ class domMesh;
class LLMeshSkinInfo
{
public:
- LLUUID mMeshID;
- std::vector mJointNames;
- std::vector mInvBindMatrix;
- std::vector mAlternateBindMatrix;
- std::map mJointMap;
-
- LLMeshSkinInfo() { }
+ LLMeshSkinInfo();
LLMeshSkinInfo(LLSD& data);
void fromLLSD(LLSD& data);
- LLSD asLLSD(bool include_joints) const;
+ LLSD asLLSD(bool include_joints, bool lock_scale_if_joint_position) const;
+
+ LLUUID mMeshID;
+ std::vector mJointNames;
+ mutable std::vector