Merge branch 'contribute' of https://github.com/secondlife/viewer
# Conflicts: # autobuild.xml # indra/llcommon/llsdserialize.cpp # indra/llcommon/lluuid.cpp # indra/llcommon/lluuid.h # indra/llprimitive/llmodel.cpp # indra/newview/llfilepicker.cpp # indra/newview/llfilepicker.h # indra/newview/llfilepicker_mac.h # indra/newview/llfilepicker_mac.mm # indra/newview/llinventorymodel.cpp # indra/newview/llviewerwearable.h # indra/newview/skins/default/xui/da/floater_about.xml # indra/newview/skins/default/xui/da/floater_fs_voice_controls.xml # indra/newview/skins/default/xui/da/menu_inspect_self_gear.xml # indra/newview/skins/default/xui/de/floater_about.xml # indra/newview/skins/default/xui/de/floater_perm_prefs.xml # indra/newview/skins/default/xui/de/menu_inspect_avatar_gear.xml # indra/newview/skins/default/xui/de/menu_inspect_self_gear.xml # indra/newview/skins/default/xui/en/floater_about.xml # indra/newview/skins/default/xui/en/strings.xml # indra/newview/skins/default/xui/es/floater_about.xml # indra/newview/skins/default/xui/es/menu_inspect_avatar_gear.xml # indra/newview/skins/default/xui/es/menu_inspect_self_gear.xml # indra/newview/skins/default/xui/fr/floater_about.xml # indra/newview/skins/default/xui/fr/floater_perm_prefs.xml # indra/newview/skins/default/xui/fr/menu_inspect_avatar_gear.xml # indra/newview/skins/default/xui/fr/menu_inspect_self_gear.xml # indra/newview/skins/default/xui/it/floater_about.xml # indra/newview/skins/default/xui/it/floater_perm_prefs.xml # indra/newview/skins/default/xui/it/menu_inspect_avatar_gear.xml # indra/newview/skins/default/xui/it/menu_inspect_self_gear.xml # indra/newview/skins/default/xui/ja/floater_fs_voice_controls.xml # indra/newview/skins/default/xui/ja/menu_inspect_self_gear.xml # indra/newview/skins/default/xui/pl/floater_about.xml # indra/newview/skins/default/xui/pl/floater_about_land.xml # indra/newview/skins/default/xui/pl/floater_animation_bvh_preview.xml # indra/newview/skins/default/xui/pl/floater_associate_listing.xml # indra/newview/skins/default/xui/pl/notifications.xml # indra/newview/skins/default/xui/pl/panel_group_creation_sidetray.xml # indra/newview/skins/default/xui/pl/panel_outfit_gallery.xml # indra/newview/skins/default/xui/pl/panel_outfit_snapshot_inventory.xml # indra/newview/skins/default/xui/pl/panel_profile_firstlife.xml # indra/newview/skins/default/xui/pl/panel_profile_pick.xml # indra/newview/skins/default/xui/pl/panel_profile_picks.xml # indra/newview/skins/default/xui/pl/panel_profile_secondlife.xml # indra/newview/skins/default/xui/pl/panel_region_access.xml # indra/newview/skins/default/xui/pl/panel_script_experience.xml # indra/newview/skins/default/xui/pl/strings.xml # indra/newview/skins/default/xui/pt/floater_about.xml # indra/newview/skins/default/xui/pt/floater_fs_voice_controls.xml # indra/newview/skins/default/xui/pt/menu_inspect_self_gear.xml # indra/newview/skins/default/xui/ru/floater_about.xml # indra/newview/skins/default/xui/ru/floater_perm_prefs.xml # indra/newview/skins/default/xui/ru/menu_inspect_avatar_gear.xml # indra/newview/skins/default/xui/ru/menu_inspect_self_gear.xml # indra/newview/skins/default/xui/tr/floater_about.xml # indra/newview/skins/default/xui/tr/floater_fs_voice_controls.xml # indra/newview/skins/default/xui/tr/menu_inspect_self_gear.xml # indra/newview/skins/default/xui/zh/floater_about.xml # indra/newview/skins/default/xui/zh/floater_fs_voice_controls.xml # indra/newview/skins/default/xui/zh/menu_inspect_self_gear.xmlmaster
commit
f65cddf860
|
|
@ -15,8 +15,8 @@ jobs:
|
|||
- uses: actions/stale@v6
|
||||
id: stale
|
||||
with:
|
||||
stale-pr-message: This pull request is stale because it has been open 60 days with no activity. Remove stale label or comment or it will be closed in 7 days
|
||||
days-before-stale: 60
|
||||
stale-pr-message: This pull request is stale because it has been open 30 days with no activity. Remove stale label or comment or it will be closed in 7 days
|
||||
days-before-stale: 30
|
||||
days-before-close: 7
|
||||
exempt-pr-labels: blocked,must,should,keep
|
||||
stale-pr-label: stale
|
||||
|
|
|
|||
|
|
@ -3353,6 +3353,36 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
|||
<key>version</key>
|
||||
<string>0.54.1.555529</string>
|
||||
</map>
|
||||
<key>xxhash</key>
|
||||
<map>
|
||||
<key>copyright</key>
|
||||
<string>Copyright 2012-2020 Yann Collet</string>
|
||||
<key>description</key>
|
||||
<string>xxHash Extremely fast hash algorithm</string>
|
||||
<key>license</key>
|
||||
<string>bsd</string>
|
||||
<key>license_file</key>
|
||||
<string>LICENSES/xxhash.txt</string>
|
||||
<key>name</key>
|
||||
<string>xxhash</string>
|
||||
<key>platforms</key>
|
||||
<map>
|
||||
<key>common</key>
|
||||
<map>
|
||||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>e4f77ba0a9b8ec3cc3fabc51c4da81d2</string>
|
||||
<key>url</key>
|
||||
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/110070/956941/xxhash-0.8.1.578006-windows-578006.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>common</string>
|
||||
</map>
|
||||
</map>
|
||||
<key>version</key>
|
||||
<string>0.8.1</string>
|
||||
</map>
|
||||
<key>zlib-ng</key>
|
||||
<map>
|
||||
<key>canonical_repo</key>
|
||||
|
|
|
|||
|
|
@ -593,6 +593,8 @@ Henri Beauchamp
|
|||
VWR-1406
|
||||
VWR-4157
|
||||
SL-15175
|
||||
SL-19110
|
||||
SL-19159
|
||||
herina Bode
|
||||
Hikkoshi Sakai
|
||||
VWR-429
|
||||
|
|
@ -1187,6 +1189,7 @@ PanteraPolnocy
|
|||
SL-18891
|
||||
SL-18904
|
||||
SL-18937
|
||||
SL-19207
|
||||
Parvati Silverweb
|
||||
Patric Mills
|
||||
VWR-2645
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ set(cmake_SOURCE_FILES
|
|||
VisualLeakDetector.cmake
|
||||
LibVLCPlugin.cmake
|
||||
XmlRpcEpi.cmake
|
||||
xxHash.cmake
|
||||
ZLIBNG.cmake
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ include(APR)
|
|||
include(Boost)
|
||||
include(EXPAT)
|
||||
include(Tracy)
|
||||
include(xxHash)
|
||||
include(ZLIBNG)
|
||||
|
||||
set(LLCOMMON_INCLUDE_DIRS
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
# -*- cmake -*-
|
||||
if (XXHASH_CMAKE_INCLUDED)
|
||||
return()
|
||||
endif (XXHASH_CMAKE_INCLUDED)
|
||||
set (XXHASH_CMAKE_INCLUDED TRUE)
|
||||
|
||||
include(Prebuilt)
|
||||
use_prebuilt_binary(xxhash)
|
||||
|
|
@ -1086,7 +1086,6 @@ BOOL LLAvatarAppearance::loadSkeletonNode ()
|
|||
|
||||
// SKELETAL DISTORTIONS
|
||||
{
|
||||
LLAvatarXmlInfo::skeletal_distortion_info_list_t::iterator iter;
|
||||
for (LLViewerVisualParamInfo* visual_param_info : sAvatarXmlInfo->mSkeletalDistortionInfoList)
|
||||
{
|
||||
LLPolySkeletalDistortionInfo *info = (LLPolySkeletalDistortionInfo*)visual_param_info;
|
||||
|
|
|
|||
|
|
@ -365,7 +365,6 @@ BOOL LLPolyMorphTarget::setInfo(LLPolyMorphTargetInfo* info)
|
|||
setWeight(getDefaultWeight(), FALSE);
|
||||
|
||||
LLAvatarAppearance* avatarp = mMesh->getAvatar();
|
||||
LLPolyMorphTargetInfo::volume_info_list_t::iterator iter;
|
||||
for (LLPolyVolumeMorphInfo& volume_info : getInfo()->mVolumeInfoList)
|
||||
{
|
||||
for (S32 i = 0; i < avatarp->mNumCollisionVolumes; i++)
|
||||
|
|
|
|||
|
|
@ -145,7 +145,6 @@ BOOL LLPolySkeletalDistortion::setInfo(LLPolySkeletalDistortionInfo *info)
|
|||
//setWeight(getDefaultWeight());
|
||||
setWeight(getDefaultWeight(), FALSE);
|
||||
|
||||
LLPolySkeletalDistortionInfo::bone_info_list_t::iterator iter;
|
||||
for (LLPolySkeletalBoneInfo& bone_info : getInfo()->mBoneInfoList)
|
||||
{
|
||||
LLJoint* joint = mAvatar->getJoint(bone_info.mBoneName);
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
#include "llsaleinfo.h"
|
||||
#include "llwearabletype.h"
|
||||
|
||||
class LLMD5;
|
||||
class LLMD5; // <FS:Ansariel> [Legacy Bake]
|
||||
class LLVisualParam;
|
||||
class LLTexGlobalColorInfo;
|
||||
class LLTexGlobalColor;
|
||||
|
|
@ -131,6 +131,7 @@ public:
|
|||
// Something happened that requires the wearable to be updated (e.g. worn/unworn).
|
||||
virtual void setUpdated() const = 0;
|
||||
|
||||
// <FS:Ansariel> [Legacy Bake]
|
||||
// Update the baked texture hash.
|
||||
virtual void addToBakedTextureHash(LLMD5& hash) const = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
#include "llavatarappearance.h"
|
||||
#include "llavatarappearancedefines.h"
|
||||
#include "lldriverparam.h"
|
||||
#include "llmd5.h"
|
||||
#include "llmd5.h" // <FS:Ansariel> [Legacy Bake]
|
||||
|
||||
LLWearableData::LLWearableData() :
|
||||
mAvatarAppearance(NULL)
|
||||
|
|
@ -355,6 +355,7 @@ U32 LLWearableData::getWearableCount(const U32 tex_index) const
|
|||
return getWearableCount(wearable_type);
|
||||
}
|
||||
|
||||
// <FS:Ansariel> [Legacy Bake]
|
||||
LLUUID LLWearableData::computeBakedTextureHash(LLAvatarAppearanceDefines::EBakedTextureIndex baked_index,
|
||||
BOOL generate_valid_hash) // Set to false if you want to upload the baked texture w/o putting it in the cache
|
||||
{
|
||||
|
|
@ -391,5 +392,6 @@ LLUUID LLWearableData::computeBakedTextureHash(LLAvatarAppearanceDefines::EBaked
|
|||
|
||||
return hash_id;
|
||||
}
|
||||
// </FS:Ansariel> [Legacy Bake]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ protected:
|
|||
private:
|
||||
void pullCrossWearableValues(const LLWearableType::EType type);
|
||||
|
||||
// <FS:Ansariel> [Legacy Bake]
|
||||
//--------------------------------------------------------------------
|
||||
// Server Communication
|
||||
//--------------------------------------------------------------------
|
||||
|
|
@ -93,6 +94,7 @@ public:
|
|||
BOOL generate_valid_hash = TRUE);
|
||||
protected:
|
||||
virtual void invalidateBakedTextureHash(LLMD5& hash) const {}
|
||||
// </FS:Ansariel> [Legacy Bake]
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Member variables
|
||||
|
|
|
|||
|
|
@ -954,7 +954,6 @@ ELoadStatus LLBVHLoader::loadBVHFile(const char *buffer, char* error_text, S32 &
|
|||
//------------------------------------------------------------------------
|
||||
void LLBVHLoader::applyTranslations()
|
||||
{
|
||||
JointVector::iterator ji;
|
||||
for (Joint* joint : mJoints)
|
||||
{
|
||||
//----------------------------------------------------------------
|
||||
|
|
@ -1069,7 +1068,6 @@ void LLBVHLoader::optimize()
|
|||
mEaseOut *= factor;
|
||||
}
|
||||
|
||||
JointVector::iterator ji;
|
||||
for (Joint* joint : mJoints)
|
||||
{
|
||||
BOOL pos_changed = FALSE;
|
||||
|
|
|
|||
|
|
@ -742,7 +742,6 @@ void LLJoint::showAttachmentScaleOverrides(const std::string& av_info) const
|
|||
{
|
||||
LL_DEBUGS("Avatar") << "no conflicts" << LL_ENDL;
|
||||
}
|
||||
std::set<LLVector3>::iterator dit = distinct_offsets.begin();
|
||||
for (const LLVector3& offset : distinct_offsets)
|
||||
{
|
||||
std::string highlight = (has_active_override && offset == active_override) ? "*" : "";
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ set(llcommon_SOURCE_FILES
|
|||
lluriparser.cpp
|
||||
lluuid.cpp
|
||||
llworkerthread.cpp
|
||||
hbxxh.cpp
|
||||
u64.cpp
|
||||
threadpool.cpp
|
||||
workqueue.cpp
|
||||
|
|
@ -254,6 +255,7 @@ set(llcommon_HEADER_FILES
|
|||
llwin32headers.h
|
||||
llwin32headerslean.h
|
||||
llworkerthread.h
|
||||
hbxxh.h
|
||||
lockstatic.h
|
||||
stdtypes.h
|
||||
stringize.h
|
||||
|
|
|
|||
|
|
@ -0,0 +1,377 @@
|
|||
/**
|
||||
* @file hbxxh.cpp
|
||||
* @brief High performances vectorized hashing based on xxHash.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2023&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (c) 2023, Henri Beauchamp.
|
||||
*
|
||||
* 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"
|
||||
|
||||
// This define ensures that xxHash will be compiled within this module, with
|
||||
// vectorized (*) and inlined functions (with no exported API symbol); our
|
||||
// xxhash "pre-built library" package actually only contains the xxhash.h
|
||||
// header (no library needed at link time).
|
||||
// (*) SSE2 is normally used for x86(_64) builds, unless you enabled AVX2
|
||||
// in your build, in which case the latter would be used instead. For ARM64
|
||||
// builds, this would also automatically enable NEON vectorization.
|
||||
#define XXH_INLINE_ALL
|
||||
#include "xxhash/xxhash.h"
|
||||
|
||||
#include "hbxxh.h"
|
||||
|
||||
// How many bytes to grab at a time when hashing files or streams
|
||||
constexpr size_t BLOCK_LEN = 4096;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// HBXXH64 class
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//static
|
||||
U64 HBXXH64::digest(const void* buffer, size_t len)
|
||||
{
|
||||
return XXH3_64bits(buffer, len);
|
||||
}
|
||||
|
||||
//static
|
||||
U64 HBXXH64::digest(const char* str)
|
||||
{
|
||||
return XXH3_64bits((const void*)str, strlen(str));
|
||||
}
|
||||
|
||||
//static
|
||||
U64 HBXXH64::digest(const std::string& str)
|
||||
{
|
||||
return XXH3_64bits((const void*)str.c_str(), str.size());
|
||||
}
|
||||
|
||||
// Must be called by all constructors.
|
||||
void HBXXH64::init()
|
||||
{
|
||||
mDigest = 0;
|
||||
mState = (void*)XXH3_createState();
|
||||
if (!mState || XXH3_64bits_reset((XXH3_state_t*)mState) != XXH_OK)
|
||||
{
|
||||
LL_WARNS() << "Failed to initialize state !" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
HBXXH64::~HBXXH64()
|
||||
{
|
||||
if (mState)
|
||||
{
|
||||
XXH3_freeState((XXH3_state_t*)mState);
|
||||
}
|
||||
}
|
||||
|
||||
void HBXXH64::update(const void* buffer, size_t len)
|
||||
{
|
||||
if (mState)
|
||||
{
|
||||
XXH3_64bits_update((XXH3_state_t*)mState, buffer, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "Cannot update a finalized digest !" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
void HBXXH64::update(const std::string& str)
|
||||
{
|
||||
if (mState)
|
||||
{
|
||||
XXH3_64bits_update((XXH3_state_t*)mState, (const void*)str.c_str(),
|
||||
str.length());
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "Cannot update a finalized digest !" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
void HBXXH64::update(std::istream& stream)
|
||||
{
|
||||
if (!mState)
|
||||
{
|
||||
LL_WARNS() << "Cannot update a finalized digest !" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[BLOCK_LEN];
|
||||
size_t len;
|
||||
while (stream.good())
|
||||
{
|
||||
stream.read(buffer, BLOCK_LEN);
|
||||
len = stream.gcount();
|
||||
XXH3_64bits_update((XXH3_state_t*)mState, (const void*)buffer, len);
|
||||
}
|
||||
}
|
||||
|
||||
void HBXXH64::update(FILE* file)
|
||||
{
|
||||
if (!mState)
|
||||
{
|
||||
LL_WARNS() << "Cannot update a finalized digest !" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[BLOCK_LEN];
|
||||
size_t len;
|
||||
while ((len = fread((void*)buffer, 1, BLOCK_LEN, file)))
|
||||
{
|
||||
XXH3_64bits_update((XXH3_state_t*)mState, (const void*)buffer, len);
|
||||
}
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
void HBXXH64::finalize()
|
||||
{
|
||||
if (!mState)
|
||||
{
|
||||
LL_WARNS() << "Already finalized !" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
mDigest = XXH3_64bits_digest((XXH3_state_t*)mState);
|
||||
XXH3_freeState((XXH3_state_t*)mState);
|
||||
mState = NULL;
|
||||
}
|
||||
|
||||
U64 HBXXH64::digest() const
|
||||
{
|
||||
return mState ? XXH3_64bits_digest((XXH3_state_t*)mState) : mDigest;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, HBXXH64 context)
|
||||
{
|
||||
stream << context.digest();
|
||||
return stream;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// HBXXH128 class
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//static
|
||||
LLUUID HBXXH128::digest(const void* buffer, size_t len)
|
||||
{
|
||||
XXH128_hash_t hash = XXH3_128bits(buffer, len);
|
||||
LLUUID id;
|
||||
U64* data = (U64*)id.mData;
|
||||
// Note: we do not check endianness here and we just store in the same
|
||||
// order as XXH128_hash_t, that is low word "first".
|
||||
data[0] = hash.low64;
|
||||
data[1] = hash.high64;
|
||||
return id;
|
||||
}
|
||||
|
||||
//static
|
||||
LLUUID HBXXH128::digest(const char* str)
|
||||
{
|
||||
XXH128_hash_t hash = XXH3_128bits((const void*)str, strlen(str));
|
||||
LLUUID id;
|
||||
U64* data = (U64*)id.mData;
|
||||
// Note: we do not check endianness here and we just store in the same
|
||||
// order as XXH128_hash_t, that is low word "first".
|
||||
data[0] = hash.low64;
|
||||
data[1] = hash.high64;
|
||||
return id;
|
||||
}
|
||||
|
||||
//static
|
||||
LLUUID HBXXH128::digest(const std::string& str)
|
||||
{
|
||||
XXH128_hash_t hash = XXH3_128bits((const void*)str.c_str(), str.size());
|
||||
LLUUID id;
|
||||
U64* data = (U64*)id.mData;
|
||||
// Note: we do not check endianness here and we just store in the same
|
||||
// order as XXH128_hash_t, that is low word "first".
|
||||
data[0] = hash.low64;
|
||||
data[1] = hash.high64;
|
||||
return id;
|
||||
}
|
||||
|
||||
//static
|
||||
void HBXXH128::digest(LLUUID& result, const void* buffer, size_t len)
|
||||
{
|
||||
XXH128_hash_t hash = XXH3_128bits(buffer, len);
|
||||
U64* data = (U64*)result.mData;
|
||||
// Note: we do not check endianness here and we just store in the same
|
||||
// order as XXH128_hash_t, that is low word "first".
|
||||
data[0] = hash.low64;
|
||||
data[1] = hash.high64;
|
||||
}
|
||||
|
||||
//static
|
||||
void HBXXH128::digest(LLUUID& result, const char* str)
|
||||
{
|
||||
XXH128_hash_t hash = XXH3_128bits((const void*)str, strlen(str));
|
||||
U64* data = (U64*)result.mData;
|
||||
// Note: we do not check endianness here and we just store in the same
|
||||
// order as XXH128_hash_t, that is low word "first".
|
||||
data[0] = hash.low64;
|
||||
data[1] = hash.high64;
|
||||
}
|
||||
|
||||
//static
|
||||
void HBXXH128::digest(LLUUID& result, const std::string& str)
|
||||
{
|
||||
XXH128_hash_t hash = XXH3_128bits((const void*)str.c_str(), str.size());
|
||||
U64* data = (U64*)result.mData;
|
||||
// Note: we do not check endianness here and we just store in the same
|
||||
// order as XXH128_hash_t, that is low word "first".
|
||||
data[0] = hash.low64;
|
||||
data[1] = hash.high64;
|
||||
}
|
||||
|
||||
// Must be called by all constructors.
|
||||
void HBXXH128::init()
|
||||
{
|
||||
mState = (void*)XXH3_createState();
|
||||
if (!mState || XXH3_128bits_reset((XXH3_state_t*)mState) != XXH_OK)
|
||||
{
|
||||
LL_WARNS() << "Failed to initialize state !" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
HBXXH128::~HBXXH128()
|
||||
{
|
||||
if (mState)
|
||||
{
|
||||
XXH3_freeState((XXH3_state_t*)mState);
|
||||
}
|
||||
}
|
||||
|
||||
void HBXXH128::update(const void* buffer, size_t len)
|
||||
{
|
||||
if (mState)
|
||||
{
|
||||
XXH3_128bits_update((XXH3_state_t*)mState, buffer, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "Cannot update a finalized digest !" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
void HBXXH128::update(const std::string& str)
|
||||
{
|
||||
if (mState)
|
||||
{
|
||||
XXH3_128bits_update((XXH3_state_t*)mState, (const void*)str.c_str(),
|
||||
str.length());
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "Cannot update a finalized digest !" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
void HBXXH128::update(std::istream& stream)
|
||||
{
|
||||
if (!mState)
|
||||
{
|
||||
LL_WARNS() << "Cannot update a finalized digest !" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[BLOCK_LEN];
|
||||
size_t len;
|
||||
while (stream.good())
|
||||
{
|
||||
stream.read(buffer, BLOCK_LEN);
|
||||
len = stream.gcount();
|
||||
XXH3_128bits_update((XXH3_state_t*)mState, (const void*)buffer, len);
|
||||
}
|
||||
}
|
||||
|
||||
void HBXXH128::update(FILE* file)
|
||||
{
|
||||
if (!mState)
|
||||
{
|
||||
LL_WARNS() << "Cannot update a finalized digest !" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[BLOCK_LEN];
|
||||
size_t len;
|
||||
while ((len = fread((void*)buffer, 1, BLOCK_LEN, file)))
|
||||
{
|
||||
XXH3_128bits_update((XXH3_state_t*)mState, (const void*)buffer, len);
|
||||
}
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
void HBXXH128::finalize()
|
||||
{
|
||||
if (!mState)
|
||||
{
|
||||
LL_WARNS() << "Already finalized !" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
XXH128_hash_t hash = XXH3_128bits_digest((XXH3_state_t*)mState);
|
||||
U64* data = (U64*)mDigest.mData;
|
||||
// Note: we do not check endianness here and we just store in the same
|
||||
// order as XXH128_hash_t, that is low word "first".
|
||||
data[0] = hash.low64;
|
||||
data[1] = hash.high64;
|
||||
XXH3_freeState((XXH3_state_t*)mState);
|
||||
mState = NULL;
|
||||
}
|
||||
|
||||
const LLUUID& HBXXH128::digest() const
|
||||
{
|
||||
if (mState)
|
||||
{
|
||||
XXH128_hash_t hash = XXH3_128bits_digest((XXH3_state_t*)mState);
|
||||
// We cheat the const-ness of the method here, but this is OK, since
|
||||
// mDigest is private and cannot be accessed indirectly by other
|
||||
// methods than digest() ones, that do check for mState to decide
|
||||
// wether mDigest's current value may be provided as is or not. This
|
||||
// cheat saves us a temporary LLLUID copy.
|
||||
U64* data = (U64*)mDigest.mData;
|
||||
// Note: we do not check endianness here and we just store in the same
|
||||
// order as XXH128_hash_t, that is low word "first".
|
||||
data[0] = hash.low64;
|
||||
data[1] = hash.high64;
|
||||
}
|
||||
return mDigest;
|
||||
}
|
||||
|
||||
void HBXXH128::digest(LLUUID& result) const
|
||||
{
|
||||
if (!mState)
|
||||
{
|
||||
result = mDigest;
|
||||
return;
|
||||
}
|
||||
XXH128_hash_t hash = XXH3_128bits_digest((XXH3_state_t*)mState);
|
||||
U64* data = (U64*)result.mData;
|
||||
// Note: we do not check endianness here and we just store in the same
|
||||
// order as XXH128_hash_t, that is low word "first".
|
||||
data[0] = hash.low64;
|
||||
data[1] = hash.high64;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, HBXXH128 context)
|
||||
{
|
||||
stream << context.digest();
|
||||
return stream;
|
||||
}
|
||||
|
|
@ -0,0 +1,277 @@
|
|||
/**
|
||||
* @file hbxxh.h
|
||||
* @brief High performances vectorized hashing based on xxHash.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2023&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (c) 2023, Henri Beauchamp.
|
||||
*
|
||||
* 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$
|
||||
*/
|
||||
|
||||
#ifndef LL_HBXXH_H
|
||||
#define LL_HBXXH_H
|
||||
|
||||
#include "lluuid.h"
|
||||
|
||||
// HBXXH* classes are to be used where speed matters and cryptographic quality
|
||||
// is not required (no "one-way" guarantee, though they are likely not worst in
|
||||
// this respect than MD5 which got busted and is now considered too weak). The
|
||||
// xxHash code they are built upon is vectorized and about 50 times faster than
|
||||
// MD5. A 64 bits hash class is also provided for when 128 bits of entropy are
|
||||
// not needed. The hashes collision rate is similar to MD5's.
|
||||
// See https://github.com/Cyan4973/xxHash#readme for details.
|
||||
|
||||
// 64 bits hashing class
|
||||
|
||||
class HBXXH64
|
||||
{
|
||||
friend std::ostream& operator<<(std::ostream&, HBXXH64);
|
||||
|
||||
protected:
|
||||
LOG_CLASS(HBXXH64);
|
||||
|
||||
public:
|
||||
inline HBXXH64() { init(); }
|
||||
|
||||
// Constructors for special circumstances; they all digest the first passed
|
||||
// parameter. Set 'do_finalize' to false if you do not want to finalize the
|
||||
// context, which is useful/needed when you want to update() it afterwards.
|
||||
// Ideally, the compiler should be smart enough to get our clue and
|
||||
// optimize out the const bool test during inlining...
|
||||
|
||||
inline HBXXH64(const void* buffer, size_t len,
|
||||
const bool do_finalize = true)
|
||||
{
|
||||
init();
|
||||
update(buffer, len);
|
||||
if (do_finalize)
|
||||
{
|
||||
finalize();
|
||||
}
|
||||
}
|
||||
|
||||
inline HBXXH64(const std::string& str, const bool do_finalize = true)
|
||||
{
|
||||
init();
|
||||
update(str);
|
||||
if (do_finalize)
|
||||
{
|
||||
finalize();
|
||||
}
|
||||
}
|
||||
|
||||
inline HBXXH64(std::istream& s, const bool do_finalize = true)
|
||||
{
|
||||
init();
|
||||
update(s);
|
||||
if (do_finalize)
|
||||
{
|
||||
finalize();
|
||||
}
|
||||
}
|
||||
|
||||
inline HBXXH64(FILE* file, const bool do_finalize = true)
|
||||
{
|
||||
init();
|
||||
update(file);
|
||||
if (do_finalize)
|
||||
{
|
||||
finalize();
|
||||
}
|
||||
}
|
||||
|
||||
// Make this class no-copy (it would be possible, with custom copy
|
||||
// operators, but it is not trivially copyable, because of the mState
|
||||
// pointer): it does not really make sense to allow copying it anyway,
|
||||
// since all we care about is the resulting digest (so you should only
|
||||
// need and care about storing/copying the digest and not a class
|
||||
// instance).
|
||||
HBXXH64(const HBXXH64&) noexcept = delete;
|
||||
HBXXH64& operator=(const HBXXH64&) noexcept = delete;
|
||||
|
||||
~HBXXH64();
|
||||
|
||||
void update(const void* buffer, size_t len);
|
||||
void update(const std::string& str);
|
||||
void update(std::istream& s);
|
||||
void update(FILE* file);
|
||||
|
||||
// Note that unlike what happens with LLMD5, you do not need to finalize()
|
||||
// HBXXH64 before using digest(), and you may keep updating() it even after
|
||||
// you got a first digest() (the next digest would of course change after
|
||||
// any update). It is still useful to use finalize() when you do not want
|
||||
// to store a final digest() result in a separate U64; after this method
|
||||
// has been called, digest() simply returns mDigest value.
|
||||
void finalize();
|
||||
|
||||
U64 digest() const;
|
||||
|
||||
// Fast static methods. Use them when hashing just one contiguous block of
|
||||
// data.
|
||||
static U64 digest(const void* buffer, size_t len);
|
||||
static U64 digest(const char* str); // str must be NUL-terminated
|
||||
static U64 digest(const std::string& str);
|
||||
|
||||
private:
|
||||
void init();
|
||||
|
||||
private:
|
||||
// We use a void pointer to avoid including xxhash.h here for XXH3_state_t
|
||||
// (which cannot either be trivially forward-declared, due to complex API
|
||||
// related pre-processor macros in xxhash.h).
|
||||
void* mState;
|
||||
U64 mDigest;
|
||||
};
|
||||
|
||||
inline bool operator==(const HBXXH64& a, const HBXXH64& b)
|
||||
{
|
||||
return a.digest() == b.digest();
|
||||
}
|
||||
|
||||
inline bool operator!=(const HBXXH64& a, const HBXXH64& b)
|
||||
{
|
||||
return a.digest() != b.digest();
|
||||
}
|
||||
|
||||
// 128 bits hashing class
|
||||
|
||||
class HBXXH128
|
||||
{
|
||||
friend std::ostream& operator<<(std::ostream&, HBXXH128);
|
||||
|
||||
protected:
|
||||
LOG_CLASS(HBXXH128);
|
||||
|
||||
public:
|
||||
inline HBXXH128() { init(); }
|
||||
|
||||
// Constructors for special circumstances; they all digest the first passed
|
||||
// parameter. Set 'do_finalize' to false if you do not want to finalize the
|
||||
// context, which is useful/needed when you want to update() it afterwards.
|
||||
// Ideally, the compiler should be smart enough to get our clue and
|
||||
// optimize out the const bool test during inlining...
|
||||
|
||||
inline HBXXH128(const void* buffer, size_t len,
|
||||
const bool do_finalize = true)
|
||||
{
|
||||
init();
|
||||
update(buffer, len);
|
||||
if (do_finalize)
|
||||
{
|
||||
finalize();
|
||||
}
|
||||
}
|
||||
|
||||
inline HBXXH128(const std::string& str, const bool do_finalize = true)
|
||||
{
|
||||
init();
|
||||
update(str);
|
||||
if (do_finalize)
|
||||
{
|
||||
finalize();
|
||||
}
|
||||
}
|
||||
|
||||
inline HBXXH128(std::istream& s, const bool do_finalize = true)
|
||||
{
|
||||
init();
|
||||
update(s);
|
||||
if (do_finalize)
|
||||
{
|
||||
finalize();
|
||||
}
|
||||
}
|
||||
|
||||
inline HBXXH128(FILE* file, const bool do_finalize = true)
|
||||
{
|
||||
init();
|
||||
update(file);
|
||||
if (do_finalize)
|
||||
{
|
||||
finalize();
|
||||
}
|
||||
}
|
||||
|
||||
// Make this class no-copy (it would be possible, with custom copy
|
||||
// operators, but it is not trivially copyable, because of the mState
|
||||
// pointer): it does not really make sense to allow copying it anyway,
|
||||
// since all we care about is the resulting digest (so you should only
|
||||
// need and care about storing/copying the digest and not a class
|
||||
// instance).
|
||||
HBXXH128(const HBXXH128&) noexcept = delete;
|
||||
HBXXH128& operator=(const HBXXH128&) noexcept = delete;
|
||||
|
||||
~HBXXH128();
|
||||
|
||||
void update(const void* buffer, size_t len);
|
||||
void update(const std::string& str);
|
||||
void update(std::istream& s);
|
||||
void update(FILE* file);
|
||||
|
||||
// Note that unlike what happens with LLMD5, you do not need to finalize()
|
||||
// HBXXH128 before using digest(), and you may keep updating() it even
|
||||
// after you got a first digest() (the next digest would of course change
|
||||
// after any update). It is still useful to use finalize() when you do not
|
||||
// want to store a final digest() result in a separate LLUUID; after this
|
||||
// method has been called, digest() simply returns a reference on mDigest.
|
||||
void finalize();
|
||||
|
||||
// We use an LLUUID for the digest, since this is a 128 bits wide native
|
||||
// type available in the viewer code, making it easy to manipulate. It also
|
||||
// allows to use HBXXH128 efficiently in LLUUID generate() and combine()
|
||||
// methods.
|
||||
const LLUUID& digest() const;
|
||||
|
||||
// Here, we avoid an LLUUID copy whenever we already got one to store the
|
||||
// result *and* we did not yet call finalize().
|
||||
void digest(LLUUID& result) const;
|
||||
|
||||
// Fast static methods. Use them when hashing just one contiguous block of
|
||||
// data.
|
||||
static LLUUID digest(const void* buffer, size_t len);
|
||||
static LLUUID digest(const char* str); // str must be NUL-terminated
|
||||
static LLUUID digest(const std::string& str);
|
||||
// Same as above, but saves you from an LLUUID copy when you already got
|
||||
// one for storage use.
|
||||
static void digest(LLUUID& result, const void* buffer, size_t len);
|
||||
static void digest(LLUUID& result, const char* str); // str NUL-terminated
|
||||
static void digest(LLUUID& result, const std::string& str);
|
||||
|
||||
private:
|
||||
void init();
|
||||
|
||||
private:
|
||||
// We use a void pointer to avoid including xxhash.h here for XXH3_state_t
|
||||
// (which cannot either be trivially forward-declared, due to complex API
|
||||
// related pre-processor macros in xxhash.h).
|
||||
void* mState;
|
||||
LLUUID mDigest;
|
||||
};
|
||||
|
||||
inline bool operator==(const HBXXH128& a, const HBXXH128& b)
|
||||
{
|
||||
return a.digest() == b.digest();
|
||||
}
|
||||
|
||||
inline bool operator!=(const HBXXH128& a, const HBXXH128& b)
|
||||
{
|
||||
return a.digest() != b.digest();
|
||||
}
|
||||
|
||||
#endif // LL_HBXXH_H
|
||||
|
|
@ -141,7 +141,6 @@ void LLAllocatorHeapProfile::parse(std::string const & prof_text)
|
|||
|
||||
void LLAllocatorHeapProfile::dump(std::ostream & out) const
|
||||
{
|
||||
lines_t::const_iterator i;
|
||||
for (const LLAllocatorHeapProfile::line& line : mLines)
|
||||
{
|
||||
out << line.mLiveCount << ": " << line.mLiveSize << '[' << line.mTotalCount << ": " << line.mTotalSize << "] @";
|
||||
|
|
|
|||
|
|
@ -514,16 +514,21 @@ public:
|
|||
// former broken behavior has finally been fixed -- and our builds
|
||||
// treat warnings as errors.
|
||||
{
|
||||
for (typename const DepNodeMap::value_type& nm_pair : mNodes)
|
||||
for (typename DepNodeMap::const_iterator nmi = mNodes.begin(), nmend = mNodes.end();
|
||||
nmi != nmend; ++nmi)
|
||||
{
|
||||
vmap.insert(typename VertexMap::value_type(nm_pair.first, vmap.size()));
|
||||
for (typename const KEY& after_k : nm_pair.second.after)
|
||||
vmap.insert(typename VertexMap::value_type(nmi->first, vmap.size()));
|
||||
for (typename DepNode::dep_set::const_iterator ai = nmi->second.after.begin(),
|
||||
aend = nmi->second.after.end();
|
||||
ai != aend; ++ai)
|
||||
{
|
||||
vmap.insert(typename VertexMap::value_type(after_k, vmap.size()));
|
||||
vmap.insert(typename VertexMap::value_type(*ai, vmap.size()));
|
||||
}
|
||||
for (typename const KEY& before_k : nm_pair.second.before)
|
||||
for (typename DepNode::dep_set::const_iterator bi = nmi->second.before.begin(),
|
||||
bend = nmi->second.before.end();
|
||||
bi != bend; ++bi)
|
||||
{
|
||||
vmap.insert(typename VertexMap::value_type(before_k, vmap.size()));
|
||||
vmap.insert(typename VertexMap::value_type(*bi, vmap.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -531,19 +536,24 @@ public:
|
|||
// all the known key dependencies to integer pairs.
|
||||
EdgeList edges;
|
||||
{
|
||||
for (typename const DepNodeMap::value_type& nm_pair : mNodes)
|
||||
for (typename DepNodeMap::const_iterator nmi = mNodes.begin(), nmend = mNodes.end();
|
||||
nmi != nmend; ++nmi)
|
||||
{
|
||||
auto thisnode = vmap[nm_pair.first];
|
||||
auto thisnode = vmap[nmi->first];
|
||||
// after dependencies: build edges from the named node to this one
|
||||
for (typename const KEY& after_k : nm_pair.second.after)
|
||||
for (typename DepNode::dep_set::const_iterator ai = nmi->second.after.begin(),
|
||||
aend = nmi->second.after.end();
|
||||
ai != aend; ++ai)
|
||||
{
|
||||
edges.push_back(EdgeList::value_type(vmap[after_k], thisnode));
|
||||
edges.push_back(EdgeList::value_type(vmap[*ai], thisnode));
|
||||
}
|
||||
// before dependencies: build edges from this node to the
|
||||
// named one
|
||||
for (typename const KEY& before_k : nm_pair.second.before)
|
||||
for (typename DepNode::dep_set::const_iterator bi = nmi->second.before.begin(),
|
||||
bend = nmi->second.before.end();
|
||||
bi != bend; ++bi)
|
||||
{
|
||||
edges.push_back(EdgeList::value_type(thisnode, vmap[before_k]));
|
||||
edges.push_back(EdgeList::value_type(thisnode, vmap[*bi]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -555,19 +565,21 @@ public:
|
|||
// and we're certain that the associated int values are distinct
|
||||
// indexes. The fact that they're not in order is irrelevant.
|
||||
KeyList vkeys(vmap.size());
|
||||
for (typename const VertexMap::value_type& vm_pair : vmap)
|
||||
for (typename VertexMap::const_iterator vmi = vmap.begin(), vmend = vmap.end();
|
||||
vmi != vmend; ++vmi)
|
||||
{
|
||||
vkeys[vm_pair.second] = vm_pair.first;
|
||||
vkeys[vmi->second] = vmi->first;
|
||||
}
|
||||
// Walk the sorted output list, building the result into mCache so
|
||||
// we'll have it next time someone asks.
|
||||
mCache.clear();
|
||||
for (const size_t sv : sorted)
|
||||
for (VertexList::const_iterator svi = sorted.begin(), svend = sorted.end();
|
||||
svi != svend; ++svi)
|
||||
{
|
||||
// We're certain that vkeys[sv] exists. However, there might not
|
||||
// We're certain that vkeys[*svi] exists. However, there might not
|
||||
// yet be a corresponding entry in mNodes.
|
||||
self_type* non_const_this(const_cast<self_type*>(this));
|
||||
typename DepNodeMap::iterator found = non_const_this->mNodes.find(vkeys[sv]);
|
||||
typename DepNodeMap::iterator found = non_const_this->mNodes.find(vkeys[*svi]);
|
||||
if (found != non_const_this->mNodes.end())
|
||||
{
|
||||
// Make an iterator of appropriate type.
|
||||
|
|
|
|||
|
|
@ -316,7 +316,7 @@ namespace LLInitParam
|
|||
{
|
||||
// Ensure this param has not already been inspected
|
||||
bool duplicate = false;
|
||||
for (const ParamDescriptorPtr ptr : block_data.mUnnamedParams)
|
||||
for (const ParamDescriptorPtr &ptr : block_data.mUnnamedParams)
|
||||
{
|
||||
if (param_handle == ptr->mParamHandle)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -204,30 +204,35 @@ public:
|
|||
LLSD packet(LLSDMap("pump", pump)("data", data));
|
||||
|
||||
std::ostringstream buffer;
|
||||
buffer << LLSDNotationStreamer(packet);
|
||||
// SL-18330: for large data blocks, it's much faster to parse binary
|
||||
// LLSD than notation LLSD. Use serialize(LLSD_BINARY) rather than
|
||||
// directly calling LLSDBinaryFormatter because, unlike the latter,
|
||||
// serialize() prepends the relevant header, needed by a general-
|
||||
// purpose LLSD parser to distinguish binary from notation.
|
||||
LLSDSerialize::serialize(packet, buffer, LLSDSerialize::LLSD_BINARY,
|
||||
LLSDFormatter::OPTIONS_NONE);
|
||||
|
||||
/*==========================================================================*|
|
||||
// DEBUGGING ONLY: don't copy str() if we can avoid it.
|
||||
std::string strdata(buffer.str());
|
||||
if (std::size_t(buffer.tellp()) != strdata.length())
|
||||
{
|
||||
LL_ERRS("LLLeap") << "tellp() -> " << buffer.tellp() << " != "
|
||||
LL_ERRS("LLLeap") << "tellp() -> " << static_cast<U64>(buffer.tellp()) << " != "
|
||||
<< "str().length() -> " << strdata.length() << LL_ENDL;
|
||||
}
|
||||
// DEBUGGING ONLY: reading back is terribly inefficient.
|
||||
std::istringstream readback(strdata);
|
||||
LLSD echo;
|
||||
LLPointer<LLSDParser> parser(new LLSDNotationParser());
|
||||
S32 parse_status(parser->parse(readback, echo, strdata.length()));
|
||||
if (parse_status == LLSDParser::PARSE_FAILURE)
|
||||
bool parse_status(LLSDSerialize::deserialize(echo, readback, strdata.length()));
|
||||
if (! parse_status)
|
||||
{
|
||||
LL_ERRS("LLLeap") << "LLSDNotationParser() cannot parse output of "
|
||||
<< "LLSDNotationStreamer()" << LL_ENDL;
|
||||
LL_ERRS("LLLeap") << "LLSDSerialize::deserialize() cannot parse output of "
|
||||
<< "LLSDSerialize::serialize(LLSD_BINARY)" << LL_ENDL;
|
||||
}
|
||||
if (! llsd_equals(echo, packet))
|
||||
{
|
||||
LL_ERRS("LLLeap") << "LLSDNotationParser() produced different LLSD "
|
||||
<< "than passed to LLSDNotationStreamer()" << LL_ENDL;
|
||||
LL_ERRS("LLLeap") << "LLSDSerialize::deserialize() returned different LLSD "
|
||||
<< "than passed to LLSDSerialize::serialize()" << LL_ENDL;
|
||||
}
|
||||
|*==========================================================================*/
|
||||
|
||||
|
|
@ -314,9 +319,17 @@ public:
|
|||
LL_DEBUGS("LLLeap") << "needed " << mExpect << " bytes, got "
|
||||
<< childout.size() << ", parsing LLSD" << LL_ENDL;
|
||||
LLSD data;
|
||||
#if 1
|
||||
// specifically require notation LLSD from child
|
||||
LLPointer<LLSDParser> parser(new LLSDNotationParser());
|
||||
S32 parse_status(parser->parse(childout.get_istream(), data, mExpect));
|
||||
if (parse_status == LLSDParser::PARSE_FAILURE)
|
||||
#else
|
||||
// SL-18330: accept any valid LLSD serialization format from child
|
||||
// Unfortunately this runs into trouble we have not yet debugged.
|
||||
bool parse_status(LLSDSerialize::deserialize(data, childout.get_istream(), mExpect));
|
||||
if (! parse_status)
|
||||
#endif
|
||||
{
|
||||
bad_protocol("unparseable LLSD data");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@
|
|||
#endif
|
||||
|
||||
#include "lldate.h"
|
||||
#include "llmemorystream.h"
|
||||
#include "llsd.h"
|
||||
#include "llstring.h"
|
||||
#include "lluri.h"
|
||||
|
|
@ -64,6 +65,23 @@ const std::string LLSD_NOTATION_HEADER("llsd/notation");
|
|||
#define windowBits 15
|
||||
#define ENABLE_ZLIB_GZIP 32
|
||||
|
||||
// If we published this in llsdserialize.h, we could use it in the
|
||||
// implementation of LLSDOStreamer's operator<<().
|
||||
template <class Formatter>
|
||||
void format_using(const LLSD& data, std::ostream& ostr,
|
||||
LLSDFormatter::EFormatterOptions options=LLSDFormatter::OPTIONS_PRETTY_BINARY)
|
||||
{
|
||||
LLPointer<Formatter> f{ new Formatter };
|
||||
f->format(data, ostr, options);
|
||||
}
|
||||
|
||||
template <class Parser>
|
||||
S32 parse_using(std::istream& istr, LLSD& data, size_t max_bytes, S32 max_depth=-1)
|
||||
{
|
||||
LLPointer<Parser> p{ new Parser };
|
||||
return p->parse(istr, data, max_bytes, max_depth);
|
||||
}
|
||||
|
||||
/**
|
||||
* LLSDSerialize
|
||||
*/
|
||||
|
|
@ -86,10 +104,10 @@ void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize
|
|||
f = new LLSDXMLFormatter;
|
||||
break;
|
||||
|
||||
case LLSD_NOTATION:
|
||||
str << "<? " << LLSD_NOTATION_HEADER << " ?>\n";
|
||||
f = new LLSDNotationFormatter;
|
||||
break;
|
||||
case LLSD_NOTATION:
|
||||
str << "<? " << LLSD_NOTATION_HEADER << " ?>\n";
|
||||
f = new LLSDNotationFormatter;
|
||||
break;
|
||||
|
||||
default:
|
||||
LL_WARNS() << "serialize request for unknown ELLSD_Serialize" << LL_ENDL;
|
||||
|
|
@ -104,18 +122,31 @@ void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize
|
|||
// static
|
||||
bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, llssize max_bytes)
|
||||
{
|
||||
LLPointer<LLSDParser> p = NULL;
|
||||
char hdr_buf[MAX_HDR_LEN + 1] = ""; /* Flawfinder: ignore */
|
||||
int i;
|
||||
int inbuf = 0;
|
||||
bool legacy_no_header = false;
|
||||
bool fail_if_not_legacy = false;
|
||||
std::string header;
|
||||
|
||||
/*
|
||||
* Get the first line before anything.
|
||||
* Get the first line before anything. Don't read more than max_bytes:
|
||||
* this get() overload reads no more than (count-1) bytes into the
|
||||
* specified buffer. In the usual case when max_bytes exceeds
|
||||
* sizeof(hdr_buf), get() will read no more than sizeof(hdr_buf)-2.
|
||||
*/
|
||||
str.get(hdr_buf, MAX_HDR_LEN, '\n');
|
||||
str.get(hdr_buf, llmin(max_bytes+1, sizeof(hdr_buf)-1), '\n');
|
||||
auto inbuf = str.gcount();
|
||||
// https://en.cppreference.com/w/cpp/io/basic_istream/get
|
||||
// When the get() above sees the specified delimiter '\n', it stops there
|
||||
// without pulling it from the stream. If it turns out that the stream
|
||||
// does NOT contain a header, and the content includes meaningful '\n',
|
||||
// it's important to pull that into hdr_buf too.
|
||||
if (inbuf < max_bytes && str.get(hdr_buf[inbuf]))
|
||||
{
|
||||
// got the delimiting '\n'
|
||||
++inbuf;
|
||||
// None of the following requires that hdr_buf contain a final '\0'
|
||||
// byte. We could store one if needed, since even the incremented
|
||||
// inbuf won't exceed sizeof(hdr_buf)-1, but there's no need.
|
||||
}
|
||||
std::string header{ hdr_buf, narrow(inbuf) };
|
||||
if (str.fail())
|
||||
{
|
||||
str.clear();
|
||||
|
|
@ -123,79 +154,97 @@ bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, llssize max_bytes)
|
|||
}
|
||||
|
||||
if (!strncasecmp(LEGACY_NON_HEADER, hdr_buf, strlen(LEGACY_NON_HEADER))) /* Flawfinder: ignore */
|
||||
{
|
||||
legacy_no_header = true;
|
||||
inbuf = (int)str.gcount();
|
||||
{ // Create a LLSD XML parser, and parse the first chunk read above.
|
||||
LLSDXMLParser x;
|
||||
x.parsePart(hdr_buf, inbuf); // Parse the first part that was already read
|
||||
auto parsed = x.parse(str, sd, max_bytes - inbuf); // Parse the rest of it
|
||||
// Formally we should probably check (parsed != PARSE_FAILURE &&
|
||||
// parsed > 0), but since PARSE_FAILURE is -1, this suffices.
|
||||
return (parsed > 0);
|
||||
}
|
||||
else
|
||||
|
||||
if (fail_if_not_legacy)
|
||||
{
|
||||
if (fail_if_not_legacy)
|
||||
goto fail;
|
||||
/*
|
||||
* Remove the newline chars
|
||||
*/
|
||||
for (i = 0; i < MAX_HDR_LEN; i++)
|
||||
{
|
||||
if (hdr_buf[i] == 0 || hdr_buf[i] == '\r' ||
|
||||
hdr_buf[i] == '\n')
|
||||
{
|
||||
hdr_buf[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
header = hdr_buf;
|
||||
LL_WARNS() << "deserialize LLSD parse failure" << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string::size_type start = std::string::npos;
|
||||
std::string::size_type end = std::string::npos;
|
||||
start = header.find_first_not_of("<? ");
|
||||
if (start != std::string::npos)
|
||||
{
|
||||
end = header.find_first_of(" ?", start);
|
||||
}
|
||||
if ((start == std::string::npos) || (end == std::string::npos))
|
||||
goto fail;
|
||||
/*
|
||||
* Remove the newline chars
|
||||
*/
|
||||
std::string::size_type lastchar = header.find_last_not_of("\r\n");
|
||||
if (lastchar != std::string::npos)
|
||||
{
|
||||
// It's important that find_last_not_of() returns size_type, which is
|
||||
// why lastchar explicitly declares the type above. erase(size_type)
|
||||
// erases from that offset to the end of the string, whereas
|
||||
// erase(iterator) erases only a single character.
|
||||
header.erase(lastchar+1);
|
||||
}
|
||||
|
||||
header = header.substr(start, end - start);
|
||||
ws(str);
|
||||
// trim off the <? ... ?> header syntax
|
||||
auto start = header.find_first_not_of("<? ");
|
||||
if (start != std::string::npos)
|
||||
{
|
||||
auto end = header.find_first_of(" ?", start);
|
||||
if (end != std::string::npos)
|
||||
{
|
||||
header = header.substr(start, end - start);
|
||||
ws(str);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Create the parser as appropriate
|
||||
*/
|
||||
if (legacy_no_header)
|
||||
{ // Create a LLSD XML parser, and parse the first chunk read above
|
||||
LLSDXMLParser* x = new LLSDXMLParser();
|
||||
x->parsePart(hdr_buf, inbuf); // Parse the first part that was already read
|
||||
x->parseLines(str, sd); // Parse the rest of it
|
||||
delete x;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (header == LLSD_BINARY_HEADER)
|
||||
if (0 == LLStringUtil::compareInsensitive(header, LLSD_BINARY_HEADER))
|
||||
{
|
||||
p = new LLSDBinaryParser;
|
||||
return (parse_using<LLSDBinaryParser>(str, sd, max_bytes-inbuf) > 0);
|
||||
}
|
||||
else if (header == LLSD_XML_HEADER)
|
||||
else if (0 == LLStringUtil::compareInsensitive(header, LLSD_XML_HEADER))
|
||||
{
|
||||
p = new LLSDXMLParser;
|
||||
return (parse_using<LLSDXMLParser>(str, sd, max_bytes-inbuf) > 0);
|
||||
}
|
||||
else if (header == LLSD_NOTATION_HEADER)
|
||||
else if (0 == LLStringUtil::compareInsensitive(header, LLSD_NOTATION_HEADER))
|
||||
{
|
||||
p = new LLSDNotationParser;
|
||||
return (parse_using<LLSDNotationParser>(str, sd, max_bytes-inbuf) > 0);
|
||||
}
|
||||
else
|
||||
else // no header we recognize
|
||||
{
|
||||
LL_WARNS() << "deserialize request for unknown ELLSD_Serialize" << LL_ENDL;
|
||||
LLPointer<LLSDParser> p;
|
||||
if (inbuf && hdr_buf[0] == '<')
|
||||
{
|
||||
// looks like XML
|
||||
LL_DEBUGS() << "deserialize request with no header, assuming XML" << LL_ENDL;
|
||||
p = new LLSDXMLParser;
|
||||
}
|
||||
else
|
||||
{
|
||||
// assume notation
|
||||
LL_DEBUGS() << "deserialize request with no header, assuming notation" << LL_ENDL;
|
||||
p = new LLSDNotationParser;
|
||||
}
|
||||
// Since we've already read 'inbuf' bytes into 'hdr_buf', prepend that
|
||||
// data to whatever remains in 'str'.
|
||||
LLMemoryStreamBuf already(reinterpret_cast<const U8*>(hdr_buf), inbuf);
|
||||
cat_streambuf prebuff(&already, str.rdbuf());
|
||||
std::istream prepend(&prebuff);
|
||||
#if 1
|
||||
return (p->parse(prepend, sd, max_bytes) > 0);
|
||||
#else
|
||||
// debugging the reconstituted 'prepend' stream
|
||||
// allocate a buffer that we hope is big enough for the whole thing
|
||||
std::vector<char> wholemsg((max_bytes == size_t(SIZE_UNLIMITED))? 1024 : max_bytes);
|
||||
prepend.read(wholemsg.data(), std::min(max_bytes, wholemsg.size()));
|
||||
LLMemoryStream replay(reinterpret_cast<const U8*>(wholemsg.data()), prepend.gcount());
|
||||
auto success{ p->parse(replay, sd, prepend.gcount()) > 0 };
|
||||
{
|
||||
LL_DEBUGS() << (success? "parsed: $$" : "failed: '")
|
||||
<< std::string(wholemsg.data(), llmin(prepend.gcount(), 100)) << "$$"
|
||||
<< LL_ENDL;
|
||||
}
|
||||
return success;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (p.notNull())
|
||||
{
|
||||
p->parse(str, sd, max_bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
fail:
|
||||
LL_WARNS() << "deserialize LLSD parse failure" << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2294,169 +2343,41 @@ LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, const U8* in, S32
|
|||
free(result);
|
||||
return ZR_OK;
|
||||
}
|
||||
// </FS:Beq pp Rye>
|
||||
//This unzip function will only work with a gzip header and trailer - while the contents
|
||||
//of the actual compressed data is the same for either format (gzip vs zlib ), the headers
|
||||
//and trailers are different for the formats.
|
||||
U8* unzip_llsdNavMesh( bool& valid, size_t& outsize, std::istream& is, S32 size )
|
||||
{
|
||||
// <FS:Beq pp Rye> Add non-allocating variants of unzip_llsd
|
||||
// if (size == 0)
|
||||
// {
|
||||
// LL_WARNS() << "No data to unzip." << LL_ENDL;
|
||||
// return NULL;
|
||||
// }
|
||||
|
||||
// U8* result = NULL;
|
||||
// U32 cur_size = 0;
|
||||
// z_stream strm;
|
||||
|
||||
// const U32 CHUNK = 0x4000;
|
||||
|
||||
// U8 *in = new(std::nothrow) U8[size];
|
||||
// if (in == NULL)
|
||||
// {
|
||||
// LL_WARNS() << "Memory allocation failure." << LL_ENDL;
|
||||
// return NULL;
|
||||
// }
|
||||
// is.read((char*) in, size);
|
||||
|
||||
// U8 out[CHUNK];
|
||||
|
||||
// strm.zalloc = Z_NULL;
|
||||
// strm.zfree = Z_NULL;
|
||||
// strm.opaque = Z_NULL;
|
||||
// strm.avail_in = size;
|
||||
// strm.next_in = in;
|
||||
|
||||
// valid = true; // <FS:ND/> Default is all okay.
|
||||
// S32 ret = inflateInit2(&strm, windowBits | ENABLE_ZLIB_GZIP );
|
||||
// do
|
||||
// {
|
||||
// strm.avail_out = CHUNK;
|
||||
// strm.next_out = out;
|
||||
// ret = inflate(&strm, Z_NO_FLUSH);
|
||||
// if (ret == Z_STREAM_ERROR)
|
||||
// {
|
||||
// inflateEnd(&strm);
|
||||
// // free(result);
|
||||
// if( result )
|
||||
// free(result);
|
||||
// delete [] in;
|
||||
// in = NULL; result = NULL;// <FS:ND> Or we get a double free aftr the while loop ...
|
||||
// valid = false;
|
||||
// }
|
||||
|
||||
// switch (ret)
|
||||
// {
|
||||
// case Z_NEED_DICT:
|
||||
// ret = Z_DATA_ERROR;
|
||||
// case Z_DATA_ERROR:
|
||||
// case Z_MEM_ERROR:
|
||||
// inflateEnd(&strm);
|
||||
// // free(result);
|
||||
// if( result )
|
||||
// free(result);
|
||||
// delete [] in;
|
||||
// valid = false;
|
||||
// in = NULL; result = NULL;// <FS:ND> Or we get a double free aftr the while loop ...
|
||||
// break;
|
||||
// }
|
||||
|
||||
// if( valid ) {// <FS:ND> in case this stream is invalid, do not pass the already freed buffer to realloc.
|
||||
|
||||
// U32 have = CHUNK-strm.avail_out;
|
||||
|
||||
// U8* new_result = (U8*) realloc(result, cur_size + have);
|
||||
// if (new_result == NULL)
|
||||
// {
|
||||
// LL_WARNS() << "Failed to unzip LLSD NavMesh block: can't reallocate memory, current size: " << cur_size
|
||||
// << " bytes; requested " << cur_size + have
|
||||
// << " bytes; total syze: ." << size << " bytes."
|
||||
// << LL_ENDL;
|
||||
// inflateEnd(&strm);
|
||||
// if (result)
|
||||
// {
|
||||
// free(result);
|
||||
// }
|
||||
// delete[] in;
|
||||
// valid = false;
|
||||
// return NULL;
|
||||
// }
|
||||
// result = new_result;
|
||||
// memcpy(result+cur_size, out, have);
|
||||
// cur_size += have;
|
||||
|
||||
// } // </FS:ND>
|
||||
|
||||
// } while (ret == Z_OK);
|
||||
|
||||
// inflateEnd(&strm);
|
||||
// delete [] in;
|
||||
|
||||
// if (ret != Z_STREAM_END)
|
||||
// {
|
||||
// // <FS:ND> result might have been freed above. And calling free with a null pointer is not defined.
|
||||
// // free(result);
|
||||
// if( result )
|
||||
// free(result);
|
||||
// // </FS:ND>
|
||||
|
||||
// valid = false;
|
||||
// return NULL;
|
||||
// }
|
||||
|
||||
// //result now points to the decompressed LLSD block
|
||||
// {
|
||||
// outsize= cur_size;
|
||||
// valid = true;
|
||||
// }
|
||||
|
||||
// return result;
|
||||
// }
|
||||
if (size == 0)
|
||||
{
|
||||
LL_WARNS() << "No data to unzip." << LL_ENDL;
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<U8[]> in;
|
||||
try
|
||||
{
|
||||
in = std::make_unique<U8[]>(size);
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
LL_WARNS() << "Memory allocation failure." << LL_ENDL;
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
is.read((char*)in.get(), size);
|
||||
return unzip_llsdNavMesh(valid, outsize, in.get(), size);
|
||||
}
|
||||
|
||||
U8* unzip_llsdNavMesh(bool& valid, unsigned int& outsize, const U8* in, S32 size)
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
LL_WARNS() << "No data to unzip." << LL_ENDL;
|
||||
return nullptr;
|
||||
}
|
||||
U8* result = nullptr;
|
||||
U8* result = NULL;
|
||||
U32 cur_size = 0;
|
||||
z_stream strm;
|
||||
|
||||
|
||||
const U32 CHUNK = 0x4000;
|
||||
|
||||
U8 out[CHUNK];
|
||||
U8 *in = new(std::nothrow) U8[size];
|
||||
if (in == NULL)
|
||||
{
|
||||
LL_WARNS() << "Memory allocation failure." << LL_ENDL;
|
||||
return NULL;
|
||||
}
|
||||
is.read((char*) in, size);
|
||||
|
||||
U8 out[CHUNK];
|
||||
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
strm.avail_in = size;
|
||||
strm.next_in = const_cast<U8*>(in);
|
||||
strm.next_in = in;
|
||||
|
||||
|
||||
S32 ret = inflateInit2(&strm, windowBits | ENABLE_ZLIB_GZIP);
|
||||
|
||||
S32 ret = inflateInit2(&strm, windowBits | ENABLE_ZLIB_GZIP );
|
||||
do
|
||||
{
|
||||
strm.avail_out = CHUNK;
|
||||
|
|
@ -2466,9 +2387,10 @@ U8* unzip_llsdNavMesh(bool& valid, unsigned int& outsize, const U8* in, S32 size
|
|||
{
|
||||
inflateEnd(&strm);
|
||||
free(result);
|
||||
return nullptr;
|
||||
delete [] in;
|
||||
valid = false;
|
||||
}
|
||||
|
||||
|
||||
switch (ret)
|
||||
{
|
||||
case Z_NEED_DICT:
|
||||
|
|
@ -2478,14 +2400,15 @@ U8* unzip_llsdNavMesh(bool& valid, unsigned int& outsize, const U8* in, S32 size
|
|||
case Z_MEM_ERROR:
|
||||
inflateEnd(&strm);
|
||||
free(result);
|
||||
delete [] in;
|
||||
valid = false;
|
||||
return nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
U32 have = CHUNK - strm.avail_out;
|
||||
U32 have = CHUNK-strm.avail_out;
|
||||
|
||||
U8* new_result = (U8*)realloc(result, cur_size + have);
|
||||
if (!new_result)
|
||||
U8* new_result = (U8*) realloc(result, cur_size + have);
|
||||
if (new_result == NULL)
|
||||
{
|
||||
LL_WARNS() << "Failed to unzip LLSD NavMesh block: can't reallocate memory, current size: " << cur_size
|
||||
<< " bytes; requested " << cur_size + have
|
||||
|
|
@ -2496,33 +2419,34 @@ U8* unzip_llsdNavMesh(bool& valid, unsigned int& outsize, const U8* in, S32 size
|
|||
{
|
||||
free(result);
|
||||
}
|
||||
delete[] in;
|
||||
valid = false;
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
result = new_result;
|
||||
memcpy(result + cur_size, out, have);
|
||||
memcpy(result+cur_size, out, have);
|
||||
cur_size += have;
|
||||
|
||||
} while (ret == Z_OK);
|
||||
|
||||
inflateEnd(&strm);
|
||||
delete [] in;
|
||||
|
||||
if (ret != Z_STREAM_END)
|
||||
{
|
||||
free(result);
|
||||
valid = false;
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//result now points to the decompressed LLSD block
|
||||
{
|
||||
outsize = cur_size;
|
||||
valid = true;
|
||||
outsize= cur_size;
|
||||
valid = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
// </FS:Beq pp Rye>
|
||||
|
||||
char* strip_deprecated_header(char* in, llssize& cur_size, llssize* header_size)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -871,8 +871,6 @@ LL_COMMON_API std::string zip_llsd(LLSD& data);
|
|||
|
||||
|
||||
LL_COMMON_API U8* unzip_llsdNavMesh( bool& valid, size_t& outsize,std::istream& is, S32 size);
|
||||
// <FS:Beq pp Rye> Add non-allocating variants of unzip_llsd
|
||||
LL_COMMON_API U8* unzip_llsdNavMesh(bool& valid, unsigned int& outsize, const U8* in, S32 size);
|
||||
|
||||
// returns a pointer to the array or past the array if the deprecated header exists
|
||||
LL_COMMON_API char* strip_deprecated_header(char* in, llssize& cur_size, llssize* header_size = nullptr);
|
||||
|
|
|
|||
|
|
@ -513,3 +513,29 @@ std::istream& operator>>(std::istream& str, const char *tocheck)
|
|||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
int cat_streambuf::underflow()
|
||||
{
|
||||
if (gptr() == egptr())
|
||||
{
|
||||
// here because our buffer is empty
|
||||
std::streamsize size = 0;
|
||||
// Until we've run out of mInputs, try reading the first of them
|
||||
// into mBuffer. If that fetches some characters, break the loop.
|
||||
while (! mInputs.empty()
|
||||
&& ! (size = mInputs.front()->sgetn(mBuffer.data(), mBuffer.size())))
|
||||
{
|
||||
// We tried to read mInputs.front() but got zero characters.
|
||||
// Discard the first streambuf and try the next one.
|
||||
mInputs.pop_front();
|
||||
}
|
||||
// Either we ran out of mInputs or we succeeded in reading some
|
||||
// characters, that is, size != 0. Tell base class what we have.
|
||||
setg(mBuffer.data(), mBuffer.data(), mBuffer.data() + size);
|
||||
}
|
||||
// If we fell out of the above loop with mBuffer still empty, return
|
||||
// eof(), otherwise return the next character.
|
||||
return (gptr() == egptr())
|
||||
? std::char_traits<char>::eof()
|
||||
: std::char_traits<char>::to_int_type(*gptr());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,8 +27,10 @@
|
|||
#ifndef LL_STREAM_TOOLS_H
|
||||
#define LL_STREAM_TOOLS_H
|
||||
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// unless specifed otherwise these all return input_stream.good()
|
||||
|
||||
|
|
@ -113,6 +115,27 @@ LL_COMMON_API std::streamsize fullread(
|
|||
|
||||
LL_COMMON_API std::istream& operator>>(std::istream& str, const char *tocheck);
|
||||
|
||||
/**
|
||||
* cat_streambuf is a std::streambuf subclass that accepts a variadic number
|
||||
* of std::streambuf* (e.g. some_istream.rdbuf()) and virtually concatenates
|
||||
* their contents.
|
||||
*/
|
||||
// derived from https://stackoverflow.com/a/49441066/5533635
|
||||
class cat_streambuf: public std::streambuf
|
||||
{
|
||||
private:
|
||||
std::deque<std::streambuf*> mInputs;
|
||||
std::vector<char> mBuffer;
|
||||
|
||||
public:
|
||||
// only valid for std::streambuf* arguments
|
||||
template <typename... Inputs>
|
||||
cat_streambuf(Inputs... inputs):
|
||||
mInputs{inputs...},
|
||||
mBuffer(1024)
|
||||
{}
|
||||
|
||||
int underflow() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1682,13 +1682,13 @@ std::basic_string<T> LLStringUtilBase<T>::quote(const string_type& str,
|
|||
// For whatever reason, we must quote this string.
|
||||
string_type result;
|
||||
result.push_back('"');
|
||||
for (typename const S8 c : str)
|
||||
for (typename string_type::const_iterator ci(str.begin()), cend(str.end()); ci != cend; ++ci)
|
||||
{
|
||||
if (c == '"')
|
||||
if (*ci == '"')
|
||||
{
|
||||
result.append(escape);
|
||||
}
|
||||
result.push_back(c);
|
||||
result.push_back(*ci);
|
||||
}
|
||||
result.push_back('"');
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -40,11 +40,13 @@
|
|||
#include "lluuid.h"
|
||||
#include "llerror.h"
|
||||
#include "llrand.h"
|
||||
#include "llmd5.h"
|
||||
#include "llstring.h"
|
||||
#include "lltimer.h"
|
||||
#include "llthread.h"
|
||||
#include "llmutex.h"
|
||||
#include "llmd5.h"
|
||||
#include "hbxxh.h"
|
||||
|
||||
#include "llprofiler.h"
|
||||
const LLUUID LLUUID::null;
|
||||
const LLTransactionID LLTransactionID::tnull;
|
||||
|
|
@ -403,6 +405,9 @@ LLUUID LLUUID::operator^(const LLUUID& rhs) const
|
|||
return id;
|
||||
}
|
||||
|
||||
// WARNING: this algorithm SHALL NOT be changed. It is also used by the server
|
||||
// and plays a role in some assets validation (e.g. clothing items). Changing
|
||||
// it would cause invalid assets.
|
||||
void LLUUID::combine(const LLUUID& other, LLUUID& result) const
|
||||
{
|
||||
LLMD5 md5_uuid;
|
||||
|
|
@ -860,17 +865,12 @@ void LLUUID::generate()
|
|||
tmp >>= 8;
|
||||
mData[8] = (unsigned char) tmp;
|
||||
|
||||
LLMD5 md5_uuid;
|
||||
|
||||
md5_uuid.update(mData,16);
|
||||
md5_uuid.finalize();
|
||||
md5_uuid.raw_digest(mData);
|
||||
HBXXH128::digest(*this, (const void*)mData, 16);
|
||||
}
|
||||
|
||||
void LLUUID::generate(const std::string& hash_string)
|
||||
{
|
||||
LLMD5 md5_uuid((U8*)hash_string.c_str());
|
||||
md5_uuid.raw_digest(mData);
|
||||
HBXXH128::digest(*this, hash_string);
|
||||
}
|
||||
|
||||
U32 LLUUID::getRandomSeed()
|
||||
|
|
@ -888,13 +888,8 @@ U32 LLUUID::getRandomSeed()
|
|||
seed[7]=(unsigned char)(pid);
|
||||
getSystemTime((uuid_time_t *)(&seed[8]));
|
||||
|
||||
LLMD5 md5_seed;
|
||||
|
||||
md5_seed.update(seed,16);
|
||||
md5_seed.finalize();
|
||||
md5_seed.raw_digest(seed);
|
||||
|
||||
return(*(U32 *)seed);
|
||||
U64 seed64 = HBXXH64::digest((const void*)seed, 16);
|
||||
return U32(seed64) ^ U32(seed64 >> 32);
|
||||
}
|
||||
|
||||
BOOL LLUUID::parseUUID(const std::string& buf, LLUUID* value)
|
||||
|
|
|
|||
|
|
@ -171,6 +171,14 @@ public:
|
|||
U16 getCRC16() const;
|
||||
U32 getCRC32() const;
|
||||
|
||||
// Returns a 64 bits digest of the UUID, by XORing its two 64 bits long
|
||||
// words. HB
|
||||
inline U64 getDigest64() const
|
||||
{
|
||||
U64* tmp = (U64*)mData;
|
||||
return tmp[0] ^ tmp[1];
|
||||
}
|
||||
|
||||
static BOOL validate(const std::string& in_string); // Validate that the UUID string is legal.
|
||||
|
||||
static const LLUUID null;
|
||||
|
|
@ -222,29 +230,23 @@ public:
|
|||
LLAssetID makeAssetID(const LLUUID& session) const;
|
||||
};
|
||||
|
||||
// Generate a hash of an LLUUID object using the boost hash templates.
|
||||
|
||||
// <FS:ND> GCC 4.9 does not like the specialization in form of boost::hash but rather wants a namespace
|
||||
// template <>
|
||||
// struct boost::hash<LLUUID>
|
||||
namespace boost { template <> struct hash<LLUUID>
|
||||
// </FS:ND>
|
||||
// std::hash implementation for LLUUID
|
||||
namespace std
|
||||
{
|
||||
typedef LLUUID argument_type;
|
||||
typedef std::size_t result_type;
|
||||
result_type operator()(argument_type const& s) const
|
||||
{
|
||||
result_type seed(0);
|
||||
template<> struct hash<LLUUID>
|
||||
{
|
||||
inline size_t operator()(const LLUUID& id) const noexcept
|
||||
{
|
||||
return (size_t)id.getDigest64();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
for (S32 i = 0; i < UUID_BYTES; ++i)
|
||||
{
|
||||
boost::hash_combine(seed, s.mData[i]);
|
||||
}
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
} // <FS:ND/> close namespace
|
||||
// For use with boost containers.
|
||||
inline size_t hash_value(const LLUUID& id) noexcept
|
||||
{
|
||||
return (size_t)id.getDigest64();
|
||||
}
|
||||
|
||||
// <FS:Ansariel> UUID hash calculation
|
||||
struct FSUUIDHash
|
||||
|
|
@ -256,17 +258,4 @@ struct FSUUIDHash
|
|||
};
|
||||
// </FS:Ansariel> UUID hash calculation
|
||||
|
||||
// Adapt boost hash to std hash
|
||||
namespace std
|
||||
{
|
||||
template<> struct hash<LLUUID>
|
||||
{
|
||||
std::size_t operator()(LLUUID const& s) const noexcept
|
||||
{
|
||||
return boost::hash<LLUUID>()(s);
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif // LL_LLUUID_H
|
||||
|
|
|
|||
|
|
@ -110,12 +110,12 @@ namespace tut
|
|||
"import os\n"
|
||||
"import sys\n"
|
||||
"\n"
|
||||
// Don't forget that this Python script is written to some
|
||||
// temp directory somewhere! Its __file__ is useless in
|
||||
// finding indra/lib/python. Use our __FILE__, with
|
||||
// raw-string syntax to deal with Windows pathnames.
|
||||
"mydir = os.path.dirname(r'" << __FILE__ << "')\n"
|
||||
"from llbase import llsd\n"
|
||||
"try:\n"
|
||||
// new freestanding llsd package
|
||||
" import llsd\n"
|
||||
"except ImportError:\n"
|
||||
// older llbase.llsd module
|
||||
" from llbase import llsd\n"
|
||||
"\n"
|
||||
"class ProtocolError(Exception):\n"
|
||||
" def __init__(self, msg, data):\n"
|
||||
|
|
@ -126,26 +126,26 @@ namespace tut
|
|||
" pass\n"
|
||||
"\n"
|
||||
"def get():\n"
|
||||
" hdr = ''\n"
|
||||
" while ':' not in hdr and len(hdr) < 20:\n"
|
||||
" hdr += sys.stdin.read(1)\n"
|
||||
" hdr = []\n"
|
||||
" while b':' not in hdr and len(hdr) < 20:\n"
|
||||
" hdr.append(sys.stdin.buffer.read(1))\n"
|
||||
" if not hdr:\n"
|
||||
" sys.exit(0)\n"
|
||||
" if not hdr.endswith(':'):\n"
|
||||
" if not hdr[-1] == b':':\n"
|
||||
" raise ProtocolError('Expected len:data, got %r' % hdr, hdr)\n"
|
||||
" try:\n"
|
||||
" length = int(hdr[:-1])\n"
|
||||
" length = int(b''.join(hdr[:-1]))\n"
|
||||
" except ValueError:\n"
|
||||
" raise ProtocolError('Non-numeric len %r' % hdr[:-1], hdr[:-1])\n"
|
||||
" parts = []\n"
|
||||
" received = 0\n"
|
||||
" while received < length:\n"
|
||||
" parts.append(sys.stdin.read(length - received))\n"
|
||||
" parts.append(sys.stdin.buffer.read(length - received))\n"
|
||||
" received += len(parts[-1])\n"
|
||||
" data = ''.join(parts)\n"
|
||||
" data = b''.join(parts)\n"
|
||||
" assert len(data) == length\n"
|
||||
" try:\n"
|
||||
" return llsd.parse(data.encode())\n"
|
||||
" return llsd.parse(data)\n"
|
||||
// Seems the old indra.base.llsd module didn't properly
|
||||
// convert IndexError (from running off end of string) to
|
||||
// LLSDParseError.
|
||||
|
|
@ -185,11 +185,11 @@ namespace tut
|
|||
" return _reply\n"
|
||||
"\n"
|
||||
"def put(req):\n"
|
||||
" sys.stdout.write(':'.join((str(len(req)), req)))\n"
|
||||
" sys.stdout.buffer.write(b'%d:%b' % (len(req), req))\n"
|
||||
" sys.stdout.flush()\n"
|
||||
"\n"
|
||||
"def send(pump, data):\n"
|
||||
" put(llsd.format_notation(dict(pump=pump, data=data)).decode())\n"
|
||||
" put(llsd.format_notation(dict(pump=pump, data=data)))\n"
|
||||
"\n"
|
||||
"def request(pump, data):\n"
|
||||
" # we expect 'data' is a dict\n"
|
||||
|
|
|
|||
|
|
@ -46,20 +46,24 @@ typedef U32 uint32_t;
|
|||
|
||||
#include "boost/range.hpp"
|
||||
#include "boost/foreach.hpp"
|
||||
#include "boost/function.hpp"
|
||||
#include "boost/bind.hpp"
|
||||
#include "boost/phoenix/bind/bind_function.hpp"
|
||||
#include "boost/phoenix/core/argument.hpp"
|
||||
using namespace boost::phoenix;
|
||||
|
||||
#include "../llsd.h"
|
||||
#include "../llsdserialize.h"
|
||||
#include "llsd.h"
|
||||
#include "llsdserialize.h"
|
||||
#include "llsdutil.h"
|
||||
#include "../llformat.h"
|
||||
#include "llformat.h"
|
||||
#include "llmemorystream.h"
|
||||
|
||||
#include "../test/lltut.h"
|
||||
#include "../test/namedtempfile.h"
|
||||
#include "stringize.h"
|
||||
#include <functional>
|
||||
|
||||
typedef std::function<void(const LLSD& data, std::ostream& str)> FormatterFunction;
|
||||
typedef std::function<bool(std::istream& istr, LLSD& data, llssize max_bytes)> ParserFunction;
|
||||
|
||||
std::vector<U8> string_to_vector(const std::string& str)
|
||||
{
|
||||
|
|
@ -112,7 +116,7 @@ namespace tut
|
|||
mSD = LLUUID::null;
|
||||
expected = "<llsd><uuid /></llsd>\n";
|
||||
xml_test("null uuid", expected);
|
||||
|
||||
|
||||
mSD = LLUUID("c96f9b1e-f589-4100-9774-d98643ce0bed");
|
||||
expected = "<llsd><uuid>c96f9b1e-f589-4100-9774-d98643ce0bed</uuid></llsd>\n";
|
||||
xml_test("uuid", expected);
|
||||
|
|
@ -136,7 +140,7 @@ namespace tut
|
|||
expected = "<llsd><binary encoding=\"base64\">aGVsbG8=</binary></llsd>\n";
|
||||
xml_test("binary", expected);
|
||||
}
|
||||
|
||||
|
||||
template<> template<>
|
||||
void sd_xml_object::test<2>()
|
||||
{
|
||||
|
|
@ -225,7 +229,7 @@ namespace tut
|
|||
expected = "<llsd><map><key>baz</key><undef /><key>foo</key><string>bar</string></map></llsd>\n";
|
||||
xml_test("2 element map", expected);
|
||||
}
|
||||
|
||||
|
||||
template<> template<>
|
||||
void sd_xml_object::test<6>()
|
||||
{
|
||||
|
|
@ -241,7 +245,7 @@ namespace tut
|
|||
expected = "<llsd><binary encoding=\"base64\">Nnw2fGFzZGZoYXBweWJveHw2MGU0NGVjNS0zMDVjLTQzYzItOWExOS1iNGI4OWIxYWUyYTZ8NjBlNDRlYzUtMzA1Yy00M2MyLTlhMTktYjRiODliMWFlMmE2fDYwZTQ0ZWM1LTMwNWMtNDNjMi05YTE5LWI0Yjg5YjFhZTJhNnwwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDB8N2ZmZmZmZmZ8N2ZmZmZmZmZ8MHwwfDgyMDAwfDQ1MGZlMzk0LTI5MDQtYzlhZC0yMTRjLWEwN2ViN2ZlZWMyOXwoTm8gRGVzY3JpcHRpb24pfDB8MTB8MA==</binary></llsd>\n";
|
||||
xml_test("binary", expected);
|
||||
}
|
||||
|
||||
|
||||
class TestLLSDSerializeData
|
||||
{
|
||||
public:
|
||||
|
|
@ -250,9 +254,34 @@ namespace tut
|
|||
|
||||
void doRoundTripTests(const std::string&);
|
||||
void checkRoundTrip(const std::string&, const LLSD& v);
|
||||
|
||||
LLPointer<LLSDFormatter> mFormatter;
|
||||
LLPointer<LLSDParser> mParser;
|
||||
|
||||
void setFormatterParser(LLPointer<LLSDFormatter> formatter, LLPointer<LLSDParser> parser)
|
||||
{
|
||||
mFormatter = [formatter](const LLSD& data, std::ostream& str)
|
||||
{
|
||||
formatter->format(data, str);
|
||||
};
|
||||
// this lambda must be mutable since otherwise the bound 'parser'
|
||||
// is assumed to point to a const LLSDParser
|
||||
mParser = [parser](std::istream& istr, LLSD& data, llssize max_bytes) mutable
|
||||
{
|
||||
// reset() call is needed since test code re-uses parser object
|
||||
parser->reset();
|
||||
return (parser->parse(istr, data, max_bytes) > 0);
|
||||
};
|
||||
}
|
||||
|
||||
void setParser(bool (*parser)(LLSD&, std::istream&, llssize))
|
||||
{
|
||||
// why does LLSDSerialize::deserialize() reverse the parse() params??
|
||||
mParser = [parser](std::istream& istr, LLSD& data, llssize max_bytes)
|
||||
{
|
||||
return (parser(data, istr, max_bytes) > 0);
|
||||
};
|
||||
}
|
||||
|
||||
FormatterFunction mFormatter;
|
||||
ParserFunction mParser;
|
||||
};
|
||||
|
||||
TestLLSDSerializeData::TestLLSDSerializeData()
|
||||
|
|
@ -265,12 +294,11 @@ namespace tut
|
|||
|
||||
void TestLLSDSerializeData::checkRoundTrip(const std::string& msg, const LLSD& v)
|
||||
{
|
||||
std::stringstream stream;
|
||||
mFormatter->format(v, stream);
|
||||
std::stringstream stream;
|
||||
mFormatter(v, stream);
|
||||
//LL_INFOS() << "checkRoundTrip: length " << stream.str().length() << LL_ENDL;
|
||||
LLSD w;
|
||||
mParser->reset(); // reset() call is needed since test code re-uses mParser
|
||||
mParser->parse(stream, w, stream.str().size());
|
||||
mParser(stream, w, stream.str().size());
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -299,52 +327,52 @@ namespace tut
|
|||
fillmap(root[key], width, depth - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TestLLSDSerializeData::doRoundTripTests(const std::string& msg)
|
||||
{
|
||||
LLSD v;
|
||||
checkRoundTrip(msg + " undefined", v);
|
||||
|
||||
|
||||
v = true;
|
||||
checkRoundTrip(msg + " true bool", v);
|
||||
|
||||
|
||||
v = false;
|
||||
checkRoundTrip(msg + " false bool", v);
|
||||
|
||||
|
||||
v = 1;
|
||||
checkRoundTrip(msg + " positive int", v);
|
||||
|
||||
|
||||
v = 0;
|
||||
checkRoundTrip(msg + " zero int", v);
|
||||
|
||||
|
||||
v = -1;
|
||||
checkRoundTrip(msg + " negative int", v);
|
||||
|
||||
|
||||
v = 1234.5f;
|
||||
checkRoundTrip(msg + " positive float", v);
|
||||
|
||||
|
||||
v = 0.0f;
|
||||
checkRoundTrip(msg + " zero float", v);
|
||||
|
||||
|
||||
v = -1234.5f;
|
||||
checkRoundTrip(msg + " negative float", v);
|
||||
|
||||
|
||||
// FIXME: need a NaN test
|
||||
|
||||
|
||||
v = LLUUID::null;
|
||||
checkRoundTrip(msg + " null uuid", v);
|
||||
|
||||
|
||||
LLUUID newUUID;
|
||||
newUUID.generate();
|
||||
v = newUUID;
|
||||
checkRoundTrip(msg + " new uuid", v);
|
||||
|
||||
|
||||
v = "";
|
||||
checkRoundTrip(msg + " empty string", v);
|
||||
|
||||
|
||||
v = "some string";
|
||||
checkRoundTrip(msg + " non-empty string", v);
|
||||
|
||||
|
||||
v =
|
||||
"Second Life is a 3-D virtual world entirely built and owned by its residents. "
|
||||
"Since opening to the public in 2003, it has grown explosively and today is "
|
||||
|
|
@ -372,7 +400,7 @@ namespace tut
|
|||
for (U32 block = 0x000000; block <= 0x10ffff; block += block_size)
|
||||
{
|
||||
std::ostringstream out;
|
||||
|
||||
|
||||
for (U32 c = block; c < block + block_size; ++c)
|
||||
{
|
||||
if (c <= 0x000001f
|
||||
|
|
@ -386,7 +414,7 @@ namespace tut
|
|||
if (0x00fdd0 <= c && c <= 0x00fdef) { continue; }
|
||||
if ((c & 0x00fffe) == 0x00fffe) { continue; }
|
||||
// see Unicode standard, section 15.8
|
||||
|
||||
|
||||
if (c <= 0x00007f)
|
||||
{
|
||||
out << (char)(c & 0x7f);
|
||||
|
|
@ -410,55 +438,55 @@ namespace tut
|
|||
out << (char)(0x80 | ((c >> 0) & 0x3f));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
v = out.str();
|
||||
|
||||
std::ostringstream blockmsg;
|
||||
blockmsg << msg << " unicode string block 0x" << std::hex << block;
|
||||
checkRoundTrip(blockmsg.str(), v);
|
||||
}
|
||||
|
||||
|
||||
LLDate epoch;
|
||||
v = epoch;
|
||||
checkRoundTrip(msg + " epoch date", v);
|
||||
|
||||
|
||||
LLDate aDay("2002-12-07T05:07:15.00Z");
|
||||
v = aDay;
|
||||
checkRoundTrip(msg + " date", v);
|
||||
|
||||
|
||||
LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/");
|
||||
v = path;
|
||||
checkRoundTrip(msg + " url", v);
|
||||
|
||||
|
||||
const char source[] = "it must be a blue moon again";
|
||||
std::vector<U8> data;
|
||||
// note, includes terminating '\0'
|
||||
copy(&source[0], &source[sizeof(source)], back_inserter(data));
|
||||
|
||||
|
||||
v = data;
|
||||
checkRoundTrip(msg + " binary", v);
|
||||
|
||||
|
||||
v = LLSD::emptyMap();
|
||||
checkRoundTrip(msg + " empty map", v);
|
||||
|
||||
|
||||
v = LLSD::emptyMap();
|
||||
v["name"] = "luke"; //v.insert("name", "luke");
|
||||
v["age"] = 3; //v.insert("age", 3);
|
||||
checkRoundTrip(msg + " map", v);
|
||||
|
||||
|
||||
v.clear();
|
||||
v["a"]["1"] = true;
|
||||
v["b"]["0"] = false;
|
||||
checkRoundTrip(msg + " nested maps", v);
|
||||
|
||||
|
||||
v = LLSD::emptyArray();
|
||||
checkRoundTrip(msg + " empty array", v);
|
||||
|
||||
|
||||
v = LLSD::emptyArray();
|
||||
v.append("ali");
|
||||
v.append(28);
|
||||
checkRoundTrip(msg + " array", v);
|
||||
|
||||
|
||||
v.clear();
|
||||
v[0][0] = true;
|
||||
v[1][0] = false;
|
||||
|
|
@ -468,7 +496,7 @@ namespace tut
|
|||
fillmap(v, 10, 3); // 10^6 maps
|
||||
checkRoundTrip(msg + " many nested maps", v);
|
||||
}
|
||||
|
||||
|
||||
typedef tut::test_group<TestLLSDSerializeData> TestLLSDSerializeGroup;
|
||||
typedef TestLLSDSerializeGroup::object TestLLSDSerializeObject;
|
||||
TestLLSDSerializeGroup gTestLLSDSerializeGroup("llsd serialization");
|
||||
|
|
@ -476,35 +504,106 @@ namespace tut
|
|||
template<> template<>
|
||||
void TestLLSDSerializeObject::test<1>()
|
||||
{
|
||||
mFormatter = new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_PRETTY_BINARY);
|
||||
mParser = new LLSDNotationParser();
|
||||
setFormatterParser(new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_PRETTY_BINARY),
|
||||
new LLSDNotationParser());
|
||||
doRoundTripTests("pretty binary notation serialization");
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void TestLLSDSerializeObject::test<2>()
|
||||
{
|
||||
mFormatter = new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_NONE);
|
||||
mParser = new LLSDNotationParser();
|
||||
setFormatterParser(new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_NONE),
|
||||
new LLSDNotationParser());
|
||||
doRoundTripTests("raw binary notation serialization");
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void TestLLSDSerializeObject::test<3>()
|
||||
{
|
||||
mFormatter = new LLSDXMLFormatter();
|
||||
mParser = new LLSDXMLParser();
|
||||
setFormatterParser(new LLSDXMLFormatter(), new LLSDXMLParser());
|
||||
doRoundTripTests("xml serialization");
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void TestLLSDSerializeObject::test<4>()
|
||||
{
|
||||
mFormatter = new LLSDBinaryFormatter();
|
||||
mParser = new LLSDBinaryParser();
|
||||
setFormatterParser(new LLSDBinaryFormatter(), new LLSDBinaryParser());
|
||||
doRoundTripTests("binary serialization");
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void TestLLSDSerializeObject::test<5>()
|
||||
{
|
||||
mFormatter = [](const LLSD& sd, std::ostream& str)
|
||||
{
|
||||
LLSDSerialize::serialize(sd, str, LLSDSerialize::LLSD_BINARY);
|
||||
};
|
||||
setParser(LLSDSerialize::deserialize);
|
||||
doRoundTripTests("serialize(LLSD_BINARY)");
|
||||
};
|
||||
|
||||
template<> template<>
|
||||
void TestLLSDSerializeObject::test<6>()
|
||||
{
|
||||
mFormatter = [](const LLSD& sd, std::ostream& str)
|
||||
{
|
||||
LLSDSerialize::serialize(sd, str, LLSDSerialize::LLSD_XML);
|
||||
};
|
||||
setParser(LLSDSerialize::deserialize);
|
||||
doRoundTripTests("serialize(LLSD_XML)");
|
||||
};
|
||||
|
||||
template<> template<>
|
||||
void TestLLSDSerializeObject::test<7>()
|
||||
{
|
||||
mFormatter = [](const LLSD& sd, std::ostream& str)
|
||||
{
|
||||
LLSDSerialize::serialize(sd, str, LLSDSerialize::LLSD_NOTATION);
|
||||
};
|
||||
setParser(LLSDSerialize::deserialize);
|
||||
// In this test, serialize(LLSD_NOTATION) emits a header recognized by
|
||||
// deserialize().
|
||||
doRoundTripTests("serialize(LLSD_NOTATION)");
|
||||
};
|
||||
|
||||
template<> template<>
|
||||
void TestLLSDSerializeObject::test<8>()
|
||||
{
|
||||
setFormatterParser(new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_NONE),
|
||||
new LLSDNotationParser());
|
||||
setParser(LLSDSerialize::deserialize);
|
||||
// This is an interesting test because LLSDNotationFormatter does not
|
||||
// emit an llsd/notation header.
|
||||
doRoundTripTests("LLSDNotationFormatter -> deserialize");
|
||||
};
|
||||
|
||||
template<> template<>
|
||||
void TestLLSDSerializeObject::test<9>()
|
||||
{
|
||||
setFormatterParser(new LLSDXMLFormatter(false, "", LLSDFormatter::OPTIONS_NONE),
|
||||
new LLSDXMLParser());
|
||||
setParser(LLSDSerialize::deserialize);
|
||||
// This is an interesting test because LLSDXMLFormatter does not
|
||||
// emit an LLSD/XML header.
|
||||
doRoundTripTests("LLSDXMLFormatter -> deserialize");
|
||||
};
|
||||
|
||||
/*==========================================================================*|
|
||||
// We do not expect this test to succeed. Without a header, neither
|
||||
// notation LLSD nor binary LLSD reliably start with a distinct character,
|
||||
// the way XML LLSD starts with '<'. By convention, we default to notation
|
||||
// rather than binary.
|
||||
template<> template<>
|
||||
void TestLLSDSerializeObject::test<10>()
|
||||
{
|
||||
setFormatterParser(new LLSDBinaryFormatter(false, "", LLSDFormatter::OPTIONS_NONE),
|
||||
new LLSDBinaryParser());
|
||||
setParser(LLSDSerialize::deserialize);
|
||||
// This is an interesting test because LLSDBinaryFormatter does not
|
||||
// emit an LLSD/Binary header.
|
||||
doRoundTripTests("LLSDBinaryFormatter -> deserialize");
|
||||
};
|
||||
|*==========================================================================*/
|
||||
|
||||
/**
|
||||
* @class TestLLSDParsing
|
||||
|
|
@ -555,7 +654,7 @@ namespace tut
|
|||
public:
|
||||
TestLLSDXMLParsing() {}
|
||||
};
|
||||
|
||||
|
||||
typedef tut::test_group<TestLLSDXMLParsing> TestLLSDXMLParsingGroup;
|
||||
typedef TestLLSDXMLParsingGroup::object TestLLSDXMLParsingObject;
|
||||
TestLLSDXMLParsingGroup gTestLLSDXMLParsingGroup("llsd XML parsing");
|
||||
|
|
@ -586,8 +685,8 @@ namespace tut
|
|||
LLSD(),
|
||||
LLSDParser::PARSE_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<> template<>
|
||||
void TestLLSDXMLParsingObject::test<2>()
|
||||
{
|
||||
|
|
@ -596,7 +695,7 @@ namespace tut
|
|||
v["amy"] = 23;
|
||||
v["bob"] = LLSD();
|
||||
v["cam"] = 1.23;
|
||||
|
||||
|
||||
ensureParse(
|
||||
"unknown data type",
|
||||
"<llsd><map>"
|
||||
|
|
@ -607,16 +706,16 @@ namespace tut
|
|||
v,
|
||||
v.size() + 1);
|
||||
}
|
||||
|
||||
|
||||
template<> template<>
|
||||
void TestLLSDXMLParsingObject::test<3>()
|
||||
{
|
||||
// test handling of nested bad data
|
||||
|
||||
|
||||
LLSD v;
|
||||
v["amy"] = 23;
|
||||
v["cam"] = 1.23;
|
||||
|
||||
|
||||
ensureParse(
|
||||
"map with html",
|
||||
"<llsd><map>"
|
||||
|
|
@ -626,7 +725,7 @@ namespace tut
|
|||
"</map></llsd>",
|
||||
v,
|
||||
v.size() + 1);
|
||||
|
||||
|
||||
v.clear();
|
||||
v["amy"] = 23;
|
||||
v["cam"] = 1.23;
|
||||
|
|
@ -639,7 +738,7 @@ namespace tut
|
|||
"</map></llsd>",
|
||||
v,
|
||||
v.size() + 1);
|
||||
|
||||
|
||||
v.clear();
|
||||
v["amy"] = 23;
|
||||
v["bob"] = LLSD::emptyMap();
|
||||
|
|
@ -661,7 +760,7 @@ namespace tut
|
|||
v[0] = 23;
|
||||
v[1] = LLSD();
|
||||
v[2] = 1.23;
|
||||
|
||||
|
||||
ensureParse(
|
||||
"array value of html",
|
||||
"<llsd><array>"
|
||||
|
|
@ -671,7 +770,7 @@ namespace tut
|
|||
"</array></llsd>",
|
||||
v,
|
||||
v.size() + 1);
|
||||
|
||||
|
||||
v.clear();
|
||||
v[0] = 23;
|
||||
v[1] = LLSD::emptyMap();
|
||||
|
|
@ -1225,7 +1324,7 @@ namespace tut
|
|||
vec[0] = 'a'; vec[1] = 'b'; vec[2] = 'c';
|
||||
vec[3] = '3'; vec[4] = '2'; vec[5] = '1';
|
||||
LLSD value = vec;
|
||||
|
||||
|
||||
vec.resize(11);
|
||||
vec[0] = 'b'; // for binary
|
||||
vec[5] = 'a'; vec[6] = 'b'; vec[7] = 'c';
|
||||
|
|
@ -1694,85 +1793,83 @@ namespace tut
|
|||
ensureBinaryAndXML("map", test);
|
||||
}
|
||||
|
||||
struct TestPythonCompatible
|
||||
// helper for TestPythonCompatible
|
||||
static std::string import_llsd("import os.path\n"
|
||||
"import sys\n"
|
||||
"try:\n"
|
||||
// new freestanding llsd package
|
||||
" import llsd\n"
|
||||
"except ImportError:\n"
|
||||
// older llbase.llsd module
|
||||
" from llbase import llsd\n");
|
||||
|
||||
// helper for TestPythonCompatible
|
||||
template <typename CONTENT>
|
||||
void python(const std::string& desc, const CONTENT& script, int expect=0)
|
||||
{
|
||||
TestPythonCompatible():
|
||||
// Note the peculiar insertion of __FILE__ into this string. Since
|
||||
// this script is being written into a platform-dependent temp
|
||||
// directory, we can't locate indra/lib/python relative to
|
||||
// Python's __file__. Use __FILE__ instead, navigating relative
|
||||
// to this C++ source file. Use Python raw-string syntax so
|
||||
// Windows pathname backslashes won't mislead Python's string
|
||||
// scanner.
|
||||
import_llsd("import os.path\n"
|
||||
"import sys\n"
|
||||
"from llbase import llsd\n")
|
||||
{}
|
||||
~TestPythonCompatible() {}
|
||||
auto PYTHON(LLStringUtil::getenv("PYTHON"));
|
||||
ensure("Set $PYTHON to the Python interpreter", !PYTHON.empty());
|
||||
|
||||
std::string import_llsd;
|
||||
|
||||
template <typename CONTENT>
|
||||
void python(const std::string& desc, const CONTENT& script, int expect=0)
|
||||
{
|
||||
auto PYTHON(LLStringUtil::getenv("PYTHON"));
|
||||
ensure("Set $PYTHON to the Python interpreter", !PYTHON.empty());
|
||||
|
||||
NamedTempFile scriptfile("py", script);
|
||||
NamedTempFile scriptfile("py", script);
|
||||
|
||||
#if LL_WINDOWS
|
||||
std::string q("\"");
|
||||
std::string qPYTHON(q + PYTHON + q);
|
||||
std::string qscript(q + scriptfile.getName() + q);
|
||||
int rc = _spawnl(_P_WAIT, PYTHON.c_str(), qPYTHON.c_str(), qscript.c_str(), NULL);
|
||||
if (rc == -1)
|
||||
{
|
||||
char buffer[256];
|
||||
strerror_s(buffer, errno); // C++ can infer the buffer size! :-O
|
||||
ensure(STRINGIZE("Couldn't run Python " << desc << "script: " << buffer), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc), rc, expect);
|
||||
}
|
||||
std::string q("\"");
|
||||
std::string qPYTHON(q + PYTHON + q);
|
||||
std::string qscript(q + scriptfile.getName() + q);
|
||||
int rc = _spawnl(_P_WAIT, PYTHON.c_str(), qPYTHON.c_str(), qscript.c_str(), NULL);
|
||||
if (rc == -1)
|
||||
{
|
||||
char buffer[256];
|
||||
strerror_s(buffer, errno); // C++ can infer the buffer size! :-O
|
||||
ensure(STRINGIZE("Couldn't run Python " << desc << "script: " << buffer), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc), rc, expect);
|
||||
}
|
||||
|
||||
#else // LL_DARWIN, LL_LINUX
|
||||
LLProcess::Params params;
|
||||
params.executable = PYTHON;
|
||||
params.args.add(scriptfile.getName());
|
||||
LLProcessPtr py(LLProcess::create(params));
|
||||
ensure(STRINGIZE("Couldn't launch " << desc << " script"), bool(py));
|
||||
// Implementing timeout would mean messing with alarm() and
|
||||
// catching SIGALRM... later maybe...
|
||||
int status(0);
|
||||
if (waitpid(py->getProcessID(), &status, 0) == -1)
|
||||
LLProcess::Params params;
|
||||
params.executable = PYTHON;
|
||||
params.args.add(scriptfile.getName());
|
||||
LLProcessPtr py(LLProcess::create(params));
|
||||
ensure(STRINGIZE("Couldn't launch " << desc << " script"), bool(py));
|
||||
// Implementing timeout would mean messing with alarm() and
|
||||
// catching SIGALRM... later maybe...
|
||||
int status(0);
|
||||
if (waitpid(py->getProcessID(), &status, 0) == -1)
|
||||
{
|
||||
int waitpid_errno(errno);
|
||||
ensure_equals(STRINGIZE("Couldn't retrieve rc from " << desc << " script: "
|
||||
"waitpid() errno " << waitpid_errno),
|
||||
waitpid_errno, ECHILD);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (WIFEXITED(status))
|
||||
{
|
||||
int waitpid_errno(errno);
|
||||
ensure_equals(STRINGIZE("Couldn't retrieve rc from " << desc << " script: "
|
||||
"waitpid() errno " << waitpid_errno),
|
||||
waitpid_errno, ECHILD);
|
||||
int rc(WEXITSTATUS(status));
|
||||
ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc),
|
||||
rc, expect);
|
||||
}
|
||||
else if (WIFSIGNALED(status))
|
||||
{
|
||||
ensure(STRINGIZE(desc << " script terminated by signal " << WTERMSIG(status)),
|
||||
false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (WIFEXITED(status))
|
||||
{
|
||||
int rc(WEXITSTATUS(status));
|
||||
ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc),
|
||||
rc, expect);
|
||||
}
|
||||
else if (WIFSIGNALED(status))
|
||||
{
|
||||
ensure(STRINGIZE(desc << " script terminated by signal " << WTERMSIG(status)),
|
||||
false);
|
||||
}
|
||||
else
|
||||
{
|
||||
ensure(STRINGIZE(desc << " script produced impossible status " << status),
|
||||
false);
|
||||
}
|
||||
ensure(STRINGIZE(desc << " script produced impossible status " << status),
|
||||
false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
struct TestPythonCompatible
|
||||
{
|
||||
TestPythonCompatible() {}
|
||||
~TestPythonCompatible() {}
|
||||
};
|
||||
|
||||
typedef tut::test_group<TestPythonCompatible> TestPythonCompatibleGroup;
|
||||
|
|
@ -1798,25 +1895,33 @@ namespace tut
|
|||
"print('Running on', sys.platform)\n");
|
||||
}
|
||||
|
||||
// helper for test<3>
|
||||
static void writeLLSDArray(std::ostream& out, const LLSD& array)
|
||||
// helper for test<3> - test<7>
|
||||
static void writeLLSDArray(const FormatterFunction& serialize,
|
||||
std::ostream& out, const LLSD& array)
|
||||
{
|
||||
BOOST_FOREACH(LLSD item, llsd::inArray(array))
|
||||
for (const LLSD& item : llsd::inArray(array))
|
||||
{
|
||||
LLSDSerialize::toNotation(item, out);
|
||||
// It's important to separate with newlines because Python's llsd
|
||||
// module doesn't support parsing from a file stream, only from a
|
||||
// string, so we have to know how much of the file to read into a
|
||||
// string.
|
||||
out << '\n';
|
||||
// It's important to delimit the entries in this file somehow
|
||||
// because, although Python's llsd.parse() can accept a file
|
||||
// stream, the XML parser expects EOF after a single outer element
|
||||
// -- it doesn't just stop. So we must extract a sequence of bytes
|
||||
// strings from the file. But since one of the serialization
|
||||
// formats we want to test is binary, we can't pick any single
|
||||
// byte value as a delimiter! Use a binary integer length prefix
|
||||
// instead.
|
||||
std::ostringstream buffer;
|
||||
serialize(item, buffer);
|
||||
auto buffstr{ buffer.str() };
|
||||
int bufflen{ static_cast<int>(buffstr.length()) };
|
||||
out.write(reinterpret_cast<const char*>(&bufflen), sizeof(bufflen));
|
||||
out.write(buffstr.c_str(), buffstr.length());
|
||||
}
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void TestPythonCompatibleObject::test<3>()
|
||||
// helper for test<3> - test<7>
|
||||
static void toPythonUsing(const std::string& desc,
|
||||
const FormatterFunction& serialize)
|
||||
{
|
||||
set_test_name("verify sequence to Python");
|
||||
|
||||
LLSD cdata(LLSDArray(17)(3.14)
|
||||
("This string\n"
|
||||
"has several\n"
|
||||
|
|
@ -1836,7 +1941,7 @@ namespace tut
|
|||
" except StopIteration:\n"
|
||||
" pass\n"
|
||||
" else:\n"
|
||||
" assert False, 'Too many data items'\n";
|
||||
" raise AssertionError('Too many data items')\n";
|
||||
|
||||
// Create an llsdXXXXXX file containing 'data' serialized to
|
||||
// notation.
|
||||
|
|
@ -1845,32 +1950,128 @@ namespace tut
|
|||
// takes a callable. To this callable it passes the
|
||||
// std::ostream with which it's writing the
|
||||
// NamedTempFile.
|
||||
boost::bind(writeLLSDArray, _1, cdata));
|
||||
[serialize, cdata]
|
||||
(std::ostream& out)
|
||||
{ writeLLSDArray(serialize, out, cdata); });
|
||||
|
||||
python("read C++ notation",
|
||||
python("read C++ " + desc,
|
||||
placeholders::arg1 <<
|
||||
import_llsd <<
|
||||
"def parse_each(iterable):\n"
|
||||
" for item in iterable:\n"
|
||||
" yield llsd.parse(item)\n" <<
|
||||
pydata <<
|
||||
"from functools import partial\n"
|
||||
"import io\n"
|
||||
"import struct\n"
|
||||
"lenformat = struct.Struct('i')\n"
|
||||
"def parse_each(inf):\n"
|
||||
" for rawlen in iter(partial(inf.read, lenformat.size), b''):\n"
|
||||
" len = lenformat.unpack(rawlen)[0]\n"
|
||||
// Since llsd.parse() has no max_bytes argument, instead of
|
||||
// passing the input stream directly to parse(), read the item
|
||||
// into a distinct bytes object and parse that.
|
||||
" data = inf.read(len)\n"
|
||||
" try:\n"
|
||||
" frombytes = llsd.parse(data)\n"
|
||||
" except llsd.LLSDParseError as err:\n"
|
||||
" print(f'*** {err}')\n"
|
||||
" print(f'Bad content:\\n{data!r}')\n"
|
||||
" raise\n"
|
||||
// Also try parsing from a distinct stream.
|
||||
" stream = io.BytesIO(data)\n"
|
||||
" fromstream = llsd.parse(stream)\n"
|
||||
" assert frombytes == fromstream\n"
|
||||
" yield frombytes\n"
|
||||
<< pydata <<
|
||||
// Don't forget raw-string syntax for Windows pathnames.
|
||||
"verify(parse_each(open(r'" << file.getName() << "', 'rb')))\n");
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void TestPythonCompatibleObject::test<3>()
|
||||
{
|
||||
set_test_name("to Python using LLSDSerialize::serialize(LLSD_XML)");
|
||||
toPythonUsing("LLSD_XML",
|
||||
[](const LLSD& sd, std::ostream& out)
|
||||
{ LLSDSerialize::serialize(sd, out, LLSDSerialize::LLSD_XML); });
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void TestPythonCompatibleObject::test<4>()
|
||||
{
|
||||
set_test_name("verify sequence from Python");
|
||||
set_test_name("to Python using LLSDSerialize::serialize(LLSD_NOTATION)");
|
||||
toPythonUsing("LLSD_NOTATION",
|
||||
[](const LLSD& sd, std::ostream& out)
|
||||
{ LLSDSerialize::serialize(sd, out, LLSDSerialize::LLSD_NOTATION); });
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void TestPythonCompatibleObject::test<5>()
|
||||
{
|
||||
set_test_name("to Python using LLSDSerialize::serialize(LLSD_BINARY)");
|
||||
toPythonUsing("LLSD_BINARY",
|
||||
[](const LLSD& sd, std::ostream& out)
|
||||
{ LLSDSerialize::serialize(sd, out, LLSDSerialize::LLSD_BINARY); });
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void TestPythonCompatibleObject::test<6>()
|
||||
{
|
||||
set_test_name("to Python using LLSDSerialize::toXML()");
|
||||
toPythonUsing("toXML()", LLSDSerialize::toXML);
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void TestPythonCompatibleObject::test<7>()
|
||||
{
|
||||
set_test_name("to Python using LLSDSerialize::toNotation()");
|
||||
toPythonUsing("toNotation()", LLSDSerialize::toNotation);
|
||||
}
|
||||
|
||||
/*==========================================================================*|
|
||||
template<> template<>
|
||||
void TestPythonCompatibleObject::test<8>()
|
||||
{
|
||||
set_test_name("to Python using LLSDSerialize::toBinary()");
|
||||
// We don't expect this to work because, without a header,
|
||||
// llsd.parse() will assume notation rather than binary.
|
||||
toPythonUsing("toBinary()", LLSDSerialize::toBinary);
|
||||
}
|
||||
|*==========================================================================*/
|
||||
|
||||
// helper for test<8> - test<12>
|
||||
bool itemFromStream(std::istream& istr, LLSD& item, const ParserFunction& parse)
|
||||
{
|
||||
// reset the output value for debugging clarity
|
||||
item.clear();
|
||||
// We use an int length prefix as a foolproof delimiter even for
|
||||
// binary serialized streams.
|
||||
int length{ 0 };
|
||||
istr.read(reinterpret_cast<char*>(&length), sizeof(length));
|
||||
// return parse(istr, item, length);
|
||||
// Sadly, as of 2022-12-01 it seems we can't really trust our LLSD
|
||||
// parsers to honor max_bytes: this test works better when we read
|
||||
// each item into its own distinct LLMemoryStream, instead of passing
|
||||
// the original istr with a max_bytes constraint.
|
||||
std::vector<U8> buffer(length);
|
||||
istr.read(reinterpret_cast<char*>(buffer.data()), length);
|
||||
LLMemoryStream stream(buffer.data(), length);
|
||||
return parse(stream, item, length);
|
||||
}
|
||||
|
||||
// helper for test<8> - test<12>
|
||||
void fromPythonUsing(const std::string& pyformatter,
|
||||
const ParserFunction& parse=
|
||||
[](std::istream& istr, LLSD& data, llssize max_bytes)
|
||||
{ return LLSDSerialize::deserialize(data, istr, max_bytes); })
|
||||
{
|
||||
// Create an empty data file. This is just a placeholder for our
|
||||
// script to write into. Create it to establish a unique name that
|
||||
// we know.
|
||||
NamedTempFile file("llsd", "");
|
||||
|
||||
python("write Python notation",
|
||||
python("Python " + pyformatter,
|
||||
placeholders::arg1 <<
|
||||
import_llsd <<
|
||||
"import struct\n"
|
||||
"lenformat = struct.Struct('i')\n"
|
||||
"DATA = [\n"
|
||||
" 17,\n"
|
||||
" 3.14,\n"
|
||||
|
|
@ -1881,34 +2082,87 @@ namespace tut
|
|||
"]\n"
|
||||
// Don't forget raw-string syntax for Windows pathnames.
|
||||
// N.B. Using 'print' implicitly adds newlines.
|
||||
"with open(r'" << file.getName() << "', 'w') as f:\n"
|
||||
"with open(r'" << file.getName() << "', 'wb') as f:\n"
|
||||
" for item in DATA:\n"
|
||||
" print(llsd.format_notation(item).decode(), file=f)\n");
|
||||
" serialized = llsd." << pyformatter << "(item)\n"
|
||||
" f.write(lenformat.pack(len(serialized)))\n"
|
||||
" f.write(serialized)\n");
|
||||
|
||||
std::ifstream inf(file.getName().c_str());
|
||||
LLSD item;
|
||||
// Notice that we're not doing anything special to parse out the
|
||||
// newlines: LLSDSerialize::fromNotation ignores them. While it would
|
||||
// seem they're not strictly necessary, going in this direction, we
|
||||
// want to ensure that notation-separated-by-newlines works in both
|
||||
// directions -- since in practice, a given file might be read by
|
||||
// either language.
|
||||
ensure_equals("Failed to read LLSD::Integer from Python",
|
||||
LLSDSerialize::fromNotation(item, inf, LLSDSerialize::SIZE_UNLIMITED),
|
||||
1);
|
||||
ensure_equals(item.asInteger(), 17);
|
||||
ensure_equals("Failed to read LLSD::Real from Python",
|
||||
LLSDSerialize::fromNotation(item, inf, LLSDSerialize::SIZE_UNLIMITED),
|
||||
1);
|
||||
ensure_approximately_equals("Bad LLSD::Real value from Python",
|
||||
item.asReal(), 3.14, 7); // 7 bits ~= 0.01
|
||||
ensure_equals("Failed to read LLSD::String from Python",
|
||||
LLSDSerialize::fromNotation(item, inf, LLSDSerialize::SIZE_UNLIMITED),
|
||||
1);
|
||||
ensure_equals(item.asString(),
|
||||
"This string\n"
|
||||
"has several\n"
|
||||
"lines.");
|
||||
|
||||
try
|
||||
{
|
||||
ensure("Failed to read LLSD::Integer from Python",
|
||||
itemFromStream(inf, item, parse));
|
||||
ensure_equals(item.asInteger(), 17);
|
||||
ensure("Failed to read LLSD::Real from Python",
|
||||
itemFromStream(inf, item, parse));
|
||||
ensure_approximately_equals("Bad LLSD::Real value from Python",
|
||||
item.asReal(), 3.14, 7); // 7 bits ~= 0.01
|
||||
ensure("Failed to read LLSD::String from Python",
|
||||
itemFromStream(inf, item, parse));
|
||||
ensure_equals(item.asString(),
|
||||
"This string\n"
|
||||
"has several\n"
|
||||
"lines.");
|
||||
}
|
||||
catch (const tut::failure& err)
|
||||
{
|
||||
std::cout << "for " << err.what() << ", item = " << item << std::endl;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void TestPythonCompatibleObject::test<8>()
|
||||
{
|
||||
set_test_name("from Python XML using LLSDSerialize::deserialize()");
|
||||
fromPythonUsing("format_xml");
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void TestPythonCompatibleObject::test<9>()
|
||||
{
|
||||
set_test_name("from Python notation using LLSDSerialize::deserialize()");
|
||||
fromPythonUsing("format_notation");
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void TestPythonCompatibleObject::test<10>()
|
||||
{
|
||||
set_test_name("from Python binary using LLSDSerialize::deserialize()");
|
||||
fromPythonUsing("format_binary");
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void TestPythonCompatibleObject::test<11>()
|
||||
{
|
||||
set_test_name("from Python XML using fromXML()");
|
||||
// fromXML()'s optional 3rd param isn't max_bytes, it's emit_errors
|
||||
fromPythonUsing("format_xml",
|
||||
[](std::istream& istr, LLSD& data, llssize)
|
||||
{ return LLSDSerialize::fromXML(data, istr) > 0; });
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void TestPythonCompatibleObject::test<12>()
|
||||
{
|
||||
set_test_name("from Python notation using fromNotation()");
|
||||
fromPythonUsing("format_notation",
|
||||
[](std::istream& istr, LLSD& data, llssize max_bytes)
|
||||
{ return LLSDSerialize::fromNotation(data, istr, max_bytes) > 0; });
|
||||
}
|
||||
|
||||
/*==========================================================================*|
|
||||
template<> template<>
|
||||
void TestPythonCompatibleObject::test<13>()
|
||||
{
|
||||
set_test_name("from Python binary using fromBinary()");
|
||||
// We don't expect this to work because format_binary() emits a
|
||||
// header, but fromBinary() won't recognize a header.
|
||||
fromPythonUsing("format_binary",
|
||||
[](std::istream& istr, LLSD& data, llssize max_bytes)
|
||||
{ return LLSDSerialize::fromBinary(data, istr, max_bytes) > 0; });
|
||||
}
|
||||
|*==========================================================================*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,14 @@ public:
|
|||
|
||||
static const LLMaterialID null;
|
||||
|
||||
// Returns a 64 bits digest of the material Id, by XORing its two 64 bits
|
||||
// long words. HB
|
||||
inline U64 getDigest64() const
|
||||
{
|
||||
U64* tmp = (U64*)mID;
|
||||
return tmp[0] ^ tmp[1];
|
||||
}
|
||||
|
||||
private:
|
||||
void parseFromBinary(const LLSD::Binary& pMaterialID);
|
||||
void copyFromOtherMaterialID(const LLMaterialID& pOtherMaterialID);
|
||||
|
|
@ -74,5 +82,23 @@ private:
|
|||
U8 mID[MATERIAL_ID_SIZE];
|
||||
} ;
|
||||
|
||||
// std::hash implementation for LLMaterialID
|
||||
namespace std
|
||||
{
|
||||
template<> struct hash<LLMaterialID>
|
||||
{
|
||||
inline size_t operator()(const LLMaterialID& id) const noexcept
|
||||
{
|
||||
return (size_t)id.getDigest64();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// For use with boost containers.
|
||||
inline size_t hash_value(const LLMaterialID& id) noexcept
|
||||
{
|
||||
return (size_t)id.getDigest64();
|
||||
}
|
||||
|
||||
#endif // LL_LLMATERIALID_H
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
#include "llconvexdecomposition.h"
|
||||
#include "llsdserialize.h"
|
||||
#include "llvector4a.h"
|
||||
#include "llmd5.h"
|
||||
#include "hbxxh.h"
|
||||
#include "llcontrol.h"
|
||||
|
||||
#ifdef LL_USESYSTEMLIBS
|
||||
|
|
@ -1580,7 +1580,7 @@ LLSD LLMeshSkinInfo::asLLSD(bool include_joints, bool lock_scale_if_joint_positi
|
|||
void LLMeshSkinInfo::updateHash()
|
||||
{
|
||||
// get hash of data relevant to render batches
|
||||
LLMD5 hash;
|
||||
HBXXH64 hash;
|
||||
|
||||
//mJointNames
|
||||
for (auto& name : mJointNames)
|
||||
|
|
@ -1591,24 +1591,19 @@ void LLMeshSkinInfo::updateHash()
|
|||
}
|
||||
|
||||
//mJointNums
|
||||
hash.update((U8*)&(mJointNums[0]), sizeof(S32) * mJointNums.size());
|
||||
hash.update((const void*)mJointNums.data(), sizeof(S32) * mJointNums.size());
|
||||
|
||||
//mInvBindMatrix
|
||||
F32* src = mInvBindMatrix[0].getF32ptr();
|
||||
|
||||
for (int i = 0; i < mInvBindMatrix.size() * 16; ++i)
|
||||
for (size_t i = 0, count = mInvBindMatrix.size() * 16; i < count; ++i)
|
||||
{
|
||||
S32 t = llround(src[i] * 10000.f);
|
||||
hash.update((U8*)&t, sizeof(S32));
|
||||
hash.update((const void*)&t, sizeof(S32));
|
||||
}
|
||||
//hash.update((U8*)&(mInvBindMatrix[0]), sizeof(LLMatrix4a) * mInvBindMatrix.size());
|
||||
//hash.update((const void*)mInvBindMatrix.data(), sizeof(LLMatrix4a) * mInvBindMatrix.size());
|
||||
|
||||
hash.finalize();
|
||||
|
||||
U64 digest[2];
|
||||
hash.raw_digest((U8*) digest);
|
||||
|
||||
mHash = digest[0];
|
||||
mHash = hash.digest();
|
||||
}
|
||||
|
||||
U32 LLMeshSkinInfo::sizeBytes() const
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@
|
|||
*/
|
||||
|
||||
#include "llmodelloader.h"
|
||||
|
||||
#include "llapp.h"
|
||||
#include "llsdserialize.h"
|
||||
#include "lljoint.h"
|
||||
#include "llcallbacklist.h"
|
||||
|
|
@ -381,7 +383,10 @@ bool LLModelLoader::isAlive(LLModelLoader* loader)
|
|||
|
||||
void LLModelLoader::loadModelCallback()
|
||||
{
|
||||
mLoadCallback(mScene,mModelList,mLod, mOpaqueData);
|
||||
if (!LLApp::isExiting())
|
||||
{
|
||||
mLoadCallback(mScene, mModelList, mLod, mOpaqueData);
|
||||
}
|
||||
|
||||
while (!isStopped())
|
||||
{ //wait until this thread is stopped before deleting self
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
#include "llinventoryobserver.h"
|
||||
#include "llinventorypanel.h"
|
||||
#include "lllocaltextureobject.h"
|
||||
#include "llmd5.h"
|
||||
#include "llmd5.h" // <FS:Ansariel> [Legacy Bake]
|
||||
#include "llnotificationsutil.h"
|
||||
#include "lloutfitobserver.h"
|
||||
#include "llsidepanelappearance.h"
|
||||
|
|
|
|||
|
|
@ -41,7 +41,9 @@
|
|||
#include "llinventorybridge.h"
|
||||
#include "llinventoryfunctions.h"
|
||||
#include "llinventoryobserver.h"
|
||||
#include "llmd5.h"
|
||||
#include "llnotificationsutil.h"
|
||||
#include "llmd5.h"
|
||||
#include "lloutfitobserver.h"
|
||||
#include "lloutfitslist.h"
|
||||
#include "llselectmgr.h"
|
||||
|
|
|
|||
|
|
@ -311,6 +311,15 @@ BOOL LLFilePicker::getOpenFile(ELoadFilter filter, bool blocking)
|
|||
return success;
|
||||
}
|
||||
|
||||
BOOL LLFilePicker::getOpenFileModeless(ELoadFilter filter,
|
||||
void (*callback)(bool, std::vector<std::string> &, void*),
|
||||
void *userdata)
|
||||
{
|
||||
// not supposed to be used yet, use LLFilePickerThread
|
||||
LL_ERRS() << "NOT IMPLEMENTED" << LL_ENDL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL LLFilePicker::getMultipleOpenFiles(ELoadFilter filter, bool blocking)
|
||||
{
|
||||
if( mLocked )
|
||||
|
|
@ -388,6 +397,15 @@ BOOL LLFilePicker::getMultipleOpenFiles(ELoadFilter filter, bool blocking)
|
|||
return success;
|
||||
}
|
||||
|
||||
BOOL LLFilePicker::getMultipleOpenFilesModeless(ELoadFilter filter,
|
||||
void (*callback)(bool, std::vector<std::string> &, void*),
|
||||
void *userdata )
|
||||
{
|
||||
// not supposed to be used yet, use LLFilePickerThread
|
||||
LL_ERRS() << "NOT IMPLEMENTED" << LL_ENDL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename, bool blocking)
|
||||
{
|
||||
if( mLocked )
|
||||
|
|
@ -641,6 +659,16 @@ BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename,
|
|||
return success;
|
||||
}
|
||||
|
||||
BOOL LLFilePicker::getSaveFileModeless(ESaveFilter filter,
|
||||
const std::string& filename,
|
||||
void (*callback)(bool, std::string&, void*),
|
||||
void *userdata)
|
||||
{
|
||||
// not supposed to be used yet, use LLFilePickerThread
|
||||
LL_ERRS() << "NOT IMPLEMENTED" << LL_ENDL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#elif LL_DARWIN
|
||||
|
||||
std::unique_ptr<std::vector<std::string>> LLFilePicker::navOpenFilterProc(ELoadFilter filter) //(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode)
|
||||
|
|
@ -747,129 +775,123 @@ bool LLFilePicker::doNavChooseDialog(ELoadFilter filter)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool LLFilePicker::doNavChooseDialogModeless(ELoadFilter filter,
|
||||
void (*callback)(bool, std::vector<std::string> &,void*),
|
||||
void *userdata)
|
||||
{
|
||||
// if local file browsing is turned off, return without opening dialog
|
||||
if ( check_local_file_access_enabled() == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<std::vector<std::string>> allowed_types=navOpenFilterProc(filter);
|
||||
|
||||
doLoadDialogModeless(allowed_types.get(),
|
||||
mPickOptions,
|
||||
callback,
|
||||
userdata);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void set_nav_save_data(LLFilePicker::ESaveFilter filter, std::string &extension, std::string &type, std::string &creator)
|
||||
{
|
||||
switch (filter)
|
||||
{
|
||||
case LLFilePicker::FFSAVE_WAV:
|
||||
type = "WAVE";
|
||||
creator = "TVOD";
|
||||
extension = "wav";
|
||||
break;
|
||||
case LLFilePicker::FFSAVE_TGA:
|
||||
type = "TPIC";
|
||||
creator = "prvw";
|
||||
extension = "tga";
|
||||
break;
|
||||
case LLFilePicker::FFSAVE_TGAPNG:
|
||||
type = "PNG";
|
||||
creator = "prvw";
|
||||
extension = "png,tga";
|
||||
break;
|
||||
case LLFilePicker::FFSAVE_BMP:
|
||||
type = "BMPf";
|
||||
creator = "prvw";
|
||||
extension = "bmp";
|
||||
break;
|
||||
case LLFilePicker::FFSAVE_JPEG:
|
||||
type = "JPEG";
|
||||
creator = "prvw";
|
||||
extension = "jpeg";
|
||||
break;
|
||||
case LLFilePicker::FFSAVE_PNG:
|
||||
type = "PNG ";
|
||||
creator = "prvw";
|
||||
extension = "png";
|
||||
break;
|
||||
case LLFilePicker::FFSAVE_AVI:
|
||||
type = "\?\?\?\?";
|
||||
creator = "\?\?\?\?";
|
||||
extension = "mov";
|
||||
break;
|
||||
|
||||
case LLFilePicker::FFSAVE_ANIM:
|
||||
type = "\?\?\?\?";
|
||||
creator = "\?\?\?\?";
|
||||
extension = "xaf";
|
||||
break;
|
||||
|
||||
#ifdef _CORY_TESTING
|
||||
case LLFilePicker::FFSAVE_GEOMETRY:
|
||||
type = "\?\?\?\?";
|
||||
creator = "\?\?\?\?";
|
||||
extension = "slg";
|
||||
break;
|
||||
#endif
|
||||
|
||||
case LLFilePicker::FFSAVE_XML:
|
||||
type = "\?\?\?\?";
|
||||
creator = "\?\?\?\?";
|
||||
extension = "xml";
|
||||
break;
|
||||
|
||||
case LLFilePicker::FFSAVE_RAW:
|
||||
type = "\?\?\?\?";
|
||||
creator = "\?\?\?\?";
|
||||
extension = "raw";
|
||||
break;
|
||||
|
||||
case LLFilePicker::FFSAVE_J2C:
|
||||
type = "\?\?\?\?";
|
||||
creator = "prvw";
|
||||
extension = "j2c";
|
||||
break;
|
||||
|
||||
case LLFilePicker::FFSAVE_SCRIPT:
|
||||
type = "LSL ";
|
||||
creator = "\?\?\?\?";
|
||||
extension = "lsl";
|
||||
break;
|
||||
|
||||
case LLFilePicker::FFSAVE_ALL:
|
||||
default:
|
||||
type = "\?\?\?\?";
|
||||
creator = "\?\?\?\?";
|
||||
extension = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool LLFilePicker::doNavSaveDialog(ESaveFilter filter, const std::string& filename)
|
||||
{
|
||||
|
||||
// Setup the type, creator, and extension
|
||||
std::string extension, type, creator;
|
||||
|
||||
switch (filter)
|
||||
{
|
||||
case FFSAVE_WAV:
|
||||
type = "WAVE";
|
||||
creator = "TVOD";
|
||||
extension = "wav";
|
||||
break;
|
||||
case FFSAVE_TGA:
|
||||
type = "TPIC";
|
||||
creator = "prvw";
|
||||
extension = "tga";
|
||||
break;
|
||||
case FFSAVE_TGAPNG:
|
||||
type = "PNG";
|
||||
creator = "prvw";
|
||||
extension = "png,tga";
|
||||
break;
|
||||
case FFSAVE_BMP:
|
||||
type = "BMPf";
|
||||
creator = "prvw";
|
||||
extension = "bmp";
|
||||
break;
|
||||
case FFSAVE_JPEG:
|
||||
type = "JPEG";
|
||||
creator = "prvw";
|
||||
extension = "jpeg";
|
||||
break;
|
||||
case FFSAVE_PNG:
|
||||
type = "PNG ";
|
||||
creator = "prvw";
|
||||
extension = "png";
|
||||
break;
|
||||
case FFSAVE_AVI:
|
||||
type = "\?\?\?\?";
|
||||
creator = "\?\?\?\?";
|
||||
extension = "mov";
|
||||
break;
|
||||
|
||||
case FFSAVE_ANIM:
|
||||
type = "\?\?\?\?";
|
||||
creator = "\?\?\?\?";
|
||||
extension = "xaf";
|
||||
break;
|
||||
|
||||
#ifdef _CORY_TESTING
|
||||
case FFSAVE_GEOMETRY:
|
||||
type = "\?\?\?\?";
|
||||
creator = "\?\?\?\?";
|
||||
extension = "slg";
|
||||
break;
|
||||
#endif
|
||||
// <FS:TS> Compile fix
|
||||
// case FFSAVE_XML:
|
||||
// type = "\?\?\?\?";
|
||||
// creator = "\?\?\?\?";
|
||||
// extension = "xml";
|
||||
// break;
|
||||
// </FS:TS> Compile fix
|
||||
|
||||
case FFSAVE_RAW:
|
||||
type = "\?\?\?\?";
|
||||
creator = "\?\?\?\?";
|
||||
extension = "raw";
|
||||
break;
|
||||
|
||||
case FFSAVE_J2C:
|
||||
type = "\?\?\?\?";
|
||||
creator = "prvw";
|
||||
extension = "j2c";
|
||||
break;
|
||||
|
||||
case FFSAVE_SCRIPT:
|
||||
type = "LSL ";
|
||||
creator = "\?\?\?\?";
|
||||
extension = "lsl";
|
||||
break;
|
||||
// <FS:CR> Export filter
|
||||
case FFSAVE_EXPORT:
|
||||
type = "OXP ";
|
||||
creator = "\?\?\?\?";
|
||||
extension = "oxp";
|
||||
break;
|
||||
case FFSAVE_COLLADA:
|
||||
type = "DAE ";
|
||||
creator = "\?\?\?\?";
|
||||
extension = "dae";
|
||||
break;
|
||||
// <FS:CR> CSV Filter
|
||||
case FFSAVE_CSV:
|
||||
type = "CSV ";
|
||||
creator = "\?\?\?\?";
|
||||
extension = "csv";
|
||||
break;
|
||||
// </FS:CR>
|
||||
case FFSAVE_BEAM:
|
||||
case FFSAVE_XML:
|
||||
type = "XML ";
|
||||
creator = "\?\?\?\?";
|
||||
extension = "xml";
|
||||
break;
|
||||
case FFSAVE_ALL:
|
||||
default:
|
||||
type = "\?\?\?\?";
|
||||
creator = "\?\?\?\?";
|
||||
extension = "";
|
||||
break;
|
||||
}
|
||||
set_nav_save_data(filter, extension, type, creator);
|
||||
|
||||
std::string namestring = filename;
|
||||
if (namestring.empty()) namestring="Untitled";
|
||||
|
||||
// if (! boost::algorithm::ends_with(namestring, extension) )
|
||||
// {
|
||||
// namestring = namestring + "." + extension;
|
||||
//
|
||||
// }
|
||||
|
||||
gViewerWindow->getWindow()->beforeDialog();
|
||||
|
||||
// Run the dialog
|
||||
|
|
@ -890,6 +912,30 @@ bool LLFilePicker::doNavSaveDialog(ESaveFilter filter, const std::string& filena
|
|||
return false;
|
||||
}
|
||||
|
||||
bool LLFilePicker::doNavSaveDialogModeless(ESaveFilter filter,
|
||||
const std::string& filename,
|
||||
void (*callback)(bool, std::string&, void*),
|
||||
void *userdata)
|
||||
{
|
||||
// Setup the type, creator, and extension
|
||||
std::string extension, type, creator;
|
||||
|
||||
set_nav_save_data(filter, extension, type, creator);
|
||||
|
||||
std::string namestring = filename;
|
||||
if (namestring.empty()) namestring="Untitled";
|
||||
|
||||
// Run the dialog
|
||||
doSaveDialogModeless(&namestring,
|
||||
&type,
|
||||
&creator,
|
||||
&extension,
|
||||
mPickOptions,
|
||||
callback,
|
||||
userdata);
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL LLFilePicker::getOpenFile(ELoadFilter filter, bool blocking)
|
||||
{
|
||||
if( mLocked )
|
||||
|
|
@ -945,18 +991,52 @@ BOOL LLFilePicker::getOpenFile(ELoadFilter filter, bool blocking)
|
|||
return success;
|
||||
}
|
||||
|
||||
|
||||
BOOL LLFilePicker::getOpenFileModeless(ELoadFilter filter,
|
||||
void (*callback)(bool, std::vector<std::string> &, void*),
|
||||
void *userdata)
|
||||
{
|
||||
if( mLocked )
|
||||
return FALSE;
|
||||
|
||||
// if local file browsing is turned off, return without opening dialog
|
||||
if ( check_local_file_access_enabled() == false )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
reset();
|
||||
|
||||
mPickOptions &= ~F_MULTIPLE;
|
||||
mPickOptions |= F_FILE;
|
||||
|
||||
if (filter == FFLOAD_DIRECTORY) //This should only be called from lldirpicker.
|
||||
{
|
||||
|
||||
mPickOptions |= ( F_NAV_SUPPORT | F_DIRECTORY );
|
||||
mPickOptions &= ~F_FILE;
|
||||
}
|
||||
|
||||
if (filter == FFLOAD_ALL) // allow application bundles etc. to be traversed; important for DEV-16869, but generally useful
|
||||
{
|
||||
mPickOptions |= F_NAV_SUPPORT;
|
||||
}
|
||||
|
||||
return doNavChooseDialogModeless(filter, callback, userdata);
|
||||
}
|
||||
|
||||
BOOL LLFilePicker::getMultipleOpenFiles(ELoadFilter filter, bool blocking)
|
||||
{
|
||||
if( mLocked )
|
||||
return FALSE;
|
||||
|
||||
BOOL success = FALSE;
|
||||
|
||||
// if local file browsing is turned off, return without opening dialog
|
||||
if ( check_local_file_access_enabled() == false )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL success = FALSE;
|
||||
|
||||
reset();
|
||||
|
||||
|
|
@ -990,6 +1070,29 @@ BOOL LLFilePicker::getMultipleOpenFiles(ELoadFilter filter, bool blocking)
|
|||
return success;
|
||||
}
|
||||
|
||||
|
||||
BOOL LLFilePicker::getMultipleOpenFilesModeless(ELoadFilter filter,
|
||||
void (*callback)(bool, std::vector<std::string> &, void*),
|
||||
void *userdata )
|
||||
{
|
||||
if( mLocked )
|
||||
return FALSE;
|
||||
|
||||
// if local file browsing is turned off, return without opening dialog
|
||||
if ( check_local_file_access_enabled() == false )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
reset();
|
||||
|
||||
mPickOptions |= F_FILE;
|
||||
|
||||
mPickOptions |= F_MULTIPLE;
|
||||
|
||||
return doNavChooseDialogModeless(filter, callback, userdata);
|
||||
}
|
||||
|
||||
BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename, bool blocking)
|
||||
{
|
||||
|
||||
|
|
@ -1030,6 +1133,27 @@ BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename,
|
|||
LLFrameTimer::updateFrameTime();
|
||||
return success;
|
||||
}
|
||||
|
||||
BOOL LLFilePicker::getSaveFileModeless(ESaveFilter filter,
|
||||
const std::string& filename,
|
||||
void (*callback)(bool, std::string&, void*),
|
||||
void *userdata)
|
||||
{
|
||||
if( mLocked )
|
||||
return false;
|
||||
|
||||
// if local file browsing is turned off, return without opening dialog
|
||||
if ( check_local_file_access_enabled() == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
reset();
|
||||
|
||||
mPickOptions &= ~F_MULTIPLE;
|
||||
|
||||
return doNavSaveDialogModeless(filter, filename, callback, userdata);
|
||||
}
|
||||
//END LL_DARWIN
|
||||
|
||||
#elif LL_LINUX
|
||||
|
|
@ -1829,6 +1953,15 @@ BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename,
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL LLFilePicker::getSaveFileModeless(ESaveFilter filter,
|
||||
const std::string& filename,
|
||||
void (*callback)(bool, std::string&, void*),
|
||||
void *userdata)
|
||||
{
|
||||
LL_ERRS() << "NOT IMPLEMENTED" << LL_ENDL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL LLFilePicker::getOpenFile( ELoadFilter filter, bool blocking )
|
||||
{
|
||||
// if local file browsing is turned off, return without opening dialog
|
||||
|
|
@ -1854,6 +1987,14 @@ BOOL LLFilePicker::getOpenFile( ELoadFilter filter, bool blocking )
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL LLFilePicker::getOpenFileModeless(ELoadFilter filter,
|
||||
void (*callback)(bool, std::vector<std::string> &, void*),
|
||||
void *userdata)
|
||||
{
|
||||
LL_ERRS() << "NOT IMPLEMENTED" << LL_ENDL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter, bool blocking)
|
||||
{
|
||||
// if local file browsing is turned off, return without opening dialog
|
||||
|
|
@ -1867,6 +2008,14 @@ BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter, bool blocking)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL LLFilePicker::getMultipleOpenFilesModeless(ELoadFilter filter,
|
||||
void (*callback)(bool, std::vector<std::string> &, void*),
|
||||
void *userdata )
|
||||
{
|
||||
LL_ERRS() << "NOT IMPLEMENTED" << LL_ENDL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#endif // LL_GTK
|
||||
|
||||
#else // not implemented
|
||||
|
|
|
|||
|
|
@ -129,8 +129,16 @@ public:
|
|||
|
||||
// open the dialog. This is a modal operation
|
||||
BOOL getSaveFile( ESaveFilter filter = FFSAVE_ALL, const std::string& filename = LLStringUtil::null, bool blocking = true);
|
||||
BOOL getSaveFileModeless(ESaveFilter filter,
|
||||
const std::string& filename,
|
||||
void (*callback)(bool, std::string&, void*),
|
||||
void *userdata);
|
||||
BOOL getOpenFile( ELoadFilter filter = FFLOAD_ALL, bool blocking = true );
|
||||
// Todo: implement getOpenFileModeless and getMultipleOpenFilesModeless
|
||||
// for windows and use directly instead of ugly LLFilePickerThread
|
||||
BOOL getOpenFileModeless( ELoadFilter filter, void (*callback)(bool, std::vector<std::string> &, void*), void *userdata); // MAC only.
|
||||
BOOL getMultipleOpenFiles( ELoadFilter filter = FFLOAD_ALL, bool blocking = true );
|
||||
BOOL getMultipleOpenFilesModeless( ELoadFilter filter, void (*callback)(bool, std::vector<std::string> &, void*), void *userdata ); // MAC only
|
||||
|
||||
// Get the filename(s) found. getFirstFile() sets the pointer to
|
||||
// the start of the structure and allows the start of iteration.
|
||||
|
|
@ -181,8 +189,15 @@ private:
|
|||
std::vector<std::string> mFileVector;
|
||||
|
||||
bool doNavChooseDialog(ELoadFilter filter);
|
||||
bool doNavChooseDialogModeless(ELoadFilter filter,
|
||||
void (*callback)(bool, std::vector<std::string>&, void*),
|
||||
void *userdata);
|
||||
bool doNavSaveDialog(ESaveFilter filter, const std::string& filename);
|
||||
std::unique_ptr<std::vector<std::string>> navOpenFilterProc(ELoadFilter filter);
|
||||
bool doNavSaveDialogModeless(ESaveFilter filter,
|
||||
const std::string& filename,
|
||||
void (*callback)(bool, std::string&, void*),
|
||||
void *userdata);
|
||||
#endif
|
||||
|
||||
#if LL_GTK
|
||||
|
|
|
|||
|
|
@ -41,11 +41,25 @@
|
|||
//void modelessPicker();
|
||||
std::unique_ptr<std::vector<std::string>> doLoadDialog(const std::vector<std::string>* allowed_types,
|
||||
unsigned int flags);
|
||||
|
||||
void doLoadDialogModeless(const std::vector<std::string>* allowed_types,
|
||||
unsigned int flags,
|
||||
void (*callback)(bool, std::vector<std::string>&, void*),
|
||||
void *userdata);
|
||||
|
||||
std::unique_ptr<std::string> doSaveDialog(const std::string* file,
|
||||
const std::string* type,
|
||||
const std::string* creator,
|
||||
const std::string* extension,
|
||||
unsigned int flags);
|
||||
|
||||
void doSaveDialogModeless(const std::string* file,
|
||||
const std::string* type,
|
||||
const std::string* creator,
|
||||
const std::string* extension,
|
||||
unsigned int flags,
|
||||
void (*callback)(bool, std::string&, void*),
|
||||
void *userdata);
|
||||
enum {
|
||||
F_FILE = 0x00000001,
|
||||
F_DIRECTORY = 0x00000002,
|
||||
|
|
|
|||
|
|
@ -29,53 +29,62 @@
|
|||
#include <iostream>
|
||||
#include "llfilepicker_mac.h"
|
||||
|
||||
NSOpenPanel *init_panel(const std::vector<std::string>* allowed_types, unsigned int flags)
|
||||
{
|
||||
int i;
|
||||
|
||||
NSOpenPanel *panel = [NSOpenPanel openPanel];
|
||||
NSMutableArray *fileTypes = nil;
|
||||
|
||||
|
||||
if ( allowed_types && !allowed_types->empty())
|
||||
{
|
||||
fileTypes = [[NSMutableArray alloc] init];
|
||||
|
||||
for (i=0;i<allowed_types->size();++i)
|
||||
{
|
||||
[fileTypes addObject:
|
||||
[NSString stringWithCString:(*allowed_types)[i].c_str()
|
||||
encoding:[NSString defaultCStringEncoding]]];
|
||||
}
|
||||
}
|
||||
|
||||
//[panel setMessage:@"Import one or more files or directories."];
|
||||
[panel setAllowsMultipleSelection: ( (flags & F_MULTIPLE)?true:false ) ];
|
||||
[panel setCanChooseDirectories: ( (flags & F_DIRECTORY)?true:false ) ];
|
||||
[panel setCanCreateDirectories: true];
|
||||
[panel setResolvesAliases: true];
|
||||
[panel setCanChooseFiles: ( (flags & F_FILE)?true:false )];
|
||||
[panel setTreatsFilePackagesAsDirectories: ( flags & F_NAV_SUPPORT ) ];
|
||||
|
||||
if (fileTypes)
|
||||
{
|
||||
[panel setAllowedFileTypes:fileTypes];
|
||||
}
|
||||
else
|
||||
{
|
||||
// I suggest it's better to open the last path and let this default to home dir as necessary
|
||||
// for consistency with other OS X apps
|
||||
//
|
||||
//[panel setDirectoryURL: fileURLWithPath(NSHomeDirectory()) ];
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
|
||||
std::unique_ptr<std::vector<std::string>> doLoadDialog(const std::vector<std::string>* allowed_types,
|
||||
unsigned int flags)
|
||||
{
|
||||
std::unique_ptr<std::vector<std::string>> outfiles;
|
||||
|
||||
@autoreleasepool {
|
||||
int i, result;
|
||||
@autoreleasepool
|
||||
{
|
||||
int result;
|
||||
//Aura TODO: We could init a small window and release it at the end of this routine
|
||||
//for a modeless interface.
|
||||
|
||||
NSOpenPanel *panel = [NSOpenPanel openPanel];
|
||||
//NSString *fileName = nil;
|
||||
NSMutableArray *fileTypes = nil;
|
||||
|
||||
if ( allowed_types && !allowed_types->empty())
|
||||
{
|
||||
fileTypes = [[[NSMutableArray alloc] init] autorelease];
|
||||
|
||||
for (i=0;i<allowed_types->size();++i)
|
||||
{
|
||||
[fileTypes addObject:
|
||||
[NSString stringWithCString:(*allowed_types)[i].c_str()
|
||||
encoding:[NSString defaultCStringEncoding]]];
|
||||
}
|
||||
}
|
||||
|
||||
//[panel setMessage:@"Import one or more files or directories."];
|
||||
[panel setAllowsMultipleSelection: ( (flags & F_MULTIPLE)?true:false ) ];
|
||||
[panel setCanChooseDirectories: ( (flags & F_DIRECTORY)?true:false ) ];
|
||||
[panel setCanCreateDirectories: true];
|
||||
[panel setResolvesAliases: true];
|
||||
[panel setCanChooseFiles: ( (flags & F_FILE)?true:false )];
|
||||
[panel setTreatsFilePackagesAsDirectories: ( flags & F_NAV_SUPPORT ) ];
|
||||
|
||||
if (fileTypes)
|
||||
{
|
||||
[panel setAllowedFileTypes:fileTypes];
|
||||
result = [panel runModal];
|
||||
}
|
||||
else
|
||||
{
|
||||
// I suggest it's better to open the last path and let this default to home dir as necessary
|
||||
// for consistency with other OS X apps
|
||||
//
|
||||
//[panel setDirectoryURL: fileURLWithPath(NSHomeDirectory()) ];
|
||||
result = [panel runModal];
|
||||
}
|
||||
|
||||
NSOpenPanel *panel = init_panel(allowed_types,flags);
|
||||
|
||||
result = [panel runModal];
|
||||
|
||||
if (result == NSOKButton)
|
||||
{
|
||||
|
|
@ -98,6 +107,48 @@ std::unique_ptr<std::vector<std::string>> doLoadDialog(const std::vector<std::st
|
|||
return outfiles;
|
||||
}
|
||||
|
||||
void doLoadDialogModeless(const std::vector<std::string>* allowed_types,
|
||||
unsigned int flags,
|
||||
void (*callback)(bool, std::vector<std::string> &, void*),
|
||||
void *userdata)
|
||||
{
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
// Note: might need to return and save this panel
|
||||
// so that it does not close immediately
|
||||
NSOpenPanel *panel = init_panel(allowed_types,flags);
|
||||
|
||||
[panel beginWithCompletionHandler:^(NSModalResponse result)
|
||||
{
|
||||
std::vector<std::string> outfiles;
|
||||
if (result == NSOKButton)
|
||||
{
|
||||
NSArray *filesToOpen = [panel URLs];
|
||||
int i, count = [filesToOpen count];
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
|
||||
for (i=0; i<count; i++) {
|
||||
NSString *aFile = [[filesToOpen objectAtIndex:i] path];
|
||||
std::string *afilestr = new std::string([aFile UTF8String]);
|
||||
outfiles.push_back(*afilestr);
|
||||
}
|
||||
callback(true, outfiles, userdata);
|
||||
}
|
||||
else // no valid result
|
||||
{
|
||||
callback(false, outfiles, userdata);
|
||||
}
|
||||
}
|
||||
else // cancel
|
||||
{
|
||||
callback(false, outfiles, userdata);
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<std::string> doSaveDialog(const std::string* file,
|
||||
const std::string* type,
|
||||
|
|
@ -106,7 +157,8 @@ std::unique_ptr<std::string> doSaveDialog(const std::string* file,
|
|||
unsigned int flags)
|
||||
{
|
||||
std::unique_ptr<std::string> outfile;
|
||||
@autoreleasepool {
|
||||
@autoreleasepool
|
||||
{
|
||||
NSSavePanel *panel = [NSSavePanel savePanel];
|
||||
|
||||
NSString *extensionns = [NSString stringWithCString:extension->c_str() encoding:[NSString defaultCStringEncoding]];
|
||||
|
|
@ -133,4 +185,48 @@ std::unique_ptr<std::string> doSaveDialog(const std::string* file,
|
|||
return outfile;
|
||||
}
|
||||
|
||||
void doSaveDialogModeless(const std::string* file,
|
||||
const std::string* type,
|
||||
const std::string* creator,
|
||||
const std::string* extension,
|
||||
unsigned int flags,
|
||||
void (*callback)(bool, std::string&, void*),
|
||||
void *userdata)
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSSavePanel *panel = [NSSavePanel savePanel];
|
||||
|
||||
NSString *extensionns = [NSString stringWithCString:extension->c_str() encoding:[NSString defaultCStringEncoding]];
|
||||
NSArray *fileType = [extensionns componentsSeparatedByString:@","];
|
||||
|
||||
//[panel setMessage:@"Save Image File"];
|
||||
[panel setTreatsFilePackagesAsDirectories: ( flags & F_NAV_SUPPORT ) ];
|
||||
[panel setCanSelectHiddenExtension:true];
|
||||
[panel setAllowedFileTypes:fileType];
|
||||
NSString *fileName = [NSString stringWithCString:file->c_str() encoding:[NSString defaultCStringEncoding]];
|
||||
|
||||
NSURL* url = [NSURL fileURLWithPath:fileName];
|
||||
[panel setNameFieldStringValue: fileName];
|
||||
[panel setDirectoryURL: url];
|
||||
|
||||
|
||||
[panel beginWithCompletionHandler:^(NSModalResponse result)
|
||||
{
|
||||
if (result == NSOKButton)
|
||||
{
|
||||
NSURL* url = [panel URL];
|
||||
NSString* p = [url path];
|
||||
std::string outfile([p UTF8String]);
|
||||
|
||||
callback(true, outfile, userdata);
|
||||
}
|
||||
else // cancel
|
||||
{
|
||||
std::string outfile;
|
||||
callback(false, outfile, userdata);
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@
|
|||
#include "bufferarray.h"
|
||||
#include "bufferstream.h"
|
||||
#include "llcorehttputil.h"
|
||||
#include "hbxxh.h"
|
||||
// [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1a)
|
||||
#include "rlvhandler.h"
|
||||
#include "rlvlocks.h"
|
||||
|
|
@ -518,17 +519,16 @@ void LLInventoryModel::getDirectDescendentsOf(const LLUUID& cat_id,
|
|||
items = get_ptr_in_map(mParentChildItemTree, cat_id);
|
||||
}
|
||||
|
||||
LLMD5 LLInventoryModel::hashDirectDescendentNames(const LLUUID& cat_id) const
|
||||
LLInventoryModel::digest_t LLInventoryModel::hashDirectDescendentNames(const LLUUID& cat_id) const
|
||||
{
|
||||
LLInventoryModel::cat_array_t* cat_array;
|
||||
LLInventoryModel::item_array_t* item_array;
|
||||
getDirectDescendentsOf(cat_id,cat_array,item_array);
|
||||
LLMD5 item_name_hash;
|
||||
if (!item_array)
|
||||
{
|
||||
item_name_hash.finalize();
|
||||
return item_name_hash;
|
||||
return LLUUID::null;
|
||||
}
|
||||
HBXXH128 item_name_hash;
|
||||
for (LLInventoryModel::item_array_t::const_iterator iter = item_array->begin();
|
||||
iter != item_array->end();
|
||||
iter++)
|
||||
|
|
@ -538,8 +538,7 @@ LLMD5 LLInventoryModel::hashDirectDescendentNames(const LLUUID& cat_id) const
|
|||
continue;
|
||||
item_name_hash.update(item->getName());
|
||||
}
|
||||
item_name_hash.finalize();
|
||||
return item_name_hash;
|
||||
return item_name_hash.digest();
|
||||
}
|
||||
|
||||
// SJB: Added version to lock the arrays to catch potential logic bugs
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@
|
|||
#include "llpermissionsflags.h"
|
||||
#include "llviewerinventory.h"
|
||||
#include "llstring.h"
|
||||
#include "llmd5.h"
|
||||
#include "httpcommon.h"
|
||||
#include "httprequest.h"
|
||||
#include "httpoptions.h"
|
||||
|
|
@ -268,8 +267,9 @@ public:
|
|||
cat_array_t*& categories,
|
||||
item_array_t*& items) const;
|
||||
|
||||
typedef LLUUID digest_t; // To clarify the actual usage of this "UUID"
|
||||
// Compute a hash of direct descendant names (for detecting child name changes)
|
||||
LLMD5 hashDirectDescendentNames(const LLUUID& cat_id) const;
|
||||
digest_t hashDirectDescendentNames(const LLUUID& cat_id) const;
|
||||
|
||||
// Starting with the object specified, add its descendants to the
|
||||
// array provided, but do not add the inventory object specified
|
||||
|
|
|
|||
|
|
@ -677,7 +677,7 @@ void LLInventoryCategoriesObserver::changed(U32 mask)
|
|||
// computed, or (b) a name has changed.
|
||||
if (!cat_data.mIsNameHashInitialized || (mask & LLInventoryObserver::LABEL))
|
||||
{
|
||||
LLMD5 item_name_hash = gInventory.hashDirectDescendentNames(cat_id);
|
||||
digest_t item_name_hash = gInventory.hashDirectDescendentNames(cat_id);
|
||||
if (cat_data.mItemNameHash != item_name_hash)
|
||||
{
|
||||
cat_data.mIsNameHashInitialized = true;
|
||||
|
|
@ -738,7 +738,7 @@ bool LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t
|
|||
{
|
||||
if(init_name_hash)
|
||||
{
|
||||
LLMD5 item_name_hash = gInventory.hashDirectDescendentNames(cat_id);
|
||||
digest_t item_name_hash = gInventory.hashDirectDescendentNames(cat_id);
|
||||
mCategoryMap.insert(category_map_value_t(cat_id,LLCategoryData(cat_id, cb, version, current_num_known_descendents,item_name_hash)));
|
||||
}
|
||||
else
|
||||
|
|
@ -764,11 +764,10 @@ LLInventoryCategoriesObserver::LLCategoryData::LLCategoryData(
|
|||
, mDescendentsCount(num_descendents)
|
||||
, mIsNameHashInitialized(false)
|
||||
{
|
||||
mItemNameHash.finalize();
|
||||
}
|
||||
|
||||
LLInventoryCategoriesObserver::LLCategoryData::LLCategoryData(
|
||||
const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents, LLMD5 name_hash)
|
||||
const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents, const digest_t& name_hash)
|
||||
|
||||
: mCatID(cat_id)
|
||||
, mCallback(cb)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@
|
|||
#define LL_LLINVENTORYOBSERVERS_H
|
||||
|
||||
#include "lluuid.h"
|
||||
#include "llmd5.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -271,14 +270,15 @@ public:
|
|||
void removeCategory(const LLUUID& cat_id);
|
||||
|
||||
protected:
|
||||
typedef LLUUID digest_t; // To clarify the actual usage of this "UUID"
|
||||
struct LLCategoryData
|
||||
{
|
||||
LLCategoryData(const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents);
|
||||
LLCategoryData(const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents, LLMD5 name_hash);
|
||||
LLCategoryData(const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents, const digest_t& name_hash);
|
||||
callback_t mCallback;
|
||||
S32 mVersion;
|
||||
S32 mDescendentsCount;
|
||||
LLMD5 mItemNameHash;
|
||||
digest_t mItemNameHash;
|
||||
bool mIsNameHashInitialized;
|
||||
LLUUID mCatID;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -93,7 +93,11 @@ private:
|
|||
|
||||
bool operator==(const TEMaterialPair& b) const { return (materialID == b.materialID) && (te == b.te); }
|
||||
};
|
||||
|
||||
|
||||
// definitions follow class
|
||||
friend std::hash<TEMaterialPair>;
|
||||
friend size_t hash_value(const TEMaterialPair&) noexcept;
|
||||
|
||||
friend inline bool operator<(
|
||||
const LLMaterialMgr::TEMaterialPair& lhs,
|
||||
const LLMaterialMgr::TEMaterialPair& rhs)
|
||||
|
|
@ -102,13 +106,6 @@ private:
|
|||
(lhs.materialID < rhs.materialID);
|
||||
}
|
||||
|
||||
struct TEMaterialPairHasher
|
||||
{
|
||||
enum { bucket_size = 8 };
|
||||
size_t operator()(const TEMaterialPair& key_value) const { return *((size_t*)key_value.materialID.get()); } // cheesy, but effective
|
||||
bool operator()(const TEMaterialPair& left, const TEMaterialPair& right) const { return left < right; }
|
||||
};
|
||||
|
||||
typedef std::set<LLMaterialID> material_queue_t;
|
||||
typedef std::map<LLUUID, material_queue_t> get_queue_t;
|
||||
typedef std::pair<const LLUUID, LLMaterialID> pending_material_t;
|
||||
|
|
@ -116,7 +113,7 @@ private:
|
|||
typedef std::map<LLMaterialID, get_callback_t*> get_callback_map_t;
|
||||
|
||||
|
||||
typedef boost::unordered_map<TEMaterialPair, get_callback_te_t*, TEMaterialPairHasher> get_callback_te_map_t;
|
||||
typedef boost::unordered_map<TEMaterialPair, get_callback_te_t*> get_callback_te_map_t;
|
||||
typedef std::set<LLUUID> getall_queue_t;
|
||||
typedef std::map<LLUUID, F64> getall_pending_map_t;
|
||||
typedef std::map<LLUUID, getall_callback_t*> getall_callback_map_t;
|
||||
|
|
@ -146,5 +143,23 @@ private:
|
|||
U32 getMaxEntries(const LLViewerRegion* regionp);
|
||||
};
|
||||
|
||||
// std::hash implementation for TEMaterialPair
|
||||
namespace std
|
||||
{
|
||||
template<> struct hash<LLMaterialMgr::TEMaterialPair>
|
||||
{
|
||||
inline size_t operator()(const LLMaterialMgr::TEMaterialPair& p) const noexcept
|
||||
{
|
||||
return size_t((p.te + 1) * p.materialID.getDigest64());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// For use with boost containers.
|
||||
inline size_t hash_value(const LLMaterialMgr::TEMaterialPair& p) noexcept
|
||||
{
|
||||
return size_t((p.te + 1) * p.materialID.getDigest64());
|
||||
}
|
||||
|
||||
#endif // LL_LLMATERIALMGR_H
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@
|
|||
LLOutfitObserver::LLOutfitObserver() :
|
||||
mCOFLastVersion(LLViewerInventoryCategory::VERSION_UNKNOWN)
|
||||
{
|
||||
mItemNameHash.finalize();
|
||||
gInventory.addObserver(this);
|
||||
}
|
||||
|
||||
|
|
@ -83,7 +82,7 @@ bool LLOutfitObserver::checkCOF()
|
|||
return false;
|
||||
|
||||
bool cof_changed = false;
|
||||
LLMD5 item_name_hash = gInventory.hashDirectDescendentNames(cof);
|
||||
LLUUID item_name_hash = gInventory.hashDirectDescendentNames(cof);
|
||||
if (item_name_hash != mItemNameHash)
|
||||
{
|
||||
cof_changed = true;
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@
|
|||
#define LL_OUTFITOBSERVER_H
|
||||
|
||||
#include "llsingleton.h"
|
||||
#include "llmd5.h"
|
||||
|
||||
/**
|
||||
* Outfit observer facade that provides simple possibility to subscribe on
|
||||
|
|
@ -78,7 +77,7 @@ protected:
|
|||
|
||||
bool mLastOutfitDirtiness;
|
||||
|
||||
LLMD5 mItemNameHash;
|
||||
LLUUID mItemNameHash;
|
||||
|
||||
private:
|
||||
signal_t mBOFReplaced;
|
||||
|
|
|
|||
|
|
@ -140,17 +140,12 @@ void LLPathfindingNavMesh::handleNavMeshResult(const LLSD &pContent, U32 pNavMes
|
|||
if ( pContent.has(NAVMESH_DATA_FIELD) )
|
||||
{
|
||||
const LLSD::Binary &value = pContent.get(NAVMESH_DATA_FIELD).asBinary();
|
||||
// <FS:Beq pp Rye> Use new variants of unzip_llsd calls
|
||||
// unsigned int binSize = value.size();
|
||||
// std::string newStr(reinterpret_cast<const char *>(&value[0]), binSize);
|
||||
// std::istringstream streamdecomp( newStr );
|
||||
// </FS:Beq pp Rye>
|
||||
unsigned int binSize = value.size();
|
||||
std::string newStr(reinterpret_cast<const char *>(&value[0]), binSize);
|
||||
std::istringstream streamdecomp( newStr );
|
||||
size_t decompBinSize = 0;
|
||||
bool valid = false;
|
||||
// <FS:Beq pp Rye> Use new variants of unzip_llsd calls
|
||||
// U8* pUncompressedNavMeshContainer = unzip_llsdNavMesh( valid, decompBinSize, streamdecomp, binSize ) ;
|
||||
U8* pUncompressedNavMeshContainer = unzip_llsdNavMesh( valid, decompBinSize, value.data(), value.size() ) ;
|
||||
// </FS:Beq pp Rye>
|
||||
U8* pUncompressedNavMeshContainer = unzip_llsdNavMesh( valid, decompBinSize, streamdecomp, binSize ) ;
|
||||
if ( !valid )
|
||||
{
|
||||
LL_WARNS() << "Unable to decompress the navmesh llsd." << LL_ENDL;
|
||||
|
|
|
|||
|
|
@ -39,7 +39,9 @@
|
|||
#include "llinventorydefines.h"
|
||||
#include "llinventorymodel.h"
|
||||
#include "lllineeditor.h"
|
||||
#include "llmd5.h"
|
||||
#include "llnotificationsutil.h"
|
||||
#include "llmd5.h"
|
||||
#include "llresmgr.h"
|
||||
#include "roles_constants.h"
|
||||
#include "llscrollbar.h"
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
#include "llinventorymodel.h"
|
||||
#include "llkeyboard.h"
|
||||
#include "lllineeditor.h"
|
||||
#include "llmd5.h"
|
||||
#include "llhelp.h"
|
||||
#include "llnotificationsutil.h"
|
||||
#include "llresmgr.h"
|
||||
|
|
|
|||
|
|
@ -131,7 +131,10 @@ std::queue<LLFilePickerThread*> LLFilePickerThread::sDeadQ;
|
|||
void LLFilePickerThread::getFile()
|
||||
{
|
||||
#if LL_WINDOWS
|
||||
// Todo: get rid of LLFilePickerThread and make this modeless
|
||||
start();
|
||||
#elif LL_DARWIN
|
||||
runModeless();
|
||||
#else
|
||||
run();
|
||||
#endif
|
||||
|
|
@ -174,7 +177,82 @@ void LLFilePickerThread::run()
|
|||
LLMutexLock lock(sMutex);
|
||||
sDeadQ.push(this);
|
||||
}
|
||||
}
|
||||
|
||||
void LLFilePickerThread::runModeless()
|
||||
{
|
||||
BOOL result = FALSE;
|
||||
LLFilePicker picker;
|
||||
|
||||
if (mIsSaveDialog)
|
||||
{
|
||||
result = picker.getSaveFileModeless(mSaveFilter,
|
||||
mProposedName,
|
||||
modelessStringCallback,
|
||||
this);
|
||||
}
|
||||
else if (mIsGetMultiple)
|
||||
{
|
||||
result = picker.getMultipleOpenFilesModeless(mLoadFilter, modelessVectorCallback, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = picker.getOpenFileModeless(mLoadFilter, modelessVectorCallback, this);
|
||||
}
|
||||
|
||||
if (!result)
|
||||
{
|
||||
LLMutexLock lock(sMutex);
|
||||
sDeadQ.push(this);
|
||||
}
|
||||
}
|
||||
|
||||
void LLFilePickerThread::modelessStringCallback(bool success,
|
||||
std::string &response,
|
||||
void *user_data)
|
||||
{
|
||||
LLFilePickerThread *picker = (LLFilePickerThread*)user_data;
|
||||
if (success)
|
||||
{
|
||||
picker->mResponses.push_back(response);
|
||||
}
|
||||
|
||||
{
|
||||
LLMutexLock lock(sMutex);
|
||||
sDeadQ.push(picker);
|
||||
}
|
||||
}
|
||||
|
||||
void LLFilePickerThread::modelessVectorCallback(bool success,
|
||||
std::vector<std::string> &responses,
|
||||
void *user_data)
|
||||
{
|
||||
LLFilePickerThread *picker = (LLFilePickerThread*)user_data;
|
||||
if (success)
|
||||
{
|
||||
if (picker->mIsGetMultiple)
|
||||
{
|
||||
picker->mResponses = responses;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<std::string>::iterator iter = responses.begin();
|
||||
while (iter != responses.end())
|
||||
{
|
||||
if (!iter->empty())
|
||||
{
|
||||
picker->mResponses.push_back(*iter);
|
||||
break;
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
LLMutexLock lock(sMutex);
|
||||
sDeadQ.push(picker);
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
|
|
|
|||
|
|
@ -106,6 +106,9 @@ public:
|
|||
void getFile();
|
||||
|
||||
virtual void run();
|
||||
void runModeless();
|
||||
static void modelessStringCallback(bool success, std::string &response, void *user_data);
|
||||
static void modelessVectorCallback(bool success, std::vector<std::string> &responses, void *user_data);
|
||||
|
||||
virtual void notify(const std::vector<std::string>& filenames) = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
#include "llviewerwearable.h"
|
||||
#include "llviewercontrol.h"
|
||||
#include "llviewerregion.h"
|
||||
#include "llmd5.h" // <FS:Ansariel> [Legacy Bake]
|
||||
|
||||
using namespace LLAvatarAppearanceDefines;
|
||||
|
||||
|
|
|
|||
|
|
@ -98,7 +98,6 @@ public:
|
|||
// not the wearable asset itself.
|
||||
void refreshName();
|
||||
// <FS:Ansariel> [Legacy Bake]
|
||||
///*virtual*/void addToBakedTextureHash(LLMD5& hash) const {}
|
||||
/*virtual*/void addToBakedTextureHash(LLMD5& hash) const;
|
||||
|
||||
protected:
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<floater name="perm prefs" title="STANDARD RETTIGHEDER">
|
||||
<panel label="Tilladelser" name="permissions">
|
||||
<button label="?" label_selected="?" name="help"/>
|
||||
<check_box label="Del med gruppe" name="share_with_group"/>
|
||||
<check_box label="Tillad enhver at kopiere" name="everyone_copy"/>
|
||||
<text name="NextOwnerLabel">
|
||||
Næste ejer kan:
|
||||
</text>
|
||||
<check_box label="Redigere" name="next_owner_modify"/>
|
||||
<check_box label="Kopiére" name="next_owner_copy"/>
|
||||
<check_box label="Sælge/Give væk" name="next_owner_transfer"/>
|
||||
</panel>
|
||||
<button label="OK" label_selected="OK" name="ok"/>
|
||||
<button label="Annullér" label_selected="Annullér" name="cancel"/>
|
||||
</floater>
|
||||
|
|
@ -65,6 +65,7 @@ PCRE Copyright (c) 1997-2012 University of Cambridge.
|
|||
SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga.
|
||||
SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com).
|
||||
xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
|
||||
xxHash Copyright (C) 2012-2020 Yann Collet.
|
||||
zlib Copyright (C) 1995-2012 Jean-loup Gailly und Mark Adler.
|
||||
|
||||
Second Life Viewer verwendet Havok (TM) Physics. (c)Copyright 1999-2010 Havok.com Inc. (und Lizenzgeber). Alle Rechte vorbehalten. Details siehe www.havok.com.
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<floater name="perm prefs" title="Standard-Berechtigungen für Hochladen">
|
||||
<panel label="Berechtigungen" name="permissions">
|
||||
<button label="?" label_selected="?" name="help"/>
|
||||
<check_box label="Mit Gruppe teilen" name="share_with_group"/>
|
||||
<check_box label="Kopieren allen erlauben" name="everyone_copy"/>
|
||||
<text name="NextOwnerLabel">
|
||||
Nächster Eigentümer kann:
|
||||
</text>
|
||||
<check_box label="Bearbeiten" name="next_owner_modify"/>
|
||||
<check_box label="Kopieren" name="next_owner_copy"/>
|
||||
<check_box label="Verkaufen/Weggeben" name="next_owner_transfer"/>
|
||||
</panel>
|
||||
<button label="OK" label_selected="OK" name="ok"/>
|
||||
<button label="Abbrechen" label_selected="Abbrechen" name="cancel"/>
|
||||
</floater>
|
||||
|
|
@ -260,6 +260,7 @@ PCRE Copyright (c) 1997-2012 University of Cambridge
|
|||
SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
|
||||
SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
|
||||
xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
|
||||
xxHash Copyright (C) 2012-2020 Yann Collet.
|
||||
zlib Copyright (C) 1995-2012 Jean-loup Gailly and Mark Adler.
|
||||
Some icons by Joseph Wain / glyphish.com
|
||||
|
||||
|
|
|
|||
|
|
@ -309,6 +309,17 @@
|
|||
media_plugin_cef
|
||||
</impl>
|
||||
</mimetype>
|
||||
<mimetype name="application/octet-stream">
|
||||
<label name="application/octet-stream_label">
|
||||
Stream
|
||||
</label>
|
||||
<widgettype>
|
||||
movie
|
||||
</widgettype>
|
||||
<impl>
|
||||
media_plugin_libvlc
|
||||
</impl>
|
||||
</mimetype>
|
||||
<mimetype name="audio/mid">
|
||||
<label name="audio/mid_label">
|
||||
Audio (MIDI)
|
||||
|
|
@ -474,6 +485,17 @@
|
|||
media_plugin_libvlc
|
||||
</impl>
|
||||
</mimetype>
|
||||
<mimetype name="video/x-flv">
|
||||
<label name="video/x-flv_label">
|
||||
Movie (flv)
|
||||
</label>
|
||||
<widgettype>
|
||||
movie
|
||||
</widgettype>
|
||||
<impl>
|
||||
media_plugin_libvlc
|
||||
</impl>
|
||||
</mimetype>
|
||||
<mimetype name="application/octet-stream">
|
||||
<label name="video/octet-stream">
|
||||
Movie
|
||||
|
|
|
|||
|
|
@ -287,6 +287,17 @@
|
|||
media_plugin_cef
|
||||
</impl>
|
||||
</mimetype>
|
||||
<mimetype name="application/octet-stream">
|
||||
<label name="application/octet-stream_label">
|
||||
Stream
|
||||
</label>
|
||||
<widgettype>
|
||||
movie
|
||||
</widgettype>
|
||||
<impl>
|
||||
media_plugin_gstreamer
|
||||
</impl>
|
||||
</mimetype>
|
||||
<mimetype name="audio/mid">
|
||||
<label name="audio/mid_label">
|
||||
Audio (MIDI)
|
||||
|
|
@ -452,6 +463,17 @@
|
|||
media_plugin_gstreamer
|
||||
</impl>
|
||||
</mimetype>
|
||||
<mimetype name="video/x-flv">
|
||||
<label name="video/x-flv_label">
|
||||
Movie (flv)
|
||||
</label>
|
||||
<widgettype>
|
||||
movie
|
||||
</widgettype>
|
||||
<impl>
|
||||
media_plugin_gstreamer
|
||||
</impl>
|
||||
</mimetype>
|
||||
<mimetype menu="1" name="video/quicktime">
|
||||
<label name="video/quicktime_label">
|
||||
Movie (QuickTime)
|
||||
|
|
|
|||
|
|
@ -287,6 +287,17 @@
|
|||
media_plugin_cef
|
||||
</impl>
|
||||
</mimetype>
|
||||
<mimetype name="application/octet-stream">
|
||||
<label name="application/octet-stream_label">
|
||||
Stream
|
||||
</label>
|
||||
<widgettype>
|
||||
movie
|
||||
</widgettype>
|
||||
<impl>
|
||||
media_plugin_libvlc
|
||||
</impl>
|
||||
</mimetype>
|
||||
<mimetype name="audio/mid">
|
||||
<label name="audio/mid_label">
|
||||
Audio (MIDI)
|
||||
|
|
@ -452,6 +463,17 @@
|
|||
media_plugin_libvlc
|
||||
</impl>
|
||||
</mimetype>
|
||||
<mimetype name="video/x-flv">
|
||||
<label name="video/x-flv_label">
|
||||
Movie (flv)
|
||||
</label>
|
||||
<widgettype>
|
||||
movie
|
||||
</widgettype>
|
||||
<impl>
|
||||
media_plugin_libvlc
|
||||
</impl>
|
||||
</mimetype>
|
||||
<mimetype menu="1" name="video/quicktime">
|
||||
<label name="video/quicktime_label">
|
||||
Movie (QuickTime)
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ https://www.firestormviewer.org/choose-your-platform/
|
|||
|
||||
For more information, see our FAQ below:
|
||||
http://secondlife.com/viewer-access-faq</string>
|
||||
<string name="LoginFailed">Grid emergency login failure.
|
||||
<string name="LoginFailed">Login failure.
|
||||
If you feel this is an error, please contact the grid support.</string>
|
||||
<string name="LoginIntermediateOptionalUpdateAvailable">Optional viewer update available: [VERSION]</string>
|
||||
<string name="LoginFailedRequiredUpdate">Required viewer update: [VERSION]</string>
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ PCRE Copyright (c) 1997-2012 University of Cambridge
|
|||
SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
|
||||
SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
|
||||
xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
|
||||
xxHash Copyright (C) 2012-2020 Yann Collet.
|
||||
zlib Copyright (C) 1995-2012 Jean-loup Gailly y Mark Adler.
|
||||
Algunos iconos por Joseph Wain / glyphish.com
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<floater name="perm prefs" title="PERMISOS POR DEFECTO DE SUBIDA">
|
||||
<panel label="Permisos" name="permissions">
|
||||
<button label="?" label_selected="?" name="help"/>
|
||||
<check_box label="Compartir con el grupo" name="share_with_group"/>
|
||||
<check_box label="Permitir a cualquiera que lo copie" name="everyone_copy"/>
|
||||
<text name="NextOwnerLabel">
|
||||
El próximo propietario puede:
|
||||
</text>
|
||||
<check_box label="Modificarlo" name="next_owner_modify"/>
|
||||
<check_box label="Copiarlo" name="next_owner_copy"/>
|
||||
<check_box label="Revenderlo/Darlo" name="next_owner_transfer"/>
|
||||
</panel>
|
||||
<button label="OK" label_selected="OK" name="ok"/>
|
||||
<button label="Cancelar" label_selected="Cancelar" name="cancel"/>
|
||||
</floater>
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<floater name="perm prefs" title="Permissions par défaut des objets mis en ligne">
|
||||
<panel label="Permissions" name="permissions">
|
||||
<check_box label="Partager avec le groupe" name="share_with_group"/>
|
||||
<check_box label="Autoriser n'importe qui à copier" name="everyone_copy"/>
|
||||
<text name="NextOwnerLabel">Le prochain propriétaire pourra :</text>
|
||||
<check_box label="Modifier" name="next_owner_modify"/>
|
||||
<check_box label="Copier" name="next_owner_copy"/>
|
||||
<check_box label="Transférer" name="next_owner_transfer"/>
|
||||
</panel>
|
||||
<button label="OK" label_selected="OK" name="ok"/>
|
||||
<button label="Annuler" label_selected="Annuler" name="cancel"/>
|
||||
</floater>
|
||||
|
|
@ -85,6 +85,7 @@ PCRE Copyright (c) 1997-2012 University of Cambridge
|
|||
SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
|
||||
SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
|
||||
xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
|
||||
xxHash Copyright (C) 2012-2020 Yann Collet.
|
||||
zlib Copyright (C) 1995-2012 Jean-loup Gailly and Mark Adler.
|
||||
|
||||
Il Viewer Second Life utilizza Havok (TM) Physics. (c)Copyright 1999-2010 Havok.com Inc. (e licenziatari). Tutti i diritti riservati. Per informazioni dettagliate, vedere www.havok.com.
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<floater name="perm prefs" title="Permessi predefiniti di caricamento">
|
||||
<panel label="Permessi" name="permissions">
|
||||
<button label="?" label_selected="?" name="help"/>
|
||||
<check_box label="Condividi con il gruppo" name="share_with_group"/>
|
||||
<check_box label="Permetti a chiunque di copiare" name="everyone_copy"/>
|
||||
<text name="NextOwnerLabel">
|
||||
Il prossimo possessore può:
|
||||
</text>
|
||||
<check_box label="Modificare" name="next_owner_modify"/>
|
||||
<check_box label="Copiare" name="next_owner_copy"/>
|
||||
<check_box label="Trasferire" name="next_owner_transfer"/>
|
||||
</panel>
|
||||
<button label="Annulla" label_selected="Annulla" name="cancel"/>
|
||||
</floater>
|
||||
|
|
@ -30,6 +30,7 @@ PCRE Copyright (c) 1997-2012 University of Cambridge
|
|||
SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
|
||||
SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
|
||||
xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
|
||||
xxHash Copyright (C) 2012-2020 Yann Collet.
|
||||
zlib Copyright (C) 1995-2012 Jean-loup Gailly and Mark Adler.
|
||||
|
||||
Second Life ビューワでは Havok (TM) Physics が使用されています。(c)Copyright 1999-2010 Havok.com Inc. (and its Licensors).無断複写・複製・転載を禁じます。詳細については www.havok.com をご参照ください。
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<floater name="perm prefs" title="デフォルトのアップロード権限">
|
||||
<panel label="権限" name="permissions">
|
||||
<button label="?" label_selected="?" name="help"/>
|
||||
<check_box label="グループで共同管理" name="share_with_group"/>
|
||||
<check_box label="誰に対してもコピーを許可" name="everyone_copy"/>
|
||||
<text name="NextOwnerLabel">
|
||||
次の所有者ができる操作:
|
||||
</text>
|
||||
<check_box label="修正" name="next_owner_modify"/>
|
||||
<check_box label="コピー" name="next_owner_copy"/>
|
||||
<check_box label="再販・プレゼント" name="next_owner_transfer"/>
|
||||
</panel>
|
||||
<button label="OK" label_selected="OK" name="ok"/>
|
||||
<button label="取り消し" label_selected="取り消し" name="cancel"/>
|
||||
</floater>
|
||||
|
|
@ -85,6 +85,7 @@ PCRE Copyright (c) 1997-2012 University of Cambridge
|
|||
SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
|
||||
SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
|
||||
xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
|
||||
xxHash Copyright (C) 2012-2020 Yann Collet.
|
||||
zlib Copyright (C) 1995-2012 Jean-loup Gailly and Mark Adler.
|
||||
|
||||
O Visualizador do Second Life usa Havok (TM) Physics. (c)Copyright 1999-2010 Havok.com Inc. (e seus Licenciantes). Todos os direitos reservados. Consulte www.havok.com para obter detalhes.
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<floater name="perm prefs" title="PERMISSÕES PADRÃO DE UPLOAD">
|
||||
<panel label="Permissões" name="permissions">
|
||||
<button label="?" label_selected="?" name="help"/>
|
||||
<check_box label="Compartilhar com o grupo" name="share_with_group"/>
|
||||
<check_box label="Permitir que qualquer um copie" name="everyone_copy"/>
|
||||
<text name="NextOwnerLabel">
|
||||
O próximo dono pode:
|
||||
</text>
|
||||
<check_box label="Modificar" name="next_owner_modify"/>
|
||||
<check_box label="Copiar" name="next_owner_copy"/>
|
||||
<check_box label="Revender/Dar" name="next_owner_transfer"/>
|
||||
</panel>
|
||||
<button label="OK" label_selected="OK" name="ok"/>
|
||||
<button label="Cancelar" label_selected="Cancelar" name="cancel"/>
|
||||
</floater>
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<floater name="perm prefs" title="Стандартные разрешения">
|
||||
<panel label="Разрешения" name="permissions">
|
||||
<check_box label="Поделиться с группой" name="share_with_group"/>
|
||||
<check_box label="Разрешить всем копировать" name="everyone_copy"/>
|
||||
<text name="NextOwnerLabel">
|
||||
Следующий владелец может:
|
||||
</text>
|
||||
<check_box label="изменять" name="next_owner_modify"/>
|
||||
<check_box label="копировать" name="next_owner_copy"/>
|
||||
<check_box initial_value="true" label="перепродавать/отдавать" name="next_owner_transfer"/>
|
||||
</panel>
|
||||
<button label="ОК" label_selected="ОК" name="ok"/>
|
||||
<button label="Отмена" label_selected="Отмена" name="cancel"/>
|
||||
</floater>
|
||||
|
|
@ -78,6 +78,7 @@ PCRE Telif Hakkı (c) 1997-2012 University of Cambridge
|
|||
SDL Telif Hakkı (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
|
||||
SSLeay Telif Hakkı (C) 1995-1998 Eric Young (eay@cryptsoft.com)
|
||||
xmlrpc-epi Telif Hakkı (C) 2000 Epinions, Inc.
|
||||
xxHash Copyright (C) 2012-2020 Yann Collet.
|
||||
zlib Telif Hakkı (C) 1995-2012 Jean-loup Gailly ve Mark Adler.
|
||||
|
||||
Second Life Görüntüleyicisi Havok (TM) Fizik motorunu kullanmaktadır. (c)Telif Hakkı 1999-2010 Havok.com Inc. (ve Lisans Verenleri). Tüm Hakları Saklıdır. Ayrıntılı bilgi için bkz. www.havok.com
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<floater name="perm prefs" title="VARSAYILAN KARŞIYA YÜKLEME İZİNLERİ">
|
||||
<panel label="İzinler" name="permissions">
|
||||
<check_box label="Grupla paylaş" name="share_with_group"/>
|
||||
<check_box label="Herkese kopyalama izni ver" name="everyone_copy"/>
|
||||
<text name="NextOwnerLabel">
|
||||
Sonraki sahip şunu yapabilir:
|
||||
</text>
|
||||
<check_box label="Değiştir" name="next_owner_modify"/>
|
||||
<check_box label="Kopyala" name="next_owner_copy"/>
|
||||
<check_box initial_value="true" label="Tekrar sat/Ver" name="next_owner_transfer"/>
|
||||
</panel>
|
||||
<button label="Tamam" label_selected="Tamam" name="ok"/>
|
||||
<button label="İptal" label_selected="İptal" name="cancel"/>
|
||||
</floater>
|
||||
|
|
@ -72,6 +72,7 @@ PCRE Copyright (c) 1997-2012 University of Cambridge
|
|||
SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
|
||||
SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
|
||||
xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
|
||||
xxHash Copyright (C) 2012-2020 Yann Collet.
|
||||
zlib Copyright (C) 1995-2012 Jean-loup Gailly and Mark Adler.
|
||||
|
||||
第二人生 Viewer 採用 Havok (TM) 物理引擎。 (c)Copyright 1999-2010 Havok.com Inc.(及其放照人)。 保留一切權利。 詳情見 www.havok.com。
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<floater name="perm prefs" title="預設上傳權限">
|
||||
<panel label="權限" name="permissions">
|
||||
<check_box label="與群組分享" name="share_with_group"/>
|
||||
<check_box label="允許任何人覆製" name="everyone_copy"/>
|
||||
<text name="NextOwnerLabel">
|
||||
下一個所有人可以:
|
||||
</text>
|
||||
<check_box label="修改" name="next_owner_modify"/>
|
||||
<check_box label="恚庨" name="next_owner_copy"/>
|
||||
<check_box initial_value="true" label="轉售 / 送人" name="next_owner_transfer"/>
|
||||
</panel>
|
||||
<button label="確定" label_selected="確定" name="ok"/>
|
||||
<button label="取消" label_selected="取消" name="cancel"/>
|
||||
</floater>
|
||||
Loading…
Reference in New Issue